diff --git a/package-lock.json b/package-lock.json
index 0890e5a..2c9443d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "3.1.0",
"license": "MIT",
"dependencies": {
+ "flatpickr": "^4.6.13",
"front-matter": "^4.0.2",
"handlebars": "^4.7.7",
"html-entities": "^2.3.2",
@@ -65,6 +66,7 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -3916,6 +3918,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.3.4",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz",
@@ -8597,6 +8605,7 @@
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.2.0",
diff --git a/package.json b/package.json
index caca106..46db854 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,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 36bd554..405a7ba 100644
--- a/src/parser.ts
+++ b/src/parser.ts
@@ -94,7 +94,7 @@ export class Parser {
variableObjects[variableName] = getVariableFromDefinition(variableName, variables[variableName]);
});
- await setTemplateVariablesView(this.dialog, title, variableObjects);
+ await setTemplateVariablesView(this.dialog, title, variableObjects, this.utils);
const dialogResponse = (await joplin.views.dialogs.open(this.dialog));
if (dialogResponse.id === "cancel") {
diff --git a/src/variables/types/base.ts b/src/variables/types/base.ts
index b88a212..b61a19e 100644
--- a/src/variables/types/base.ts
+++ b/src/variables/types/base.ts
@@ -13,11 +13,11 @@ export class CustomVariable {
this.label = label;
}
- protected inputHTML(): string {
+ protected inputHTML(dateAndTimeUtils?: DateAndTimeUtils): string {
return "";
}
- public toHTML(): string {
+ public toHTML(dateAndTimeUtils?: DateAndTimeUtils): string {
return (
`
@@ -25,7 +25,7 @@ export class CustomVariable {
${encode(this.label)}
- ${this.inputHTML()}
+ ${this.inputHTML(dateAndTimeUtils)}
`
diff --git a/src/variables/types/date.ts b/src/variables/types/date.ts
index 3dd5241..51b90f6 100644
--- a/src/variables/types/date.ts
+++ b/src/variables/types/date.ts
@@ -1,16 +1,40 @@
import { DateAndTimeUtils } from "@templates/utils/dateAndTime";
import { encode } from "html-entities";
+import * as moment from "moment";
import { CustomVariable } from "./base";
export class DateCustomVariable extends CustomVariable {
static definitionName = "date";
public processInput(input: string, dateAndTimeUtils: DateAndTimeUtils): string {
- const inputDate = new Date(input);
- return dateAndTimeUtils.formatMsToLocal(inputDate.getTime(), dateAndTimeUtils.getDateFormat());
+ if (!input || input.trim() === "") {
+ return "";
+ }
+
+ const dateFormat = dateAndTimeUtils.getDateFormat();
+
+ try {
+ return dateAndTimeUtils.formatMsToLocal(
+ dateAndTimeUtils.formatLocalToJoplinCompatibleUnixTime(input, dateFormat),
+ dateFormat
+ );
+ } catch {
+ // If the user typed an ISO date (e.g. YYYY-MM-DD), try parsing as ISO
+ const isoParsed = moment(input, moment.ISO_8601, true);
+ if (isoParsed.isValid()) {
+ return dateAndTimeUtils.formatMsToLocal(isoParsed.valueOf(), dateFormat);
+ }
+
+ // Fall back to raw input rather than throwing, preventing the template dialog from crashing
+ return input;
+ }
}
- protected inputHTML(): string {
- return ``;
+ protected inputHTML(dateAndTimeUtils?: DateAndTimeUtils): string {
+ const dateFormat = dateAndTimeUtils ? dateAndTimeUtils.getDateFormat() : "YYYY-MM-DD";
+ // 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