diff --git a/common/components/primitives.py b/common/components/primitives.py index ef1ec21..16a5dae 100644 --- a/common/components/primitives.py +++ b/common/components/primitives.py @@ -542,9 +542,15 @@ def StaticScript(filename: str) -> SafeText: return mark_safe(f'') -# Media for the Flowbite-datepicker year picker (vendored UMD bundle). Declared -# on the YearPicker node so Page() loads it wherever a YearPicker appears. -_YEAR_PICKER_MEDIA = Media(js_external=("datepicker.umd.js",)) +# Media for the Flowbite-datepicker year picker: the vendored UMD bundle +# (classic script) plus the ts/year_picker.ts glue (compiled module). Declared +# on the YearPicker node so Page() loads both wherever a YearPicker appears. The +# UMD bundle is a classic script (runs during parse) while year_picker.js is a +# deferred module (runs after parse), so Datepicker is defined by the time the +# module executes regardless of tag order. +_YEAR_PICKER_MEDIA = Media( + js_external=("datepicker.umd.js",), js=("dist/year_picker.js",) +) def YearPicker( @@ -589,44 +595,7 @@ def YearPicker( data-available-years="{years_csv}" data-selected-year="{selected}" data-url-template="{url_template}"> - -""", +""", media=_YEAR_PICKER_MEDIA, ) diff --git a/games/static/js/year_picker.js b/games/static/js/year_picker.js deleted file mode 100644 index 7eeb843..0000000 --- a/games/static/js/year_picker.js +++ /dev/null @@ -1,38 +0,0 @@ -import { onSwap } from "./utils.js"; - -onSwap("#year-picker-input", function(pickerEl) { - const selectedYear = pickerEl.dataset.selectedYear; - const urlTemplate = pickerEl.dataset.urlTemplate; - const currentYear = new Date().getFullYear(); - const availableYears = new Set( - pickerEl.dataset.availableYears - .split(",") - .map(s => parseInt(s.trim())) - .filter(n => !isNaN(n)) - ); - - const picker = new Datepicker(pickerEl, { - pickLevel: 2, - format: "yyyy", - minDate: new Date(1999, 0, 1), - maxDate: new Date(currentYear, 11, 31), - autohide: false, - orientation: "bottom end", - showOnClick: false, - showOnFocus: false, - beforeShowYear: (date) => ({ enabled: availableYears.has(date.getFullYear()) }), - }); - pickerEl._pickerInstance = picker; - - picker.element.addEventListener("changeDate", (event) => { - const year = event.detail.date?.getFullYear(); - if (year && urlTemplate) { - window.location.href = urlTemplate.replace("__year__", year); - } - }); - - if (selectedYear) { - picker.dates = [new Date(parseInt(selectedYear), 0, 1)]; - picker.update(); - } -}); diff --git a/ts/year_picker.ts b/ts/year_picker.ts new file mode 100644 index 0000000..a11d3aa --- /dev/null +++ b/ts/year_picker.ts @@ -0,0 +1,54 @@ +/** + * YearPicker — wires the Flowbite-datepicker year grid behind the YearPicker + * component (common/components/primitives.py). The component renders a hidden + * #year-picker-input carrying data-available-years / data-selected-year / + * data-url-template; this turns it into a year-level Datepicker and navigates + * to the chosen year's URL. Datepicker comes from the vendored UMD bundle + * (datepicker.umd.js), loaded as a classic script before this module runs. + */ +import { onSwap } from "./utils.js"; + +declare const Datepicker: any; + +// The Alpine toggle button reaches the Datepicker instance through this prop. +interface PickerElement extends HTMLInputElement { + _pickerInstance?: any; +} + +onSwap("#year-picker-input", (element) => { + const pickerElement = element as PickerElement; + const selectedYear = pickerElement.dataset.selectedYear; + const urlTemplate = pickerElement.dataset.urlTemplate; + const currentYear = new Date().getFullYear(); + const availableYears = new Set( + (pickerElement.dataset.availableYears ?? "") + .split(",") + .map((part) => parseInt(part.trim(), 10)) + .filter((year) => !isNaN(year)) + ); + + const picker = new Datepicker(pickerElement, { + pickLevel: 2, + format: "yyyy", + minDate: new Date(1999, 0, 1), + maxDate: new Date(currentYear, 11, 31), + autohide: false, + orientation: "bottom end", + showOnClick: false, + showOnFocus: false, + beforeShowYear: (date: Date) => ({ enabled: availableYears.has(date.getFullYear()) }), + }); + pickerElement._pickerInstance = picker; + + picker.element.addEventListener("changeDate", (event: Event) => { + const year = (event as CustomEvent).detail.date?.getFullYear(); + if (year && urlTemplate) { + window.location.href = urlTemplate.replace("__year__", String(year)); + } + }); + + if (selectedYear) { + picker.dates = [new Date(parseInt(selectedYear, 10), 0, 1)]; + picker.update(); + } +});