@stimulus-plumbers/controllers 0.2.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.
@@ -0,0 +1,1459 @@
1
+ import { Controller as u } from "@hotwired/stimulus";
2
+ const H = [
3
+ "a[href]",
4
+ "area[href]",
5
+ "button:not([disabled])",
6
+ "input:not([disabled])",
7
+ "select:not([disabled])",
8
+ "textarea:not([disabled])",
9
+ '[tabindex]:not([tabindex="-1"])',
10
+ "audio[controls]",
11
+ "video[controls]",
12
+ '[contenteditable]:not([contenteditable="false"])'
13
+ ].join(",");
14
+ function V(s) {
15
+ return Array.from(s.querySelectorAll(H)).filter((t) => v(t));
16
+ }
17
+ function v(s) {
18
+ return !!(s.offsetWidth || s.offsetHeight || s.getClientRects().length);
19
+ }
20
+ function $(s) {
21
+ const t = V(s);
22
+ return t.length > 0 ? (t[0].focus(), !0) : !1;
23
+ }
24
+ class j {
25
+ constructor(t, e = {}) {
26
+ this.container = t, this.previouslyFocused = null, this.options = e, this.isActive = !1;
27
+ }
28
+ activate() {
29
+ this.isActive || (this.previouslyFocused = document.activeElement, this.isActive = !0, this.options.initialFocus ? this.options.initialFocus.focus() : $(this.container), this.container.addEventListener("keydown", this.handleKeyDown));
30
+ }
31
+ deactivate() {
32
+ if (!this.isActive) return;
33
+ this.isActive = !1, this.container.removeEventListener("keydown", this.handleKeyDown);
34
+ const t = this.options.returnFocus || this.previouslyFocused;
35
+ t && v(t) && t.focus();
36
+ }
37
+ handleKeyDown = (t) => {
38
+ if (t.key === "Escape" && this.options.escapeDeactivates) {
39
+ t.preventDefault(), this.deactivate();
40
+ return;
41
+ }
42
+ if (t.key !== "Tab") return;
43
+ const e = V(this.container);
44
+ if (e.length === 0) return;
45
+ const i = e[0], n = e[e.length - 1];
46
+ t.shiftKey && document.activeElement === i ? (t.preventDefault(), n.focus()) : !t.shiftKey && document.activeElement === n && (t.preventDefault(), i.focus());
47
+ };
48
+ }
49
+ class ot {
50
+ constructor() {
51
+ this.savedElement = null;
52
+ }
53
+ save() {
54
+ this.savedElement = document.activeElement;
55
+ }
56
+ restore() {
57
+ this.savedElement && v(this.savedElement) && (this.savedElement.focus(), this.savedElement = null);
58
+ }
59
+ }
60
+ function lt(s, t) {
61
+ return s.key === t;
62
+ }
63
+ function ht(s) {
64
+ return s.key === "Enter" || s.key === " ";
65
+ }
66
+ function ct(s) {
67
+ return ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(s.key);
68
+ }
69
+ function dt(s) {
70
+ s.preventDefault(), s.stopPropagation();
71
+ }
72
+ class ut {
73
+ constructor(t, e = 0) {
74
+ this.items = t, this.currentIndex = e, this.updateTabIndex();
75
+ }
76
+ handleKeyDown(t) {
77
+ let e;
78
+ switch (t.key) {
79
+ case "ArrowDown":
80
+ case "ArrowRight":
81
+ t.preventDefault(), e = (this.currentIndex + 1) % this.items.length;
82
+ break;
83
+ case "ArrowUp":
84
+ case "ArrowLeft":
85
+ t.preventDefault(), e = this.currentIndex === 0 ? this.items.length - 1 : this.currentIndex - 1;
86
+ break;
87
+ case "Home":
88
+ t.preventDefault(), e = 0;
89
+ break;
90
+ case "End":
91
+ t.preventDefault(), e = this.items.length - 1;
92
+ break;
93
+ default:
94
+ return;
95
+ }
96
+ this.setCurrentIndex(e);
97
+ }
98
+ setCurrentIndex(t) {
99
+ t >= 0 && t < this.items.length && (this.currentIndex = t, this.updateTabIndex(), this.items[t].focus());
100
+ }
101
+ updateTabIndex() {
102
+ this.items.forEach((t, e) => {
103
+ t.tabIndex = e === this.currentIndex ? 0 : -1;
104
+ });
105
+ }
106
+ updateItems(t) {
107
+ this.items = t, this.currentIndex = Math.min(this.currentIndex, t.length - 1), this.updateTabIndex();
108
+ }
109
+ }
110
+ const N = (s, t, e) => {
111
+ let i = document.querySelector(`[data-live-region="${s}"]`);
112
+ return i || (i = document.createElement("div"), i.className = "sr-only", i.dataset.liveRegion = s, i.setAttribute("aria-live", s), i.setAttribute("aria-atomic", t.toString()), i.setAttribute("aria-relevant", e), document.body.appendChild(i)), i;
113
+ };
114
+ function k(s, t = {}) {
115
+ const { politeness: e = "polite", atomic: i = !0, relevant: n = "additions text" } = t, a = N(e, i, n);
116
+ a.textContent = "", setTimeout(() => {
117
+ a.textContent = s;
118
+ }, 100);
119
+ }
120
+ const _ = (s = "a11y") => `${s}-${Math.random().toString(36).substr(2, 9)}`, ft = (s, t = "element") => s.id || (s.id = _(t)), b = (s, t, e) => {
121
+ s.setAttribute(t, e.toString());
122
+ }, mt = (s, t) => b(s, "aria-expanded", t), gt = (s, t) => b(s, "aria-pressed", t), yt = (s, t) => b(s, "aria-checked", t);
123
+ function pt(s, t) {
124
+ b(s, "aria-disabled", t), t ? s.setAttribute("tabindex", "-1") : s.removeAttribute("tabindex");
125
+ }
126
+ const P = {
127
+ menu: "menu",
128
+ listbox: "listbox",
129
+ tree: "tree",
130
+ grid: "grid",
131
+ dialog: "dialog"
132
+ }, C = (s, t, e) => {
133
+ Object.entries(t).forEach(([i, n]) => {
134
+ s.setAttribute(i, n), e[i] = n;
135
+ });
136
+ }, p = (s, t, e) => e || !s.hasAttribute(t);
137
+ function q({ trigger: s, target: t, role: e = null, override: i = !1 }) {
138
+ const n = { trigger: {}, target: {} };
139
+ if (!s || !t) return n;
140
+ const a = {}, r = {};
141
+ if (e && p(t, "role", i) && (r.role = e), t.id && (p(s, "aria-controls", i) && (a["aria-controls"] = t.id), e === "tooltip" && p(s, "aria-describedby", i) && (a["aria-describedby"] = t.id)), e && p(s, "aria-haspopup", i)) {
142
+ const o = P[e] || "true";
143
+ a["aria-haspopup"] = o;
144
+ }
145
+ return C(t, r, n.target), C(s, a, n.trigger), n;
146
+ }
147
+ const E = (s, t) => {
148
+ t.forEach((e) => {
149
+ s.hasAttribute(e) && s.removeAttribute(e);
150
+ });
151
+ };
152
+ function bt({ trigger: s, target: t, attributes: e = null }) {
153
+ if (!s || !t) return;
154
+ E(s, e || ["aria-controls", "aria-haspopup", "aria-describedby"]), (!e || e.includes("role")) && E(t, ["role"]);
155
+ }
156
+ const I = {
157
+ get visibleOnly() {
158
+ return !0;
159
+ },
160
+ hiddenClass: null
161
+ }, R = {
162
+ get top() {
163
+ return "bottom";
164
+ },
165
+ get bottom() {
166
+ return "top";
167
+ },
168
+ get left() {
169
+ return "right";
170
+ },
171
+ get right() {
172
+ return "left";
173
+ }
174
+ };
175
+ function d({ x: s, y: t, width: e, height: i }) {
176
+ return {
177
+ x: s,
178
+ y: t,
179
+ width: e,
180
+ height: i,
181
+ left: s,
182
+ right: s + e,
183
+ top: t,
184
+ bottom: t + i
185
+ };
186
+ }
187
+ function T() {
188
+ return d({
189
+ x: 0,
190
+ y: 0,
191
+ width: window.innerWidth || document.documentElement.clientWidth,
192
+ height: window.innerHeight || document.documentElement.clientHeight
193
+ });
194
+ }
195
+ function B(s) {
196
+ if (!(s instanceof HTMLElement)) return !1;
197
+ const t = T(), e = s.getBoundingClientRect(), i = e.top <= t.height && e.top + e.height > 0, n = e.left <= t.width && e.left + e.width > 0;
198
+ return i && n;
199
+ }
200
+ function g(s) {
201
+ return s instanceof Date && !isNaN(s);
202
+ }
203
+ function m(...s) {
204
+ if (s.length === 0) throw "Missing values to parse as date";
205
+ if (s.length === 1) {
206
+ const t = new Date(s[0]);
207
+ if (s[0] && g(t)) return t;
208
+ } else {
209
+ const t = new Date(...s);
210
+ if (g(t)) return t;
211
+ }
212
+ }
213
+ const z = {
214
+ element: null,
215
+ visible: null,
216
+ dispatch: !0,
217
+ prefix: ""
218
+ };
219
+ class y {
220
+ /**
221
+ * Creates a new Plumber instance.
222
+ * @param {Object} controller - Stimulus controller instance
223
+ * @param {Object} options - Configuration options
224
+ * @param {HTMLElement} [options.element] - Target element (defaults to controller.element)
225
+ * @param {boolean|string} [options.visible=true] - Visibility check configuration
226
+ * @param {boolean} [options.dispatch=true] - Enable event dispatching
227
+ * @param {string} [options.prefix] - Event prefix (defaults to controller.identifier)
228
+ */
229
+ constructor(t, e = {}) {
230
+ this.controller = t;
231
+ const i = Object.assign({}, z, e), { element: n, visible: a, dispatch: r, prefix: o } = i;
232
+ this.element = n || t.element, this.visibleOnly = typeof a == "boolean" ? a : I.visibleOnly, this.visibleCallback = typeof a == "string" ? a : null, this.notify = !!r, this.prefix = typeof o == "string" && o ? o : t.identifier;
233
+ }
234
+ /**
235
+ * Checks if the element is visible in viewport.
236
+ * @returns {boolean} True if element is visible
237
+ */
238
+ get visible() {
239
+ return this.element instanceof HTMLElement ? this.visibleOnly ? B(this.element) && this.isVisible(this.element) : !0 : !1;
240
+ }
241
+ /**
242
+ * Determines if a target element is visible.
243
+ * @param {HTMLElement} target - Element to check
244
+ * @returns {boolean} True if element is visible
245
+ */
246
+ isVisible(t) {
247
+ if (this.visibleCallback) {
248
+ const e = this.findCallback(this.visibleCallback);
249
+ if (typeof e == "function") return e(t);
250
+ }
251
+ return t instanceof HTMLElement ? !t.hasAttribute("hidden") : !1;
252
+ }
253
+ /**
254
+ * Dispatches a custom event from the controller.
255
+ * @param {string} name - Event name
256
+ * @param {Object} [options] - Event options
257
+ * @param {HTMLElement} [options.target] - Event target element
258
+ * @param {string} [options.prefix] - Event prefix
259
+ * @param {*} [options.detail] - Event detail data
260
+ * @returns {boolean|undefined} Dispatch result
261
+ */
262
+ dispatch(t, { target: e = null, prefix: i = null, detail: n = null } = {}) {
263
+ if (this.notify)
264
+ return this.controller.dispatch(t, {
265
+ target: e || this.element,
266
+ prefix: i || this.prefix,
267
+ detail: n
268
+ });
269
+ }
270
+ /**
271
+ * Finds and binds a callback function by name from controller or plumber.
272
+ * @param {string} name - Callback name or dot-notation path
273
+ * @returns {Function|undefined} Bound callback function
274
+ */
275
+ findCallback(t) {
276
+ if (typeof t != "string") return;
277
+ const e = this, i = t.split(".").reduce((a, r) => a && a[r], e.controller);
278
+ if (typeof i == "function")
279
+ return i.bind(e.controller);
280
+ const n = t.split(".").reduce((a, r) => a && a[r], e);
281
+ if (typeof n == "function")
282
+ return n.bind(e);
283
+ }
284
+ /**
285
+ * Executes a callback function and awaits if it returns a Promise.
286
+ * @param {string|Function} callback - Callback name or function
287
+ * @param {...*} args - Arguments to pass to callback
288
+ * @returns {Promise<*>} Result of callback execution
289
+ */
290
+ async awaitCallback(t, ...e) {
291
+ if (typeof t == "string" && (t = this.findCallback(t)), typeof t == "function") {
292
+ const i = t(...e);
293
+ return i instanceof Promise ? await i : i;
294
+ }
295
+ }
296
+ }
297
+ const L = 7, A = {
298
+ locales: ["default"],
299
+ today: "",
300
+ day: null,
301
+ month: null,
302
+ year: null,
303
+ since: null,
304
+ till: null,
305
+ disabledDates: [],
306
+ disabledWeekdays: [],
307
+ disabledDays: [],
308
+ disabledMonths: [],
309
+ disabledYears: [],
310
+ firstDayOfWeek: 0,
311
+ onNavigated: "navigated"
312
+ };
313
+ class K extends y {
314
+ /**
315
+ * Creates a new Calendar plumber instance with date navigation and validation.
316
+ * @param {Object} controller - Stimulus controller instance
317
+ * @param {Object} [options] - Configuration options
318
+ * @param {string[]} [options.locales=['default']] - Locale identifiers for date formatting
319
+ * @param {string|Date} [options.today=''] - Initial "today" date
320
+ * @param {number} [options.day] - Initial day
321
+ * @param {number} [options.month] - Initial month (0-11)
322
+ * @param {number} [options.year] - Initial year
323
+ * @param {string|Date} [options.since] - Minimum selectable date
324
+ * @param {string|Date} [options.till] - Maximum selectable date
325
+ * @param {Array<string|Date>} [options.disabledDates=[]] - Array of disabled dates
326
+ * @param {string[]|number[]} [options.disabledWeekdays=[]] - Array of disabled weekdays
327
+ * @param {string[]|number[]} [options.disabledDays=[]] - Array of disabled day numbers
328
+ * @param {string[]|number[]} [options.disabledMonths=[]] - Array of disabled months
329
+ * @param {string[]|number[]} [options.disabledYears=[]] - Array of disabled years
330
+ * @param {number} [options.firstDayOfWeek=0] - First day of week (0=Sunday, 1=Monday, etc.)
331
+ * @param {string} [options.onNavigated='navigated'] - Callback name when navigated
332
+ */
333
+ constructor(t, e = {}) {
334
+ super(t, e);
335
+ const i = Object.assign({}, A, e), { onNavigated: n, since: a, till: r, firstDayOfWeek: o } = i;
336
+ this.onNavigated = n, this.since = m(a), this.till = m(r), this.firstDayOfWeek = 0 <= o && o < 7 ? o : A.firstDayOfWeek;
337
+ const { disabledDates: l, disabledWeekdays: c, disabledDays: h, disabledMonths: f, disabledYears: D } = i;
338
+ this.disabledDates = Array.isArray(l) ? l : [], this.disabledWeekdays = Array.isArray(c) ? c : [], this.disabledDays = Array.isArray(h) ? h : [], this.disabledMonths = Array.isArray(f) ? f : [], this.disabledYears = Array.isArray(D) ? D : [];
339
+ const { today: Y, day: x, month: O, year: M } = i;
340
+ this.now = m(Y) || /* @__PURE__ */ new Date(), typeof M == "number" && typeof O == "number" && typeof x == "number" ? this.current = m(M, O, x) : this.current = this.now, this.build(), this.enhance();
341
+ }
342
+ /**
343
+ * Builds all calendar data structures (days of week, days of month, months of year).
344
+ */
345
+ build() {
346
+ this.daysOfWeek = this.buildDaysOfWeek(), this.daysOfMonth = this.buildDaysOfMonth(), this.monthsOfYear = this.buildMonthsOfYear();
347
+ }
348
+ /**
349
+ * Builds array of weekday objects with localized names.
350
+ * @returns {Array<Object>} Array of weekday objects
351
+ */
352
+ buildDaysOfWeek() {
353
+ const t = new Intl.DateTimeFormat(this.localesValue, { weekday: "long" }), e = new Intl.DateTimeFormat(this.localesValue, { weekday: "short" }), i = /* @__PURE__ */ new Date("2024-10-06"), n = [];
354
+ for (let a = this.firstDayOfWeek, r = a + 7; a < r; a++) {
355
+ const o = new Date(i);
356
+ o.setDate(i.getDate() + a), n.push({
357
+ date: o,
358
+ value: o.getDay(),
359
+ long: t.format(o),
360
+ short: e.format(o)
361
+ });
362
+ }
363
+ return n;
364
+ }
365
+ /**
366
+ * Builds array of day objects for the current month view, including overflow from adjacent months.
367
+ * @returns {Array<Object>} Array of day objects with metadata
368
+ */
369
+ buildDaysOfMonth() {
370
+ const t = this.month, e = this.year, i = [], n = (h) => ({
371
+ current: this.month === h.getMonth() && this.year === h.getFullYear(),
372
+ date: h,
373
+ value: h.getDate(),
374
+ month: h.getMonth(),
375
+ year: h.getFullYear(),
376
+ iso: h.toISOString()
377
+ }), a = new Date(e, t).getDay(), r = this.firstDayOfWeek - a;
378
+ for (let h = r > 0 ? r - 7 : r; h < 0; h++) {
379
+ const f = new Date(e, t, h + 1);
380
+ i.push(n(f));
381
+ }
382
+ const o = new Date(e, t + 1, 0).getDate();
383
+ for (let h = 1; h <= o; h++) {
384
+ const f = new Date(e, t, h);
385
+ i.push(n(f));
386
+ }
387
+ const l = i.length % L, c = l === 0 ? 0 : L - l;
388
+ for (let h = 1; h <= c; h++) {
389
+ const f = new Date(e, t + 1, h);
390
+ i.push(n(f));
391
+ }
392
+ return i;
393
+ }
394
+ /**
395
+ * Builds array of month objects with localized names for the current year.
396
+ * @returns {Array<Object>} Array of month objects
397
+ */
398
+ buildMonthsOfYear() {
399
+ const t = new Intl.DateTimeFormat(this.localesValue, { month: "long" }), e = new Intl.DateTimeFormat(this.localesValue, { month: "short" }), i = new Intl.DateTimeFormat(this.localesValue, { month: "numeric" }), n = [];
400
+ for (let a = 0; a < 12; a++) {
401
+ const r = new Date(this.year, a);
402
+ n.push({
403
+ date: r,
404
+ value: r.getMonth(),
405
+ long: t.format(r),
406
+ short: e.format(r),
407
+ numeric: i.format(r)
408
+ });
409
+ }
410
+ return n;
411
+ }
412
+ /**
413
+ * Gets the current "today" reference date.
414
+ * @returns {Date} Today's date
415
+ */
416
+ get today() {
417
+ return this.now;
418
+ }
419
+ /**
420
+ * Sets the "today" reference date.
421
+ * @param {Date} value - New today date
422
+ */
423
+ set today(t) {
424
+ if (!g(t)) return;
425
+ const e = this.month ? this.month : t.getMonth(), i = this.year ? this.year : t.getFullYear(), n = e == t.getMonth() && i == t.getFullYear(), a = this.hasDayValue ? this.day : n ? t.getDate() : 1;
426
+ this.now = new Date(i, e, a).toISOString();
427
+ }
428
+ /**
429
+ * Gets the current selected date.
430
+ * @returns {Date|null} Current date or null if not fully specified
431
+ */
432
+ get current() {
433
+ return typeof this.year == "number" && typeof this.month == "number" && typeof this.day == "number" ? m(this.year, this.month, this.day) : null;
434
+ }
435
+ /**
436
+ * Sets the current selected date.
437
+ * @param {Date} value - New current date
438
+ */
439
+ set current(t) {
440
+ g(t) && (this.day = t.getDate(), this.month = t.getMonth(), this.year = t.getFullYear());
441
+ }
442
+ /**
443
+ * Navigates to a specific date, dispatching events and rebuilding calendar.
444
+ * @param {Date} to - Target date to navigate to
445
+ * @returns {Promise<void>}
446
+ */
447
+ navigate = async (t) => {
448
+ if (!g(t)) return;
449
+ const e = this.current, i = t.toISOString(), n = e.toISOString();
450
+ this.dispatch("navigate", { detail: { from: n, to: i } }), this.current = t, this.build(), await this.awaitCallback(this.onNavigated, { from: n, to: i }), this.dispatch("navigated", { detail: { from: n, to: i } });
451
+ };
452
+ /**
453
+ * Steps the calendar by a given amount in a specific unit (year, month, or day).
454
+ * @param {string} type - Type of step ('year', 'month', 'day')
455
+ * @param {number} value - Number of units to step (positive or negative)
456
+ * @returns {Promise<void>}
457
+ */
458
+ step = async (t, e) => {
459
+ if (e === 0) return;
460
+ const i = this.current;
461
+ switch (t) {
462
+ case "year": {
463
+ i.setFullYear(i.getFullYear() + e);
464
+ break;
465
+ }
466
+ case "month": {
467
+ i.setMonth(i.getMonth() + e);
468
+ break;
469
+ }
470
+ case "day": {
471
+ i.setDate(i.getDate() + e);
472
+ break;
473
+ }
474
+ default:
475
+ return;
476
+ }
477
+ await this.navigate(i);
478
+ };
479
+ /**
480
+ * Checks if a date is disabled based on configured rules.
481
+ * @param {Date} date - Date to check
482
+ * @returns {boolean} True if date is disabled
483
+ */
484
+ isDisabled = (t) => {
485
+ if (!g(t)) return !1;
486
+ if (this.disabledDates.length) {
487
+ const e = t.getTime();
488
+ for (const i of this.disabledDates)
489
+ if (e === new Date(i).getTime()) return !0;
490
+ }
491
+ if (this.disabledWeekdays.length) {
492
+ const e = t.getDay(), i = this.daysOfWeek, n = i.findIndex((a) => a.value === e);
493
+ if (n >= 0) {
494
+ const a = i[n];
495
+ for (const r of this.disabledWeekdays)
496
+ if (a.value == r || a.short === r || a.long === r) return !0;
497
+ }
498
+ }
499
+ if (this.disabledDays.length) {
500
+ const e = t.getDate();
501
+ for (const i of this.disabledDays)
502
+ if (e == i) return !0;
503
+ }
504
+ if (this.disabledMonths.length) {
505
+ const e = t.getMonth(), i = this.monthsOfYear, n = i.findIndex((a) => a.value === e);
506
+ if (n >= 0) {
507
+ const a = i[n];
508
+ for (const r of this.disabledMonths)
509
+ if (a.value == r || a.short === r || a.long === r) return !0;
510
+ }
511
+ }
512
+ if (this.disabledYears.length) {
513
+ const e = t.getFullYear();
514
+ for (const i of this.disabledYears)
515
+ if (e == i) return !0;
516
+ }
517
+ return !1;
518
+ };
519
+ /**
520
+ * Checks if a date is within the allowed range (since/till).
521
+ * @param {Date} date - Date to check
522
+ * @returns {boolean} True if date is within range
523
+ */
524
+ isWithinRange = (t) => {
525
+ if (!g(t)) return !1;
526
+ let e = !0;
527
+ return this.since && (e = e && t >= this.since), this.till && (e = e && t <= this.till), e;
528
+ };
529
+ enhance() {
530
+ const t = this;
531
+ Object.assign(this.controller, {
532
+ get calendar() {
533
+ return {
534
+ get today() {
535
+ return t.today;
536
+ },
537
+ get current() {
538
+ return t.current;
539
+ },
540
+ get day() {
541
+ return t.day;
542
+ },
543
+ get month() {
544
+ return t.month;
545
+ },
546
+ get year() {
547
+ return t.year;
548
+ },
549
+ get since() {
550
+ return t.since;
551
+ },
552
+ get till() {
553
+ return t.till;
554
+ },
555
+ get firstDayOfWeek() {
556
+ return t.firstDayOfWeek;
557
+ },
558
+ get disabledDates() {
559
+ return t.disabledDates;
560
+ },
561
+ get disabledWeekdays() {
562
+ return t.disabledWeekdays;
563
+ },
564
+ get disabledDays() {
565
+ return t.disabledDays;
566
+ },
567
+ get disabledMonths() {
568
+ return t.disabledMonths;
569
+ },
570
+ get disabledYears() {
571
+ return t.disabledYears;
572
+ },
573
+ get daysOfWeek() {
574
+ return t.daysOfWeek;
575
+ },
576
+ get daysOfMonth() {
577
+ return t.daysOfMonth;
578
+ },
579
+ get monthsOfYear() {
580
+ return t.monthsOfYear;
581
+ },
582
+ navigate: async (e) => await t.navigate(e),
583
+ step: async (e, i) => await t.step(e, i),
584
+ isDisabled: (e) => t.isDisabled(e),
585
+ isWithinRange: (e) => t.isWithinRange(e)
586
+ };
587
+ }
588
+ });
589
+ }
590
+ }
591
+ const U = (s, t) => new K(s, t), w = {
592
+ content: null,
593
+ url: "",
594
+ reload: "never",
595
+ stale: 3600,
596
+ onLoad: "contentLoad",
597
+ onLoading: "contentLoading",
598
+ onLoaded: "contentLoaded"
599
+ };
600
+ class X extends y {
601
+ /**
602
+ * Creates a new ContentLoader plumber instance for async content loading.
603
+ * @param {Object} controller - Stimulus controller instance
604
+ * @param {Object} [options] - Configuration options
605
+ * @param {*} [options.content] - Initial content value
606
+ * @param {string} [options.url=''] - URL to fetch content from
607
+ * @param {string} [options.reload='never'] - Reload strategy ('never', 'always', or 'stale')
608
+ * @param {number} [options.stale=3600] - Seconds before content becomes stale
609
+ * @param {string} [options.onLoad='contentLoad'] - Callback name to check if loadable
610
+ * @param {string} [options.onLoading='contentLoading'] - Callback name to load content
611
+ * @param {string} [options.onLoaded='contentLoaded'] - Callback name after loading
612
+ */
613
+ constructor(t, e = {}) {
614
+ super(t, e);
615
+ const i = Object.assign({}, w, e), { content: n, url: a, reload: r, stale: o } = i;
616
+ this.content = n, this.url = a, this.reload = typeof r == "string" ? r : w.reload, this.stale = typeof o == "number" ? o : w.stale;
617
+ const { onLoad: l, onLoading: c, onLoaded: h } = i;
618
+ this.onLoad = l, this.onLoading = c, this.onLoaded = h, this.enhance();
619
+ }
620
+ /**
621
+ * Checks if content should be reloaded based on reload strategy.
622
+ * @returns {boolean} True if content should be reloaded
623
+ */
624
+ get reloadable() {
625
+ switch (this.reload) {
626
+ case "never":
627
+ return !1;
628
+ case "always":
629
+ return !0;
630
+ default: {
631
+ const t = m(this.loadedAt);
632
+ return t && /* @__PURE__ */ new Date() - t > this.stale * 1e3;
633
+ }
634
+ }
635
+ }
636
+ /**
637
+ * Checks if content should be loaded based on URL presence.
638
+ * Override this method to provide custom loading conditions.
639
+ * @param {Object} params - Load parameters
640
+ * @param {string} params.url - URL to load from
641
+ * @returns {Promise<boolean>} True if content should be loaded
642
+ */
643
+ contentLoadable = ({ url: t }) => !!t;
644
+ /**
645
+ * Loads content from remote or local source.
646
+ * Override this method to provide custom loading logic.
647
+ * @param {Object} params - Load parameters
648
+ * @param {string} params.url - URL to load from
649
+ * @returns {Promise<string>} Loaded content
650
+ */
651
+ contentLoading = async ({ url: t }) => t ? await this.remoteContentLoader(t) : await this.contentLoader();
652
+ /**
653
+ * Provides local/static content when no URL is available.
654
+ * Override this method to provide static content.
655
+ * @returns {Promise<string>} Local content
656
+ */
657
+ contentLoader = async () => "";
658
+ /**
659
+ * Fetches content from a remote URL.
660
+ * Override this method to customize remote loading.
661
+ * @param {string} url - URL to fetch from
662
+ * @returns {Promise<string>} Fetched content
663
+ */
664
+ remoteContentLoader = async (t) => (await fetch(t)).text();
665
+ /**
666
+ * Loads content from remote or local source with lifecycle events.
667
+ * Checks if loadable via onLoad, fetches content via onLoading,
668
+ * and notifies via onLoaded callback.
669
+ * @returns {Promise<void>}
670
+ */
671
+ load = async () => {
672
+ if (this.loadedAt && !this.reloadable) return;
673
+ const t = this.findCallback(this.onLoad), e = await this.awaitCallback(t || this.contentLoadable, { url: this.url });
674
+ if (this.dispatch("load", { detail: { url: this.url } }), !e) return;
675
+ const i = this.url ? await this.remoteContentLoader(this.url) : await this.contentLoader();
676
+ this.dispatch("loading", { detail: { url: this.url } }), i && (await this.awaitCallback(this.onLoaded, { url: this.url, content: i }), this.loadedAt = (/* @__PURE__ */ new Date()).getTime(), this.dispatch("loaded", { detail: { url: this.url, content: i } }));
677
+ };
678
+ enhance() {
679
+ const t = this;
680
+ Object.assign(this.controller, {
681
+ load: t.load.bind(t)
682
+ });
683
+ }
684
+ }
685
+ const G = (s, t) => new X(s, t), J = {
686
+ trigger: null,
687
+ events: ["click"],
688
+ onDismissed: "dismissed"
689
+ };
690
+ class Q extends y {
691
+ /**
692
+ * Creates a new Dismisser plumber instance for handling outside-click dismissals.
693
+ * @param {Object} controller - Stimulus controller instance
694
+ * @param {Object} [options] - Configuration options
695
+ * @param {HTMLElement} [options.trigger] - Trigger element (defaults to controller element)
696
+ * @param {string[]} [options.events=['click']] - Events to listen for dismissal
697
+ * @param {string} [options.onDismissed='dismissed'] - Callback name when dismissed
698
+ */
699
+ constructor(t, e = {}) {
700
+ super(t, e);
701
+ const { trigger: i, events: n, onDismissed: a } = Object.assign({}, J, e);
702
+ this.onDismissed = a, this.trigger = i || this.element, this.events = n, this.enhance(), this.observe();
703
+ }
704
+ /**
705
+ * Handles dismissal when clicking outside the element.
706
+ * @param {Event} event - DOM event
707
+ * @returns {Promise<void>}
708
+ */
709
+ dismiss = async (t) => {
710
+ const { target: e } = t;
711
+ e instanceof HTMLElement && (this.element.contains(e) || this.visible && (this.dispatch("dismiss"), await this.awaitCallback(this.onDismissed, { target: this.trigger }), this.dispatch("dismissed")));
712
+ };
713
+ /**
714
+ * Starts observing configured events for dismissal.
715
+ */
716
+ observe() {
717
+ this.events.forEach((t) => {
718
+ window.addEventListener(t, this.dismiss, !0);
719
+ });
720
+ }
721
+ /**
722
+ * Stops observing events for dismissal.
723
+ */
724
+ unobserve() {
725
+ this.events.forEach((t) => {
726
+ window.removeEventListener(t, this.dismiss, !0);
727
+ });
728
+ }
729
+ enhance() {
730
+ const t = this, e = t.controller.disconnect.bind(t.controller);
731
+ Object.assign(this.controller, {
732
+ disconnect: () => {
733
+ t.unobserve(), e();
734
+ }
735
+ });
736
+ }
737
+ }
738
+ const W = (s, t) => new Q(s, t), Z = {
739
+ anchor: null,
740
+ events: ["click"],
741
+ placement: "bottom",
742
+ alignment: "start",
743
+ onFlipped: "flipped",
744
+ ariaRole: null,
745
+ respectMotion: !0
746
+ };
747
+ class tt extends y {
748
+ /**
749
+ * Creates a new Flipper plumber instance for smart positioning relative to an anchor.
750
+ * @param {Object} controller - Stimulus controller instance
751
+ * @param {Object} [options] - Configuration options
752
+ * @param {HTMLElement} [options.anchor] - Anchor element for positioning
753
+ * @param {string[]} [options.events=['click']] - Events triggering flip calculation
754
+ * @param {string} [options.placement='bottom'] - Initial placement direction ('top', 'bottom', 'left', 'right')
755
+ * @param {string} [options.alignment='start'] - Alignment ('start', 'center', 'end')
756
+ * @param {string} [options.onFlipped='flipped'] - Callback name when flipped
757
+ * @param {string} [options.ariaRole=null] - ARIA role to set on element
758
+ * @param {boolean} [options.respectMotion=true] - Respect prefers-reduced-motion preference
759
+ */
760
+ constructor(t, e = {}) {
761
+ super(t, e);
762
+ const { anchor: i, events: n, placement: a, alignment: r, onFlipped: o, ariaRole: l, respectMotion: c } = Object.assign(
763
+ {},
764
+ Z,
765
+ e
766
+ );
767
+ this.anchor = i, this.events = n, this.placement = a, this.alignment = r, this.onFlipped = o, this.ariaRole = l, this.respectMotion = c, this.prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches, this.anchor && this.element && q({
768
+ trigger: this.anchor,
769
+ target: this.element,
770
+ role: this.ariaRole
771
+ }), this.enhance(), this.observe();
772
+ }
773
+ /**
774
+ * Attempts to place element in configured direction, flipping to opposite direction if needed.
775
+ * For example, if placement is 'bottom' but no space below anchor, flips to 'top'.
776
+ * @returns {Promise<void>}
777
+ */
778
+ flip = async () => {
779
+ if (!this.visible) return;
780
+ this.dispatch("flip"), window.getComputedStyle(this.element).position != "absolute" && (this.element.style.position = "absolute");
781
+ const e = this.flippedRect(this.anchor.getBoundingClientRect(), this.element.getBoundingClientRect());
782
+ this.element.style.transition = this.respectMotion && this.prefersReducedMotion ? "none" : "";
783
+ for (const [i, n] of Object.entries(e))
784
+ this.element.style[i] = n;
785
+ await this.awaitCallback(this.onFlipped, { target: this.element, placement: e }), this.dispatch("flipped", { detail: { placement: e } });
786
+ };
787
+ /**
788
+ * Determines the best position that fits within viewport boundaries.
789
+ * @param {DOMRect} anchorRect - Anchor element's bounding rect
790
+ * @param {DOMRect} referenceRect - Reference element's bounding rect
791
+ * @returns {Object} Position object with top and left styles
792
+ */
793
+ flippedRect(t, e) {
794
+ const i = this.quadrumRect(t, T()), n = [this.placement, R[this.placement]];
795
+ let a = {};
796
+ for (; !Object.keys(a).length && n.length > 0; ) {
797
+ const r = n.shift();
798
+ if (!this.biggerRectThan(i[r], e)) continue;
799
+ const o = this.quadrumPlacement(t, r, e), l = this.quadrumAlignment(t, r, o);
800
+ a.top = `${l.top + window.scrollY}px`, a.left = `${l.left + window.scrollX}px`;
801
+ }
802
+ return Object.keys(a).length || (a.top = "", a.left = ""), a;
803
+ }
804
+ /**
805
+ * Calculates available space in each direction around the inner rect within outer rect.
806
+ * @param {Object} inner - Inner rect object
807
+ * @param {Object} outer - Outer rect object
808
+ * @returns {Object} Rect objects for each direction (left, right, top, bottom)
809
+ */
810
+ quadrumRect(t, e) {
811
+ return {
812
+ left: d({
813
+ x: e.x,
814
+ y: e.y,
815
+ width: t.x - e.x,
816
+ height: e.height
817
+ }),
818
+ right: d({
819
+ x: t.x + t.width,
820
+ y: e.y,
821
+ width: e.width - (t.x + t.width),
822
+ height: e.height
823
+ }),
824
+ top: d({
825
+ x: e.x,
826
+ y: e.y,
827
+ width: e.width,
828
+ height: t.y - e.y
829
+ }),
830
+ bottom: d({
831
+ x: e.x,
832
+ y: t.y + t.height,
833
+ width: e.width,
834
+ height: e.height - (t.y + t.height)
835
+ })
836
+ };
837
+ }
838
+ /**
839
+ * Calculates placement rect for reference element in given direction from anchor.
840
+ * @param {Object} anchor - Anchor rect object
841
+ * @param {string} direction - Direction ('top', 'bottom', 'left', 'right')
842
+ * @param {Object} reference - Reference rect object
843
+ * @returns {Object} Placed rect object
844
+ * @throws {string} If direction is invalid
845
+ */
846
+ quadrumPlacement(t, e, i) {
847
+ switch (e) {
848
+ case "top":
849
+ return d({
850
+ x: i.x,
851
+ y: t.y - i.height,
852
+ width: i.width,
853
+ height: i.height
854
+ });
855
+ case "bottom":
856
+ return d({
857
+ x: i.x,
858
+ y: t.y + t.height,
859
+ width: i.width,
860
+ height: i.height
861
+ });
862
+ case "left":
863
+ return d({
864
+ x: t.x - i.width,
865
+ y: i.y,
866
+ width: i.width,
867
+ height: i.height
868
+ });
869
+ case "right":
870
+ return d({
871
+ x: t.x + t.width,
872
+ y: i.y,
873
+ width: i.width,
874
+ height: i.height
875
+ });
876
+ default:
877
+ throw `Unable place at the quadrum, ${e}`;
878
+ }
879
+ }
880
+ /**
881
+ * Applies alignment adjustment to placed rect based on configuration.
882
+ * @param {Object} anchor - Anchor rect object
883
+ * @param {string} direction - Direction ('top', 'bottom', 'left', 'right')
884
+ * @param {Object} reference - Reference rect object
885
+ * @returns {Object} Aligned rect object
886
+ * @throws {string} If direction is invalid
887
+ */
888
+ quadrumAlignment(t, e, i) {
889
+ switch (e) {
890
+ case "top":
891
+ case "bottom": {
892
+ let n = t.x;
893
+ return this.alignment === "center" ? n = t.x + t.width / 2 - i.width / 2 : this.alignment === "end" && (n = t.x + t.width - i.width), d({
894
+ x: n,
895
+ y: i.y,
896
+ width: i.width,
897
+ height: i.height
898
+ });
899
+ }
900
+ case "left":
901
+ case "right": {
902
+ let n = t.y;
903
+ return this.alignment === "center" ? n = t.y + t.height / 2 - i.height / 2 : this.alignment === "end" && (n = t.y + t.height - i.height), d({
904
+ x: i.x,
905
+ y: n,
906
+ width: i.width,
907
+ height: i.height
908
+ });
909
+ }
910
+ default:
911
+ throw `Unable align at the quadrum, ${e}`;
912
+ }
913
+ }
914
+ /**
915
+ * Checks if the big rect can contain the small rect dimensions.
916
+ * @param {Object} big - Larger rect object
917
+ * @param {Object} small - Smaller rect object
918
+ * @returns {boolean} True if big rect can contain small rect
919
+ */
920
+ biggerRectThan(t, e) {
921
+ return t.height >= e.height && t.width >= e.width;
922
+ }
923
+ /**
924
+ * Starts observing configured events for flipping.
925
+ */
926
+ observe() {
927
+ this.events.forEach((t) => {
928
+ window.addEventListener(t, this.flip, !0);
929
+ });
930
+ }
931
+ /**
932
+ * Stops observing events for flipping.
933
+ */
934
+ unobserve() {
935
+ this.events.forEach((t) => {
936
+ window.removeEventListener(t, this.flip, !0);
937
+ });
938
+ }
939
+ enhance() {
940
+ const t = this, e = t.controller.disconnect.bind(t.controller);
941
+ Object.assign(this.controller, {
942
+ disconnect: () => {
943
+ t.unobserve(), e();
944
+ },
945
+ flip: t.flip.bind(t)
946
+ });
947
+ }
948
+ }
949
+ const et = (s, t) => new tt(s, t), it = {
950
+ events: ["resize"],
951
+ boundaries: ["top", "left", "right"],
952
+ onShifted: "shifted",
953
+ respectMotion: !0
954
+ };
955
+ class st extends y {
956
+ /**
957
+ * Creates a new Shifter plumber instance for viewport boundary shifting.
958
+ * @param {Object} controller - Stimulus controller instance
959
+ * @param {Object} [options] - Configuration options
960
+ * @param {string[]} [options.events=['resize']] - Events triggering shift calculation
961
+ * @param {string[]} [options.boundaries=['top','left','right']] - Boundaries to check (valid values: 'top', 'bottom', 'left', 'right')
962
+ * @param {string} [options.onShifted='shifted'] - Callback name when shifted
963
+ * @param {boolean} [options.respectMotion=true] - Respect prefers-reduced-motion preference
964
+ */
965
+ constructor(t, e = {}) {
966
+ super(t, e);
967
+ const { onShifted: i, events: n, boundaries: a, respectMotion: r } = Object.assign({}, it, e);
968
+ this.onShifted = i, this.events = n, this.boundaries = a, this.respectMotion = r, this.prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches, this.enhance(), this.observe();
969
+ }
970
+ /**
971
+ * Calculates and applies transform to shift element within viewport boundaries.
972
+ * @returns {Promise<void>}
973
+ */
974
+ shift = async () => {
975
+ if (!this.visible) return;
976
+ this.dispatch("shift");
977
+ const t = this.overflowRect(this.element.getBoundingClientRect(), this.elementTranslations(this.element)), e = t.left || t.right || 0, i = t.top || t.bottom || 0;
978
+ this.element.style.transition = this.respectMotion && this.prefersReducedMotion ? "none" : "", this.element.style.transform = `translate(${e}px, ${i}px)`, await this.awaitCallback(this.onShifted, t), this.dispatch("shifted", { detail: t });
979
+ };
980
+ /**
981
+ * Calculates overflow distances for each boundary direction.
982
+ * @param {DOMRect} targetRect - Target element's bounding rect
983
+ * @param {Object} translations - Current transform translations
984
+ * @returns {Object} Overflow distances by direction
985
+ */
986
+ overflowRect(t, e) {
987
+ const i = {}, n = T(), a = d({
988
+ x: t.x - e.x,
989
+ y: t.y - e.y,
990
+ width: t.width,
991
+ height: t.height
992
+ });
993
+ for (const r of this.boundaries) {
994
+ const o = this.directionDistance(a, r, n), l = R[r];
995
+ o < 0 ? a[l] + o >= n[l] && !i[l] && (i[r] = o) : i[r] = "";
996
+ }
997
+ return i;
998
+ }
999
+ /**
1000
+ * Calculates distance from inner rect to outer boundary in given direction.
1001
+ * @param {Object} inner - Inner rect object
1002
+ * @param {string} direction - Direction ('top', 'bottom', 'left', 'right')
1003
+ * @param {Object} outer - Outer rect object
1004
+ * @returns {number} Distance to boundary (negative if overflowing)
1005
+ * @throws {string} If direction is invalid
1006
+ */
1007
+ directionDistance(t, e, i) {
1008
+ switch (e) {
1009
+ case "top":
1010
+ case "left":
1011
+ return t[e] - i[e];
1012
+ case "bottom":
1013
+ case "right":
1014
+ return i[e] - t[e];
1015
+ default:
1016
+ throw `Invalid direction to calcuate distance, ${e}`;
1017
+ }
1018
+ }
1019
+ /**
1020
+ * Extracts current translate values from element's transform style.
1021
+ * @param {HTMLElement} target - Target element
1022
+ * @returns {Object} Translation object with x and y values
1023
+ */
1024
+ elementTranslations(t) {
1025
+ const e = window.getComputedStyle(t), i = e.transform || e.webkitTransform || e.mozTransform;
1026
+ if (i === "none" || typeof i > "u")
1027
+ return { x: 0, y: 0 };
1028
+ const n = i.includes("3d") ? "3d" : "2d", a = i.match(/matrix.*\((.+)\)/)[1].split(", ");
1029
+ return n === "2d" ? { x: Number(a[4]), y: Number(a[5]) } : { x: 0, y: 0 };
1030
+ }
1031
+ /**
1032
+ * Starts observing configured events for shifting.
1033
+ */
1034
+ observe() {
1035
+ this.events.forEach((t) => {
1036
+ window.addEventListener(t, this.shift, !0);
1037
+ });
1038
+ }
1039
+ /**
1040
+ * Stops observing events for shifting.
1041
+ */
1042
+ unobserve() {
1043
+ this.events.forEach((t) => {
1044
+ window.removeEventListener(t, this.shift, !0);
1045
+ });
1046
+ }
1047
+ enhance() {
1048
+ const t = this, e = t.controller.disconnect.bind(t.controller);
1049
+ Object.assign(this.controller, {
1050
+ disconnect: () => {
1051
+ t.unobserve(), e();
1052
+ },
1053
+ shift: t.shift.bind(t)
1054
+ });
1055
+ }
1056
+ }
1057
+ const nt = (s, t) => new st(s, t), F = {
1058
+ visibility: "visibility",
1059
+ onShown: "shown",
1060
+ onHidden: "hidden"
1061
+ };
1062
+ class at extends y {
1063
+ /**
1064
+ * Creates a new Visibility plumber instance.
1065
+ * @param {Object} controller - Stimulus controller instance
1066
+ * @param {Object} [options] - Configuration options
1067
+ * @param {string} [options.visibility='visibility'] - Namespace for visibility helpers
1068
+ * @param {string} [options.onShown='shown'] - Method name on plumber instance called after showing
1069
+ * @param {string} [options.onHidden='hidden'] - Method name on plumber instance called after hiding
1070
+ */
1071
+ constructor(t, e = {}) {
1072
+ const { visibility: i, onShown: n, onHidden: a, activator: r } = Object.assign({}, F, e), o = typeof i == "string" ? i : F.namespace, l = typeof e.visible == "string" ? e.visible : "isVisible";
1073
+ (typeof e.visible != "boolean" || e.visible) && (e.visible = `${o}.${l}`), super(t, e), this.visibility = o, this.visibilityResolver = l, this.onShown = n, this.onHidden = a, this.activator = r instanceof HTMLElement ? r : null, this.enhance(), this.element instanceof HTMLElement && this.activate(this.isVisible(this.element));
1074
+ }
1075
+ /**
1076
+ * Checks if a target element is visible.
1077
+ * @param {HTMLElement} target - Element to check
1078
+ * @returns {boolean} True if element is visible
1079
+ */
1080
+ isVisible(t) {
1081
+ if (!(t instanceof HTMLElement)) return !1;
1082
+ const e = I.hiddenClass;
1083
+ return e ? !t.classList.contains(e) : !t.hasAttribute("hidden");
1084
+ }
1085
+ /**
1086
+ * Toggles element visibility using class or hidden attribute.
1087
+ * @param {HTMLElement} target - Element to toggle
1088
+ * @param {boolean} visible - True to show, false to hide
1089
+ */
1090
+ toggle(t, e) {
1091
+ t instanceof HTMLElement && (e ? t.removeAttribute("hidden") : t.setAttribute("hidden", !0));
1092
+ }
1093
+ /**
1094
+ * Sets aria-expanded on the activator element.
1095
+ * @param {boolean} isExpanded - True to mark as expanded, false to mark as collapsed
1096
+ */
1097
+ activate(t) {
1098
+ this.activator && this.activator.setAttribute("aria-expanded", t ? "true" : "false");
1099
+ }
1100
+ /**
1101
+ * Shows the element and dispatches show events.
1102
+ * @returns {Promise<void>}
1103
+ */
1104
+ async show() {
1105
+ !(this.element instanceof HTMLElement) || this.isVisible(this.element) || (this.dispatch("show"), this.toggle(this.element, !0), this.activate(!0), await this.awaitCallback(this.onShown, { target: this.element }), this.dispatch("shown"));
1106
+ }
1107
+ /**
1108
+ * Hides the element and dispatches hide events.
1109
+ * @returns {Promise<void>}
1110
+ */
1111
+ async hide() {
1112
+ !(this.element instanceof HTMLElement) || !this.isVisible(this.element) || (this.dispatch("hide"), this.toggle(this.element, !1), this.activate(!1), await this.awaitCallback(this.onHidden, { target: this.element }), this.dispatch("hidden"));
1113
+ }
1114
+ enhance() {
1115
+ const t = this, e = {
1116
+ show: t.show.bind(t),
1117
+ hide: t.hide.bind(t)
1118
+ };
1119
+ Object.defineProperty(e, "visible", {
1120
+ get() {
1121
+ return t.isVisible(t.element);
1122
+ }
1123
+ }), Object.defineProperty(e, this.visibilityResolver, {
1124
+ value: t.isVisible.bind(t)
1125
+ }), Object.defineProperty(this.controller, this.visibility, {
1126
+ get() {
1127
+ return e;
1128
+ }
1129
+ });
1130
+ }
1131
+ }
1132
+ const S = (s, t) => new at(s, t);
1133
+ class wt extends u {
1134
+ static targets = ["modal", "overlay"];
1135
+ connect() {
1136
+ if (!this.hasModalTarget) {
1137
+ console.error('ModalController requires a modal target. Add data-modal-target="modal" to your element.');
1138
+ return;
1139
+ }
1140
+ this.isNativeDialog = this.modalTarget instanceof HTMLDialogElement, this.isNativeDialog ? (this.modalTarget.addEventListener("cancel", this.close), this.modalTarget.addEventListener("click", this.handleBackdropClick)) : (this.focusTrap = new j(this.modalTarget, {
1141
+ escapeDeactivates: !0
1142
+ }), W(this, { element: this.modalTarget }));
1143
+ }
1144
+ dismissed = () => {
1145
+ this.close();
1146
+ };
1147
+ disconnect() {
1148
+ this.isNativeDialog && (this.modalTarget.removeEventListener("cancel", this.close), this.modalTarget.removeEventListener("click", this.handleBackdropClick));
1149
+ }
1150
+ open(t) {
1151
+ if (t && t.preventDefault(), !!this.hasModalTarget) {
1152
+ if (this.isNativeDialog)
1153
+ this.previouslyFocused = document.activeElement, this.modalTarget.showModal();
1154
+ else {
1155
+ const e = this.hasOverlayTarget ? this.overlayTarget : this.modalTarget;
1156
+ e.hidden = !1, document.body.style.overflow = "hidden", this.focusTrap && this.focusTrap.activate();
1157
+ }
1158
+ k("Modal opened");
1159
+ }
1160
+ }
1161
+ close(t) {
1162
+ if (t && t.preventDefault(), !!this.hasModalTarget) {
1163
+ if (this.isNativeDialog)
1164
+ this.modalTarget.close(), this.previouslyFocused && this.previouslyFocused.isConnected && setTimeout(() => {
1165
+ this.previouslyFocused.focus();
1166
+ }, 0);
1167
+ else {
1168
+ const e = this.hasOverlayTarget ? this.overlayTarget : this.modalTarget;
1169
+ e.hidden = !0, document.body.style.overflow = "", this.focusTrap && this.focusTrap.deactivate();
1170
+ }
1171
+ k("Modal closed");
1172
+ }
1173
+ }
1174
+ handleBackdropClick = (t) => {
1175
+ const e = this.modalTarget.getBoundingClientRect();
1176
+ (t.clientY < e.top || t.clientY > e.bottom || t.clientX < e.left || t.clientX > e.right) && this.close();
1177
+ };
1178
+ }
1179
+ class vt extends u {
1180
+ static targets = ["trigger"];
1181
+ connect() {
1182
+ W(this, { trigger: this.hasTriggerTarget ? this.triggerTarget : null });
1183
+ }
1184
+ }
1185
+ class Tt extends u {
1186
+ static targets = ["anchor", "reference"];
1187
+ static values = {
1188
+ placement: { type: String, default: "bottom" },
1189
+ alignment: { type: String, default: "start" },
1190
+ role: { type: String, default: "tooltip" }
1191
+ };
1192
+ connect() {
1193
+ if (!this.hasReferenceTarget) {
1194
+ console.error(
1195
+ 'FlipperController requires a reference target. Add data-flipper-target="reference" to your element.'
1196
+ );
1197
+ return;
1198
+ }
1199
+ if (!this.hasAnchorTarget) {
1200
+ console.error('FlipperController requires an anchor target. Add data-flipper-target="anchor" to your element.');
1201
+ return;
1202
+ }
1203
+ et(this, {
1204
+ element: this.referenceTarget,
1205
+ anchor: this.anchorTarget,
1206
+ placement: this.placementValue,
1207
+ alignment: this.alignmentValue,
1208
+ ariaRole: this.roleValue
1209
+ });
1210
+ }
1211
+ }
1212
+ class Dt extends u {
1213
+ static targets = ["content", "template", "loader", "activator"];
1214
+ static classes = ["hidden"];
1215
+ static values = {
1216
+ url: String,
1217
+ loadedAt: String,
1218
+ reload: { type: String, default: "never" },
1219
+ staleAfter: { type: Number, default: 3600 }
1220
+ };
1221
+ connect() {
1222
+ G(this, {
1223
+ element: this.hasContentTarget ? this.contentTarget : null,
1224
+ url: this.hasUrlValue ? this.urlValue : null
1225
+ }), this.hasContentTarget && S(this, {
1226
+ element: this.contentTarget,
1227
+ activator: this.hasActivatorTarget ? this.activatorTarget : null
1228
+ }), this.hasLoaderTarget && S(this, { element: this.loaderTarget, visibility: "contentLoaderVisibility" });
1229
+ }
1230
+ async show() {
1231
+ await this.visibility.show();
1232
+ }
1233
+ async hide() {
1234
+ await this.visibility.hide();
1235
+ }
1236
+ async shown() {
1237
+ await this.load();
1238
+ }
1239
+ contentLoad() {
1240
+ return this.hasContentTarget && this.contentTarget.tagName.toLowerCase() === "turbo-frame" ? (this.hasUrlValue && this.contentTarget.setAttribute("src", this.urlValue), !1) : !0;
1241
+ }
1242
+ async contentLoading() {
1243
+ this.hasLoaderTarget && await this.contentLoaderVisibility.show();
1244
+ }
1245
+ async contentLoaded({ content: t }) {
1246
+ this.hasContentTarget && this.contentTarget.replaceChildren(this.getContentNode(t)), this.hasLoaderTarget && await this.contentLoaderVisibility.hide();
1247
+ }
1248
+ getContentNode(t) {
1249
+ if (typeof t == "string") {
1250
+ const e = document.createElement("template");
1251
+ return e.innerHTML = t, document.importNode(e.content, !0);
1252
+ }
1253
+ return document.importNode(t, !0);
1254
+ }
1255
+ contentLoader() {
1256
+ if (this.hasTemplateTarget)
1257
+ return this.templateTarget instanceof HTMLTemplateElement ? this.templateTarget.content : this.templateTarget.innerHTML;
1258
+ }
1259
+ }
1260
+ class xt extends u {
1261
+ static targets = ["daysOfWeek", "daysOfMonth"];
1262
+ static classes = ["dayOfWeek", "dayOfMonth"];
1263
+ static values = {
1264
+ locales: { type: Array, default: ["default"] },
1265
+ weekdayFormat: { type: String, default: "short" },
1266
+ dayFormat: { type: String, default: "numeric" },
1267
+ daysOfOtherMonth: { type: Boolean, default: !1 }
1268
+ };
1269
+ initialize() {
1270
+ U(this);
1271
+ }
1272
+ connect() {
1273
+ this.draw();
1274
+ }
1275
+ navigated() {
1276
+ this.draw();
1277
+ }
1278
+ draw() {
1279
+ this.drawDaysOfWeek(), this.drawDaysOfMonth();
1280
+ }
1281
+ createDayElement(t, { selectable: e = !1, disabled: i = !1 } = {}) {
1282
+ const n = document.createElement(e ? "button" : "div");
1283
+ return n.tabIndex = -1, t ? n.textContent = t : n.setAttribute("aria-hidden", "true"), i && (n instanceof HTMLButtonElement ? n.disabled = !0 : n.setAttribute("aria-disabled", "true")), n;
1284
+ }
1285
+ drawDaysOfWeek() {
1286
+ if (!this.hasDaysOfWeekTarget) return;
1287
+ const t = new Intl.DateTimeFormat(this.localesValue, {
1288
+ weekday: this.weekdayFormatValue
1289
+ }), e = [];
1290
+ for (const n of this.calendar.daysOfWeek) {
1291
+ const a = this.createDayElement(t.format(n.date));
1292
+ a.setAttribute("role", "columnheader"), a.title = n.long, this.hasDayOfWeekClass && a.classList.add(...this.dayOfWeekClasses), e.push(a);
1293
+ }
1294
+ const i = document.createElement("div");
1295
+ i.setAttribute("role", "row"), i.replaceChildren(...e), this.daysOfWeekTarget.replaceChildren(i);
1296
+ }
1297
+ drawDaysOfMonth() {
1298
+ if (!this.hasDaysOfMonthTarget) return;
1299
+ const t = this.calendar.today, e = new Date(t.getFullYear(), t.getMonth(), t.getDate()).getTime(), i = [];
1300
+ for (const a of this.calendar.daysOfMonth) {
1301
+ const r = !a.current || this.calendar.isDisabled(a.date) || !this.calendar.isWithinRange(a.date), o = a.current || this.daysOfOtherMonthValue ? a.value : "", l = this.createDayElement(o, {
1302
+ selectable: a.current,
1303
+ disabled: r
1304
+ });
1305
+ e === a.date.getTime() && l.setAttribute("aria-current", "date"), this.hasDayOfMonthClass && l.classList.add(...this.dayOfMonthClasses);
1306
+ const c = document.createElement("time");
1307
+ c.dateTime = a.iso, l.appendChild(c), i.push(l);
1308
+ }
1309
+ const n = [];
1310
+ for (let a = 0; a < i.length; a += 7) {
1311
+ const r = document.createElement("div");
1312
+ r.setAttribute("role", "row");
1313
+ for (const o of i.slice(a, a + 7))
1314
+ o.setAttribute("role", "gridcell"), r.appendChild(o);
1315
+ n.push(r);
1316
+ }
1317
+ this.daysOfMonthTarget.replaceChildren(...n);
1318
+ }
1319
+ }
1320
+ class Ot extends u {
1321
+ select(t) {
1322
+ if (!(t.target instanceof HTMLElement)) return;
1323
+ t.preventDefault();
1324
+ const e = t.target instanceof HTMLTimeElement ? t.target.parentElement : t.target;
1325
+ if (e.disabled || e.getAttribute("aria-disabled") === "true") return;
1326
+ this.dispatch("select", { target: e });
1327
+ const i = t.target instanceof HTMLTimeElement ? t.target : t.target.querySelector("time");
1328
+ if (!i) return console.error(`unable to locate time element within ${e}`);
1329
+ const n = m(i.dateTime);
1330
+ if (!n) return console.error(`unable to parse ${i.dateTime} found within the time element`);
1331
+ this.dispatch("selected", {
1332
+ target: e,
1333
+ detail: { epoch: n.getTime(), iso: n.toISOString() }
1334
+ });
1335
+ }
1336
+ }
1337
+ class Mt extends u {
1338
+ static targets = ["previous", "next", "day", "month", "year", "input", "display"];
1339
+ static outlets = ["calendar-month"];
1340
+ static values = {
1341
+ locales: { type: Array, default: ["default"] },
1342
+ dayFormat: { type: String, default: "numeric" },
1343
+ monthFormat: { type: String, default: "long" },
1344
+ yearFormat: { type: String, default: "numeric" }
1345
+ };
1346
+ initialize() {
1347
+ this.previous = this.previous.bind(this), this.next = this.next.bind(this);
1348
+ }
1349
+ async calendarMonthOutletConnected() {
1350
+ if (this.hasInputTarget && this.inputTarget.value) {
1351
+ const t = m(this.inputTarget.value);
1352
+ t && await this.calendarMonthOutlet.calendar.navigate(t);
1353
+ }
1354
+ this.draw();
1355
+ }
1356
+ selected(t) {
1357
+ this.hasInputTarget && (this.inputTarget.value = t.detail.iso), this.hasDisplayTarget && (this.displayTarget.value = this.formatDate(new Date(t.detail.iso)));
1358
+ }
1359
+ formatDate(t) {
1360
+ return new Intl.DateTimeFormat(this.localesValue, {
1361
+ day: this.dayFormatValue,
1362
+ month: this.monthFormatValue,
1363
+ year: this.yearFormatValue
1364
+ }).format(t);
1365
+ }
1366
+ previousTargetConnected(t) {
1367
+ t.addEventListener("click", this.previous);
1368
+ }
1369
+ previousTargetDisconnected(t) {
1370
+ t.removeEventListener("click", this.previous);
1371
+ }
1372
+ async previous() {
1373
+ await this.calendarMonthOutlet.calendar.step("month", -1), this.draw();
1374
+ }
1375
+ nextTargetConnected(t) {
1376
+ t.addEventListener("click", this.next);
1377
+ }
1378
+ nextTargetDisconnected(t) {
1379
+ t.removeEventListener("click", this.next);
1380
+ }
1381
+ async next() {
1382
+ await this.calendarMonthOutlet.calendar.step("month", 1), this.draw();
1383
+ }
1384
+ draw() {
1385
+ this.drawDay(), this.drawMonth(), this.drawYear();
1386
+ }
1387
+ drawDay() {
1388
+ if (!this.hasDayTarget || !this.hasCalendarMonthOutlet) return;
1389
+ const { year: t, month: e, day: i } = this.calendarMonthOutlet.calendar, n = new Intl.DateTimeFormat(this.localesValue, { day: this.dayFormatValue });
1390
+ this.dayTarget.textContent = n.format(new Date(t, e, i));
1391
+ }
1392
+ drawMonth() {
1393
+ if (!this.hasMonthTarget || !this.hasCalendarMonthOutlet) return;
1394
+ const { year: t, month: e } = this.calendarMonthOutlet.calendar, i = new Intl.DateTimeFormat(this.localesValue, { month: this.monthFormatValue });
1395
+ this.monthTarget.textContent = i.format(new Date(t, e));
1396
+ }
1397
+ drawYear() {
1398
+ if (!this.hasYearTarget || !this.hasCalendarMonthOutlet) return;
1399
+ const { year: t } = this.calendarMonthOutlet.calendar, e = new Intl.DateTimeFormat(this.localesValue, { year: this.yearFormatValue });
1400
+ this.yearTarget.textContent = e.format(new Date(t, 0));
1401
+ }
1402
+ }
1403
+ class kt extends u {
1404
+ static targets = ["content"];
1405
+ connect() {
1406
+ nt(this, { element: this.hasContentTarget ? this.contentTarget : null });
1407
+ }
1408
+ }
1409
+ class Ct extends u {
1410
+ static targets = ["input"];
1411
+ toggle() {
1412
+ this.inputTarget.type = this.inputTarget.type === "password" ? "text" : "password";
1413
+ }
1414
+ }
1415
+ class Et extends u {
1416
+ connect() {
1417
+ this.resize(), this.element.addEventListener("input", this.resize.bind(this));
1418
+ }
1419
+ disconnect() {
1420
+ this.element.removeEventListener("input", this.resize.bind(this));
1421
+ }
1422
+ resize() {
1423
+ this.element.style.height = "auto", this.element.style.height = `${this.element.scrollHeight}px`;
1424
+ }
1425
+ }
1426
+ export {
1427
+ P as ARIA_HASPOPUP_VALUES,
1428
+ Et as AutoResizeController,
1429
+ xt as CalendarMonthController,
1430
+ Ot as CalendarMonthObserverController,
1431
+ Mt as DatepickerController,
1432
+ vt as DismisserController,
1433
+ H as FOCUSABLE_SELECTOR,
1434
+ Tt as FlipperController,
1435
+ ot as FocusRestoration,
1436
+ j as FocusTrap,
1437
+ wt as ModalController,
1438
+ kt as PannerController,
1439
+ Ct as PasswordRevealController,
1440
+ Dt as PopoverController,
1441
+ ut as RovingTabIndex,
1442
+ k as announce,
1443
+ q as connectTriggerToTarget,
1444
+ bt as disconnectTriggerFromTarget,
1445
+ ft as ensureId,
1446
+ $ as focusFirst,
1447
+ _ as generateId,
1448
+ V as getFocusableElements,
1449
+ ht as isActivationKey,
1450
+ ct as isArrowKey,
1451
+ lt as isKey,
1452
+ v as isVisible,
1453
+ dt as preventDefault,
1454
+ b as setAriaState,
1455
+ yt as setChecked,
1456
+ pt as setDisabled,
1457
+ mt as setExpanded,
1458
+ gt as setPressed
1459
+ };