From 3681032bde8af38779d4ba8fabd92031daf285c6 Mon Sep 17 00:00:00 2001 From: ShivamChaudhary Date: Wed, 25 Feb 2026 23:23:23 +0530 Subject: [PATCH] fix: replace native date input with flatpickr custom date picker (fixes #112) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced with a flatpickr-powered custom date picker - flatpickr is initialized in datepicker.js using the user's configured Joplin date format (e.g. YYYY-MM-DD), converted from moment.js to flatpickr tokens - Added a year dropdown so users can jump to any year quickly (±10 years) - The calendar theme is adapted to Joplin's dark/light CSS variables - Date format is injected into each DateCustomVariable via setDateFormat() called from parser.ts before the dialog is rendered --- package-lock.json | 27 +++--- package.json | 1 + src/parser.ts | 9 ++ src/variables/types/date.ts | 24 ++++- src/views/datepicker.js | 88 +++++++++++++++++++ src/views/flatpickr.min.css | 13 +++ src/views/flatpickr.min.js | 2 + src/views/templateVariables.ts | 3 + src/views/webview.css | 155 ++++++++++++++++++++++++++++++++- tests/parser.spec.ts | 6 +- 10 files changed, 309 insertions(+), 19 deletions(-) create mode 100644 src/views/datepicker.js create mode 100644 src/views/flatpickr.min.css create mode 100644 src/views/flatpickr.min.js diff --git a/package-lock.json b/package-lock.json index 744ad22..0c0fb12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "3.0.0", "license": "MIT", "dependencies": { + "flatpickr": "^4.6.13", "front-matter": "^4.0.2", "handlebars": "^4.7.7", "html-entities": "^2.3.2", @@ -5407,6 +5408,12 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==", + "license": "MIT" + }, "node_modules/flatted": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", @@ -14419,8 +14426,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "7.2.0", @@ -14486,15 +14492,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true, - "requires": {} + "dev": true }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "ansi-colors": { "version": "4.1.1", @@ -17030,6 +17034,11 @@ "rimraf": "^3.0.2" } }, + "flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==" + }, "flatted": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", @@ -18521,8 +18530,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} + "dev": true }, "jest-regex-util": { "version": "27.0.6", @@ -22683,8 +22691,7 @@ "version": "7.5.3", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", - "dev": true, - "requires": {} + "dev": true }, "xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index a135cfc..e720439 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "yargs": "^16.2.0" }, "dependencies": { + "flatpickr": "^4.6.13", "front-matter": "^4.0.2", "handlebars": "^4.7.7", "html-entities": "^2.3.2", diff --git a/src/parser.ts b/src/parser.ts index 8234bce..4de23bb 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -7,6 +7,7 @@ import { Note } from "./utils/templates"; import { notEmpty } from "./utils/typescript"; import { getVariableFromDefinition } from "./variables/parser"; import { CustomVariable } from "./variables/types/base"; +import { DateCustomVariable } from "./variables/types/date"; import { setTemplateVariablesView } from "./views/templateVariables"; import { HelperFactory } from "./helpers"; @@ -90,6 +91,14 @@ export class Parser { variableObjects[variableName] = getVariableFromDefinition(variableName, variables[variableName]); }); + // Inject the user's Joplin date format into each DateCustomVariable + // so the placeholder matches their configured format (fixes issue #112). + for (const variable of Object.values(variableObjects)) { + if (variable instanceof DateCustomVariable) { + variable.setDateFormat(this.utils.getDateFormat()); + } + } + await setTemplateVariablesView(this.dialog, title, variableObjects); const dialogResponse = (await joplin.views.dialogs.open(this.dialog)); diff --git a/src/variables/types/date.ts b/src/variables/types/date.ts index 9647faf..b6be00b 100644 --- a/src/variables/types/date.ts +++ b/src/variables/types/date.ts @@ -5,12 +5,30 @@ import { CustomVariable } from "./base"; export class DateCustomVariable extends CustomVariable { static definitionName = "date"; + // The Joplin date format (e.g. "YYYY-MM-DD"), injected by parser.ts before rendering. + private dateFormat = "YYYY-MM-DD"; + + /** + * Called by parser.ts before the dialog is rendered so the flatpickr + * date picker can use the user's configured Joplin date format. + */ + public setDateFormat(format: string): void { + this.dateFormat = format; + } + public processInput(input: string, dateAndTimeUtils: DateAndTimeUtils): string { - const inputDate = new Date(input); - return dateAndTimeUtils.formatMsToLocal(inputDate.getTime(), dateAndTimeUtils.getDateFormat()); + // Parse the typed/picked date using Joplin's date format, then + // re-format it consistently with the same format. + return dateAndTimeUtils.formatMsToLocal( + dateAndTimeUtils.formatLocalToJoplinCompatibleUnixTime(input, dateAndTimeUtils.getDateFormat()), + dateAndTimeUtils.getDateFormat() + ); } protected inputHTML(): string { - return ``; + // Use type="text" so flatpickr (loaded in the dialog webview) can + // attach its custom calendar UI. The data-datepicker-format attribute + // tells datepicker.js which Joplin format to use (fixes issue #112). + return ``; } } diff --git a/src/views/datepicker.js b/src/views/datepicker.js new file mode 100644 index 0000000..1b1ca2f --- /dev/null +++ b/src/views/datepicker.js @@ -0,0 +1,88 @@ +// datepicker.js — initializes flatpickr on all date inputs in the template dialog. +// Runs inside Joplin's dialog webview (Electron/Chromium). + +(function () { + /** + * Converts a Joplin/moment.js date format string to a flatpickr format string. + * Only covers the date formats supported by Joplin's settings. + */ + function momentToFlatpickr(momentFormat) { + return momentFormat + .replace(/YYYY/g, 'Y') // 4-digit year + .replace(/YY/g, 'y') // 2-digit year + .replace(/MM/g, 'm') // 2-digit month + .replace(/M/g, 'n') // 1-digit month (no leading zero) + .replace(/DD/g, 'd') // 2-digit day + .replace(/D/g, 'j'); // 1-digit day (no leading zero) + } + + /** + * Replaces the flatpickr year with a