Commit 9c9433a8 authored by ransome1's avatar ransome1
Browse files

Added file tabs, refined CSS, updated Windows screenshots

parent 8c20ef64
{
"name": "sleek",
"productName": "sleek",
"version": "1.0.9",
"version": "1.1.0",
"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",
"category": "ProjectManagement",
......
......@@ -285,6 +285,9 @@ body.dark #drawerContainer .drawer::-webkit-scrollbar-thumb {
body.dark #drawerContainer #drawerClose {
background: #3B3B3B !important;
}
body.dark #todoTableWrapper header {
background-color: #212224;
}
body.dark #todoTableWrapper::-webkit-scrollbar-thumb:window-inactive,
body.dark #todoTableWrapper::-webkit-scrollbar-thumb {
background: #3B3B3B;
......@@ -310,6 +313,25 @@ body.dark #filterContext .card #filterContextDelete,
body.dark #todoContext .card #filterContextDelete {
color: #ff3860 !important;
}
body.dark #fileTabBar ul {
background-color: #2d2d2d;
}
body.dark #fileTabBar ul li {
color: #ccc;
}
body.dark #fileTabBar ul li i {
color: #2d2d2d;
}
body.dark #fileTabBar ul li:hover i {
color: #3273dc;
}
body.dark #fileTabBar ul li.is-highlighted {
background: #212224;
color: white;
}
body.dark #fileTabBar ul li.is-highlighted i {
color: #212224;
}
body.dark .contexts button {
color: #c5ede3;
background: #247561;
......@@ -454,14 +476,6 @@ body.dark .datepicker {
body.dark .datepicker button {
background: transparent !important;
}
body.dark .datepicker .datepicker-cell.today.focused:not(.selected),
body.dark .datepicker .datepicker-cell.today:not(.selected) {
background: #212224 !important;
}
body.dark .datepicker .datepicker-cell.today.focused:not(.selected),
body.dark .datepicker .datepicker-cell.today:hover {
color: white !important;
}
body.dark .datepicker .datepicker-picker {
background: #2d2d2d;
color: white;
......@@ -475,15 +489,24 @@ body.dark .datepicker .datepicker-picker .datepicker-header .datepicker-controls
body.dark .datepicker .datepicker-picker .datepicker-footer .datepicker-controls button {
color: inherit;
}
body.dark .datepicker .datepicker-picker .datepicker-cell {
border-color: #2d2d2d;
background: #2d2d2d;
}
body.dark .datepicker .datepicker-picker .datepicker-cell.today,
body.dark .datepicker .datepicker-picker .datepicker-cell.today.selected {
color: #2d2d2d !important;
background: #ccc !important;
}
body.dark .datepicker .datepicker-picker .datepicker-cell.selected,
body.dark .datepicker .datepicker-picker .datepicker-cell.selected:hover {
color: #2d2d2d;
background: white;
body.dark .datepicker .datepicker-picker .datepicker-cell.selected:hover,
body.dark .datepicker .datepicker-picker .datepicker-cell.selected.focused {
color: white !important;
background: #3273dc !important;
}
body.dark .datepicker .datepicker-picker .datepicker-cell.focused:not(.selected),
body.dark .datepicker .datepicker-picker .datepicker-cell:hover {
color: #2d2d2d;
background: #CCCDCF;
body.dark .datepicker .datepicker-picker .datepicker-cell:hover,
body.dark .datepicker .datepicker-picker .datepicker-cell.focused {
border-color: #3273dc;
}
body.dark .contentContainer .title.is-1 {
color: white;
......@@ -838,10 +861,18 @@ code, pre {
.columns .column .is-right {
float: right;
}
.columns .column.content {
.columns #todoTableWrapper {
overflow-y: auto;
overflow-x: hidden;
}
.columns #todoTableWrapper header {
width: 100%;
float: left;
position: sticky;
top: 0;
z-index: 45;
background: white;
}
#messages {
width: 25em;
......@@ -871,6 +902,71 @@ code, pre {
background-color: #ff3860;
}
#fileTabBar {
width: 100%;
height: 1.85em;
display: none;
overflow-y: hidden;
}
#fileTabBar ul {
width: 100%;
float: left;
font-size: 0.8em;
margin-bottom: 1em;
box-shadow: -0.3em -0.3em 0.5em rgba(0, 0, 0, 0.05) inset;
background: #ebebeb;
padding-left: 2.5em;
}
#fileTabBar ul li {
width: auto;
float: left;
color: #5a5a5a;
background: transparent;
padding: 0.35em 1em 0.35em 2.35em;
margin-top: 2px;
cursor: pointer;
}
#fileTabBar ul li i {
color: #ebebeb;
padding-left: 0.5em;
}
#fileTabBar ul li:hover i {
color: #3273dc;
}
#fileTabBar ul li.is-highlighted {
background: white;
border-top-left-radius: 0.65em;
border-top-right-radius: 0.65em;
color: #3273dc;
font-family: "FreeSansBold";
}
#fileTabBar ul li.is-highlighted i {
color: white;
}
#fileTabBar.is-active {
display: block;
}
.tabs {
display: flex;
flex-shrink: 0;
margin-bottom: 0;
}
.tabs ul {
border: none !important;
}
.tabs ul li a {
color: #5a5a5a !important;
border-color: #ccc !important;
border-width: 2px;
margin-bottom: 0;
}
.tabs ul li.is-active a {
color: #3273dc !important;
border-color: #3273dc !important;
}
nav {
height: 100%;
position: relative;
......@@ -967,19 +1063,20 @@ nav ul:nth-child(2):hover #versionNumber {
}
#todoTableSearchContainer {
width: 100%;
float: left;
position: relative;
display: none;
margin: 0 1.5em;
padding-bottom: 0.5em;
box-sizing: border-box;
margin: 0;
padding: 0.5em 1.5em;
background: white;
position: sticky;
top: 0;
z-index: 45;
}
#todoTableSearchContainer #todoTableSearchAddTodo {
display: none;
position: absolute;
top: 2.4em;
right: 5em;
top: 2.35em;
right: 7em;
color: #3273dc;
background: white;
text-decoration: none;
......@@ -994,7 +1091,7 @@ nav ul:nth-child(2):hover #versionNumber {
background: white !important;
}
#todoTableSearchContainer input[type=search] {
padding-left: 3.5em;
padding-left: 3.2em;
}
#todoTableSearchContainer input[type=search]::-webkit-search-cancel-button {
-webkit-appearance: none;
......@@ -1008,15 +1105,16 @@ nav ul:nth-child(2):hover #versionNumber {
margin-top: -0.7em;
}
#todoTableSearchContainer label {
left: 5.9em;
top: 1.2em;
left: 7.5em;
}
#todoTableSearchContainer i.fa-search,
#todoTableSearchContainer .todoTableSearchQuestionmark {
font-size: 1.3em;
line-height: 1;
position: absolute;
top: 0.8em;
left: 1.4em;
top: 0.7em;
left: 2.5em;
z-index: 70;
padding: 0.75em 0;
}
......@@ -1656,21 +1754,6 @@ body.compact #autoCompleteContainer h4 {
display: block;
margin: 0 auto;
}
.modal.content .tabs {
display: flex;
flex-shrink: 0;
margin-bottom: 0;
}
.modal.content .tabs li a {
color: #5a5a5a !important;
border-color: #ccc !important;
border-width: 2px;
margin-bottom: 0;
}
.modal.content .tabs li.is-active a {
color: #3273dc !important;
border-color: #3273dc !important;
}
#modalForm[open]:not(:focus-within) {
background-color: #fffffe;
......@@ -2106,9 +2189,17 @@ body.compact #autoCompleteContainer h4 {
display: none;
z-index: 60;
}
.datepicker .datepicker-cell {
border: 2px solid white;
background: white;
}
.datepicker .datepicker-cell.today.focused:not(.selected),
.datepicker .datepicker-cell.today:not(.selected) {
background: #f0f0f0 !important;
background: #ebebeb;
}
.datepicker .datepicker-cell:hover,
.datepicker .datepicker-cell.focused {
border: 2px solid #3273dc;
}
.datepicker .datepicker-footer {
background: none;
......
This diff is collapsed.
......@@ -183,6 +183,19 @@
</span>
</td>
</tr>
<tr>
<td>
<p id="viewToggleFileTabs"></p>
</td>
<td>
<span class="toggle">
<label class="switch" for="fileTabs">
<input type="checkbox" id="fileTabs" class="viewToggle" tabindex="0">
<span class="slider round"></span>
</label>
</span>
</td>
</tr>
<tr>
<td id="viewToggleZoom"></td>
<td class="zoom">
......@@ -200,19 +213,28 @@
</div>
<div id="todoTableWrapper" class="column content">
<div id="todoTableWrapper" class="column">
<header>
<section id="fileTabBar">
<ul></ul>
</section>
<section id="todoTableSearchContainer" class="inputWrapper">
<a href="https://github.com/ransome1/sleek/wiki/FIlter-Expressions-for-Advanced-Search" target="_blank" class="todoTableSearchQuestionmark"><i class="fas fa-question-circle"></i></a>
<i class="fas fa-search"></i>
<label id="todoTableSearchLabel" for="todoTableSearch"></label>
<input id="todoTableSearch" class="input is-medium" type="search" tabindex="1" placeholder="(A) Todo text +project @context due:2020-12-12 rec:d">
<button id="todoTableSearchAddTodo" class="tag" tabindex="5"><i class="fas fa-plus"></i>&nbsp;Add as todo</button>
<section id="resultStats">
<span class="tag"></span>
</section>
<section id="todoTableSearchContainer" class="inputWrapper">
<a href="https://github.com/ransome1/sleek/wiki/FIlter-Expressions-for-Advanced-Search" target="_blank" class="todoTableSearchQuestionmark"><i class="fas fa-question-circle"></i></a>
<i class="fas fa-search"></i>
<label id="todoTableSearchLabel" for="todoTableSearch"></label>
<input id="todoTableSearch" class="input is-medium" type="search" tabindex="1" placeholder="(A) Todo text +project @context due:2020-12-12 rec:d">
<button id="todoTableSearchAddTodo" class="tag" tabindex="5"><i class="fas fa-plus"></i>&nbsp;Add as todo</button>
<section id="resultStats">
<span class="tag"></span>
</section>
</section>
</header>
<section id="todoTable" tabindex="-1"></section>
......
"use strict";
import { resetFilters, resetModal, handleError, userData, setUserData, translations } from "../render.js";
import { resetFilters, resetModal, handleError, userData, setUserData, translations, getConfirmation } from "../render.js";
import { _paq } from "./matomo.mjs";
import { createModalJail } from "../configs/modal.config.mjs";
const btnOpenTodoFile = document.getElementById("btnOpenTodoFile");
const modalChangeFile = document.getElementById("modalChangeFile");
const modalChangeFileTable = document.getElementById("modalChangeFileTable");
const fileTabBarList = document.querySelector("#fileTabBar ul");
function removeFileFromList(filePath, files) {
try {
// remove file from files array
files = files.filter(function(file) {
return file[1] != filePath;
});
// persist new files array
setUserData("files", files);
// reset everything and load again
resetFilters().then(function(response) {
console.info(response);
}).catch(function(error) {
handleError(error);
});
return Promise.resolve("Success: File removed from list: " + filePath);
} catch (error) {
return Promise.reject(error);
}
}
function selectFileFromList(file) {
try {
resetFilters().then(function(response) {
console.info(response);
}).catch(function(error) {
handleError(error);
});
resetModal().then(response => {
window.api.send("startFileWatcher", file);
console.info(response);
}).catch(error => {
handleError(error);
});
return Promise.resolve("Success: File selected from list: " + file);
} catch (error) {
return Promise.reject(error);
}
}
function generateFileTabs() {
let files = userData.files;
if(files.length>1 && userData.fileTabs) {
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 {
// only add remove button to not selected ones to reduce complexity
listItem.querySelector("i").onclick = function() {
getConfirmation(removeFileFromList, translations.fileRemovalPrompt, filePath, files);
}
}
listItem.onclick = function(event) {
// 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 {
......@@ -13,10 +93,10 @@ function showFiles() {
modalChangeFile.classList.add("is-active");
modalChangeFile.focus();
modalChangeFileTable.innerHTML = null;
modalChangeFileTable.classList.add("files");
for (let file in files) {
// skip if file doesn't exist
modalChangeFileTable.classList.add("files");
let row = modalChangeFileTable.insertRow(0);
let row = modalChangeFileTable.insertRow(-1);
let cell1 = row.insertCell(0);
let cell2 = row.insertCell(1);
let cell3 = row.insertCell(2);
......@@ -26,31 +106,19 @@ function showFiles() {
} else {
cell1.innerHTML = "<button tabindex=\"0\">" + translations.select + "</button>";
cell1.onclick = function() {
resetFilters().then(function(response) {
// just send the new file to filewatcher
selectFileFromList(this.parentElement.getAttribute("data-path")).then(function(response) {
console.info(response);
}).catch(function(error) {
handleError(error);
});
resetModal().then(response => {
window.api.send("startFileWatcher", this.parentElement.getAttribute("data-path"));
console.info(response);
}).catch(error => {
handleError(error);
});
// trigger matomo event
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 = function() {
let path = this.parentElement.getAttribute("data-path");
// remove file from files array
files = files.filter(function(file) {
return file[1] != path;
});
// persist new files array
setUserData("files", files);
// after array is updated, open the modal again
cell3.onclick = async function() {
await getConfirmation(removeFileFromList, translations.fileRemovalPrompt, this.parentElement.getAttribute("data-path"), files);
showFiles().then(response => {
console.info(response);
}).catch(error => {
......@@ -81,3 +149,5 @@ btnOpenTodoFile.onclick = function() {
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Menu", "Click on Files"]);
}
export { generateFileTabs };
......@@ -31,6 +31,7 @@ const viewToggleShowEmptyFilters = document.getElementById("viewToggleShowEmptyF
const compactView = document.getElementById("compactView");
const sortByContainer = document.getElementById("sortByContainer");
const viewToggleDeferredTodos = document.getElementById("viewToggleDeferredTodos");
const viewToggleFileTabs = document.getElementById("viewToggleFileTabs");
sortBy.innerHTML = translations.sortBy;
viewHeadlineAppView.innerHTML = translations.viewHeadlineAppView;
......@@ -47,6 +48,7 @@ zoomRangePicker.innerHTML = translations.zoomRangePicker;
viewToggleZoom.innerHTML = translations.viewToggleZoom;
viewToggleShowEmptyFilters.innerHTML = translations.viewToggleShowEmptyFilters;
viewToggleDeferredTodos.innerHTML = translations.deferredTodos;
viewToggleFileTabs.innerHTML = translations.fileTabs;
// build the sortBy list
userData.sortBy.forEach((sortBy) => {
......
......@@ -153,5 +153,7 @@
"deleteCategoryPrompt": "Dieser Filter wird unwiderruflich aus allen Todos entfernt.",
"modalBackgroundAttention": "Dieses Fenster wird geschlossen, dabei gehen vorgenommene Änderungen verloren.",
"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.",
"fileTabs": "Dateireiter"
}
......@@ -152,5 +152,7 @@
"deleteCategoryPrompt": "This filter will be irrevocably removed from all todos.",
"modalBackgroundAttention": "This window will be closed and its contents cleared.",
"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.",
"fileTabs": "File tabs"
}
......@@ -151,5 +151,7 @@
"deleteCategoryPrompt": "Este filtro se eliminará irrevocablemente de todas las tareas.",
"modalBackgroundAttention": "Esta ventana se cerrará y se borrará su contenido.",
"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.",
"fileTabs": "Pestañas de archivo"
}
......@@ -152,5 +152,7 @@
"deleteCategoryPrompt": "Ce filtre sera irrévocablement supprimé de toutes les tâches.",
"modalBackgroundAttention": "Cette fenêtre sera fermée et son contenu effacé.",
"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.",
"fileTabs": "Onglets de fichier"
}
......@@ -151,5 +151,7 @@
"deleteCategoryPrompt": "Questo filtro verrà irrevocabilmente rimosso da tutte le attività.",
"modalBackgroundAttention": "Questa finestra verrà chiusa e il suo contenuto cancellato.",
"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à.",
"fileTabs": "Schede di file"
}
......@@ -152,5 +152,7 @@
"deleteCategoryPrompt": "このフィルターは、すべてのタスクから取り消せないほど削除されます。.",
"modalBackgroundAttention": "このウィンドウは閉じられ、その内容はクリアされます。",
"deferredTodos": "将来のしきい値",
"noFiltersFound": "フィルタが見つかりません"
"noFiltersFound": "フィルタが見つかりません",
"fileRemovalPrompt": "これにより、ファイルリストからファイルが削除されるだけで、ドライブからは削除されません。",
"fileTabs": "ファイルタブ"
}
......@@ -152,5 +152,7 @@
"deleteCategoryPrompt": "Este filtro será removido irrevogavelmente de todas as tarefas.",
"modalBackgroundAttention": "Esta janela será fechada e seu conteúdo apagado.",
"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.",
"fileTabs": "Guias de arquivo"
}
......@@ -152,5 +152,7 @@
"deleteCategoryPrompt": "此过滤器将从所有任务中不可撤销地删除。.",
"modalBackgroundAttention": "此窗口将关闭并清除其内容。",
"deferredTodos": "未来的阈值日期",
"noFiltersFound": "未找到过滤器"
"noFiltersFound": "未找到过滤器",
"fileRemovalPrompt": "这只会从您的文件列表中删除该文件,而不会从您的驱动器中删除它。",
"fileTabs": "文件标签"
}
......@@ -285,6 +285,7 @@ const createWindow = async function() {
if(!Array.isArray(userData.data.hideFilterCategories)) userData.set("hideFilterCategories", []);
if(!Array.isArray(userData.data.sortBy)) userData.set("sortBy", ["priority", "dueString", "contexts", "projects"]);
if(typeof userData.data.deferredTodos != "boolean") userData.data.deferredTodos = true;
if(typeof userData.data.fileTabs != "boolean") userData.data.fileTabs = true;
return Promise.resolve(userData);
} catch(error) {
error.functionName = getUserData.id;
......
......@@ -65,6 +65,7 @@ let
appData,
content,
drawer,
files,
filters,
form,
matomo,
......@@ -109,6 +110,8 @@ async function getConfirmation() {
}
function configureMainView() {
try {
// generate file tabs
files.generateFileTabs();
// close filterContext if open
if(document.getElementById("filterContext").classList.contains("is-active")) document.getElementById("filterContext").classList.remove("is-active");
// set scaling factor if default font size has changed
......@@ -130,21 +133,6 @@ function configureMainView() {
}).catch(function(error) {
handleError(error);