Commit 3a40b612 authored by ransome1's avatar ransome1
Browse files

WIP: Adding datepicker support for threshold dates

parent 8ceee98e
{
"name": "sleek",
"productName": "sleek",
"version": "1.2.0-rc.1",
"version": "1.2.0-rc.2",
"description": "todo.txt manager for Linux, Windows and MacOS, free and open-source (FOSS)",
"synopsis": "todo.txt manager for Linux, Windows and MacOS, free and open-source (FOSS)",
"category": "ProjectManagement",
......
......@@ -223,7 +223,14 @@ body #todoTable .todo .cell.hint.itemDueDate.past i {
body #todoTable .todo:focus {
background: #3a3a3a;
}
body #modalForm .field {
/*input:focus-visible,
select:focus-visible {
background-color: $darker-grey;
}*/
}
body #modalForm .field input,
body #modalForm .field #datePickerResult,
body #modalForm .field select,
body #modalForm .field input::placeholder {
color: white;
......@@ -231,9 +238,17 @@ body #modalForm .field input::placeholder {
body #modalForm .field select option {
background: #3B3B3B;
}
body #modalForm .field input:focus-visible,
body #modalForm .field select:focus-visible {
background-color: #2d2d2d;
body #modalForm .field .transparentInput {
width: 100% !important;
height: 100%;
background: transparent;
border: none;
position: absolute;
top: 0;
left: 0;
z-index: 30;
color: transparent;
cursor: pointer;
}
body #modalForm #autoCompleteContainer {
background: #2d2d2d;
......@@ -415,20 +430,6 @@ body .modal.content table td {
body .modal.content .card {
background: #f0f0f0;
}
body .modal.content .tabs ul {
border-color: transparent;
}
body .modal.content .tabs ul li a {
color: white;
border-color: #5a5a5a;
}
body .modal.content .tabs ul li.is-active a {
color: #3273dc;
border-color: #3273dc;
}
body .modal.content .tabs ul li a:focus-visible {
background: #5a5a5a;
}
body .message.fixed {
background: transparent;
}
......@@ -497,6 +498,21 @@ body .contentContainer .btnOnboarding:hover span {
background-color: #2d2d2d;
color: #f0f0f0;
}
body .tabs ul {
border-color: transparent;
}
body .tabs ul li a {
color: white;
border-color: #5a5a5a;
}
body .tabs ul li a:hover {
color: white;
border-color: #5a5a5a;
}
body .tabs ul li.is-active a {
color: #3273dc;
border-color: #3273dc;
}
}
@font-face {
font-family: "FreeSans";
......@@ -11121,20 +11137,21 @@ a.has-text-danger-dark:hover, a.has-text-danger-dark:focus {
border: none;
}
.tabs ul li a {
color: #5a5a5a;
border-color: #ccc;
border-width: 2px;
margin-bottom: 0;
border-top-right-radius: 0.65em;
border-top-left-radius: 0.65em;
}
.tabs ul li a:hover {
border-color: #5a5a5a;
}
.tabs ul li a:focus-visible {
border-color: #5a5a5a;
}
.tabs ul li.is-active a {
color: #3273dc;
border-color: #3273dc;
}
.tabs ul li a:focus-visible {
background: #ccc;
}
nav {
height: 100%;
......@@ -11834,7 +11851,8 @@ a.button.priority.C:focus-visible span.tag {
input[type=number],
.button:focus, .button.is-focused,
.select select, .textarea, .input,
.select select:focus, .textarea:focus, .input:focus, .select select.is-focused, .is-focused.textarea, .is-focused.input, .select select:active, .textarea:active, .input:active, .select select.is-active, .is-active.textarea, .is-active.input {
.select select:focus, .textarea:focus, .input:focus, .select select.is-focused, .is-focused.textarea, .is-focused.input, .select select:active, .textarea:active, .input:active, .select select.is-active, .is-active.textarea, .is-active.input,
.transparentInput {
border: none;
box-shadow: none;
outline: none;
......@@ -11883,6 +11901,19 @@ input.is-medium.input {
border-color: #3273dc;
}
.transparentInput {
width: 100% !important;
height: 100%;
background-color: transparent;
border: none;
position: absolute;
top: 0;
left: 0;
z-index: 30;
color: transparent;
cursor: pointer;
}
#modalPrompt {
z-index: 60;
}
......@@ -12044,21 +12075,30 @@ input.is-medium.input {
font-size: 1.2em;
color: #3273dc;
}
#modalForm .icon {
#modalForm i {
color: #3273dc;
}
#modalForm .field {
width: auto;
height: 2.5em;
line-height: 2.5em;
float: left;
margin: 0.5em 0.5em 0.5em 0;
/*input:focus-visible,
select:focus-visible {
background-color: $light-grey;
}*/
}
#modalForm .field input,
#modalForm .field select {
background-color: transparent;
}
#modalForm .field input:focus-visible,
#modalForm .field select:focus-visible {
background-color: #ebebeb;
#modalForm .field #datePickerResult {
color: #2d2d2d;
}
#modalForm .field .prefix {
color: #3273dc;
font-family: "FreeSansBold";
}
#modalForm .message {
display: none;
......@@ -12119,9 +12159,6 @@ input.is-medium.input {
margin: 0 0 0 2em;
z-index: 45;
}
#messages i {
color: #3273dc;
}
#messages .message {
display: none;
}
......@@ -12130,6 +12167,9 @@ input.is-medium.input {
margin-right: 0.2em;
color: #3273dc;
}
#messages .message.is-active {
display: block;
}
#messages .message.review table tr td {
text-align: center;
}
......@@ -12337,18 +12377,6 @@ input.is-medium.input {
background: #5a5a5a;
font-family: FreeSansBold;
}
#todoTable .cell.hint .transparentInput {
width: 100%;
height: 100%;
background: transparent;
border: none;
position: absolute;
top: 0;
left: 0;
z-index: 30;
color: transparent;
cursor: pointer;
}
#todoTable .cell:hover .tags,
#todoTable .cell:focus .tags {
display: block;
......@@ -12593,6 +12621,9 @@ input.is-medium.input {
-ms-user-select: none;
user-select: none;
}
.datepicker-picker .tabs {
margin: 0;
}
.datepicker-main {
padding: 2px;
......@@ -12793,11 +12824,11 @@ input.is-medium.input {
}
.datepicker-input.in-edit {
border-color: #2366d1;
border-color: transparent;
}
.datepicker-input.in-edit:active, .datepicker-input.in-edit:focus {
box-shadow: 0 0 0.25em 0.25em rgba(35, 102, 209, 0.2);
box-shadow: none;
}
@media print {
......
This diff is collapsed.
......@@ -291,12 +291,11 @@
</div>
</div>
</div>
<div class="field dueDate">
<div class="field">
<div class="control has-icons-left">
<input id="datePickerInput" class="input" tabindex="0" readonly>
<a href="#" class="icon is-left" tabindex="-1">
<i class="fa-solid fa-clock is-left"></i>
</a>
<input id="datePickerInput" class="transparentInput" tabindex="0" readonly>
<i class="fa-solid fa-clock is-left"></i>&nbsp;
<span id="datePickerResult"></span>
</div>
</div>
<div id="recurrencePicker" class="field">
......
......@@ -6,7 +6,7 @@
"use strict";
import { userData } from "../render.js";
import { userData, translations } from "../render.js";
import { _paq } from "./matomo.mjs";
import { resizeInput } from "./form.mjs";
import { convertDate } from "./date.mjs";
......@@ -18,22 +18,46 @@ import es from "../../node_modules/vanillajs-datepicker/js/i18n/locales/es.js";
import fr from "../../node_modules/vanillajs-datepicker/js/i18n/locales/fr.js";
Object.assign(Datepicker.locales, de, it, es, fr);
const datePickerInput = document.getElementById("datePickerInput");
const datePickerResult = document.getElementById("datePickerResult");
const dateTypes = ["due", "t"];
datePickerResult.innerHTML = translations.dueDate;
let
extension,
modalFormInput,
datePicker,
datePickerOptions = {
autohide: true,
language: userData.language,
format: "yyyy-mm-dd",
clearBtn: true,
container: "body",
calendarWeeks: true,
weekStart: 1,
beforeShowDay: function(date) {
const today = new Date();
if(date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear()) return { classes: "today" };
}
};
autohide: true,
language: userData.language,
format: "yyyy-mm-dd",
clearBtn: true,
container: "body",
calendarWeeks: true,
weekStart: 1,
beforeShowDay: function(date) {
const today = new Date();
if(date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear()) return { classes: "today" };
}
};
function fillDatePickerInput(todo) {
modalFormInput = document.getElementById("modalFormInput");
datePickerResult.innerHTML = "";
dateTypes.forEach(function(type) {
if(!todo[type]) return false;
datePickerResult.innerHTML += `<span class="prefix">${type}:</span>${todo[type + "String"]}&nbsp;`;
});
if(datePickerResult.innerHTML === "") datePickerResult.innerHTML = translations.formSelectDueDate;
// if a due date is set, the recurrence picker will be shown);
modalFormInput.value = todo.toString();
// put focus on input field
modalFormInput.focus();
}
async function createDatepickerInstance(attachToElement, addDateToElement, extension, todo ) {
......@@ -53,20 +77,92 @@ async function createDatepickerInstance(attachToElement, addDateToElement, exten
// ******************************************************
if(!todo) {
modalFormInput = document.getElementById("modalFormInput");
// generate the object of input value, so we don't overwrite previous inputs of user
todo = await generateTodoTxtObject(modalFormInput.value).then(response => {
todo = await generateTodoTxtObject(document.getElementById("modalFormInput").value).then(response => {
return response;
}).catch(error => {
handleError(error);
});
}
if(todo[extension]) datePicker.setDate(todo[extension]);
if(todo[extension]) datePicker.setDate(todo[extension])
datePicker.show();
attachToElement.addEventListener("changeDate", function() {
// close datepicker when container loses focus
attachToElement.onblur = function() { datePicker.destroy(); }
// destroy datepicker on Escape
attachToElement.onkeyup = function(event) { if(event.key === "Escape") datePicker.destroy(); }
// insert due and threshold buttons
const header = document.createRange().createContextualFragment(`
<div class="dateSwitcher tabs is-centered">
<ul>
<li class="is-active">
<a href="#">Due</a>
</li>
<li>
<a href="#">Threshold</a>
</li>
</ul>
</div>`);
const datepickerContainer = document.querySelector(".datepicker .datepicker-picker");
const firstChild = document.querySelector(".datepicker .datepicker-picker .datepicker-header");
const clearButton = datepickerContainer.querySelector(".clear-btn");
datepickerContainer.insertBefore(header, firstChild);
const tabs = datepickerContainer.querySelectorAll("li");
const dueButton = tabs[0];
const thresholdButton = tabs[1];
clearButton.onclick = function(event) {
event.stopPropagation();
delete todo[extension];
delete todo[extension + "String"];
if(!addDateToElement) {
// get position of current todo in array
const index = items.objects.map(function(item) { return item; }).indexOf(todo);
// finally pass new todo on for changing
editTodo(index, todo).then(response => {
console.log(response)
}).catch(error => {
console.log(error);
});
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Todo-Table", "Datepicker used to change a date"]);
} else {
fillDatePickerInput(todo);
// resize the due date input field after date was added
resizeInput(datePickerInput);
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Form", "Datepicker used to add date to input"]);
}
}
dueButton.addEventListener("click", function() {
(todo.due) ? datePicker.setDate(todo.due) : datePicker.setDate({ clear: true })
this.classList.add("is-active");
tabs[1].classList.remove("is-active");
extension = "due";
});
thresholdButton.addEventListener("click", function() {
(todo.t) ? datePicker.setDate(todo.t) : datePicker.setDate({ clear: true })
this.classList.add("is-active");
tabs[0].classList.remove("is-active");
extension = "t";
});
datePicker.picker.main.addEventListener("click", function(event) {
// ******************************************************
// add due date to todo object or remove a given one if no date is passed
......@@ -78,10 +174,6 @@ async function createDatepickerInstance(attachToElement, addDateToElement, exten
if(date) {
todo[extension] = date;
todo[extension + "String"] = convertDate(date);
// reset due date in object
} else {
todo[extension] = undefined;
todo[extension + "String"] = undefined;
}
// ******************************************************
......@@ -109,14 +201,10 @@ async function createDatepickerInstance(attachToElement, addDateToElement, exten
} else {
// if a due date is set, the recurrence picker will be shown);
modalFormInput.value = todo.toString();
// put focus on input field
modalFormInput.focus();
fillDatePickerInput(todo);
// resize the due date input field after date was added
resizeInput(document.getElementById("datePickerInput"));
resizeInput(datePickerInput);
// trigger matomo event
if(userData.matomoEvents) _paq.push(["trackEvent", "Form", "Datepicker used to add date to input"]);
......@@ -124,12 +212,6 @@ async function createDatepickerInstance(attachToElement, addDateToElement, exten
});
// close datepicker when container loses focus
attachToElement.onblur = function() { datePicker.destroy(); }
// destroy datepicker on Escape
attachToElement.onkeyup = function(event) { if(event.key === "Escape") datePicker.destroy(); }
return Promise.resolve("Success: " + extension + " datepicker intance created");
} catch(error) {
......@@ -139,4 +221,4 @@ async function createDatepickerInstance(attachToElement, addDateToElement, exten
}
export { createDatepickerInstance };
export { createDatepickerInstance, fillDatePickerInput };
......@@ -6,6 +6,7 @@ import { handleError } from "./helper.mjs";
import { items, item, setTodoComplete, generateTodoTxtObject } from "./todos.mjs";
import { getCaretPosition } from "./helper.mjs";
import { showRecurrences, setInput } from "./recurrencePicker.mjs";
import { fillDatePickerInput } from "./datePicker.mjs";
import { userData, setUserData, translations } from "../render.js";
const autoCompleteContainer = document.getElementById("autoCompleteContainer");
......@@ -21,7 +22,8 @@ const todoContext = document.getElementById("todoContext");
btnSave.innerHTML = translations.save;
btnCancel.innerHTML = translations.cancel;
datePickerInput.placeholder = translations.formSelectDueDate;
//datePickerInput.placeholder = translations.formSelectDueDate;
datePickerResult.innerHTML = translations.formSelectDueDate;
btnItemStatus.onclick = async function() {
try {
......@@ -403,7 +405,7 @@ function resetForm() {
priorityPicker.querySelectorAll("option")[0].selected = "selected";
// empty datepicker input element
datePickerInput.value = null;
//datePickerInput.value = null;
// if recurrence picker was open it is now being closed
recurrencePickerContainer.classList.remove("is-active");
......@@ -434,6 +436,8 @@ async function show(todo, templated) {
// hide "complete" button
btnItemStatus.classList.add("is-hidden");
datePickerResult.innerHTML = translations.formSelectDueDate;
//
if(todo) {
// we need to check if there already is a due date in the object
......@@ -479,8 +483,9 @@ async function show(todo, templated) {
// if there is a recurrence
if(todo.rec) setInput(todo.rec)
// if so we paste it into the input field
if(todo.dueString) datePickerInput.value = todo.dueString;
if(todo.due || todo.t) fillDatePickerInput(todo)
}
// resize all necessary input elements
......
......@@ -394,25 +394,31 @@ function generateTableRow(todo) {
}
// check for and add a given due date
if(todo.due) {
let dateFieldContent = convertDate(todo.due);
if(todo.due || todo.t) {
if(isToday(todo.due)) {
todoTableBodyCellDueDate.innerHTML = `<i class="fa-solid fa-clock"></i>`;
let dateFieldContent;
if(todo.due) {
dateFieldContent = convertDate(todo.due);
todoTableBodyCellDueDate.innerHTML += `
<div class="tags has-addons">
<span class="tag">due:</span><span class="tag is-dark">${dateFieldContent}</span>
</div>
<i class="fas fa-sort-down"></i>`;
}
if(todo.due && isToday(todo.due)) {
todoTableBodyCellDueDate.classList.add("today");
dateFieldContent = translations.today;
} else if(isTomorrow(todo.due)) {
} else if(todo.due && isTomorrow(todo.due)) {
todoTableBodyCellDueDate.classList.add("tomorrow");
dateFieldContent = translations.tomorrow;
} else if(isPast(todo.due)) {
} else if(todo.due && isPast(todo.due)) {
todoTableBodyCellDueDate.classList.add("past");
}
todoTableBodyCellDueDate.innerHTML = `
<i class="fa-solid fa-clock"></i>
<div class="tags has-addons">
<span class="tag">due:</span><span class="tag is-dark">${dateFieldContent}</span>
</div>
<i class="fas fa-sort-down"></i>`;
// ugly workaround so datepicker does not fallback to inline mode
const todoTableBodyCellDueDateHiddenInput = document.createElement("input");
......@@ -440,10 +446,10 @@ function generateTableRow(todo) {
}
// add threshold date icon
if(todo.t) {
todoTableBodyCellTDate.innerHTML = `<i class="fa-regular fa-clock"></i>`
todoTableBodyRow.appendChild(todoTableBodyCellTDate);
}
// if(todo.t) {
// todoTableBodyCellTDate.innerHTML = `<i class="fa-regular fa-clock"></i>`
// todoTableBodyRow.appendChild(todoTableBodyCellTDate);
// }
// TODO: Add recurrence picker function
// add recurrence icon
......
......@@ -109,4 +109,4 @@ function resolveRelativeDate(relativeDate) {
return addIntervalToDate(today, increment, unit);
}
export { RecExtension, SugarDueExtension, ThresholdExtension, resolveRelativeDate, PriExtension };
\ No newline at end of file
export { RecExtension, SugarDueExtension, ThresholdExtension, PriExtension };
\ No newline at end of file
......@@ -254,6 +254,7 @@
#modalForm {
.field {
input,
#datePickerResult,
select,
input::placeholder {
color: white;
......@@ -261,9 +262,21 @@
select option {
background: $even-darker-grey;
}
input:focus-visible,
/*input:focus-visible,
select:focus-visible {
background-color: $darker-grey;
}*/
.transparentInput {
width: 100%!important;
height: 100%;
background: transparent;
border: none;
position: absolute;
top: 0;
left: 0;
z-index: 30;
color: transparent;
cursor: pointer;
}
}
#autoCompleteContainer {
......@@ -456,22 +469,6 @@
.card {
background: $almost-white;
}
.tabs {
ul {
border-color: transparent;
}
ul li a {
color: white;
border-color: $dark-grey;
}