stimeo-ui 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -0
- package/README.md +27 -11
- package/dist/cable/index.d.ts +88 -31
- package/dist/cable/index.js +384 -449
- package/dist/cable/index.js.map +1 -1
- package/dist/coerce-JtZVNG5h.d.ts +20 -0
- package/dist/controllers/accordion_controller.d.ts +4 -2
- package/dist/controllers/accordion_controller.js +174 -55
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +12 -2
- package/dist/controllers/alert_dialog_controller.js +903 -235
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +37 -5
- package/dist/controllers/announcer_controller.js +238 -205
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.js +0 -10
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +30 -6
- package/dist/controllers/auto_submit_controller.js +414 -145
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +185 -107
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.js +0 -130
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +21 -3
- package/dist/controllers/bulk_select_controller.js +282 -58
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +38 -16
- package/dist/controllers/calendar_controller.js +228 -260
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +43 -12
- package/dist/controllers/carousel_controller.js +536 -436
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +44 -7
- package/dist/controllers/character_counter_controller.js +493 -207
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +7 -2
- package/dist/controllers/checkbox_controller.js +38 -74
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +16 -5
- package/dist/controllers/clipboard_controller.js +261 -149
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +17 -5
- package/dist/controllers/collapsible_controller.js +304 -120
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +18 -6
- package/dist/controllers/color_picker_controller.js +178 -146
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +24 -6
- package/dist/controllers/combobox_controller.js +522 -156
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +51 -20
- package/dist/controllers/command_palette_controller.js +1156 -395
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +15 -2
- package/dist/controllers/conditional_fields_controller.js +300 -171
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +49 -12
- package/dist/controllers/confirm_controller.js +980 -262
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +80 -18
- package/dist/controllers/context_menu_controller.js +346 -153
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +13 -1
- package/dist/controllers/count_up_controller.js +73 -21
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +47 -3
- package/dist/controllers/countdown_controller.js +281 -158
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +93 -10
- package/dist/controllers/currency_input_controller.js +575 -225
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +25 -12
- package/dist/controllers/data_grid_controller.js +248 -214
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +42 -14
- package/dist/controllers/date_range_picker_controller.js +338 -235
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +17 -6
- package/dist/controllers/dialog_controller.js +899 -237
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +25 -8
- package/dist/controllers/direct_upload_controller.js +97 -167
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +6 -1
- package/dist/controllers/dirty_form_controller.js +72 -69
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +19 -0
- package/dist/controllers/dismissible_controller.js +196 -24
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +39 -11
- package/dist/controllers/drawer_controller.js +974 -355
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +31 -1
- package/dist/controllers/dropdown_controller.js +339 -96
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +315 -88
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +79 -58
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +63 -18
- package/dist/controllers/file_dropzone_controller.js +322 -211
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.js +77 -42
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +35 -12
- package/dist/controllers/flash_controller.js +238 -347
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +15 -6
- package/dist/controllers/focus_controller.js +679 -240
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +156 -132
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.d.ts +3 -3
- package/dist/controllers/form_validation_controller.js +31 -97
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +47 -14
- package/dist/controllers/frame_loading_controller.js +275 -217
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +27 -6
- package/dist/controllers/highlight_controller.js +107 -75
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +60 -4
- package/dist/controllers/hover_card_controller.js +327 -130
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +48 -6
- package/dist/controllers/idle_controller.js +271 -101
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +30 -13
- package/dist/controllers/input_mask_controller.js +483 -97
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.d.ts +32 -13
- package/dist/controllers/intersection_controller.js +150 -106
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +19 -2
- package/dist/controllers/lazy_frame_controller.js +24 -68
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +44 -9
- package/dist/controllers/listbox_controller.js +523 -240
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +9 -7
- package/dist/controllers/local_time_controller.js +102 -77
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +11 -3
- package/dist/controllers/masonry_controller.js +80 -96
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +55 -11
- package/dist/controllers/menu_controller.js +302 -188
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +51 -9
- package/dist/controllers/menubar_controller.js +232 -345
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +33 -20
- package/dist/controllers/meter_controller.js +417 -87
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +48 -18
- package/dist/controllers/multi_select_controller.js +785 -393
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +27 -6
- package/dist/controllers/navigation_menu_controller.js +190 -250
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +10 -0
- package/dist/controllers/nested_form_controller.js +396 -192
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +23 -0
- package/dist/controllers/network_status_controller.js +268 -62
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +50 -8
- package/dist/controllers/number_input_controller.js +610 -303
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.d.ts +8 -5
- package/dist/controllers/optimistic_controller.js +45 -87
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +55 -8
- package/dist/controllers/otp_controller.js +658 -255
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
- package/dist/controllers/overflow_indicator_controller.js +122 -113
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +58 -20
- package/dist/controllers/overflow_menu_controller.js +470 -295
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +37 -7
- package/dist/controllers/pagination_controller.js +332 -123
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +46 -8
- package/dist/controllers/password_reveal_controller.js +392 -121
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +15 -6
- package/dist/controllers/password_strength_controller.js +111 -158
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +11 -4
- package/dist/controllers/persist_controller.js +165 -111
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +35 -12
- package/dist/controllers/pointer_drag_controller.js +434 -188
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +41 -5
- package/dist/controllers/popover_controller.js +269 -113
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +215 -76
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +10 -4
- package/dist/controllers/preview_guard_controller.js +243 -116
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +20 -19
- package/dist/controllers/progress_controller.js +391 -78
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +21 -9
- package/dist/controllers/radio_group_controller.js +278 -211
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +47 -6
- package/dist/controllers/range_slider_controller.js +220 -113
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +20 -4
- package/dist/controllers/rating_controller.js +458 -211
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +2 -0
- package/dist/controllers/read_more_controller.js +138 -63
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +1 -1
- package/dist/controllers/reading_progress_controller.js +166 -137
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +20 -6
- package/dist/controllers/relative_time_controller.js +211 -112
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
- package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +31 -2
- package/dist/controllers/resizable_controller.js +299 -150
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +3 -3
- package/dist/controllers/roving_controller.js +49 -88
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -3
- package/dist/controllers/scroll_area_controller.js +490 -246
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.d.ts +3 -3
- package/dist/controllers/scroll_restore_controller.js +14 -55
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
- package/dist/controllers/scroll_visibility_controller.js +432 -149
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +6 -1
- package/dist/controllers/scrollspy_controller.js +193 -248
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +20 -5
- package/dist/controllers/separator_controller.js +285 -142
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +65 -8
- package/dist/controllers/sidebar_controller.js +1069 -404
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +259 -101
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +29 -2
- package/dist/controllers/slider_controller.js +187 -86
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +22 -4
- package/dist/controllers/smart_sticky_header_controller.js +173 -66
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.d.ts +4 -4
- package/dist/controllers/sortable_controller.js +26 -125
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +288 -173
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +9 -4
- package/dist/controllers/step_indicator_controller.js +105 -46
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +14 -6
- package/dist/controllers/stepper_controller.js +120 -73
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +9 -1
- package/dist/controllers/stick_to_bottom_controller.js +275 -140
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +11 -0
- package/dist/controllers/sticky_observer_controller.js +220 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +24 -8
- package/dist/controllers/submit_once_controller.js +376 -227
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +18 -4
- package/dist/controllers/switch_controller.js +201 -84
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +9 -6
- package/dist/controllers/tabs_controller.js +41 -30
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +16 -3
- package/dist/controllers/tags_input_controller.js +293 -173
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +11 -1
- package/dist/controllers/textarea_autosize_controller.js +108 -74
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +24 -11
- package/dist/controllers/theme_controller.js +140 -116
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +55 -5
- package/dist/controllers/time_picker_controller.js +331 -127
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +40 -16
- package/dist/controllers/toast_controller.js +244 -324
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +14 -6
- package/dist/controllers/toggle_group_controller.js +252 -191
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.d.ts +8 -6
- package/dist/controllers/toolbar_controller.js +219 -120
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +49 -4
- package/dist/controllers/tooltip_controller.js +158 -124
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +22 -4
- package/dist/controllers/transition_controller.js +123 -119
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +22 -12
- package/dist/controllers/tree_view_controller.js +283 -369
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +8573 -10410
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +38 -14
- package/dist/inspector/cli.js +108 -40
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +145 -64
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +20 -20
- package/dist/inspector/manifest.json +802 -101
- package/dist/positioning/index.d.ts +15 -7
- package/dist/positioning/index.js +154 -47
- package/dist/positioning/index.js.map +1 -1
- package/package.json +11 -10
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -1,19 +1,65 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
|
|
4
|
+
var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
|
|
5
|
+
function stateReasonFor(event) {
|
|
6
|
+
if (!event) return "api";
|
|
7
|
+
if (FOCUS_EVENTS.has(event.type)) return "focus";
|
|
8
|
+
if (POINTER_EVENTS.has(event.type)) return "pointer";
|
|
9
|
+
return "user";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function actionSource(source) {
|
|
13
|
+
if (source instanceof HTMLElement) {
|
|
14
|
+
return { event: null, host: source, origin: source, reason: "api" };
|
|
15
|
+
}
|
|
16
|
+
const event = source ?? null;
|
|
17
|
+
return {
|
|
18
|
+
event,
|
|
19
|
+
host: event?.currentTarget instanceof Element ? event.currentTarget : null,
|
|
20
|
+
origin: event?.target instanceof Element ? event.target : null,
|
|
21
|
+
reason: stateReasonFor(event)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
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
|
+
}
|
|
4
31
|
|
|
5
|
-
// src/utils/logical_scroll.ts
|
|
6
32
|
function isRtl(element) {
|
|
7
33
|
return window.getComputedStyle(element).direction === "rtl";
|
|
8
34
|
}
|
|
9
35
|
|
|
10
|
-
// src/utils/arrow_step.ts
|
|
11
36
|
function isReservedArrowChord(event, allow = []) {
|
|
12
37
|
if (!event.key.startsWith("Arrow")) return false;
|
|
13
38
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
14
39
|
}
|
|
15
40
|
|
|
16
|
-
|
|
41
|
+
var ClickVerdicts = class {
|
|
42
|
+
#verdicts = /* @__PURE__ */ new WeakMap();
|
|
43
|
+
record(event, entries, refused) {
|
|
44
|
+
this.#verdicts.set(event, { entries, refused });
|
|
45
|
+
}
|
|
46
|
+
for(event, entry) {
|
|
47
|
+
const verdict = event && this.#verdicts.get(event);
|
|
48
|
+
if (!verdict?.entries.includes(entry)) return void 0;
|
|
49
|
+
return verdict.refused;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function sharedRegistry(key, create) {
|
|
54
|
+
const symbol = Symbol.for(key);
|
|
55
|
+
const scope = globalThis;
|
|
56
|
+
const existing = scope[symbol];
|
|
57
|
+
if (existing !== void 0) return existing;
|
|
58
|
+
const registry = create();
|
|
59
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
60
|
+
return registry;
|
|
61
|
+
}
|
|
62
|
+
|
|
17
63
|
function claimsWhileFocusWithin(element) {
|
|
18
64
|
return () => {
|
|
19
65
|
const active = element.ownerDocument.activeElement;
|
|
@@ -21,17 +67,11 @@ function claimsWhileFocusWithin(element) {
|
|
|
21
67
|
};
|
|
22
68
|
}
|
|
23
69
|
var EscapeLayer = class _EscapeLayer {
|
|
24
|
-
static #registries =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
#claims = null;
|
|
30
|
-
/**
|
|
31
|
-
* Activates this layer at the top of its document's Escape stack, installing
|
|
32
|
-
* the document's shared resolver listener if this is its first layer.
|
|
33
|
-
* Re-activating an already-active layer moves it to the top.
|
|
34
|
-
*/
|
|
70
|
+
static #registries = sharedRegistry(
|
|
71
|
+
"stimeo-ui.escape-layer.registry.v1",
|
|
72
|
+
() => /* @__PURE__ */ new WeakMap()
|
|
73
|
+
);
|
|
74
|
+
#active = null;
|
|
35
75
|
activate(ownerDocument = document, options) {
|
|
36
76
|
this.deactivate();
|
|
37
77
|
let registry = _EscapeLayer.#registries.get(ownerDocument);
|
|
@@ -40,44 +80,31 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
40
80
|
_EscapeLayer.#registries.set(ownerDocument, registry);
|
|
41
81
|
ownerDocument.addEventListener("keydown", registry.onKeydown);
|
|
42
82
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this.#
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Removes this layer from its document's Escape stack, uninstalling the
|
|
50
|
-
* shared listener when the stack empties. Safe to call when inactive.
|
|
51
|
-
*/
|
|
83
|
+
const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
|
|
84
|
+
registry.stack.push(entry);
|
|
85
|
+
this.#active = { document: ownerDocument, entry };
|
|
86
|
+
}
|
|
52
87
|
deactivate() {
|
|
53
|
-
const
|
|
54
|
-
if (!
|
|
55
|
-
const registry = _EscapeLayer.#registries.get(
|
|
88
|
+
const active = this.#active;
|
|
89
|
+
if (!active) return;
|
|
90
|
+
const registry = _EscapeLayer.#registries.get(active.document);
|
|
56
91
|
if (registry) {
|
|
57
|
-
const index = registry.stack.lastIndexOf(
|
|
92
|
+
const index = registry.stack.lastIndexOf(active.entry);
|
|
58
93
|
if (index >= 0) registry.stack.splice(index, 1);
|
|
59
94
|
if (registry.stack.length === 0) {
|
|
60
|
-
|
|
61
|
-
_EscapeLayer.#registries.delete(
|
|
95
|
+
active.document.removeEventListener("keydown", registry.onKeydown);
|
|
96
|
+
_EscapeLayer.#registries.delete(active.document);
|
|
62
97
|
}
|
|
63
98
|
}
|
|
64
|
-
this.#
|
|
65
|
-
|
|
66
|
-
this.#claims = null;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Whether this active layer would own a press right now: it is the topmost
|
|
70
|
-
* layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests
|
|
71
|
-
* and diagnostics — production dismissal goes through the shared listener.
|
|
72
|
-
*/
|
|
99
|
+
this.#active = null;
|
|
100
|
+
}
|
|
73
101
|
get ownsEscape() {
|
|
74
|
-
const
|
|
75
|
-
if (!
|
|
76
|
-
const registry = _EscapeLayer.#registries.get(
|
|
102
|
+
const active = this.#active;
|
|
103
|
+
if (!active) return false;
|
|
104
|
+
const registry = _EscapeLayer.#registries.get(active.document);
|
|
77
105
|
if (!registry) return false;
|
|
78
|
-
return _EscapeLayer.#resolveOwner(registry.stack) ===
|
|
106
|
+
return _EscapeLayer.#resolveOwner(registry.stack) === active.entry;
|
|
79
107
|
}
|
|
80
|
-
/** Builds a document's registry with its shared resolver listener. */
|
|
81
108
|
static #createRegistry() {
|
|
82
109
|
const registry = {
|
|
83
110
|
stack: [],
|
|
@@ -86,66 +113,106 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
86
113
|
const owner = _EscapeLayer.#resolveOwner(registry.stack);
|
|
87
114
|
if (!owner) return;
|
|
88
115
|
event.preventDefault();
|
|
89
|
-
owner
|
|
116
|
+
owner.onDismiss();
|
|
90
117
|
}
|
|
91
118
|
};
|
|
92
119
|
return registry;
|
|
93
120
|
}
|
|
94
|
-
/** The topmost stack layer whose claims predicate passes, or `null`. */
|
|
95
121
|
static #resolveOwner(stack) {
|
|
96
122
|
for (let index = stack.length - 1; index >= 0; index--) {
|
|
97
123
|
const layer = stack[index];
|
|
98
124
|
if (!layer) continue;
|
|
99
|
-
if (layer
|
|
125
|
+
if (layer.claims && !layer.claims()) continue;
|
|
100
126
|
return layer;
|
|
101
127
|
}
|
|
102
128
|
return null;
|
|
103
129
|
}
|
|
104
130
|
};
|
|
105
131
|
|
|
106
|
-
// src/utils/event_owner.ts
|
|
107
132
|
function ownerIndex(candidates, node) {
|
|
108
133
|
if (!(node instanceof Node)) return -1;
|
|
109
134
|
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
110
135
|
}
|
|
136
|
+
function ownersOf(candidates, node) {
|
|
137
|
+
if (!(node instanceof Node)) return [];
|
|
138
|
+
return candidates.filter((candidate) => candidate.contains(node));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
var ListenerSet = class {
|
|
142
|
+
#abort = new AbortController();
|
|
143
|
+
add(target, type, handler, options) {
|
|
144
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
145
|
+
}
|
|
146
|
+
dispose() {
|
|
147
|
+
this.#abort.abort();
|
|
148
|
+
this.#abort = new AbortController();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
153
|
+
|
|
154
|
+
var NUMBER_BOUNDS = {
|
|
155
|
+
timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
|
|
156
|
+
function matchesNumberBounds(value, bounds) {
|
|
157
|
+
if (!Number.isFinite(value)) {
|
|
158
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
159
|
+
if (direction === null) return false;
|
|
160
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
161
|
+
}
|
|
162
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
163
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
164
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
165
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
166
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function readNumber(raw, fallback, bounds) {
|
|
171
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
var NumberValueReader = class {
|
|
175
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
176
|
+
read(owner, name, raw, fallback, bounds) {
|
|
177
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
178
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
179
|
+
this.#lastRejected.delete(name);
|
|
180
|
+
return resolved;
|
|
181
|
+
}
|
|
182
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
183
|
+
const literal = owner.element.getAttribute(attribute);
|
|
184
|
+
if (literal === null) {
|
|
185
|
+
this.#lastRejected.delete(name);
|
|
186
|
+
return resolved;
|
|
187
|
+
}
|
|
188
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
189
|
+
this.#lastRejected.set(name, literal);
|
|
190
|
+
console.warn(
|
|
191
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return resolved;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
111
197
|
|
|
112
|
-
// src/utils/safe_timeout.ts
|
|
113
198
|
var TimerRegistry = class {
|
|
114
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
115
199
|
ids = /* @__PURE__ */ new Set();
|
|
116
|
-
/**
|
|
117
|
-
* Cancels a single tracked timer.
|
|
118
|
-
*
|
|
119
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
120
|
-
* registry), so callers can clear defensively without guarding.
|
|
121
|
-
*/
|
|
122
200
|
clear(id) {
|
|
123
201
|
if (this.ids.delete(id)) {
|
|
124
202
|
this.cancel(id);
|
|
125
203
|
}
|
|
126
204
|
}
|
|
127
|
-
/**
|
|
128
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
129
|
-
* to guarantee no timer outlives the element.
|
|
130
|
-
*/
|
|
131
205
|
clearAll() {
|
|
132
206
|
for (const id of this.ids) {
|
|
133
207
|
this.cancel(id);
|
|
134
208
|
}
|
|
135
209
|
this.ids.clear();
|
|
136
210
|
}
|
|
137
|
-
/** Number of timers currently tracked (pending). */
|
|
138
211
|
get size() {
|
|
139
212
|
return this.ids.size;
|
|
140
213
|
}
|
|
141
214
|
};
|
|
142
215
|
var SafeTimeout = class extends TimerRegistry {
|
|
143
|
-
/**
|
|
144
|
-
* Schedules `callback` after `delay` ms and returns the timer id.
|
|
145
|
-
*
|
|
146
|
-
* The id is removed from the registry automatically when the timeout fires,
|
|
147
|
-
* so {@link TimerRegistry.size | size} reflects only still-pending timers.
|
|
148
|
-
*/
|
|
149
216
|
set(callback, delay) {
|
|
150
217
|
const id = this.schedule(() => {
|
|
151
218
|
this.ids.delete(id);
|
|
@@ -162,137 +229,84 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
162
229
|
}
|
|
163
230
|
};
|
|
164
231
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
|
|
168
|
-
function stateReasonFor(event) {
|
|
169
|
-
if (!event) return "api";
|
|
170
|
-
if (FOCUS_EVENTS.has(event.type)) return "focus";
|
|
171
|
-
if (POINTER_EVENTS.has(event.type)) return "pointer";
|
|
172
|
-
return "user";
|
|
232
|
+
function targetSelector(identifier, name) {
|
|
233
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
173
234
|
}
|
|
174
235
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
static targets = ["trigger", "panel", "hoverArea"];
|
|
236
|
+
var NavigationMenuController = class _NavigationMenuController extends Controller {
|
|
237
|
+
#numbers = new NumberValueReader();
|
|
238
|
+
static targets = ["trigger", "panel", "hoverArea", "list"];
|
|
178
239
|
static values = {
|
|
179
240
|
openOnHover: { type: Boolean, default: false },
|
|
180
241
|
hoverDelay: { type: Number, default: 150 }
|
|
181
242
|
};
|
|
243
|
+
static valueConstraints = {
|
|
244
|
+
hoverDelay: NUMBER_BOUNDS.timer
|
|
245
|
+
};
|
|
182
246
|
static actions = ["onTriggerKeydown", "toggle"];
|
|
183
247
|
static events = ["close", "open"];
|
|
184
|
-
/** Open/close delay timers for hover mode; cleared together on disconnect. */
|
|
185
248
|
#hoverTimers = new SafeTimeout();
|
|
186
|
-
|
|
187
|
-
* Elements currently carrying hover listeners. Removal always mirrors this
|
|
188
|
-
* set (not a recomputed target snapshot), so a rebuild driven by target churn
|
|
189
|
-
* can never strand a listener on an element that stopped being a target.
|
|
190
|
-
*/
|
|
191
|
-
#hoverWired = /* @__PURE__ */ new Set();
|
|
192
|
-
/**
|
|
193
|
-
* The trigger whose hover region the pointer currently occupies, if any. Only
|
|
194
|
-
* the click semantics under `openOnHover` read it (see {@link toggle}).
|
|
195
|
-
*/
|
|
249
|
+
#hoverListeners = new ListenerSet();
|
|
196
250
|
#hoveredTrigger = null;
|
|
197
|
-
/**
|
|
198
|
-
* Whether the controller is between `connect()` and `disconnect()`. Stimulus
|
|
199
|
-
* disconnects the controller **before** disconnecting its targets, so the
|
|
200
|
-
* target callbacks below also fire during teardown; without this flag their
|
|
201
|
-
* rebuild would re-wire hover listeners onto an already-disconnected nav.
|
|
202
|
-
*/
|
|
203
251
|
#connected = false;
|
|
204
|
-
/** Whether state moves are reported: set once `connect()` settled the baseline. */
|
|
205
252
|
#reporting = false;
|
|
206
|
-
/** Escape-stack membership while any panel is open; the shared resolver dismisses via it. */
|
|
207
253
|
#escapeLayer = new EscapeLayer();
|
|
208
|
-
|
|
254
|
+
#listeners = new ListenerSet();
|
|
255
|
+
#clickVerdicts = new ClickVerdicts();
|
|
209
256
|
connect() {
|
|
210
257
|
this.#connected = true;
|
|
211
258
|
this.#closeAll("api");
|
|
212
|
-
|
|
213
|
-
this.
|
|
259
|
+
this.#listeners.add(this.element, "click", this.#onClickCapture, { capture: true });
|
|
260
|
+
this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
|
|
261
|
+
this.#listeners.add(this.element, "focusout", this.#onFocusOut);
|
|
214
262
|
if (this.openOnHoverValue) this.#addHoverListeners();
|
|
215
263
|
this.#reporting = true;
|
|
216
264
|
}
|
|
217
|
-
/** Removes every listener, pending hover timer, and stack membership taken while connected. */
|
|
218
265
|
disconnect() {
|
|
219
266
|
this.#connected = false;
|
|
220
267
|
this.#reporting = false;
|
|
221
268
|
this.#escapeLayer.deactivate();
|
|
222
|
-
|
|
223
|
-
this.element.removeEventListener("focusout", this.#onFocusOut);
|
|
269
|
+
this.#listeners.dispose();
|
|
224
270
|
this.#removeHoverListeners();
|
|
225
271
|
this.#hoverTimers.clearAll();
|
|
226
272
|
}
|
|
227
|
-
/**
|
|
228
|
-
* Follows a runtime flip of `openOnHover` (a morph or a scripted attribute
|
|
229
|
-
* change) by wiring or unwiring the hover listeners in place. Stimulus also
|
|
230
|
-
* fires this once before `connect()`, which the `#connected` guard skips —
|
|
231
|
-
* `connect()` owns the initial wiring.
|
|
232
|
-
*/
|
|
233
273
|
openOnHoverValueChanged() {
|
|
234
274
|
if (!this.#connected) return;
|
|
235
275
|
if (this.openOnHoverValue) this.#addHoverListeners();
|
|
236
276
|
else this.#removeHoverListeners();
|
|
237
277
|
}
|
|
238
|
-
/** Re-wires hover listeners when a target is added after connect (Turbo Streams etc.). */
|
|
239
278
|
triggerTargetConnected() {
|
|
240
279
|
this.#rewireHoverListeners();
|
|
241
280
|
}
|
|
242
|
-
/** Re-wires hover listeners when a target is removed while connected. */
|
|
243
281
|
triggerTargetDisconnected() {
|
|
244
282
|
this.#rewireHoverListeners();
|
|
245
283
|
}
|
|
246
|
-
/** See {@link NavigationMenuController.triggerTargetConnected}. */
|
|
247
284
|
panelTargetConnected() {
|
|
248
285
|
this.#rewireHoverListeners();
|
|
249
286
|
}
|
|
250
|
-
/** See {@link NavigationMenuController.triggerTargetDisconnected}. */
|
|
251
287
|
panelTargetDisconnected() {
|
|
252
288
|
this.#rewireHoverListeners();
|
|
253
289
|
}
|
|
254
|
-
/** See {@link NavigationMenuController.triggerTargetConnected}. */
|
|
255
290
|
hoverAreaTargetConnected() {
|
|
256
291
|
this.#rewireHoverListeners();
|
|
257
292
|
}
|
|
258
|
-
/** See {@link NavigationMenuController.triggerTargetDisconnected}. */
|
|
259
293
|
hoverAreaTargetDisconnected() {
|
|
260
294
|
this.#rewireHoverListeners();
|
|
261
295
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
*
|
|
269
|
-
* Under `openOnHover`, closing a panel the pointer itself is holding open is
|
|
270
|
-
* refused: the pointer stays put, `mouseenter` does not re-fire, and the panel
|
|
271
|
-
* would be unopenable until the pointer left and came back. Keyboard
|
|
272
|
-
* activation (pointer elsewhere) still toggles it closed, and `Escape` and
|
|
273
|
-
* outside clicks dismiss either way.
|
|
274
|
-
*/
|
|
275
|
-
toggle(event) {
|
|
276
|
-
const trigger = event.currentTarget;
|
|
277
|
-
const reason = stateReasonFor(event);
|
|
296
|
+
toggle(source) {
|
|
297
|
+
const { event, host, origin, reason } = actionSource(source);
|
|
298
|
+
const trigger = host?.closest(targetSelector(this.identifier, "trigger"));
|
|
299
|
+
if (!trigger || !this.triggerTargets.includes(trigger)) return;
|
|
300
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
301
|
+
return;
|
|
278
302
|
this.#hoverTimers.clearAll();
|
|
279
303
|
if (!this.#isExpanded(trigger)) {
|
|
280
|
-
this.#openPanel(trigger, reason);
|
|
304
|
+
if (!this.#refuses(trigger, event, origin)) this.#openPanel(trigger, reason);
|
|
281
305
|
return;
|
|
282
306
|
}
|
|
283
307
|
if (this.openOnHoverValue && this.#hoveredTrigger === trigger) return;
|
|
284
308
|
this.#closePanel(trigger, reason);
|
|
285
309
|
}
|
|
286
|
-
/**
|
|
287
|
-
* `ArrowLeft`/`ArrowRight` move focus between triggers (keeping Tab order).
|
|
288
|
-
*
|
|
289
|
-
* Only navigable triggers are destinations, so a `hidden` or disabled item
|
|
290
|
-
* never swallows the press. A modified arrow belongs to the browser or OS
|
|
291
|
-
* (`Alt+←`/`Alt+→` is history back/forward), and with no destination at all
|
|
292
|
-
* (a single-trigger nav) the press is left to the page — `preventDefault()` is
|
|
293
|
-
* called only when focus actually moves, keeping the scroll-suppression
|
|
294
|
-
* contract honest.
|
|
295
|
-
*/
|
|
296
310
|
onTriggerKeydown(event) {
|
|
297
311
|
if (event.defaultPrevented) return;
|
|
298
312
|
if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
|
|
@@ -304,17 +318,11 @@ var NavigationMenuController = class extends Controller {
|
|
|
304
318
|
const index = triggers.indexOf(current);
|
|
305
319
|
if (index === -1 || triggers.length < 2) return;
|
|
306
320
|
event.preventDefault();
|
|
307
|
-
const forward = isRtl(this.element) ? "ArrowLeft" : "ArrowRight";
|
|
321
|
+
const forward = isRtl(this.hasListTarget ? this.listTarget : this.element) ? "ArrowLeft" : "ArrowRight";
|
|
308
322
|
const step = event.key === forward ? 1 : -1;
|
|
309
323
|
triggers[(index + step + triggers.length) % triggers.length]?.focus();
|
|
310
324
|
}
|
|
311
|
-
/**
|
|
312
|
-
* Opens `trigger`'s panel, closing any other open panel first. Re-opening an
|
|
313
|
-
* already-open panel only re-asserts the Escape layer: the close/open
|
|
314
|
-
* round-trip would rewrite `aria-expanded` and `hidden` for no state change.
|
|
315
|
-
*/
|
|
316
325
|
#openPanel(trigger, reason) {
|
|
317
|
-
if (trigger.getAttribute("aria-disabled") === "true") return;
|
|
318
326
|
if (this.#isExpanded(trigger)) {
|
|
319
327
|
this.#syncEscapeLayer();
|
|
320
328
|
return;
|
|
@@ -327,7 +335,6 @@ var NavigationMenuController = class extends Controller {
|
|
|
327
335
|
this.#report("open", trigger, panel, reason);
|
|
328
336
|
this.#syncEscapeLayer();
|
|
329
337
|
}
|
|
330
|
-
/** Closes `trigger`'s panel, reflects the collapsed state, and reports a move. */
|
|
331
338
|
#closePanel(trigger, reason) {
|
|
332
339
|
const was = this.#isExpanded(trigger);
|
|
333
340
|
const panel = this.#panelFor(trigger);
|
|
@@ -336,15 +343,9 @@ var NavigationMenuController = class extends Controller {
|
|
|
336
343
|
if (was && panel) this.#report("close", trigger, panel, reason);
|
|
337
344
|
this.#syncEscapeLayer();
|
|
338
345
|
}
|
|
339
|
-
/**
|
|
340
|
-
* Closes every open panel and normalizes `aria-expanded="false"` on **all**
|
|
341
|
-
* triggers, so a trigger whose panel was hidden by other means cannot keep
|
|
342
|
-
* advertising an open panel.
|
|
343
|
-
*/
|
|
344
346
|
#closeAll(reason) {
|
|
345
347
|
for (const trigger of this.triggerTargets) this.#closePanel(trigger, reason);
|
|
346
348
|
}
|
|
347
|
-
/** Names the panel that moved, so a subscriber does not have to re-derive it. */
|
|
348
349
|
#report(name, trigger, panel, reason) {
|
|
349
350
|
if (!this.#reporting) return;
|
|
350
351
|
const index = this.triggerTargets.indexOf(trigger);
|
|
@@ -352,27 +353,20 @@ var NavigationMenuController = class extends Controller {
|
|
|
352
353
|
if (name === "open") this.dispatch("open", { detail, cancelable: false });
|
|
353
354
|
else this.dispatch("close", { detail, cancelable: false });
|
|
354
355
|
}
|
|
355
|
-
/**
|
|
356
|
-
* Whether a trigger can receive arrow-key focus: not `hidden`, not a natively
|
|
357
|
-
* `disabled` control (mirrors `toolbar`). CSS-only visibility cannot be
|
|
358
|
-
* detected headlessly and stays the consumer's responsibility.
|
|
359
|
-
*
|
|
360
|
-
* **`aria-disabled="true"` stays reachable.** APG separates the attributes by
|
|
361
|
-
* intent: `disabled` is for controls a neighbour makes inferable, while
|
|
362
|
-
* `aria-disabled` marks one that must remain *discoverable* — and a nav
|
|
363
|
-
* section the user cannot even arrow to is a section they cannot learn exists.
|
|
364
|
-
* Opening is suppressed separately, so the trigger announces itself and does
|
|
365
|
-
* nothing.
|
|
366
|
-
*/
|
|
367
356
|
#isNavigable(trigger) {
|
|
368
357
|
if (trigger.hasAttribute("hidden")) return false;
|
|
369
358
|
return !trigger.disabled;
|
|
370
359
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
360
|
+
#isActivationDisabled(trigger) {
|
|
361
|
+
return !this.#isNavigable(trigger) || isAriaDisabled(trigger);
|
|
362
|
+
}
|
|
363
|
+
#refuses(trigger, event, origin) {
|
|
364
|
+
const recorded = this.#clickVerdicts.for(event, trigger);
|
|
365
|
+
if (recorded !== void 0) return recorded;
|
|
366
|
+
if (this.#isActivationDisabled(trigger)) return true;
|
|
367
|
+
const target = event ? event.target : origin;
|
|
368
|
+
return ownersOf(this.triggerTargets, target).some((owner) => this.#isActivationDisabled(owner));
|
|
369
|
+
}
|
|
376
370
|
#syncEscapeLayer() {
|
|
377
371
|
if (this.#isAnyOpen) {
|
|
378
372
|
this.#escapeLayer.activate(document, {
|
|
@@ -383,11 +377,6 @@ var NavigationMenuController = class extends Controller {
|
|
|
383
377
|
this.#escapeLayer.deactivate();
|
|
384
378
|
}
|
|
385
379
|
}
|
|
386
|
-
/**
|
|
387
|
-
* Closes any open panel and returns focus to its trigger (Escape path). A
|
|
388
|
-
* pending hover open would otherwise re-open the panel the user just
|
|
389
|
-
* dismissed, so the reservation is dropped with it.
|
|
390
|
-
*/
|
|
391
380
|
#closeAndRestore() {
|
|
392
381
|
const open = this.#openTrigger;
|
|
393
382
|
if (!open) return;
|
|
@@ -395,113 +384,65 @@ var NavigationMenuController = class extends Controller {
|
|
|
395
384
|
this.#closePanel(open, "escape");
|
|
396
385
|
open.focus();
|
|
397
386
|
}
|
|
398
|
-
|
|
387
|
+
#onClickCapture = (event) => {
|
|
388
|
+
const triggers = ownersOf(this.triggerTargets, event.target);
|
|
389
|
+
this.#clickVerdicts.record(
|
|
390
|
+
event,
|
|
391
|
+
triggers,
|
|
392
|
+
triggers.some((trigger) => this.#isActivationDisabled(trigger))
|
|
393
|
+
);
|
|
394
|
+
};
|
|
399
395
|
#onOutsideClick = (event) => {
|
|
400
396
|
if (this.#isAnyOpen && !this.element.contains(event.target)) this.#closeAll("outside");
|
|
401
397
|
};
|
|
402
|
-
/**
|
|
403
|
-
* Closes (without restoring focus) when focus leaves the nav for a known
|
|
404
|
-
* external destination. A null/non-Node destination is indeterminate:
|
|
405
|
-
* browsers use it for clicks on non-focusable content and for window
|
|
406
|
-
* deactivation, so those never close the nav here (matching the popover
|
|
407
|
-
* convention) — the outside-click handler decides pointer dismissal, and the
|
|
408
|
-
* Escape stack's body-focus claim keeps keyboard dismissal working.
|
|
409
|
-
*/
|
|
410
398
|
#onFocusOut = (event) => {
|
|
411
399
|
const next = event.relatedTarget;
|
|
412
400
|
if (!(next instanceof Node) || this.element.contains(next)) return;
|
|
413
401
|
this.#closeAll("focus");
|
|
414
402
|
};
|
|
415
|
-
/**
|
|
416
|
-
* Opens a trigger's panel after the hover delay (hover mode).
|
|
417
|
-
*
|
|
418
|
-
* @stimeoRuntimeOnly `hoverDelay` is the delay of the one hover timer this entry arms.
|
|
419
|
-
*/
|
|
420
403
|
#onPointerEnter = (event) => {
|
|
421
404
|
const trigger = this.#triggerForHover(event.currentTarget);
|
|
422
405
|
if (!trigger) return;
|
|
423
406
|
this.#hoveredTrigger = trigger;
|
|
424
407
|
this.#hoverTimers.clearAll();
|
|
425
|
-
this.#hoverTimers.set(() => this.#openFromHover(trigger), this
|
|
408
|
+
this.#hoverTimers.set(() => this.#openFromHover(trigger), this.#safeHoverDelay);
|
|
426
409
|
};
|
|
427
|
-
/**
|
|
428
|
-
* Closes the open panel after the hover delay (hover mode) — unless the
|
|
429
|
-
* pointer moved directly into another part of the hover region (an adjacent
|
|
430
|
-
* trigger, panel, or hoverArea): crossing a shared edge must not schedule a
|
|
431
|
-
* spurious close, e.g. a hoverArea whose panel sits outside it as a sibling.
|
|
432
|
-
*
|
|
433
|
-
* @stimeoRuntimeOnly `hoverDelay` is the delay of the one hover timer this exit arms.
|
|
434
|
-
*/
|
|
435
410
|
#onPointerLeave = (event) => {
|
|
436
411
|
const next = event instanceof MouseEvent ? event.relatedTarget : null;
|
|
437
412
|
if (ownerIndex(this.#hoverElements, next) !== -1) return;
|
|
438
413
|
this.#hoveredTrigger = null;
|
|
439
414
|
this.#hoverTimers.clearAll();
|
|
440
|
-
this.#hoverTimers.set(() => this.#closeFromHover(), this
|
|
415
|
+
this.#hoverTimers.set(() => this.#closeFromHover(), this.#safeHoverDelay);
|
|
441
416
|
};
|
|
442
|
-
/**
|
|
443
|
-
* Hover-driven open. Switching panels would hide the outgoing one, so focus
|
|
444
|
-
* standing inside it is handed back to its trigger first: pointer movement
|
|
445
|
-
* must never send focus to the body (the keyboard user would lose their
|
|
446
|
-
* place). Focus outside the closing panel is left alone.
|
|
447
|
-
*/
|
|
448
417
|
#openFromHover(trigger) {
|
|
449
|
-
if (trigger
|
|
418
|
+
if (this.#refuses(trigger, null, trigger)) return;
|
|
450
419
|
const open = this.#openTrigger;
|
|
451
420
|
if (open && open !== trigger && this.#holdsFocus(this.#panelFor(open))) open.focus();
|
|
452
421
|
this.#openPanel(trigger, "pointer");
|
|
453
422
|
}
|
|
454
|
-
/**
|
|
455
|
-
* Hover-driven close: skipped while focus sits inside the panel it would hide,
|
|
456
|
-
* for the same no-focus-loss reason (cf. `hover_card`'s delayed close). The
|
|
457
|
-
* panel then stays open until `Escape`, an outside click, or focus leaving the
|
|
458
|
-
* nav dismisses it — all of which restore or keep focus deliberately.
|
|
459
|
-
*/
|
|
460
423
|
#closeFromHover() {
|
|
461
424
|
const open = this.#openTrigger;
|
|
462
425
|
if (open && this.#holdsFocus(this.#panelFor(open))) return;
|
|
463
426
|
this.#closeAll("pointer");
|
|
464
427
|
}
|
|
465
|
-
/** Whether `panel` exists and currently contains the focused element. */
|
|
466
428
|
#holdsFocus(panel) {
|
|
467
429
|
return panel?.contains(document.activeElement) ?? false;
|
|
468
430
|
}
|
|
469
|
-
/** Wires hover open/close on each hover element (opt-in), tracking what was wired. */
|
|
470
431
|
#addHoverListeners() {
|
|
471
432
|
for (const element of this.#hoverElements) {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
this.#hoverWired.add(element);
|
|
433
|
+
this.#hoverListeners.add(element, "mouseenter", this.#onPointerEnter);
|
|
434
|
+
this.#hoverListeners.add(element, "mouseleave", this.#onPointerLeave);
|
|
475
435
|
}
|
|
476
436
|
}
|
|
477
|
-
/** Removes the hover listeners from exactly the elements that were wired. */
|
|
478
437
|
#removeHoverListeners() {
|
|
479
|
-
|
|
480
|
-
element.removeEventListener("mouseenter", this.#onPointerEnter);
|
|
481
|
-
element.removeEventListener("mouseleave", this.#onPointerLeave);
|
|
482
|
-
}
|
|
483
|
-
this.#hoverWired.clear();
|
|
438
|
+
this.#hoverListeners.dispose();
|
|
484
439
|
this.#hoveredTrigger = null;
|
|
485
440
|
}
|
|
486
|
-
/**
|
|
487
|
-
* Rebuilds the hover wiring from the current targets. Target callbacks call
|
|
488
|
-
* this so items added or removed after connect (e.g. a Turbo Stream append)
|
|
489
|
-
* participate in hover; the wired-set removal keeps the rebuild symmetric.
|
|
490
|
-
* Stimulus fires those callbacks during teardown too, hence the `#connected`
|
|
491
|
-
* guard — a rebuild after `disconnect()` would resurrect the listeners.
|
|
492
|
-
*/
|
|
493
441
|
#rewireHoverListeners() {
|
|
494
442
|
if (!this.#connected || !this.openOnHoverValue) return;
|
|
495
443
|
this.#removeHoverListeners();
|
|
496
444
|
this.#addHoverListeners();
|
|
497
445
|
}
|
|
498
|
-
/**
|
|
499
|
-
* Elements that participate in hover: each `hoverArea`, plus every trigger and
|
|
500
|
-
* panel **not** wrapped by one. A wrapped trigger/panel must defer to its
|
|
501
|
-
* wrapper — its own mouseleave would otherwise schedule a close while the
|
|
502
|
-
* pointer is still inside the area (mouseenter does not re-fire on the wrapper
|
|
503
|
-
* when moving among its descendants), flickering the panel shut.
|
|
504
|
-
*/
|
|
505
446
|
get #hoverElements() {
|
|
506
447
|
const areas = this.hoverAreaTargets;
|
|
507
448
|
const covered = (element) => ownerIndex(areas, element) !== -1;
|
|
@@ -511,11 +452,6 @@ var NavigationMenuController = class extends Controller {
|
|
|
511
452
|
...this.panelTargets.filter((panel) => !covered(panel))
|
|
512
453
|
];
|
|
513
454
|
}
|
|
514
|
-
/**
|
|
515
|
-
* Resolves the trigger for a hovered element: the trigger itself, the trigger
|
|
516
|
-
* controlling a hovered panel, or the first trigger contained in a hovered
|
|
517
|
-
* `hoverArea` wrapper.
|
|
518
|
-
*/
|
|
519
455
|
#triggerForHover(element) {
|
|
520
456
|
if (this.triggerTargets.includes(element)) return element;
|
|
521
457
|
if (this.panelTargets.includes(element)) {
|
|
@@ -526,25 +462,29 @@ var NavigationMenuController = class extends Controller {
|
|
|
526
462
|
}
|
|
527
463
|
return null;
|
|
528
464
|
}
|
|
529
|
-
/** The panel controlled by `trigger` (matched by `aria-controls`/`id`). */
|
|
530
465
|
#panelFor(trigger) {
|
|
531
466
|
const id = trigger.getAttribute("aria-controls");
|
|
532
467
|
return id ? this.panelTargets.find((panel) => panel.id === id) ?? null : null;
|
|
533
468
|
}
|
|
534
|
-
/** Whether `trigger`'s panel is currently expanded. */
|
|
535
469
|
#isExpanded(trigger) {
|
|
536
470
|
return trigger.getAttribute("aria-expanded") === "true";
|
|
537
471
|
}
|
|
538
|
-
/** The trigger whose panel is currently open, if any. */
|
|
539
472
|
get #openTrigger() {
|
|
540
473
|
return this.triggerTargets.find((trigger) => this.#isExpanded(trigger)) ?? null;
|
|
541
474
|
}
|
|
542
|
-
/** Whether any panel is currently open. */
|
|
543
475
|
get #isAnyOpen() {
|
|
544
476
|
return this.#openTrigger !== null;
|
|
545
477
|
}
|
|
478
|
+
get #safeHoverDelay() {
|
|
479
|
+
return this.#numbers.read(
|
|
480
|
+
this,
|
|
481
|
+
"hoverDelay",
|
|
482
|
+
this.hoverDelayValue,
|
|
483
|
+
_NavigationMenuController.values.hoverDelay.default,
|
|
484
|
+
_NavigationMenuController.valueConstraints.hoverDelay
|
|
485
|
+
);
|
|
486
|
+
}
|
|
546
487
|
};
|
|
547
488
|
|
|
548
489
|
export { NavigationMenuController };
|
|
549
|
-
//# sourceMappingURL=navigation_menu_controller.js.map
|
|
550
490
|
//# sourceMappingURL=navigation_menu_controller.js.map
|