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,37 +1,5 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
// src/controllers/overflow_menu_controller.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/before_cache_reset.ts
|
|
6
|
-
var BeforeCacheReset = class _BeforeCacheReset {
|
|
7
|
-
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
8
|
-
static #subscribers = /* @__PURE__ */ new Set();
|
|
9
|
-
/** The shared listener; installed while at least one instance is subscribed. */
|
|
10
|
-
static #onBeforeCache = () => {
|
|
11
|
-
for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
|
|
12
|
-
};
|
|
13
|
-
#rewind;
|
|
14
|
-
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
15
|
-
constructor(rewind) {
|
|
16
|
-
this.#rewind = rewind;
|
|
17
|
-
}
|
|
18
|
-
/** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
|
|
19
|
-
activate() {
|
|
20
|
-
const first = _BeforeCacheReset.#subscribers.size === 0;
|
|
21
|
-
_BeforeCacheReset.#subscribers.add(this);
|
|
22
|
-
if (first) {
|
|
23
|
-
document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
|
|
27
|
-
deactivate() {
|
|
28
|
-
_BeforeCacheReset.#subscribers.delete(this);
|
|
29
|
-
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
30
|
-
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// src/utils/event_owner.ts
|
|
35
3
|
function ownerIndex(candidates, node) {
|
|
36
4
|
if (!(node instanceof Node)) return -1;
|
|
37
5
|
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
@@ -40,7 +8,6 @@ function ownerOf(candidates, node) {
|
|
|
40
8
|
return candidates[ownerIndex(candidates, node)] ?? null;
|
|
41
9
|
}
|
|
42
10
|
|
|
43
|
-
// src/utils/focus_candidate.ts
|
|
44
11
|
function inheritsFieldsetDisabled(control) {
|
|
45
12
|
let fieldset = control.closest("fieldset[disabled]");
|
|
46
13
|
while (fieldset) {
|
|
@@ -50,26 +17,39 @@ function inheritsFieldsetDisabled(control) {
|
|
|
50
17
|
}
|
|
51
18
|
return false;
|
|
52
19
|
}
|
|
20
|
+
function unslotted(element) {
|
|
21
|
+
return element.assignedSlot === null && element.parentElement?.shadowRoot != null;
|
|
22
|
+
}
|
|
23
|
+
function flatTreeParent(element) {
|
|
24
|
+
if (unslotted(element)) return null;
|
|
25
|
+
const parent = element.assignedSlot ?? element.parentNode;
|
|
26
|
+
if (parent instanceof ShadowRoot) return parent.host;
|
|
27
|
+
return parent instanceof Element ? parent : null;
|
|
28
|
+
}
|
|
53
29
|
function canTakeFocus(element) {
|
|
54
|
-
|
|
30
|
+
for (let node = element; node; node = flatTreeParent(node)) {
|
|
31
|
+
if (unslotted(node) || node.matches("[hidden], [inert]")) return false;
|
|
32
|
+
}
|
|
55
33
|
if (element instanceof HTMLInputElement && element.type === "hidden") return false;
|
|
56
34
|
if (!("disabled" in element)) return true;
|
|
57
35
|
if (element.disabled) return false;
|
|
58
36
|
return !inheritsFieldsetDisabled(element);
|
|
59
37
|
}
|
|
38
|
+
function deepActiveElement(ownerDocument) {
|
|
39
|
+
let active = ownerDocument.activeElement;
|
|
40
|
+
while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
|
|
41
|
+
return active;
|
|
42
|
+
}
|
|
60
43
|
|
|
61
|
-
// src/utils/layout_observer.ts
|
|
62
44
|
var LayoutObserver = class {
|
|
63
45
|
#callback;
|
|
64
46
|
#resizeObserverFactory;
|
|
65
47
|
#resizeObserver = null;
|
|
66
48
|
#observingViewport = false;
|
|
67
49
|
#loadContainer = null;
|
|
68
|
-
/** Stable bound handler so add/removeEventListener target the same reference. */
|
|
69
50
|
#handleViewportResize = () => {
|
|
70
51
|
this.#callback();
|
|
71
52
|
};
|
|
72
|
-
/** Stable bound handler for the capture-phase `load`; see {@link observeDescendantLoads}. */
|
|
73
53
|
#handleDescendantLoad = () => {
|
|
74
54
|
this.#callback();
|
|
75
55
|
};
|
|
@@ -77,11 +57,6 @@ var LayoutObserver = class {
|
|
|
77
57
|
this.#callback = callback;
|
|
78
58
|
this.#resizeObserverFactory = options.resizeObserverFactory ?? (typeof ResizeObserver === "undefined" ? null : (cb) => new ResizeObserver(cb));
|
|
79
59
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Starts observing an element's size. Repeated calls observe additional
|
|
82
|
-
* elements through the same shared observer. No-ops when no
|
|
83
|
-
* `ResizeObserver` implementation is available.
|
|
84
|
-
*/
|
|
85
60
|
observe(element) {
|
|
86
61
|
if (!this.#resizeObserverFactory) return;
|
|
87
62
|
if (!this.#resizeObserver) {
|
|
@@ -91,47 +66,28 @@ var LayoutObserver = class {
|
|
|
91
66
|
}
|
|
92
67
|
this.#resizeObserver.observe(element);
|
|
93
68
|
}
|
|
94
|
-
/** Stops observing a single element while leaving any others in place. */
|
|
95
69
|
unobserve(element) {
|
|
96
70
|
this.#resizeObserver?.unobserve(element);
|
|
97
71
|
}
|
|
98
|
-
/** Starts observing viewport resizes. Idempotent: the listener is added once. */
|
|
99
72
|
observeViewport() {
|
|
100
73
|
if (this.#observingViewport) return;
|
|
101
74
|
this.#observingViewport = true;
|
|
102
75
|
window.addEventListener("resize", this.#handleViewportResize);
|
|
103
76
|
}
|
|
104
|
-
/** Stops observing viewport resizes without affecting element observation. */
|
|
105
77
|
unobserveViewport() {
|
|
106
78
|
if (!this.#observingViewport) return;
|
|
107
79
|
this.#observingViewport = false;
|
|
108
80
|
window.removeEventListener("resize", this.#handleViewportResize);
|
|
109
81
|
}
|
|
110
|
-
/**
|
|
111
|
-
* Starts reporting a `load` from anywhere inside `container` — an image or a
|
|
112
|
-
* frame settling changes the box it sits in, and it measures as zero high until
|
|
113
|
-
* then. `load` does not bubble, so the subscription is a capture-phase listener
|
|
114
|
-
* on the container itself and nothing the caller spells.
|
|
115
|
-
*
|
|
116
|
-
* **One container at a time.** A further call moves the observation, so a widget
|
|
117
|
-
* whose content element is swapped at runtime releases the element it let go by
|
|
118
|
-
* naming the new one — there is no second place for the release to drift from.
|
|
119
|
-
*/
|
|
120
82
|
observeDescendantLoads(container) {
|
|
121
83
|
this.unobserveDescendantLoads();
|
|
122
84
|
this.#loadContainer = container;
|
|
123
85
|
container.addEventListener("load", this.#handleDescendantLoad, true);
|
|
124
86
|
}
|
|
125
|
-
/** Stops reporting descendant loads without affecting element or viewport observation. */
|
|
126
87
|
unobserveDescendantLoads() {
|
|
127
88
|
this.#loadContainer?.removeEventListener("load", this.#handleDescendantLoad, true);
|
|
128
89
|
this.#loadContainer = null;
|
|
129
90
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Releases every observation: disconnects the {@link ResizeObserver} and removes
|
|
132
|
-
* the viewport and descendant-load listeners. Safe to call multiple times. Call
|
|
133
|
-
* this from a controller's `disconnect()`.
|
|
134
|
-
*/
|
|
135
91
|
disconnect() {
|
|
136
92
|
this.#resizeObserver?.disconnect();
|
|
137
93
|
this.#resizeObserver = null;
|
|
@@ -140,43 +96,228 @@ var LayoutObserver = class {
|
|
|
140
96
|
}
|
|
141
97
|
};
|
|
142
98
|
|
|
143
|
-
|
|
99
|
+
var ListenerSet = class {
|
|
100
|
+
#abort = new AbortController();
|
|
101
|
+
add(target, type, handler, options) {
|
|
102
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
103
|
+
}
|
|
104
|
+
dispose() {
|
|
105
|
+
this.#abort.abort();
|
|
106
|
+
this.#abort = new AbortController();
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
var MicrotaskCoalescer = class {
|
|
111
|
+
#run;
|
|
112
|
+
#queued = false;
|
|
113
|
+
#active = false;
|
|
114
|
+
#generation = 0;
|
|
115
|
+
constructor(run) {
|
|
116
|
+
this.#run = run;
|
|
117
|
+
}
|
|
118
|
+
activate() {
|
|
119
|
+
this.#active = true;
|
|
120
|
+
}
|
|
121
|
+
cancel() {
|
|
122
|
+
this.#active = false;
|
|
123
|
+
this.#queued = false;
|
|
124
|
+
this.#generation += 1;
|
|
125
|
+
}
|
|
126
|
+
get pending() {
|
|
127
|
+
return this.#queued;
|
|
128
|
+
}
|
|
129
|
+
schedule() {
|
|
130
|
+
if (!this.#active || this.#queued) return;
|
|
131
|
+
this.#queued = true;
|
|
132
|
+
const generation = this.#generation;
|
|
133
|
+
queueMicrotask(() => {
|
|
134
|
+
if (generation !== this.#generation || !this.#queued || !this.#active) return;
|
|
135
|
+
this.#queued = false;
|
|
136
|
+
this.#run();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
var MorphRenderWatcher = class {
|
|
142
|
+
#listeners = new ListenerSet();
|
|
143
|
+
#pass;
|
|
144
|
+
#element = null;
|
|
145
|
+
#onMorph = () => this.#pass.schedule();
|
|
146
|
+
constructor(run) {
|
|
147
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
148
|
+
}
|
|
149
|
+
observe(element) {
|
|
150
|
+
if (this.#element === element) return;
|
|
151
|
+
this.disconnect();
|
|
152
|
+
this.#element = element;
|
|
153
|
+
this.#pass.activate();
|
|
154
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
155
|
+
}
|
|
156
|
+
schedule() {
|
|
157
|
+
this.#pass.schedule();
|
|
158
|
+
}
|
|
159
|
+
get pending() {
|
|
160
|
+
return this.#pass.pending;
|
|
161
|
+
}
|
|
162
|
+
disconnect() {
|
|
163
|
+
this.#listeners.dispose();
|
|
164
|
+
this.#pass.cancel();
|
|
165
|
+
this.#element = null;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
170
|
+
|
|
171
|
+
var NUMBER_BOUNDS = {
|
|
172
|
+
timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
|
|
173
|
+
function matchesNumberBounds(value, bounds) {
|
|
174
|
+
if (!Number.isFinite(value)) {
|
|
175
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
176
|
+
if (direction === null) return false;
|
|
177
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
178
|
+
}
|
|
179
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
180
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
181
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
182
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
183
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function readNumber(raw, fallback, bounds) {
|
|
188
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
var NumberValueReader = class {
|
|
192
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
193
|
+
read(owner, name, raw, fallback, bounds) {
|
|
194
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
195
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
196
|
+
this.#lastRejected.delete(name);
|
|
197
|
+
return resolved;
|
|
198
|
+
}
|
|
199
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
200
|
+
const literal = owner.element.getAttribute(attribute);
|
|
201
|
+
if (literal === null) {
|
|
202
|
+
this.#lastRejected.delete(name);
|
|
203
|
+
return resolved;
|
|
204
|
+
}
|
|
205
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
206
|
+
this.#lastRejected.set(name, literal);
|
|
207
|
+
console.warn(
|
|
208
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return resolved;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
216
|
+
if (element.getAttribute(name) === value) return false;
|
|
217
|
+
if (value === null) element.removeAttribute(name);
|
|
218
|
+
else element.setAttribute(name, value);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
var OwnedText = class {
|
|
223
|
+
#attribute;
|
|
224
|
+
#channel;
|
|
225
|
+
#guard;
|
|
226
|
+
#writes = /* @__PURE__ */ new WeakMap();
|
|
227
|
+
constructor(attribute, channel = "text", guard = () => true) {
|
|
228
|
+
this.#attribute = attribute;
|
|
229
|
+
this.#channel = channel;
|
|
230
|
+
this.#guard = guard;
|
|
231
|
+
}
|
|
232
|
+
hasRecord(element) {
|
|
233
|
+
return element.hasAttribute(this.#attribute);
|
|
234
|
+
}
|
|
235
|
+
read(element, evidence = {}) {
|
|
236
|
+
if (evidence.source === "live") return this.#writes.get(element) ?? null;
|
|
237
|
+
const raw = element.getAttribute(this.#attribute);
|
|
238
|
+
if (raw === null) return null;
|
|
239
|
+
try {
|
|
240
|
+
const record = JSON.parse(raw);
|
|
241
|
+
if (!Array.isArray(record) || record.length !== 3 || record[0] !== this.#channel || typeof record[1] !== "string" || typeof record[2] !== "string")
|
|
242
|
+
return null;
|
|
243
|
+
return { original: record[1], written: record[2] };
|
|
244
|
+
} catch {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
owns(element, evidence = {}) {
|
|
249
|
+
return this.#ownedRecord(element, evidence) !== null;
|
|
250
|
+
}
|
|
251
|
+
#ownedRecord(element, evidence) {
|
|
252
|
+
const record = this.read(element, evidence);
|
|
253
|
+
if (record === null || !this.#guard(element)) return null;
|
|
254
|
+
if (this.#channel === "text" && element.firstElementChild !== null) return null;
|
|
255
|
+
const current = this.#current(element);
|
|
256
|
+
return current === record.written || evidence.source === "live" && evidence.allowOriginal === true && current === record.original ? record : null;
|
|
257
|
+
}
|
|
258
|
+
write(element, written, original, force = false) {
|
|
259
|
+
const current = this.#current(element);
|
|
260
|
+
if (current === null) return;
|
|
261
|
+
const before = original ?? this.#writes.get(element)?.original ?? this.read(element)?.original ?? current;
|
|
262
|
+
if (force || current !== written) this.#put(element, written);
|
|
263
|
+
this.remember(element, written, before);
|
|
264
|
+
}
|
|
265
|
+
remember(element, written, original) {
|
|
266
|
+
const current = this.#current(element);
|
|
267
|
+
if (current === null) return;
|
|
268
|
+
const before = original ?? this.#writes.get(element)?.original ?? this.read(element)?.original ?? current;
|
|
269
|
+
this.#writes.set(element, { original: before, written });
|
|
270
|
+
setAttributeIfChanged(
|
|
271
|
+
element,
|
|
272
|
+
this.#attribute,
|
|
273
|
+
JSON.stringify([this.#channel, before, written])
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
clear(element, evidence = {}) {
|
|
277
|
+
if (!this.owns(element, evidence)) return false;
|
|
278
|
+
if (this.#current(element) !== "") this.#put(element, "");
|
|
279
|
+
this.forget(element);
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
restore(element, evidence = {}) {
|
|
283
|
+
const record = this.#ownedRecord(element, evidence);
|
|
284
|
+
if (record === null) return false;
|
|
285
|
+
if (this.#current(element) !== record.original) this.#put(element, record.original);
|
|
286
|
+
this.forget(element);
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
forget(element) {
|
|
290
|
+
setAttributeIfChanged(element, this.#attribute, null);
|
|
291
|
+
this.#writes.delete(element);
|
|
292
|
+
}
|
|
293
|
+
#current(element) {
|
|
294
|
+
if (this.#channel === "text") return element.textContent ?? "";
|
|
295
|
+
return element instanceof HTMLInputElement ? element.value : null;
|
|
296
|
+
}
|
|
297
|
+
#put(element, text) {
|
|
298
|
+
if (this.#channel === "text") element.textContent = text;
|
|
299
|
+
else if (element instanceof HTMLInputElement) element.value = text;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
144
303
|
var TimerRegistry = class {
|
|
145
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
146
304
|
ids = /* @__PURE__ */ new Set();
|
|
147
|
-
/**
|
|
148
|
-
* Cancels a single tracked timer.
|
|
149
|
-
*
|
|
150
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
151
|
-
* registry), so callers can clear defensively without guarding.
|
|
152
|
-
*/
|
|
153
305
|
clear(id) {
|
|
154
306
|
if (this.ids.delete(id)) {
|
|
155
307
|
this.cancel(id);
|
|
156
308
|
}
|
|
157
309
|
}
|
|
158
|
-
/**
|
|
159
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
160
|
-
* to guarantee no timer outlives the element.
|
|
161
|
-
*/
|
|
162
310
|
clearAll() {
|
|
163
311
|
for (const id of this.ids) {
|
|
164
312
|
this.cancel(id);
|
|
165
313
|
}
|
|
166
314
|
this.ids.clear();
|
|
167
315
|
}
|
|
168
|
-
/** Number of timers currently tracked (pending). */
|
|
169
316
|
get size() {
|
|
170
317
|
return this.ids.size;
|
|
171
318
|
}
|
|
172
319
|
};
|
|
173
320
|
var SafeTimeout = class extends TimerRegistry {
|
|
174
|
-
/**
|
|
175
|
-
* Schedules `callback` after `delay` ms and returns the timer id.
|
|
176
|
-
*
|
|
177
|
-
* The id is removed from the registry automatically when the timeout fires,
|
|
178
|
-
* so {@link TimerRegistry.size | size} reflects only still-pending timers.
|
|
179
|
-
*/
|
|
180
321
|
set(callback, delay) {
|
|
181
322
|
const id = this.schedule(() => {
|
|
182
323
|
this.ids.delete(id);
|
|
@@ -193,38 +334,136 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
193
334
|
}
|
|
194
335
|
};
|
|
195
336
|
|
|
196
|
-
|
|
197
|
-
|
|
337
|
+
function queryOpenDescendants(root, selector) {
|
|
338
|
+
const matches = [];
|
|
339
|
+
const roots = [root];
|
|
340
|
+
for (const scope of roots) {
|
|
341
|
+
matches.push(...scope.querySelectorAll(selector));
|
|
342
|
+
if (scope.nodeType === Node.ELEMENT_NODE) {
|
|
343
|
+
const shadow = scope.shadowRoot;
|
|
344
|
+
if (shadow) roots.push(shadow);
|
|
345
|
+
}
|
|
346
|
+
for (const element of scope.querySelectorAll("*")) {
|
|
347
|
+
if (element.shadowRoot) roots.push(element.shadowRoot);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return matches;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function sharedRegistry(key, create) {
|
|
354
|
+
const symbol = Symbol.for(key);
|
|
355
|
+
const scope = globalThis;
|
|
356
|
+
const existing = scope[symbol];
|
|
357
|
+
if (existing !== void 0) return existing;
|
|
358
|
+
const registry = create();
|
|
359
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
360
|
+
return registry;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function holdsFocus(element) {
|
|
364
|
+
return element.matches(":focus") || deepActiveElement(element.ownerDocument) === element;
|
|
365
|
+
}
|
|
366
|
+
var TabindexLoan = class _TabindexLoan {
|
|
367
|
+
static #registry = sharedRegistry(
|
|
368
|
+
"stimeo-ui.tabindex-loan.registry.v1",
|
|
369
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
|
|
370
|
+
);
|
|
198
371
|
#value;
|
|
372
|
+
#record;
|
|
373
|
+
#preserveFocus;
|
|
199
374
|
#lent = /* @__PURE__ */ new Set();
|
|
200
|
-
|
|
201
|
-
#beforeCache = new BeforeCacheReset(() => this.returnAll());
|
|
202
|
-
/**
|
|
203
|
-
* @param value - the `tabindex` to lend. `"-1"` (the default) is
|
|
204
|
-
* programmatically focusable but not a Tab stop; `"0"` is a real Tab stop,
|
|
205
|
-
* which a scroll region with no focusable content of its own needs.
|
|
206
|
-
*/
|
|
207
|
-
constructor(value = "-1") {
|
|
375
|
+
constructor(value, owner, options = {}) {
|
|
208
376
|
this.#value = value;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
377
|
+
this.#record = `data-${owner}-tabindex-loan`;
|
|
378
|
+
this.#preserveFocus = options.preserveFocus === true;
|
|
379
|
+
_TabindexLoan.#registry.names.add(this.#record);
|
|
380
|
+
}
|
|
381
|
+
lend(element, options = {}) {
|
|
382
|
+
const stack = this.#stack(element);
|
|
383
|
+
const current = element.getAttribute("tabindex");
|
|
384
|
+
const held = stack.find((hold) => hold.loan === this) ?? stack.find(
|
|
385
|
+
(hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
|
|
386
|
+
);
|
|
387
|
+
const linked = stack.at(-1)?.value === current;
|
|
388
|
+
if (!held && options.onlyIfUnfocusable && (current !== null || element.tabIndex >= 0)) return;
|
|
389
|
+
if (!held && current !== null && !linked) return;
|
|
214
390
|
this.#lent.add(element);
|
|
215
|
-
|
|
391
|
+
if (held) held.loan = this;
|
|
392
|
+
else stack.push({ loan: this, record: this.#record, value: this.#value, linked });
|
|
393
|
+
this.#mark(element, stack);
|
|
394
|
+
if (current === null || !held && current !== this.#value) {
|
|
395
|
+
element.setAttribute("tabindex", this.#value);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return(element) {
|
|
399
|
+
const stack = this.#stack(element);
|
|
400
|
+
const hold = stack.find((held) => held.loan === this) ?? stack.find(
|
|
401
|
+
(held) => held.loan === null && held.record === this.#record && held.value === this.#value
|
|
402
|
+
);
|
|
403
|
+
if (!hold) return false;
|
|
404
|
+
if (!this.#release(element, stack, hold, this.#preserveFocus)) {
|
|
405
|
+
hold.loan = this;
|
|
406
|
+
this.#lent.add(element);
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
this.#lent.delete(element);
|
|
410
|
+
return true;
|
|
216
411
|
}
|
|
217
|
-
/** Takes back every loan whose value is still the one that was lent. */
|
|
218
412
|
returnAll() {
|
|
219
|
-
for (const element of this.#lent)
|
|
220
|
-
|
|
413
|
+
for (const element of Array.from(this.#lent)) this.return(element);
|
|
414
|
+
}
|
|
415
|
+
reclaim(element) {
|
|
416
|
+
if (element.getAttribute(this.#record) !== this.#value) return;
|
|
417
|
+
const stack = this.#stack(element);
|
|
418
|
+
const hold = stack.find((held) => held.loan === null && held.record === this.#record);
|
|
419
|
+
if (hold && !holdsFocus(element)) this.#release(element, stack, hold);
|
|
420
|
+
}
|
|
421
|
+
reclaimWithin(root) {
|
|
422
|
+
for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
|
|
423
|
+
this.reclaim(element);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
#stack(element) {
|
|
427
|
+
const stacks = _TabindexLoan.#registry.stacks;
|
|
428
|
+
const stack = stacks.get(element) ?? this.#seed(element);
|
|
429
|
+
stacks.set(element, stack);
|
|
430
|
+
return stack;
|
|
431
|
+
}
|
|
432
|
+
#seed(element) {
|
|
433
|
+
const current = element.getAttribute("tabindex");
|
|
434
|
+
const holds = [];
|
|
435
|
+
for (const record of _TabindexLoan.#registry.names) {
|
|
436
|
+
const value = element.getAttribute(record);
|
|
437
|
+
if (value !== null) holds.push({ loan: null, record, value, linked: true });
|
|
438
|
+
}
|
|
439
|
+
holds.sort((one, other) => Number(one.value === current) - Number(other.value === current));
|
|
440
|
+
for (const [index, hold] of holds.entries()) hold.linked = index > 0;
|
|
441
|
+
return holds;
|
|
442
|
+
}
|
|
443
|
+
#release(element, stack, hold, preserveFocus = false) {
|
|
444
|
+
const current = element.getAttribute("tabindex");
|
|
445
|
+
const index = stack.indexOf(hold);
|
|
446
|
+
const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
|
|
447
|
+
if (owns && preserveFocus && holdsFocus(element)) return false;
|
|
448
|
+
stack.splice(index, 1);
|
|
449
|
+
const above = stack[index];
|
|
450
|
+
if (above) above.linked &&= hold.linked;
|
|
451
|
+
this.#mark(element, stack);
|
|
452
|
+
if (!owns) return true;
|
|
453
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
454
|
+
if (!below) element.removeAttribute("tabindex");
|
|
455
|
+
else if (below.value !== current) element.setAttribute("tabindex", below.value);
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
#mark(element, stack) {
|
|
459
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
460
|
+
for (const hold of stack) records.set(hold.record, hold.value);
|
|
461
|
+
for (const [name, recorded] of records) {
|
|
462
|
+
setAttributeIfChanged(element, name, recorded);
|
|
221
463
|
}
|
|
222
|
-
this.#lent.clear();
|
|
223
|
-
this.#beforeCache.deactivate();
|
|
224
464
|
}
|
|
225
465
|
};
|
|
226
466
|
|
|
227
|
-
// src/controllers/overflow_menu_controller.ts
|
|
228
467
|
var BANKED = "banked";
|
|
229
468
|
var INDEX = "index";
|
|
230
469
|
var BOUNDARY = "boundary";
|
|
@@ -234,8 +473,13 @@ var SAVED_MENU_TARGET = "saved-menu";
|
|
|
234
473
|
var MENU_TARGET = "data-stimeo--menu-target";
|
|
235
474
|
var BOOKKEEPING = [BANKED, INDEX, SAVED_ROLE, SAVED_TABINDEX, SAVED_MENU_TARGET];
|
|
236
475
|
var OWNED_LABEL = "owns-label";
|
|
237
|
-
var OverflowMenuController = class extends Controller {
|
|
238
|
-
|
|
476
|
+
var OverflowMenuController = class _OverflowMenuController extends Controller {
|
|
477
|
+
#labelText = new OwnedText(
|
|
478
|
+
this.#attr(OWNED_LABEL),
|
|
479
|
+
"text",
|
|
480
|
+
(trigger) => this.#carriesOnlyText(trigger)
|
|
481
|
+
);
|
|
482
|
+
#numbers = new NumberValueReader();
|
|
239
483
|
#attr(name) {
|
|
240
484
|
return `data-${this.identifier}-${name}`;
|
|
241
485
|
}
|
|
@@ -244,66 +488,84 @@ var OverflowMenuController = class extends Controller {
|
|
|
244
488
|
moreLabel: { type: String, default: "More" },
|
|
245
489
|
debounce: { type: Number, default: 100 }
|
|
246
490
|
};
|
|
491
|
+
static valueConstraints = {
|
|
492
|
+
debounce: NUMBER_BOUNDS.timer
|
|
493
|
+
};
|
|
247
494
|
static actions = ["update"];
|
|
248
|
-
static events = ["change"];
|
|
495
|
+
static events = ["change", "reconcile"];
|
|
249
496
|
#layout = new LayoutObserver(() => this.#scheduleUpdate());
|
|
250
497
|
#timers = new SafeTimeout();
|
|
251
|
-
/** The managed items in canonical (authored) order, rebuilt from the DOM each pass. */
|
|
252
498
|
#items = [];
|
|
253
499
|
#index = /* @__PURE__ */ new WeakMap();
|
|
254
|
-
/** Cached bar-context width per item; see the class remarks for the staleness caveat. */
|
|
255
500
|
#widths = /* @__PURE__ */ new WeakMap();
|
|
256
|
-
/** Last reported overflow count, so `change` fires only on transitions. */
|
|
257
501
|
#lastHidden = null;
|
|
258
|
-
|
|
259
|
-
#
|
|
260
|
-
|
|
261
|
-
#
|
|
262
|
-
|
|
263
|
-
* Whether `connect()` has run for this connection. Stimulus delivers Value
|
|
264
|
-
* callbacks ahead of it, and the first pass writes the label anyway.
|
|
265
|
-
*/
|
|
502
|
+
#pass = 0;
|
|
503
|
+
#restoring = false;
|
|
504
|
+
#tabindex = new TabindexLoan("-1", this.identifier);
|
|
505
|
+
#bar = null;
|
|
506
|
+
#wrapper = null;
|
|
266
507
|
#connected = false;
|
|
508
|
+
#morphRender = new MorphRenderWatcher(() => this.#reconcile());
|
|
267
509
|
connect() {
|
|
510
|
+
this.#tabindex.reclaim(this.element);
|
|
511
|
+
this.#morphRender.observe(this.element);
|
|
268
512
|
this.#connected = true;
|
|
269
513
|
if (!this.hasItemsTarget || !this.hasMoreTarget) return;
|
|
270
|
-
this.#beforeCache.activate();
|
|
271
514
|
this.#layout.observe(this.element);
|
|
272
515
|
this.#layout.observeViewport();
|
|
273
516
|
this.update();
|
|
274
517
|
}
|
|
275
518
|
disconnect() {
|
|
519
|
+
this.#morphRender.disconnect();
|
|
276
520
|
this.#connected = false;
|
|
277
521
|
this.#layout.disconnect();
|
|
278
522
|
this.#timers.clearAll();
|
|
279
|
-
this.#beforeCache.deactivate();
|
|
280
523
|
this.#restoreAll();
|
|
281
524
|
this.#lastHidden = null;
|
|
282
525
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
526
|
+
itemsTargetConnected() {
|
|
527
|
+
this.#followTargets();
|
|
528
|
+
}
|
|
529
|
+
itemsTargetDisconnected(bar) {
|
|
530
|
+
if (bar === this.#bar && !this.itemsTargets.includes(bar)) this.#release();
|
|
531
|
+
this.#followTargets();
|
|
532
|
+
}
|
|
533
|
+
moreTargetConnected() {
|
|
534
|
+
this.#followTargets();
|
|
535
|
+
}
|
|
536
|
+
moreTargetDisconnected(more) {
|
|
537
|
+
if (more === this.#wrapper && !this.moreTargets.includes(more)) this.#release();
|
|
538
|
+
this.#followTargets();
|
|
539
|
+
}
|
|
288
540
|
moreLabelValueChanged() {
|
|
289
541
|
if (!this.#connected || !this.hasItemsTarget || !this.hasMoreTarget) return;
|
|
290
542
|
this.#syncLabel();
|
|
291
543
|
}
|
|
292
|
-
/** Re-measures and rebalances items between the bar and the More menu. */
|
|
293
544
|
update() {
|
|
294
|
-
|
|
545
|
+
const detail = this.#render();
|
|
546
|
+
if (detail) this.dispatch("change", { detail });
|
|
547
|
+
}
|
|
548
|
+
#reconcile() {
|
|
549
|
+
const detail = this.#render();
|
|
550
|
+
if (detail) this.dispatch("reconcile", { detail });
|
|
551
|
+
}
|
|
552
|
+
#render() {
|
|
553
|
+
if (this.#restoring || !this.hasItemsTarget || !this.hasMoreTarget) return null;
|
|
554
|
+
const pass = ++this.#pass;
|
|
555
|
+
this.#bar = this.itemsTarget;
|
|
556
|
+
this.#wrapper = this.moreTarget;
|
|
295
557
|
this.#syncLabel();
|
|
296
|
-
this.#syncItems();
|
|
558
|
+
this.#syncItems(this.itemsTarget, this.moreTarget);
|
|
297
559
|
this.moreTarget.hidden = false;
|
|
298
560
|
if (this.#items.some((item) => item.parentElement !== this.itemsTarget && !this.#widths.has(item))) {
|
|
299
|
-
this.#removeBoundary();
|
|
561
|
+
this.#removeBoundary(this.itemsTarget);
|
|
300
562
|
for (const item of this.#items) this.#unbank(item);
|
|
301
563
|
this.#reorder(this.itemsTarget, this.#items);
|
|
302
564
|
}
|
|
303
565
|
for (const item of this.#items) {
|
|
304
566
|
if (item.parentElement === this.itemsTarget) this.#widths.set(item, item.offsetWidth);
|
|
305
567
|
}
|
|
306
|
-
const moreWidth = (this.#trigger() ?? this.moreTarget).offsetWidth;
|
|
568
|
+
const moreWidth = (this.#trigger(this.moreTarget) ?? this.moreTarget).offsetWidth;
|
|
307
569
|
const itemGap = this.#gap(this.itemsTarget);
|
|
308
570
|
const barGap = this.#gap(this.element);
|
|
309
571
|
const widthOf = (el) => this.#widths.get(el) ?? el.offsetWidth;
|
|
@@ -325,36 +587,39 @@ var OverflowMenuController = class extends Controller {
|
|
|
325
587
|
}
|
|
326
588
|
const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
327
589
|
const focused = active === null ? null : this.#ownerOf(active);
|
|
328
|
-
const retreating = focused !== null && hidden.has(focused) && focused.parentElement === this.itemsTarget &&
|
|
329
|
-
!this.#menuExpanded();
|
|
330
|
-
const returning = focused !== null && !hidden.has(focused) && focused.parentElement !== this.itemsTarget &&
|
|
331
|
-
this.#menuExpanded();
|
|
332
|
-
if (hidden.size === 0 || hidden.size !== this.#items.length)
|
|
590
|
+
const retreating = focused !== null && hidden.has(focused) && focused.parentElement === this.itemsTarget &&
|
|
591
|
+
!this.#menuExpanded(this.moreTarget);
|
|
592
|
+
const returning = focused !== null && !hidden.has(focused) && focused.parentElement !== this.itemsTarget &&
|
|
593
|
+
this.#menuExpanded(this.moreTarget);
|
|
594
|
+
if (hidden.size === 0 || hidden.size !== this.#items.length) {
|
|
595
|
+
this.#removeBoundary(this.itemsTarget);
|
|
596
|
+
}
|
|
333
597
|
for (const item of this.#items) {
|
|
334
598
|
if (hidden.has(item)) this.#bank(item);
|
|
335
599
|
else this.#unbank(item);
|
|
336
600
|
}
|
|
337
601
|
this.#reorder(
|
|
338
|
-
this.#menuList(),
|
|
602
|
+
this.#menuList(this.moreTarget),
|
|
339
603
|
this.#items.filter((item) => hidden.has(item))
|
|
340
604
|
);
|
|
341
605
|
this.#reorder(
|
|
342
606
|
this.itemsTarget,
|
|
343
607
|
this.#items.filter((item) => !hidden.has(item))
|
|
344
608
|
);
|
|
345
|
-
if (retreating) (this.#trigger() ?? this.moreTarget).focus();
|
|
609
|
+
if (retreating) (this.#trigger(this.moreTarget) ?? this.moreTarget).focus();
|
|
346
610
|
else if (active !== null && this.#lostFocus(active)) active.focus();
|
|
611
|
+
if (pass !== this.#pass) return null;
|
|
347
612
|
const count = hidden.size;
|
|
348
613
|
for (const item of this.#items) {
|
|
349
614
|
if (count > 0) item.setAttribute(this.#attr(INDEX), String(this.#indexOf(item)));
|
|
350
615
|
else item.removeAttribute(this.#attr(INDEX));
|
|
351
616
|
}
|
|
352
617
|
if (count > 0 && count === this.#items.length) this.#ensureBoundary();
|
|
353
|
-
else this.#removeBoundary();
|
|
354
618
|
const inMore = document.activeElement instanceof HTMLElement && this.moreTarget.contains(document.activeElement);
|
|
355
619
|
if (count === 0 || returning) {
|
|
356
|
-
this.#closeMenu();
|
|
357
|
-
if (inMore) this.#rescueFocus();
|
|
620
|
+
this.#closeMenu(this.moreTarget);
|
|
621
|
+
if (inMore && pass === this.#pass) this.#rescueFocus();
|
|
622
|
+
if (pass !== this.#pass) return null;
|
|
358
623
|
}
|
|
359
624
|
this.moreTarget.hidden = count === 0;
|
|
360
625
|
if (count > 0) this.element.setAttribute("data-overflowing", "true");
|
|
@@ -362,15 +627,14 @@ var OverflowMenuController = class extends Controller {
|
|
|
362
627
|
this.element.setAttribute("data-overflow-count", String(count));
|
|
363
628
|
if (this.#lastHidden !== count) {
|
|
364
629
|
this.#lastHidden = count;
|
|
365
|
-
|
|
630
|
+
return { overflowCount: count, total: this.#items.length };
|
|
366
631
|
}
|
|
632
|
+
return null;
|
|
367
633
|
}
|
|
368
|
-
/** The flex `column-gap` on `el` in px (0 when none / unsupported). */
|
|
369
634
|
#gap(el) {
|
|
370
635
|
const style = this.#styleOf(el);
|
|
371
636
|
return style === null ? 0 : this.#px(style.columnGap || style.gap);
|
|
372
637
|
}
|
|
373
|
-
/** Horizontal padding on `el` in px — `clientWidth` counts it, the content box cannot. */
|
|
374
638
|
#paddingX(el) {
|
|
375
639
|
const style = this.#styleOf(el);
|
|
376
640
|
return style === null ? 0 : this.#px(style.paddingLeft) + this.#px(style.paddingRight);
|
|
@@ -382,16 +646,11 @@ var OverflowMenuController = class extends Controller {
|
|
|
382
646
|
const parsed = Number.parseFloat(value ?? "");
|
|
383
647
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
384
648
|
}
|
|
385
|
-
|
|
386
|
-
* Rebuilds the managed set and the canonical order from the DOM. The bar's children
|
|
387
|
-
* give the order of everything inline (so a leading insert is not silently appended)
|
|
388
|
-
* and {@link #merge} weaves the banked items back among them.
|
|
389
|
-
*/
|
|
390
|
-
#syncItems() {
|
|
649
|
+
#syncItems(row, more) {
|
|
391
650
|
const previous = this.#items;
|
|
392
651
|
const bar = [];
|
|
393
652
|
let boundaryAt;
|
|
394
|
-
for (const el of
|
|
653
|
+
for (const el of row.children) {
|
|
395
654
|
if (el instanceof HTMLTemplateElement && el.hasAttribute(this.#attr(BOUNDARY))) {
|
|
396
655
|
boundaryAt ??= bar.length;
|
|
397
656
|
continue;
|
|
@@ -399,7 +658,7 @@ var OverflowMenuController = class extends Controller {
|
|
|
399
658
|
if (el instanceof HTMLElement) bar.push(el);
|
|
400
659
|
}
|
|
401
660
|
const banked = [];
|
|
402
|
-
for (const el of this.#menuList().children) {
|
|
661
|
+
for (const el of this.#menuList(more).children) {
|
|
403
662
|
if (el instanceof HTMLElement && el.hasAttribute(this.#attr(BANKED))) banked.push(el);
|
|
404
663
|
}
|
|
405
664
|
banked.sort((a, b) => this.#bankedIndex(a) - this.#bankedIndex(b));
|
|
@@ -416,29 +675,6 @@ var OverflowMenuController = class extends Controller {
|
|
|
416
675
|
this.#index.set(el, i);
|
|
417
676
|
});
|
|
418
677
|
}
|
|
419
|
-
/**
|
|
420
|
-
* Interleaves the banked items back among the bar's children.
|
|
421
|
-
*
|
|
422
|
-
* A saved index is a position in the *canonical* order, so it can only be compared
|
|
423
|
-
* against another canonical position — never used as an offset into the current bar,
|
|
424
|
-
* which is a shorter and differently-indexed list. (Doing so survives an untouched
|
|
425
|
-
* round trip and silently scrambles the order as soon as the consumer inserts into a
|
|
426
|
-
* bar that is already short, and `#bank()` then burns the scrambled index back into
|
|
427
|
-
* the attribute, so widening never heals it.) Each bar child the controller already
|
|
428
|
-
* knows carries its canonical index from the previous pass; a banked item goes in
|
|
429
|
-
* front of the first known child whose index is higher.
|
|
430
|
-
*
|
|
431
|
-
* Children never seen before — inserted by the consumer since the last pass — have no
|
|
432
|
-
* canonical position yet, so they keep their DOM neighbours. The exception is a
|
|
433
|
-
* *trailing* run of them: appending to the bar means appending to the toolbar, so
|
|
434
|
-
* everything banked is emitted before it.
|
|
435
|
-
*
|
|
436
|
-
* With no known child at all (a fresh instance connecting to markup that already
|
|
437
|
-
* holds banked items), a fully-banked snapshot's inert boundary preserves whether an
|
|
438
|
-
* unindexed run was prepended or appended before connect. Markup that holds banked items but no boundary —
|
|
439
|
-
* server-rendered or hand-authored — uses the saved index as the
|
|
440
|
-
* offset, the inverse of the move that banked it.
|
|
441
|
-
*/
|
|
442
678
|
#merge(bar, banked, boundaryAt) {
|
|
443
679
|
const canonical = bar.map((el) => this.#index.get(el) ?? this.#savedIndex(el));
|
|
444
680
|
let lastKnown = -1;
|
|
@@ -474,56 +710,50 @@ var OverflowMenuController = class extends Controller {
|
|
|
474
710
|
drainWhile(() => true);
|
|
475
711
|
return items;
|
|
476
712
|
}
|
|
477
|
-
/** The canonical index a banked item saved, or ∞ (append) when it carries none. */
|
|
478
713
|
#bankedIndex(item) {
|
|
479
714
|
return this.#savedIndex(item) ?? Number.POSITIVE_INFINITY;
|
|
480
715
|
}
|
|
481
|
-
/** A finite canonical index stored on either side of the overflow split. */
|
|
482
716
|
#savedIndex(item) {
|
|
483
717
|
const raw = item.getAttribute(this.#attr(INDEX));
|
|
484
718
|
if (raw === null) return void 0;
|
|
485
719
|
const value = Number(raw);
|
|
486
720
|
return Number.isFinite(value) ? value : void 0;
|
|
487
721
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
for (const el of this.itemsTarget.children) {
|
|
722
|
+
#boundary(bar) {
|
|
723
|
+
for (const el of bar.children) {
|
|
491
724
|
if (el instanceof HTMLTemplateElement && el.hasAttribute(this.#attr(BOUNDARY))) return el;
|
|
492
725
|
}
|
|
493
726
|
return null;
|
|
494
727
|
}
|
|
495
728
|
#ensureBoundary() {
|
|
496
|
-
if (this.#boundary() !== null) return;
|
|
729
|
+
if (this.#boundary(this.itemsTarget) !== null) return;
|
|
497
730
|
const boundary = document.createElement("template");
|
|
498
731
|
boundary.setAttribute(this.#attr(BOUNDARY), "");
|
|
499
732
|
this.itemsTarget.appendChild(boundary);
|
|
500
733
|
}
|
|
501
|
-
#removeBoundary() {
|
|
502
|
-
this.#boundary()?.remove();
|
|
734
|
+
#removeBoundary(bar) {
|
|
735
|
+
this.#boundary(bar)?.remove();
|
|
503
736
|
}
|
|
504
|
-
/** Debounced re-measure for resize-driven churn. */
|
|
505
737
|
#scheduleUpdate() {
|
|
506
738
|
this.#timers.clearAll();
|
|
507
|
-
this.#timers.set(() => this.update(), this
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
* @stimeoRenderRoot
|
|
524
|
-
*/
|
|
739
|
+
this.#timers.set(() => this.update(), this.#safeDebounce);
|
|
740
|
+
}
|
|
741
|
+
#followTargets() {
|
|
742
|
+
if (this.#restoring || this.#lastHidden === null) return;
|
|
743
|
+
const bar = this.hasItemsTarget ? this.itemsTarget : null;
|
|
744
|
+
const more = this.hasMoreTarget ? this.moreTarget : null;
|
|
745
|
+
if (bar === this.#bar && more === this.#wrapper) return;
|
|
746
|
+
this.#release();
|
|
747
|
+
this.#morphRender.schedule();
|
|
748
|
+
}
|
|
749
|
+
#menuList(more) {
|
|
750
|
+
return more.querySelector('[data-stimeo--menu-target="menu"]') ?? more;
|
|
751
|
+
}
|
|
752
|
+
#trigger(more) {
|
|
753
|
+
return more.querySelector('[data-stimeo--menu-target="trigger"]');
|
|
754
|
+
}
|
|
525
755
|
#syncLabel() {
|
|
526
|
-
const trigger = this.#trigger();
|
|
756
|
+
const trigger = this.#trigger(this.moreTarget);
|
|
527
757
|
if (trigger === null) return;
|
|
528
758
|
const owned = this.#ownsLabel(trigger);
|
|
529
759
|
if (!owned && !this.#isBareTrigger(trigger)) return;
|
|
@@ -532,93 +762,43 @@ var OverflowMenuController = class extends Controller {
|
|
|
532
762
|
this.#releaseLabel(trigger);
|
|
533
763
|
return;
|
|
534
764
|
}
|
|
535
|
-
|
|
536
|
-
trigger.textContent = label;
|
|
537
|
-
trigger.setAttribute(this.#attr(OWNED_LABEL), label);
|
|
765
|
+
this.#labelText.write(trigger, label);
|
|
538
766
|
}
|
|
539
|
-
/**
|
|
540
|
-
* Whether the trigger holds the label this controller wrote there and nothing
|
|
541
|
-
* else: the text still matches the marker, and the trigger carries nothing but
|
|
542
|
-
* text. Ownership is read afresh on every call, so a trigger the consumer adds to
|
|
543
|
-
* is theirs for as long as the addition is there.
|
|
544
|
-
*/
|
|
545
767
|
#ownsLabel(trigger) {
|
|
546
|
-
return
|
|
768
|
+
return this.#labelText.owns(trigger);
|
|
547
769
|
}
|
|
548
|
-
/**
|
|
549
|
-
* Takes the label this controller wrote back out of the trigger, leaving it bare.
|
|
550
|
-
* A trigger that is not this controller's is left exactly as it is, marker
|
|
551
|
-
* included: taking the marker off would itself write to it.
|
|
552
|
-
*/
|
|
553
770
|
#releaseLabel(trigger) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
trigger.removeAttribute(this.#attr(OWNED_LABEL));
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* Whether the More trigger is bare, so the label is safe to write: no text, no
|
|
560
|
-
* element child, no `aria-label` and no `aria-labelledby`. A trigger with neither
|
|
561
|
-
* text, nor children, nor a name has nothing to lose and gets the fallback; one
|
|
562
|
-
* that is empty *and* unnamed is the only case the label rescues.
|
|
563
|
-
*/
|
|
771
|
+
this.#labelText.clear(trigger);
|
|
772
|
+
}
|
|
564
773
|
#isBareTrigger(trigger) {
|
|
565
774
|
return (trigger.textContent ?? "").trim() === "" && this.#carriesOnlyText(trigger);
|
|
566
775
|
}
|
|
567
|
-
/**
|
|
568
|
-
* Whether the trigger carries nothing but text: no element child, no
|
|
569
|
-
* `aria-label` and no `aria-labelledby`. Assigning `textContent` replaces
|
|
570
|
-
* *every* child node, so a trigger holding an icon would silently lose it — and
|
|
571
|
-
* the loss is permanent, since only the label this controller wrote is ever
|
|
572
|
-
* taken back. A trigger that carries an accessible name is left alone too:
|
|
573
|
-
* visible text written under a different `aria-label` would make the label and
|
|
574
|
-
* the name disagree. Only a trigger that carries nothing but text is ever
|
|
575
|
-
* written or emptied.
|
|
576
|
-
*/
|
|
577
776
|
#carriesOnlyText(trigger) {
|
|
578
777
|
return trigger.firstElementChild === null && !trigger.hasAttribute("aria-label") && !trigger.hasAttribute("aria-labelledby");
|
|
579
778
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
#menuExpanded() {
|
|
586
|
-
return this.#trigger()?.getAttribute("aria-expanded") === "true";
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Collapses the More menu before its wrapper is hidden. Menu owns the state (and the
|
|
590
|
-
* Escape-stack membership that goes with it), so ask it; the DOM fallback covers markup
|
|
591
|
-
* that reached an expanded state with no Menu mounted.
|
|
592
|
-
*/
|
|
593
|
-
#closeMenu() {
|
|
594
|
-
if (!this.#menuExpanded()) return;
|
|
779
|
+
#menuExpanded(more) {
|
|
780
|
+
return this.#trigger(more)?.getAttribute("aria-expanded") === "true";
|
|
781
|
+
}
|
|
782
|
+
#closeMenu(more) {
|
|
783
|
+
if (!this.#menuExpanded(more)) return;
|
|
595
784
|
const menu = this.application.getControllerForElementAndIdentifier(
|
|
596
|
-
|
|
785
|
+
more,
|
|
597
786
|
"stimeo--menu"
|
|
598
787
|
);
|
|
599
788
|
if (menu) {
|
|
600
789
|
menu.close();
|
|
601
790
|
return;
|
|
602
791
|
}
|
|
603
|
-
this.#trigger()?.setAttribute("aria-expanded", "false");
|
|
604
|
-
const list = this.#menuList();
|
|
605
|
-
if (list !==
|
|
792
|
+
this.#trigger(more)?.setAttribute("aria-expanded", "false");
|
|
793
|
+
const list = this.#menuList(more);
|
|
794
|
+
if (list !== more) list.hidden = true;
|
|
606
795
|
}
|
|
607
|
-
/** The managed item that is, or contains, `el` — focus rescue works on either. */
|
|
608
796
|
#ownerOf(el) {
|
|
609
797
|
return ownerOf(this.#items, el);
|
|
610
798
|
}
|
|
611
|
-
/** Whether the item takes up room in the bar; an authored `hidden` one does not. */
|
|
612
799
|
#rendered(item) {
|
|
613
800
|
return !item.hasAttribute("hidden");
|
|
614
801
|
}
|
|
615
|
-
/**
|
|
616
|
-
* Moves focus out of the More wrapper just before it is hidden, to the last
|
|
617
|
-
* item in canonical order that can hold it. When every item refuses, the root
|
|
618
|
-
* takes focus instead of letting the browser drop it to `<body>`, borrowing a
|
|
619
|
-
* `tabindex="-1"` just-in-time — not a Tab stop, and handed back on restore
|
|
620
|
-
* when this controller was the one that added it.
|
|
621
|
-
*/
|
|
622
802
|
#rescueFocus() {
|
|
623
803
|
for (let i = this.#items.length - 1; i >= 0; i--) {
|
|
624
804
|
const item = this.#items[i];
|
|
@@ -630,38 +810,38 @@ var OverflowMenuController = class extends Controller {
|
|
|
630
810
|
this.#tabindex.lend(this.element);
|
|
631
811
|
this.element.focus();
|
|
632
812
|
}
|
|
633
|
-
/**
|
|
634
|
-
* Whether `el` held focus, is still in a visible part of this controller, and lost it —
|
|
635
|
-
* what a re-insert does in a real browser. An item that retreated into a collapsed menu
|
|
636
|
-
* is excluded: it sits in a `hidden` subtree and must not be focused back.
|
|
637
|
-
*/
|
|
638
813
|
#lostFocus(el) {
|
|
639
814
|
return el.isConnected && document.activeElement !== el && this.element.contains(el) && el.closest("[hidden]") === null;
|
|
640
815
|
}
|
|
641
|
-
/** Returns every item to the bar and removes all traces of this controller. */
|
|
642
816
|
#restoreAll() {
|
|
643
817
|
if (!this.hasItemsTarget || !this.hasMoreTarget) return;
|
|
644
|
-
this.#
|
|
645
|
-
|
|
646
|
-
|
|
818
|
+
this.#giveBack(this.itemsTarget, this.moreTarget);
|
|
819
|
+
}
|
|
820
|
+
#release() {
|
|
821
|
+
if (this.#bar && this.#wrapper) this.#giveBack(this.#bar, this.#wrapper);
|
|
822
|
+
}
|
|
823
|
+
#giveBack(bar, more) {
|
|
824
|
+
this.#bar = null;
|
|
825
|
+
this.#wrapper = null;
|
|
826
|
+
const restoring = this.#restoring;
|
|
827
|
+
this.#restoring = true;
|
|
828
|
+
this.#pass++;
|
|
829
|
+
this.#syncItems(bar, more);
|
|
830
|
+
this.#removeBoundary(bar);
|
|
831
|
+
this.#reorder(bar, this.#items);
|
|
647
832
|
for (const item of this.#items) {
|
|
648
833
|
this.#unbank(item);
|
|
649
834
|
item.removeAttribute(this.#attr(INDEX));
|
|
650
835
|
}
|
|
651
|
-
this.#closeMenu();
|
|
652
|
-
const trigger = this.#trigger();
|
|
836
|
+
this.#closeMenu(more);
|
|
837
|
+
const trigger = this.#trigger(more);
|
|
653
838
|
if (trigger !== null) this.#releaseLabel(trigger);
|
|
654
|
-
|
|
839
|
+
more.hidden = true;
|
|
655
840
|
this.element.removeAttribute("data-overflowing");
|
|
656
841
|
this.element.removeAttribute("data-overflow-count");
|
|
657
842
|
this.#tabindex.returnAll();
|
|
843
|
+
this.#restoring = restoring;
|
|
658
844
|
}
|
|
659
|
-
/**
|
|
660
|
-
* Makes `parent`'s managed children match `sequence`, moving only what is out of place.
|
|
661
|
-
* Walking backwards lets each item be compared against the element that should follow
|
|
662
|
-
* it, so an unchanged run costs zero DOM mutations — the point of the exercise, since
|
|
663
|
-
* every re-insert blurs the focused node.
|
|
664
|
-
*/
|
|
665
845
|
#reorder(parent, sequence) {
|
|
666
846
|
let next = null;
|
|
667
847
|
for (let i = sequence.length - 1; i >= 0; i--) {
|
|
@@ -673,7 +853,6 @@ var OverflowMenuController = class extends Controller {
|
|
|
673
853
|
next = item;
|
|
674
854
|
}
|
|
675
855
|
}
|
|
676
|
-
/** Relocates `item`, preferring `moveBefore` (keeps focus and element state). */
|
|
677
856
|
#insert(parent, item, before) {
|
|
678
857
|
const move = parent.moveBefore;
|
|
679
858
|
if (typeof move === "function") {
|
|
@@ -685,10 +864,6 @@ var OverflowMenuController = class extends Controller {
|
|
|
685
864
|
}
|
|
686
865
|
parent.insertBefore(item, before);
|
|
687
866
|
}
|
|
688
|
-
/**
|
|
689
|
-
* Gives an item menuitem semantics for Menu to drive and records its canonical index,
|
|
690
|
-
* saving every authored value it overwrites ("" means the attribute was absent).
|
|
691
|
-
*/
|
|
692
867
|
#bank(item) {
|
|
693
868
|
if (!item.hasAttribute(this.#attr(BANKED))) {
|
|
694
869
|
item.setAttribute(this.#attr(BANKED), "true");
|
|
@@ -701,7 +876,6 @@ var OverflowMenuController = class extends Controller {
|
|
|
701
876
|
}
|
|
702
877
|
item.setAttribute(this.#attr(INDEX), String(this.#indexOf(item)));
|
|
703
878
|
}
|
|
704
|
-
/** Undoes the banking: restores every authored value and drops the bookkeeping. */
|
|
705
879
|
#unbank(item) {
|
|
706
880
|
if (!item.hasAttribute(this.#attr(BANKED))) return;
|
|
707
881
|
this.#restoreAttr(item, "role", item.getAttribute(this.#attr(SAVED_ROLE)));
|
|
@@ -709,7 +883,6 @@ var OverflowMenuController = class extends Controller {
|
|
|
709
883
|
this.#restoreAttr(item, MENU_TARGET, item.getAttribute(this.#attr(SAVED_MENU_TARGET)));
|
|
710
884
|
for (const name of BOOKKEEPING) item.removeAttribute(this.#attr(name));
|
|
711
885
|
}
|
|
712
|
-
/** Re-applies a saved attribute value, or removes the attribute when it was absent. */
|
|
713
886
|
#restoreAttr(item, name, original) {
|
|
714
887
|
if (original === null || original === "") item.removeAttribute(name);
|
|
715
888
|
else item.setAttribute(name, original);
|
|
@@ -717,20 +890,22 @@ var OverflowMenuController = class extends Controller {
|
|
|
717
890
|
#indexOf(item) {
|
|
718
891
|
return this.#index.get(item) ?? 0;
|
|
719
892
|
}
|
|
720
|
-
/**
|
|
721
|
-
* Retention rank: lower keeps longer. An absent, empty, or non-numeric `data-priority`
|
|
722
|
-
* ranks lowest (drops first) — empty is the common ERB accident
|
|
723
|
-
* (`data-priority="<%= maybe_nil %>"`), and `Number("")` is 0, which would otherwise
|
|
724
|
-
* read as the *highest* retention.
|
|
725
|
-
*/
|
|
726
893
|
#rank(item) {
|
|
727
894
|
const raw = item.getAttribute("data-priority");
|
|
728
895
|
if (raw === null || raw.trim() === "") return Number.POSITIVE_INFINITY;
|
|
729
896
|
const value = Number(raw);
|
|
730
897
|
return Number.isNaN(value) ? Number.POSITIVE_INFINITY : value;
|
|
731
898
|
}
|
|
899
|
+
get #safeDebounce() {
|
|
900
|
+
return this.#numbers.read(
|
|
901
|
+
this,
|
|
902
|
+
"debounce",
|
|
903
|
+
this.debounceValue,
|
|
904
|
+
_OverflowMenuController.values.debounce.default,
|
|
905
|
+
_OverflowMenuController.valueConstraints.debounce
|
|
906
|
+
);
|
|
907
|
+
}
|
|
732
908
|
};
|
|
733
909
|
|
|
734
910
|
export { OverflowMenuController };
|
|
735
|
-
//# sourceMappingURL=overflow_menu_controller.js.map
|
|
736
911
|
//# sourceMappingURL=overflow_menu_controller.js.map
|