stimeo-ui 0.17.0 → 0.18.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +136 -2
- data/README.md +19 -10
- data/dist/cable/index.js +22 -14
- data/dist/controllers/accordion_controller.js +14 -6
- data/dist/controllers/alert_dialog_controller.js +145 -118
- data/dist/controllers/announcer_controller.js +113 -17
- data/dist/controllers/auto_submit_controller.js +185 -60
- data/dist/controllers/avatar_controller.js +55 -43
- data/dist/controllers/bulk_select_controller.js +64 -50
- data/dist/controllers/calendar_controller.js +55 -53
- data/dist/controllers/carousel_controller.js +124 -86
- data/dist/controllers/character_counter_controller.js +291 -95
- data/dist/controllers/checkbox_controller.js +6 -0
- data/dist/controllers/clipboard_controller.js +118 -27
- data/dist/controllers/collapsible_controller.js +52 -49
- data/dist/controllers/color_picker_controller.js +29 -7
- data/dist/controllers/combobox_controller.js +210 -71
- data/dist/controllers/command_palette_controller.js +290 -140
- data/dist/controllers/conditional_fields_controller.js +145 -58
- data/dist/controllers/confirm_controller.js +145 -118
- data/dist/controllers/context_menu_controller.js +138 -73
- data/dist/controllers/countdown_controller.js +109 -21
- data/dist/controllers/currency_input_controller.js +446 -72
- data/dist/controllers/data_grid_controller.js +134 -28
- data/dist/controllers/date_range_picker_controller.js +93 -72
- data/dist/controllers/dialog_controller.js +145 -118
- data/dist/controllers/direct_upload_controller.js +24 -14
- data/dist/controllers/dirty_form_controller.js +68 -31
- data/dist/controllers/dismissible_controller.js +66 -46
- data/dist/controllers/drawer_controller.js +145 -118
- data/dist/controllers/dropdown_controller.js +76 -63
- data/dist/controllers/editable_controller.js +200 -62
- data/dist/controllers/empty_state_controller.js +13 -6
- data/dist/controllers/file_dropzone_controller.js +55 -48
- data/dist/controllers/filter_controller.js +9 -2
- data/dist/controllers/flash_controller.js +66 -33
- data/dist/controllers/focus_controller.js +39 -14
- data/dist/controllers/form_field_controller.js +52 -43
- data/dist/controllers/form_validation_controller.js +18 -11
- data/dist/controllers/frame_loading_controller.js +55 -48
- data/dist/controllers/hover_card_controller.js +67 -61
- data/dist/controllers/idle_controller.js +10 -6
- data/dist/controllers/input_mask_controller.js +434 -24
- data/dist/controllers/intersection_controller.js +3 -0
- data/dist/controllers/lazy_frame_controller.js +15 -3
- data/dist/controllers/listbox_controller.js +260 -79
- data/dist/controllers/local_time_controller.js +48 -13
- data/dist/controllers/masonry_controller.js +11 -4
- data/dist/controllers/menu_controller.js +138 -82
- data/dist/controllers/menubar_controller.js +65 -19
- data/dist/controllers/meter_controller.js +270 -24
- data/dist/controllers/multi_select_controller.js +272 -95
- data/dist/controllers/navigation_menu_controller.js +67 -19
- data/dist/controllers/nested_form_controller.js +98 -64
- data/dist/controllers/network_status_controller.js +70 -52
- data/dist/controllers/number_input_controller.js +344 -94
- data/dist/controllers/optimistic_controller.js +29 -11
- data/dist/controllers/otp_controller.js +502 -104
- data/dist/controllers/overflow_indicator_controller.js +6 -0
- data/dist/controllers/overflow_menu_controller.js +136 -21
- data/dist/controllers/pagination_controller.js +47 -14
- data/dist/controllers/password_reveal_controller.js +52 -49
- data/dist/controllers/password_strength_controller.js +13 -6
- data/dist/controllers/persist_controller.js +48 -128
- data/dist/controllers/pointer_drag_controller.js +255 -24
- data/dist/controllers/popover_controller.js +91 -65
- data/dist/controllers/portal_controller.js +6 -0
- data/dist/controllers/preview_guard_controller.js +14 -3
- data/dist/controllers/progress_controller.js +271 -27
- data/dist/controllers/radio_group_controller.js +167 -80
- data/dist/controllers/range_slider_controller.js +27 -7
- data/dist/controllers/rating_controller.js +93 -57
- data/dist/controllers/read_more_controller.js +10 -6
- data/dist/controllers/reading_progress_controller.js +22 -5
- data/dist/controllers/relative_time_controller.js +75 -11
- data/dist/controllers/reset_on_restore_controller.js +6 -0
- data/dist/controllers/resizable_controller.js +55 -22
- data/dist/controllers/roving_controller.js +10 -4
- data/dist/controllers/scroll_area_controller.js +129 -88
- data/dist/controllers/scroll_restore_controller.js +14 -2
- data/dist/controllers/scroll_visibility_controller.js +93 -60
- data/dist/controllers/scrollspy_controller.js +11 -5
- data/dist/controllers/separator_controller.js +59 -43
- data/dist/controllers/sidebar_controller.js +150 -122
- data/dist/controllers/skeleton_controller.js +55 -48
- data/dist/controllers/slider_controller.js +27 -6
- data/dist/controllers/smart_sticky_header_controller.js +13 -5
- data/dist/controllers/sortable_controller.js +27 -14
- data/dist/controllers/spinner_controller.js +55 -48
- data/dist/controllers/step_indicator_controller.js +6 -0
- data/dist/controllers/stepper_controller.js +6 -0
- data/dist/controllers/stick_to_bottom_controller.js +19 -8
- data/dist/controllers/sticky_observer_controller.js +49 -43
- data/dist/controllers/submit_once_controller.js +145 -85
- data/dist/controllers/switch_controller.js +172 -28
- data/dist/controllers/tabs_controller.js +6 -1
- data/dist/controllers/tags_input_controller.js +157 -28
- data/dist/controllers/textarea_autosize_controller.js +40 -19
- data/dist/controllers/theme_controller.js +10 -4
- data/dist/controllers/time_picker_controller.js +192 -54
- data/dist/controllers/toast_controller.js +58 -27
- data/dist/controllers/toggle_group_controller.js +158 -56
- data/dist/controllers/toolbar_controller.js +150 -21
- data/dist/controllers/tree_view_controller.js +134 -25
- data/dist/index.js +2515 -1315
- data/dist/positioning/index.js +6 -0
- data/lib/generators/stimeo/install/install_generator.rb +7 -2
- data/lib/generators/stimeo/install/templates/stimeo.js +19 -6
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +2 -2
|
@@ -30,17 +30,31 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
30
30
|
if (!event.key.startsWith("Arrow")) return false;
|
|
31
31
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
32
32
|
}
|
|
33
|
+
function hasModifierChord(event) {
|
|
34
|
+
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
38
|
+
if (element.getAttribute(name) === value) return false;
|
|
39
|
+
if (value === null) element.removeAttribute(name);
|
|
40
|
+
else element.setAttribute(name, value);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
33
43
|
|
|
34
44
|
function sharedRegistry(key, create) {
|
|
35
45
|
const symbol = Symbol.for(key);
|
|
36
46
|
const scope = globalThis;
|
|
37
47
|
const existing = scope[symbol];
|
|
38
48
|
if (existing !== void 0) return existing;
|
|
39
|
-
const
|
|
40
|
-
Object.defineProperty(globalThis, symbol, { value:
|
|
41
|
-
return
|
|
49
|
+
const registry2 = create();
|
|
50
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
51
|
+
return registry2;
|
|
42
52
|
}
|
|
43
53
|
|
|
54
|
+
var registry = sharedRegistry(
|
|
55
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
56
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
57
|
+
);
|
|
44
58
|
function isAttributeValue(value) {
|
|
45
59
|
return value === null || typeof value === "string";
|
|
46
60
|
}
|
|
@@ -57,24 +71,46 @@ function readLeaseRecord(element, record) {
|
|
|
57
71
|
return void 0;
|
|
58
72
|
}
|
|
59
73
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
);
|
|
74
|
+
function attributeStack(element, attribute) {
|
|
75
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
76
|
+
registry.stacks.set(element, byAttribute);
|
|
77
|
+
const known = byAttribute.get(attribute);
|
|
78
|
+
if (known) return known;
|
|
79
|
+
const found = [];
|
|
80
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
81
|
+
const read = readLeaseRecord(element, record);
|
|
82
|
+
if (read) found.push({ ...read, record });
|
|
83
|
+
}
|
|
84
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
85
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
86
|
+
lease: null,
|
|
87
|
+
record,
|
|
88
|
+
written,
|
|
89
|
+
original,
|
|
90
|
+
linked: linked && index > 0
|
|
91
|
+
}));
|
|
92
|
+
byAttribute.set(attribute, stack);
|
|
93
|
+
return stack;
|
|
94
|
+
}
|
|
95
|
+
function findHold(stack, lease, record) {
|
|
96
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
97
|
+
}
|
|
98
|
+
var AttributeLease = class {
|
|
65
99
|
#attribute;
|
|
66
100
|
#record;
|
|
67
101
|
#base;
|
|
102
|
+
#preserveAuthor;
|
|
68
103
|
#held = /* @__PURE__ */ new Set();
|
|
69
104
|
constructor(attribute, owner, options = {}) {
|
|
70
105
|
this.#attribute = attribute;
|
|
71
106
|
this.#record = `data-${owner}-${attribute}-lease`;
|
|
72
107
|
this.#base = options.base === true;
|
|
73
|
-
|
|
108
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
109
|
+
const names = registry.names;
|
|
74
110
|
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
75
111
|
}
|
|
76
112
|
write(element, value) {
|
|
77
|
-
const stack = this.#
|
|
113
|
+
const stack = attributeStack(element, this.#attribute);
|
|
78
114
|
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
79
115
|
this.#held.add(element);
|
|
80
116
|
const shows = !this.#covered(element, stack, hold);
|
|
@@ -84,8 +120,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
84
120
|
}
|
|
85
121
|
return(element) {
|
|
86
122
|
const held = this.#held.delete(element);
|
|
87
|
-
const known =
|
|
88
|
-
const stack = held || known || element.hasAttribute(this.#record) ? this.#
|
|
123
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
124
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
89
125
|
const hold = this.#take(stack);
|
|
90
126
|
if (!hold) return false;
|
|
91
127
|
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
@@ -103,31 +139,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
103
139
|
returnAll() {
|
|
104
140
|
for (const element of Array.from(this.#held)) this.return(element);
|
|
105
141
|
}
|
|
106
|
-
#stack(element) {
|
|
107
|
-
const stacks = _AttributeLease.#registry.stacks;
|
|
108
|
-
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
109
|
-
stacks.set(element, byAttribute);
|
|
110
|
-
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
111
|
-
byAttribute.set(this.#attribute, stack);
|
|
112
|
-
return stack;
|
|
113
|
-
}
|
|
114
|
-
#seed(element) {
|
|
115
|
-
const found = [];
|
|
116
|
-
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
117
|
-
const read = readLeaseRecord(element, record);
|
|
118
|
-
if (read) found.push({ ...read, record });
|
|
119
|
-
}
|
|
120
|
-
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
121
|
-
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
122
|
-
lease: null,
|
|
123
|
-
record,
|
|
124
|
-
written,
|
|
125
|
-
original,
|
|
126
|
-
linked: linked && index > 0
|
|
127
|
-
}));
|
|
128
|
-
}
|
|
129
142
|
#take(stack) {
|
|
130
|
-
const hold = stack
|
|
143
|
+
const hold = findHold(stack, this, this.#record);
|
|
131
144
|
if (hold) hold.lease = this;
|
|
132
145
|
return hold;
|
|
133
146
|
}
|
|
@@ -136,7 +149,7 @@ var AttributeLease = class _AttributeLease {
|
|
|
136
149
|
const top = stack.at(-1);
|
|
137
150
|
const bottom = stack[0];
|
|
138
151
|
const over = top?.written === current ? top : void 0;
|
|
139
|
-
const from = this.#base ? bottom : over;
|
|
152
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
140
153
|
const hold = {
|
|
141
154
|
lease: this,
|
|
142
155
|
record: this.#record,
|
|
@@ -166,15 +179,11 @@ var AttributeLease = class _AttributeLease {
|
|
|
166
179
|
);
|
|
167
180
|
});
|
|
168
181
|
for (const [name, recorded] of records) {
|
|
169
|
-
|
|
170
|
-
if (recorded === null) element.removeAttribute(name);
|
|
171
|
-
else element.setAttribute(name, recorded);
|
|
182
|
+
setAttributeIfChanged(element, name, recorded);
|
|
172
183
|
}
|
|
173
184
|
}
|
|
174
185
|
#reflect(element, value) {
|
|
175
|
-
|
|
176
|
-
if (value === null) element.removeAttribute(this.#attribute);
|
|
177
|
-
else element.setAttribute(this.#attribute, value);
|
|
186
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
178
187
|
}
|
|
179
188
|
};
|
|
180
189
|
|
|
@@ -214,6 +223,9 @@ var MicrotaskCoalescer = class {
|
|
|
214
223
|
this.#queued = false;
|
|
215
224
|
this.#generation += 1;
|
|
216
225
|
}
|
|
226
|
+
get pending() {
|
|
227
|
+
return this.#queued;
|
|
228
|
+
}
|
|
217
229
|
schedule() {
|
|
218
230
|
if (!this.#active || this.#queued) return;
|
|
219
231
|
this.#queued = true;
|
|
@@ -244,6 +256,9 @@ var MorphRenderWatcher = class {
|
|
|
244
256
|
schedule() {
|
|
245
257
|
this.#pass.schedule();
|
|
246
258
|
}
|
|
259
|
+
get pending() {
|
|
260
|
+
return this.#pass.pending;
|
|
261
|
+
}
|
|
247
262
|
disconnect() {
|
|
248
263
|
this.#listeners.dispose();
|
|
249
264
|
this.#pass.cancel();
|
|
@@ -323,6 +338,11 @@ var RovingTabindex = class {
|
|
|
323
338
|
if (focus) items[index]?.focus();
|
|
324
339
|
}
|
|
325
340
|
};
|
|
341
|
+
function deepActiveElement(ownerDocument) {
|
|
342
|
+
let active = ownerDocument.activeElement;
|
|
343
|
+
while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
|
|
344
|
+
return active;
|
|
345
|
+
}
|
|
326
346
|
|
|
327
347
|
function queryOpenDescendants(root, selector) {
|
|
328
348
|
const matches = [];
|
|
@@ -340,6 +360,9 @@ function queryOpenDescendants(root, selector) {
|
|
|
340
360
|
return matches;
|
|
341
361
|
}
|
|
342
362
|
|
|
363
|
+
function holdsFocus(element) {
|
|
364
|
+
return element.matches(":focus") || deepActiveElement(element.ownerDocument) === element;
|
|
365
|
+
}
|
|
343
366
|
var TabindexLoan = class _TabindexLoan {
|
|
344
367
|
static #registry = sharedRegistry(
|
|
345
368
|
"stimeo-ui.tabindex-loan.registry.v1",
|
|
@@ -347,19 +370,22 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
347
370
|
);
|
|
348
371
|
#value;
|
|
349
372
|
#record;
|
|
373
|
+
#preserveFocus;
|
|
350
374
|
#lent = /* @__PURE__ */ new Set();
|
|
351
|
-
constructor(value, owner) {
|
|
375
|
+
constructor(value, owner, options = {}) {
|
|
352
376
|
this.#value = value;
|
|
353
377
|
this.#record = `data-${owner}-tabindex-loan`;
|
|
378
|
+
this.#preserveFocus = options.preserveFocus === true;
|
|
354
379
|
_TabindexLoan.#registry.names.add(this.#record);
|
|
355
380
|
}
|
|
356
|
-
lend(element) {
|
|
381
|
+
lend(element, options = {}) {
|
|
357
382
|
const stack = this.#stack(element);
|
|
358
383
|
const current = element.getAttribute("tabindex");
|
|
359
384
|
const held = stack.find((hold) => hold.loan === this) ?? stack.find(
|
|
360
385
|
(hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
|
|
361
386
|
);
|
|
362
387
|
const linked = stack.at(-1)?.value === current;
|
|
388
|
+
if (!held && options.onlyIfUnfocusable && (current !== null || element.tabIndex >= 0)) return;
|
|
363
389
|
if (!held && current !== null && !linked) return;
|
|
364
390
|
this.#lent.add(element);
|
|
365
391
|
if (held) held.loan = this;
|
|
@@ -369,19 +395,28 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
369
395
|
element.setAttribute("tabindex", this.#value);
|
|
370
396
|
}
|
|
371
397
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
398
|
+
return(element) {
|
|
399
|
+
const stack = this.#stack(element);
|
|
400
|
+
const hold = stack.find((held) => held.loan === this) ?? stack.find(
|
|
401
|
+
(held) => held.loan === null && held.record === this.#record && held.value === this.#value
|
|
402
|
+
);
|
|
403
|
+
if (!hold) return false;
|
|
404
|
+
if (!this.#release(element, stack, hold, this.#preserveFocus)) {
|
|
405
|
+
hold.loan = this;
|
|
406
|
+
this.#lent.add(element);
|
|
407
|
+
return false;
|
|
377
408
|
}
|
|
378
|
-
this.#lent.
|
|
409
|
+
this.#lent.delete(element);
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
returnAll() {
|
|
413
|
+
for (const element of Array.from(this.#lent)) this.return(element);
|
|
379
414
|
}
|
|
380
415
|
reclaim(element) {
|
|
381
416
|
if (element.getAttribute(this.#record) !== this.#value) return;
|
|
382
417
|
const stack = this.#stack(element);
|
|
383
418
|
const hold = stack.find((held) => held.loan === null && held.record === this.#record);
|
|
384
|
-
if (hold && !element
|
|
419
|
+
if (hold && !holdsFocus(element)) this.#release(element, stack, hold);
|
|
385
420
|
}
|
|
386
421
|
reclaimWithin(root) {
|
|
387
422
|
for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
|
|
@@ -405,26 +440,26 @@ var TabindexLoan = class _TabindexLoan {
|
|
|
405
440
|
for (const [index, hold] of holds.entries()) hold.linked = index > 0;
|
|
406
441
|
return holds;
|
|
407
442
|
}
|
|
408
|
-
#release(element, stack, hold) {
|
|
443
|
+
#release(element, stack, hold, preserveFocus = false) {
|
|
409
444
|
const current = element.getAttribute("tabindex");
|
|
410
445
|
const index = stack.indexOf(hold);
|
|
411
446
|
const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
|
|
447
|
+
if (owns && preserveFocus && holdsFocus(element)) return false;
|
|
412
448
|
stack.splice(index, 1);
|
|
413
449
|
const above = stack[index];
|
|
414
450
|
if (above) above.linked &&= hold.linked;
|
|
415
451
|
this.#mark(element, stack);
|
|
416
|
-
if (!owns) return;
|
|
452
|
+
if (!owns) return true;
|
|
417
453
|
const below = hold.linked ? stack[index - 1] : void 0;
|
|
418
454
|
if (!below) element.removeAttribute("tabindex");
|
|
419
455
|
else if (below.value !== current) element.setAttribute("tabindex", below.value);
|
|
456
|
+
return true;
|
|
420
457
|
}
|
|
421
458
|
#mark(element, stack) {
|
|
422
459
|
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
423
460
|
for (const hold of stack) records.set(hold.record, hold.value);
|
|
424
461
|
for (const [name, recorded] of records) {
|
|
425
|
-
|
|
426
|
-
if (recorded === null) element.removeAttribute(name);
|
|
427
|
-
else element.setAttribute(name, recorded);
|
|
462
|
+
setAttributeIfChanged(element, name, recorded);
|
|
428
463
|
}
|
|
429
464
|
}
|
|
430
465
|
};
|
|
@@ -529,6 +564,7 @@ var RatingController = class _RatingController extends Controller {
|
|
|
529
564
|
}
|
|
530
565
|
onKeydown(event) {
|
|
531
566
|
if (event.defaultPrevented || isReservedArrowChord(event) || this.readonlyValue) return;
|
|
567
|
+
if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
|
|
532
568
|
const current = this.#normalize(this.#safeValue);
|
|
533
569
|
let next = null;
|
|
534
570
|
const rtl = isRtl(this.element);
|
|
@@ -107,6 +107,13 @@ function stateReasonFor(event) {
|
|
|
107
107
|
return "user";
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
111
|
+
if (element.getAttribute(name) === value) return false;
|
|
112
|
+
if (value === null) element.removeAttribute(name);
|
|
113
|
+
else element.setAttribute(name, value);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
110
117
|
function sharedRegistry(key, create) {
|
|
111
118
|
const symbol = Symbol.for(key);
|
|
112
119
|
const scope = globalThis;
|
|
@@ -161,9 +168,8 @@ var StateRegions = class _StateRegions {
|
|
|
161
168
|
const below = hold.linked ? stack[index - 1] : void 0;
|
|
162
169
|
if (below) {
|
|
163
170
|
if (region.hidden !== below.written) region.hidden = below.written;
|
|
164
|
-
} else
|
|
165
|
-
|
|
166
|
-
else region.setAttribute("hidden", hold.authored);
|
|
171
|
+
} else {
|
|
172
|
+
setAttributeIfChanged(region, "hidden", hold.authored);
|
|
167
173
|
}
|
|
168
174
|
}
|
|
169
175
|
this.#mark(region, stack);
|
|
@@ -238,9 +244,7 @@ var StateRegions = class _StateRegions {
|
|
|
238
244
|
);
|
|
239
245
|
});
|
|
240
246
|
for (const [name, recorded] of records) {
|
|
241
|
-
|
|
242
|
-
if (recorded === null) region.removeAttribute(name);
|
|
243
|
-
else region.setAttribute(name, recorded);
|
|
247
|
+
setAttributeIfChanged(region, name, recorded);
|
|
244
248
|
}
|
|
245
249
|
}
|
|
246
250
|
#recorded(region, name) {
|
|
@@ -71,6 +71,24 @@ var LayoutObserver = class {
|
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
+
var ListenerSet = class {
|
|
75
|
+
#abort = new AbortController();
|
|
76
|
+
add(target, type, handler, options) {
|
|
77
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
78
|
+
}
|
|
79
|
+
dispose() {
|
|
80
|
+
this.#abort.abort();
|
|
81
|
+
this.#abort = new AbortController();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
86
|
+
if (element.getAttribute(name) === value) return false;
|
|
87
|
+
if (value === null) element.removeAttribute(name);
|
|
88
|
+
else element.setAttribute(name, value);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
74
92
|
function sharedRegistry(key, create) {
|
|
75
93
|
const symbol = Symbol.for(key);
|
|
76
94
|
const scope = globalThis;
|
|
@@ -210,9 +228,7 @@ var StylePropertyLease = class _StylePropertyLease {
|
|
|
210
228
|
);
|
|
211
229
|
});
|
|
212
230
|
for (const [name, recorded] of records) {
|
|
213
|
-
|
|
214
|
-
if (recorded === null) element.removeAttribute(name);
|
|
215
|
-
else element.setAttribute(name, recorded);
|
|
231
|
+
setAttributeIfChanged(element, name, recorded);
|
|
216
232
|
}
|
|
217
233
|
}
|
|
218
234
|
#carries(element, hold) {
|
|
@@ -238,10 +254,11 @@ var ReadingProgressController = class extends Controller {
|
|
|
238
254
|
#progress = -1;
|
|
239
255
|
#baselined = false;
|
|
240
256
|
#onScroll = () => this.#frames.schedule(() => this.#measure());
|
|
257
|
+
#listeners = new ListenerSet();
|
|
241
258
|
connect() {
|
|
242
259
|
this.#progress = -1;
|
|
243
260
|
this.#baselined = false;
|
|
244
|
-
|
|
261
|
+
this.#listeners.add(window, "scroll", this.#onScroll, { passive: true, capture: true });
|
|
245
262
|
this.#layout.observe(this.element);
|
|
246
263
|
this.#layout.observeViewport();
|
|
247
264
|
this.#measure();
|
|
@@ -251,7 +268,7 @@ var ReadingProgressController = class extends Controller {
|
|
|
251
268
|
});
|
|
252
269
|
}
|
|
253
270
|
disconnect() {
|
|
254
|
-
|
|
271
|
+
this.#listeners.dispose();
|
|
255
272
|
this.#layout.disconnect();
|
|
256
273
|
this.#frames.cancel();
|
|
257
274
|
this.#lease.return(this.element);
|
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
4
|
+
if (element.getAttribute(name) === value) return false;
|
|
5
|
+
if (value === null) element.removeAttribute(name);
|
|
6
|
+
else element.setAttribute(name, value);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
var DATETIME = /^([+-]\d{6}|\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
11
|
+
function readDatetime(element) {
|
|
12
|
+
const raw = element.getAttribute("datetime");
|
|
13
|
+
if (raw === null) return null;
|
|
14
|
+
const match = DATETIME.exec(raw.trim());
|
|
15
|
+
if (match === null) return null;
|
|
16
|
+
const [
|
|
17
|
+
,
|
|
18
|
+
yearText,
|
|
19
|
+
monthText,
|
|
20
|
+
dayText,
|
|
21
|
+
hourText = "00",
|
|
22
|
+
minuteText = "00",
|
|
23
|
+
secondText = "00",
|
|
24
|
+
fraction = "",
|
|
25
|
+
zone = "Z"
|
|
26
|
+
] = match;
|
|
27
|
+
const year = Number(yearText);
|
|
28
|
+
const month = Number(monthText);
|
|
29
|
+
const day = Number(dayText);
|
|
30
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
31
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
32
|
+
if (month < 1 || month > 12 || day < 1 || day > (days[month - 1] ?? 0)) return null;
|
|
33
|
+
const hour = Number(hourText);
|
|
34
|
+
const minute = Number(minuteText);
|
|
35
|
+
const second = Number(secondText);
|
|
36
|
+
if (hour === 24 && (minute !== 0 || second !== 0 || /[1-9]/.test(fraction))) return null;
|
|
37
|
+
let offset = zone;
|
|
38
|
+
if (zone !== "Z") {
|
|
39
|
+
const digits = zone.slice(1).replace(":", "");
|
|
40
|
+
const offsetHour = digits.slice(0, 2);
|
|
41
|
+
const offsetMinute = digits.slice(2);
|
|
42
|
+
offset = `${zone[0]}${offsetHour}:${offsetMinute}`;
|
|
43
|
+
}
|
|
44
|
+
const milliseconds = fraction.slice(0, 3).padEnd(3, "0");
|
|
45
|
+
const canonical = `${yearText}-${monthText}-${dayText}T${hourText}:${minuteText}:${secondText}.${milliseconds}${offset}`;
|
|
46
|
+
const instant = Date.parse(canonical);
|
|
47
|
+
return Number.isNaN(instant) ? null : instant;
|
|
48
|
+
}
|
|
49
|
+
|
|
3
50
|
var formatters = /* @__PURE__ */ new WeakMap();
|
|
4
51
|
function intlFormatter(factory, locale, options, fallbackLocale) {
|
|
5
52
|
const cache = cacheFor(factory);
|
|
@@ -59,6 +106,9 @@ var MicrotaskCoalescer = class {
|
|
|
59
106
|
this.#queued = false;
|
|
60
107
|
this.#generation += 1;
|
|
61
108
|
}
|
|
109
|
+
get pending() {
|
|
110
|
+
return this.#queued;
|
|
111
|
+
}
|
|
62
112
|
schedule() {
|
|
63
113
|
if (!this.#active || this.#queued) return;
|
|
64
114
|
this.#queued = true;
|
|
@@ -89,6 +139,9 @@ var MorphRenderWatcher = class {
|
|
|
89
139
|
schedule() {
|
|
90
140
|
this.#pass.schedule();
|
|
91
141
|
}
|
|
142
|
+
get pending() {
|
|
143
|
+
return this.#pass.pending;
|
|
144
|
+
}
|
|
92
145
|
disconnect() {
|
|
93
146
|
this.#listeners.dispose();
|
|
94
147
|
this.#pass.cancel();
|
|
@@ -202,6 +255,12 @@ var RelativeTimeController = class _RelativeTimeController extends Controller {
|
|
|
202
255
|
};
|
|
203
256
|
#timers = new SafeTimeout();
|
|
204
257
|
#resync = new MorphRenderWatcher(() => {
|
|
258
|
+
const target = readDatetime(this.element) ?? Number.NaN;
|
|
259
|
+
if (!Object.is(target, this.#targetMs)) {
|
|
260
|
+
this.#targetMs = target;
|
|
261
|
+
this.#adoptAuthoredText();
|
|
262
|
+
this.#valuesDirty = true;
|
|
263
|
+
}
|
|
205
264
|
if (this.#valuesDirty) {
|
|
206
265
|
this.#valuesDirty = false;
|
|
207
266
|
this.#resyncToValues();
|
|
@@ -210,25 +269,32 @@ var RelativeTimeController = class _RelativeTimeController extends Controller {
|
|
|
210
269
|
}
|
|
211
270
|
});
|
|
212
271
|
#valuesDirty = false;
|
|
272
|
+
#datetimeWatch = new MutationObserver(() => this.#resync.schedule());
|
|
213
273
|
#targetMs = Number.NaN;
|
|
214
274
|
#absoluteText = "";
|
|
215
275
|
connect() {
|
|
276
|
+
this.#timers.clearAll();
|
|
216
277
|
this.#valuesDirty = false;
|
|
217
278
|
this.#resync.observe(this.element);
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const recorded = this.#recordedText();
|
|
222
|
-
if (recorded !== null) this.#absoluteText = recorded;
|
|
223
|
-
}
|
|
224
|
-
this.#targetMs = Date.parse(this.element.getAttribute("datetime") ?? "");
|
|
279
|
+
this.#datetimeWatch.observe(this.element, { attributeFilter: ["datetime"] });
|
|
280
|
+
this.#adoptAuthoredText();
|
|
281
|
+
this.#targetMs = readDatetime(this.element) ?? Number.NaN;
|
|
225
282
|
if (Number.isNaN(this.#targetMs)) return;
|
|
226
283
|
this.#schedule();
|
|
227
284
|
}
|
|
228
285
|
disconnect() {
|
|
229
286
|
this.#resync.disconnect();
|
|
287
|
+
this.#datetimeWatch.disconnect();
|
|
230
288
|
this.#timers.clearAll();
|
|
231
289
|
}
|
|
290
|
+
#adoptAuthoredText() {
|
|
291
|
+
if (this.element.getAttribute("data-state") !== "relative") {
|
|
292
|
+
this.#absoluteText = (this.element.textContent ?? "").trim();
|
|
293
|
+
} else {
|
|
294
|
+
const recorded = this.#recordedText();
|
|
295
|
+
if (recorded !== null) this.#absoluteText = recorded;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
232
298
|
get #textRecord() {
|
|
233
299
|
return `data-${this.identifier}-${TEXT_RECORD}`;
|
|
234
300
|
}
|
|
@@ -253,8 +319,8 @@ var RelativeTimeController = class _RelativeTimeController extends Controller {
|
|
|
253
319
|
this.#resync.schedule();
|
|
254
320
|
}
|
|
255
321
|
#resyncToValues() {
|
|
256
|
-
if (Number.isNaN(this.#targetMs)) return;
|
|
257
322
|
this.#timers.clearAll();
|
|
323
|
+
if (Number.isNaN(this.#targetMs)) return;
|
|
258
324
|
this.#schedule();
|
|
259
325
|
}
|
|
260
326
|
#schedule() {
|
|
@@ -282,9 +348,7 @@ var RelativeTimeController = class _RelativeTimeController extends Controller {
|
|
|
282
348
|
this.element.textContent = formatter.format(value, scale.unit);
|
|
283
349
|
this.element.setAttribute("data-state", "relative");
|
|
284
350
|
const record = JSON.stringify(this.#absoluteText);
|
|
285
|
-
|
|
286
|
-
this.element.setAttribute(this.#textRecord, record);
|
|
287
|
-
}
|
|
351
|
+
setAttributeIfChanged(this.element, this.#textRecord, record);
|
|
288
352
|
return nextDelay;
|
|
289
353
|
}
|
|
290
354
|
get #formatter() {
|
|
@@ -46,6 +46,9 @@ var MicrotaskCoalescer = class {
|
|
|
46
46
|
this.#queued = false;
|
|
47
47
|
this.#generation += 1;
|
|
48
48
|
}
|
|
49
|
+
get pending() {
|
|
50
|
+
return this.#queued;
|
|
51
|
+
}
|
|
49
52
|
schedule() {
|
|
50
53
|
if (!this.#active || this.#queued) return;
|
|
51
54
|
this.#queued = true;
|
|
@@ -76,6 +79,9 @@ var MorphRenderWatcher = class {
|
|
|
76
79
|
schedule() {
|
|
77
80
|
this.#pass.schedule();
|
|
78
81
|
}
|
|
82
|
+
get pending() {
|
|
83
|
+
return this.#pass.pending;
|
|
84
|
+
}
|
|
79
85
|
disconnect() {
|
|
80
86
|
this.#listeners.dispose();
|
|
81
87
|
this.#pass.cancel();
|