diff --git a/common/components/date_range_picker.py b/common/components/date_range_picker.py
index 860694b..70cc539 100644
--- a/common/components/date_range_picker.py
+++ b/common/components/date_range_picker.py
@@ -12,17 +12,17 @@
The committed value lives in two hidden ISO-date inputs named
``{input_name_prefix}-min`` / ``{input_name_prefix}-max`` — the same contract
-as the older ``DateRangeFilter``, so ``filter_bar.js`` serializes either
+as the older ``DateRangeFilter``, so ``filter_bar.ts`` serializes either
widget into a ``DateCriterion`` unchanged. All behaviour is wired by
-``games/static/js/date_range_picker.js``.
+``ts/date_range_picker.ts`` (compiled to ``dist/date_range_picker.js``).
"""
from common.components.core import Element, HTMLAttribute, Media, Node, Safe
from common.components.primitives import Div, Input, Span
from common.time import DatePartSpec, date_parts
-# Wired by date_range_picker.js.
-_DATE_RANGE_MEDIA = Media(js=("date_range_picker.js",))
+# Wired by ts/date_range_picker.ts (compiled to dist/).
+_DATE_RANGE_MEDIA = Media(js=("dist/date_range_picker.js",))
_FIELD_CONTAINER_CLASS = (
"flex items-center gap-0.5 w-full rounded-base border border-default-medium "
diff --git a/e2e/test_date_range_picker_e2e.py b/e2e/test_date_range_picker_e2e.py
index 43c8a63..11585bd 100644
--- a/e2e/test_date_range_picker_e2e.py
+++ b/e2e/test_date_range_picker_e2e.py
@@ -31,7 +31,7 @@ def _bar_page(filter_json: str = "") -> str:
-
+
diff --git a/tests/test_node_tree.py b/tests/test_node_tree.py
index 286c65e..4843b69 100644
--- a/tests/test_node_tree.py
+++ b/tests/test_node_tree.py
@@ -149,7 +149,7 @@ class RealComponentMediaTest(unittest.TestCase):
media = collect_media(
DateRangePicker(label="Played", input_name_prefix="played")
)
- self.assertEqual(media.js, ("date_range_picker.js",))
+ self.assertEqual(media.js, ("dist/date_range_picker.js",))
def test_range_slider_declares_its_script(self):
from common.components.filters import RangeSlider
diff --git a/games/static/js/date_range_picker.js b/ts/date_range_picker.ts
similarity index 59%
rename from games/static/js/date_range_picker.js
rename to ts/date_range_picker.ts
index ad3da6b..6a0f370 100644
--- a/games/static/js/date_range_picker.js
+++ b/ts/date_range_picker.ts
@@ -1,5 +1,5 @@
/**
- * DateRangePicker — vanilla JavaScript implementation.
+ * DateRangePicker — vanilla TypeScript implementation.
*
* Drives the DateRangePicker component (common/components/date_range_picker.py):
*
@@ -15,42 +15,62 @@
* clicked date.
*
* The committed value lives in the two hidden ISO inputs ({prefix}-min /
- * {prefix}-max) that filter_bar.js serializes into a DateCriterion.
+ * {prefix}-max) that filter_bar.ts serializes into a DateCriterion.
*
* NB: class strings below are emitted verbatim so the Tailwind scanner picks
* them up — keep them as plain literals.
*/
-(function () {
+import { onSwap } from "./utils.js";
+
+type Anchor = "" | "start" | "end";
+
+interface CalendarState {
+ open: boolean;
+ viewYear: number;
+ viewMonth: number;
+ startIso: string;
+ endIso: string;
+ // The anchor is the fixed endpoint: "start" while picking the EndDate,
+ // "end" once the range is complete (further picks move the StartDate).
+ anchor: Anchor;
+ hoverIso: string;
+ // True while showing a committed range the user has not edited yet —
+ // the track renders muted until the first pick.
+ readOnly: boolean;
+ refreshFromField: () => void;
+}
+
+(() => {
"use strict";
- var WEEKDAY_LABELS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
+ const WEEKDAY_LABELS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
- var WEEKDAY_CLASS =
+ const WEEKDAY_CLASS =
"w-8 h-6 flex items-center justify-center text-xs text-body select-none";
- var DAY_BASE_CLASS =
+ const DAY_BASE_CLASS =
"date-range-day w-8 h-8 flex items-center justify-center text-sm " +
"text-heading cursor-pointer hover:bg-neutral-tertiary-medium";
- var DAY_ROUNDED_CLASS = "rounded-base";
- var DAY_OUTSIDE_MONTH_CLASS = "opacity-40";
- var DAY_SELECTED_CLASS = "bg-brand text-white hover:bg-brand-strong";
- var DAY_ANCHOR_CLASS =
+ const DAY_ROUNDED_CLASS = "rounded-base";
+ const DAY_OUTSIDE_MONTH_CLASS = "opacity-40";
+ const DAY_SELECTED_CLASS = "bg-brand text-white hover:bg-brand-strong";
+ const DAY_ANCHOR_CLASS =
"bg-brand text-white ring-2 ring-inset ring-brand-strong hover:bg-brand-strong";
// The three visual states of the date range track (the days between the
// two endpoints): outlined while picking the second date, filled once both
// are picked, muted when showing an already-committed range read-only.
- var TRACK_OUTLINED_CLASS = "border-y border-brand/70 bg-brand/10";
- var TRACK_FILLED_CLASS = "bg-brand/30";
- var TRACK_MUTED_CLASS = "bg-brand/15";
+ const TRACK_OUTLINED_CLASS = "border-y border-brand/70 bg-brand/10";
+ const TRACK_FILLED_CLASS = "bg-brand/30";
+ const TRACK_MUTED_CLASS = "bg-brand/15";
// ── Date helpers (all local-time; values are ISO YYYY-MM-DD strings) ──
- function padNumber(value, width) {
- var text = String(value);
+ function padNumber(value: number, width: number): string {
+ let text = String(value);
while (text.length < width) text = "0" + text;
return text;
}
- function isoFromDate(dateObject) {
+ function isoFromDate(dateObject: Date): string {
return (
padNumber(dateObject.getFullYear(), 4) +
"-" +
@@ -60,8 +80,8 @@
);
}
- function dateFromIso(isoString) {
- var pieces = isoString.split("-");
+ function dateFromIso(isoString: string): Date {
+ const pieces = isoString.split("-");
return new Date(
parseInt(pieces[0], 10),
parseInt(pieces[1], 10) - 1,
@@ -69,15 +89,15 @@
);
}
- function addDays(dateObject, dayCount) {
- var copy = new Date(dateObject.getTime());
+ function addDays(dateObject: Date, dayCount: number): Date {
+ const copy = new Date(dateObject.getTime());
copy.setDate(copy.getDate() + dayCount);
return copy;
}
/** Validate a (year, month, day) triple as a real calendar date. */
- function isoFromParts(year, month, day) {
- var candidate = new Date(year, month - 1, day);
+ function isoFromParts(year: number, month: number, day: number): string {
+ const candidate = new Date(year, month - 1, day);
if (
candidate.getFullYear() !== year ||
candidate.getMonth() !== month - 1 ||
@@ -88,12 +108,12 @@
return isoFromDate(candidate);
}
- function presetRange(presetName) {
- var today = new Date();
+ function presetRange(presetName: string): [Date, Date] | null {
+ const today = new Date();
today.setHours(0, 0, 0, 0);
- var yesterday = addDays(today, -1);
- var year = today.getFullYear();
- var month = today.getMonth();
+ const yesterday = addDays(today, -1);
+ const year = today.getFullYear();
+ const month = today.getMonth();
switch (presetName) {
case "today":
return [today, today];
@@ -116,42 +136,42 @@
// ── DateRangeField: segmented manual entry ──────────────────────────────
- function segmentBuffer(segment) {
+ function segmentBuffer(segment: HTMLInputElement): string {
return segment.dataset.typedDigits || "";
}
- function setSegmentBuffer(segment, buffer) {
+ function setSegmentBuffer(segment: HTMLInputElement, buffer: string): void {
segment.dataset.typedDigits = buffer;
if (buffer === "") {
segment.value = "";
return;
}
- var placeholder = segment.getAttribute("placeholder");
+ const placeholder = segment.getAttribute("placeholder") ?? "";
// Fill the placeholder from the right: typing 19 into YYYY shows YY19.
segment.value = placeholder.slice(0, placeholder.length - buffer.length) + buffer;
}
- function segmentsForSide(picker, side) {
- return Array.prototype.slice.call(
- picker.querySelectorAll('input[data-date-side="' + side + '"]')
+ function segmentsForSide(picker: HTMLElement, side: string): HTMLInputElement[] {
+ return Array.from(
+ picker.querySelectorAll(`input[data-date-side="${side}"]`)
);
}
/** Recompute one hidden ISO input from its side's segment buffers. */
- function syncHiddenFromSegments(picker, side) {
- var hidden = picker.querySelector(
- 'input[data-date-range-hidden="' + side + '"]'
- );
- var partValues = {};
- var complete = true;
- segmentsForSide(picker, side).forEach(function (segment) {
- var buffer = segmentBuffer(segment);
- if (buffer.length !== parseInt(segment.getAttribute("maxlength"), 10)) {
+ function syncHiddenFromSegments(picker: HTMLElement, side: string): boolean {
+ const hidden = picker.querySelector(
+ `input[data-date-range-hidden="${side}"]`
+ )!;
+ const partValues: Record = {};
+ let complete = true;
+ segmentsForSide(picker, side).forEach((segment) => {
+ const buffer = segmentBuffer(segment);
+ if (buffer.length !== parseInt(segment.getAttribute("maxlength") ?? "", 10)) {
complete = false;
}
- partValues[segment.dataset.datePart] = buffer;
+ partValues[segment.dataset.datePart ?? ""] = buffer;
});
- var previousValue = hidden.value;
+ const previousValue = hidden.value;
if (complete) {
hidden.value = isoFromParts(
parseInt(partValues.year, 10),
@@ -165,69 +185,70 @@
}
/** Push an ISO value (or "") into a side's segments and hidden input. */
- function setSideValue(picker, side, isoString) {
- var hidden = picker.querySelector(
- 'input[data-date-range-hidden="' + side + '"]'
- );
+ function setSideValue(picker: HTMLElement, side: string, isoString: string): void {
+ const hidden = picker.querySelector(
+ `input[data-date-range-hidden="${side}"]`
+ )!;
hidden.value = isoString;
- var partValues = { year: "", month: "", day: "" };
+ let partValues: Record = { year: "", month: "", day: "" };
if (isoString) {
- var pieces = isoString.split("-");
+ const pieces = isoString.split("-");
partValues = { year: pieces[0], month: pieces[1], day: pieces[2] };
}
- segmentsForSide(picker, side).forEach(function (segment) {
- setSegmentBuffer(segment, partValues[segment.dataset.datePart]);
+ segmentsForSide(picker, side).forEach((segment) => {
+ setSegmentBuffer(segment, partValues[segment.dataset.datePart ?? ""]);
});
}
- function initField(picker, calendarState) {
- var field = picker.querySelector("[data-date-range-field]");
- var segments = Array.prototype.slice.call(
- picker.querySelectorAll("input[data-date-part]")
+ function initField(picker: HTMLElement, calendarState: CalendarState): void {
+ const field = picker.querySelector("[data-date-range-field]")!;
+ const segments = Array.from(
+ picker.querySelectorAll("input[data-date-part]")
);
// Adopt server-rendered values (prefilled filter) as typed buffers.
- segments.forEach(function (segment) {
+ segments.forEach((segment) => {
if (segment.value) setSegmentBuffer(segment, segment.value);
});
// Clicking anywhere in the container that is not a date part activates
// the first date part.
- field.addEventListener("mousedown", function (event) {
- if (event.target.closest("input[data-date-part]")) return;
- if (event.target.closest("[data-date-range-calendar-toggle]")) return;
+ field.addEventListener("mousedown", (event) => {
+ const target = event.target as Element;
+ if (target.closest("input[data-date-part]")) return;
+ if (target.closest("[data-date-range-calendar-toggle]")) return;
event.preventDefault();
segments[0].focus();
});
- segments.forEach(function (segment, segmentIndex) {
- segment.addEventListener("keydown", function (event) {
+ segments.forEach((segment, segmentIndex) => {
+ segment.addEventListener("keydown", (event) => {
if (event.key === "Tab") return; // native Tab / Shift+Tab navigation
if (event.key === "Enter") return; // let the filter form submit
if (event.key === "Backspace" || event.key === "Delete") {
event.preventDefault();
setSegmentBuffer(segment, "");
- syncHiddenFromSegments(picker, segment.dataset.dateSide);
+ syncHiddenFromSegments(picker, segment.dataset.dateSide ?? "");
return;
}
if (event.ctrlKey || event.metaKey || event.altKey) return;
event.preventDefault();
if (!/^[0-9]$/.test(event.key)) return; // only numbers can be typed
- var maximumLength = parseInt(segment.getAttribute("maxlength"), 10);
- var buffer = segmentBuffer(segment);
+ const maximumLength = parseInt(segment.getAttribute("maxlength") ?? "", 10);
+ let buffer = segmentBuffer(segment);
// Typing into an already-full part starts it over.
buffer = buffer.length >= maximumLength ? event.key : buffer + event.key;
setSegmentBuffer(segment, buffer);
- syncHiddenFromSegments(picker, segment.dataset.dateSide);
+ syncHiddenFromSegments(picker, segment.dataset.dateSide ?? "");
if (buffer.length === maximumLength && segmentIndex + 1 < segments.length) {
segments[segmentIndex + 1].focus();
}
});
// Swallow any input that bypassed keydown (e.g. IME/paste).
- segment.addEventListener("input", function () {
+ segment.addEventListener("input", () => {
setSegmentBuffer(segment, segmentBuffer(segment));
});
- segment.addEventListener("focus", function () {
+ segment.addEventListener("focus", () => {
if (calendarState) calendarState.refreshFromField();
});
});
@@ -235,51 +256,47 @@
// ── DateRangeCalendar: popup month grid ────────────────────────────────
- function createCalendarState(picker) {
- var popup = picker.querySelector("[data-date-range-calendar]");
- var grid = popup.querySelector("[data-date-range-grid]");
- var monthLabel = popup.querySelector("[data-date-range-month-label]");
+ function createCalendarState(picker: HTMLElement): CalendarState {
+ const popup = picker.querySelector("[data-date-range-calendar]")!;
+ const grid = popup.querySelector("[data-date-range-grid]")!;
+ const monthLabel = popup.querySelector("[data-date-range-month-label]")!;
- var today = new Date();
- var state = {
+ const today = new Date();
+
+ function hiddenValue(side: string): string {
+ return picker.querySelector(
+ `input[data-date-range-hidden="${side}"]`
+ )!.value;
+ }
+
+ const state: CalendarState = {
open: false,
viewYear: today.getFullYear(),
viewMonth: today.getMonth(),
startIso: "",
endIso: "",
- // The anchor is the fixed endpoint: "start" while picking the EndDate,
- // "end" once the range is complete (further picks move the StartDate).
anchor: "",
hoverIso: "",
- // True while showing a committed range the user has not edited yet —
- // the track renders muted until the first pick.
readOnly: false,
+ refreshFromField() {
+ if (state.open) return;
+ state.startIso = hiddenValue("min");
+ state.endIso = hiddenValue("max");
+ },
};
- function hiddenValue(side) {
- return picker.querySelector(
- 'input[data-date-range-hidden="' + side + '"]'
- ).value;
- }
-
- state.refreshFromField = function () {
- if (state.open) return;
- state.startIso = hiddenValue("min");
- state.endIso = hiddenValue("max");
- };
-
- function syncSelectionToField() {
+ function syncSelectionToField(): void {
setSideValue(picker, "min", state.startIso);
setSideValue(picker, "max", state.endIso);
}
- function openPopup() {
+ function openPopup(): void {
state.startIso = hiddenValue("min");
state.endIso = hiddenValue("max");
state.anchor = state.startIso && state.endIso ? "end" : state.startIso ? "start" : "";
state.readOnly = Boolean(state.startIso && state.endIso);
state.hoverIso = "";
- var focusDate = state.startIso ? dateFromIso(state.startIso) : new Date();
+ const focusDate = state.startIso ? dateFromIso(state.startIso) : new Date();
state.viewYear = focusDate.getFullYear();
state.viewMonth = focusDate.getMonth();
state.open = true;
@@ -287,13 +304,13 @@
render();
}
- function closePopup() {
+ function closePopup(): void {
state.open = false;
state.hoverIso = "";
popup.classList.add("hidden");
}
- function clearSelection() {
+ function clearSelection(): void {
state.startIso = "";
state.endIso = "";
state.anchor = "";
@@ -312,7 +329,7 @@
* moves the StartDate (extend/shorten); a pick after it clears the
* range and restarts from the clicked date
*/
- function pickDate(isoString) {
+ function pickDate(isoString: string): void {
state.readOnly = false;
if (!state.startIso) {
state.startIso = isoString;
@@ -339,8 +356,8 @@
render();
}
- function applyPreset(presetName) {
- var range = presetRange(presetName);
+ function applyPreset(presetName: string): void {
+ const range = presetRange(presetName);
if (!range) return;
state.startIso = isoFromDate(range[0]);
state.endIso = isoFromDate(range[1]);
@@ -355,28 +372,32 @@
/** The (inclusive-exclusive of endpoints) track between the two range
* ends; while picking the second date the hovered day acts as the
* provisional other end. */
- function trackBounds() {
+ function trackBounds(): [string, string, string] | null {
if (state.startIso && state.endIso) {
- return [state.startIso, state.endIso, state.readOnly ? TRACK_MUTED_CLASS : TRACK_FILLED_CLASS];
+ return [
+ state.startIso,
+ state.endIso,
+ state.readOnly ? TRACK_MUTED_CLASS : TRACK_FILLED_CLASS,
+ ];
}
if (state.startIso && state.hoverIso && state.hoverIso !== state.startIso) {
- var lower = state.hoverIso < state.startIso ? state.hoverIso : state.startIso;
- var upper = state.hoverIso < state.startIso ? state.startIso : state.hoverIso;
+ const lower = state.hoverIso < state.startIso ? state.hoverIso : state.startIso;
+ const upper = state.hoverIso < state.startIso ? state.startIso : state.hoverIso;
return [lower, upper, TRACK_OUTLINED_CLASS];
}
return null;
}
- function dayCellClass(isoString, inViewMonth) {
- var classes = [DAY_BASE_CLASS];
- var isStart = isoString === state.startIso;
- var isEnd = isoString === state.endIso;
- var isAnchor =
+ function dayCellClass(isoString: string, inViewMonth: boolean): string {
+ const classes = [DAY_BASE_CLASS];
+ const isStart = isoString === state.startIso;
+ const isEnd = isoString === state.endIso;
+ const isAnchor =
(state.anchor === "start" && isStart) || (state.anchor === "end" && isEnd);
- var track = trackBounds();
- var inTrack = track && isoString > track[0] && isoString < track[1];
+ const track = trackBounds();
+ const inTrack = track !== null && isoString > track[0] && isoString < track[1];
if (inTrack) {
- classes.push(track[2]);
+ classes.push(track![2]);
} else {
classes.push(DAY_ROUNDED_CLASS);
}
@@ -390,7 +411,7 @@
return classes.join(" ");
}
- function render() {
+ function render(): void {
monthLabel.textContent = new Date(
state.viewYear,
state.viewMonth,
@@ -398,20 +419,20 @@
).toLocaleDateString(undefined, { month: "long", year: "numeric" });
grid.textContent = "";
- WEEKDAY_LABELS.forEach(function (weekdayLabel) {
- var headerCell = document.createElement("span");
+ WEEKDAY_LABELS.forEach((weekdayLabel) => {
+ const headerCell = document.createElement("span");
headerCell.className = WEEKDAY_CLASS;
headerCell.textContent = weekdayLabel;
grid.appendChild(headerCell);
});
- var firstOfMonth = new Date(state.viewYear, state.viewMonth, 1);
+ const firstOfMonth = new Date(state.viewYear, state.viewMonth, 1);
// Monday-first offset of the leading overflow days.
- var leadingDays = (firstOfMonth.getDay() + 6) % 7;
- var cellDate = addDays(firstOfMonth, -leadingDays);
- for (var cellIndex = 0; cellIndex < 42; cellIndex++) {
- var isoString = isoFromDate(cellDate);
- var dayButton = document.createElement("button");
+ const leadingDays = (firstOfMonth.getDay() + 6) % 7;
+ let cellDate = addDays(firstOfMonth, -leadingDays);
+ for (let cellIndex = 0; cellIndex < 42; cellIndex++) {
+ const isoString = isoFromDate(cellDate);
+ const dayButton = document.createElement("button");
dayButton.type = "button";
dayButton.setAttribute("data-date", isoString);
dayButton.className = dayCellClass(
@@ -426,30 +447,30 @@
// ── Wiring ──
picker
- .querySelector("[data-date-range-calendar-toggle]")
- .addEventListener("click", function () {
+ .querySelector("[data-date-range-calendar-toggle]")!
+ .addEventListener("click", () => {
if (state.open) closePopup();
else openPopup();
});
- grid.addEventListener("click", function (event) {
- var dayButton = event.target.closest("button[data-date]");
- if (dayButton) pickDate(dayButton.getAttribute("data-date"));
+ grid.addEventListener("click", (event) => {
+ const dayButton = (event.target as Element).closest("button[data-date]");
+ if (dayButton) pickDate(dayButton.getAttribute("data-date") ?? "");
});
- grid.addEventListener("mouseover", function (event) {
+ grid.addEventListener("mouseover", (event) => {
if (!state.startIso || state.endIso) return;
- var dayButton = event.target.closest("button[data-date]");
+ const dayButton = (event.target as Element).closest("button[data-date]");
if (!dayButton) return;
- var hoveredIso = dayButton.getAttribute("data-date");
+ const hoveredIso = dayButton.getAttribute("data-date") ?? "";
if (hoveredIso === state.hoverIso) return;
state.hoverIso = hoveredIso;
render();
});
popup
- .querySelector("[data-date-range-prev]")
- .addEventListener("click", function () {
+ .querySelector("[data-date-range-prev]")!
+ .addEventListener("click", () => {
state.viewMonth -= 1;
if (state.viewMonth < 0) {
state.viewMonth = 11;
@@ -459,8 +480,8 @@
});
popup
- .querySelector("[data-date-range-next]")
- .addEventListener("click", function () {
+ .querySelector("[data-date-range-next]")!
+ .addEventListener("click", () => {
state.viewMonth += 1;
if (state.viewMonth > 11) {
state.viewMonth = 0;
@@ -469,62 +490,50 @@
render();
});
- popup.querySelectorAll("[data-date-range-preset]").forEach(function (button) {
- button.addEventListener("click", function () {
- applyPreset(button.getAttribute("data-date-range-preset"));
+ popup.querySelectorAll("[data-date-range-preset]").forEach((button) => {
+ button.addEventListener("click", () => {
+ applyPreset(button.getAttribute("data-date-range-preset") ?? "");
});
});
// Cancel: close the popup and clear the selected dates.
popup
- .querySelector("[data-date-range-cancel]")
- .addEventListener("click", function () {
+ .querySelector("[data-date-range-cancel]")!
+ .addEventListener("click", () => {
clearSelection();
closePopup();
});
// Clear: clear the selected dates but keep the popup open.
popup
- .querySelector("[data-date-range-clear]")
- .addEventListener("click", function () {
+ .querySelector("[data-date-range-clear]")!
+ .addEventListener("click", () => {
clearSelection();
render();
});
// Select: close the popup, keeping the selected dates.
popup
- .querySelector("[data-date-range-select]")
- .addEventListener("click", function () {
+ .querySelector("[data-date-range-select]")!
+ .addEventListener("click", () => {
closePopup();
});
- document.addEventListener("keydown", function (event) {
+ document.addEventListener("keydown", (event) => {
if (event.key === "Escape" && state.open) closePopup();
});
- document.addEventListener("mousedown", function (event) {
- if (state.open && !picker.contains(event.target)) closePopup();
+ document.addEventListener("mousedown", (event) => {
+ if (state.open && !picker.contains(event.target as Node)) closePopup();
});
return state;
}
- function initPicker(picker) {
- if (picker.dataset.dateRangePickerInitialized) return;
- picker.dataset.dateRangePickerInitialized = "true";
- var calendarState = createCalendarState(picker);
+ function initPicker(picker: HTMLElement): void {
+ const calendarState = createCalendarState(picker);
initField(picker, calendarState);
}
- function initAllPickers() {
- document.querySelectorAll("[data-date-range-picker]").forEach(initPicker);
- }
-
- window.initDateRangePickers = initAllPickers;
-
- if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", initAllPickers);
- } else {
- initAllPickers();
- }
+ onSwap("[data-date-range-picker]", (picker) => initPicker(picker as HTMLElement));
})();