Commit 6000e80f authored by ransome1's avatar ransome1
Browse files

Improvements on file tabbing function, updated readme, updates translations

parent 3c223e28
...@@ -123,3 +123,4 @@ A prioritized backlog of new features and known issues can be found <a href="htt ...@@ -123,3 +123,4 @@ A prioritized backlog of new features and known issues can be found <a href="htt
- Matomo: https://github.com/matomo-org/matomo - Matomo: https://github.com/matomo-org/matomo
- chokidar: https://github.com/paulmillr/chokidar - chokidar: https://github.com/paulmillr/chokidar
- Sugar: https://github.com/andrewplummer/Sugar - Sugar: https://github.com/andrewplummer/Sugar
- PEG.js: https://github.com/pegjs/pegjs
{ {
"name": "sleek", "name": "sleek",
"productName": "sleek", "productName": "sleek",
"version": "1.1.0-rc.2", "version": "1.1.0-rc.3",
"description": "Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source", "description": "Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source",
"synopsis": "Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source", "synopsis": "Todo app based on todo.txt for Linux, Windows and MacOS, free and open-source",
"category": "ProjectManagement", "category": "ProjectManagement",
......
...@@ -322,7 +322,8 @@ body.dark #fileTabBar ul li { ...@@ -322,7 +322,8 @@ body.dark #fileTabBar ul li {
body.dark #fileTabBar ul li i { body.dark #fileTabBar ul li i {
color: #2d2d2d; color: #2d2d2d;
} }
body.dark #fileTabBar ul li:hover i { body.dark #fileTabBar ul li:hover i,
body.dark #fileTabBar ul li.is-highlighted:hover i {
color: #3273dc; color: #3273dc;
} }
body.dark #fileTabBar ul li.is-highlighted { body.dark #fileTabBar ul li.is-highlighted {
...@@ -918,19 +919,29 @@ code, pre { ...@@ -918,19 +919,29 @@ code, pre {
padding-left: 2.5em; padding-left: 2.5em;
} }
#fileTabBar ul li { #fileTabBar ul li {
width: auto; position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 15em;
min-width: 8.5em;
float: left; float: left;
color: #5a5a5a; color: #5a5a5a;
background: transparent; background: transparent;
padding: 0.35em 1em 0.35em 2.35em; padding: 0.35em 2.35em;
margin-top: 2px; margin-top: 2px;
cursor: pointer; cursor: pointer;
text-align: center;
} }
#fileTabBar ul li i { #fileTabBar ul li i {
position: absolute;
top: 0.65em;
right: 0.5em;
color: #ebebeb; color: #ebebeb;
padding-left: 0.5em; padding-left: 0.5em;
} }
#fileTabBar ul li:hover i { #fileTabBar ul li:hover i,
#fileTabBar ul li.is-highlighted:hover i {
color: #3273dc; color: #3273dc;
} }
#fileTabBar ul li.is-highlighted { #fileTabBar ul li.is-highlighted {
...@@ -2472,6 +2483,16 @@ body { ...@@ -2472,6 +2483,16 @@ body {
nav ul, nav ul,
header, header,
#drawerClose,
#viewDrawer,
#todoContext,
#filterContext,
#modalChangeFile,
#modalPrompt,
#messages,
#modalForm,
#modalHelp,
#modalSettings,
#btnFiltersResetFilters { #btnFiltersResetFilters {
display: none !important; display: none !important;
} }
......
This diff is collapsed.
...@@ -744,6 +744,12 @@ ...@@ -744,6 +744,12 @@
<td><a href="https://github.com/viktor-shmigol/electron-windows-badge/" target="_blank">Electron Windows Badge</a></td> <td><a href="https://github.com/viktor-shmigol/electron-windows-badge/" target="_blank">Electron Windows Badge</a></td>
<td><a href="https://github.com/andrewplummer/Sugar" target="_blank">Sugar</a></td> <td><a href="https://github.com/andrewplummer/Sugar" target="_blank">Sugar</a></td>
</tr> </tr>
<tr>
<td><a href="https://github.com/pegjs/pegjs" target="_blank">PEG.js</a></td>
<td></td>
<td></td>
<td></td>
</tr>
</table> </table>
</section> </section>
<button class="modal-close close is-large" aria-label="close" tabindex="0"></button> <button class="modal-close close is-large" aria-label="close" tabindex="0"></button>
......
"use strict"; "use strict";
import { resetFilters, resetModal, handleError, userData, setUserData, translations, getConfirmation } from "../render.js"; import { resetFilters, resetModal, handleError, userData, setUserData, translations } from "../render.js";
import { _paq } from "./matomo.mjs"; import { _paq } from "./matomo.mjs";
import { createModalJail } from "../configs/modal.config.mjs"; import { createModalJail } from "../configs/modal.config.mjs";
...@@ -8,27 +8,29 @@ const modalChangeFile = document.getElementById("modalChangeFile"); ...@@ -8,27 +8,29 @@ const modalChangeFile = document.getElementById("modalChangeFile");
const modalChangeFileTable = document.getElementById("modalChangeFileTable"); const modalChangeFileTable = document.getElementById("modalChangeFileTable");
const fileTabBarList = document.querySelector("#fileTabBar ul"); const fileTabBarList = document.querySelector("#fileTabBar ul");
function removeFileFromList(filePath, files) { function removeFileFromList(isActive, index) {
try { try {
// remove file from files array if(isActive && index-1 === -1) {
files = files.filter(function(file) { userData.files[index+1][0] = 1;
return file[1] != filePath; } else if(isActive && index-1 >= 0) {
}); userData.files[index-1][0] = 1;
// persist new files array }
setUserData("files", files); userData.files.splice(index, 1);
// reset everything and load again setUserData("files", userData.files);
resetFilters().then(function(response) { resetFilters().then(function(response) {
console.info(response); console.info(response);
index = userData.files.findIndex(file => file[0] === 1);
window.api.send("startFileWatcher", userData.files[index][1]);
}).catch(function(error) { }).catch(function(error) {
handleError(error); handleError(error);
}); });
return Promise.resolve("Success: File removed from list: " + filePath); return Promise.resolve("Success: File removed from list");
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
} }
function selectFileFromList(file) { function selectFileFromList(index) {
try { try {
resetFilters().then(function(response) { resetFilters().then(function(response) {
console.info(response); console.info(response);
...@@ -36,78 +38,60 @@ function selectFileFromList(file) { ...@@ -36,78 +38,60 @@ function selectFileFromList(file) {
handleError(error); handleError(error);
}); });
resetModal().then(response => { resetModal().then(response => {
window.api.send("startFileWatcher", file); window.api.send("startFileWatcher", userData.files[index][1]);
console.info(response); console.info(response);
}).catch(error => { }).catch(error => {
handleError(error); handleError(error);
}); });
return Promise.resolve("Success: File selected from list: " + file); return Promise.resolve("Success: File selected");
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
} }
function generateFileTabs() { function generateFileList() {
let files = userData.files; try {
if(files.length>1 && userData.fileTabs) { if(userData.files.length>1 && userData.fileTabs) {
fileTabBar.classList.add("is-active"); fileTabBar.classList.add("is-active");
} else {
fileTabBar.classList.remove("is-active");
return false;
}
fileTabBarList.innerHTML = null;
for (let file in files) {
const isActive = files[file][0];
const filePath = files[file][1];
const fileName = files[file][1].split("/").pop();
let listItem = document.createElement("li");
listItem.innerHTML = fileName;
listItem.innerHTML += "<i class=\"fas fa-minus-circle\"></i>";
if(isActive===1) {
listItem.classList.add("is-highlighted");
} else { } else {
// only add remove button to not selected ones to reduce complexity fileTabBar.classList.remove("is-active");
listItem.querySelector("i").onclick = function() {
getConfirmation(removeFileFromList, translations.fileRemovalPrompt, filePath, files);
}
} }
listItem.onclick = function(event) { fileTabBarList.innerHTML = null;
// if click was on minus circle function is aborted
if(event.target.classList.contains("fas")) return false;
// just send the new file to filewatcher
selectFileFromList(filePath).then(function(response) {
console.info(response);
}).catch(function(error) {
handleError(error);
});
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "File-Tab", "Click on tab"]);
}
fileTabBarList.appendChild(listItem);
}
}
function showFiles() {
try {
let files = userData.files;
modalChangeFile.classList.add("is-active");
modalChangeFile.focus();
modalChangeFileTable.innerHTML = null; modalChangeFileTable.innerHTML = null;
modalChangeFileTable.classList.add("files"); modalChangeFileTable.classList.add("files");
for (let file in files) { for (let i = 0; i < userData.files.length; i++) {
// skip if file doesn't exist let isActive = userData.files[i][0];
let fileName = userData.files[i][1].split("/").pop();
let listItem = document.createElement("li");
listItem.innerHTML = fileName;
listItem.innerHTML += "<i class=\"fas fa-minus-circle\"></i>";
if(isActive===1) listItem.classList.add("is-highlighted");
listItem.querySelector("i").onclick = function() {
removeFileFromList(isActive, i);
}
if(!isActive) {
listItem.onclick = function(event) {
if(event.target.classList.contains("fas")) return false;
selectFileFromList(i).then(function(response) {
console.info(response);
}).catch(function(error) {
handleError(error);
});
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "File-Tab", "Click on tab"]);
}
}
fileTabBarList.appendChild(listItem);
let row = modalChangeFileTable.insertRow(-1); let row = modalChangeFileTable.insertRow(-1);
let cell1 = row.insertCell(0); let cell1 = row.insertCell(0);
let cell2 = row.insertCell(1); let cell2 = row.insertCell(1);
let cell3 = row.insertCell(2); let cell3 = row.insertCell(2);
row.setAttribute("data-path", files[file][1]); if(userData.files[i][0]===1) {
if(files[file][0]===1) {
cell1.innerHTML = "<button disabled>" + translations.selected + "</button>"; cell1.innerHTML = "<button disabled>" + translations.selected + "</button>";
} else { } else {
cell1.innerHTML = "<button tabindex=\"0\">" + translations.select + "</button>"; cell1.innerHTML = "<button tabindex=\"0\">" + translations.select + "</button>";
cell1.onclick = function() { cell1.onclick = function() {
// just send the new file to filewatcher selectFileFromList(i).then(function(response) {
selectFileFromList(this.parentElement.getAttribute("data-path")).then(function(response) {
console.info(response); console.info(response);
}).catch(function(error) { }).catch(function(error) {
handleError(error); handleError(error);
...@@ -115,21 +99,21 @@ function showFiles() { ...@@ -115,21 +99,21 @@ function showFiles() {
// trigger matomo event // trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "File", "Click on select button"]); if(userData.matomoEvents) _paq.push(["trackEvent", "File", "Click on select button"]);
} }
cell3.innerHTML = "<a href=\"#\" tabindex=\"0\"><i class=\"fas fa-minus-circle\"></i></a>";
cell3.title = translations.delete;
cell3.onclick = async function() {
await getConfirmation(removeFileFromList, translations.fileRemovalPrompt, this.parentElement.getAttribute("data-path"), files);
showFiles().then(response => {
console.info(response);
}).catch(error => {
handleError(error);
});
}
} }
cell2.innerHTML = files[file][1]; cell2.innerHTML = userData.files[i][1];
cell3.innerHTML = "<a href=\"#\" tabindex=\"0\"><i class=\"fas fa-minus-circle\"></i></a>";
cell3.title = translations.delete;
cell3.onclick = function() {
removeFileFromList(isActive, i);
generateFileList().then(response => {
modalChangeFile.classList.add("is-active");
modalChangeFile.focus();
console.info(response);
}).catch(error => {
handleError(error);
});
}
} }
// create the modal jail, so tabbing won't leave modal
createModalJail(modalChangeFile);
return Promise.resolve("Success: File changer modal built and opened"); return Promise.resolve("Success: File changer modal built and opened");
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
...@@ -138,8 +122,11 @@ function showFiles() { ...@@ -138,8 +122,11 @@ function showFiles() {
btnOpenTodoFile.onclick = function() { btnOpenTodoFile.onclick = function() {
if(typeof userData.files === "object" && userData.files.length>0) { if(typeof userData.files === "object" && userData.files.length>0) {
showFiles().then(response => { generateFileList().then(response => {
console.info(response); console.info(response);
modalChangeFile.classList.add("is-active");
modalChangeFile.focus();
createModalJail(modalChangeFile);
}).catch(error => { }).catch(error => {
handleError(error); handleError(error);
}); });
...@@ -150,4 +137,4 @@ btnOpenTodoFile.onclick = function() { ...@@ -150,4 +137,4 @@ btnOpenTodoFile.onclick = function() {
if(userData.matomoEvents) _paq.push(["trackEvent", "Menu", "Click on Files"]); if(userData.matomoEvents) _paq.push(["trackEvent", "Menu", "Click on Files"]);
} }
export { generateFileTabs }; export { generateFileList, removeFileFromList };
...@@ -155,5 +155,5 @@ ...@@ -155,5 +155,5 @@
"deferredTodos": "Verzögertes Datum", "deferredTodos": "Verzögertes Datum",
"noFiltersFound": "Keine Filter gefunden", "noFiltersFound": "Keine Filter gefunden",
"fileRemovalPrompt": "Die Datei wird lediglich aus dieser Liste entfernt, dabei aber nicht von der Festplatte gelöscht.", "fileRemovalPrompt": "Die Datei wird lediglich aus dieser Liste entfernt, dabei aber nicht von der Festplatte gelöscht.",
"fileTabs": "Dateireiter" "fileTabs": "Reiter"
} }
...@@ -154,5 +154,5 @@ ...@@ -154,5 +154,5 @@
"deferredTodos": "Threshold date in the future", "deferredTodos": "Threshold date in the future",
"noFiltersFound": "No filters found", "noFiltersFound": "No filters found",
"fileRemovalPrompt": "This will only remove the file from your file list, it will not be deleted from your drive.", "fileRemovalPrompt": "This will only remove the file from your file list, it will not be deleted from your drive.",
"fileTabs": "File tabs" "fileTabs": "Tabs"
} }
...@@ -153,5 +153,5 @@ ...@@ -153,5 +153,5 @@
"deferredTodos": "Fecha límite en el futuro", "deferredTodos": "Fecha límite en el futuro",
"noFiltersFound": "No se encontraron filtros", "noFiltersFound": "No se encontraron filtros",
"fileRemovalPrompt": "Esto solo eliminará el archivo de su lista de archivos, no se eliminará de su unidad.", "fileRemovalPrompt": "Esto solo eliminará el archivo de su lista de archivos, no se eliminará de su unidad.",
"fileTabs": "Pestañas de archivo" "fileTabs": "Pestañas"
} }
...@@ -154,5 +154,5 @@ ...@@ -154,5 +154,5 @@
"deferredTodos": "Date seuil dans le futur", "deferredTodos": "Date seuil dans le futur",
"noFiltersFound": "Aucun filtre trouvé", "noFiltersFound": "Aucun filtre trouvé",
"fileRemovalPrompt": "Cela supprimera uniquement le fichier de votre liste de fichiers, il ne sera pas supprimé de votre lecteur.", "fileRemovalPrompt": "Cela supprimera uniquement le fichier de votre liste de fichiers, il ne sera pas supprimé de votre lecteur.",
"fileTabs": "Onglets de fichier" "fileTabs": "Onglets"
} }
...@@ -153,5 +153,5 @@ ...@@ -153,5 +153,5 @@
"deferredTodos": "Data limite nel futuro", "deferredTodos": "Data limite nel futuro",
"noFiltersFound": "Nessun filtro trovato", "noFiltersFound": "Nessun filtro trovato",
"fileRemovalPrompt": "Questo rimuoverà solo il file dall'elenco dei file, non verrà eliminato dall'unità.", "fileRemovalPrompt": "Questo rimuoverà solo il file dall'elenco dei file, non verrà eliminato dall'unità.",
"fileTabs": "Schede di file" "fileTabs": "Schede"
} }
...@@ -154,5 +154,5 @@ ...@@ -154,5 +154,5 @@
"deferredTodos": "将来のしきい値", "deferredTodos": "将来のしきい値",
"noFiltersFound": "フィルタが見つかりません", "noFiltersFound": "フィルタが見つかりません",
"fileRemovalPrompt": "これにより、ファイルリストからファイルが削除されるだけで、ドライブからは削除されません。", "fileRemovalPrompt": "これにより、ファイルリストからファイルが削除されるだけで、ドライブからは削除されません。",
"fileTabs": "ファイルタブ" "fileTabs": "タブ"
} }
...@@ -154,5 +154,5 @@ ...@@ -154,5 +154,5 @@
"deferredTodos": "Data limite no futuro", "deferredTodos": "Data limite no futuro",
"noFiltersFound": "Nenhum filtro encontrado", "noFiltersFound": "Nenhum filtro encontrado",
"fileRemovalPrompt": "Isso removerá apenas o arquivo da lista de arquivos, não será excluído da unidade.", "fileRemovalPrompt": "Isso removerá apenas o arquivo da lista de arquivos, não será excluído da unidade.",
"fileTabs": "Guias de arquivo" "fileTabs": "Abas"
} }
...@@ -154,5 +154,5 @@ ...@@ -154,5 +154,5 @@
"deferredTodos": "未来的阈值日期", "deferredTodos": "未来的阈值日期",
"noFiltersFound": "未找到过滤器", "noFiltersFound": "未找到过滤器",
"fileRemovalPrompt": "这只会从您的文件列表中删除该文件,而不会从您的驱动器中删除它。", "fileRemovalPrompt": "这只会从您的文件列表中删除该文件,而不会从您的驱动器中删除它。",
"fileTabs": "文件标签" "fileTabs": "标签"
} }
...@@ -386,8 +386,9 @@ const createWindow = async function() { ...@@ -386,8 +386,9 @@ const createWindow = async function() {
}, },
{ type: "separator" }, { type: "separator" },
{ {
role: "close", click: function () {
accelerator: "Command+W", mainWindow.close();
},
label: translations.close label: translations.close
}, },
{ {
...@@ -445,7 +446,9 @@ const createWindow = async function() { ...@@ -445,7 +446,9 @@ const createWindow = async function() {
}, },
{ type: "separator" }, { type: "separator" },
{ {
role: "close", click: function () {
mainWindow.close();
},
label: translations.close label: translations.close
} }
]; ];
...@@ -666,6 +669,9 @@ const createWindow = async function() { ...@@ -666,6 +669,9 @@ const createWindow = async function() {
const configureWindowEvents = function() { const configureWindowEvents = function() {
try { try {
ipcMain ipcMain
.on("closeWindow", (event, args) => {
mainWindow.close();
})
.on("userData", (event, args) => { .on("userData", (event, args) => {
if(args) userData.set(args[0], args[1]); if(args) userData.set(args[0], args[1]);
mainWindow.webContents.send("userData", userData.data); mainWindow.webContents.send("userData", userData.data);
......
...@@ -16,7 +16,8 @@ contextBridge.exposeInMainWorld( ...@@ -16,7 +16,8 @@ contextBridge.exposeInMainWorld(
"update-badge", "update-badge",
"triggerFunction", "triggerFunction",
"restart", "restart",
"setTheme" "setTheme",
"closeWindow"
]; ];
if (validChannels.includes(channel)) { if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data); ipcRenderer.send(channel, data);
......
...@@ -111,7 +111,7 @@ async function getConfirmation() { ...@@ -111,7 +111,7 @@ async function getConfirmation() {
function configureMainView() { function configureMainView() {
try { try {
// generate file tabs // generate file tabs
files.generateFileTabs(); files.generateFileList();
// close filterContext if open // close filterContext if open
if(document.getElementById("filterContext").classList.contains("is-active")) document.getElementById("filterContext").classList.remove("is-active"); if(document.getElementById("filterContext").classList.contains("is-active")) document.getElementById("filterContext").classList.remove("is-active");
// set scaling factor if default font size has changed // set scaling factor if default font size has changed
...@@ -384,6 +384,15 @@ function registerKeyboardShortcuts() { ...@@ -384,6 +384,15 @@ function registerKeyboardShortcuts() {
if((event.ctrlKey || event.metaKey) && event.key === "c" && (document.activeElement.id!="todoTableSearch" && document.activeElement.id!="filterContextInput" && document.activeElement.id!="modalFormInput")) { if((event.ctrlKey || event.metaKey) && event.key === "c" && (document.activeElement.id!="todoTableSearch" && document.activeElement.id!="filterContextInput" && document.activeElement.id!="modalFormInput")) {
window.api.send("openOrCreateFile", "create"); window.api.send("openOrCreateFile", "create");
} }
// close tab or window
if((event.ctrlKey || event.metaKey) && event.key === "w") {
if(userData.files.length > 1) {
let index = userData.files.findIndex(file => file[0] === 1);
files.removeFileFromList(1, index);
} else {
window.api.send("closeWindow");
}
}
}, true) }, true)
window.addEventListener("keyup", function(event) { window.addEventListener("keyup", function(event) {
// switch files // switch files
...@@ -391,6 +400,23 @@ function registerKeyboardShortcuts() { ...@@ -391,6 +400,23 @@ function registerKeyboardShortcuts() {
if(event.key.match(regex) && userData.files.length > 1 && !modalForm.classList.contains("is-active") && (document.activeElement.id!="todoTableSearch" && document.activeElement.id!="filterContextInput" && document.activeElement.id!="modalFormInput")) { if(event.key.match(regex) && userData.files.length > 1 && !modalForm.classList.contains("is-active") && (document.activeElement.id!="todoTableSearch" && document.activeElement.id!="filterContextInput" && document.activeElement.id!="modalFormInput")) {
window.api.send("startFileWatcher", userData.files[event.key-1][1]); window.api.send("startFileWatcher", userData.files[event.key-1][1]);
} }
// cycle through tabs
if(event.ctrlKey && !event.shiftKey && event.keyCode === 9) {
let index = userData.files.findIndex(file => file[0] === 1);
if(!userData.files[index+1]) {
window.api.send("startFileWatcher", userData.files[0][1]);
} else {
window.api.send("startFileWatcher", userData.files[index+1][1]);
}
}
if(event.ctrlKey && event.shiftKey && event.keyCode === 9) {
let index = userData.files.findIndex(file => file[0] === 1);
if(!userData.files[index-1]) {
window.api.send("startFileWatcher", userData.files[userData.files.length-1][1]);
} else {
window.api.send("startFileWatcher", userData.files[index-1][1]);
}
}
// open settings // open settings
if(event.key === "," && !modalForm.classList.contains("is-active") && (document.activeElement.id!="todoTableSearch" && document.activeElement.id!="filterContextInput" && document.activeElement.id!="modalFormInput")) { if(event.key === "," && !modalForm.classList.contains("is-active") && (document.