@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.
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/dist/stimulus-plumbers-controllers.es.js +1459 -0
- package/dist/stimulus-plumbers-controllers.umd.js +1 -0
- package/package.json +57 -0
- package/src/aria.js +173 -0
- package/src/controllers/auto_resize_controller.js +17 -0
- package/src/controllers/calendar_month_controller.js +100 -0
- package/src/controllers/calendar_month_observer_controller.js +24 -0
- package/src/controllers/datepicker_controller.js +101 -0
- package/src/controllers/dismisser_controller.js +10 -0
- package/src/controllers/flipper_controller.js +33 -0
- package/src/controllers/form-field_controller.js +77 -0
- package/src/controllers/modal_controller.js +104 -0
- package/src/controllers/panner_controller.js +10 -0
- package/src/controllers/password_reveal_controller.js +9 -0
- package/src/controllers/popover_controller.js +76 -0
- package/src/controllers/visibility_controller.js +32 -0
- package/src/focus.js +128 -0
- package/src/index.js +23 -0
- package/src/keyboard.js +92 -0
- package/src/plumbers/calendar.js +399 -0
- package/src/plumbers/content_loader.js +134 -0
- package/src/plumbers/dismisser.js +82 -0
- package/src/plumbers/flipper.js +272 -0
- package/src/plumbers/index.js +10 -0
- package/src/plumbers/plumber/index.js +110 -0
- package/src/plumbers/plumber/support.js +101 -0
- package/src/plumbers/shifter.js +164 -0
- package/src/plumbers/visibility.js +136 -0
|
@@ -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
|
+
};
|