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
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
+
function fillTemplate(template, values) {
|
|
4
|
+
return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
|
|
5
|
+
const replacement = Object.hasOwn(values, name) ? values[name] : void 0;
|
|
6
|
+
return replacement === void 0 ? match : String(replacement);
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
function announce(message, options = {}) {
|
|
4
11
|
const text = message.trim();
|
|
5
12
|
if (text.length === 0) return;
|
|
@@ -9,11 +16,12 @@ function announce(message, options = {}) {
|
|
|
9
16
|
})
|
|
10
17
|
);
|
|
11
18
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
|
|
20
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
21
|
+
if (element.getAttribute(name) === value) return false;
|
|
22
|
+
if (value === null) element.removeAttribute(name);
|
|
23
|
+
else element.setAttribute(name, value);
|
|
24
|
+
return true;
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
function sharedRegistry(key, create) {
|
|
@@ -21,11 +29,15 @@ function sharedRegistry(key, create) {
|
|
|
21
29
|
const scope = globalThis;
|
|
22
30
|
const existing = scope[symbol];
|
|
23
31
|
if (existing !== void 0) return existing;
|
|
24
|
-
const
|
|
25
|
-
Object.defineProperty(globalThis, symbol, { value:
|
|
26
|
-
return
|
|
32
|
+
const registry2 = create();
|
|
33
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
34
|
+
return registry2;
|
|
27
35
|
}
|
|
28
36
|
|
|
37
|
+
var registry = sharedRegistry(
|
|
38
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
39
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
40
|
+
);
|
|
29
41
|
function isAttributeValue(value) {
|
|
30
42
|
return value === null || typeof value === "string";
|
|
31
43
|
}
|
|
@@ -42,24 +54,46 @@ function readLeaseRecord(element, record) {
|
|
|
42
54
|
return void 0;
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
57
|
+
function attributeStack(element, attribute) {
|
|
58
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
59
|
+
registry.stacks.set(element, byAttribute);
|
|
60
|
+
const known = byAttribute.get(attribute);
|
|
61
|
+
if (known) return known;
|
|
62
|
+
const found = [];
|
|
63
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
64
|
+
const read = readLeaseRecord(element, record);
|
|
65
|
+
if (read) found.push({ ...read, record });
|
|
66
|
+
}
|
|
67
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
68
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
69
|
+
lease: null,
|
|
70
|
+
record,
|
|
71
|
+
written,
|
|
72
|
+
original,
|
|
73
|
+
linked: linked && index > 0
|
|
74
|
+
}));
|
|
75
|
+
byAttribute.set(attribute, stack);
|
|
76
|
+
return stack;
|
|
77
|
+
}
|
|
78
|
+
function findHold(stack, lease, record) {
|
|
79
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
80
|
+
}
|
|
81
|
+
var AttributeLease = class {
|
|
50
82
|
#attribute;
|
|
51
83
|
#record;
|
|
52
84
|
#base;
|
|
85
|
+
#preserveAuthor;
|
|
53
86
|
#held = /* @__PURE__ */ new Set();
|
|
54
87
|
constructor(attribute, owner, options = {}) {
|
|
55
88
|
this.#attribute = attribute;
|
|
56
89
|
this.#record = `data-${owner}-${attribute}-lease`;
|
|
57
90
|
this.#base = options.base === true;
|
|
58
|
-
|
|
91
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
92
|
+
const names = registry.names;
|
|
59
93
|
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
60
94
|
}
|
|
61
95
|
write(element, value) {
|
|
62
|
-
const stack = this.#
|
|
96
|
+
const stack = attributeStack(element, this.#attribute);
|
|
63
97
|
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
64
98
|
this.#held.add(element);
|
|
65
99
|
const shows = !this.#covered(element, stack, hold);
|
|
@@ -69,8 +103,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
69
103
|
}
|
|
70
104
|
return(element) {
|
|
71
105
|
const held = this.#held.delete(element);
|
|
72
|
-
const known =
|
|
73
|
-
const stack = held || known || element.hasAttribute(this.#record) ? this.#
|
|
106
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
107
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
74
108
|
const hold = this.#take(stack);
|
|
75
109
|
if (!hold) return false;
|
|
76
110
|
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
@@ -88,31 +122,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
88
122
|
returnAll() {
|
|
89
123
|
for (const element of Array.from(this.#held)) this.return(element);
|
|
90
124
|
}
|
|
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
125
|
#take(stack) {
|
|
115
|
-
const hold = stack
|
|
126
|
+
const hold = findHold(stack, this, this.#record);
|
|
116
127
|
if (hold) hold.lease = this;
|
|
117
128
|
return hold;
|
|
118
129
|
}
|
|
@@ -121,7 +132,7 @@ var AttributeLease = class _AttributeLease {
|
|
|
121
132
|
const top = stack.at(-1);
|
|
122
133
|
const bottom = stack[0];
|
|
123
134
|
const over = top?.written === current ? top : void 0;
|
|
124
|
-
const from = this.#base ? bottom : over;
|
|
135
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
125
136
|
const hold = {
|
|
126
137
|
lease: this,
|
|
127
138
|
record: this.#record,
|
|
@@ -151,15 +162,11 @@ var AttributeLease = class _AttributeLease {
|
|
|
151
162
|
);
|
|
152
163
|
});
|
|
153
164
|
for (const [name, recorded] of records) {
|
|
154
|
-
|
|
155
|
-
if (recorded === null) element.removeAttribute(name);
|
|
156
|
-
else element.setAttribute(name, recorded);
|
|
165
|
+
setAttributeIfChanged(element, name, recorded);
|
|
157
166
|
}
|
|
158
167
|
}
|
|
159
168
|
#reflect(element, value) {
|
|
160
|
-
|
|
161
|
-
if (value === null) element.removeAttribute(this.#attribute);
|
|
162
|
-
else element.setAttribute(this.#attribute, value);
|
|
169
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
163
170
|
}
|
|
164
171
|
};
|
|
165
172
|
|
|
@@ -13,6 +13,16 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
13
13
|
if (!event.key.startsWith("Arrow")) return false;
|
|
14
14
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
15
15
|
}
|
|
16
|
+
function hasModifierChord(event) {
|
|
17
|
+
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
21
|
+
if (element.getAttribute(name) === value) return false;
|
|
22
|
+
if (value === null) element.removeAttribute(name);
|
|
23
|
+
else element.setAttribute(name, value);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
16
26
|
|
|
17
27
|
function writeField(field, value) {
|
|
18
28
|
if (field.value === value) return false;
|
|
@@ -50,6 +60,9 @@ var MicrotaskCoalescer = class {
|
|
|
50
60
|
this.#queued = false;
|
|
51
61
|
this.#generation += 1;
|
|
52
62
|
}
|
|
63
|
+
get pending() {
|
|
64
|
+
return this.#queued;
|
|
65
|
+
}
|
|
53
66
|
schedule() {
|
|
54
67
|
if (!this.#active || this.#queued) return;
|
|
55
68
|
this.#queued = true;
|
|
@@ -80,6 +93,9 @@ var MorphRenderWatcher = class {
|
|
|
80
93
|
schedule() {
|
|
81
94
|
this.#pass.schedule();
|
|
82
95
|
}
|
|
96
|
+
get pending() {
|
|
97
|
+
return this.#pass.pending;
|
|
98
|
+
}
|
|
83
99
|
disconnect() {
|
|
84
100
|
this.#listeners.dispose();
|
|
85
101
|
this.#pass.cancel();
|
|
@@ -189,7 +205,7 @@ var OwnedPointerSession = class {
|
|
|
189
205
|
function rangeFraction(value, min, max) {
|
|
190
206
|
const span = max - min;
|
|
191
207
|
if (!(span > 0)) return 0;
|
|
192
|
-
const clamped =
|
|
208
|
+
const clamped = clampRange(value, min, max);
|
|
193
209
|
let fraction;
|
|
194
210
|
if (Number.isFinite(span)) {
|
|
195
211
|
fraction = (clamped - min) / span;
|
|
@@ -200,6 +216,9 @@ function rangeFraction(value, min, max) {
|
|
|
200
216
|
if (!Number.isFinite(fraction)) return 0;
|
|
201
217
|
return Math.min(1, Math.max(0, fraction));
|
|
202
218
|
}
|
|
219
|
+
function clampRange(value, min, max) {
|
|
220
|
+
return Math.min(max, Math.max(min, value));
|
|
221
|
+
}
|
|
203
222
|
|
|
204
223
|
function effectiveStep(step) {
|
|
205
224
|
return Number.isFinite(step) && step > 0 ? step : 1;
|
|
@@ -330,8 +349,8 @@ var SliderController = class _SliderController extends Controller {
|
|
|
330
349
|
#drag = null;
|
|
331
350
|
#settled = 0;
|
|
332
351
|
#repaint = new MorphRenderWatcher(() => this.#render());
|
|
333
|
-
get #
|
|
334
|
-
return this.
|
|
352
|
+
get #directionElement() {
|
|
353
|
+
return this.hasTrackTarget ? this.trackTarget : this.element;
|
|
335
354
|
}
|
|
336
355
|
connect() {
|
|
337
356
|
this.#repaint.observe(this.element);
|
|
@@ -378,10 +397,12 @@ var SliderController = class _SliderController extends Controller {
|
|
|
378
397
|
}
|
|
379
398
|
onKeydown(event) {
|
|
380
399
|
if (isReservedArrowChord(event)) return;
|
|
400
|
+
if ((event.key === "Home" || event.key === "End" || event.key === "PageUp" || event.key === "PageDown") && hasModifierChord(event))
|
|
401
|
+
return;
|
|
381
402
|
let next = null;
|
|
382
403
|
const declared = this.#safeValue;
|
|
383
404
|
const range = this.#steppedRange;
|
|
384
|
-
switch (this
|
|
405
|
+
switch (this.logicalTrackValue ? logicalArrowKey(event.key, this.#directionElement) : event.key) {
|
|
385
406
|
case "ArrowRight":
|
|
386
407
|
case "ArrowUp":
|
|
387
408
|
next = stepSteppedValue(declared, 1, range);
|
|
@@ -412,7 +433,7 @@ var SliderController = class _SliderController extends Controller {
|
|
|
412
433
|
if (event.button !== 0 || this.#drag || !this.hasTrackTarget || !this.hasThumbTarget) return;
|
|
413
434
|
const track = this.trackTarget;
|
|
414
435
|
const thumb = this.thumbTarget;
|
|
415
|
-
const mirrored = this
|
|
436
|
+
const mirrored = this.logicalTrackValue && isRtl(track);
|
|
416
437
|
const value = this.#valueFromClientX(event.clientX, mirrored, track);
|
|
417
438
|
if (value === null) return;
|
|
418
439
|
event.preventDefault();
|
|
@@ -479,7 +500,7 @@ var SliderController = class _SliderController extends Controller {
|
|
|
479
500
|
"aria-valuenow": String(value)
|
|
480
501
|
};
|
|
481
502
|
for (const [name, next] of Object.entries(attributes)) {
|
|
482
|
-
|
|
503
|
+
setAttributeIfChanged(thumb, name, next);
|
|
483
504
|
}
|
|
484
505
|
}
|
|
485
506
|
#renderFraction(value) {
|
|
@@ -62,6 +62,9 @@ var MicrotaskCoalescer = class {
|
|
|
62
62
|
this.#queued = false;
|
|
63
63
|
this.#generation += 1;
|
|
64
64
|
}
|
|
65
|
+
get pending() {
|
|
66
|
+
return this.#queued;
|
|
67
|
+
}
|
|
65
68
|
schedule() {
|
|
66
69
|
if (!this.#active || this.#queued) return;
|
|
67
70
|
this.#queued = true;
|
|
@@ -92,6 +95,9 @@ var MorphRenderWatcher = class {
|
|
|
92
95
|
schedule() {
|
|
93
96
|
this.#pass.schedule();
|
|
94
97
|
}
|
|
98
|
+
get pending() {
|
|
99
|
+
return this.#pass.pending;
|
|
100
|
+
}
|
|
95
101
|
disconnect() {
|
|
96
102
|
this.#listeners.dispose();
|
|
97
103
|
this.#pass.cancel();
|
|
@@ -170,6 +176,8 @@ var SmartStickyHeaderController = class _SmartStickyHeaderController extends Con
|
|
|
170
176
|
static events = ["change"];
|
|
171
177
|
#frames = new FrameCoalescer();
|
|
172
178
|
#scrollerEl = window;
|
|
179
|
+
#scrollListeners = new ListenerSet();
|
|
180
|
+
#listeners = new ListenerSet();
|
|
173
181
|
#containerSelector = "";
|
|
174
182
|
#lastY = 0;
|
|
175
183
|
#hidden = null;
|
|
@@ -193,25 +201,25 @@ var SmartStickyHeaderController = class _SmartStickyHeaderController extends Con
|
|
|
193
201
|
this.#morphRender.observe(this.element);
|
|
194
202
|
this.#hidden = null;
|
|
195
203
|
this.#listenTo(resolveScrollSource(this.#containerSelector));
|
|
196
|
-
this.element
|
|
204
|
+
this.#listeners.add(this.element, "focusin", this.#onFocusin);
|
|
197
205
|
this.#apply(false, false);
|
|
198
206
|
}
|
|
199
207
|
disconnect() {
|
|
200
208
|
this.#morphRender.disconnect();
|
|
201
|
-
this.#
|
|
202
|
-
this.
|
|
209
|
+
this.#scrollListeners.dispose();
|
|
210
|
+
this.#listeners.dispose();
|
|
203
211
|
this.#frames.cancel();
|
|
204
212
|
}
|
|
205
213
|
#followSource() {
|
|
206
214
|
const source = resolveScrollSource(this.#containerSelector);
|
|
207
215
|
if (source === this.#scrollerEl) return;
|
|
208
|
-
this.#
|
|
216
|
+
this.#scrollListeners.dispose();
|
|
209
217
|
this.#listenTo(source);
|
|
210
218
|
}
|
|
211
219
|
#listenTo(source) {
|
|
212
220
|
this.#scrollerEl = source;
|
|
213
221
|
this.#lastY = scrollOffset(source);
|
|
214
|
-
|
|
222
|
+
this.#scrollListeners.add(source, "scroll", this.#onScroll, { passive: true });
|
|
215
223
|
}
|
|
216
224
|
#measure() {
|
|
217
225
|
const y = scrollOffset(this.#scrollerEl);
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
+
function fillTemplate(template, values) {
|
|
4
|
+
return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
|
|
5
|
+
const replacement = Object.hasOwn(values, name) ? values[name] : void 0;
|
|
6
|
+
return replacement === void 0 ? match : String(replacement);
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
function announce(message, options = {}) {
|
|
4
11
|
const text = message.trim();
|
|
5
12
|
if (text.length === 0) return;
|
|
@@ -9,12 +16,17 @@ function announce(message, options = {}) {
|
|
|
9
16
|
})
|
|
10
17
|
);
|
|
11
18
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
19
|
+
|
|
20
|
+
var ListenerSet = class {
|
|
21
|
+
#abort = new AbortController();
|
|
22
|
+
add(target, type, handler, options) {
|
|
23
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
24
|
+
}
|
|
25
|
+
dispose() {
|
|
26
|
+
this.#abort.abort();
|
|
27
|
+
this.#abort = new AbortController();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
18
30
|
|
|
19
31
|
function isRtl(element) {
|
|
20
32
|
return window.getComputedStyle(element).direction === "rtl";
|
|
@@ -36,6 +48,9 @@ var MicrotaskCoalescer = class {
|
|
|
36
48
|
this.#queued = false;
|
|
37
49
|
this.#generation += 1;
|
|
38
50
|
}
|
|
51
|
+
get pending() {
|
|
52
|
+
return this.#queued;
|
|
53
|
+
}
|
|
39
54
|
schedule() {
|
|
40
55
|
if (!this.#active || this.#queued) return;
|
|
41
56
|
this.#queued = true;
|
|
@@ -71,19 +86,17 @@ var SortableController = class extends Controller {
|
|
|
71
86
|
static events = ["reorder"];
|
|
72
87
|
#session = null;
|
|
73
88
|
#settle = new MicrotaskCoalescer(() => this.#dropLostSession());
|
|
89
|
+
#listeners = new ListenerSet();
|
|
74
90
|
connect() {
|
|
75
91
|
TRANSIENT.reset(this.element);
|
|
76
92
|
this.#settle.activate();
|
|
77
|
-
this.element
|
|
78
|
-
this.element
|
|
79
|
-
this.element
|
|
80
|
-
this.element
|
|
93
|
+
this.#listeners.add(this.element, "stimeo--pointer-drag:start", this.#onDragStart);
|
|
94
|
+
this.#listeners.add(this.element, "stimeo--pointer-drag:move", this.#onDragMove);
|
|
95
|
+
this.#listeners.add(this.element, "stimeo--pointer-drag:end", this.#onDragEnd);
|
|
96
|
+
this.#listeners.add(this.element, "stimeo--pointer-drag:cancel", this.#onDragCancel);
|
|
81
97
|
}
|
|
82
98
|
disconnect() {
|
|
83
|
-
this.
|
|
84
|
-
this.element.removeEventListener("stimeo--pointer-drag:move", this.#onDragMove);
|
|
85
|
-
this.element.removeEventListener("stimeo--pointer-drag:end", this.#onDragEnd);
|
|
86
|
-
this.element.removeEventListener("stimeo--pointer-drag:cancel", this.#onDragCancel);
|
|
99
|
+
this.#listeners.dispose();
|
|
87
100
|
this.#settle.cancel();
|
|
88
101
|
this.#session = null;
|
|
89
102
|
this.element.removeAttribute("data-sortable-dragging");
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
+
function fillTemplate(template, values) {
|
|
4
|
+
return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
|
|
5
|
+
const replacement = Object.hasOwn(values, name) ? values[name] : void 0;
|
|
6
|
+
return replacement === void 0 ? match : String(replacement);
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
function announce(message, options = {}) {
|
|
4
11
|
const text = message.trim();
|
|
5
12
|
if (text.length === 0) return;
|
|
@@ -9,11 +16,12 @@ function announce(message, options = {}) {
|
|
|
9
16
|
})
|
|
10
17
|
);
|
|
11
18
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
|
|
20
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
21
|
+
if (element.getAttribute(name) === value) return false;
|
|
22
|
+
if (value === null) element.removeAttribute(name);
|
|
23
|
+
else element.setAttribute(name, value);
|
|
24
|
+
return true;
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
function sharedRegistry(key, create) {
|
|
@@ -21,11 +29,15 @@ function sharedRegistry(key, create) {
|
|
|
21
29
|
const scope = globalThis;
|
|
22
30
|
const existing = scope[symbol];
|
|
23
31
|
if (existing !== void 0) return existing;
|
|
24
|
-
const
|
|
25
|
-
Object.defineProperty(globalThis, symbol, { value:
|
|
26
|
-
return
|
|
32
|
+
const registry2 = create();
|
|
33
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
34
|
+
return registry2;
|
|
27
35
|
}
|
|
28
36
|
|
|
37
|
+
var registry = sharedRegistry(
|
|
38
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
39
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
40
|
+
);
|
|
29
41
|
function isAttributeValue(value) {
|
|
30
42
|
return value === null || typeof value === "string";
|
|
31
43
|
}
|
|
@@ -42,24 +54,46 @@ function readLeaseRecord(element, record) {
|
|
|
42
54
|
return void 0;
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
57
|
+
function attributeStack(element, attribute) {
|
|
58
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
59
|
+
registry.stacks.set(element, byAttribute);
|
|
60
|
+
const known = byAttribute.get(attribute);
|
|
61
|
+
if (known) return known;
|
|
62
|
+
const found = [];
|
|
63
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
64
|
+
const read = readLeaseRecord(element, record);
|
|
65
|
+
if (read) found.push({ ...read, record });
|
|
66
|
+
}
|
|
67
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
68
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
69
|
+
lease: null,
|
|
70
|
+
record,
|
|
71
|
+
written,
|
|
72
|
+
original,
|
|
73
|
+
linked: linked && index > 0
|
|
74
|
+
}));
|
|
75
|
+
byAttribute.set(attribute, stack);
|
|
76
|
+
return stack;
|
|
77
|
+
}
|
|
78
|
+
function findHold(stack, lease, record) {
|
|
79
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
80
|
+
}
|
|
81
|
+
var AttributeLease = class {
|
|
50
82
|
#attribute;
|
|
51
83
|
#record;
|
|
52
84
|
#base;
|
|
85
|
+
#preserveAuthor;
|
|
53
86
|
#held = /* @__PURE__ */ new Set();
|
|
54
87
|
constructor(attribute, owner, options = {}) {
|
|
55
88
|
this.#attribute = attribute;
|
|
56
89
|
this.#record = `data-${owner}-${attribute}-lease`;
|
|
57
90
|
this.#base = options.base === true;
|
|
58
|
-
|
|
91
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
92
|
+
const names = registry.names;
|
|
59
93
|
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
60
94
|
}
|
|
61
95
|
write(element, value) {
|
|
62
|
-
const stack = this.#
|
|
96
|
+
const stack = attributeStack(element, this.#attribute);
|
|
63
97
|
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
64
98
|
this.#held.add(element);
|
|
65
99
|
const shows = !this.#covered(element, stack, hold);
|
|
@@ -69,8 +103,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
69
103
|
}
|
|
70
104
|
return(element) {
|
|
71
105
|
const held = this.#held.delete(element);
|
|
72
|
-
const known =
|
|
73
|
-
const stack = held || known || element.hasAttribute(this.#record) ? this.#
|
|
106
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
107
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
74
108
|
const hold = this.#take(stack);
|
|
75
109
|
if (!hold) return false;
|
|
76
110
|
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
@@ -88,31 +122,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
88
122
|
returnAll() {
|
|
89
123
|
for (const element of Array.from(this.#held)) this.return(element);
|
|
90
124
|
}
|
|
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
125
|
#take(stack) {
|
|
115
|
-
const hold = stack
|
|
126
|
+
const hold = findHold(stack, this, this.#record);
|
|
116
127
|
if (hold) hold.lease = this;
|
|
117
128
|
return hold;
|
|
118
129
|
}
|
|
@@ -121,7 +132,7 @@ var AttributeLease = class _AttributeLease {
|
|
|
121
132
|
const top = stack.at(-1);
|
|
122
133
|
const bottom = stack[0];
|
|
123
134
|
const over = top?.written === current ? top : void 0;
|
|
124
|
-
const from = this.#base ? bottom : over;
|
|
135
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
125
136
|
const hold = {
|
|
126
137
|
lease: this,
|
|
127
138
|
record: this.#record,
|
|
@@ -151,15 +162,11 @@ var AttributeLease = class _AttributeLease {
|
|
|
151
162
|
);
|
|
152
163
|
});
|
|
153
164
|
for (const [name, recorded] of records) {
|
|
154
|
-
|
|
155
|
-
if (recorded === null) element.removeAttribute(name);
|
|
156
|
-
else element.setAttribute(name, recorded);
|
|
165
|
+
setAttributeIfChanged(element, name, recorded);
|
|
157
166
|
}
|
|
158
167
|
}
|
|
159
168
|
#reflect(element, value) {
|
|
160
|
-
|
|
161
|
-
if (value === null) element.removeAttribute(this.#attribute);
|
|
162
|
-
else element.setAttribute(this.#attribute, value);
|
|
169
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
163
170
|
}
|
|
164
171
|
};
|
|
165
172
|
|
|
@@ -27,6 +27,9 @@ var MicrotaskCoalescer = class {
|
|
|
27
27
|
this.#queued = false;
|
|
28
28
|
this.#generation += 1;
|
|
29
29
|
}
|
|
30
|
+
get pending() {
|
|
31
|
+
return this.#queued;
|
|
32
|
+
}
|
|
30
33
|
schedule() {
|
|
31
34
|
if (!this.#active || this.#queued) return;
|
|
32
35
|
this.#queued = true;
|
|
@@ -57,6 +60,9 @@ var MorphRenderWatcher = class {
|
|
|
57
60
|
schedule() {
|
|
58
61
|
this.#pass.schedule();
|
|
59
62
|
}
|
|
63
|
+
get pending() {
|
|
64
|
+
return this.#pass.pending;
|
|
65
|
+
}
|
|
60
66
|
disconnect() {
|
|
61
67
|
this.#listeners.dispose();
|
|
62
68
|
this.#pass.cancel();
|
|
@@ -53,6 +53,9 @@ var MicrotaskCoalescer = class {
|
|
|
53
53
|
this.#queued = false;
|
|
54
54
|
this.#generation += 1;
|
|
55
55
|
}
|
|
56
|
+
get pending() {
|
|
57
|
+
return this.#queued;
|
|
58
|
+
}
|
|
56
59
|
schedule() {
|
|
57
60
|
if (!this.#active || this.#queued) return;
|
|
58
61
|
this.#queued = true;
|
|
@@ -83,6 +86,9 @@ var MorphRenderWatcher = class {
|
|
|
83
86
|
schedule() {
|
|
84
87
|
this.#pass.schedule();
|
|
85
88
|
}
|
|
89
|
+
get pending() {
|
|
90
|
+
return this.#pass.pending;
|
|
91
|
+
}
|
|
86
92
|
disconnect() {
|
|
87
93
|
this.#listeners.dispose();
|
|
88
94
|
this.#pass.cancel();
|