From e1c464db15d9b0af1f6f0cafde690b251bbc7b8f Mon Sep 17 00:00:00 2001 From: Christen Date: Wed, 29 Jul 2026 18:00:28 +0200 Subject: [PATCH] improve custom recurrence dialog UI, i18n, and native date/time styling --- docs/releases/unreleased.md | 8 + i18n.manifest.json | 5 + i18n.state.json | 160 ++++++ src/components/RecurrenceContextMenu.ts | 652 +++++++++++++----------- src/i18n/resources/de.ts | 5 + src/i18n/resources/en.ts | 5 + src/i18n/resources/es.ts | 5 + src/i18n/resources/fr.ts | 5 + src/i18n/resources/ja.ts | 5 + src/i18n/resources/ko.ts | 5 + src/i18n/resources/pt.ts | 5 + src/i18n/resources/ru.ts | 5 + src/i18n/resources/zh.ts | 5 + src/modals/DateTimePickerModal.ts | 4 +- src/modals/ReminderModal.ts | 2 + src/modals/TimeblockCreationModal.ts | 2 + styles/modal-bem.css | 207 +++++++- 17 files changed, 767 insertions(+), 318 deletions(-) diff --git a/docs/releases/unreleased.md b/docs/releases/unreleased.md index f4584701d..a80fa2555 100644 --- a/docs/releases/unreleased.md +++ b/docs/releases/unreleased.md @@ -44,3 +44,11 @@ When a change has user-facing documentation, include a canonical tasknotes.dev l guides for adopting TaskNotes in an existing vault, mobile use, custom Bases, and backup and recovery. See the [TaskNotes documentation](https://tasknotes.dev/). +- Custom recurrence dialog Interval and Days of week descriptions now update based on the selected frequency (for example, "Every x days" when Daily is selected). +- Custom recurrence dialog weekday checkboxes stay on one row with clearer spacing below the description, and radio option markers are centered when selected. +- Native date and time fields across recurrence, date/time picker, timeblock, and reminder modals share consistent padding, border, and corner rounding. + +## Fixed + +- Fixed the Custom recurrence dialog's Monthly, Yearly, and End condition options so switching between "on this date" and "on the Nth weekday" (or between "never"/"after N occurrences"/"until a date") greys out the fields that no longer apply, instead of leaving both sets of controls active and editable at once. +- Fixed the Custom recurrence dialog not being translated, despite translations already being available for it. diff --git a/i18n.manifest.json b/i18n.manifest.json index a30b9bbec..c3a555310 100644 --- a/i18n.manifest.json +++ b/i18n.manifest.json @@ -2175,8 +2175,13 @@ "components.recurrenceContextMenu.customRecurrenceModal.frequency": "89836a870e44fb467d13116254b39e2482f5cdf1", "components.recurrenceContextMenu.customRecurrenceModal.interval": "011efcd591019b5969a332a24dff728a8107dbe2", "components.recurrenceContextMenu.customRecurrenceModal.intervalDesc": "5e0d9119317427c6517942235c7ee7c72c3f1eba", + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDesc": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": "b6edf522d10c13fd3723d455ebee16141a70ed68", "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrenceDesc": "4c49c84e721518ec3861af880e70c526308bcf98", "components.recurrenceContextMenu.customRecurrenceModal.yearlyRecurrence": "abfca4467453d216a82a9a556893f4ad1677b935", diff --git a/i18n.state.json b/i18n.state.json index 01d90a504..25116914c 100644 --- a/i18n.state.json +++ b/i18n.state.json @@ -8704,6 +8704,22 @@ "source": "5e0d9119317427c6517942235c7ee7c72c3f1eba", "translation": "d45992fe7e4b03553a46221afe4558b4f3690e36" }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": { + "source": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "translation": "c3d515af1d633fe683e20618d0e3de3420829dc3" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": { + "source": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "translation": "cc25dcbd4f8ca8c9f7dc3d9644e92cff560d08df" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": { + "source": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "translation": "a9ad003eae6ce2c4b051b5b702e12724c9881f4c" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": { + "source": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", + "translation": "090ee18bc0de4e0f6bf14e3e78246c73d40a0928" + }, "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": { "source": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "translation": "1d474635b5d26d18871e4f555a4d8634855eb08a" @@ -8712,6 +8728,10 @@ "source": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", "translation": "003b98a14b62214b48b25d3e5757c7e8984b946b" }, + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": { + "source": "b6edf522d10c13fd3723d455ebee16141a70ed68", + "translation": "04a621198f0ca39052fc214130af6fe12b6b3e6d" + }, "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": { "source": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "translation": "6892ee18121a784f543035c219f74eb1a432b883" @@ -17570,6 +17590,22 @@ "source": "5e0d9119317427c6517942235c7ee7c72c3f1eba", "translation": "b324e86167e22e49f7d26014422c07c24e4dd7cd" }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": { + "source": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "translation": "843f410b90d8490068b3b18305b89eb7eee87b48" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": { + "source": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "translation": "c8de42daea359ccde55b95527712d66b382eddea" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": { + "source": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "translation": "098f02de83f54ea69d4e634630fc336df6841128" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": { + "source": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", + "translation": "3380675e6d1f84934396df46338b1b5f4ad38037" + }, "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": { "source": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "translation": "e65fcf42b9a3b24a99b6b67999aaf7603db83692" @@ -17578,6 +17614,10 @@ "source": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", "translation": "ab2808699f882a069aaf0cbfe083267840da74d3" }, + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": { + "source": "b6edf522d10c13fd3723d455ebee16141a70ed68", + "translation": "60529e6a37b5e1f18b79e8d3f16bb809e53dec29" + }, "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": { "source": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "translation": "99f4fd170f22be947baeb2a8eb188e05ef02f01c" @@ -26436,6 +26476,22 @@ "source": "5e0d9119317427c6517942235c7ee7c72c3f1eba", "translation": "1292eb0c8dceb4123a55700ed789ec1105dde4d5" }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": { + "source": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "translation": "a3ac31d20d9eaa5cd76bd0d848246e68e45acfc4" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": { + "source": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "translation": "99f1aa3847319036bab6ff1959931f7aae7d7ed5" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": { + "source": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "translation": "38876d26d7759820aeb72fb03ddef230a6571436" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": { + "source": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", + "translation": "df80775997ab731d4000ed5bd80f76cfcae58e3c" + }, "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": { "source": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "translation": "8cf350a0e2caf535ce868898a8943f9755ebab10" @@ -26444,6 +26500,10 @@ "source": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", "translation": "a8099711803b56edc81fda3fce33fa7cc2f76e53" }, + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": { + "source": "b6edf522d10c13fd3723d455ebee16141a70ed68", + "translation": "e0e63f921cf98f2700cd532496bcbb235e24e887" + }, "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": { "source": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "translation": "cc5a4e7850e54e4953522d213fc48d6cbb3b90c8" @@ -35302,6 +35362,22 @@ "source": "5e0d9119317427c6517942235c7ee7c72c3f1eba", "translation": "f8e1d3ebb3803e2c1e6cb007fbca9826f5ab0842" }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": { + "source": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "translation": "9ff5a5d0be000d16c6184cb9639223f0287cd18a" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": { + "source": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "translation": "2878740708f635d155610290632a1a9dbfc637d2" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": { + "source": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "translation": "96df23a6cc46acf4c28492b992055e93ccf35657" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": { + "source": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", + "translation": "c48ed1fb5f54e1c07ce4053baca0ba04e08f29c4" + }, "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": { "source": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "translation": "234df0a0cf39f296d2b6cf2944354a6380e90e80" @@ -35310,6 +35386,10 @@ "source": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", "translation": "f24641e4e57c9d5505d420ade3a74dff5b66c215" }, + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": { + "source": "b6edf522d10c13fd3723d455ebee16141a70ed68", + "translation": "b0a95ec35cc5d0a157cdf1910b1b4d6182f25f82" + }, "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": { "source": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "translation": "b8bbbbf564ce9e152b6bc7ae83ff86f31f5d2c4f" @@ -44168,6 +44248,22 @@ "source": "5e0d9119317427c6517942235c7ee7c72c3f1eba", "translation": "77a102dc23b4e112c190328f9a616b65d9e045a4" }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": { + "source": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "translation": "28317054b0464c43e8123c2bd9dc1fe7e5b3b0a0" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": { + "source": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "translation": "3092f778e898a8fd5cf4e6675ccc221bb49fe195" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": { + "source": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "translation": "296f6d311b85421bc279e5b9a3a7ffcee823042a" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": { + "source": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", + "translation": "b9e12b2af18059e0dd79d51fbdaecb6a3c736aa4" + }, "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": { "source": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "translation": "e7215fb35261da63918112f9017c9c0305f65694" @@ -44176,6 +44272,10 @@ "source": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", "translation": "b29adffec57cd5506483d26aee4d9aac9530cd8f" }, + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": { + "source": "b6edf522d10c13fd3723d455ebee16141a70ed68", + "translation": "16132e5e1ada3b2f0ee8b9f02c19e831e2c8d422" + }, "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": { "source": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "translation": "61dfaade24252fe0c6cbb09d78617306f1d40bdd" @@ -53034,6 +53134,22 @@ "source": "5e0d9119317427c6517942235c7ee7c72c3f1eba", "translation": "962411a1c0e633fb1ad58fa2cf31f10ac1bfd754" }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": { + "source": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "translation": "2520d43ff40983d2696a4ccec1a30f358c811cf7" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": { + "source": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "translation": "b17e3681182e8742476ecaa14f31d939829677cb" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": { + "source": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "translation": "3731e4458b9e091e8ee8eeeee45575de19fdb1de" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": { + "source": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", + "translation": "5e3951e90e521117d17b8059c3904d438f5e44cf" + }, "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": { "source": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "translation": "cec95235f4dee9444e15056b440e4a18ac394f4a" @@ -53042,6 +53158,10 @@ "source": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", "translation": "24016c4914db9f6f5d80511538ca8902e6d30054" }, + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": { + "source": "b6edf522d10c13fd3723d455ebee16141a70ed68", + "translation": "677cbea33b6321c8961b76f282005bcee6c49578" + }, "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": { "source": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "translation": "bfeb544b73d3b18c572c6addf6f65aaff4d024b4" @@ -61900,6 +62020,22 @@ "source": "5e0d9119317427c6517942235c7ee7c72c3f1eba", "translation": "79cd1a9542a4a265933552f506dc12fef41a6bc7" }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": { + "source": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "translation": "7e7f976bdca45d108aae2bd1e3c59d97dbc7ed47" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": { + "source": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "translation": "301a3611a61c55aabf99d31a082c437e3d664fea" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": { + "source": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "translation": "956e658af99cf34e07e288f0b493ad57d164e61b" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": { + "source": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", + "translation": "8a7d812133c4324ab9d72c57b49b11c822476554" + }, "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": { "source": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "translation": "5b471418f723305e477e2366b05932e22325b297" @@ -61908,6 +62044,10 @@ "source": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", "translation": "0e0ce5484ff11a3058e2b3d8a5b8dc6f1042c69d" }, + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": { + "source": "b6edf522d10c13fd3723d455ebee16141a70ed68", + "translation": "71dd74361470b441b3ec0b8daad98a3374c28409" + }, "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": { "source": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "translation": "8ef10984235523669bd111e0d1c2db8e43eb7f47" @@ -70766,6 +70906,22 @@ "source": "5e0d9119317427c6517942235c7ee7c72c3f1eba", "translation": "db774e0760bf80d7718cb376f15d3e4d5a099e7c" }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily": { + "source": "e236ef92e5f3f2d38f99c1aa182f757fbf097bdb", + "translation": "482e6c2514ef82879b26e4f524f6c7973b30054e" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly": { + "source": "cf6745ebffd319ac91db22b896353d0dd8d72dd2", + "translation": "9b75d17b29dcd40daaaf528a95b1f97acc45ce5f" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly": { + "source": "80c69ef898a0d3731a9f09fe4f4cc07cbc332ce5", + "translation": "3bc98c36d67c2e9b02391033e715ca163a687cc9" + }, + "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly": { + "source": "dca87051697daf49d7f7da5b2d97a6f8b37e70de", + "translation": "3dff2bb8bd58d097f88e9c1ea05fa526c7532e9d" + }, "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek": { "source": "318d817389a2ddc335aabaaea49dd75a998bc7b5", "translation": "417ed486f9a6c1909e97ff5285e6deea2c0d73ac" @@ -70774,6 +70930,10 @@ "source": "a6eebf484377fe1bbc03824b639a3b4f7d8b3225", "translation": "e92f42241a2e1a3778ac58de2434c88c346b68ee" }, + "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily": { + "source": "b6edf522d10c13fd3723d455ebee16141a70ed68", + "translation": "8d37386127b3c6d7598e58f7e90ed919b24ce51c" + }, "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence": { "source": "e0c2c6f69efcf30626e7a5b71ba3b504ee5fe8e6", "translation": "4ea55aa42d1f6c301b2dcc9ccf075333c3970c44" diff --git a/src/components/RecurrenceContextMenu.ts b/src/components/RecurrenceContextMenu.ts index 4b1dccce8..414f48ee6 100644 --- a/src/components/RecurrenceContextMenu.ts +++ b/src/components/RecurrenceContextMenu.ts @@ -722,21 +722,67 @@ class CustomRecurrenceModal extends Modal { onOpen() { const { contentEl } = this; contentEl.empty(); + contentEl.addClass("tasknotes-plugin"); + contentEl.addClass("tn-custom-recurrence-modal"); + this.modalEl.addClass("tn-custom-recurrence-modal-host"); const calendarLocale = getPluginCalendarLocale(this.plugin); const firstDay = this.plugin.settings?.calendarViewSettings?.firstDay; const orderedWeekdays = getOrderedRecurrenceWeekdays(firstDay, calendarLocale); const recurrenceStartDate = getRecurrenceStartDate(this.scheduledDate); + const getIntervalDescKey = (): TranslationKey => { + switch (this.frequency) { + case "WEEKLY": + return "components.recurrenceContextMenu.customRecurrenceModal.intervalDescWeekly"; + case "MONTHLY": + return "components.recurrenceContextMenu.customRecurrenceModal.intervalDescMonthly"; + case "YEARLY": + return "components.recurrenceContextMenu.customRecurrenceModal.intervalDescYearly"; + case "DAILY": + default: + return "components.recurrenceContextMenu.customRecurrenceModal.intervalDescDaily"; + } + }; + + const getDaysOfWeekDescKey = (): TranslationKey => { + return this.frequency === "DAILY" + ? "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDescDaily" + : "components.recurrenceContextMenu.customRecurrenceModal.daysOfWeekDesc"; + }; + + // Chromium doesn't render ::before/::after generated content on + // elements, so the "selected" indicator has to be a real sibling element + // inside its own positioning wrapper, rather than a pseudo-element on the + // input itself. + const createRadioInput = ( + radioLabel: HTMLElement, + name: string, + value: string, + checked: boolean + ): HTMLInputElement => { + const visual = radioLabel.createSpan({ + cls: "tn-custom-recurrence-modal__radio-visual", + }); + const input = visual.createEl("input", { type: "radio", value }); + input.name = name; + input.checked = checked; + visual.createSpan({ cls: "tn-custom-recurrence-modal__radio-dot" }); + return input; + }; + contentEl.createEl("h2", { text: this.translate("components.recurrenceContextMenu.customRecurrenceModal.title"), }); // Start date selection new Setting(contentEl) - .setName("Start date") - .setDesc("The date when the recurrence pattern begins") + .setName(this.translate("components.recurrenceContextMenu.customRecurrenceModal.startDate")) + .setDesc( + this.translate("components.recurrenceContextMenu.customRecurrenceModal.startDateDesc") + ) .addText((text) => { text.inputEl.type = "date"; + text.inputEl.addClass("tn-datetime-input"); text.setValue(this.dtstart).onChange((value) => { this.dtstart = value; }); @@ -749,10 +795,13 @@ class CustomRecurrenceModal extends Modal { // Start time selection new Setting(contentEl) - .setName("Start time") - .setDesc("The time when recurring instances should appear (optional)") + .setName(this.translate("components.recurrenceContextMenu.customRecurrenceModal.startTime")) + .setDesc( + this.translate("components.recurrenceContextMenu.customRecurrenceModal.startTimeDesc") + ) .addText((text) => { text.inputEl.type = "time"; + text.inputEl.addClass("tn-datetime-input"); text.setValue(this.dtstartTime).onChange((value) => { this.dtstartTime = value; }); @@ -760,12 +809,24 @@ class CustomRecurrenceModal extends Modal { // Recurrence anchor selection new Setting(contentEl) - .setName("Recur from") - .setDesc("When should the next occurrence be calculated from?") + .setName(this.translate("components.recurrenceContextMenu.customRecurrenceModal.recurFrom")) + .setDesc( + this.translate("components.recurrenceContextMenu.customRecurrenceModal.recurFromDesc") + ) .addDropdown((dropdown) => { dropdown - .addOption("scheduled", "Scheduled date (fixed schedule)") - .addOption("completion", "Completion date (flexible schedule)") + .addOption( + "scheduled", + this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.scheduledDate" + ) + ) + .addOption( + "completion", + this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.completionDate" + ) + ) .setValue(this.recurrenceAnchor) .onChange((value) => { this.recurrenceAnchor = value as "scheduled" | "completion"; @@ -774,35 +835,58 @@ class CustomRecurrenceModal extends Modal { }); // Frequency selection - new Setting(contentEl).setName("Frequency").addDropdown((dropdown) => { - dropdown - .addOption("DAILY", "Daily") - .addOption("WEEKLY", "Weekly") - .addOption("MONTHLY", "Monthly") - .addOption("YEARLY", "Yearly") - .setValue(this.frequency) - .onChange((value) => { - this.frequency = value; - this.updateFrequencySpecificVisibility(); - }); - }); + new Setting(contentEl) + .setName(this.translate("components.recurrenceContextMenu.customRecurrenceModal.frequency")) + .addDropdown((dropdown) => { + dropdown + .addOption( + "DAILY", + this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.frequencies.daily" + ) + ) + .addOption( + "WEEKLY", + this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.frequencies.weekly" + ) + ) + .addOption( + "MONTHLY", + this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.frequencies.monthly" + ) + ) + .addOption( + "YEARLY", + this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.frequencies.yearly" + ) + ) + .setValue(this.frequency) + .onChange((value) => { + this.frequency = value; + this.updateFrequencySpecificVisibility(); + }); + }); // Interval selection - new Setting(contentEl) - .setName("Interval") - .setDesc("Every X days/weeks/months/years") + const intervalSetting = new Setting(contentEl) + .setName(this.translate("components.recurrenceContextMenu.customRecurrenceModal.interval")) + .setDesc(this.translate(getIntervalDescKey())) .addText((text) => { text.setValue(this.interval.toString()).onChange((value) => { this.interval = parseInt(value) || 1; }); }); - // Days of week (for weekly frequency) + // Days of week (for daily or weekly frequency) const byDaySetting = new Setting(contentEl) - .setName("Days of week") - .setDesc("Select specific days (for daily or weekly recurrence)"); + .setName(this.translate("components.recurrenceContextMenu.customRecurrenceModal.daysOfWeek")) + .setDesc(this.translate(getDaysOfWeekDescKey())); + byDaySetting.settingEl.addClass("tn-custom-recurrence-modal__days-setting"); - const daysContainer = byDaySetting.controlEl.createDiv("days-container"); + const daysContainer = byDaySetting.controlEl.createDiv("tn-custom-recurrence-modal__days"); const days = orderedWeekdays.map((day) => ({ key: day.code, label: this.translate( @@ -811,20 +895,9 @@ class CustomRecurrenceModal extends Modal { })); days.forEach((day) => { - const dayEl = daysContainer.createEl("label", { cls: "day-checkbox" }); - dayEl.classList.remove( - "tn-static-display-block-2a1b75c9", - "tn-static-display-flex-4d51fc62", - "tn-static-display-flex-75816cae", - "tn-static-display-flex-8bb39979", - "tn-static-display-inline-cccfa456", - "tn-static-display-inline-flex-f984c520", - "tn-static-display-none-6b99de8b", - "tn-static-min-height-800px-997b4c8c" - ); - dayEl.classList.add("tn-static-display-inline-block-60e32dcb"); - dayEl.classList.remove("tn-static-margin-right-4px-c6b76b85"); - dayEl.classList.add("tn-static-margin-right-8px-539fa9a0"); + const dayEl = daysContainer.createEl("label", { + cls: "tn-custom-recurrence-modal__day", + }); const checkbox = dayEl.createEl("input", { type: "checkbox" }); checkbox.checked = this.byDay.includes(day.key); @@ -838,49 +911,38 @@ class CustomRecurrenceModal extends Modal { } }); - dayEl.createSpan({ text: ` ${day.label}` }); + dayEl.createSpan({ text: day.label }); }); // Monthly options const monthlyTypeSetting = new Setting(contentEl) - .setName("Monthly recurrence") - .setDesc("Choose how to repeat monthly"); - - const monthlyTypeContainer = monthlyTypeSetting.controlEl.createDiv("monthly-options"); + .setName( + this.translate("components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrence") + ) + .setDesc( + this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.monthlyRecurrenceDesc" + ) + ); - const monthlyByDateRadio = monthlyTypeContainer.createEl("label", { cls: "radio-option" }); - monthlyByDateRadio.classList.remove( - "tn-static-display-flex-4d51fc62", - "tn-static-display-flex-75816cae", - "tn-static-display-flex-8bb39979", - "tn-static-display-inline-block-60e32dcb", - "tn-static-display-inline-cccfa456", - "tn-static-display-inline-flex-f984c520", - "tn-static-display-none-6b99de8b", - "tn-static-min-height-800px-997b4c8c" + const monthlyTypeContainer = monthlyTypeSetting.controlEl.createDiv( + "tn-custom-recurrence-modal__options" ); - monthlyByDateRadio.classList.add("tn-static-display-block-2a1b75c9"); - monthlyByDateRadio.classList.remove( - "tn-static-font-size-12px-65574819", - "tn-static-font-weight-bold-0fe8c30d", - "tn-static-font-weight-bold-e0b452bd", - "tn-static-margin-bottom-0-75rem-c05a3c6e", - "tn-static-margin-bottom-20px-49f14f8f" - ); - monthlyByDateRadio.classList.add("tn-static-margin-bottom-8px-fdf33f23"); - const monthlyByDateInput = monthlyByDateRadio.createEl("input", { - type: "radio", - value: "bydate", + + const monthlyByDateRadio = monthlyTypeContainer.createEl("label", { + cls: "tn-custom-recurrence-modal__radio-option", }); - monthlyByDateInput.name = "monthly-type"; - monthlyByDateInput.checked = - this.byMonthDay.length > 0 || (this.byDay.length === 0 && this.bySetPos === undefined); + const monthlyByDateInput = createRadioInput( + monthlyByDateRadio, + "monthly-type", + "bydate", + this.byMonthDay.length > 0 || (this.byDay.length === 0 && this.bySetPos === undefined) + ); monthlyByDateRadio.createSpan({ text: " On " }); - const monthlyDateSelect = monthlyByDateRadio.createEl("select"); - monthlyDateSelect.classList.add("tn-static-margin-left-4px-46cec891"); - monthlyDateSelect.classList.remove("tn-static-margin-right-8px-539fa9a0"); - monthlyDateSelect.classList.add("tn-static-margin-right-4px-c6b76b85"); + const monthlyDateSelect = monthlyByDateRadio.createEl("select", { + cls: "tn-custom-recurrence-modal__select", + }); for (const dayOption of getMonthDayOptions()) { const option = monthlyDateSelect.createEl("option", { value: dayOption.value, @@ -895,44 +957,51 @@ class CustomRecurrenceModal extends Modal { } monthlyByDateRadio.createSpan({ text: " of each month" }); - const monthlyByDayRadio = monthlyTypeContainer.createEl("label", { cls: "radio-option" }); - monthlyByDayRadio.classList.remove( - "tn-static-display-flex-4d51fc62", - "tn-static-display-flex-75816cae", - "tn-static-display-flex-8bb39979", - "tn-static-display-inline-block-60e32dcb", - "tn-static-display-inline-cccfa456", - "tn-static-display-inline-flex-f984c520", - "tn-static-display-none-6b99de8b", - "tn-static-min-height-800px-997b4c8c" - ); - monthlyByDayRadio.classList.add("tn-static-display-block-2a1b75c9"); - monthlyByDayRadio.classList.remove( - "tn-static-font-size-12px-65574819", - "tn-static-font-weight-bold-0fe8c30d", - "tn-static-font-weight-bold-e0b452bd", - "tn-static-margin-bottom-0-75rem-c05a3c6e", - "tn-static-margin-bottom-20px-49f14f8f" - ); - monthlyByDayRadio.classList.add("tn-static-margin-bottom-8px-fdf33f23"); - const monthlyByDayInput = monthlyByDayRadio.createEl("input", { - type: "radio", - value: "byday", + const monthlyByDayRadio = monthlyTypeContainer.createEl("label", { + cls: "tn-custom-recurrence-modal__radio-option", }); - monthlyByDayInput.name = "monthly-type"; - monthlyByDayInput.checked = this.byDay.length > 0 && this.bySetPos !== undefined; + const monthlyByDayInput = createRadioInput( + monthlyByDayRadio, + "monthly-type", + "byday", + this.byDay.length > 0 && this.bySetPos !== undefined + ); monthlyByDayRadio.createSpan({ text: " On the " }); - const monthlyWeekSelect = monthlyByDayRadio.createEl("select"); - monthlyWeekSelect.classList.add("tn-static-margin-left-4px-46cec891"); - monthlyWeekSelect.classList.remove("tn-static-margin-right-8px-539fa9a0"); - monthlyWeekSelect.classList.add("tn-static-margin-right-4px-c6b76b85"); + const monthlyWeekSelect = monthlyByDayRadio.createEl("select", { + cls: "tn-custom-recurrence-modal__select", + }); const weekOptions = [ - { value: "1", text: "first" }, - { value: "2", text: "second" }, - { value: "3", text: "third" }, - { value: "4", text: "fourth" }, - { value: "-1", text: "last" }, + { + value: "1", + text: this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.weekPositions.first" + ), + }, + { + value: "2", + text: this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.weekPositions.second" + ), + }, + { + value: "3", + text: this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.weekPositions.third" + ), + }, + { + value: "4", + text: this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.weekPositions.fourth" + ), + }, + { + value: "-1", + text: this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.weekPositions.last" + ), + }, ]; weekOptions.forEach((opt) => { const option = monthlyWeekSelect.createEl("option", { @@ -946,10 +1015,9 @@ class CustomRecurrenceModal extends Modal { } }); - const monthlyDaySelect = monthlyByDayRadio.createEl("select"); - monthlyDaySelect.classList.add("tn-static-margin-left-4px-46cec891"); - monthlyDaySelect.classList.remove("tn-static-margin-right-8px-539fa9a0"); - monthlyDaySelect.classList.add("tn-static-margin-right-4px-c6b76b85"); + const monthlyDaySelect = monthlyByDayRadio.createEl("select", { + cls: "tn-custom-recurrence-modal__select", + }); const dayOptions = orderedWeekdays.map((day) => ({ value: day.code, text: this.translate( @@ -974,58 +1042,48 @@ class CustomRecurrenceModal extends Modal { // Yearly options const yearlyTypeSetting = new Setting(contentEl) - .setName("Yearly recurrence") - .setDesc("Choose how to repeat yearly"); - - const yearlyTypeContainer = yearlyTypeSetting.controlEl.createDiv("yearly-options"); + .setName( + this.translate("components.recurrenceContextMenu.customRecurrenceModal.yearlyRecurrence") + ) + .setDesc( + this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.yearlyRecurrenceDesc" + ) + ); - const yearlyByDateRadio = yearlyTypeContainer.createEl("label", { cls: "radio-option" }); - yearlyByDateRadio.classList.remove( - "tn-static-display-flex-4d51fc62", - "tn-static-display-flex-75816cae", - "tn-static-display-flex-8bb39979", - "tn-static-display-inline-block-60e32dcb", - "tn-static-display-inline-cccfa456", - "tn-static-display-inline-flex-f984c520", - "tn-static-display-none-6b99de8b", - "tn-static-min-height-800px-997b4c8c" + const yearlyTypeContainer = yearlyTypeSetting.controlEl.createDiv( + "tn-custom-recurrence-modal__options" ); - yearlyByDateRadio.classList.add("tn-static-display-block-2a1b75c9"); - yearlyByDateRadio.classList.remove( - "tn-static-font-size-12px-65574819", - "tn-static-font-weight-bold-0fe8c30d", - "tn-static-font-weight-bold-e0b452bd", - "tn-static-margin-bottom-0-75rem-c05a3c6e", - "tn-static-margin-bottom-20px-49f14f8f" - ); - yearlyByDateRadio.classList.add("tn-static-margin-bottom-8px-fdf33f23"); - const yearlyByDateInput = yearlyByDateRadio.createEl("input", { - type: "radio", - value: "bydate", + + const yearlyByDateRadio = yearlyTypeContainer.createEl("label", { + cls: "tn-custom-recurrence-modal__radio-option", }); - yearlyByDateInput.name = "yearly-type"; - yearlyByDateInput.checked = - this.byMonthDay.length > 0 || (this.byDay.length === 0 && this.bySetPos === undefined); + const yearlyByDateInput = createRadioInput( + yearlyByDateRadio, + "yearly-type", + "bydate", + this.byMonthDay.length > 0 || (this.byDay.length === 0 && this.bySetPos === undefined) + ); yearlyByDateRadio.createSpan({ text: " On " }); - const yearlyMonthSelect = yearlyByDateRadio.createEl("select"); - yearlyMonthSelect.classList.add("tn-static-margin-left-4px-46cec891"); - yearlyMonthSelect.classList.remove("tn-static-margin-right-8px-539fa9a0"); - yearlyMonthSelect.classList.add("tn-static-margin-right-4px-c6b76b85"); - const monthNames = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ]; + const yearlyMonthSelect = yearlyByDateRadio.createEl("select", { + cls: "tn-custom-recurrence-modal__select", + }); + const monthNameKeys = [ + "january", + "february", + "march", + "april", + "may", + "june", + "july", + "august", + "september", + "october", + "november", + "december", + ] as const; + const monthNames = monthNameKeys.map((key) => this.translate(`common.months.${key}`)); monthNames.forEach((month, index) => { const option = yearlyMonthSelect.createEl("option", { value: (index + 1).toString(), @@ -1041,10 +1099,9 @@ class CustomRecurrenceModal extends Modal { } }); - const yearlyDateSelect = yearlyByDateRadio.createEl("select"); - yearlyDateSelect.classList.add("tn-static-margin-left-4px-46cec891"); - yearlyDateSelect.classList.remove("tn-static-margin-right-8px-539fa9a0"); - yearlyDateSelect.classList.add("tn-static-margin-right-4px-c6b76b85"); + const yearlyDateSelect = yearlyByDateRadio.createEl("select", { + cls: "tn-custom-recurrence-modal__select", + }); for (const dayOption of getMonthDayOptions()) { const option = yearlyDateSelect.createEl("option", { value: dayOption.value, @@ -1059,38 +1116,20 @@ class CustomRecurrenceModal extends Modal { } yearlyByDateRadio.createSpan({ text: " each year" }); - const yearlyByDayRadio = yearlyTypeContainer.createEl("label", { cls: "radio-option" }); - yearlyByDayRadio.classList.remove( - "tn-static-display-flex-4d51fc62", - "tn-static-display-flex-75816cae", - "tn-static-display-flex-8bb39979", - "tn-static-display-inline-block-60e32dcb", - "tn-static-display-inline-cccfa456", - "tn-static-display-inline-flex-f984c520", - "tn-static-display-none-6b99de8b", - "tn-static-min-height-800px-997b4c8c" - ); - yearlyByDayRadio.classList.add("tn-static-display-block-2a1b75c9"); - yearlyByDayRadio.classList.remove( - "tn-static-font-size-12px-65574819", - "tn-static-font-weight-bold-0fe8c30d", - "tn-static-font-weight-bold-e0b452bd", - "tn-static-margin-bottom-0-75rem-c05a3c6e", - "tn-static-margin-bottom-20px-49f14f8f" - ); - yearlyByDayRadio.classList.add("tn-static-margin-bottom-8px-fdf33f23"); - const yearlyByDayInput = yearlyByDayRadio.createEl("input", { - type: "radio", - value: "byday", + const yearlyByDayRadio = yearlyTypeContainer.createEl("label", { + cls: "tn-custom-recurrence-modal__radio-option", }); - yearlyByDayInput.name = "yearly-type"; - yearlyByDayInput.checked = this.byDay.length > 0 && this.bySetPos !== undefined; + const yearlyByDayInput = createRadioInput( + yearlyByDayRadio, + "yearly-type", + "byday", + this.byDay.length > 0 && this.bySetPos !== undefined + ); yearlyByDayRadio.createSpan({ text: " On the " }); - const yearlyWeekSelect = yearlyByDayRadio.createEl("select"); - yearlyWeekSelect.classList.add("tn-static-margin-left-4px-46cec891"); - yearlyWeekSelect.classList.remove("tn-static-margin-right-8px-539fa9a0"); - yearlyWeekSelect.classList.add("tn-static-margin-right-4px-c6b76b85"); + const yearlyWeekSelect = yearlyByDayRadio.createEl("select", { + cls: "tn-custom-recurrence-modal__select", + }); weekOptions.forEach((opt) => { const option = yearlyWeekSelect.createEl("option", { value: opt.value, @@ -1103,10 +1142,9 @@ class CustomRecurrenceModal extends Modal { } }); - const yearlyDaySelect = yearlyByDayRadio.createEl("select"); - yearlyDaySelect.classList.add("tn-static-margin-left-4px-46cec891"); - yearlyDaySelect.classList.remove("tn-static-margin-right-8px-539fa9a0"); - yearlyDaySelect.classList.add("tn-static-margin-right-4px-c6b76b85"); + const yearlyDaySelect = yearlyByDayRadio.createEl("select", { + cls: "tn-custom-recurrence-modal__select", + }); dayOptions.forEach((opt) => { const option = yearlyDaySelect.createEl("option", { value: opt.value, text: opt.text }); if (this.byDay.length > 0 && this.byDay[0] === opt.value) { @@ -1116,10 +1154,9 @@ class CustomRecurrenceModal extends Modal { } }); - const yearlyByDayMonthSelect = yearlyByDayRadio.createEl("select"); - yearlyByDayMonthSelect.classList.add("tn-static-margin-left-4px-46cec891"); - yearlyByDayMonthSelect.classList.remove("tn-static-margin-right-8px-539fa9a0"); - yearlyByDayMonthSelect.classList.add("tn-static-margin-right-4px-c6b76b85"); + const yearlyByDayMonthSelect = yearlyByDayRadio.createEl("select", { + cls: "tn-custom-recurrence-modal__select", + }); monthNames.forEach((month, index) => { const option = yearlyByDayMonthSelect.createEl("option", { value: (index + 1).toString(), @@ -1136,125 +1173,149 @@ class CustomRecurrenceModal extends Modal { }); yearlyByDayRadio.createSpan({ text: " each year" }); + // Keep Monthly radio selection in sync with which sub-controls are being edited, + // and grey out the inactive option's controls so it's clear which one applies. + const updateMonthlyControlsDisabled = () => { + const isByDate = monthlyByDateInput.checked; + monthlyDateSelect.disabled = !isByDate; + monthlyWeekSelect.disabled = isByDate; + monthlyDaySelect.disabled = isByDate; + }; + monthlyByDateInput.addEventListener("change", updateMonthlyControlsDisabled); + monthlyByDayInput.addEventListener("change", updateMonthlyControlsDisabled); + monthlyDateSelect.addEventListener("change", () => { + monthlyByDateInput.checked = true; + updateMonthlyControlsDisabled(); + }); + [monthlyWeekSelect, monthlyDaySelect].forEach((select) => { + select.addEventListener("change", () => { + monthlyByDayInput.checked = true; + updateMonthlyControlsDisabled(); + }); + }); + updateMonthlyControlsDisabled(); + + // Same treatment for Yearly's "on date" vs "on the Nth weekday" sub-controls. + const updateYearlyControlsDisabled = () => { + const isByDate = yearlyByDateInput.checked; + yearlyMonthSelect.disabled = !isByDate; + yearlyDateSelect.disabled = !isByDate; + yearlyWeekSelect.disabled = isByDate; + yearlyDaySelect.disabled = isByDate; + yearlyByDayMonthSelect.disabled = isByDate; + }; + yearlyByDateInput.addEventListener("change", updateYearlyControlsDisabled); + yearlyByDayInput.addEventListener("change", updateYearlyControlsDisabled); + [yearlyMonthSelect, yearlyDateSelect].forEach((select) => { + select.addEventListener("change", () => { + yearlyByDateInput.checked = true; + updateYearlyControlsDisabled(); + }); + }); + [yearlyWeekSelect, yearlyDaySelect, yearlyByDayMonthSelect].forEach((select) => { + select.addEventListener("change", () => { + yearlyByDayInput.checked = true; + updateYearlyControlsDisabled(); + }); + }); + updateYearlyControlsDisabled(); + // End condition new Setting(contentEl) - .setName("End condition") - .setDesc("Choose when the recurrence should end"); + .setName( + this.translate("components.recurrenceContextMenu.customRecurrenceModal.endCondition") + ) + .setDesc( + this.translate("components.recurrenceContextMenu.customRecurrenceModal.endConditionDesc") + ); - const endConditionContainer = contentEl.createDiv("end-condition-container"); + const endConditionContainer = contentEl.createDiv( + "tn-custom-recurrence-modal__end-condition" + ); // Never ends - const neverRadio = endConditionContainer.createEl("label", { cls: "radio-option" }); - neverRadio.classList.remove( - "tn-static-display-flex-4d51fc62", - "tn-static-display-flex-75816cae", - "tn-static-display-flex-8bb39979", - "tn-static-display-inline-block-60e32dcb", - "tn-static-display-inline-cccfa456", - "tn-static-display-inline-flex-f984c520", - "tn-static-display-none-6b99de8b", - "tn-static-min-height-800px-997b4c8c" - ); - neverRadio.classList.add("tn-static-display-block-2a1b75c9"); - neverRadio.classList.remove( - "tn-static-font-size-12px-65574819", - "tn-static-font-weight-bold-0fe8c30d", - "tn-static-font-weight-bold-e0b452bd", - "tn-static-margin-bottom-0-75rem-c05a3c6e", - "tn-static-margin-bottom-20px-49f14f8f" + const neverRadio = endConditionContainer.createEl("label", { + cls: "tn-custom-recurrence-modal__radio-option", + }); + const neverInput = createRadioInput( + neverRadio, + "end-type", + "never", + this.endType === "never" ); - neverRadio.classList.add("tn-static-margin-bottom-8px-fdf33f23"); - const neverInput = neverRadio.createEl("input", { type: "radio", value: "never" }); - neverInput.name = "end-type"; - neverInput.checked = this.endType === "never"; - neverRadio.createSpan({ text: " Never ends" }); + neverRadio.createSpan({ + text: this.translate("components.recurrenceContextMenu.customRecurrenceModal.neverEnds"), + }); // End after X occurrences - const countRadio = endConditionContainer.createEl("label", { cls: "radio-option" }); - countRadio.classList.remove( - "tn-static-display-flex-4d51fc62", - "tn-static-display-flex-75816cae", - "tn-static-display-flex-8bb39979", - "tn-static-display-inline-block-60e32dcb", - "tn-static-display-inline-cccfa456", - "tn-static-display-inline-flex-f984c520", - "tn-static-display-none-6b99de8b", - "tn-static-min-height-800px-997b4c8c" - ); - countRadio.classList.add("tn-static-display-block-2a1b75c9"); - countRadio.classList.remove( - "tn-static-font-size-12px-65574819", - "tn-static-font-weight-bold-0fe8c30d", - "tn-static-font-weight-bold-e0b452bd", - "tn-static-margin-bottom-0-75rem-c05a3c6e", - "tn-static-margin-bottom-20px-49f14f8f" + const countRadio = endConditionContainer.createEl("label", { + cls: "tn-custom-recurrence-modal__radio-option", + }); + const countInput = createRadioInput( + countRadio, + "end-type", + "count", + this.endType === "count" ); - countRadio.classList.add("tn-static-margin-bottom-8px-fdf33f23"); - const countInput = countRadio.createEl("input", { type: "radio", value: "count" }); - countInput.name = "end-type"; - countInput.checked = this.endType === "count"; - countRadio.createSpan({ text: " End after " }); - const countText = countRadio.createEl("input", { type: "number", placeholder: "10" }); - countText.classList.remove( - "tn-static-width-100-0466783d", - "tn-static-width-12px-fbf353fb", - "tn-static-width-16px-7375d50b", - "tn-static-width-1px-aa77e27e", - "tn-static-width-200px-2acaf3b5", - "tn-static-width-80px-8573bae3" + const endAfterTemplate = this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.endAfterOccurrences" ); - countText.classList.add("tn-static-width-60px-bd09c419"); - countText.classList.add("tn-static-margin-left-4px-46cec891"); - countText.classList.remove("tn-static-margin-right-8px-539fa9a0"); - countText.classList.add("tn-static-margin-right-4px-c6b76b85"); + const [endAfterPrefix, endAfterSuffix] = endAfterTemplate.split("{count}"); + countRadio.createSpan({ text: endAfterPrefix }); + const countText = countRadio.createEl("input", { + type: "number", + placeholder: "10", + cls: "tn-custom-recurrence-modal__count-input", + }); countText.value = this.count ? this.count.toString() : ""; - countRadio.createSpan({ text: " occurrences" }); + countRadio.createSpan({ text: endAfterSuffix ? endAfterSuffix.trim() : "" }); // End on date - const untilRadio = endConditionContainer.createEl("label", { cls: "radio-option" }); - untilRadio.classList.remove( - "tn-static-display-flex-4d51fc62", - "tn-static-display-flex-75816cae", - "tn-static-display-flex-8bb39979", - "tn-static-display-inline-block-60e32dcb", - "tn-static-display-inline-cccfa456", - "tn-static-display-inline-flex-f984c520", - "tn-static-display-none-6b99de8b", - "tn-static-min-height-800px-997b4c8c" + const untilRadio = endConditionContainer.createEl("label", { + cls: "tn-custom-recurrence-modal__radio-option", + }); + const untilInput = createRadioInput( + untilRadio, + "end-type", + "until", + this.endType === "until" ); - untilRadio.classList.add("tn-static-display-block-2a1b75c9"); - untilRadio.classList.remove( - "tn-static-font-size-12px-65574819", - "tn-static-font-weight-bold-0fe8c30d", - "tn-static-font-weight-bold-e0b452bd", - "tn-static-margin-bottom-0-75rem-c05a3c6e", - "tn-static-margin-bottom-20px-49f14f8f" + const endOnTemplate = this.translate( + "components.recurrenceContextMenu.customRecurrenceModal.endOnDate" ); - untilRadio.classList.add("tn-static-margin-bottom-8px-fdf33f23"); - const untilInput = untilRadio.createEl("input", { type: "radio", value: "until" }); - untilInput.name = "end-type"; - untilInput.checked = this.endType === "until"; - untilRadio.createSpan({ text: " End on " }); - const untilDate = untilRadio.createEl("input", { type: "date" }); - untilDate.classList.add("tn-static-margin-left-4px-46cec891"); + const [endOnPrefix] = endOnTemplate.split("{date}"); + untilRadio.createSpan({ text: endOnPrefix }); + const untilDate = untilRadio.createEl("input", { + type: "date", + cls: "tn-custom-recurrence-modal__select tn-datetime-input", + }); untilDate.value = this.until ? this.until.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") : ""; + + const updateEndConditionDisabled = () => { + countText.disabled = !countInput.checked; + untilDate.disabled = !untilInput.checked; + }; + attachDateInputBehavior(untilDate, { onCommit: (value) => { this.until = value.replace(/-/g, ""); untilInput.checked = true; this.endType = "until"; + updateEndConditionDisabled(); }, }); - // Event listeners for end condition neverInput.addEventListener("change", () => { if (neverInput.checked) this.endType = "never"; + updateEndConditionDisabled(); }); countInput.addEventListener("change", () => { if (countInput.checked) this.endType = "count"; + updateEndConditionDisabled(); }); untilInput.addEventListener("change", () => { if (untilInput.checked) this.endType = "until"; + updateEndConditionDisabled(); }); countText.addEventListener("input", () => { @@ -1262,6 +1323,7 @@ class CustomRecurrenceModal extends Modal { if (countText.value) { countInput.checked = true; this.endType = "count"; + updateEndConditionDisabled(); } }); @@ -1270,11 +1332,16 @@ class CustomRecurrenceModal extends Modal { if (untilDate.value) { untilInput.checked = true; this.endType = "until"; + updateEndConditionDisabled(); } }); + updateEndConditionDisabled(); + this.updateFrequencySpecificVisibility = () => { const useFlexibleInterval = this.recurrenceAnchor === "completion"; + intervalSetting.setDesc(this.translate(getIntervalDescKey())); + byDaySetting.setDesc(this.translate(getDaysOfWeekDescKey())); byDaySetting.settingEl.style.display = (this.frequency === "DAILY" || this.frequency === "WEEKLY") && !useFlexibleInterval @@ -1327,12 +1394,17 @@ class CustomRecurrenceModal extends Modal { ); buttonContainer.classList.add("tn-static-margin-top-16px-1b0f4999"); - const cancelButton = buttonContainer.createEl("button", { text: "Cancel" }); + const cancelButton = buttonContainer.createEl("button", { + text: this.translate("components.recurrenceContextMenu.customRecurrenceModal.cancel"), + }); cancelButton.addEventListener("click", () => { this.close(); }); - const saveButton = buttonContainer.createEl("button", { text: "Save", cls: "mod-cta" }); + const saveButton = buttonContainer.createEl("button", { + text: this.translate("components.recurrenceContextMenu.customRecurrenceModal.save"), + cls: "mod-cta", + }); saveButton.addEventListener("click", () => { // Get current radio button states and dropdown values const monthlyType = monthlyByDateInput.checked ? "bydate" : "byday"; diff --git a/src/i18n/resources/de.ts b/src/i18n/resources/de.ts index 46a27f84c..9fbff6ef1 100644 --- a/src/i18n/resources/de.ts +++ b/src/i18n/resources/de.ts @@ -3310,8 +3310,13 @@ export const de: TranslationTree = { frequency: "Häufigkeit", interval: "Intervall", intervalDesc: "Alle X Tage/Wochen/Monate/Jahre", + intervalDescDaily: "Alle X Tage", + intervalDescWeekly: "Alle X Wochen", + intervalDescMonthly: "Alle X Monate", + intervalDescYearly: "Alle X Jahre", daysOfWeek: "Wochentage", daysOfWeekDesc: "Bestimmte Tage auswählen (für wöchentliche Wiederholung)", + daysOfWeekDescDaily: "Tägliche Vorkommen optional auf ausgewählte Wochentage beschränken", monthlyRecurrence: "Monatliche Wiederholung", monthlyRecurrenceDesc: "Wähle, wie monatlich wiederholt werden soll", yearlyRecurrence: "Jährliche Wiederholung", diff --git a/src/i18n/resources/en.ts b/src/i18n/resources/en.ts index 0926250b6..ab6390bf6 100644 --- a/src/i18n/resources/en.ts +++ b/src/i18n/resources/en.ts @@ -3486,8 +3486,13 @@ export const en: TranslationTree = { frequency: "Frequency", interval: "Interval", intervalDesc: "Every x days/weeks/months/years", + intervalDescDaily: "Every x days", + intervalDescWeekly: "Every x weeks", + intervalDescMonthly: "Every x months", + intervalDescYearly: "Every x years", daysOfWeek: "Days of week", daysOfWeekDesc: "Select specific days (for weekly recurrence)", + daysOfWeekDescDaily: "Optionally limit daily occurrences to selected weekdays", monthlyRecurrence: "Monthly recurrence", monthlyRecurrenceDesc: "Choose how to repeat monthly", yearlyRecurrence: "Yearly recurrence", diff --git a/src/i18n/resources/es.ts b/src/i18n/resources/es.ts index 07d2caeaf..1c6394c3b 100644 --- a/src/i18n/resources/es.ts +++ b/src/i18n/resources/es.ts @@ -3310,8 +3310,13 @@ export const es: TranslationTree = { frequency: "Frecuencia", interval: "Intervalo", intervalDesc: "Cada X días/semanas/meses/años", + intervalDescDaily: "Cada X días", + intervalDescWeekly: "Cada X semanas", + intervalDescMonthly: "Cada X meses", + intervalDescYearly: "Cada X años", daysOfWeek: "Días de la semana", daysOfWeekDesc: "Seleccionar días específicos (para recurrencia semanal)", + daysOfWeekDescDaily: "Limitar opcionalmente las ocurrencias diarias a los días seleccionados", monthlyRecurrence: "Recurrencia mensual", monthlyRecurrenceDesc: "Elegir cómo repetir mensualmente", yearlyRecurrence: "Recurrencia anual", diff --git a/src/i18n/resources/fr.ts b/src/i18n/resources/fr.ts index 9d16e2657..802c13ac2 100644 --- a/src/i18n/resources/fr.ts +++ b/src/i18n/resources/fr.ts @@ -3310,8 +3310,13 @@ export const fr: TranslationTree = { frequency: "Fréquence", interval: "Intervalle", intervalDesc: "Tous les X jours/semaines/mois/années", + intervalDescDaily: "Tous les X jours", + intervalDescWeekly: "Toutes les X semaines", + intervalDescMonthly: "Tous les X mois", + intervalDescYearly: "Tous les X ans", daysOfWeek: "Jours de la semaine", daysOfWeekDesc: "Sélectionnez des jours spécifiques (pour la récurrence hebdomadaire)", + daysOfWeekDescDaily: "Limiter éventuellement les occurrences quotidiennes aux jours sélectionnés", monthlyRecurrence: "Récurrence mensuelle", monthlyRecurrenceDesc: "Choisissez comment répéter mensuellement", yearlyRecurrence: "Récurrence annuelle", diff --git a/src/i18n/resources/ja.ts b/src/i18n/resources/ja.ts index 1e3f4df6e..8de780036 100644 --- a/src/i18n/resources/ja.ts +++ b/src/i18n/resources/ja.ts @@ -3310,8 +3310,13 @@ export const ja: TranslationTree = { frequency: "頻度", interval: "間隔", intervalDesc: "X日/週/月/年ごと", + intervalDescDaily: "X日ごと", + intervalDescWeekly: "X週ごと", + intervalDescMonthly: "Xか月ごと", + intervalDescYearly: "X年ごと", daysOfWeek: "曜日", daysOfWeekDesc: "特定の曜日を選択(週次繰り返し用)", + daysOfWeekDescDaily: "毎日の繰り返しを選択した曜日に限定する(任意)", monthlyRecurrence: "月次繰り返し", monthlyRecurrenceDesc: "月次繰り返し方法を選択", yearlyRecurrence: "年次繰り返し", diff --git a/src/i18n/resources/ko.ts b/src/i18n/resources/ko.ts index ee54da2c2..869687100 100644 --- a/src/i18n/resources/ko.ts +++ b/src/i18n/resources/ko.ts @@ -3294,8 +3294,13 @@ export const ko: TranslationTree = { frequency: "빈도", interval: "간격", intervalDesc: "X일/주/월/년마다", + intervalDescDaily: "X일마다", + intervalDescWeekly: "X주마다", + intervalDescMonthly: "X개월마다", + intervalDescYearly: "X년마다", daysOfWeek: "요일", daysOfWeekDesc: "특정 요일 선택 (주간 반복용)", + daysOfWeekDescDaily: "일일 반복을 선택한 요일로 선택적으로 제한", monthlyRecurrence: "월간 반복", monthlyRecurrenceDesc: "매월 반복 방법 선택", yearlyRecurrence: "연간 반복", diff --git a/src/i18n/resources/pt.ts b/src/i18n/resources/pt.ts index 7d377b4c7..51304c3df 100644 --- a/src/i18n/resources/pt.ts +++ b/src/i18n/resources/pt.ts @@ -3315,8 +3315,13 @@ export const pt: TranslationTree = { frequency: "Frequência", interval: "Intervalo", intervalDesc: "A cada X dias/semanas/meses/anos", + intervalDescDaily: "A cada X dias", + intervalDescWeekly: "A cada X semanas", + intervalDescMonthly: "A cada X meses", + intervalDescYearly: "A cada X anos", daysOfWeek: "Dias da semana", daysOfWeekDesc: "Selecione dias específicos (para recorrência semanal)", + daysOfWeekDescDaily: "Opcionalmente limitar ocorrências diárias aos dias selecionados", monthlyRecurrence: "Recorrência mensal", monthlyRecurrenceDesc: "Escolha como repetir mensalmente", yearlyRecurrence: "Recorrência anual", diff --git a/src/i18n/resources/ru.ts b/src/i18n/resources/ru.ts index d0f75346a..b270c451e 100644 --- a/src/i18n/resources/ru.ts +++ b/src/i18n/resources/ru.ts @@ -3310,8 +3310,13 @@ export const ru: TranslationTree = { frequency: "Частота", interval: "Интервал", intervalDesc: "Каждые X дней/недель/месяцев/лет", + intervalDescDaily: "Каждые X дней", + intervalDescWeekly: "Каждые X недель", + intervalDescMonthly: "Каждые X месяцев", + intervalDescYearly: "Каждые X лет", daysOfWeek: "Дни недели", daysOfWeekDesc: "Выберите конкретные дни (для еженедельного повторения)", + daysOfWeekDescDaily: "При необходимости ограничить ежедневные повторения выбранными днями недели", monthlyRecurrence: "Ежемесячное повторение", monthlyRecurrenceDesc: "Выберите, как повторять ежемесячно", yearlyRecurrence: "Ежегодное повторение", diff --git a/src/i18n/resources/zh.ts b/src/i18n/resources/zh.ts index 0665e8e0c..253e2990e 100644 --- a/src/i18n/resources/zh.ts +++ b/src/i18n/resources/zh.ts @@ -3309,8 +3309,13 @@ export const zh: TranslationTree = { frequency: "频率", interval: "间隔", intervalDesc: "每X天/周/月/年", + intervalDescDaily: "每X天", + intervalDescWeekly: "每X周", + intervalDescMonthly: "每X个月", + intervalDescYearly: "每X年", daysOfWeek: "一周中的天", daysOfWeekDesc: "选择特定天(用于每周重复)", + daysOfWeekDescDaily: "可选:将每日重复限制为所选工作日", monthlyRecurrence: "每月重复", monthlyRecurrenceDesc: "选择如何每月重复", yearlyRecurrence: "每年重复", diff --git a/src/modals/DateTimePickerModal.ts b/src/modals/DateTimePickerModal.ts index cea524b83..f6695bbd7 100644 --- a/src/modals/DateTimePickerModal.ts +++ b/src/modals/DateTimePickerModal.ts @@ -195,7 +195,7 @@ export class DateTimePickerModal extends Modal { const control = field.createDiv({ cls: "date-time-picker-modal__native-date-control" }); this.dateInput = control.createEl("input", { - cls: "date-time-picker-modal__date-input", + cls: "date-time-picker-modal__date-input tn-datetime-input", attr: { id: "tasknotes-date-time-picker-date", type: "date", @@ -243,7 +243,7 @@ export class DateTimePickerModal extends Modal { }); this.timeInput = field.createEl("input", { - cls: "date-time-picker-modal__time-input", + cls: "date-time-picker-modal__time-input tn-datetime-input", attr: { id: "tasknotes-date-time-picker-time", type: "time", diff --git a/src/modals/ReminderModal.ts b/src/modals/ReminderModal.ts index e3e8bbbfa..d31df3705 100644 --- a/src/modals/ReminderModal.ts +++ b/src/modals/ReminderModal.ts @@ -452,6 +452,7 @@ export class ReminderModal extends Modal { this.absoluteDate = value; }); text.inputEl.type = "date"; + text.inputEl.addClass("tn-datetime-input"); attachDateInputBehavior(text.inputEl, { onCommit: (value) => { this.absoluteDate = value; @@ -466,6 +467,7 @@ export class ReminderModal extends Modal { this.absoluteTime = value; }); text.inputEl.type = "time"; + text.inputEl.addClass("tn-datetime-input"); }); // Description field (common) diff --git a/src/modals/TimeblockCreationModal.ts b/src/modals/TimeblockCreationModal.ts index 5eaafb374..fab130dfb 100644 --- a/src/modals/TimeblockCreationModal.ts +++ b/src/modals/TimeblockCreationModal.ts @@ -162,6 +162,7 @@ export class TimeblockCreationModal extends Modal { .setValue(this.options.startTime || "") .onChange(() => this.validateForm()); this.startTimeInput.type = "time"; + this.startTimeInput.addClass("tn-datetime-input"); }); new Setting(contentEl) @@ -175,6 +176,7 @@ export class TimeblockCreationModal extends Modal { this.validateForm(); }); this.endTimeInput.type = "time"; + this.endTimeInput.addClass("tn-datetime-input"); }); // Description (optional) diff --git a/styles/modal-bem.css b/styles/modal-bem.css index be1068781..da24b220b 100644 --- a/styles/modal-bem.css +++ b/styles/modal-bem.css @@ -305,30 +305,35 @@ /* User-field date input: full width textbox; icon overlayed on the left */ .tasknotes-plugin .tn-date-control { - position: relative; - padding-left: 35px; /* reserve space for the icon to the left of the textbox */ + position: relative; + padding-left: 35px; + /* reserve space for the icon to the left of the textbox */ } + .tasknotes-plugin .tn-date-control input[type="text"] { - padding-left: 0; /* the padding-left is applied on the container */ + padding-left: 0; + /* the padding-left is applied on the container */ } + .tasknotes-plugin .tn-date-control .user-field-date-picker-btn { - position: absolute; - left: 10px; - top: 50%; - transform: translateY(-50%); - width: 20px; - height: 20px; - display: inline-flex; - align-items: center; - justify-content: center; - border: 0; - background: transparent; - padding: 0; - color: var(--tn-text-muted); - z-index: 1; + position: absolute; + left: 10px; + top: 50%; + transform: translateY(-50%); + width: 20px; + height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + border: 0; + background: transparent; + padding: 0; + color: var(--tn-text-muted); + z-index: 1; } + .tasknotes-plugin .tn-date-control .user-field-date-picker-btn:hover { - color: var(--text-normal); + color: var(--text-normal); } @@ -762,6 +767,7 @@ ===================================================================== */ @media (max-width: 768px) { + .tasknotes-plugin .due-date-modal, .tasknotes-plugin .scheduled-date-modal { padding: var(--tn-spacing-lg); @@ -833,7 +839,7 @@ color: var(--tn-interactive-accent); } -.tasknotes-plugin .event-details > div { +.tasknotes-plugin .event-details>div { margin-bottom: var(--size-4-1); font-size: var(--tn-font-size-md); line-height: 1.4; @@ -841,12 +847,12 @@ /* Align all modal setting controls to the right edge of the column */ .tasknotes-plugin .modal .setting-item .setting-item-control { - flex: none; - text-align: end; - display: flex; - justify-content: flex-end; - align-items: center; - gap: var(--size-4-2); + flex: none; + text-align: end; + display: flex; + justify-content: flex-end; + align-items: center; + gap: var(--size-4-2); } @@ -888,7 +894,7 @@ color: var(--tn-interactive-accent); } -.tasknotes-plugin .preview-details > div { +.tasknotes-plugin .preview-details>div { margin-bottom: var(--size-4-1); font-size: var(--tn-font-size-md); } @@ -1133,3 +1139,152 @@ color: var(--tn-text-muted); font-style: italic; } + +/* ===================================================================== + NATIVE DATE/TIME INPUTS (shared across modals) + ===================================================================== */ + +.tasknotes-plugin .tn-datetime-input { + padding: var(--size-4-1) var(--size-4-2); + border: 1px solid color-mix(in srgb, var(--background-modifier-border) 65%, transparent); + border-radius: var(--radius-s); +} + +/* ===================================================================== + CUSTOM RECURRENCE MODAL + ===================================================================== */ + +.modal.tn-custom-recurrence-modal-host { + width: min(560px, 92vw); +} + +.tasknotes-plugin.tn-custom-recurrence-modal { + max-width: 100%; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__days-setting { + flex-direction: column; + align-items: stretch; + gap: var(--size-4-3); +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__days-setting .setting-item-info { + margin-right: 0; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__days-setting .setting-item-control { + justify-content: flex-start; + width: 100%; + margin-top: var(--size-4-2); +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__days { + display: flex; + flex-wrap: nowrap; + align-items: center; + gap: var(--tn-spacing-md); + width: 100%; + overflow-x: auto; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__day { + display: inline-flex; + align-items: center; + gap: var(--tn-spacing-xs); + margin: 0; + white-space: nowrap; + flex-shrink: 0; + cursor: var(--cursor-link, pointer); +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__day input[type="checkbox"] { + margin: 0; + flex-shrink: 0; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__options, +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__end-condition { + display: flex; + flex-direction: column; + gap: var(--tn-spacing-sm); + width: 100%; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__radio-option { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: var(--tn-spacing-sm); + margin: 0; + line-height: 1.2; + cursor: var(--cursor-link, pointer); +} + +/* Chromium does not render ::before/::after generated content on + elements, so the "selected" dot is a real sibling span layered on top of + the radio input inside a small positioning wrapper, instead of a + pseudo-element on the input itself. */ +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__radio-visual { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + height: 1rem; + flex-shrink: 0; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__radio-visual input[type="radio"] { + appearance: none; + -webkit-appearance: none; + box-sizing: border-box; + width: 1rem; + height: 1rem; + margin: 0; + padding: 0; + border: 2px solid var(--text-muted); + border-radius: 50%; + background-color: transparent; + background-image: none; + box-shadow: none; + outline: none; + vertical-align: middle; + cursor: inherit; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__radio-visual input[type="radio"]:checked { + border-color: var(--interactive-accent); + background-color: transparent; + background-image: none; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__radio-dot { + position: absolute; + top: 50%; + left: 50%; + width: 0.5rem; + height: 0.5rem; + border-radius: 50%; + background-color: var(--interactive-accent); + transform: translate(-50%, -50%) scale(0); + opacity: 0; + pointer-events: none; + transition: transform 80ms ease, opacity 80ms ease; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__radio-visual input[type="radio"]:checked~.tn-custom-recurrence-modal__radio-dot { + transform: translate(-50%, -50%) scale(1); + opacity: 1; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__select { + margin: 0; + width: auto; + min-width: 0; +} + +.tasknotes-plugin.tn-custom-recurrence-modal .tn-custom-recurrence-modal__count-input { + width: 60px; + margin: 0; + text-align: center; +} \ No newline at end of file