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,20 +1,41 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
+
function isAriaDisabled(element) {
|
|
4
|
+
for (let current = element; current; current = current.parentElement) {
|
|
5
|
+
if (current.getAttribute("aria-disabled") === "true") return true;
|
|
6
|
+
}
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
function isReservedArrowChord(event, allow = []) {
|
|
4
11
|
if (!event.key.startsWith("Arrow")) return false;
|
|
5
12
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
6
13
|
}
|
|
14
|
+
function hasModifierChord(event) {
|
|
15
|
+
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
19
|
+
if (element.getAttribute(name) === value) return false;
|
|
20
|
+
if (value === null) element.removeAttribute(name);
|
|
21
|
+
else element.setAttribute(name, value);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
7
24
|
|
|
8
25
|
function sharedRegistry(key, create) {
|
|
9
26
|
const symbol = Symbol.for(key);
|
|
10
27
|
const scope = globalThis;
|
|
11
28
|
const existing = scope[symbol];
|
|
12
29
|
if (existing !== void 0) return existing;
|
|
13
|
-
const
|
|
14
|
-
Object.defineProperty(globalThis, symbol, { value:
|
|
15
|
-
return
|
|
30
|
+
const registry2 = create();
|
|
31
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
32
|
+
return registry2;
|
|
16
33
|
}
|
|
17
34
|
|
|
35
|
+
var registry = sharedRegistry(
|
|
36
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
37
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
38
|
+
);
|
|
18
39
|
function isAttributeValue(value) {
|
|
19
40
|
return value === null || typeof value === "string";
|
|
20
41
|
}
|
|
@@ -31,24 +52,46 @@ function readLeaseRecord(element, record) {
|
|
|
31
52
|
return void 0;
|
|
32
53
|
}
|
|
33
54
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
);
|
|
55
|
+
function attributeStack(element, attribute) {
|
|
56
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
57
|
+
registry.stacks.set(element, byAttribute);
|
|
58
|
+
const known = byAttribute.get(attribute);
|
|
59
|
+
if (known) return known;
|
|
60
|
+
const found = [];
|
|
61
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
62
|
+
const read = readLeaseRecord(element, record);
|
|
63
|
+
if (read) found.push({ ...read, record });
|
|
64
|
+
}
|
|
65
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
66
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
67
|
+
lease: null,
|
|
68
|
+
record,
|
|
69
|
+
written,
|
|
70
|
+
original,
|
|
71
|
+
linked: linked && index > 0
|
|
72
|
+
}));
|
|
73
|
+
byAttribute.set(attribute, stack);
|
|
74
|
+
return stack;
|
|
75
|
+
}
|
|
76
|
+
function findHold(stack, lease, record) {
|
|
77
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
78
|
+
}
|
|
79
|
+
var AttributeLease = class {
|
|
39
80
|
#attribute;
|
|
40
81
|
#record;
|
|
41
82
|
#base;
|
|
83
|
+
#preserveAuthor;
|
|
42
84
|
#held = /* @__PURE__ */ new Set();
|
|
43
85
|
constructor(attribute, owner, options = {}) {
|
|
44
86
|
this.#attribute = attribute;
|
|
45
87
|
this.#record = `data-${owner}-${attribute}-lease`;
|
|
46
88
|
this.#base = options.base === true;
|
|
47
|
-
|
|
89
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
90
|
+
const names = registry.names;
|
|
48
91
|
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
49
92
|
}
|
|
50
93
|
write(element, value) {
|
|
51
|
-
const stack = this.#
|
|
94
|
+
const stack = attributeStack(element, this.#attribute);
|
|
52
95
|
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
53
96
|
this.#held.add(element);
|
|
54
97
|
const shows = !this.#covered(element, stack, hold);
|
|
@@ -58,8 +101,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
58
101
|
}
|
|
59
102
|
return(element) {
|
|
60
103
|
const held = this.#held.delete(element);
|
|
61
|
-
const known =
|
|
62
|
-
const stack = held || known || element.hasAttribute(this.#record) ? this.#
|
|
104
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
105
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
63
106
|
const hold = this.#take(stack);
|
|
64
107
|
if (!hold) return false;
|
|
65
108
|
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
@@ -77,31 +120,8 @@ var AttributeLease = class _AttributeLease {
|
|
|
77
120
|
returnAll() {
|
|
78
121
|
for (const element of Array.from(this.#held)) this.return(element);
|
|
79
122
|
}
|
|
80
|
-
#stack(element) {
|
|
81
|
-
const stacks = _AttributeLease.#registry.stacks;
|
|
82
|
-
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
83
|
-
stacks.set(element, byAttribute);
|
|
84
|
-
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
85
|
-
byAttribute.set(this.#attribute, stack);
|
|
86
|
-
return stack;
|
|
87
|
-
}
|
|
88
|
-
#seed(element) {
|
|
89
|
-
const found = [];
|
|
90
|
-
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
91
|
-
const read = readLeaseRecord(element, record);
|
|
92
|
-
if (read) found.push({ ...read, record });
|
|
93
|
-
}
|
|
94
|
-
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
95
|
-
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
96
|
-
lease: null,
|
|
97
|
-
record,
|
|
98
|
-
written,
|
|
99
|
-
original,
|
|
100
|
-
linked: linked && index > 0
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
123
|
#take(stack) {
|
|
104
|
-
const hold = stack
|
|
124
|
+
const hold = findHold(stack, this, this.#record);
|
|
105
125
|
if (hold) hold.lease = this;
|
|
106
126
|
return hold;
|
|
107
127
|
}
|
|
@@ -110,7 +130,7 @@ var AttributeLease = class _AttributeLease {
|
|
|
110
130
|
const top = stack.at(-1);
|
|
111
131
|
const bottom = stack[0];
|
|
112
132
|
const over = top?.written === current ? top : void 0;
|
|
113
|
-
const from = this.#base ? bottom : over;
|
|
133
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
114
134
|
const hold = {
|
|
115
135
|
lease: this,
|
|
116
136
|
record: this.#record,
|
|
@@ -140,15 +160,23 @@ var AttributeLease = class _AttributeLease {
|
|
|
140
160
|
);
|
|
141
161
|
});
|
|
142
162
|
for (const [name, recorded] of records) {
|
|
143
|
-
|
|
144
|
-
if (recorded === null) element.removeAttribute(name);
|
|
145
|
-
else element.setAttribute(name, recorded);
|
|
163
|
+
setAttributeIfChanged(element, name, recorded);
|
|
146
164
|
}
|
|
147
165
|
}
|
|
148
166
|
#reflect(element, value) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
167
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
var ClickVerdicts = class {
|
|
172
|
+
#verdicts = /* @__PURE__ */ new WeakMap();
|
|
173
|
+
record(event, entries, refused) {
|
|
174
|
+
this.#verdicts.set(event, { entries, refused });
|
|
175
|
+
}
|
|
176
|
+
for(event, entry) {
|
|
177
|
+
const verdict = event && this.#verdicts.get(event);
|
|
178
|
+
if (!verdict?.entries.includes(entry)) return void 0;
|
|
179
|
+
return verdict.refused;
|
|
152
180
|
}
|
|
153
181
|
};
|
|
154
182
|
|
|
@@ -166,25 +194,25 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
166
194
|
#active = null;
|
|
167
195
|
activate(ownerDocument = document, options) {
|
|
168
196
|
this.deactivate();
|
|
169
|
-
let
|
|
170
|
-
if (!
|
|
171
|
-
|
|
172
|
-
_EscapeLayer.#registries.set(ownerDocument,
|
|
173
|
-
ownerDocument.addEventListener("keydown",
|
|
197
|
+
let registry2 = _EscapeLayer.#registries.get(ownerDocument);
|
|
198
|
+
if (!registry2) {
|
|
199
|
+
registry2 = _EscapeLayer.#createRegistry();
|
|
200
|
+
_EscapeLayer.#registries.set(ownerDocument, registry2);
|
|
201
|
+
ownerDocument.addEventListener("keydown", registry2.onKeydown);
|
|
174
202
|
}
|
|
175
203
|
const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
|
|
176
|
-
|
|
204
|
+
registry2.stack.push(entry);
|
|
177
205
|
this.#active = { document: ownerDocument, entry };
|
|
178
206
|
}
|
|
179
207
|
deactivate() {
|
|
180
208
|
const active = this.#active;
|
|
181
209
|
if (!active) return;
|
|
182
|
-
const
|
|
183
|
-
if (
|
|
184
|
-
const index =
|
|
185
|
-
if (index >= 0)
|
|
186
|
-
if (
|
|
187
|
-
active.document.removeEventListener("keydown",
|
|
210
|
+
const registry2 = _EscapeLayer.#registries.get(active.document);
|
|
211
|
+
if (registry2) {
|
|
212
|
+
const index = registry2.stack.lastIndexOf(active.entry);
|
|
213
|
+
if (index >= 0) registry2.stack.splice(index, 1);
|
|
214
|
+
if (registry2.stack.length === 0) {
|
|
215
|
+
active.document.removeEventListener("keydown", registry2.onKeydown);
|
|
188
216
|
_EscapeLayer.#registries.delete(active.document);
|
|
189
217
|
}
|
|
190
218
|
}
|
|
@@ -193,22 +221,22 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
193
221
|
get ownsEscape() {
|
|
194
222
|
const active = this.#active;
|
|
195
223
|
if (!active) return false;
|
|
196
|
-
const
|
|
197
|
-
if (!
|
|
198
|
-
return _EscapeLayer.#resolveOwner(
|
|
224
|
+
const registry2 = _EscapeLayer.#registries.get(active.document);
|
|
225
|
+
if (!registry2) return false;
|
|
226
|
+
return _EscapeLayer.#resolveOwner(registry2.stack) === active.entry;
|
|
199
227
|
}
|
|
200
228
|
static #createRegistry() {
|
|
201
|
-
const
|
|
229
|
+
const registry2 = {
|
|
202
230
|
stack: [],
|
|
203
231
|
onKeydown: (event) => {
|
|
204
232
|
if (event.key !== "Escape" || event.defaultPrevented || event.isComposing) return;
|
|
205
|
-
const owner = _EscapeLayer.#resolveOwner(
|
|
233
|
+
const owner = _EscapeLayer.#resolveOwner(registry2.stack);
|
|
206
234
|
if (!owner) return;
|
|
207
235
|
event.preventDefault();
|
|
208
236
|
owner.onDismiss();
|
|
209
237
|
}
|
|
210
238
|
};
|
|
211
|
-
return
|
|
239
|
+
return registry2;
|
|
212
240
|
}
|
|
213
241
|
static #resolveOwner(stack) {
|
|
214
242
|
for (let index = stack.length - 1; index >= 0; index--) {
|
|
@@ -228,6 +256,21 @@ function ownerIndex(candidates, node) {
|
|
|
228
256
|
function ownerOf(candidates, node) {
|
|
229
257
|
return candidates[ownerIndex(candidates, node)] ?? null;
|
|
230
258
|
}
|
|
259
|
+
function ownersOf(candidates, node) {
|
|
260
|
+
if (!(node instanceof Node)) return [];
|
|
261
|
+
return candidates.filter((candidate) => candidate.contains(node));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
var ListenerSet = class {
|
|
265
|
+
#abort = new AbortController();
|
|
266
|
+
add(target, type, handler, options) {
|
|
267
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
268
|
+
}
|
|
269
|
+
dispose() {
|
|
270
|
+
this.#abort.abort();
|
|
271
|
+
this.#abort = new AbortController();
|
|
272
|
+
}
|
|
273
|
+
};
|
|
231
274
|
|
|
232
275
|
var TimerRegistry = class {
|
|
233
276
|
ids = /* @__PURE__ */ new Set();
|
|
@@ -289,15 +332,17 @@ var MenuController = class extends Controller {
|
|
|
289
332
|
#expanded = new AttributeLease("aria-expanded", this.identifier);
|
|
290
333
|
#menu = null;
|
|
291
334
|
#clickOwner = null;
|
|
335
|
+
#clickVerdicts = new ClickVerdicts();
|
|
292
336
|
#activated = /* @__PURE__ */ new WeakSet();
|
|
293
337
|
#connected = false;
|
|
294
338
|
#reporting = false;
|
|
339
|
+
#listeners = new ListenerSet();
|
|
295
340
|
connect() {
|
|
296
341
|
this.#close("api");
|
|
297
|
-
this.element
|
|
298
|
-
this.element
|
|
299
|
-
this.element
|
|
300
|
-
|
|
342
|
+
this.#listeners.add(this.element, "click", this.#onItemClickCapture, { capture: true });
|
|
343
|
+
this.#listeners.add(this.element, "click", this.#onDelegatedItemClick);
|
|
344
|
+
this.#listeners.add(this.element, "keydown", this.#onDelegatedItemKeydown);
|
|
345
|
+
this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
|
|
301
346
|
this.#connected = true;
|
|
302
347
|
this.#reporting = true;
|
|
303
348
|
}
|
|
@@ -306,10 +351,7 @@ var MenuController = class extends Controller {
|
|
|
306
351
|
this.#reporting = false;
|
|
307
352
|
this.#timers.clearAll();
|
|
308
353
|
this.#escapeLayer.deactivate();
|
|
309
|
-
this.
|
|
310
|
-
this.element.removeEventListener("click", this.#onDelegatedItemClick);
|
|
311
|
-
this.element.removeEventListener("keydown", this.#onDelegatedItemKeydown);
|
|
312
|
-
document.removeEventListener("click", this.#onOutsideClick, true);
|
|
354
|
+
this.#listeners.dispose();
|
|
313
355
|
}
|
|
314
356
|
triggerTargetConnected() {
|
|
315
357
|
if (this.#connected) this.#reflectTrigger();
|
|
@@ -398,6 +440,7 @@ var MenuController = class extends Controller {
|
|
|
398
440
|
#handleItemKeydown(event, from) {
|
|
399
441
|
if (event.defaultPrevented) return;
|
|
400
442
|
if (isReservedArrowChord(event)) return;
|
|
443
|
+
if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
|
|
401
444
|
const items = this.#navigableItems;
|
|
402
445
|
const currentIndex = items.indexOf(from);
|
|
403
446
|
switch (event.key) {
|
|
@@ -424,12 +467,22 @@ var MenuController = class extends Controller {
|
|
|
424
467
|
}
|
|
425
468
|
}
|
|
426
469
|
activate(event) {
|
|
427
|
-
if (event
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
470
|
+
if (event === void 0) this.#closeAndRestore("select");
|
|
471
|
+
else this.#activateItem(this.#itemFrom(event.target), event);
|
|
472
|
+
}
|
|
473
|
+
#activateItem(item, event) {
|
|
474
|
+
if (item !== null && this.#refuses(item, event)) return;
|
|
475
|
+
if (this.#activated.has(event)) return;
|
|
476
|
+
this.#activated.add(event);
|
|
431
477
|
this.#closeAndRestore("select");
|
|
432
478
|
}
|
|
479
|
+
#refuses(item, event) {
|
|
480
|
+
const recorded = this.#clickVerdicts.for(event, item);
|
|
481
|
+
if (recorded !== void 0) return recorded;
|
|
482
|
+
return ownersOf(this.itemTargets, event.target).some(
|
|
483
|
+
(owner) => this.#isActivationDisabled(owner)
|
|
484
|
+
);
|
|
485
|
+
}
|
|
433
486
|
#closeAndRestore(reason) {
|
|
434
487
|
this.#close(reason);
|
|
435
488
|
if (this.hasTriggerTarget) this.triggerTarget.focus();
|
|
@@ -446,20 +499,20 @@ var MenuController = class extends Controller {
|
|
|
446
499
|
const item = this.#clickOwner;
|
|
447
500
|
this.#clickOwner = null;
|
|
448
501
|
if (item === null) return;
|
|
449
|
-
this
|
|
502
|
+
this.#activateItem(item, event);
|
|
450
503
|
};
|
|
451
504
|
#itemFrom(node) {
|
|
452
505
|
return ownerOf(this.itemTargets, node);
|
|
453
506
|
}
|
|
454
507
|
#onItemClickCapture = (event) => {
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (
|
|
508
|
+
const items = ownersOf(this.itemTargets, event.target);
|
|
509
|
+
this.#clickOwner = items[0] ?? null;
|
|
510
|
+
this.#clickVerdicts.record(
|
|
511
|
+
event,
|
|
512
|
+
items,
|
|
513
|
+
items.some((item) => this.#isActivationDisabled(item))
|
|
514
|
+
);
|
|
515
|
+
if (!items.some((item) => isAriaDisabled(item))) return;
|
|
463
516
|
this.#clickOwner = null;
|
|
464
517
|
event.preventDefault();
|
|
465
518
|
event.stopImmediatePropagation();
|
|
@@ -480,6 +533,9 @@ var MenuController = class extends Controller {
|
|
|
480
533
|
if (item.hasAttribute("hidden")) return false;
|
|
481
534
|
return !item.disabled;
|
|
482
535
|
}
|
|
536
|
+
#isActivationDisabled(item) {
|
|
537
|
+
return !this.#isNavigable(item) || isAriaDisabled(item);
|
|
538
|
+
}
|
|
483
539
|
get #isOpen() {
|
|
484
540
|
return this.hasMenuTarget && !this.menuTarget.hidden;
|
|
485
541
|
}
|
|
@@ -22,6 +22,13 @@ function actionSource(source) {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function isAriaDisabled(element) {
|
|
26
|
+
for (let current = element; current; current = current.parentElement) {
|
|
27
|
+
if (current.getAttribute("aria-disabled") === "true") return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
function isRtl(element) {
|
|
26
33
|
return window.getComputedStyle(element).direction === "rtl";
|
|
27
34
|
}
|
|
@@ -30,6 +37,21 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
30
37
|
if (!event.key.startsWith("Arrow")) return false;
|
|
31
38
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
32
39
|
}
|
|
40
|
+
function hasModifierChord(event) {
|
|
41
|
+
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
var ClickVerdicts = class {
|
|
45
|
+
#verdicts = /* @__PURE__ */ new WeakMap();
|
|
46
|
+
record(event, entries, refused) {
|
|
47
|
+
this.#verdicts.set(event, { entries, refused });
|
|
48
|
+
}
|
|
49
|
+
for(event, entry) {
|
|
50
|
+
const verdict = event && this.#verdicts.get(event);
|
|
51
|
+
if (!verdict?.entries.includes(entry)) return void 0;
|
|
52
|
+
return verdict.refused;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
33
55
|
|
|
34
56
|
function sharedRegistry(key, create) {
|
|
35
57
|
const symbol = Symbol.for(key);
|
|
@@ -117,6 +139,10 @@ function ownerIndex(candidates, node) {
|
|
|
117
139
|
function ownerOf(candidates, node) {
|
|
118
140
|
return candidates[ownerIndex(candidates, node)] ?? null;
|
|
119
141
|
}
|
|
142
|
+
function ownersOf(candidates, node) {
|
|
143
|
+
if (!(node instanceof Node)) return [];
|
|
144
|
+
return candidates.filter((candidate) => candidate.contains(node));
|
|
145
|
+
}
|
|
120
146
|
|
|
121
147
|
var ListenerSet = class {
|
|
122
148
|
#abort = new AbortController();
|
|
@@ -145,6 +171,9 @@ var MicrotaskCoalescer = class {
|
|
|
145
171
|
this.#queued = false;
|
|
146
172
|
this.#generation += 1;
|
|
147
173
|
}
|
|
174
|
+
get pending() {
|
|
175
|
+
return this.#queued;
|
|
176
|
+
}
|
|
148
177
|
schedule() {
|
|
149
178
|
if (!this.#active || this.#queued) return;
|
|
150
179
|
this.#queued = true;
|
|
@@ -175,6 +204,9 @@ var MorphRenderWatcher = class {
|
|
|
175
204
|
schedule() {
|
|
176
205
|
this.#pass.schedule();
|
|
177
206
|
}
|
|
207
|
+
get pending() {
|
|
208
|
+
return this.#pass.pending;
|
|
209
|
+
}
|
|
178
210
|
disconnect() {
|
|
179
211
|
this.#listeners.dispose();
|
|
180
212
|
this.#pass.cancel();
|
|
@@ -298,6 +330,7 @@ var MenubarController = class extends Controller {
|
|
|
298
330
|
#connected = false;
|
|
299
331
|
#reporting = false;
|
|
300
332
|
#focused = null;
|
|
333
|
+
#clickVerdicts = new ClickVerdicts();
|
|
301
334
|
#onFocusIn = (event) => {
|
|
302
335
|
this.#focused = event.target;
|
|
303
336
|
};
|
|
@@ -306,14 +339,15 @@ var MenubarController = class extends Controller {
|
|
|
306
339
|
if (next && !this.element.contains(next)) this.#focused = null;
|
|
307
340
|
};
|
|
308
341
|
#morph = new MorphRenderWatcher(() => this.#reconcile());
|
|
342
|
+
#listeners = new ListenerSet();
|
|
309
343
|
connect() {
|
|
310
344
|
this.#morph.observe(this.element);
|
|
311
345
|
this.#closeAllMenus("api");
|
|
312
346
|
this.#reconcile();
|
|
313
|
-
this.element
|
|
314
|
-
this.element
|
|
315
|
-
this.element
|
|
316
|
-
|
|
347
|
+
this.#listeners.add(this.element, "click", this.#onClickCapture, { capture: true });
|
|
348
|
+
this.#listeners.add(this.element, "focusin", this.#onFocusIn);
|
|
349
|
+
this.#listeners.add(this.element, "focusout", this.#onFocusOut);
|
|
350
|
+
this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
|
|
317
351
|
if (typeof MutationObserver !== "undefined") {
|
|
318
352
|
this.#observer = new MutationObserver(() => this.#morph.schedule());
|
|
319
353
|
this.#observer.observe(this.element, {
|
|
@@ -332,10 +366,7 @@ var MenubarController = class extends Controller {
|
|
|
332
366
|
this.#reporting = false;
|
|
333
367
|
this.#escapeLayer.deactivate();
|
|
334
368
|
this.#layerActive = false;
|
|
335
|
-
this.
|
|
336
|
-
this.element.removeEventListener("focusin", this.#onFocusIn);
|
|
337
|
-
this.element.removeEventListener("focusout", this.#onFocusOut);
|
|
338
|
-
document.removeEventListener("click", this.#onOutsideClick, true);
|
|
369
|
+
this.#listeners.dispose();
|
|
339
370
|
this.#focused = null;
|
|
340
371
|
this.#observer?.disconnect();
|
|
341
372
|
this.#observer = null;
|
|
@@ -368,16 +399,18 @@ var MenubarController = class extends Controller {
|
|
|
368
399
|
if (!top || !this.topTargets.includes(top)) return;
|
|
369
400
|
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
370
401
|
return;
|
|
402
|
+
if (this.#refuses(top, event, origin)) return;
|
|
371
403
|
const moveFocus = event !== null || this.element.contains(document.activeElement);
|
|
372
404
|
if (this.#isExpanded(top)) {
|
|
373
405
|
this.#closeMenu(top, reason);
|
|
374
406
|
} else {
|
|
375
|
-
this.#openMenu(top, "first", reason, moveFocus);
|
|
407
|
+
this.#openMenu(top, "first", reason, moveFocus, true);
|
|
376
408
|
}
|
|
377
409
|
}
|
|
378
410
|
onTopKeydown(event) {
|
|
379
411
|
if (event.defaultPrevented) return;
|
|
380
412
|
if (isReservedArrowChord(event)) return;
|
|
413
|
+
if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
|
|
381
414
|
const tops = this.#navigableTops;
|
|
382
415
|
const index = tops.indexOf(event.currentTarget);
|
|
383
416
|
if (index === -1) return;
|
|
@@ -417,6 +450,7 @@ var MenubarController = class extends Controller {
|
|
|
417
450
|
onItemKeydown(event) {
|
|
418
451
|
if (event.defaultPrevented) return;
|
|
419
452
|
if (isReservedArrowChord(event)) return;
|
|
453
|
+
if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
|
|
420
454
|
const item = event.currentTarget;
|
|
421
455
|
const menu = item.closest("[role='menu']");
|
|
422
456
|
if (!menu) return;
|
|
@@ -465,6 +499,7 @@ var MenubarController = class extends Controller {
|
|
|
465
499
|
if (!item || !this.itemTargets.includes(item)) return;
|
|
466
500
|
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
467
501
|
return;
|
|
502
|
+
if (this.#refuses(item, event, origin)) return;
|
|
468
503
|
const moveFocus = event !== null || this.element.contains(document.activeElement);
|
|
469
504
|
const menu = item.closest("[role='menu']");
|
|
470
505
|
const top = this.#topFor(menu);
|
|
@@ -482,10 +517,10 @@ var MenubarController = class extends Controller {
|
|
|
482
517
|
this.#roving.setActive(this.topTargets.indexOf(destination), { focus: true });
|
|
483
518
|
}
|
|
484
519
|
}
|
|
485
|
-
#openMenu(opener, focus, reason, moveFocus = true) {
|
|
520
|
+
#openMenu(opener, focus, reason, moveFocus = true, judged = false) {
|
|
486
521
|
if (!opener) return;
|
|
487
522
|
const menu = this.#menuFor(opener);
|
|
488
|
-
if (!menu ||
|
|
523
|
+
if (!menu || !judged && isAriaDisabled(opener)) {
|
|
489
524
|
if (menu || !opener.hasAttribute("aria-controls")) {
|
|
490
525
|
this.#closeAllMenus(reason);
|
|
491
526
|
this.#focusTop(
|
|
@@ -562,13 +597,14 @@ var MenubarController = class extends Controller {
|
|
|
562
597
|
this.#closeAllMenus("outside");
|
|
563
598
|
}
|
|
564
599
|
};
|
|
565
|
-
#
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
600
|
+
#onClickCapture = (event) => {
|
|
601
|
+
const entries = ownersOf(this.#entries, event.target);
|
|
602
|
+
this.#clickVerdicts.record(
|
|
603
|
+
event,
|
|
604
|
+
entries,
|
|
605
|
+
entries.some((entry) => this.#isActivationDisabled(entry))
|
|
570
606
|
);
|
|
571
|
-
if (!
|
|
607
|
+
if (!entries.some((entry) => isAriaDisabled(entry))) return;
|
|
572
608
|
event.preventDefault();
|
|
573
609
|
event.stopImmediatePropagation();
|
|
574
610
|
};
|
|
@@ -637,6 +673,9 @@ var MenubarController = class extends Controller {
|
|
|
637
673
|
#itemsIn(menu) {
|
|
638
674
|
return this.itemTargets.filter((item) => menu.contains(item) && this.#isNavigable(item));
|
|
639
675
|
}
|
|
676
|
+
get #entries() {
|
|
677
|
+
return [...this.topTargets, ...this.itemTargets];
|
|
678
|
+
}
|
|
640
679
|
get #navigableTops() {
|
|
641
680
|
return this.topTargets.filter((top) => this.#isNavigable(top));
|
|
642
681
|
}
|
|
@@ -644,8 +683,15 @@ var MenubarController = class extends Controller {
|
|
|
644
683
|
if (element.hasAttribute("hidden")) return false;
|
|
645
684
|
return !element.disabled;
|
|
646
685
|
}
|
|
647
|
-
#
|
|
648
|
-
return element
|
|
686
|
+
#isActivationDisabled(element) {
|
|
687
|
+
return !this.#isNavigable(element) || isAriaDisabled(element);
|
|
688
|
+
}
|
|
689
|
+
#refuses(entry, event, origin) {
|
|
690
|
+
const recorded = this.#clickVerdicts.for(event, entry);
|
|
691
|
+
if (recorded !== void 0) return recorded;
|
|
692
|
+
if (this.#isActivationDisabled(entry)) return true;
|
|
693
|
+
const target = event ? event.target : origin;
|
|
694
|
+
return ownersOf(this.#entries, target).some((owner) => this.#isActivationDisabled(owner));
|
|
649
695
|
}
|
|
650
696
|
#focusAt(items, index) {
|
|
651
697
|
items[index]?.focus();
|