stimeo-ui 0.17.0 → 0.18.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +106 -1
- 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 +289 -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 +64 -46
- data/dist/controllers/drawer_controller.js +145 -118
- data/dist/controllers/dropdown_controller.js +76 -63
- data/dist/controllers/editable_controller.js +199 -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 +411 -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 +251 -94
- 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 +249 -23
- data/dist/controllers/popover_controller.js +82 -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 +2451 -1312
- 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
|
@@ -82,6 +82,9 @@ var MicrotaskCoalescer = class {
|
|
|
82
82
|
this.#queued = false;
|
|
83
83
|
this.#generation += 1;
|
|
84
84
|
}
|
|
85
|
+
get pending() {
|
|
86
|
+
return this.#queued;
|
|
87
|
+
}
|
|
85
88
|
schedule() {
|
|
86
89
|
if (!this.#active || this.#queued) return;
|
|
87
90
|
this.#queued = true;
|
|
@@ -112,6 +115,9 @@ var MorphRenderWatcher = class {
|
|
|
112
115
|
schedule() {
|
|
113
116
|
this.#pass.schedule();
|
|
114
117
|
}
|
|
118
|
+
get pending() {
|
|
119
|
+
return this.#pass.pending;
|
|
120
|
+
}
|
|
115
121
|
disconnect() {
|
|
116
122
|
this.#listeners.dispose();
|
|
117
123
|
this.#pass.cancel();
|
|
@@ -167,6 +173,13 @@ function prefersReducedMotion() {
|
|
|
167
173
|
return typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
168
174
|
}
|
|
169
175
|
|
|
176
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
177
|
+
if (element.getAttribute(name) === value) return false;
|
|
178
|
+
if (value === null) element.removeAttribute(name);
|
|
179
|
+
else element.setAttribute(name, value);
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
170
183
|
function sharedRegistry(key, create) {
|
|
171
184
|
const symbol = Symbol.for(key);
|
|
172
185
|
const scope = globalThis;
|
|
@@ -221,9 +234,8 @@ var StateRegions = class _StateRegions {
|
|
|
221
234
|
const below = hold.linked ? stack[index - 1] : void 0;
|
|
222
235
|
if (below) {
|
|
223
236
|
if (region.hidden !== below.written) region.hidden = below.written;
|
|
224
|
-
} else
|
|
225
|
-
|
|
226
|
-
else region.setAttribute("hidden", hold.authored);
|
|
237
|
+
} else {
|
|
238
|
+
setAttributeIfChanged(region, "hidden", hold.authored);
|
|
227
239
|
}
|
|
228
240
|
}
|
|
229
241
|
this.#mark(region, stack);
|
|
@@ -298,9 +310,7 @@ var StateRegions = class _StateRegions {
|
|
|
298
310
|
);
|
|
299
311
|
});
|
|
300
312
|
for (const [name, recorded] of records) {
|
|
301
|
-
|
|
302
|
-
if (recorded === null) region.removeAttribute(name);
|
|
303
|
-
else region.setAttribute(name, recorded);
|
|
313
|
+
setAttributeIfChanged(region, name, recorded);
|
|
304
314
|
}
|
|
305
315
|
}
|
|
306
316
|
#recorded(region, name) {
|
|
@@ -357,6 +367,7 @@ var StickToBottomController = class _StickToBottomController extends Controller
|
|
|
357
367
|
this.#updatePinned();
|
|
358
368
|
this.#setHasNew(this.#hasNew);
|
|
359
369
|
});
|
|
370
|
+
#listeners = new ListenerSet();
|
|
360
371
|
connect() {
|
|
361
372
|
this.#morphRender.observe(this.element);
|
|
362
373
|
this.#connected = true;
|
|
@@ -364,14 +375,14 @@ var StickToBottomController = class _StickToBottomController extends Controller
|
|
|
364
375
|
this.#setHasNew(false);
|
|
365
376
|
this.#pinned = this.#isPinned();
|
|
366
377
|
this.#reflectPinned();
|
|
367
|
-
this.element
|
|
378
|
+
this.#listeners.add(this.element, "scroll", this.#onScroll, { passive: true });
|
|
368
379
|
this.#syncWatched();
|
|
369
380
|
if (!this.#measurable()) this.#waitForLayout();
|
|
370
381
|
}
|
|
371
382
|
disconnect() {
|
|
372
383
|
this.#morphRender.disconnect();
|
|
373
384
|
this.#connected = false;
|
|
374
|
-
this.
|
|
385
|
+
this.#listeners.dispose();
|
|
375
386
|
this.#stopWatching();
|
|
376
387
|
this.#stopWaitingForLayout();
|
|
377
388
|
}
|
|
@@ -1,15 +1,26 @@
|
|
|
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
|
+
|
|
3
10
|
function sharedRegistry(key, create) {
|
|
4
11
|
const symbol = Symbol.for(key);
|
|
5
12
|
const scope = globalThis;
|
|
6
13
|
const existing = scope[symbol];
|
|
7
14
|
if (existing !== void 0) return existing;
|
|
8
|
-
const
|
|
9
|
-
Object.defineProperty(globalThis, symbol, { value:
|
|
10
|
-
return
|
|
15
|
+
const registry2 = create();
|
|
16
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
17
|
+
return registry2;
|
|
11
18
|
}
|
|
12
19
|
|
|
20
|
+
var registry = sharedRegistry(
|
|
21
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
22
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
23
|
+
);
|
|
13
24
|
function isAttributeValue(value) {
|
|
14
25
|
return value === null || typeof value === "string";
|
|
15
26
|
}
|
|
@@ -26,24 +37,46 @@ function readLeaseRecord(element, record) {
|
|
|
26
37
|
return void 0;
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
40
|
+
function attributeStack(element, attribute) {
|
|
41
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
42
|
+
registry.stacks.set(element, byAttribute);
|
|
43
|
+
const known = byAttribute.get(attribute);
|
|
44
|
+
if (known) return known;
|
|
45
|
+
const found = [];
|
|
46
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
47
|
+
const read = readLeaseRecord(element, record);
|
|
48
|
+
if (read) found.push({ ...read, record });
|
|
49
|
+
}
|
|
50
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
51
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
52
|
+
lease: null,
|
|
53
|
+
record,
|
|
54
|
+
written,
|
|
55
|
+
original,
|
|
56
|
+
linked: linked && index > 0
|
|
57
|
+
}));
|
|
58
|
+
byAttribute.set(attribute, stack);
|
|
59
|
+
return stack;
|
|
60
|
+
}
|
|
61
|
+
function findHold(stack, lease, record) {
|
|
62
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
63
|
+
}
|
|
64
|
+
var AttributeLease = class {
|
|
34
65
|
#attribute;
|
|
35
66
|
#record;
|
|
36
67
|
#base;
|
|
68
|
+
#preserveAuthor;
|
|
37
69
|
#held = /* @__PURE__ */ new Set();
|
|
38
70
|
constructor(attribute, owner, options = {}) {
|
|
39
71
|
this.#attribute = attribute;
|
|
40
72
|
this.#record = `data-${owner}-${attribute}-lease`;
|
|
41
73
|
this.#base = options.base === true;
|
|
42
|
-
|
|
74
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
75
|
+
const names = registry.names;
|
|
43
76
|
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
44
77
|
}
|
|
45
78
|
write(element, value) {
|
|
46
|
-
const stack = this.#
|
|
79
|
+
const stack = attributeStack(element, this.#attribute);
|
|
47
80
|
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
48
81
|
this.#held.add(element);
|
|
49
82
|
const shows = !this.#covered(element, stack, hold);
|
|
@@ -53,8 +86,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
53
86
|
}
|
|
54
87
|
return(element) {
|
|
55
88
|
const held = this.#held.delete(element);
|
|
56
|
-
const known =
|
|
57
|
-
const stack = held || known || element.hasAttribute(this.#record) ? this.#
|
|
89
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
90
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
58
91
|
const hold = this.#take(stack);
|
|
59
92
|
if (!hold) return false;
|
|
60
93
|
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
@@ -72,31 +105,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
72
105
|
returnAll() {
|
|
73
106
|
for (const element of Array.from(this.#held)) this.return(element);
|
|
74
107
|
}
|
|
75
|
-
#stack(element) {
|
|
76
|
-
const stacks = _AttributeLease.#registry.stacks;
|
|
77
|
-
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
78
|
-
stacks.set(element, byAttribute);
|
|
79
|
-
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
80
|
-
byAttribute.set(this.#attribute, stack);
|
|
81
|
-
return stack;
|
|
82
|
-
}
|
|
83
|
-
#seed(element) {
|
|
84
|
-
const found = [];
|
|
85
|
-
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
86
|
-
const read = readLeaseRecord(element, record);
|
|
87
|
-
if (read) found.push({ ...read, record });
|
|
88
|
-
}
|
|
89
|
-
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
90
|
-
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
91
|
-
lease: null,
|
|
92
|
-
record,
|
|
93
|
-
written,
|
|
94
|
-
original,
|
|
95
|
-
linked: linked && index > 0
|
|
96
|
-
}));
|
|
97
|
-
}
|
|
98
108
|
#take(stack) {
|
|
99
|
-
const hold = stack
|
|
109
|
+
const hold = findHold(stack, this, this.#record);
|
|
100
110
|
if (hold) hold.lease = this;
|
|
101
111
|
return hold;
|
|
102
112
|
}
|
|
@@ -105,7 +115,7 @@ var AttributeLease = class _AttributeLease {
|
|
|
105
115
|
const top = stack.at(-1);
|
|
106
116
|
const bottom = stack[0];
|
|
107
117
|
const over = top?.written === current ? top : void 0;
|
|
108
|
-
const from = this.#base ? bottom : over;
|
|
118
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
109
119
|
const hold = {
|
|
110
120
|
lease: this,
|
|
111
121
|
record: this.#record,
|
|
@@ -135,15 +145,11 @@ var AttributeLease = class _AttributeLease {
|
|
|
135
145
|
);
|
|
136
146
|
});
|
|
137
147
|
for (const [name, recorded] of records) {
|
|
138
|
-
|
|
139
|
-
if (recorded === null) element.removeAttribute(name);
|
|
140
|
-
else element.setAttribute(name, recorded);
|
|
148
|
+
setAttributeIfChanged(element, name, recorded);
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
151
|
#reflect(element, value) {
|
|
144
|
-
|
|
145
|
-
if (value === null) element.removeAttribute(this.#attribute);
|
|
146
|
-
else element.setAttribute(this.#attribute, value);
|
|
152
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
147
153
|
}
|
|
148
154
|
};
|
|
149
155
|
|
|
@@ -10,16 +10,27 @@ function announce(message, options = {}) {
|
|
|
10
10
|
);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
14
|
+
if (element.getAttribute(name) === value) return false;
|
|
15
|
+
if (value === null) element.removeAttribute(name);
|
|
16
|
+
else element.setAttribute(name, value);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
function sharedRegistry(key, create) {
|
|
14
21
|
const symbol = Symbol.for(key);
|
|
15
22
|
const scope = globalThis;
|
|
16
23
|
const existing = scope[symbol];
|
|
17
24
|
if (existing !== void 0) return existing;
|
|
18
|
-
const
|
|
19
|
-
Object.defineProperty(globalThis, symbol, { value:
|
|
20
|
-
return
|
|
25
|
+
const registry2 = create();
|
|
26
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
27
|
+
return registry2;
|
|
21
28
|
}
|
|
22
29
|
|
|
30
|
+
var registry = sharedRegistry(
|
|
31
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
32
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
33
|
+
);
|
|
23
34
|
function isAttributeValue(value) {
|
|
24
35
|
return value === null || typeof value === "string";
|
|
25
36
|
}
|
|
@@ -36,24 +47,46 @@ function readLeaseRecord(element, record) {
|
|
|
36
47
|
return void 0;
|
|
37
48
|
}
|
|
38
49
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
);
|
|
50
|
+
function attributeStack(element, attribute) {
|
|
51
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
52
|
+
registry.stacks.set(element, byAttribute);
|
|
53
|
+
const known = byAttribute.get(attribute);
|
|
54
|
+
if (known) return known;
|
|
55
|
+
const found = [];
|
|
56
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
57
|
+
const read = readLeaseRecord(element, record);
|
|
58
|
+
if (read) found.push({ ...read, record });
|
|
59
|
+
}
|
|
60
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
61
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
62
|
+
lease: null,
|
|
63
|
+
record,
|
|
64
|
+
written,
|
|
65
|
+
original,
|
|
66
|
+
linked: linked && index > 0
|
|
67
|
+
}));
|
|
68
|
+
byAttribute.set(attribute, stack);
|
|
69
|
+
return stack;
|
|
70
|
+
}
|
|
71
|
+
function findHold(stack, lease, record) {
|
|
72
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
73
|
+
}
|
|
74
|
+
var AttributeLease = class {
|
|
44
75
|
#attribute;
|
|
45
76
|
#record;
|
|
46
77
|
#base;
|
|
78
|
+
#preserveAuthor;
|
|
47
79
|
#held = /* @__PURE__ */ new Set();
|
|
48
80
|
constructor(attribute, owner, options = {}) {
|
|
49
81
|
this.#attribute = attribute;
|
|
50
82
|
this.#record = `data-${owner}-${attribute}-lease`;
|
|
51
83
|
this.#base = options.base === true;
|
|
52
|
-
|
|
84
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
85
|
+
const names = registry.names;
|
|
53
86
|
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
54
87
|
}
|
|
55
88
|
write(element, value) {
|
|
56
|
-
const stack = this.#
|
|
89
|
+
const stack = attributeStack(element, this.#attribute);
|
|
57
90
|
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
58
91
|
this.#held.add(element);
|
|
59
92
|
const shows = !this.#covered(element, stack, hold);
|
|
@@ -63,8 +96,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
63
96
|
}
|
|
64
97
|
return(element) {
|
|
65
98
|
const held = this.#held.delete(element);
|
|
66
|
-
const known =
|
|
67
|
-
const stack = held || known || element.hasAttribute(this.#record) ? this.#
|
|
99
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
100
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
68
101
|
const hold = this.#take(stack);
|
|
69
102
|
if (!hold) return false;
|
|
70
103
|
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
@@ -82,31 +115,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
82
115
|
returnAll() {
|
|
83
116
|
for (const element of Array.from(this.#held)) this.return(element);
|
|
84
117
|
}
|
|
85
|
-
#stack(element) {
|
|
86
|
-
const stacks = _AttributeLease.#registry.stacks;
|
|
87
|
-
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
88
|
-
stacks.set(element, byAttribute);
|
|
89
|
-
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
90
|
-
byAttribute.set(this.#attribute, stack);
|
|
91
|
-
return stack;
|
|
92
|
-
}
|
|
93
|
-
#seed(element) {
|
|
94
|
-
const found = [];
|
|
95
|
-
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
96
|
-
const read = readLeaseRecord(element, record);
|
|
97
|
-
if (read) found.push({ ...read, record });
|
|
98
|
-
}
|
|
99
|
-
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
100
|
-
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
101
|
-
lease: null,
|
|
102
|
-
record,
|
|
103
|
-
written,
|
|
104
|
-
original,
|
|
105
|
-
linked: linked && index > 0
|
|
106
|
-
}));
|
|
107
|
-
}
|
|
108
118
|
#take(stack) {
|
|
109
|
-
const hold = stack
|
|
119
|
+
const hold = findHold(stack, this, this.#record);
|
|
110
120
|
if (hold) hold.lease = this;
|
|
111
121
|
return hold;
|
|
112
122
|
}
|
|
@@ -115,7 +125,7 @@ var AttributeLease = class _AttributeLease {
|
|
|
115
125
|
const top = stack.at(-1);
|
|
116
126
|
const bottom = stack[0];
|
|
117
127
|
const over = top?.written === current ? top : void 0;
|
|
118
|
-
const from = this.#base ? bottom : over;
|
|
128
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
119
129
|
const hold = {
|
|
120
130
|
lease: this,
|
|
121
131
|
record: this.#record,
|
|
@@ -145,15 +155,11 @@ var AttributeLease = class _AttributeLease {
|
|
|
145
155
|
);
|
|
146
156
|
});
|
|
147
157
|
for (const [name, recorded] of records) {
|
|
148
|
-
|
|
149
|
-
if (recorded === null) element.removeAttribute(name);
|
|
150
|
-
else element.setAttribute(name, recorded);
|
|
158
|
+
setAttributeIfChanged(element, name, recorded);
|
|
151
159
|
}
|
|
152
160
|
}
|
|
153
161
|
#reflect(element, value) {
|
|
154
|
-
|
|
155
|
-
if (value === null) element.removeAttribute(this.#attribute);
|
|
156
|
-
else element.setAttribute(this.#attribute, value);
|
|
162
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
157
163
|
}
|
|
158
164
|
};
|
|
159
165
|
|
|
@@ -251,6 +257,87 @@ var NumberValueReader = class {
|
|
|
251
257
|
}
|
|
252
258
|
};
|
|
253
259
|
|
|
260
|
+
var OwnedText = class {
|
|
261
|
+
#attribute;
|
|
262
|
+
#channel;
|
|
263
|
+
#guard;
|
|
264
|
+
#writes = /* @__PURE__ */ new WeakMap();
|
|
265
|
+
constructor(attribute, channel = "text", guard = () => true) {
|
|
266
|
+
this.#attribute = attribute;
|
|
267
|
+
this.#channel = channel;
|
|
268
|
+
this.#guard = guard;
|
|
269
|
+
}
|
|
270
|
+
hasRecord(element) {
|
|
271
|
+
return element.hasAttribute(this.#attribute);
|
|
272
|
+
}
|
|
273
|
+
read(element, evidence = {}) {
|
|
274
|
+
if (evidence.source === "live") return this.#writes.get(element) ?? null;
|
|
275
|
+
const raw = element.getAttribute(this.#attribute);
|
|
276
|
+
if (raw === null) return null;
|
|
277
|
+
try {
|
|
278
|
+
const record = JSON.parse(raw);
|
|
279
|
+
if (!Array.isArray(record) || record.length !== 3 || record[0] !== this.#channel || typeof record[1] !== "string" || typeof record[2] !== "string")
|
|
280
|
+
return null;
|
|
281
|
+
return { original: record[1], written: record[2] };
|
|
282
|
+
} catch {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
owns(element, evidence = {}) {
|
|
287
|
+
return this.#ownedRecord(element, evidence) !== null;
|
|
288
|
+
}
|
|
289
|
+
#ownedRecord(element, evidence) {
|
|
290
|
+
const record = this.read(element, evidence);
|
|
291
|
+
if (record === null || !this.#guard(element)) return null;
|
|
292
|
+
if (this.#channel === "text" && element.firstElementChild !== null) return null;
|
|
293
|
+
const current = this.#current(element);
|
|
294
|
+
return current === record.written || evidence.source === "live" && evidence.allowOriginal === true && current === record.original ? record : null;
|
|
295
|
+
}
|
|
296
|
+
write(element, written, original, force = false) {
|
|
297
|
+
const current = this.#current(element);
|
|
298
|
+
if (current === null) return;
|
|
299
|
+
const before = original ?? this.#writes.get(element)?.original ?? this.read(element)?.original ?? current;
|
|
300
|
+
if (force || current !== written) this.#put(element, written);
|
|
301
|
+
this.remember(element, written, before);
|
|
302
|
+
}
|
|
303
|
+
remember(element, written, original) {
|
|
304
|
+
const current = this.#current(element);
|
|
305
|
+
if (current === null) return;
|
|
306
|
+
const before = original ?? this.#writes.get(element)?.original ?? this.read(element)?.original ?? current;
|
|
307
|
+
this.#writes.set(element, { original: before, written });
|
|
308
|
+
setAttributeIfChanged(
|
|
309
|
+
element,
|
|
310
|
+
this.#attribute,
|
|
311
|
+
JSON.stringify([this.#channel, before, written])
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
clear(element, evidence = {}) {
|
|
315
|
+
if (!this.owns(element, evidence)) return false;
|
|
316
|
+
if (this.#current(element) !== "") this.#put(element, "");
|
|
317
|
+
this.forget(element);
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
restore(element, evidence = {}) {
|
|
321
|
+
const record = this.#ownedRecord(element, evidence);
|
|
322
|
+
if (record === null) return false;
|
|
323
|
+
if (this.#current(element) !== record.original) this.#put(element, record.original);
|
|
324
|
+
this.forget(element);
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
forget(element) {
|
|
328
|
+
setAttributeIfChanged(element, this.#attribute, null);
|
|
329
|
+
this.#writes.delete(element);
|
|
330
|
+
}
|
|
331
|
+
#current(element) {
|
|
332
|
+
if (this.#channel === "text") return element.textContent ?? "";
|
|
333
|
+
return element instanceof HTMLInputElement ? element.value : null;
|
|
334
|
+
}
|
|
335
|
+
#put(element, text) {
|
|
336
|
+
if (this.#channel === "text") element.textContent = text;
|
|
337
|
+
else if (element instanceof HTMLInputElement) element.value = text;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
254
341
|
var TimerRegistry = class {
|
|
255
342
|
ids = /* @__PURE__ */ new Set();
|
|
256
343
|
clear(id) {
|
|
@@ -289,6 +376,8 @@ var BUTTON_BUSY_LABEL = "data-submit-once-busy-label";
|
|
|
289
376
|
var LABEL_RECORD = "label";
|
|
290
377
|
var SUBMITTER_MARK = "submitter";
|
|
291
378
|
var SubmitOnceController = class _SubmitOnceController extends Controller {
|
|
379
|
+
#textLabel = new OwnedText(this.#labelRecord);
|
|
380
|
+
#valueLabel = new OwnedText(this.#labelRecord, "value");
|
|
292
381
|
#numbers = new NumberValueReader();
|
|
293
382
|
static targets = ["submit", "idle", "busy"];
|
|
294
383
|
static values = {
|
|
@@ -379,22 +468,9 @@ var SubmitOnceController = class _SubmitOnceController extends Controller {
|
|
|
379
468
|
return copied;
|
|
380
469
|
}
|
|
381
470
|
#restoreLabel(control) {
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
try {
|
|
386
|
-
record = JSON.parse(raw ?? "");
|
|
387
|
-
} catch {
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
if (!Array.isArray(record) || record.length !== 2) return;
|
|
391
|
-
const [original, written] = record;
|
|
392
|
-
if (typeof original !== "string" || typeof written !== "string") return;
|
|
393
|
-
if (control instanceof HTMLInputElement) {
|
|
394
|
-
if (control.value === written) control.value = original;
|
|
395
|
-
} else if (control.childElementCount === 0 && control.textContent === written) {
|
|
396
|
-
control.textContent = original;
|
|
397
|
-
}
|
|
471
|
+
const owner = control instanceof HTMLInputElement ? this.#valueLabel : this.#textLabel;
|
|
472
|
+
owner.restore(control);
|
|
473
|
+
owner.forget(control);
|
|
398
474
|
}
|
|
399
475
|
#ownedForms() {
|
|
400
476
|
const forms = Array.from(this.element.querySelectorAll("form"));
|
|
@@ -479,14 +555,9 @@ var SubmitOnceController = class _SubmitOnceController extends Controller {
|
|
|
479
555
|
if (label.length === 0) return;
|
|
480
556
|
if (control instanceof HTMLInputElement) {
|
|
481
557
|
if (control.type !== "submit") return;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
original: control.value,
|
|
486
|
-
written: label
|
|
487
|
-
};
|
|
488
|
-
control.setAttribute(this.#labelRecord, JSON.stringify([control.value, label]));
|
|
489
|
-
control.value = label;
|
|
558
|
+
const owner2 = new OwnedText(this.#labelRecord, "value");
|
|
559
|
+
session.labelWrite = { control, owner: owner2 };
|
|
560
|
+
owner2.write(control, label, control.value);
|
|
490
561
|
return;
|
|
491
562
|
}
|
|
492
563
|
if (control.hasAttribute("aria-label")) {
|
|
@@ -494,30 +565,19 @@ var SubmitOnceController = class _SubmitOnceController extends Controller {
|
|
|
494
565
|
return;
|
|
495
566
|
}
|
|
496
567
|
if (control.childElementCount > 0) return;
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
original: control.textContent ?? "",
|
|
501
|
-
written: label
|
|
502
|
-
};
|
|
503
|
-
control.setAttribute(this.#labelRecord, JSON.stringify([control.textContent ?? "", label]));
|
|
504
|
-
control.textContent = label;
|
|
568
|
+
const owner = new OwnedText(this.#labelRecord);
|
|
569
|
+
session.labelWrite = { control, owner };
|
|
570
|
+
owner.write(control, label, control.textContent ?? "");
|
|
505
571
|
}
|
|
506
572
|
#returnSessionState(session, unfinished = false) {
|
|
507
573
|
if (session.timeoutId !== null) {
|
|
508
574
|
this.#timers.clear(session.timeoutId);
|
|
509
575
|
session.timeoutId = null;
|
|
510
576
|
}
|
|
511
|
-
if (session.labelWrite
|
|
512
|
-
const { control,
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
} else if (session.labelWrite?.channel === "text") {
|
|
516
|
-
const { control, original, written } = session.labelWrite;
|
|
517
|
-
control.removeAttribute(this.#labelRecord);
|
|
518
|
-
if (control.childElementCount === 0 && control.textContent === written) {
|
|
519
|
-
control.textContent = original;
|
|
520
|
-
}
|
|
577
|
+
if (session.labelWrite) {
|
|
578
|
+
const { control, owner } = session.labelWrite;
|
|
579
|
+
owner.restore(control, { source: "live" });
|
|
580
|
+
owner.forget(control);
|
|
521
581
|
}
|
|
522
582
|
session.labelWrite = null;
|
|
523
583
|
if (!unfinished) session.submitter?.removeAttribute(this.#submitterMark);
|