stimeo-ui 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +212 -0
- data/README.md +120 -0
- data/dist/cable/index.js +123 -29
- data/dist/controllers/accordion_controller.js +98 -9
- data/dist/controllers/announcer_controller.js +96 -62
- data/dist/controllers/auto_submit_controller.js +83 -7
- data/dist/controllers/avatar_controller.js +1 -1
- data/dist/controllers/breadcrumb_controller.js +38 -11
- data/dist/controllers/bulk_select_controller.js +7 -6
- data/dist/controllers/calendar_controller.js +340 -123
- data/dist/controllers/carousel_controller.js +263 -38
- data/dist/controllers/character_counter_controller.js +40 -2
- data/dist/controllers/checkbox_controller.js +81 -12
- data/dist/controllers/clipboard_controller.js +63 -5
- data/dist/controllers/collapsible_controller.js +99 -14
- data/dist/controllers/color_picker_controller.js +80 -34
- data/dist/controllers/combobox_controller.js +106 -16
- data/dist/controllers/command_palette_controller.js +35 -3
- data/dist/controllers/conditional_fields_controller.js +85 -17
- data/dist/controllers/confirm_controller.js +3 -0
- data/dist/controllers/context_menu_controller.js +32 -12
- data/dist/controllers/countdown_controller.js +129 -26
- data/dist/controllers/currency_input_controller.js +221 -67
- data/dist/controllers/data_grid_controller.js +195 -29
- data/dist/controllers/date_range_picker_controller.js +151 -30
- data/dist/controllers/dialog_controller.js +35 -8
- data/dist/controllers/direct_upload_controller.js +22 -4
- data/dist/controllers/dirty_form_controller.js +46 -13
- data/dist/controllers/dismissible_controller.js +1 -0
- data/dist/controllers/drawer_controller.js +54 -19
- data/dist/controllers/dropdown_controller.js +36 -9
- data/dist/controllers/editable_controller.js +34 -0
- data/dist/controllers/file_dropzone_controller.js +144 -51
- data/dist/controllers/filter_controller.js +20 -6
- data/dist/controllers/flash_controller.js +432 -71
- data/dist/controllers/focus_controller.js +1 -0
- data/dist/controllers/form_field_controller.js +7 -5
- data/dist/controllers/form_validation_controller.js +19 -13
- data/dist/controllers/frame_loading_controller.js +45 -8
- data/dist/controllers/highlight_controller.js +82 -25
- data/dist/controllers/hover_card_controller.js +40 -14
- data/dist/controllers/idle_controller.js +90 -5
- data/dist/controllers/input_mask_controller.js +65 -9
- data/dist/controllers/intersection_controller.js +3 -0
- data/dist/controllers/lazy_frame_controller.js +11 -2
- data/dist/controllers/listbox_controller.js +203 -45
- data/dist/controllers/local_time_controller.js +10 -5
- data/dist/controllers/masonry_controller.js +31 -15
- data/dist/controllers/menu_controller.js +45 -16
- data/dist/controllers/menubar_controller.js +58 -24
- data/dist/controllers/meter_controller.js +9 -5
- data/dist/controllers/multi_select_controller.js +278 -104
- data/dist/controllers/navigation_menu_controller.js +48 -15
- data/dist/controllers/nested_form_controller.js +37 -8
- data/dist/controllers/network_status_controller.js +9 -1
- data/dist/controllers/number_input_controller.js +124 -21
- data/dist/controllers/optimistic_controller.js +42 -5
- data/dist/controllers/otp_controller.js +198 -55
- data/dist/controllers/overflow_indicator_controller.js +115 -21
- data/dist/controllers/overflow_menu_controller.js +141 -44
- data/dist/controllers/pagination_controller.js +74 -28
- data/dist/controllers/password_reveal_controller.js +59 -2
- data/dist/controllers/persist_controller.js +30 -8
- data/dist/controllers/pointer_drag_controller.js +131 -52
- data/dist/controllers/popover_controller.js +45 -11
- data/dist/controllers/portal_controller.js +6 -2
- data/dist/controllers/preview_guard_controller.js +16 -1
- data/dist/controllers/progress_controller.js +8 -4
- data/dist/controllers/radio_group_controller.js +42 -17
- data/dist/controllers/range_slider_controller.js +88 -42
- data/dist/controllers/rating_controller.js +39 -15
- data/dist/controllers/read_more_controller.js +100 -7
- data/dist/controllers/reading_progress_controller.js +65 -19
- data/dist/controllers/relative_time_controller.js +10 -5
- data/dist/controllers/resizable_controller.js +82 -22
- data/dist/controllers/scroll_area_controller.js +75 -27
- data/dist/controllers/scroll_restore_controller.js +37 -16
- data/dist/controllers/scroll_visibility_controller.js +49 -30
- data/dist/controllers/scrollspy_controller.js +71 -26
- data/dist/controllers/separator_controller.js +66 -37
- data/dist/controllers/sidebar_controller.js +77 -18
- data/dist/controllers/skeleton_controller.js +6 -1
- data/dist/controllers/slider_controller.js +82 -47
- data/dist/controllers/smart_sticky_header_controller.js +60 -26
- data/dist/controllers/sortable_controller.js +17 -2
- data/dist/controllers/spinner_controller.js +10 -2
- data/dist/controllers/step_indicator_controller.js +18 -17
- data/dist/controllers/stepper_controller.js +101 -19
- data/dist/controllers/stick_to_bottom_controller.js +104 -8
- data/dist/controllers/submit_once_controller.js +45 -9
- data/dist/controllers/switch_controller.js +101 -10
- data/dist/controllers/tabs_controller.js +21 -2
- data/dist/controllers/tags_input_controller.js +209 -59
- data/dist/controllers/textarea_autosize_controller.js +29 -3
- data/dist/controllers/theme_controller.js +64 -14
- data/dist/controllers/time_picker_controller.js +23 -8
- data/dist/controllers/toast_controller.js +451 -105
- data/dist/controllers/toggle_group_controller.js +159 -23
- data/dist/controllers/toolbar_controller.js +32 -0
- data/dist/controllers/tooltip_controller.js +39 -13
- data/dist/controllers/transition_controller.js +4 -0
- data/dist/controllers/tree_view_controller.js +169 -16
- data/dist/index.js +5002 -1911
- data/dist/positioning/index.js +2 -0
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +2 -2
|
@@ -160,6 +160,16 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
160
160
|
}
|
|
161
161
|
};
|
|
162
162
|
|
|
163
|
+
// src/utils/state_reason.ts
|
|
164
|
+
var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
|
|
165
|
+
var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
|
|
166
|
+
function stateReasonFor(event) {
|
|
167
|
+
if (!event) return "api";
|
|
168
|
+
if (FOCUS_EVENTS.has(event.type)) return "focus";
|
|
169
|
+
if (POINTER_EVENTS.has(event.type)) return "pointer";
|
|
170
|
+
return "user";
|
|
171
|
+
}
|
|
172
|
+
|
|
163
173
|
// src/controllers/menu_controller.ts
|
|
164
174
|
var MenuController = class extends Controller {
|
|
165
175
|
static targets = ["trigger", "menu", "item"];
|
|
@@ -171,6 +181,7 @@ var MenuController = class extends Controller {
|
|
|
171
181
|
"open",
|
|
172
182
|
"toggle"
|
|
173
183
|
];
|
|
184
|
+
static events = ["close", "open"];
|
|
174
185
|
#timers = new SafeTimeout();
|
|
175
186
|
/** Escape-stack membership while open; the shared resolver dismisses via it. */
|
|
176
187
|
#escapeLayer = new EscapeLayer();
|
|
@@ -182,16 +193,20 @@ var MenuController = class extends Controller {
|
|
|
182
193
|
#clickOwner = null;
|
|
183
194
|
/** Clicks already turned into an activation, so the two paths run it once. */
|
|
184
195
|
#activated = /* @__PURE__ */ new WeakSet();
|
|
196
|
+
/** Whether state moves are reported: set once `connect()` settled the baseline. */
|
|
197
|
+
#reporting = false;
|
|
185
198
|
/** Starts closed and registers activation / outside-click listeners. */
|
|
186
199
|
connect() {
|
|
187
|
-
this
|
|
200
|
+
this.#close("api");
|
|
188
201
|
this.element.addEventListener("click", this.#onItemClickCapture, true);
|
|
189
202
|
this.element.addEventListener("click", this.#onDelegatedItemClick);
|
|
190
203
|
this.element.addEventListener("keydown", this.#onDelegatedItemKeydown);
|
|
191
204
|
document.addEventListener("click", this.#onOutsideClick, true);
|
|
205
|
+
this.#reporting = true;
|
|
192
206
|
}
|
|
193
207
|
/** Releases the listeners, stack membership, and any pending Tab-close task. */
|
|
194
208
|
disconnect() {
|
|
209
|
+
this.#reporting = false;
|
|
195
210
|
this.#timers.clearAll();
|
|
196
211
|
this.#escapeLayer.deactivate();
|
|
197
212
|
this.element.removeEventListener("click", this.#onItemClickCapture, true);
|
|
@@ -200,32 +215,44 @@ var MenuController = class extends Controller {
|
|
|
200
215
|
document.removeEventListener("click", this.#onOutsideClick, true);
|
|
201
216
|
}
|
|
202
217
|
/** Toggles the menu open/closed. Bound via `data-action` (click). */
|
|
203
|
-
toggle() {
|
|
218
|
+
toggle(event) {
|
|
204
219
|
if (this.#isOpen) {
|
|
205
|
-
this.close();
|
|
220
|
+
this.close(event);
|
|
206
221
|
} else {
|
|
207
|
-
this.open();
|
|
222
|
+
this.open(event);
|
|
208
223
|
this.#focusFirst();
|
|
209
224
|
}
|
|
210
225
|
}
|
|
211
226
|
/** Opens the menu and reflects the expanded state on the trigger. */
|
|
212
|
-
open() {
|
|
227
|
+
open(event) {
|
|
228
|
+
this.#open(stateReasonFor(event));
|
|
229
|
+
}
|
|
230
|
+
/** Closes the menu and reflects the collapsed state on the trigger. */
|
|
231
|
+
close(event) {
|
|
232
|
+
this.#close(stateReasonFor(event));
|
|
233
|
+
}
|
|
234
|
+
/** Opens the menu, reflects the expanded state, and reports a move. */
|
|
235
|
+
#open(reason) {
|
|
213
236
|
this.#timers.clearAll();
|
|
214
237
|
if (!this.hasMenuTarget) return;
|
|
238
|
+
const was = this.#isOpen;
|
|
215
239
|
this.#escapeLayer.activate(document, {
|
|
216
|
-
onDismiss: () => this.#closeAndRestore(),
|
|
240
|
+
onDismiss: () => this.#closeAndRestore("escape"),
|
|
217
241
|
claims: claimsWhileFocusWithin(this.element)
|
|
218
242
|
});
|
|
219
243
|
this.menuTarget.hidden = false;
|
|
220
244
|
if (this.hasTriggerTarget) this.triggerTarget.setAttribute("aria-expanded", "true");
|
|
245
|
+
if (!was && this.#reporting) this.dispatch("open", { detail: { reason }, cancelable: false });
|
|
221
246
|
}
|
|
222
|
-
/** Closes the menu
|
|
223
|
-
close() {
|
|
247
|
+
/** Closes the menu, reflects the collapsed state, and reports a move. */
|
|
248
|
+
#close(reason) {
|
|
224
249
|
this.#timers.clearAll();
|
|
225
250
|
this.#escapeLayer.deactivate();
|
|
226
251
|
if (!this.hasMenuTarget) return;
|
|
252
|
+
const was = this.#isOpen;
|
|
227
253
|
this.menuTarget.hidden = true;
|
|
228
254
|
if (this.hasTriggerTarget) this.triggerTarget.setAttribute("aria-expanded", "false");
|
|
255
|
+
if (was && this.#reporting) this.dispatch("close", { detail: { reason }, cancelable: false });
|
|
229
256
|
}
|
|
230
257
|
/**
|
|
231
258
|
* Opens the menu with the keyboard per the APG (Down → first, Up → last).
|
|
@@ -240,11 +267,11 @@ var MenuController = class extends Controller {
|
|
|
240
267
|
if (isReservedArrowChord(event)) return;
|
|
241
268
|
if (event.key === "ArrowDown") {
|
|
242
269
|
event.preventDefault();
|
|
243
|
-
this.open();
|
|
270
|
+
this.open(event);
|
|
244
271
|
this.#focusFirst();
|
|
245
272
|
} else if (event.key === "ArrowUp") {
|
|
246
273
|
event.preventDefault();
|
|
247
|
-
this.open();
|
|
274
|
+
this.open(event);
|
|
248
275
|
this.#focusLast();
|
|
249
276
|
}
|
|
250
277
|
}
|
|
@@ -281,7 +308,7 @@ var MenuController = class extends Controller {
|
|
|
281
308
|
break;
|
|
282
309
|
case "Tab":
|
|
283
310
|
this.#timers.clearAll();
|
|
284
|
-
this.#timers.set(() => this
|
|
311
|
+
this.#timers.set(() => this.#close("focus"), 0);
|
|
285
312
|
break;
|
|
286
313
|
}
|
|
287
314
|
}
|
|
@@ -302,21 +329,21 @@ var MenuController = class extends Controller {
|
|
|
302
329
|
if (this.#activated.has(event)) return;
|
|
303
330
|
this.#activated.add(event);
|
|
304
331
|
}
|
|
305
|
-
this.#closeAndRestore();
|
|
332
|
+
this.#closeAndRestore("select");
|
|
306
333
|
}
|
|
307
334
|
/** Closes and returns focus to the trigger (Escape / item-activation path). */
|
|
308
|
-
#closeAndRestore() {
|
|
309
|
-
this
|
|
335
|
+
#closeAndRestore(reason) {
|
|
336
|
+
this.#close(reason);
|
|
310
337
|
if (this.hasTriggerTarget) this.triggerTarget.focus();
|
|
311
338
|
}
|
|
312
339
|
/** Closes the menu when a click lands outside the controller's element. */
|
|
313
340
|
#onOutsideClick = (event) => {
|
|
314
|
-
if (this.#isOpen && !this.element.contains(event.target)) this
|
|
341
|
+
if (this.#isOpen && !this.element.contains(event.target)) this.#close("outside");
|
|
315
342
|
};
|
|
316
343
|
/**
|
|
317
344
|
* Delegated twin of {@link onItemKeydown}, bound on the controller element.
|
|
318
345
|
*
|
|
319
|
-
* An item that arrives *after* connect —
|
|
346
|
+
* An item that arrives *after* connect — `stimeo--overflow-menu` moves toolbar controls
|
|
320
347
|
* into this menu at runtime — carries whatever `data-action` its author wrote,
|
|
321
348
|
* and that is exactly the binding a consumer forgets, because the markup that
|
|
322
349
|
* declares the item lives nowhere near the menu. Listening on the container
|
|
@@ -371,10 +398,12 @@ var MenuController = class extends Controller {
|
|
|
371
398
|
};
|
|
372
399
|
/** Moves focus to the first navigable item (no-op if none). */
|
|
373
400
|
#focusFirst() {
|
|
401
|
+
if (!this.#isOpen) return;
|
|
374
402
|
this.#navigableItems[0]?.focus();
|
|
375
403
|
}
|
|
376
404
|
/** Moves focus to the last navigable item (no-op if none). */
|
|
377
405
|
#focusLast() {
|
|
406
|
+
if (!this.#isOpen) return;
|
|
378
407
|
const items = this.#navigableItems;
|
|
379
408
|
items[items.length - 1]?.focus();
|
|
380
409
|
}
|
|
@@ -200,6 +200,16 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
200
200
|
}
|
|
201
201
|
};
|
|
202
202
|
|
|
203
|
+
// src/utils/state_reason.ts
|
|
204
|
+
var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
|
|
205
|
+
var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
|
|
206
|
+
function stateReasonFor(event) {
|
|
207
|
+
if (!event) return "api";
|
|
208
|
+
if (FOCUS_EVENTS.has(event.type)) return "focus";
|
|
209
|
+
if (POINTER_EVENTS.has(event.type)) return "pointer";
|
|
210
|
+
return "user";
|
|
211
|
+
}
|
|
212
|
+
|
|
203
213
|
// src/utils/typeahead.ts
|
|
204
214
|
var TYPEAHEAD_RESET_MS = 500;
|
|
205
215
|
var Typeahead = class {
|
|
@@ -263,6 +273,7 @@ var STATE_ATTRIBUTES = ["disabled", "hidden"];
|
|
|
263
273
|
var MenubarController = class extends Controller {
|
|
264
274
|
static targets = ["top", "menu", "item"];
|
|
265
275
|
static actions = ["activate", "onItemKeydown", "onTopKeydown", "toggle"];
|
|
276
|
+
static events = ["close", "open"];
|
|
266
277
|
/** Roving tabindex across the top-level menuitems (one Tab stop). */
|
|
267
278
|
#roving = new RovingTabindex(() => this.topTargets);
|
|
268
279
|
/** Typeahead query and its idle-reset timer (scoped to the open menu). */
|
|
@@ -281,6 +292,8 @@ var MenubarController = class extends Controller {
|
|
|
281
292
|
* stop on mount and from resurrecting one after teardown.
|
|
282
293
|
*/
|
|
283
294
|
#connected = false;
|
|
295
|
+
/** Whether state moves are reported: set once `connect()` settled the baseline. */
|
|
296
|
+
#reporting = false;
|
|
284
297
|
/** The element inside the menubar that last took DOM focus, if any. */
|
|
285
298
|
#focused = null;
|
|
286
299
|
/**
|
|
@@ -314,7 +327,7 @@ var MenubarController = class extends Controller {
|
|
|
314
327
|
* close the menu.
|
|
315
328
|
*/
|
|
316
329
|
connect() {
|
|
317
|
-
this.#closeAllMenus();
|
|
330
|
+
this.#closeAllMenus("api");
|
|
318
331
|
this.#reconcile();
|
|
319
332
|
this.element.addEventListener("click", this.#onDisabledClickCapture, true);
|
|
320
333
|
this.element.addEventListener("focusin", this.#onFocusIn);
|
|
@@ -330,10 +343,12 @@ var MenubarController = class extends Controller {
|
|
|
330
343
|
});
|
|
331
344
|
}
|
|
332
345
|
this.#connected = true;
|
|
346
|
+
this.#reporting = true;
|
|
333
347
|
}
|
|
334
348
|
/** Removes the listeners, stack membership, and any pending timer (typeahead / Tab close). */
|
|
335
349
|
disconnect() {
|
|
336
350
|
this.#connected = false;
|
|
351
|
+
this.#reporting = false;
|
|
337
352
|
this.#escapeLayer.deactivate();
|
|
338
353
|
this.#layerActive = false;
|
|
339
354
|
this.element.removeEventListener("click", this.#onDisabledClickCapture, true);
|
|
@@ -395,10 +410,11 @@ var MenubarController = class extends Controller {
|
|
|
395
410
|
/** Toggles a top item's menu. Bound via `data-action` (click on the top item). */
|
|
396
411
|
toggle(event) {
|
|
397
412
|
const top = event.currentTarget;
|
|
413
|
+
const reason = stateReasonFor(event);
|
|
398
414
|
if (this.#isExpanded(top)) {
|
|
399
|
-
this.#closeMenu(top);
|
|
415
|
+
this.#closeMenu(top, reason);
|
|
400
416
|
} else {
|
|
401
|
-
this.#openMenu(top, "first");
|
|
417
|
+
this.#openMenu(top, "first", reason);
|
|
402
418
|
}
|
|
403
419
|
}
|
|
404
420
|
/** Keyboard handling while focus is on a top item. */
|
|
@@ -414,27 +430,27 @@ var MenubarController = class extends Controller {
|
|
|
414
430
|
switch (event.key) {
|
|
415
431
|
case "ArrowRight":
|
|
416
432
|
event.preventDefault();
|
|
417
|
-
this.#gotoTop(tops[(index + step + length) % length], anyOpen);
|
|
433
|
+
this.#gotoTop(tops[(index + step + length) % length], anyOpen, "user");
|
|
418
434
|
break;
|
|
419
435
|
case "ArrowLeft":
|
|
420
436
|
event.preventDefault();
|
|
421
|
-
this.#gotoTop(tops[(index - step + length) % length], anyOpen);
|
|
437
|
+
this.#gotoTop(tops[(index - step + length) % length], anyOpen, "user");
|
|
422
438
|
break;
|
|
423
439
|
case "ArrowDown":
|
|
424
440
|
event.preventDefault();
|
|
425
|
-
this.#openMenu(tops[index], "first");
|
|
441
|
+
this.#openMenu(tops[index], "first", "user");
|
|
426
442
|
break;
|
|
427
443
|
case "ArrowUp":
|
|
428
444
|
event.preventDefault();
|
|
429
|
-
this.#openMenu(tops[index], "last");
|
|
445
|
+
this.#openMenu(tops[index], "last", "user");
|
|
430
446
|
break;
|
|
431
447
|
case "Home":
|
|
432
448
|
event.preventDefault();
|
|
433
|
-
this.#gotoTop(tops[0], anyOpen);
|
|
449
|
+
this.#gotoTop(tops[0], anyOpen, "user");
|
|
434
450
|
break;
|
|
435
451
|
case "End":
|
|
436
452
|
event.preventDefault();
|
|
437
|
-
this.#gotoTop(tops[length - 1], anyOpen);
|
|
453
|
+
this.#gotoTop(tops[length - 1], anyOpen, "user");
|
|
438
454
|
break;
|
|
439
455
|
case "Tab":
|
|
440
456
|
this.#closeMenusSoon();
|
|
@@ -492,14 +508,14 @@ var MenubarController = class extends Controller {
|
|
|
492
508
|
const item = event.currentTarget;
|
|
493
509
|
const menu = item.closest("[role='menu']");
|
|
494
510
|
const top = this.#topFor(menu);
|
|
495
|
-
this.#closeAllMenus();
|
|
511
|
+
this.#closeAllMenus("select");
|
|
496
512
|
this.#focusTop(top);
|
|
497
513
|
}
|
|
498
514
|
/** Moves the roving focus to `top`, opening its menu when one was open. */
|
|
499
|
-
#gotoTop(top, reopen) {
|
|
515
|
+
#gotoTop(top, reopen, reason) {
|
|
500
516
|
if (!top) return;
|
|
501
517
|
if (reopen) {
|
|
502
|
-
this.#openMenu(top, "first");
|
|
518
|
+
this.#openMenu(top, "first", reason);
|
|
503
519
|
} else {
|
|
504
520
|
this.#roving.setActive(this.topTargets.indexOf(top), { focus: true });
|
|
505
521
|
}
|
|
@@ -526,20 +542,23 @@ var MenubarController = class extends Controller {
|
|
|
526
542
|
* this "open a menu" path. A dangling top is still an ordinary roving
|
|
527
543
|
* destination while nothing is open.
|
|
528
544
|
*/
|
|
529
|
-
#openMenu(top, focus) {
|
|
545
|
+
#openMenu(top, focus, reason) {
|
|
530
546
|
if (!top) return;
|
|
531
547
|
const menu = this.#menuFor(top);
|
|
532
548
|
if (!menu || this.#isActivationBlocked(top)) {
|
|
533
549
|
if (menu || !top.hasAttribute("aria-controls")) {
|
|
534
|
-
this.#closeAllMenus();
|
|
550
|
+
this.#closeAllMenus(reason);
|
|
535
551
|
this.#focusTop(top);
|
|
536
552
|
}
|
|
537
553
|
return;
|
|
538
554
|
}
|
|
539
555
|
this.#timers.clearAll();
|
|
540
|
-
this.#
|
|
556
|
+
const was = this.#isExpanded(top);
|
|
557
|
+
this.#closeAllMenus(reason, top);
|
|
541
558
|
menu.hidden = false;
|
|
542
559
|
top.setAttribute("aria-expanded", "true");
|
|
560
|
+
if (!was) this.#report("open", top, menu, reason);
|
|
561
|
+
if (!this.#isExpanded(top)) return;
|
|
543
562
|
this.#escapeLayer.activate(document, {
|
|
544
563
|
onDismiss: () => this.#dismissOpenMenu(),
|
|
545
564
|
claims: claimsWhileFocusWithin(this.element)
|
|
@@ -554,19 +573,32 @@ var MenubarController = class extends Controller {
|
|
|
554
573
|
* no menu (a plain command in an otherwise popup-bearing menubar) is left alone
|
|
555
574
|
* — stamping `aria-expanded="false"` on it would announce a popup it lacks.
|
|
556
575
|
*/
|
|
557
|
-
#closeMenu(top) {
|
|
576
|
+
#closeMenu(top, reason, silent = false) {
|
|
558
577
|
if (!top) return;
|
|
559
578
|
const menu = this.#menuFor(top);
|
|
560
579
|
if (!menu) return;
|
|
580
|
+
const was = this.#isExpanded(top);
|
|
561
581
|
menu.hidden = true;
|
|
562
582
|
top.setAttribute("aria-expanded", "false");
|
|
583
|
+
if (was && !silent) this.#report("close", top, menu, reason);
|
|
563
584
|
this.#syncEscapeLayer();
|
|
564
585
|
}
|
|
565
|
-
/**
|
|
566
|
-
|
|
567
|
-
|
|
586
|
+
/**
|
|
587
|
+
* Closes every menu and resets the typeahead query. `except` still has its
|
|
588
|
+
* attributes rewritten — the caller is about to reopen it — but its move is
|
|
589
|
+
* left for that caller to report.
|
|
590
|
+
*/
|
|
591
|
+
#closeAllMenus(reason, except = null) {
|
|
592
|
+
for (const top of this.topTargets) this.#closeMenu(top, reason, top === except);
|
|
568
593
|
this.#typeahead.reset();
|
|
569
594
|
}
|
|
595
|
+
/** Names the menu that moved, so a subscriber does not have to re-derive it. */
|
|
596
|
+
#report(name, top, menu, reason) {
|
|
597
|
+
if (!this.#reporting) return;
|
|
598
|
+
const detail = { reason, index: this.topTargets.indexOf(top), menu };
|
|
599
|
+
if (name === "open") this.dispatch("open", { detail, cancelable: false });
|
|
600
|
+
else this.dispatch("close", { detail, cancelable: false });
|
|
601
|
+
}
|
|
570
602
|
/**
|
|
571
603
|
* Closes every menu on the next task instead of synchronously.
|
|
572
604
|
*
|
|
@@ -577,7 +609,7 @@ var MenubarController = class extends Controller {
|
|
|
577
609
|
#closeMenusSoon() {
|
|
578
610
|
if (!this.#isAnyOpen) return;
|
|
579
611
|
this.#timers.clearAll();
|
|
580
|
-
this.#timers.set(() => this.#closeAllMenus(), 0);
|
|
612
|
+
this.#timers.set(() => this.#closeAllMenus("focus"), 0);
|
|
581
613
|
}
|
|
582
614
|
/**
|
|
583
615
|
* Escape path, invoked by the shared resolver: pressed inside an open menu it
|
|
@@ -589,11 +621,11 @@ var MenubarController = class extends Controller {
|
|
|
589
621
|
const menu = ownerOf(this.menuTargets, document.activeElement);
|
|
590
622
|
if (menu) {
|
|
591
623
|
const top = this.#topFor(menu);
|
|
592
|
-
this.#closeMenu(top);
|
|
624
|
+
this.#closeMenu(top, "escape");
|
|
593
625
|
this.#focusTop(top);
|
|
594
626
|
return;
|
|
595
627
|
}
|
|
596
|
-
this.#closeAllMenus();
|
|
628
|
+
this.#closeAllMenus("escape");
|
|
597
629
|
}
|
|
598
630
|
/** Opens the menu of the navigable top item `delta` steps from `menu`'s owner. */
|
|
599
631
|
#moveToAdjacentMenu(menu, delta) {
|
|
@@ -603,7 +635,7 @@ var MenubarController = class extends Controller {
|
|
|
603
635
|
const current = tops.indexOf(top);
|
|
604
636
|
if (current === -1) return;
|
|
605
637
|
const next = (current + delta + tops.length) % tops.length;
|
|
606
|
-
this.#openMenu(tops[next], "first");
|
|
638
|
+
this.#openMenu(tops[next], "first", "user");
|
|
607
639
|
}
|
|
608
640
|
/**
|
|
609
641
|
* Closes when a click lands outside the controller's element. Focus is left on
|
|
@@ -611,7 +643,9 @@ var MenubarController = class extends Controller {
|
|
|
611
643
|
* docs.
|
|
612
644
|
*/
|
|
613
645
|
#onOutsideClick = (event) => {
|
|
614
|
-
if (this.#isAnyOpen && !this.element.contains(event.target))
|
|
646
|
+
if (this.#isAnyOpen && !this.element.contains(event.target)) {
|
|
647
|
+
this.#closeAllMenus("outside");
|
|
648
|
+
}
|
|
615
649
|
};
|
|
616
650
|
/**
|
|
617
651
|
* Captures clicks so `aria-disabled` top items and commands cannot reach
|
|
@@ -76,8 +76,12 @@ function rangeFraction(value, min, max) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// src/controllers/meter_controller.ts
|
|
79
|
-
var OWNED_VALUE_TEXT = "
|
|
79
|
+
var OWNED_VALUE_TEXT = "owns-valuetext";
|
|
80
80
|
var MeterController = class extends Controller {
|
|
81
|
+
/** The marker above, in the namespace this controller is registered under. */
|
|
82
|
+
get #ownedValueText() {
|
|
83
|
+
return `data-${this.identifier}-${OWNED_VALUE_TEXT}`;
|
|
84
|
+
}
|
|
81
85
|
static targets = ["bar"];
|
|
82
86
|
static values = {
|
|
83
87
|
announceText: { type: String, default: "" },
|
|
@@ -157,7 +161,7 @@ var MeterController = class extends Controller {
|
|
|
157
161
|
}
|
|
158
162
|
/** Whether a threshold attribute is present (absent = no threshold). */
|
|
159
163
|
#hasThreshold(name) {
|
|
160
|
-
return this.element.hasAttribute(`data
|
|
164
|
+
return this.element.hasAttribute(`data-${this.identifier}-${name}-value`);
|
|
161
165
|
}
|
|
162
166
|
/**
|
|
163
167
|
* Classifies `value` into a `low`/`medium`/`high` segment. Values at or below
|
|
@@ -199,16 +203,16 @@ var MeterController = class extends Controller {
|
|
|
199
203
|
*/
|
|
200
204
|
#applyValueText({ value, ratio, state }) {
|
|
201
205
|
if (this.valueTextValue.length === 0) {
|
|
202
|
-
if (this.element.hasAttribute(
|
|
206
|
+
if (this.element.hasAttribute(this.#ownedValueText)) {
|
|
203
207
|
this.element.removeAttribute("aria-valuetext");
|
|
204
|
-
this.element.removeAttribute(
|
|
208
|
+
this.element.removeAttribute(this.#ownedValueText);
|
|
205
209
|
}
|
|
206
210
|
return;
|
|
207
211
|
}
|
|
208
212
|
const percent = Math.round(ratio * 100);
|
|
209
213
|
const text = this.valueTextValue.replaceAll("{value}", String(value)).replaceAll("{percent}", String(percent)).replaceAll("{state}", state);
|
|
210
214
|
this.element.setAttribute("aria-valuetext", text);
|
|
211
|
-
this.element.setAttribute(
|
|
215
|
+
this.element.setAttribute(this.#ownedValueText, "");
|
|
212
216
|
}
|
|
213
217
|
};
|
|
214
218
|
|