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
|
@@ -5,9 +5,9 @@ function sharedRegistry(key, create) {
|
|
|
5
5
|
const scope = globalThis;
|
|
6
6
|
const existing = scope[symbol];
|
|
7
7
|
if (existing !== void 0) return existing;
|
|
8
|
-
const
|
|
9
|
-
Object.defineProperty(globalThis, symbol, { value:
|
|
10
|
-
return
|
|
8
|
+
const registry2 = create();
|
|
9
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
10
|
+
return registry2;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
var ids = sharedRegistry("stimeo-ui.aria-ids.registry.v1", () => ({ counter: 0 }));
|
|
@@ -26,6 +26,17 @@ function ensureId(element, prefix = "stimeo") {
|
|
|
26
26
|
return id;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
30
|
+
if (element.getAttribute(name) === value) return false;
|
|
31
|
+
if (value === null) element.removeAttribute(name);
|
|
32
|
+
else element.setAttribute(name, value);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var registry = sharedRegistry(
|
|
37
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
38
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
39
|
+
);
|
|
29
40
|
function isAttributeValue(value) {
|
|
30
41
|
return value === null || typeof value === "string";
|
|
31
42
|
}
|
|
@@ -42,24 +53,46 @@ function readLeaseRecord(element, record) {
|
|
|
42
53
|
return void 0;
|
|
43
54
|
}
|
|
44
55
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
56
|
+
function attributeStack(element, attribute) {
|
|
57
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
58
|
+
registry.stacks.set(element, byAttribute);
|
|
59
|
+
const known = byAttribute.get(attribute);
|
|
60
|
+
if (known) return known;
|
|
61
|
+
const found = [];
|
|
62
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
63
|
+
const read = readLeaseRecord(element, record);
|
|
64
|
+
if (read) found.push({ ...read, record });
|
|
65
|
+
}
|
|
66
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
67
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
68
|
+
lease: null,
|
|
69
|
+
record,
|
|
70
|
+
written,
|
|
71
|
+
original,
|
|
72
|
+
linked: linked && index > 0
|
|
73
|
+
}));
|
|
74
|
+
byAttribute.set(attribute, stack);
|
|
75
|
+
return stack;
|
|
76
|
+
}
|
|
77
|
+
function findHold(stack, lease, record) {
|
|
78
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
79
|
+
}
|
|
80
|
+
var AttributeLease = class {
|
|
50
81
|
#attribute;
|
|
51
82
|
#record;
|
|
52
83
|
#base;
|
|
84
|
+
#preserveAuthor;
|
|
53
85
|
#held = /* @__PURE__ */ new Set();
|
|
54
86
|
constructor(attribute, owner, options = {}) {
|
|
55
87
|
this.#attribute = attribute;
|
|
56
88
|
this.#record = `data-${owner}-${attribute}-lease`;
|
|
57
89
|
this.#base = options.base === true;
|
|
58
|
-
|
|
90
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
91
|
+
const names = registry.names;
|
|
59
92
|
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
60
93
|
}
|
|
61
94
|
write(element, value) {
|
|
62
|
-
const stack = this.#
|
|
95
|
+
const stack = attributeStack(element, this.#attribute);
|
|
63
96
|
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
64
97
|
this.#held.add(element);
|
|
65
98
|
const shows = !this.#covered(element, stack, hold);
|
|
@@ -69,8 +102,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
69
102
|
}
|
|
70
103
|
return(element) {
|
|
71
104
|
const held = this.#held.delete(element);
|
|
72
|
-
const known =
|
|
73
|
-
const stack = held || known || element.hasAttribute(this.#record) ? this.#
|
|
105
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
106
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
74
107
|
const hold = this.#take(stack);
|
|
75
108
|
if (!hold) return false;
|
|
76
109
|
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
@@ -88,31 +121,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
88
121
|
returnAll() {
|
|
89
122
|
for (const element of Array.from(this.#held)) this.return(element);
|
|
90
123
|
}
|
|
91
|
-
#stack(element) {
|
|
92
|
-
const stacks = _AttributeLease.#registry.stacks;
|
|
93
|
-
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
94
|
-
stacks.set(element, byAttribute);
|
|
95
|
-
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
96
|
-
byAttribute.set(this.#attribute, stack);
|
|
97
|
-
return stack;
|
|
98
|
-
}
|
|
99
|
-
#seed(element) {
|
|
100
|
-
const found = [];
|
|
101
|
-
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
102
|
-
const read = readLeaseRecord(element, record);
|
|
103
|
-
if (read) found.push({ ...read, record });
|
|
104
|
-
}
|
|
105
|
-
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
106
|
-
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
107
|
-
lease: null,
|
|
108
|
-
record,
|
|
109
|
-
written,
|
|
110
|
-
original,
|
|
111
|
-
linked: linked && index > 0
|
|
112
|
-
}));
|
|
113
|
-
}
|
|
114
124
|
#take(stack) {
|
|
115
|
-
const hold = stack
|
|
125
|
+
const hold = findHold(stack, this, this.#record);
|
|
116
126
|
if (hold) hold.lease = this;
|
|
117
127
|
return hold;
|
|
118
128
|
}
|
|
@@ -121,7 +131,7 @@ var AttributeLease = class _AttributeLease {
|
|
|
121
131
|
const top = stack.at(-1);
|
|
122
132
|
const bottom = stack[0];
|
|
123
133
|
const over = top?.written === current ? top : void 0;
|
|
124
|
-
const from = this.#base ? bottom : over;
|
|
134
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
125
135
|
const hold = {
|
|
126
136
|
lease: this,
|
|
127
137
|
record: this.#record,
|
|
@@ -151,15 +161,11 @@ var AttributeLease = class _AttributeLease {
|
|
|
151
161
|
);
|
|
152
162
|
});
|
|
153
163
|
for (const [name, recorded] of records) {
|
|
154
|
-
|
|
155
|
-
if (recorded === null) element.removeAttribute(name);
|
|
156
|
-
else element.setAttribute(name, recorded);
|
|
164
|
+
setAttributeIfChanged(element, name, recorded);
|
|
157
165
|
}
|
|
158
166
|
}
|
|
159
167
|
#reflect(element, value) {
|
|
160
|
-
|
|
161
|
-
if (value === null) element.removeAttribute(this.#attribute);
|
|
162
|
-
else element.setAttribute(this.#attribute, value);
|
|
168
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
163
169
|
}
|
|
164
170
|
};
|
|
165
171
|
|
|
@@ -177,25 +183,25 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
177
183
|
#active = null;
|
|
178
184
|
activate(ownerDocument = document, options) {
|
|
179
185
|
this.deactivate();
|
|
180
|
-
let
|
|
181
|
-
if (!
|
|
182
|
-
|
|
183
|
-
_EscapeLayer.#registries.set(ownerDocument,
|
|
184
|
-
ownerDocument.addEventListener("keydown",
|
|
186
|
+
let registry2 = _EscapeLayer.#registries.get(ownerDocument);
|
|
187
|
+
if (!registry2) {
|
|
188
|
+
registry2 = _EscapeLayer.#createRegistry();
|
|
189
|
+
_EscapeLayer.#registries.set(ownerDocument, registry2);
|
|
190
|
+
ownerDocument.addEventListener("keydown", registry2.onKeydown);
|
|
185
191
|
}
|
|
186
192
|
const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
|
|
187
|
-
|
|
193
|
+
registry2.stack.push(entry);
|
|
188
194
|
this.#active = { document: ownerDocument, entry };
|
|
189
195
|
}
|
|
190
196
|
deactivate() {
|
|
191
197
|
const active = this.#active;
|
|
192
198
|
if (!active) return;
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
-
const index =
|
|
196
|
-
if (index >= 0)
|
|
197
|
-
if (
|
|
198
|
-
active.document.removeEventListener("keydown",
|
|
199
|
+
const registry2 = _EscapeLayer.#registries.get(active.document);
|
|
200
|
+
if (registry2) {
|
|
201
|
+
const index = registry2.stack.lastIndexOf(active.entry);
|
|
202
|
+
if (index >= 0) registry2.stack.splice(index, 1);
|
|
203
|
+
if (registry2.stack.length === 0) {
|
|
204
|
+
active.document.removeEventListener("keydown", registry2.onKeydown);
|
|
199
205
|
_EscapeLayer.#registries.delete(active.document);
|
|
200
206
|
}
|
|
201
207
|
}
|
|
@@ -204,22 +210,22 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
204
210
|
get ownsEscape() {
|
|
205
211
|
const active = this.#active;
|
|
206
212
|
if (!active) return false;
|
|
207
|
-
const
|
|
208
|
-
if (!
|
|
209
|
-
return _EscapeLayer.#resolveOwner(
|
|
213
|
+
const registry2 = _EscapeLayer.#registries.get(active.document);
|
|
214
|
+
if (!registry2) return false;
|
|
215
|
+
return _EscapeLayer.#resolveOwner(registry2.stack) === active.entry;
|
|
210
216
|
}
|
|
211
217
|
static #createRegistry() {
|
|
212
|
-
const
|
|
218
|
+
const registry2 = {
|
|
213
219
|
stack: [],
|
|
214
220
|
onKeydown: (event) => {
|
|
215
221
|
if (event.key !== "Escape" || event.defaultPrevented || event.isComposing) return;
|
|
216
|
-
const owner = _EscapeLayer.#resolveOwner(
|
|
222
|
+
const owner = _EscapeLayer.#resolveOwner(registry2.stack);
|
|
217
223
|
if (!owner) return;
|
|
218
224
|
event.preventDefault();
|
|
219
225
|
owner.onDismiss();
|
|
220
226
|
}
|
|
221
227
|
};
|
|
222
|
-
return
|
|
228
|
+
return registry2;
|
|
223
229
|
}
|
|
224
230
|
static #resolveOwner(stack) {
|
|
225
231
|
for (let index = stack.length - 1; index >= 0; index--) {
|
|
@@ -259,6 +265,9 @@ var MicrotaskCoalescer = class {
|
|
|
259
265
|
this.#queued = false;
|
|
260
266
|
this.#generation += 1;
|
|
261
267
|
}
|
|
268
|
+
get pending() {
|
|
269
|
+
return this.#queued;
|
|
270
|
+
}
|
|
262
271
|
schedule() {
|
|
263
272
|
if (!this.#active || this.#queued) return;
|
|
264
273
|
this.#queued = true;
|
|
@@ -289,6 +298,9 @@ var MorphRenderWatcher = class {
|
|
|
289
298
|
schedule() {
|
|
290
299
|
this.#pass.schedule();
|
|
291
300
|
}
|
|
301
|
+
get pending() {
|
|
302
|
+
return this.#pass.pending;
|
|
303
|
+
}
|
|
292
304
|
disconnect() {
|
|
293
305
|
this.#listeners.dispose();
|
|
294
306
|
this.#pass.cancel();
|
|
@@ -323,10 +335,11 @@ var DropdownController = class extends Controller {
|
|
|
323
335
|
this.#close("outside");
|
|
324
336
|
}
|
|
325
337
|
};
|
|
338
|
+
#listeners = new ListenerSet();
|
|
326
339
|
connect() {
|
|
327
340
|
this.#associateTriggerWithMenu();
|
|
328
341
|
this.#close("api");
|
|
329
|
-
|
|
342
|
+
this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
|
|
330
343
|
this.#morphRender.observe(this.element);
|
|
331
344
|
this.#connected = true;
|
|
332
345
|
this.#reporting = true;
|
|
@@ -335,7 +348,7 @@ var DropdownController = class extends Controller {
|
|
|
335
348
|
this.#connected = false;
|
|
336
349
|
this.#reporting = false;
|
|
337
350
|
this.#escapeLayer.deactivate();
|
|
338
|
-
|
|
351
|
+
this.#listeners.dispose();
|
|
339
352
|
this.#morphRender.disconnect();
|
|
340
353
|
}
|
|
341
354
|
triggerTargetConnected() {
|
|
@@ -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
|
|
|
@@ -153,66 +159,196 @@ var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
|
153
159
|
"deleteCompositionText",
|
|
154
160
|
"deleteByComposition"
|
|
155
161
|
]);
|
|
162
|
+
function isCompositionInputType(inputType) {
|
|
163
|
+
return inputType !== void 0 && COMPOSITION_INPUT_TYPES.has(inputType);
|
|
164
|
+
}
|
|
165
|
+
var BEFORE_MORPH_ATTRIBUTE = "turbo:before-morph-attribute";
|
|
166
|
+
var textOf = (element) => element.value;
|
|
167
|
+
var holdsFocus = (element) => element instanceof Node && element.getRootNode().activeElement === element;
|
|
156
168
|
var CompositionTracker = class {
|
|
157
169
|
#observedTargets = /* @__PURE__ */ new Set();
|
|
158
|
-
#
|
|
170
|
+
#composition = null;
|
|
159
171
|
#onStart;
|
|
160
172
|
#onEnd;
|
|
173
|
+
#onAbandon;
|
|
161
174
|
#confirmedTarget = null;
|
|
175
|
+
#keyComposing = false;
|
|
176
|
+
#connected = false;
|
|
177
|
+
#abandoned = null;
|
|
162
178
|
constructor(options = {}) {
|
|
163
179
|
this.#onStart = options.onStart;
|
|
164
180
|
this.#onEnd = options.onEnd;
|
|
181
|
+
this.#onAbandon = options.onAbandon;
|
|
165
182
|
}
|
|
166
183
|
observe(target) {
|
|
184
|
+
this.#connected = true;
|
|
167
185
|
if (this.#observedTargets.has(target)) return;
|
|
168
186
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
169
187
|
target.addEventListener("compositionend", this.#handleEnd);
|
|
170
|
-
target.addEventListener("keydown", this.#handleKeydown);
|
|
188
|
+
target.addEventListener("keydown", this.#handleKeydown, true);
|
|
189
|
+
target.addEventListener("beforeinput", this.#handleBeforeInput, true);
|
|
190
|
+
target.addEventListener("input", this.#handleInput, true);
|
|
191
|
+
target.addEventListener("blur", this.#handleBlur, true);
|
|
192
|
+
target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
|
|
171
193
|
this.#observedTargets.add(target);
|
|
172
194
|
}
|
|
173
195
|
unobserve(target) {
|
|
174
196
|
if (!this.#observedTargets.delete(target)) return;
|
|
175
|
-
|
|
176
|
-
target
|
|
177
|
-
target.removeEventListener("keydown", this.#handleKeydown);
|
|
178
|
-
this.#activeTargets.delete(target);
|
|
197
|
+
this.#release(target);
|
|
198
|
+
if (this.#composition?.target === target) this.#letGo();
|
|
179
199
|
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
180
200
|
}
|
|
181
201
|
disconnect() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
target.removeEventListener("compositionend", this.#handleEnd);
|
|
185
|
-
target.removeEventListener("keydown", this.#handleKeydown);
|
|
186
|
-
}
|
|
202
|
+
this.#connected = false;
|
|
203
|
+
for (const target of this.#observedTargets) this.#release(target);
|
|
187
204
|
this.#observedTargets.clear();
|
|
188
|
-
this.#
|
|
205
|
+
this.#letGo();
|
|
189
206
|
this.#confirmedTarget = null;
|
|
207
|
+
this.#keyComposing = false;
|
|
208
|
+
this.#abandoned = null;
|
|
190
209
|
}
|
|
191
210
|
isComposing(event) {
|
|
192
|
-
|
|
211
|
+
this.#settle();
|
|
212
|
+
return event?.isComposing === true || this.#composition !== null;
|
|
213
|
+
}
|
|
214
|
+
composingElement() {
|
|
215
|
+
this.#settle();
|
|
216
|
+
return this.#composition?.element ?? null;
|
|
217
|
+
}
|
|
218
|
+
owns(event) {
|
|
219
|
+
const element = this.composingElement();
|
|
220
|
+
return event.isComposing === true || element !== null && element === event.target;
|
|
193
221
|
}
|
|
194
222
|
consumesConfirmedInput(event) {
|
|
195
223
|
const confirmed = this.#confirmedTarget;
|
|
196
224
|
this.#confirmedTarget = null;
|
|
197
225
|
if (confirmed === null || confirmed !== event.target) return false;
|
|
198
226
|
const inputType = event.inputType;
|
|
199
|
-
return !inputType ||
|
|
227
|
+
return !inputType || isCompositionInputType(inputType);
|
|
228
|
+
}
|
|
229
|
+
#release(target) {
|
|
230
|
+
target.removeEventListener("compositionstart", this.#handleStart);
|
|
231
|
+
target.removeEventListener("compositionend", this.#handleEnd);
|
|
232
|
+
target.removeEventListener("keydown", this.#handleKeydown, true);
|
|
233
|
+
target.removeEventListener("beforeinput", this.#handleBeforeInput, true);
|
|
234
|
+
target.removeEventListener("input", this.#handleInput, true);
|
|
235
|
+
target.removeEventListener("blur", this.#handleBlur, true);
|
|
236
|
+
target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
|
|
237
|
+
}
|
|
238
|
+
#letGo() {
|
|
239
|
+
const composition = this.#composition;
|
|
240
|
+
if (composition === null) return;
|
|
241
|
+
if (!holdsFocus(composition.element)) {
|
|
242
|
+
this.#composition = null;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
queueMicrotask(() => {
|
|
246
|
+
if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.#composition = null;
|
|
250
|
+
if (!this.#connected) return;
|
|
251
|
+
this.#abandoned = { element: composition.element, inPlace: true };
|
|
252
|
+
this.#runAbandon();
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
#settle() {
|
|
256
|
+
const composition = this.#composition;
|
|
257
|
+
if (composition === null) return;
|
|
258
|
+
const { target, element, text } = composition;
|
|
259
|
+
if (target instanceof Node && element instanceof Node && !target.contains(element)) {
|
|
260
|
+
this.#composition = null;
|
|
261
|
+
} else if (textOf(element) !== text) {
|
|
262
|
+
this.#abandon(false);
|
|
263
|
+
}
|
|
200
264
|
}
|
|
265
|
+
#abandon(inPlace) {
|
|
266
|
+
this.#owe(inPlace);
|
|
267
|
+
this.#composition = null;
|
|
268
|
+
queueMicrotask(this.#runAbandon);
|
|
269
|
+
}
|
|
270
|
+
#owe(inPlace) {
|
|
271
|
+
const composition = this.#composition;
|
|
272
|
+
if (composition !== null) this.#abandoned = { element: composition.element, inPlace };
|
|
273
|
+
}
|
|
274
|
+
#runAbandon = () => {
|
|
275
|
+
const abandoned = this.#abandoned;
|
|
276
|
+
if (abandoned === null) return;
|
|
277
|
+
this.#abandoned = null;
|
|
278
|
+
this.#onAbandon?.(abandoned.element, abandoned.inPlace);
|
|
279
|
+
};
|
|
201
280
|
#handleStart = (event) => {
|
|
202
281
|
this.#confirmedTarget = null;
|
|
203
|
-
|
|
282
|
+
this.#settle();
|
|
283
|
+
this.#owe(true);
|
|
284
|
+
const target = event.currentTarget;
|
|
285
|
+
const element = event.target;
|
|
286
|
+
this.#composition = { target, element, text: textOf(element) };
|
|
287
|
+
this.#runAbandon();
|
|
204
288
|
this.#onStart?.(event);
|
|
205
289
|
};
|
|
206
290
|
#handleEnd = (event) => {
|
|
207
|
-
|
|
291
|
+
this.#runAbandon();
|
|
292
|
+
if (this.#composition?.target === event.currentTarget) this.#composition = null;
|
|
208
293
|
this.#confirmedTarget = event.target;
|
|
209
294
|
this.#onEnd?.(event);
|
|
210
295
|
};
|
|
211
|
-
#handleKeydown = () => {
|
|
296
|
+
#handleKeydown = (event) => {
|
|
297
|
+
this.#settle();
|
|
298
|
+
this.#runAbandon();
|
|
212
299
|
this.#confirmedTarget = null;
|
|
300
|
+
this.#keyComposing = event.isComposing === true;
|
|
301
|
+
};
|
|
302
|
+
#handleBeforeInput = (event) => {
|
|
303
|
+
const keyComposing = this.#keyComposing;
|
|
304
|
+
this.#keyComposing = false;
|
|
305
|
+
this.#settle();
|
|
306
|
+
const { isComposing, inputType } = event;
|
|
307
|
+
if (this.#composition !== null && !keyComposing && !isComposing && inputType && !isCompositionInputType(inputType)) {
|
|
308
|
+
this.#abandon(true);
|
|
309
|
+
}
|
|
310
|
+
this.#runAbandon();
|
|
311
|
+
};
|
|
312
|
+
#handleInput = (event) => {
|
|
313
|
+
const composition = this.#composition;
|
|
314
|
+
const { isTrusted, isComposing, inputType } = event;
|
|
315
|
+
if (composition !== null && event.target === composition.element && (isTrusted || isComposing || isCompositionInputType(inputType))) {
|
|
316
|
+
composition.text = textOf(composition.element);
|
|
317
|
+
} else {
|
|
318
|
+
this.#settle();
|
|
319
|
+
}
|
|
320
|
+
this.#runAbandon();
|
|
321
|
+
};
|
|
322
|
+
#handleBlur = (event) => {
|
|
323
|
+
this.#settle();
|
|
324
|
+
this.#runAbandon();
|
|
325
|
+
const composition = this.#composition;
|
|
326
|
+
if (composition === null || composition.target === composition.element) return;
|
|
327
|
+
const { element } = composition;
|
|
328
|
+
if (!(element instanceof Node) || event.target !== element) return;
|
|
329
|
+
const root = element.getRootNode();
|
|
330
|
+
if (root.activeElement !== element) this.#composition = null;
|
|
331
|
+
};
|
|
332
|
+
#handleBeforeMorphAttribute = (event) => {
|
|
333
|
+
this.#settle();
|
|
334
|
+
const composition = this.#composition;
|
|
335
|
+
if (composition?.target !== event.currentTarget || event.target !== composition.element) return;
|
|
336
|
+
const detail = event.detail;
|
|
337
|
+
if (detail?.attributeName === "value") event.preventDefault();
|
|
213
338
|
};
|
|
214
339
|
};
|
|
215
340
|
|
|
341
|
+
var ListenerSet = class {
|
|
342
|
+
#abort = new AbortController();
|
|
343
|
+
add(target, type, handler, options) {
|
|
344
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
345
|
+
}
|
|
346
|
+
dispose() {
|
|
347
|
+
this.#abort.abort();
|
|
348
|
+
this.#abort = new AbortController();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
216
352
|
var EditableController = class extends Controller {
|
|
217
353
|
static targets = ["display", "input"];
|
|
218
354
|
static values = {
|
|
@@ -232,17 +368,18 @@ var EditableController = class extends Controller {
|
|
|
232
368
|
if (next instanceof Node && this.element.contains(next)) return;
|
|
233
369
|
if (this.submitOnBlurValue) this.#commit(false);
|
|
234
370
|
};
|
|
371
|
+
#listeners = new ListenerSet();
|
|
235
372
|
connect() {
|
|
236
373
|
this.#connected = true;
|
|
237
374
|
if (this.hasInputTarget) this.#composition.observe(this.inputTarget);
|
|
238
|
-
this.element
|
|
375
|
+
this.#listeners.add(this.element, "focusout", this.#onFocusOut);
|
|
239
376
|
this.#revertValue = null;
|
|
240
377
|
this.#setMode("display");
|
|
241
378
|
}
|
|
242
379
|
disconnect() {
|
|
243
380
|
this.#connected = false;
|
|
244
381
|
this.#composition.disconnect();
|
|
245
|
-
this.
|
|
382
|
+
this.#listeners.dispose();
|
|
246
383
|
}
|
|
247
384
|
inputTargetConnected(input) {
|
|
248
385
|
this.#composition.observe(input);
|
|
@@ -283,6 +420,7 @@ var EditableController = class extends Controller {
|
|
|
283
420
|
this.#revertValue = null;
|
|
284
421
|
}
|
|
285
422
|
onDisplayKeydown(event) {
|
|
423
|
+
if (event.defaultPrevented) return;
|
|
286
424
|
if (event.key === "F2") {
|
|
287
425
|
event.preventDefault();
|
|
288
426
|
this.edit();
|