stimeo-ui 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +184 -0
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.d.ts +3 -5
- package/dist/controllers/aspect_ratio_controller.js +19 -11
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.d.ts +36 -15
- package/dist/controllers/avatar_controller.js +195 -40
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.js +5 -1
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +12 -3
- package/dist/controllers/carousel_controller.js +90 -10
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +31 -5
- package/dist/controllers/checkbox_controller.js +136 -25
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.js +8 -3
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +1 -1
- package/dist/controllers/collapsible_controller.js +4 -1
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +9 -3
- package/dist/controllers/color_picker_controller.js +41 -11
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +1 -1
- package/dist/controllers/context_menu_controller.js +2 -2
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +5 -3
- package/dist/controllers/countdown_controller.js +5 -1
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +23 -4
- package/dist/controllers/date_range_picker_controller.js +162 -31
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +1 -1
- package/dist/controllers/direct_upload_controller.js +3 -3
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.d.ts +27 -7
- package/dist/controllers/empty_state_controller.js +107 -16
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +7 -1
- package/dist/controllers/file_dropzone_controller.js +26 -3
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +25 -5
- package/dist/controllers/flash_controller.js +161 -21
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +2 -3
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +8 -2
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +19 -2
- package/dist/controllers/frame_loading_controller.js +94 -22
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +8 -4
- package/dist/controllers/highlight_controller.js +38 -1
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +2 -2
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +5 -4
- package/dist/controllers/idle_controller.js +39 -6
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.js +2 -0
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +1 -1
- package/dist/controllers/masonry_controller.js +1 -1
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.js +5 -3
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js +3 -1
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +46 -12
- package/dist/controllers/multi_select_controller.js +460 -151
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +15 -13
- package/dist/controllers/network_status_controller.js +1 -3
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +29 -3
- package/dist/controllers/number_input_controller.js +455 -64
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.js +5 -1
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.js +38 -1
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +4 -4
- package/dist/controllers/password_strength_controller.js +21 -3
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +4 -4
- package/dist/controllers/persist_controller.js +24 -5
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.js +10 -0
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +2 -2
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +1 -2
- package/dist/controllers/portal_controller.js +10 -0
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +1 -1
- package/dist/controllers/progress_controller.js +7 -3
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +41 -15
- package/dist/controllers/radio_group_controller.js +540 -56
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +23 -0
- package/dist/controllers/range_slider_controller.js +385 -94
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +36 -31
- package/dist/controllers/rating_controller.js +274 -89
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.js +2 -0
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.js +33 -0
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +4 -0
- package/dist/controllers/roving_controller.js +60 -5
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -1
- package/dist/controllers/scroll_area_controller.js +155 -23
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +33 -0
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/separator_controller.js +13 -17
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +2 -2
- package/dist/controllers/skeleton_controller.js +71 -3
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +17 -1
- package/dist/controllers/slider_controller.js +325 -48
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +15 -10
- package/dist/controllers/spinner_controller.js +18 -3
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +1 -1
- package/dist/controllers/step_indicator_controller.js +3 -1
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.js +2 -0
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +12 -8
- package/dist/controllers/switch_controller.js +162 -18
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +41 -11
- package/dist/controllers/tags_input_controller.js +356 -120
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.js +1 -1
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +42 -10
- package/dist/controllers/time_picker_controller.js +296 -104
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +39 -13
- package/dist/controllers/toggle_group_controller.js +378 -55
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +5 -3
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +2 -2
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +2 -3
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +1 -2
- package/dist/controllers/tree_view_controller.js +24 -4
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.js +3811 -1048
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +92 -4
- package/dist/inspector/cli.js +174 -6
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +234 -8
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +24 -24
- package/dist/inspector/manifest.json +573 -59
- package/dist/positioning/index.d.ts +2 -2
- package/dist/positioning/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -18,6 +18,35 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
18
18
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// src/utils/before_cache_reset.ts
|
|
22
|
+
var BeforeCacheReset = class _BeforeCacheReset {
|
|
23
|
+
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
24
|
+
static #subscribers = /* @__PURE__ */ new Set();
|
|
25
|
+
/** The shared listener; installed while at least one instance is subscribed. */
|
|
26
|
+
static #onBeforeCache = () => {
|
|
27
|
+
for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
|
|
28
|
+
};
|
|
29
|
+
#rewind;
|
|
30
|
+
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
31
|
+
constructor(rewind) {
|
|
32
|
+
this.#rewind = rewind;
|
|
33
|
+
}
|
|
34
|
+
/** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
|
|
35
|
+
activate() {
|
|
36
|
+
const first = _BeforeCacheReset.#subscribers.size === 0;
|
|
37
|
+
_BeforeCacheReset.#subscribers.add(this);
|
|
38
|
+
if (first) {
|
|
39
|
+
document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
|
|
43
|
+
deactivate() {
|
|
44
|
+
_BeforeCacheReset.#subscribers.delete(this);
|
|
45
|
+
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
46
|
+
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
21
50
|
// src/utils/dates.ts
|
|
22
51
|
function toISODateString(date) {
|
|
23
52
|
const year = date.getFullYear();
|
|
@@ -138,16 +167,34 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
138
167
|
}
|
|
139
168
|
};
|
|
140
169
|
|
|
170
|
+
// src/utils/string_list.ts
|
|
171
|
+
function parseStringList(raw, fallback = []) {
|
|
172
|
+
const text = raw.trim();
|
|
173
|
+
if (text.length === 0) return [...fallback];
|
|
174
|
+
let parsed;
|
|
175
|
+
try {
|
|
176
|
+
parsed = JSON.parse(text);
|
|
177
|
+
} catch {
|
|
178
|
+
return [...fallback];
|
|
179
|
+
}
|
|
180
|
+
if (!Array.isArray(parsed)) return [...fallback];
|
|
181
|
+
return parsed.filter((entry) => typeof entry === "string");
|
|
182
|
+
}
|
|
183
|
+
|
|
141
184
|
// src/controllers/date_range_picker_controller.ts
|
|
142
185
|
var GRID_SIZE = 42;
|
|
143
186
|
var DateRangePickerController = class extends Controller {
|
|
144
187
|
static targets = ["grid", "monthLabel", "cell", "status", "startField", "endField"];
|
|
145
188
|
static values = {
|
|
146
189
|
min: { type: String, default: "" },
|
|
147
|
-
max: { type: String, default: "" }
|
|
190
|
+
max: { type: String, default: "" },
|
|
191
|
+
// A JSON list read through `parseStringList` rather than Stimulus's `Array`
|
|
192
|
+
// type: that reader throws out of the value observer before any callback
|
|
193
|
+
// runs, so one malformed attribute would stop the picker from connecting.
|
|
194
|
+
disabledDates: { type: String, default: "" }
|
|
148
195
|
};
|
|
149
196
|
static actions = ["applyPreset", "next", "onKeydown", "prev", "previewTo", "selectDate"];
|
|
150
|
-
static events = ["change"];
|
|
197
|
+
static events = ["change", "monthchange"];
|
|
151
198
|
/** The month currently rendered, as `YYYY-MM`. */
|
|
152
199
|
#viewMonth = "";
|
|
153
200
|
/** The confirmed range endpoints (ISO), or "" when unset. */
|
|
@@ -161,7 +208,12 @@ var DateRangePickerController = class extends Controller {
|
|
|
161
208
|
#focusedDate = /* @__PURE__ */ new Date();
|
|
162
209
|
/** Deferred focus after an async month transition (cancelled on teardown). */
|
|
163
210
|
#focusTimer = new SafeTimeout();
|
|
164
|
-
/**
|
|
211
|
+
/** The declared unavailable dates, indexed for the per-cell paint lookup. */
|
|
212
|
+
#disabledDates = /* @__PURE__ */ new Set();
|
|
213
|
+
/** Rewinds an unfinished selection before Turbo snapshots the page. */
|
|
214
|
+
#beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
|
|
215
|
+
/** Last painted month, or `null` until the initial paint has settled. */
|
|
216
|
+
#announcedMonth = null;
|
|
165
217
|
/**
|
|
166
218
|
* Collapses a morph that swaps render inputs into one repaint, and refuses the
|
|
167
219
|
* pass Stimulus delivers before `connect()`.
|
|
@@ -169,10 +221,17 @@ var DateRangePickerController = class extends Controller {
|
|
|
169
221
|
#repaint = new MicrotaskCoalescer(() => {
|
|
170
222
|
this.#render();
|
|
171
223
|
});
|
|
224
|
+
/** Seeds and normalizes the range from optional hidden fields, then paints the grid. */
|
|
172
225
|
connect() {
|
|
173
226
|
this.#repaint.activate();
|
|
174
|
-
this.#
|
|
175
|
-
|
|
227
|
+
this.#beforeCache.activate();
|
|
228
|
+
const authoredStart = this.hasStartFieldTarget ? normalizeISO(this.startFieldTarget.value) : "";
|
|
229
|
+
const authoredEnd = this.hasEndFieldTarget ? normalizeISO(this.endFieldTarget.value) : "";
|
|
230
|
+
[this.#startDate, this.#endDate] = orderRange(authoredStart, authoredEnd);
|
|
231
|
+
this.#pendingStart = "";
|
|
232
|
+
this.#previewDate = "";
|
|
233
|
+
this.#announcedMonth = null;
|
|
234
|
+
this.#commitFields();
|
|
176
235
|
const anchor = parseISODateString(this.#startDate) ?? this.#clampToBounds(/* @__PURE__ */ new Date()) ?? /* @__PURE__ */ new Date();
|
|
177
236
|
this.#focusedDate = anchor;
|
|
178
237
|
this.#viewMonth = toISOMonthString(anchor);
|
|
@@ -182,6 +241,7 @@ var DateRangePickerController = class extends Controller {
|
|
|
182
241
|
/** Cancels any pending deferred focus so it never fires on a detached element. */
|
|
183
242
|
disconnect() {
|
|
184
243
|
this.#repaint.cancel();
|
|
244
|
+
this.#beforeCache.deactivate();
|
|
185
245
|
this.#focusTimer.clearAll();
|
|
186
246
|
}
|
|
187
247
|
/** Repaints when application code (or a Turbo morph) changes `min` at runtime. */
|
|
@@ -192,6 +252,11 @@ var DateRangePickerController = class extends Controller {
|
|
|
192
252
|
maxValueChanged() {
|
|
193
253
|
this.#repaint.schedule();
|
|
194
254
|
}
|
|
255
|
+
/** Re-indexes the unavailable dates and repaints when the declared set changes. */
|
|
256
|
+
disabledDatesValueChanged() {
|
|
257
|
+
this.#disabledDates = new Set(parseStringList(this.disabledDatesValue));
|
|
258
|
+
this.#repaint.schedule();
|
|
259
|
+
}
|
|
195
260
|
/** Navigates to the previous month. */
|
|
196
261
|
prev(event) {
|
|
197
262
|
event?.preventDefault();
|
|
@@ -207,7 +272,7 @@ var DateRangePickerController = class extends Controller {
|
|
|
207
272
|
const cell = this.#cellFrom(event.target);
|
|
208
273
|
if (!cell) return;
|
|
209
274
|
const date = cell.getAttribute("data-date");
|
|
210
|
-
if (!date ||
|
|
275
|
+
if (!date || !this.#isSelectable(date)) return;
|
|
211
276
|
this.#choose(date);
|
|
212
277
|
}
|
|
213
278
|
/** Previews the range up to a hovered/focused cell while selecting. */
|
|
@@ -216,28 +281,27 @@ var DateRangePickerController = class extends Controller {
|
|
|
216
281
|
if (!cell) return;
|
|
217
282
|
const date = cell.getAttribute("data-date");
|
|
218
283
|
if (!date) return;
|
|
284
|
+
let shouldRender = false;
|
|
219
285
|
if (event.type.startsWith("focus")) {
|
|
220
286
|
const parsed = parseISODateString(date);
|
|
221
287
|
if (parsed) this.#focusedDate = parsed;
|
|
222
|
-
|
|
223
|
-
this.#render();
|
|
224
|
-
}
|
|
288
|
+
shouldRender = cell.getAttribute("tabindex") !== "0";
|
|
225
289
|
}
|
|
226
|
-
if (this.#pendingStart) {
|
|
290
|
+
if (this.#pendingStart && this.#isSelectable(date) && this.#previewDate !== date) {
|
|
227
291
|
this.#previewDate = date;
|
|
228
|
-
|
|
292
|
+
shouldRender = true;
|
|
229
293
|
}
|
|
294
|
+
if (shouldRender) this.#render();
|
|
230
295
|
}
|
|
231
296
|
/** Applies a named preset (`today` / `last7` / `last30` / `thisMonth`). */
|
|
232
297
|
applyPreset(event) {
|
|
233
298
|
const button = event.target?.closest("[data-range]");
|
|
234
|
-
const
|
|
235
|
-
if (!name) return;
|
|
236
|
-
const range = computePreset(name);
|
|
299
|
+
const range = computePreset(button?.getAttribute("data-range") ?? "");
|
|
237
300
|
if (!range) return;
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
301
|
+
const intersection = this.#intersectRange(range);
|
|
302
|
+
if (!intersection) return;
|
|
303
|
+
const { start, end } = intersection;
|
|
304
|
+
if (!this.#isSelectable(start) || !this.#isSelectable(end)) return;
|
|
241
305
|
this.#startDate = start;
|
|
242
306
|
this.#endDate = end;
|
|
243
307
|
this.#pendingStart = "";
|
|
@@ -255,12 +319,12 @@ var DateRangePickerController = class extends Controller {
|
|
|
255
319
|
if (isReservedArrowChord(event)) return;
|
|
256
320
|
const cell = this.#cellFrom(event.target);
|
|
257
321
|
if (!cell) return;
|
|
258
|
-
const dateStr = cell.getAttribute("data-date");
|
|
259
|
-
const date =
|
|
322
|
+
const dateStr = cell.getAttribute("data-date") ?? "";
|
|
323
|
+
const date = parseISODateString(dateStr);
|
|
260
324
|
if (!date) return;
|
|
261
325
|
if (event.key === "Enter" || event.key === " ") {
|
|
262
326
|
event.preventDefault();
|
|
263
|
-
if (dateStr
|
|
327
|
+
if (this.#isSelectable(dateStr)) this.#choose(dateStr);
|
|
264
328
|
return;
|
|
265
329
|
}
|
|
266
330
|
if (event.key === "Escape") {
|
|
@@ -293,10 +357,10 @@ var DateRangePickerController = class extends Controller {
|
|
|
293
357
|
next = addDays(date, 6 - date.getDay());
|
|
294
358
|
break;
|
|
295
359
|
case "PageUp":
|
|
296
|
-
next = shiftMonthClamped(date, -1);
|
|
360
|
+
next = event.shiftKey ? shiftYearClamped(date, -1) : shiftMonthClamped(date, -1);
|
|
297
361
|
break;
|
|
298
362
|
case "PageDown":
|
|
299
|
-
next = shiftMonthClamped(date, 1);
|
|
363
|
+
next = event.shiftKey ? shiftYearClamped(date, 1) : shiftMonthClamped(date, 1);
|
|
300
364
|
break;
|
|
301
365
|
default:
|
|
302
366
|
return;
|
|
@@ -327,12 +391,14 @@ var DateRangePickerController = class extends Controller {
|
|
|
327
391
|
/** Moves roving focus to `date`, transitioning the month when needed. */
|
|
328
392
|
#moveFocusTo(date) {
|
|
329
393
|
this.#focusedDate = date;
|
|
330
|
-
|
|
331
|
-
this.#
|
|
394
|
+
const iso = toISODateString(date);
|
|
395
|
+
if (this.#pendingStart && this.#isSelectable(iso)) this.#previewDate = iso;
|
|
396
|
+
this.#transitionTo(toISOMonthString(date), iso);
|
|
332
397
|
}
|
|
333
398
|
/** Renders `month`, then focuses the cell for `dateStr` (deferred if async). */
|
|
334
399
|
#transitionTo(month, dateStr) {
|
|
335
400
|
const isTransition = month !== this.#viewMonth;
|
|
401
|
+
this.#focusTimer.clearAll();
|
|
336
402
|
this.#viewMonth = month;
|
|
337
403
|
this.#render();
|
|
338
404
|
const focusCell = () => {
|
|
@@ -355,22 +421,28 @@ var DateRangePickerController = class extends Controller {
|
|
|
355
421
|
this.#focusedDate = target;
|
|
356
422
|
this.#render();
|
|
357
423
|
}
|
|
358
|
-
/**
|
|
424
|
+
/**
|
|
425
|
+
* Builds the six-week grid and binds range/roving/disabled state per cell.
|
|
426
|
+
*
|
|
427
|
+
* @stimeoRenderRoot
|
|
428
|
+
*/
|
|
359
429
|
#render() {
|
|
360
430
|
const info = parseISOMonthString(this.#viewMonth);
|
|
361
431
|
if (!info) return;
|
|
362
432
|
const { year, month } = info;
|
|
433
|
+
if (this.#previewDate && !this.#isSelectable(this.#previewDate)) this.#previewDate = "";
|
|
363
434
|
if (this.hasMonthLabelTarget) {
|
|
364
435
|
const lang = document.documentElement.lang || "en";
|
|
365
|
-
const formatter =
|
|
436
|
+
const formatter = monthFormatter(lang);
|
|
366
437
|
this.monthLabelTarget.textContent = formatter.format(new Date(year, month - 1, 1));
|
|
367
438
|
}
|
|
368
439
|
const [rangeStart, rangeEnd] = this.#visualRange();
|
|
369
440
|
const days = gridDays(year, month);
|
|
370
441
|
const focusedStr = toISODateString(this.#focusedDate);
|
|
371
442
|
const todayStr = toISODateString(/* @__PURE__ */ new Date());
|
|
443
|
+
const cells = this.cellTargets;
|
|
372
444
|
for (let i = 0; i < GRID_SIZE; i++) {
|
|
373
|
-
const el =
|
|
445
|
+
const el = cells[i];
|
|
374
446
|
const date = days[i];
|
|
375
447
|
if (!el || !date) continue;
|
|
376
448
|
const iso = toISODateString(date);
|
|
@@ -379,8 +451,8 @@ var DateRangePickerController = class extends Controller {
|
|
|
379
451
|
el.setAttribute("data-outside", String(date.getMonth() !== month - 1));
|
|
380
452
|
el.setAttribute("data-today", String(iso === todayStr));
|
|
381
453
|
el.setAttribute("tabindex", iso === focusedStr ? "0" : "-1");
|
|
382
|
-
if (this.#
|
|
383
|
-
else el.
|
|
454
|
+
if (this.#isSelectable(iso)) el.removeAttribute("aria-disabled");
|
|
455
|
+
else el.setAttribute("aria-disabled", "true");
|
|
384
456
|
const isStart = !!rangeStart && iso === rangeStart;
|
|
385
457
|
const isEnd = !!rangeEnd && iso === rangeEnd && rangeEnd !== rangeStart;
|
|
386
458
|
const inside = !!rangeStart && !!rangeEnd && iso > rangeStart && iso < rangeEnd;
|
|
@@ -394,6 +466,16 @@ var DateRangePickerController = class extends Controller {
|
|
|
394
466
|
)
|
|
395
467
|
);
|
|
396
468
|
}
|
|
469
|
+
for (const extra of cells.slice(GRID_SIZE)) extra.setAttribute("tabindex", "-1");
|
|
470
|
+
if (!cells.some((cell) => cell.getAttribute("tabindex") === "0")) {
|
|
471
|
+
const fallback = cells.find((cell) => cell.getAttribute("data-outside") === "false") ?? cells[0];
|
|
472
|
+
fallback?.setAttribute("tabindex", "0");
|
|
473
|
+
}
|
|
474
|
+
const previous = this.#announcedMonth;
|
|
475
|
+
this.#announcedMonth = this.#viewMonth;
|
|
476
|
+
if (previous !== null && previous !== this.#viewMonth) {
|
|
477
|
+
this.dispatch("monthchange", { detail: { month: this.#viewMonth } });
|
|
478
|
+
}
|
|
397
479
|
}
|
|
398
480
|
/** The ordered [start, end] pair to paint: the preview while selecting, else confirmed. */
|
|
399
481
|
#visualRange() {
|
|
@@ -414,15 +496,25 @@ var DateRangePickerController = class extends Controller {
|
|
|
414
496
|
this.statusTarget.textContent = `${this.#startDate} \u2013 ${this.#endDate}`;
|
|
415
497
|
}
|
|
416
498
|
}
|
|
499
|
+
/**
|
|
500
|
+
* True when `iso` may be chosen as a range endpoint.
|
|
501
|
+
*
|
|
502
|
+
* The single availability question in the controller: the grid paint, the
|
|
503
|
+
* preview, and both commit paths ask it, so a cell's `aria-disabled` and what
|
|
504
|
+
* a click on that cell does are the same decision rather than two that have to
|
|
505
|
+
* be kept in step.
|
|
506
|
+
*/
|
|
507
|
+
#isSelectable(iso) {
|
|
508
|
+
return !this.#outOfBounds(iso) && !this.#disabledDates.has(iso);
|
|
509
|
+
}
|
|
417
510
|
/** True when `iso` falls outside the `[min, max]` bounds. */
|
|
418
511
|
#outOfBounds(iso) {
|
|
419
512
|
if (this.minValue && iso < this.minValue) return true;
|
|
420
513
|
if (this.maxValue && iso > this.maxValue) return true;
|
|
421
514
|
return false;
|
|
422
515
|
}
|
|
423
|
-
/** Clamps
|
|
516
|
+
/** Clamps a generated ISO date string into `[min, max]`. */
|
|
424
517
|
#clampISO(iso) {
|
|
425
|
-
if (!iso) return "";
|
|
426
518
|
if (this.minValue && iso < this.minValue) return this.minValue;
|
|
427
519
|
if (this.maxValue && iso > this.maxValue) return this.maxValue;
|
|
428
520
|
return iso;
|
|
@@ -432,6 +524,27 @@ var DateRangePickerController = class extends Controller {
|
|
|
432
524
|
const clamped = this.#clampISO(toISODateString(date));
|
|
433
525
|
return parseISODateString(clamped);
|
|
434
526
|
}
|
|
527
|
+
/**
|
|
528
|
+
* Intersects a preset with `[min, max]`, rejecting a disjoint interval.
|
|
529
|
+
*
|
|
530
|
+
* Only the bounds narrow a preset here: `disabledDates` excludes single days,
|
|
531
|
+
* not sub-intervals, so it cannot shrink one to a still-contiguous range. A
|
|
532
|
+
* day it excludes is still refused as an endpoint — the caller checks that —
|
|
533
|
+
* but a preset that merely spans one keeps its span.
|
|
534
|
+
*/
|
|
535
|
+
#intersectRange(range) {
|
|
536
|
+
const start = this.minValue && range.start < this.minValue ? this.minValue : range.start;
|
|
537
|
+
const end = this.maxValue && range.end > this.maxValue ? this.maxValue : range.end;
|
|
538
|
+
return start <= end ? { start, end } : null;
|
|
539
|
+
}
|
|
540
|
+
/** Removes provisional range state before Turbo freezes a cached snapshot. */
|
|
541
|
+
#rewindForCache() {
|
|
542
|
+
this.#focusTimer.clearAll();
|
|
543
|
+
if (!this.#pendingStart && !this.#previewDate) return;
|
|
544
|
+
this.#pendingStart = "";
|
|
545
|
+
this.#previewDate = "";
|
|
546
|
+
this.#render();
|
|
547
|
+
}
|
|
435
548
|
/** Resolves the cell element from an event target, or null. */
|
|
436
549
|
#cellFrom(target) {
|
|
437
550
|
return target?.closest(
|
|
@@ -450,6 +563,12 @@ function shiftMonthClamped(date, delta) {
|
|
|
450
563
|
target.setDate(Math.min(date.getDate(), lastDay));
|
|
451
564
|
return target;
|
|
452
565
|
}
|
|
566
|
+
function shiftYearClamped(date, delta) {
|
|
567
|
+
const target = new Date(date.getFullYear() + delta, date.getMonth(), 1);
|
|
568
|
+
const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
|
|
569
|
+
target.setDate(Math.min(date.getDate(), lastDay));
|
|
570
|
+
return target;
|
|
571
|
+
}
|
|
453
572
|
function gridDays(year, month) {
|
|
454
573
|
const first = new Date(year, month - 1, 1);
|
|
455
574
|
const start = new Date(first);
|
|
@@ -466,6 +585,18 @@ function normalizeISO(value) {
|
|
|
466
585
|
const date = parseISODateString(value.trim());
|
|
467
586
|
return date ? toISODateString(date) : "";
|
|
468
587
|
}
|
|
588
|
+
function orderRange(start, end) {
|
|
589
|
+
return start && end && end < start ? [end, start] : [start, end];
|
|
590
|
+
}
|
|
591
|
+
function monthFormatter(locale) {
|
|
592
|
+
const options = { month: "long", year: "numeric" };
|
|
593
|
+
try {
|
|
594
|
+
return new Intl.DateTimeFormat(locale, options);
|
|
595
|
+
} catch (error) {
|
|
596
|
+
if (!(error instanceof RangeError)) throw error;
|
|
597
|
+
return new Intl.DateTimeFormat("en", options);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
469
600
|
function computePreset(name) {
|
|
470
601
|
const today = /* @__PURE__ */ new Date();
|
|
471
602
|
const todayStr = toISODateString(today);
|