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,130 +1,416 @@
|
|
|
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 setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
26
|
+
if (element.getAttribute(name) === value) return false;
|
|
27
|
+
if (value === null) element.removeAttribute(name);
|
|
28
|
+
else element.setAttribute(name, value);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
4
31
|
|
|
5
|
-
// src/utils/active_option.ts
|
|
6
32
|
function syncActiveOption(options, active) {
|
|
7
33
|
let written = 0;
|
|
8
34
|
for (const option of options) {
|
|
9
35
|
const next = option === active ? "true" : "false";
|
|
10
|
-
if (option
|
|
11
|
-
option.setAttribute("aria-selected", next);
|
|
12
|
-
written += 1;
|
|
36
|
+
if (setAttributeIfChanged(option, "aria-selected", next)) written += 1;
|
|
13
37
|
}
|
|
14
38
|
return written;
|
|
15
39
|
}
|
|
16
40
|
|
|
17
|
-
// src/utils/arrow_step.ts
|
|
18
41
|
function isReservedArrowChord(event, allow = []) {
|
|
19
42
|
if (!event.key.startsWith("Arrow")) return false;
|
|
20
43
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
21
44
|
}
|
|
45
|
+
function hasModifierChord(event) {
|
|
46
|
+
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function sharedRegistry(key, create) {
|
|
50
|
+
const symbol = Symbol.for(key);
|
|
51
|
+
const scope = globalThis;
|
|
52
|
+
const existing = scope[symbol];
|
|
53
|
+
if (existing !== void 0) return existing;
|
|
54
|
+
const registry2 = create();
|
|
55
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
56
|
+
return registry2;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
var registry = sharedRegistry(
|
|
60
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
61
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
62
|
+
);
|
|
63
|
+
function isAttributeValue(value) {
|
|
64
|
+
return value === null || typeof value === "string";
|
|
65
|
+
}
|
|
66
|
+
function readLeaseRecord(element, record) {
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
69
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
70
|
+
const [original, written, ...rest] = parsed;
|
|
71
|
+
const linked = rest[0] !== false;
|
|
72
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
73
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
74
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
75
|
+
} catch {
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function attributeStack(element, attribute) {
|
|
80
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
81
|
+
registry.stacks.set(element, byAttribute);
|
|
82
|
+
const known = byAttribute.get(attribute);
|
|
83
|
+
if (known) return known;
|
|
84
|
+
const found = [];
|
|
85
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
86
|
+
const read = readLeaseRecord(element, record);
|
|
87
|
+
if (read) found.push({ ...read, record });
|
|
88
|
+
}
|
|
89
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
90
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
91
|
+
lease: null,
|
|
92
|
+
record,
|
|
93
|
+
written,
|
|
94
|
+
original,
|
|
95
|
+
linked: linked && index > 0
|
|
96
|
+
}));
|
|
97
|
+
byAttribute.set(attribute, stack);
|
|
98
|
+
return stack;
|
|
99
|
+
}
|
|
100
|
+
function findHold(stack, lease, record) {
|
|
101
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
102
|
+
}
|
|
103
|
+
var AttributeLease = class {
|
|
104
|
+
#attribute;
|
|
105
|
+
#record;
|
|
106
|
+
#base;
|
|
107
|
+
#preserveAuthor;
|
|
108
|
+
#held = /* @__PURE__ */ new Set();
|
|
109
|
+
constructor(attribute, owner, options = {}) {
|
|
110
|
+
this.#attribute = attribute;
|
|
111
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
112
|
+
this.#base = options.base === true;
|
|
113
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
114
|
+
const names = registry.names;
|
|
115
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
116
|
+
}
|
|
117
|
+
write(element, value) {
|
|
118
|
+
const stack = attributeStack(element, this.#attribute);
|
|
119
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
120
|
+
this.#held.add(element);
|
|
121
|
+
const shows = !this.#covered(element, stack, hold);
|
|
122
|
+
hold.written = value;
|
|
123
|
+
this.#mark(element, stack);
|
|
124
|
+
if (shows) this.#reflect(element, value);
|
|
125
|
+
}
|
|
126
|
+
return(element) {
|
|
127
|
+
const held = this.#held.delete(element);
|
|
128
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
129
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
130
|
+
const hold = this.#take(stack);
|
|
131
|
+
if (!hold) return false;
|
|
132
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
133
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
134
|
+
const index = stack.indexOf(hold);
|
|
135
|
+
stack.splice(index, 1);
|
|
136
|
+
const above = stack[index];
|
|
137
|
+
if (above) above.linked &&= hold.linked;
|
|
138
|
+
this.#mark(element, stack);
|
|
139
|
+
if (!owns) return true;
|
|
140
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
141
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
returnAll() {
|
|
145
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
146
|
+
}
|
|
147
|
+
#take(stack) {
|
|
148
|
+
const hold = findHold(stack, this, this.#record);
|
|
149
|
+
if (hold) hold.lease = this;
|
|
150
|
+
return hold;
|
|
151
|
+
}
|
|
152
|
+
#acquire(element, stack) {
|
|
153
|
+
const current = element.getAttribute(this.#attribute);
|
|
154
|
+
const top = stack.at(-1);
|
|
155
|
+
const bottom = stack[0];
|
|
156
|
+
const over = top?.written === current ? top : void 0;
|
|
157
|
+
const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
|
|
158
|
+
const hold = {
|
|
159
|
+
lease: this,
|
|
160
|
+
record: this.#record,
|
|
161
|
+
written: current,
|
|
162
|
+
original: from ? from.original : current,
|
|
163
|
+
linked: !this.#base && over !== void 0
|
|
164
|
+
};
|
|
165
|
+
if (this.#base && bottom) {
|
|
166
|
+
bottom.linked = true;
|
|
167
|
+
stack.unshift(hold);
|
|
168
|
+
} else stack.push(hold);
|
|
169
|
+
return hold;
|
|
170
|
+
}
|
|
171
|
+
#covered(element, stack, hold) {
|
|
172
|
+
const current = element.getAttribute(this.#attribute);
|
|
173
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
|
|
174
|
+
}
|
|
175
|
+
#mark(element, stack) {
|
|
176
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
177
|
+
stack.forEach((hold, index) => {
|
|
178
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
179
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
180
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
181
|
+
records.set(
|
|
182
|
+
hold.record,
|
|
183
|
+
hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
for (const [name, recorded] of records) {
|
|
187
|
+
setAttributeIfChanged(element, name, recorded);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
#reflect(element, value) {
|
|
191
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
22
194
|
|
|
23
|
-
// src/utils/composition_tracker.ts
|
|
24
195
|
var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
25
196
|
"insertCompositionText",
|
|
26
197
|
"insertFromComposition",
|
|
27
198
|
"deleteCompositionText",
|
|
28
199
|
"deleteByComposition"
|
|
29
200
|
]);
|
|
201
|
+
function isCompositionInputType(inputType) {
|
|
202
|
+
return inputType !== void 0 && COMPOSITION_INPUT_TYPES.has(inputType);
|
|
203
|
+
}
|
|
204
|
+
var BEFORE_MORPH_ATTRIBUTE = "turbo:before-morph-attribute";
|
|
205
|
+
var textOf = (element) => element.value;
|
|
206
|
+
var holdsFocus = (element) => element instanceof Node && element.getRootNode().activeElement === element;
|
|
30
207
|
var CompositionTracker = class {
|
|
31
208
|
#observedTargets = /* @__PURE__ */ new Set();
|
|
32
|
-
#
|
|
209
|
+
#composition = null;
|
|
33
210
|
#onStart;
|
|
34
211
|
#onEnd;
|
|
35
|
-
|
|
212
|
+
#onAbandon;
|
|
36
213
|
#confirmedTarget = null;
|
|
214
|
+
#keyComposing = false;
|
|
215
|
+
#connected = false;
|
|
216
|
+
#abandoned = null;
|
|
37
217
|
constructor(options = {}) {
|
|
38
218
|
this.#onStart = options.onStart;
|
|
39
219
|
this.#onEnd = options.onEnd;
|
|
220
|
+
this.#onAbandon = options.onAbandon;
|
|
40
221
|
}
|
|
41
|
-
/** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
|
|
42
222
|
observe(target) {
|
|
223
|
+
this.#connected = true;
|
|
43
224
|
if (this.#observedTargets.has(target)) return;
|
|
44
225
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
45
226
|
target.addEventListener("compositionend", this.#handleEnd);
|
|
46
|
-
target.addEventListener("keydown", this.#handleKeydown);
|
|
227
|
+
target.addEventListener("keydown", this.#handleKeydown, true);
|
|
228
|
+
target.addEventListener("beforeinput", this.#handleBeforeInput, true);
|
|
229
|
+
target.addEventListener("input", this.#handleInput, true);
|
|
230
|
+
target.addEventListener("blur", this.#handleBlur, true);
|
|
231
|
+
target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
|
|
47
232
|
this.#observedTargets.add(target);
|
|
48
233
|
}
|
|
49
|
-
/** Stops tracking one target and clears any active composition it owned. */
|
|
50
234
|
unobserve(target) {
|
|
51
235
|
if (!this.#observedTargets.delete(target)) return;
|
|
52
|
-
|
|
53
|
-
target
|
|
54
|
-
target.removeEventListener("keydown", this.#handleKeydown);
|
|
55
|
-
this.#activeTargets.delete(target);
|
|
236
|
+
this.#release(target);
|
|
237
|
+
if (this.#composition?.target === target) this.#letGo();
|
|
56
238
|
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
57
239
|
}
|
|
58
|
-
/** Releases every listener and clears state so reconnect starts cleanly. */
|
|
59
240
|
disconnect() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
target.removeEventListener("compositionend", this.#handleEnd);
|
|
63
|
-
target.removeEventListener("keydown", this.#handleKeydown);
|
|
64
|
-
}
|
|
241
|
+
this.#connected = false;
|
|
242
|
+
for (const target of this.#observedTargets) this.#release(target);
|
|
65
243
|
this.#observedTargets.clear();
|
|
66
|
-
this.#
|
|
244
|
+
this.#letGo();
|
|
67
245
|
this.#confirmedTarget = null;
|
|
246
|
+
this.#keyComposing = false;
|
|
247
|
+
this.#abandoned = null;
|
|
68
248
|
}
|
|
69
|
-
/** True when lifecycle tracking or the current event reports composition. */
|
|
70
249
|
isComposing(event) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
250
|
+
this.#settle();
|
|
251
|
+
return event?.isComposing === true || this.#composition !== null;
|
|
252
|
+
}
|
|
253
|
+
composingElement() {
|
|
254
|
+
this.#settle();
|
|
255
|
+
return this.#composition?.element ?? null;
|
|
256
|
+
}
|
|
257
|
+
owns(event) {
|
|
258
|
+
const element = this.composingElement();
|
|
259
|
+
return event.isComposing === true || element !== null && element === event.target;
|
|
260
|
+
}
|
|
79
261
|
consumesConfirmedInput(event) {
|
|
80
262
|
const confirmed = this.#confirmedTarget;
|
|
81
263
|
this.#confirmedTarget = null;
|
|
82
264
|
if (confirmed === null || confirmed !== event.target) return false;
|
|
83
265
|
const inputType = event.inputType;
|
|
84
|
-
return !inputType ||
|
|
266
|
+
return !inputType || isCompositionInputType(inputType);
|
|
267
|
+
}
|
|
268
|
+
#release(target) {
|
|
269
|
+
target.removeEventListener("compositionstart", this.#handleStart);
|
|
270
|
+
target.removeEventListener("compositionend", this.#handleEnd);
|
|
271
|
+
target.removeEventListener("keydown", this.#handleKeydown, true);
|
|
272
|
+
target.removeEventListener("beforeinput", this.#handleBeforeInput, true);
|
|
273
|
+
target.removeEventListener("input", this.#handleInput, true);
|
|
274
|
+
target.removeEventListener("blur", this.#handleBlur, true);
|
|
275
|
+
target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
|
|
276
|
+
}
|
|
277
|
+
#letGo() {
|
|
278
|
+
const composition = this.#composition;
|
|
279
|
+
if (composition === null) return;
|
|
280
|
+
if (!holdsFocus(composition.element)) {
|
|
281
|
+
this.#composition = null;
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
queueMicrotask(() => {
|
|
285
|
+
if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
this.#composition = null;
|
|
289
|
+
if (!this.#connected) return;
|
|
290
|
+
this.#abandoned = { element: composition.element, inPlace: true };
|
|
291
|
+
this.#runAbandon();
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
#settle() {
|
|
295
|
+
const composition = this.#composition;
|
|
296
|
+
if (composition === null) return;
|
|
297
|
+
const { target, element, text } = composition;
|
|
298
|
+
if (target instanceof Node && element instanceof Node && !target.contains(element)) {
|
|
299
|
+
this.#composition = null;
|
|
300
|
+
} else if (textOf(element) !== text) {
|
|
301
|
+
this.#abandon(false);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
#abandon(inPlace) {
|
|
305
|
+
this.#owe(inPlace);
|
|
306
|
+
this.#composition = null;
|
|
307
|
+
queueMicrotask(this.#runAbandon);
|
|
308
|
+
}
|
|
309
|
+
#owe(inPlace) {
|
|
310
|
+
const composition = this.#composition;
|
|
311
|
+
if (composition !== null) this.#abandoned = { element: composition.element, inPlace };
|
|
85
312
|
}
|
|
313
|
+
#runAbandon = () => {
|
|
314
|
+
const abandoned = this.#abandoned;
|
|
315
|
+
if (abandoned === null) return;
|
|
316
|
+
this.#abandoned = null;
|
|
317
|
+
this.#onAbandon?.(abandoned.element, abandoned.inPlace);
|
|
318
|
+
};
|
|
86
319
|
#handleStart = (event) => {
|
|
87
320
|
this.#confirmedTarget = null;
|
|
88
|
-
|
|
321
|
+
this.#settle();
|
|
322
|
+
this.#owe(true);
|
|
323
|
+
const target = event.currentTarget;
|
|
324
|
+
const element = event.target;
|
|
325
|
+
this.#composition = { target, element, text: textOf(element) };
|
|
326
|
+
this.#runAbandon();
|
|
89
327
|
this.#onStart?.(event);
|
|
90
328
|
};
|
|
91
329
|
#handleEnd = (event) => {
|
|
92
|
-
|
|
330
|
+
this.#runAbandon();
|
|
331
|
+
if (this.#composition?.target === event.currentTarget) this.#composition = null;
|
|
93
332
|
this.#confirmedTarget = event.target;
|
|
94
333
|
this.#onEnd?.(event);
|
|
95
334
|
};
|
|
96
|
-
|
|
97
|
-
|
|
335
|
+
#handleKeydown = (event) => {
|
|
336
|
+
this.#settle();
|
|
337
|
+
this.#runAbandon();
|
|
98
338
|
this.#confirmedTarget = null;
|
|
339
|
+
this.#keyComposing = event.isComposing === true;
|
|
340
|
+
};
|
|
341
|
+
#handleBeforeInput = (event) => {
|
|
342
|
+
const keyComposing = this.#keyComposing;
|
|
343
|
+
this.#keyComposing = false;
|
|
344
|
+
this.#settle();
|
|
345
|
+
const { isComposing, inputType } = event;
|
|
346
|
+
if (this.#composition !== null && !keyComposing && !isComposing && inputType && !isCompositionInputType(inputType)) {
|
|
347
|
+
this.#abandon(true);
|
|
348
|
+
}
|
|
349
|
+
this.#runAbandon();
|
|
350
|
+
};
|
|
351
|
+
#handleInput = (event) => {
|
|
352
|
+
const composition = this.#composition;
|
|
353
|
+
const { isTrusted, isComposing, inputType } = event;
|
|
354
|
+
if (composition !== null && event.target === composition.element && (isTrusted || isComposing || isCompositionInputType(inputType))) {
|
|
355
|
+
composition.text = textOf(composition.element);
|
|
356
|
+
} else {
|
|
357
|
+
this.#settle();
|
|
358
|
+
}
|
|
359
|
+
this.#runAbandon();
|
|
360
|
+
};
|
|
361
|
+
#handleBlur = (event) => {
|
|
362
|
+
this.#settle();
|
|
363
|
+
this.#runAbandon();
|
|
364
|
+
const composition = this.#composition;
|
|
365
|
+
if (composition === null || composition.target === composition.element) return;
|
|
366
|
+
const { element } = composition;
|
|
367
|
+
if (!(element instanceof Node) || event.target !== element) return;
|
|
368
|
+
const root = element.getRootNode();
|
|
369
|
+
if (root.activeElement !== element) this.#composition = null;
|
|
370
|
+
};
|
|
371
|
+
#handleBeforeMorphAttribute = (event) => {
|
|
372
|
+
this.#settle();
|
|
373
|
+
const composition = this.#composition;
|
|
374
|
+
if (composition?.target !== event.currentTarget || event.target !== composition.element) return;
|
|
375
|
+
const detail = event.detail;
|
|
376
|
+
if (detail?.attributeName === "value") event.preventDefault();
|
|
99
377
|
};
|
|
100
378
|
};
|
|
101
379
|
|
|
102
|
-
// src/utils/field_mirror.ts
|
|
103
380
|
function commitField(target) {
|
|
104
381
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
105
382
|
}
|
|
106
383
|
|
|
107
|
-
|
|
384
|
+
var ListenerSet = class {
|
|
385
|
+
#abort = new AbortController();
|
|
386
|
+
add(target, type, handler, options) {
|
|
387
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
388
|
+
}
|
|
389
|
+
dispose() {
|
|
390
|
+
this.#abort.abort();
|
|
391
|
+
this.#abort = new AbortController();
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
108
395
|
var MicrotaskCoalescer = class {
|
|
109
396
|
#run;
|
|
110
397
|
#queued = false;
|
|
111
398
|
#active = false;
|
|
112
399
|
#generation = 0;
|
|
113
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
114
400
|
constructor(run) {
|
|
115
401
|
this.#run = run;
|
|
116
402
|
}
|
|
117
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
118
403
|
activate() {
|
|
119
404
|
this.#active = true;
|
|
120
405
|
}
|
|
121
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
122
406
|
cancel() {
|
|
123
407
|
this.#active = false;
|
|
124
408
|
this.#queued = false;
|
|
125
409
|
this.#generation += 1;
|
|
126
410
|
}
|
|
127
|
-
|
|
411
|
+
get pending() {
|
|
412
|
+
return this.#queued;
|
|
413
|
+
}
|
|
128
414
|
schedule() {
|
|
129
415
|
if (!this.#active || this.#queued) return;
|
|
130
416
|
this.#queued = true;
|
|
@@ -137,7 +423,17 @@ var MicrotaskCoalescer = class {
|
|
|
137
423
|
}
|
|
138
424
|
};
|
|
139
425
|
|
|
140
|
-
|
|
426
|
+
var MoveCounter = class {
|
|
427
|
+
#sequence = 0;
|
|
428
|
+
record() {
|
|
429
|
+
this.#sequence += 1;
|
|
430
|
+
return this.#sequence;
|
|
431
|
+
}
|
|
432
|
+
isLatest(token) {
|
|
433
|
+
return token === this.#sequence;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
141
437
|
function scrollOptionIntoView(list, option) {
|
|
142
438
|
if (list.scrollHeight <= list.clientHeight) return;
|
|
143
439
|
const listRect = list.getBoundingClientRect();
|
|
@@ -149,16 +445,20 @@ function scrollOptionIntoView(list, option) {
|
|
|
149
445
|
}
|
|
150
446
|
}
|
|
151
447
|
|
|
152
|
-
|
|
153
|
-
|
|
448
|
+
var StateRegions = class _StateRegions {
|
|
449
|
+
static #registry = sharedRegistry(
|
|
450
|
+
"stimeo-ui.state-regions.registry.v1",
|
|
451
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
|
|
452
|
+
);
|
|
154
453
|
#whenTrue;
|
|
155
454
|
#whenFalse;
|
|
156
|
-
#
|
|
157
|
-
constructor(options) {
|
|
455
|
+
#record;
|
|
456
|
+
constructor(options, owner) {
|
|
158
457
|
this.#whenTrue = options.whenTrue;
|
|
159
458
|
this.#whenFalse = options.whenFalse ?? null;
|
|
459
|
+
this.#record = `data-${owner}-hidden-region`;
|
|
460
|
+
_StateRegions.#registry.names.add(this.#record);
|
|
160
461
|
}
|
|
161
|
-
/** Shows the regions inside `host` that belong to `isTrue` and hides the others. */
|
|
162
462
|
reflect(host, isTrue) {
|
|
163
463
|
const shown = this.#inside(host, this.#whenTrue());
|
|
164
464
|
if (!this.#whenFalse) {
|
|
@@ -174,103 +474,199 @@ var StateRegions = class {
|
|
|
174
474
|
for (const region of shown) this.#write(region, !isTrue);
|
|
175
475
|
for (const region of hiddenSide) this.#write(region, isTrue);
|
|
176
476
|
}
|
|
177
|
-
|
|
477
|
+
release(host) {
|
|
478
|
+
for (const region of [host, ...host.querySelectorAll("*")]) {
|
|
479
|
+
if (!(region instanceof HTMLElement)) continue;
|
|
480
|
+
const stack = this.#held(region);
|
|
481
|
+
const hold = stack && this.#take(stack);
|
|
482
|
+
if (!stack || !hold) continue;
|
|
483
|
+
const owns = region.hidden === hold.written && !this.#covered(region, stack, hold);
|
|
484
|
+
const index = stack.indexOf(hold);
|
|
485
|
+
stack.splice(index, 1);
|
|
486
|
+
const above = stack[index];
|
|
487
|
+
if (above) above.linked &&= hold.linked;
|
|
488
|
+
if (owns) {
|
|
489
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
490
|
+
if (below) {
|
|
491
|
+
if (region.hidden !== below.written) region.hidden = below.written;
|
|
492
|
+
} else {
|
|
493
|
+
setAttributeIfChanged(region, "hidden", hold.authored);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
this.#mark(region, stack);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
178
499
|
#write(region, hidden) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
500
|
+
const stack = this.#stack(region);
|
|
501
|
+
const hold = this.#take(stack) ?? this.#acquire(region, stack);
|
|
502
|
+
const shows = !this.#covered(region, stack, hold);
|
|
503
|
+
hold.written = hidden;
|
|
504
|
+
if (shows && region.hidden !== hidden) region.hidden = hidden;
|
|
505
|
+
this.#mark(region, stack);
|
|
182
506
|
}
|
|
183
|
-
/** Returns a region this instance hid; one it never hid keeps what it carries. */
|
|
184
507
|
#give(region) {
|
|
185
|
-
|
|
508
|
+
const stack = this.#held(region);
|
|
509
|
+
if (stack && this.#take(stack)?.written) this.#write(region, false);
|
|
510
|
+
}
|
|
511
|
+
#held(region) {
|
|
512
|
+
return _StateRegions.#registry.stacks.has(region) || region.hasAttribute(this.#record) ? this.#stack(region) : void 0;
|
|
513
|
+
}
|
|
514
|
+
#stack(region) {
|
|
515
|
+
const stacks = _StateRegions.#registry.stacks;
|
|
516
|
+
const stack = stacks.get(region) ?? this.#seed(region);
|
|
517
|
+
stacks.set(region, stack);
|
|
518
|
+
return stack;
|
|
519
|
+
}
|
|
520
|
+
#seed(region) {
|
|
521
|
+
const found = [];
|
|
522
|
+
for (const record of _StateRegions.#registry.names) {
|
|
523
|
+
const read = this.#recorded(region, record);
|
|
524
|
+
if (read) found.push({ ...read, record });
|
|
525
|
+
}
|
|
526
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
527
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, authored, written, linked }, index) => ({
|
|
528
|
+
instance: null,
|
|
529
|
+
record,
|
|
530
|
+
authored,
|
|
531
|
+
written,
|
|
532
|
+
linked: linked && index > 0
|
|
533
|
+
}));
|
|
534
|
+
}
|
|
535
|
+
#take(stack) {
|
|
536
|
+
const hold = stack.find((held) => held.instance === this) ?? stack.find((held) => held.instance === null && held.record === this.#record);
|
|
537
|
+
if (hold) hold.instance = this;
|
|
538
|
+
return hold;
|
|
539
|
+
}
|
|
540
|
+
#covered(region, stack, hold) {
|
|
541
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === region.hidden);
|
|
542
|
+
}
|
|
543
|
+
#acquire(region, stack) {
|
|
544
|
+
const top = stack.at(-1);
|
|
545
|
+
const over = top && region.hidden === top.written ? top : void 0;
|
|
546
|
+
const hold = {
|
|
547
|
+
instance: this,
|
|
548
|
+
record: this.#record,
|
|
549
|
+
authored: over ? over.authored : region.getAttribute("hidden"),
|
|
550
|
+
written: false,
|
|
551
|
+
linked: over !== void 0
|
|
552
|
+
};
|
|
553
|
+
stack.push(hold);
|
|
554
|
+
return hold;
|
|
555
|
+
}
|
|
556
|
+
#mark(region, stack) {
|
|
557
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
558
|
+
stack.forEach((hold, index) => {
|
|
559
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
560
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
561
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
562
|
+
records.set(
|
|
563
|
+
hold.record,
|
|
564
|
+
region.getAttribute("hidden") === hold.authored && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.authored, hold.written, ...marker, ...beneath])
|
|
565
|
+
);
|
|
566
|
+
});
|
|
567
|
+
for (const [name, recorded] of records) {
|
|
568
|
+
setAttributeIfChanged(region, name, recorded);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
#recorded(region, name) {
|
|
572
|
+
try {
|
|
573
|
+
const parsed = JSON.parse(region.getAttribute(name) ?? "null");
|
|
574
|
+
if (!Array.isArray(parsed)) return null;
|
|
575
|
+
const [authored, written, ...rest] = parsed;
|
|
576
|
+
if (authored !== null && typeof authored !== "string") return null;
|
|
577
|
+
const linked = rest[0] !== false;
|
|
578
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
579
|
+
if (!beneath.every((under) => typeof under === "string")) return null;
|
|
580
|
+
return typeof written === "boolean" ? { authored, written, linked, beneath } : null;
|
|
581
|
+
} catch {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
186
584
|
}
|
|
187
|
-
/** The declared regions that sit within `host`, which may be the host itself. */
|
|
188
585
|
#inside(host, regions) {
|
|
189
586
|
return regions.filter((region) => host.contains(region));
|
|
190
587
|
}
|
|
191
588
|
};
|
|
192
589
|
|
|
193
|
-
|
|
590
|
+
function targetSelector(identifier, name) {
|
|
591
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
592
|
+
}
|
|
593
|
+
|
|
194
594
|
var ComboboxController = class extends Controller {
|
|
595
|
+
#moves = new MoveCounter();
|
|
596
|
+
#moveToken = 0;
|
|
195
597
|
static targets = ["input", "list", "option", "empty"];
|
|
196
|
-
static actions = ["close", "filter", "onKeydown", "open", "
|
|
598
|
+
static actions = ["close", "filter", "onKeydown", "open", "select"];
|
|
197
599
|
static events = ["selected"];
|
|
198
|
-
|
|
199
|
-
#emptyRegion = new StateRegions({ whenTrue: () => this.emptyTargets });
|
|
200
|
-
/** Stable ID of the active option; the live element is resolved before every use. */
|
|
600
|
+
#emptyRegion = new StateRegions({ whenTrue: () => this.emptyTargets }, this.identifier);
|
|
201
601
|
#activeId = null;
|
|
202
602
|
#connected = false;
|
|
203
|
-
|
|
603
|
+
#list = null;
|
|
604
|
+
#listHidden = new AttributeLease("hidden", this.identifier);
|
|
605
|
+
#expanded = new AttributeLease("aria-expanded", this.identifier);
|
|
606
|
+
#activeDescendant = new AttributeLease(
|
|
607
|
+
"aria-activedescendant",
|
|
608
|
+
this.identifier
|
|
609
|
+
);
|
|
204
610
|
#reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());
|
|
205
|
-
/**
|
|
206
|
-
* Suppresses {@link ComboboxController.open | open} for the duration of the
|
|
207
|
-
* programmatic re-focus in `#select`, so committing a value (which returns focus
|
|
208
|
-
* to the input) does not immediately re-open the listbox via a `focus`-bound
|
|
209
|
-
* action.
|
|
210
|
-
*/
|
|
211
611
|
#suppressOpen = false;
|
|
212
|
-
/** Owns IME lifecycle state; confirmed text re-filters the list once. */
|
|
213
612
|
#composition = new CompositionTracker({ onEnd: () => this.filter() });
|
|
214
|
-
|
|
613
|
+
#listeners = new ListenerSet();
|
|
215
614
|
connect() {
|
|
216
615
|
if (this.hasInputTarget) this.#composition.observe(this.inputTarget);
|
|
217
616
|
this.close();
|
|
218
|
-
|
|
617
|
+
this.#list = this.hasListTarget ? this.listTarget : null;
|
|
618
|
+
this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
|
|
219
619
|
this.#connected = true;
|
|
220
620
|
this.#reconcile.activate();
|
|
221
621
|
}
|
|
222
|
-
/** Removes the document-level listener registered in {@link connect}. */
|
|
223
622
|
disconnect() {
|
|
224
623
|
this.#connected = false;
|
|
225
624
|
this.#reconcile.cancel();
|
|
226
625
|
this.#composition.disconnect();
|
|
227
|
-
|
|
626
|
+
this.#listeners.dispose();
|
|
228
627
|
}
|
|
229
|
-
/** Tracks an input added initially or after connect. */
|
|
230
628
|
inputTargetConnected(input) {
|
|
231
629
|
this.#composition.observe(input);
|
|
232
630
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
233
631
|
}
|
|
234
|
-
/** Removes composition listeners when the active input is replaced or removed. */
|
|
235
632
|
inputTargetDisconnected(input) {
|
|
236
633
|
this.#composition.unobserve(input);
|
|
634
|
+
if (!this.inputTargets.includes(input)) {
|
|
635
|
+
this.#expanded.return(input);
|
|
636
|
+
this.#activeDescendant.return(input);
|
|
637
|
+
}
|
|
638
|
+
this.#queueOptionReconciliation();
|
|
639
|
+
}
|
|
640
|
+
listTargetConnected() {
|
|
641
|
+
this.#adoptList();
|
|
642
|
+
}
|
|
643
|
+
listTargetDisconnected(list) {
|
|
644
|
+
this.#adoptList();
|
|
645
|
+
if (!this.listTargets.includes(list)) this.#listHidden.return(list);
|
|
237
646
|
}
|
|
238
|
-
/** Settles a connecting region on the side the empty state is on. */
|
|
239
647
|
emptyTargetConnected() {
|
|
240
648
|
this.#reflectEmptyState();
|
|
241
649
|
}
|
|
242
|
-
/** Establishes the active-state baseline, then reconciles a runtime addition. */
|
|
243
650
|
optionTargetConnected(option) {
|
|
244
651
|
option.setAttribute("aria-selected", "false");
|
|
245
652
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
246
653
|
}
|
|
247
|
-
/** Clears the disconnected node's active state, then reconciles the survivors. */
|
|
248
654
|
optionTargetDisconnected(option) {
|
|
249
655
|
option.setAttribute("aria-selected", "false");
|
|
250
656
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
251
657
|
}
|
|
252
|
-
/** Filters confirmed input text and opens the listbox. */
|
|
253
658
|
filter(event) {
|
|
254
659
|
if (this.#composition.isComposing(event)) return;
|
|
255
660
|
this.open();
|
|
256
661
|
}
|
|
257
|
-
/**
|
|
258
|
-
* Opens the listbox, re-filtering the options against the current input value
|
|
259
|
-
* so the visible options and empty state always match what is typed (e.g.
|
|
260
|
-
* re-opening with a stale non-matching value still surfaces the empty state).
|
|
261
|
-
*/
|
|
262
662
|
open() {
|
|
263
663
|
if (!this.hasListTarget || !this.hasInputTarget || this.#suppressOpen) return;
|
|
264
664
|
this.#applyFilter();
|
|
265
|
-
this.listTarget
|
|
266
|
-
this.inputTarget
|
|
665
|
+
this.#listHidden.write(this.listTarget, null);
|
|
666
|
+
this.#expanded.write(this.inputTarget, "true");
|
|
267
667
|
this.#setActive(-1);
|
|
268
668
|
this.#reflectEmptyState();
|
|
269
669
|
}
|
|
270
|
-
/**
|
|
271
|
-
* Hides options that don't match the current input value (case-insensitive
|
|
272
|
-
* substring). An empty query shows every option. Does not change open state.
|
|
273
|
-
*/
|
|
274
670
|
#applyFilter() {
|
|
275
671
|
const query = this.inputTarget.value.trim().toLowerCase();
|
|
276
672
|
for (const option of this.optionTargets) {
|
|
@@ -278,28 +674,18 @@ var ComboboxController = class extends Controller {
|
|
|
278
674
|
option.hidden = query.length > 0 && !text.includes(query);
|
|
279
675
|
}
|
|
280
676
|
}
|
|
281
|
-
/**
|
|
282
|
-
* Closes the listbox, clears the active option, and updates ARIA state.
|
|
283
|
-
*
|
|
284
|
-
* Survives a missing input in both directions. {@link inputTargetConnected}
|
|
285
|
-
* accepts an input added after connect, so dereferencing it here would abort
|
|
286
|
-
* `connect()` before it registers the outside-click listener, and nothing
|
|
287
|
-
* re-registers it later. Symmetrically, an input removed while the popup is
|
|
288
|
-
* open must still let the popup come down — so hiding the list is
|
|
289
|
-
* unconditional and only the ARIA half is guarded.
|
|
290
|
-
*/
|
|
291
677
|
close() {
|
|
292
678
|
if (!this.hasListTarget) return;
|
|
293
|
-
this.listTarget
|
|
679
|
+
this.#listHidden.write(this.listTarget, "");
|
|
294
680
|
this.#reflectEmptyState();
|
|
295
|
-
if (this.hasInputTarget) this.inputTarget
|
|
681
|
+
if (this.hasInputTarget) this.#expanded.write(this.inputTarget, "false");
|
|
296
682
|
this.#setActive(-1);
|
|
297
683
|
}
|
|
298
|
-
/** Routes keyboard interaction per the APG combobox model. */
|
|
299
684
|
onKeydown(event) {
|
|
300
685
|
if (this.#composition.isComposing(event)) return;
|
|
301
686
|
if (event.defaultPrevented) return;
|
|
302
687
|
if (isReservedArrowChord(event, ["alt"])) return;
|
|
688
|
+
if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
|
|
303
689
|
if (event.altKey && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
|
|
304
690
|
if (event.key === "ArrowDown" ? !this.#isClosed : this.#isClosed) return;
|
|
305
691
|
event.preventDefault();
|
|
@@ -368,56 +754,41 @@ var ComboboxController = class extends Controller {
|
|
|
368
754
|
break;
|
|
369
755
|
}
|
|
370
756
|
}
|
|
371
|
-
/**
|
|
372
|
-
* Closes the listbox when a click lands outside the combobox. Mirrors the menu
|
|
373
|
-
* button's outside-click behavior; clicks on an option are inside the element,
|
|
374
|
-
* so `#select` (not this handler) closes the popup after committing.
|
|
375
|
-
*/
|
|
376
757
|
#onOutsideClick = (event) => {
|
|
377
758
|
if (!this.#isClosed && !this.element.contains(event.target)) this.close();
|
|
378
759
|
};
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
* does too: the popup comes down and listeners still hear the choice, with only
|
|
390
|
-
* the field-bound half — the value write, the focus return, and the native
|
|
391
|
-
* `change` — skipped, because there is no field to carry them.
|
|
392
|
-
*/
|
|
393
|
-
#select(option) {
|
|
760
|
+
select(source) {
|
|
761
|
+
const { event, host, origin, reason } = actionSource(source);
|
|
762
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
763
|
+
return;
|
|
764
|
+
const option = host?.closest(targetSelector(this.identifier, "option"));
|
|
765
|
+
if (!option || !this.optionTargets.includes(option)) return;
|
|
766
|
+
const focus = event !== null || this.element.contains(document.activeElement);
|
|
767
|
+
this.#select(option, reason, focus);
|
|
768
|
+
}
|
|
769
|
+
#select(option, reason = "user", focus = true) {
|
|
394
770
|
const value = option.dataset.value ?? (option.textContent ?? "").trim();
|
|
395
771
|
if (!this.hasInputTarget) {
|
|
396
772
|
this.close();
|
|
397
|
-
this.dispatch("selected", { detail: { value } });
|
|
773
|
+
this.dispatch("selected", { detail: { value, reason } });
|
|
398
774
|
return;
|
|
399
775
|
}
|
|
400
776
|
const changed = this.inputTarget.value !== value;
|
|
777
|
+
if (changed) this.#moveToken = this.#moves.record();
|
|
778
|
+
const token = this.#moveToken;
|
|
401
779
|
this.inputTarget.value = value;
|
|
402
780
|
this.close();
|
|
403
781
|
this.#suppressOpen = true;
|
|
404
|
-
this.
|
|
782
|
+
if (focus && (reason !== "api" || this.element.contains(document.activeElement)))
|
|
783
|
+
this.inputTarget.focus();
|
|
405
784
|
this.#suppressOpen = false;
|
|
785
|
+
if (!this.#moves.isLatest(token)) return;
|
|
406
786
|
if (changed) {
|
|
407
787
|
commitField(this.inputTarget);
|
|
408
788
|
}
|
|
409
|
-
this.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
* Writes whether the open listbox has zero matching options to the state hook
|
|
413
|
-
* `data-stimeo--combobox-empty` on the root element and to the regions declared
|
|
414
|
-
* for that state. Behavior only: consumers decide how to present the empty state
|
|
415
|
-
* via CSS keyed off the attribute, while the `empty` targets carry the copy.
|
|
416
|
-
*
|
|
417
|
-
* Every path that can change the answer — opening, filtering, reconciling a
|
|
418
|
-
* runtime option change, and closing — ends here, so the hook and the regions
|
|
419
|
-
* are a pure function of the state rather than of the order things happened in.
|
|
420
|
-
*/
|
|
789
|
+
if (!this.#moves.isLatest(token)) return;
|
|
790
|
+
this.dispatch("selected", { detail: { value, reason } });
|
|
791
|
+
}
|
|
421
792
|
#reflectEmptyState() {
|
|
422
793
|
const empty = !this.#isClosed && this.#visibleOptions().length === 0;
|
|
423
794
|
if (empty) {
|
|
@@ -427,31 +798,31 @@ var ComboboxController = class extends Controller {
|
|
|
427
798
|
}
|
|
428
799
|
this.#emptyRegion.reflect(this.element, empty);
|
|
429
800
|
}
|
|
430
|
-
/**
|
|
431
|
-
* Marks the visible option at `index` active via `aria-selected` and the
|
|
432
|
-
* input's `aria-activedescendant`. Pass `-1` to clear the active option.
|
|
433
|
-
*/
|
|
434
801
|
#setActive(index) {
|
|
435
802
|
const visible = this.#visibleOptions();
|
|
436
803
|
const active = index === -1 ? null : visible[index] ?? null;
|
|
437
804
|
syncActiveOption(this.optionTargets, active);
|
|
438
805
|
this.#activeId = active?.id || null;
|
|
439
|
-
if (this.hasInputTarget)
|
|
440
|
-
if (active?.id) {
|
|
441
|
-
this.inputTarget.setAttribute("aria-activedescendant", active.id);
|
|
442
|
-
} else {
|
|
443
|
-
this.inputTarget.removeAttribute("aria-activedescendant");
|
|
444
|
-
}
|
|
445
|
-
}
|
|
806
|
+
if (this.hasInputTarget) this.#activeDescendant.write(this.inputTarget, active?.id || null);
|
|
446
807
|
if (active && this.hasListTarget) scrollOptionIntoView(this.listTarget, active);
|
|
447
808
|
}
|
|
448
|
-
|
|
809
|
+
#adoptList() {
|
|
810
|
+
if (!this.#connected) return;
|
|
811
|
+
const list = this.hasListTarget ? this.listTarget : null;
|
|
812
|
+
const previous = this.#list;
|
|
813
|
+
if (list === previous) return;
|
|
814
|
+
this.#list = list;
|
|
815
|
+
if (list) this.#listHidden.write(list, previous?.hidden ?? true ? "" : null);
|
|
816
|
+
this.#queueOptionReconciliation();
|
|
817
|
+
}
|
|
449
818
|
#reconcileOptions() {
|
|
450
|
-
if (this.hasInputTarget
|
|
819
|
+
if (this.hasInputTarget) {
|
|
820
|
+
this.#expanded.write(this.inputTarget, String(!this.#isClosed));
|
|
821
|
+
if (!this.#isClosed) this.#applyFilter();
|
|
822
|
+
}
|
|
451
823
|
this.#reconcileActive();
|
|
452
824
|
this.#reflectEmptyState();
|
|
453
825
|
}
|
|
454
|
-
/** Preserves a surviving active ID; disappearance deliberately clears active state. */
|
|
455
826
|
#reconcileActive() {
|
|
456
827
|
if (!this.hasInputTarget || this.#isClosed) {
|
|
457
828
|
this.#setActive(-1);
|
|
@@ -467,26 +838,21 @@ var ComboboxController = class extends Controller {
|
|
|
467
838
|
const stateMatches = active ? this.#activeId === (active.id || null) && selected.length === 1 && selected[0] === active && idref === (active.id || null) : this.#activeId === null && selected.length === 0 && idref === null;
|
|
468
839
|
if (!stateMatches) this.#setActive(index);
|
|
469
840
|
}
|
|
470
|
-
/** Resolves the stable ID, with `aria-selected` as the ID-less compatibility marker. */
|
|
471
841
|
#findActiveIndex(options) {
|
|
472
842
|
const activeId = (this.hasInputTarget ? this.inputTarget.getAttribute("aria-activedescendant") : null) ?? this.#activeId;
|
|
473
843
|
if (activeId) return options.findIndex((option) => option.id === activeId);
|
|
474
844
|
return options.findIndex((option) => option.getAttribute("aria-selected") === "true");
|
|
475
845
|
}
|
|
476
|
-
/** Coalesces all target callbacks from one MutationObserver batch. */
|
|
477
846
|
#queueOptionReconciliation() {
|
|
478
847
|
this.#reconcile.schedule();
|
|
479
848
|
}
|
|
480
|
-
/** The options currently shown (not filtered out). */
|
|
481
849
|
#visibleOptions() {
|
|
482
850
|
return this.optionTargets.filter((option) => !option.hidden);
|
|
483
851
|
}
|
|
484
|
-
/** Whether the listbox is currently hidden. */
|
|
485
852
|
get #isClosed() {
|
|
486
853
|
return !this.hasListTarget || this.listTarget.hidden !== false;
|
|
487
854
|
}
|
|
488
855
|
};
|
|
489
856
|
|
|
490
857
|
export { ComboboxController };
|
|
491
|
-
//# sourceMappingURL=combobox_controller.js.map
|
|
492
858
|
//# sourceMappingURL=combobox_controller.js.map
|