stimeo-ui 0.15.0 → 0.17.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 +209 -0
- package/README.md +128 -1
- package/dist/cable/index.d.ts +79 -23
- package/dist/cable/index.js +392 -417
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +34 -5
- package/dist/controllers/accordion_controller.js +228 -28
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +20 -9
- package/dist/controllers/alert_dialog_controller.js +862 -221
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +38 -7
- package/dist/controllers/announcer_controller.js +135 -191
- 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 +295 -110
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +172 -106
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +0 -129
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +22 -4
- package/dist/controllers/bulk_select_controller.js +264 -53
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +109 -28
- package/dist/controllers/calendar_controller.js +346 -180
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +99 -27
- package/dist/controllers/carousel_controller.js +587 -300
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +18 -2
- package/dist/controllers/character_counter_controller.js +271 -143
- 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 +77 -75
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +44 -7
- package/dist/controllers/clipboard_controller.js +187 -108
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +41 -5
- package/dist/controllers/collapsible_controller.js +374 -108
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +47 -14
- package/dist/controllers/color_picker_controller.js +183 -127
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +33 -6
- package/dist/controllers/combobox_controller.js +426 -109
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +45 -14
- package/dist/controllers/command_palette_controller.js +994 -350
- 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 +212 -125
- 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 +939 -245
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +36 -3
- package/dist/controllers/context_menu_controller.js +275 -127
- 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 +75 -2
- package/dist/controllers/countdown_controller.js +235 -97
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +93 -15
- package/dist/controllers/currency_input_controller.js +241 -118
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +78 -22
- package/dist/controllers/data_grid_controller.js +229 -135
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +64 -11
- package/dist/controllers/date_range_picker_controller.js +359 -165
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +22 -3
- package/dist/controllers/dialog_controller.js +887 -225
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +24 -7
- package/dist/controllers/direct_upload_controller.js +80 -142
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +2 -2
- package/dist/controllers/dirty_form_controller.js +26 -47
- 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 +175 -20
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +52 -16
- package/dist/controllers/drawer_controller.js +963 -336
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +39 -3
- package/dist/controllers/dropdown_controller.js +342 -85
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +182 -58
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +67 -53
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +60 -18
- package/dist/controllers/file_dropzone_controller.js +426 -229
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +10 -4
- package/dist/controllers/filter_controller.js +78 -36
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +57 -14
- package/dist/controllers/flash_controller.js +328 -211
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +12 -3
- package/dist/controllers/focus_controller.js +654 -239
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +153 -136
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +29 -96
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +41 -9
- package/dist/controllers/frame_loading_controller.js +263 -198
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +22 -2
- package/dist/controllers/highlight_controller.js +119 -70
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +65 -4
- package/dist/controllers/hover_card_controller.js +335 -118
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +58 -4
- package/dist/controllers/idle_controller.js +316 -65
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +5 -2
- package/dist/controllers/input_mask_controller.js +116 -61
- 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 +147 -103
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +16 -0
- package/dist/controllers/lazy_frame_controller.js +9 -56
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +84 -10
- package/dist/controllers/listbox_controller.js +413 -153
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +1 -1
- package/dist/controllers/local_time_controller.js +54 -64
- 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 +70 -95
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +40 -9
- package/dist/controllers/menu_controller.js +254 -167
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +15 -4
- package/dist/controllers/menubar_controller.js +206 -331
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +20 -0
- package/dist/controllers/meter_controller.js +159 -71
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +50 -7
- package/dist/controllers/multi_select_controller.js +797 -388
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +19 -1
- package/dist/controllers/navigation_menu_controller.js +156 -231
- 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 +373 -174
- 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 +241 -45
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +62 -10
- package/dist/controllers/number_input_controller.js +387 -227
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +28 -75
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +38 -7
- package/dist/controllers/otp_controller.js +344 -196
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +13 -12
- package/dist/controllers/overflow_indicator_controller.js +167 -93
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +66 -9
- package/dist/controllers/overflow_menu_controller.js +408 -277
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +52 -15
- package/dist/controllers/pagination_controller.js +342 -120
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +60 -9
- package/dist/controllers/password_reveal_controller.js +424 -99
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +14 -5
- package/dist/controllers/password_strength_controller.js +99 -153
- 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 +252 -96
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +19 -7
- package/dist/controllers/pointer_drag_controller.js +263 -164
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +46 -4
- package/dist/controllers/popover_controller.js +267 -94
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +209 -72
- 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 -112
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +11 -0
- package/dist/controllers/progress_controller.js +132 -59
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +19 -8
- package/dist/controllers/radio_group_controller.js +138 -133
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +92 -4
- package/dist/controllers/range_slider_controller.js +245 -112
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +33 -6
- package/dist/controllers/rating_controller.js +429 -194
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +26 -2
- package/dist/controllers/read_more_controller.js +185 -47
- 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 +147 -135
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +18 -4
- package/dist/controllers/relative_time_controller.js +145 -110
- 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} +97 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +44 -4
- package/dist/controllers/resizable_controller.js +294 -118
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.js +39 -84
- 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 +428 -221
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +0 -53
- 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 +395 -146
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +9 -3
- package/dist/controllers/scrollspy_controller.js +181 -236
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +59 -17
- package/dist/controllers/separator_controller.js +288 -132
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +81 -7
- package/dist/controllers/sidebar_controller.js +1071 -375
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +248 -92
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +72 -8
- package/dist/controllers/slider_controller.js +211 -96
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
- package/dist/controllers/smart_sticky_header_controller.js +163 -54
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +13 -110
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +277 -161
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +24 -17
- package/dist/controllers/step_indicator_controller.js +107 -53
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +43 -9
- package/dist/controllers/stepper_controller.js +171 -48
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +25 -0
- package/dist/controllers/stick_to_bottom_controller.js +310 -116
- 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 +214 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +22 -6
- package/dist/controllers/submit_once_controller.js +294 -192
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +40 -7
- package/dist/controllers/switch_controller.js +107 -43
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +13 -1
- package/dist/controllers/tabs_controller.js +50 -25
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +27 -3
- package/dist/controllers/tags_input_controller.js +290 -149
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
- package/dist/controllers/textarea_autosize_controller.js +71 -58
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +37 -8
- package/dist/controllers/theme_controller.js +148 -80
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +18 -1
- package/dist/controllers/time_picker_controller.js +162 -81
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +85 -23
- package/dist/controllers/toast_controller.js +365 -193
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +60 -9
- package/dist/controllers/toggle_group_controller.js +194 -99
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +92 -90
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +52 -2
- package/dist/controllers/tooltip_controller.js +185 -125
- 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 -115
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +59 -18
- package/dist/controllers/tree_view_controller.js +248 -290
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +33 -3
- package/dist/index.js +8649 -8572
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +95 -9
- package/dist/inspector/cli.js +343 -77
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +390 -96
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +45 -45
- package/dist/inspector/manifest.json +1308 -155
- package/dist/positioning/index.d.ts +8 -0
- package/dist/positioning/index.js +148 -45
- package/dist/positioning/index.js.map +1 -1
- package/package.json +7 -7
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -1,8 +1,27 @@
|
|
|
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
|
+
}
|
|
4
24
|
|
|
5
|
-
// src/utils/active_option.ts
|
|
6
25
|
function syncActiveOption(options, active) {
|
|
7
26
|
let written = 0;
|
|
8
27
|
for (const option of options) {
|
|
@@ -14,80 +33,244 @@ function syncActiveOption(options, active) {
|
|
|
14
33
|
return written;
|
|
15
34
|
}
|
|
16
35
|
|
|
17
|
-
// src/utils/arrow_step.ts
|
|
18
36
|
function isReservedArrowChord(event, allow = []) {
|
|
19
37
|
if (!event.key.startsWith("Arrow")) return false;
|
|
20
38
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
21
39
|
}
|
|
22
40
|
|
|
23
|
-
|
|
41
|
+
function sharedRegistry(key, create) {
|
|
42
|
+
const symbol = Symbol.for(key);
|
|
43
|
+
const scope = globalThis;
|
|
44
|
+
const existing = scope[symbol];
|
|
45
|
+
if (existing !== void 0) return existing;
|
|
46
|
+
const registry = create();
|
|
47
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
48
|
+
return registry;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isAttributeValue(value) {
|
|
52
|
+
return value === null || typeof value === "string";
|
|
53
|
+
}
|
|
54
|
+
function readLeaseRecord(element, record) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
57
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
58
|
+
const [original, written, ...rest] = parsed;
|
|
59
|
+
const linked = rest[0] !== false;
|
|
60
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
61
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
62
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
63
|
+
} catch {
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
var AttributeLease = class _AttributeLease {
|
|
68
|
+
static #registry = sharedRegistry(
|
|
69
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
70
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
71
|
+
);
|
|
72
|
+
#attribute;
|
|
73
|
+
#record;
|
|
74
|
+
#base;
|
|
75
|
+
#held = /* @__PURE__ */ new Set();
|
|
76
|
+
constructor(attribute, owner, options = {}) {
|
|
77
|
+
this.#attribute = attribute;
|
|
78
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
79
|
+
this.#base = options.base === true;
|
|
80
|
+
const names = _AttributeLease.#registry.names;
|
|
81
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
82
|
+
}
|
|
83
|
+
write(element, value) {
|
|
84
|
+
const stack = this.#stack(element);
|
|
85
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
86
|
+
this.#held.add(element);
|
|
87
|
+
const shows = !this.#covered(element, stack, hold);
|
|
88
|
+
hold.written = value;
|
|
89
|
+
this.#mark(element, stack);
|
|
90
|
+
if (shows) this.#reflect(element, value);
|
|
91
|
+
}
|
|
92
|
+
return(element) {
|
|
93
|
+
const held = this.#held.delete(element);
|
|
94
|
+
const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
|
|
95
|
+
const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
|
|
96
|
+
const hold = this.#take(stack);
|
|
97
|
+
if (!hold) return false;
|
|
98
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
99
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
100
|
+
const index = stack.indexOf(hold);
|
|
101
|
+
stack.splice(index, 1);
|
|
102
|
+
const above = stack[index];
|
|
103
|
+
if (above) above.linked &&= hold.linked;
|
|
104
|
+
this.#mark(element, stack);
|
|
105
|
+
if (!owns) return true;
|
|
106
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
107
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
returnAll() {
|
|
111
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
112
|
+
}
|
|
113
|
+
#stack(element) {
|
|
114
|
+
const stacks = _AttributeLease.#registry.stacks;
|
|
115
|
+
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
116
|
+
stacks.set(element, byAttribute);
|
|
117
|
+
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
118
|
+
byAttribute.set(this.#attribute, stack);
|
|
119
|
+
return stack;
|
|
120
|
+
}
|
|
121
|
+
#seed(element) {
|
|
122
|
+
const found = [];
|
|
123
|
+
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
124
|
+
const read = readLeaseRecord(element, record);
|
|
125
|
+
if (read) found.push({ ...read, record });
|
|
126
|
+
}
|
|
127
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
128
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
129
|
+
lease: null,
|
|
130
|
+
record,
|
|
131
|
+
written,
|
|
132
|
+
original,
|
|
133
|
+
linked: linked && index > 0
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
#take(stack) {
|
|
137
|
+
const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
|
|
138
|
+
if (hold) hold.lease = this;
|
|
139
|
+
return hold;
|
|
140
|
+
}
|
|
141
|
+
#acquire(element, stack) {
|
|
142
|
+
const current = element.getAttribute(this.#attribute);
|
|
143
|
+
const top = stack.at(-1);
|
|
144
|
+
const bottom = stack[0];
|
|
145
|
+
const over = top?.written === current ? top : void 0;
|
|
146
|
+
const from = this.#base ? bottom : over;
|
|
147
|
+
const hold = {
|
|
148
|
+
lease: this,
|
|
149
|
+
record: this.#record,
|
|
150
|
+
written: current,
|
|
151
|
+
original: from ? from.original : current,
|
|
152
|
+
linked: !this.#base && over !== void 0
|
|
153
|
+
};
|
|
154
|
+
if (this.#base && bottom) {
|
|
155
|
+
bottom.linked = true;
|
|
156
|
+
stack.unshift(hold);
|
|
157
|
+
} else stack.push(hold);
|
|
158
|
+
return hold;
|
|
159
|
+
}
|
|
160
|
+
#covered(element, stack, hold) {
|
|
161
|
+
const current = element.getAttribute(this.#attribute);
|
|
162
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
|
|
163
|
+
}
|
|
164
|
+
#mark(element, stack) {
|
|
165
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
166
|
+
stack.forEach((hold, index) => {
|
|
167
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
168
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
169
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
170
|
+
records.set(
|
|
171
|
+
hold.record,
|
|
172
|
+
hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
for (const [name, recorded] of records) {
|
|
176
|
+
if (element.getAttribute(name) === recorded) continue;
|
|
177
|
+
if (recorded === null) element.removeAttribute(name);
|
|
178
|
+
else element.setAttribute(name, recorded);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
#reflect(element, value) {
|
|
182
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
183
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
184
|
+
else element.setAttribute(this.#attribute, value);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
189
|
+
"insertCompositionText",
|
|
190
|
+
"insertFromComposition",
|
|
191
|
+
"deleteCompositionText",
|
|
192
|
+
"deleteByComposition"
|
|
193
|
+
]);
|
|
24
194
|
var CompositionTracker = class {
|
|
25
195
|
#observedTargets = /* @__PURE__ */ new Set();
|
|
26
196
|
#activeTargets = /* @__PURE__ */ new Set();
|
|
27
197
|
#onStart;
|
|
28
198
|
#onEnd;
|
|
199
|
+
#confirmedTarget = null;
|
|
29
200
|
constructor(options = {}) {
|
|
30
201
|
this.#onStart = options.onStart;
|
|
31
202
|
this.#onEnd = options.onEnd;
|
|
32
203
|
}
|
|
33
|
-
/** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
|
|
34
204
|
observe(target) {
|
|
35
205
|
if (this.#observedTargets.has(target)) return;
|
|
36
206
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
37
207
|
target.addEventListener("compositionend", this.#handleEnd);
|
|
208
|
+
target.addEventListener("keydown", this.#handleKeydown);
|
|
38
209
|
this.#observedTargets.add(target);
|
|
39
210
|
}
|
|
40
|
-
/** Stops tracking one target and clears any active composition it owned. */
|
|
41
211
|
unobserve(target) {
|
|
42
212
|
if (!this.#observedTargets.delete(target)) return;
|
|
43
213
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
44
214
|
target.removeEventListener("compositionend", this.#handleEnd);
|
|
215
|
+
target.removeEventListener("keydown", this.#handleKeydown);
|
|
45
216
|
this.#activeTargets.delete(target);
|
|
217
|
+
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
46
218
|
}
|
|
47
|
-
/** Releases every listener and clears state so reconnect starts cleanly. */
|
|
48
219
|
disconnect() {
|
|
49
220
|
for (const target of this.#observedTargets) {
|
|
50
221
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
51
222
|
target.removeEventListener("compositionend", this.#handleEnd);
|
|
223
|
+
target.removeEventListener("keydown", this.#handleKeydown);
|
|
52
224
|
}
|
|
53
225
|
this.#observedTargets.clear();
|
|
54
226
|
this.#activeTargets.clear();
|
|
227
|
+
this.#confirmedTarget = null;
|
|
55
228
|
}
|
|
56
|
-
/** True when lifecycle tracking or the current event reports composition. */
|
|
57
229
|
isComposing(event) {
|
|
58
230
|
return this.#activeTargets.size > 0 || event?.isComposing === true;
|
|
59
231
|
}
|
|
232
|
+
consumesConfirmedInput(event) {
|
|
233
|
+
const confirmed = this.#confirmedTarget;
|
|
234
|
+
this.#confirmedTarget = null;
|
|
235
|
+
if (confirmed === null || confirmed !== event.target) return false;
|
|
236
|
+
const inputType = event.inputType;
|
|
237
|
+
return !inputType || COMPOSITION_INPUT_TYPES.has(inputType);
|
|
238
|
+
}
|
|
60
239
|
#handleStart = (event) => {
|
|
240
|
+
this.#confirmedTarget = null;
|
|
61
241
|
if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
|
|
62
242
|
this.#onStart?.(event);
|
|
63
243
|
};
|
|
64
244
|
#handleEnd = (event) => {
|
|
65
245
|
if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
|
|
246
|
+
this.#confirmedTarget = event.target;
|
|
66
247
|
this.#onEnd?.(event);
|
|
67
248
|
};
|
|
249
|
+
#handleKeydown = () => {
|
|
250
|
+
this.#confirmedTarget = null;
|
|
251
|
+
};
|
|
68
252
|
};
|
|
69
253
|
|
|
70
|
-
|
|
254
|
+
function commitField(target) {
|
|
255
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
256
|
+
}
|
|
257
|
+
|
|
71
258
|
var MicrotaskCoalescer = class {
|
|
72
259
|
#run;
|
|
73
260
|
#queued = false;
|
|
74
261
|
#active = false;
|
|
75
262
|
#generation = 0;
|
|
76
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
77
263
|
constructor(run) {
|
|
78
264
|
this.#run = run;
|
|
79
265
|
}
|
|
80
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
81
266
|
activate() {
|
|
82
267
|
this.#active = true;
|
|
83
268
|
}
|
|
84
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
85
269
|
cancel() {
|
|
86
270
|
this.#active = false;
|
|
87
271
|
this.#queued = false;
|
|
88
272
|
this.#generation += 1;
|
|
89
273
|
}
|
|
90
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
91
274
|
schedule() {
|
|
92
275
|
if (!this.#active || this.#queued) return;
|
|
93
276
|
this.#queued = true;
|
|
@@ -100,7 +283,17 @@ var MicrotaskCoalescer = class {
|
|
|
100
283
|
}
|
|
101
284
|
};
|
|
102
285
|
|
|
103
|
-
|
|
286
|
+
var MoveCounter = class {
|
|
287
|
+
#sequence = 0;
|
|
288
|
+
record() {
|
|
289
|
+
this.#sequence += 1;
|
|
290
|
+
return this.#sequence;
|
|
291
|
+
}
|
|
292
|
+
isLatest(token) {
|
|
293
|
+
return token === this.#sequence;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
104
297
|
function scrollOptionIntoView(list, option) {
|
|
105
298
|
if (list.scrollHeight <= list.clientHeight) return;
|
|
106
299
|
const listRect = list.getBoundingClientRect();
|
|
@@ -112,80 +305,230 @@ function scrollOptionIntoView(list, option) {
|
|
|
112
305
|
}
|
|
113
306
|
}
|
|
114
307
|
|
|
115
|
-
|
|
308
|
+
var StateRegions = class _StateRegions {
|
|
309
|
+
static #registry = sharedRegistry(
|
|
310
|
+
"stimeo-ui.state-regions.registry.v1",
|
|
311
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
|
|
312
|
+
);
|
|
313
|
+
#whenTrue;
|
|
314
|
+
#whenFalse;
|
|
315
|
+
#record;
|
|
316
|
+
constructor(options, owner) {
|
|
317
|
+
this.#whenTrue = options.whenTrue;
|
|
318
|
+
this.#whenFalse = options.whenFalse ?? null;
|
|
319
|
+
this.#record = `data-${owner}-hidden-region`;
|
|
320
|
+
_StateRegions.#registry.names.add(this.#record);
|
|
321
|
+
}
|
|
322
|
+
reflect(host, isTrue) {
|
|
323
|
+
const shown = this.#inside(host, this.#whenTrue());
|
|
324
|
+
if (!this.#whenFalse) {
|
|
325
|
+
for (const region of shown) this.#write(region, !isTrue);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const hiddenSide = this.#inside(host, this.#whenFalse());
|
|
329
|
+
if (shown.length === 0 || hiddenSide.length === 0) {
|
|
330
|
+
for (const region of shown) this.#give(region);
|
|
331
|
+
for (const region of hiddenSide) this.#give(region);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
for (const region of shown) this.#write(region, !isTrue);
|
|
335
|
+
for (const region of hiddenSide) this.#write(region, isTrue);
|
|
336
|
+
}
|
|
337
|
+
release(host) {
|
|
338
|
+
for (const region of [host, ...host.querySelectorAll("*")]) {
|
|
339
|
+
if (!(region instanceof HTMLElement)) continue;
|
|
340
|
+
const stack = this.#held(region);
|
|
341
|
+
const hold = stack && this.#take(stack);
|
|
342
|
+
if (!stack || !hold) continue;
|
|
343
|
+
const owns = region.hidden === hold.written && !this.#covered(region, stack, hold);
|
|
344
|
+
const index = stack.indexOf(hold);
|
|
345
|
+
stack.splice(index, 1);
|
|
346
|
+
const above = stack[index];
|
|
347
|
+
if (above) above.linked &&= hold.linked;
|
|
348
|
+
if (owns) {
|
|
349
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
350
|
+
if (below) {
|
|
351
|
+
if (region.hidden !== below.written) region.hidden = below.written;
|
|
352
|
+
} else if (region.getAttribute("hidden") !== hold.authored) {
|
|
353
|
+
if (hold.authored === null) region.removeAttribute("hidden");
|
|
354
|
+
else region.setAttribute("hidden", hold.authored);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
this.#mark(region, stack);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
#write(region, hidden) {
|
|
361
|
+
const stack = this.#stack(region);
|
|
362
|
+
const hold = this.#take(stack) ?? this.#acquire(region, stack);
|
|
363
|
+
const shows = !this.#covered(region, stack, hold);
|
|
364
|
+
hold.written = hidden;
|
|
365
|
+
if (shows && region.hidden !== hidden) region.hidden = hidden;
|
|
366
|
+
this.#mark(region, stack);
|
|
367
|
+
}
|
|
368
|
+
#give(region) {
|
|
369
|
+
const stack = this.#held(region);
|
|
370
|
+
if (stack && this.#take(stack)?.written) this.#write(region, false);
|
|
371
|
+
}
|
|
372
|
+
#held(region) {
|
|
373
|
+
return _StateRegions.#registry.stacks.has(region) || region.hasAttribute(this.#record) ? this.#stack(region) : void 0;
|
|
374
|
+
}
|
|
375
|
+
#stack(region) {
|
|
376
|
+
const stacks = _StateRegions.#registry.stacks;
|
|
377
|
+
const stack = stacks.get(region) ?? this.#seed(region);
|
|
378
|
+
stacks.set(region, stack);
|
|
379
|
+
return stack;
|
|
380
|
+
}
|
|
381
|
+
#seed(region) {
|
|
382
|
+
const found = [];
|
|
383
|
+
for (const record of _StateRegions.#registry.names) {
|
|
384
|
+
const read = this.#recorded(region, record);
|
|
385
|
+
if (read) found.push({ ...read, record });
|
|
386
|
+
}
|
|
387
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
388
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, authored, written, linked }, index) => ({
|
|
389
|
+
instance: null,
|
|
390
|
+
record,
|
|
391
|
+
authored,
|
|
392
|
+
written,
|
|
393
|
+
linked: linked && index > 0
|
|
394
|
+
}));
|
|
395
|
+
}
|
|
396
|
+
#take(stack) {
|
|
397
|
+
const hold = stack.find((held) => held.instance === this) ?? stack.find((held) => held.instance === null && held.record === this.#record);
|
|
398
|
+
if (hold) hold.instance = this;
|
|
399
|
+
return hold;
|
|
400
|
+
}
|
|
401
|
+
#covered(region, stack, hold) {
|
|
402
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === region.hidden);
|
|
403
|
+
}
|
|
404
|
+
#acquire(region, stack) {
|
|
405
|
+
const top = stack.at(-1);
|
|
406
|
+
const over = top && region.hidden === top.written ? top : void 0;
|
|
407
|
+
const hold = {
|
|
408
|
+
instance: this,
|
|
409
|
+
record: this.#record,
|
|
410
|
+
authored: over ? over.authored : region.getAttribute("hidden"),
|
|
411
|
+
written: false,
|
|
412
|
+
linked: over !== void 0
|
|
413
|
+
};
|
|
414
|
+
stack.push(hold);
|
|
415
|
+
return hold;
|
|
416
|
+
}
|
|
417
|
+
#mark(region, stack) {
|
|
418
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
419
|
+
stack.forEach((hold, index) => {
|
|
420
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
421
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
422
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
423
|
+
records.set(
|
|
424
|
+
hold.record,
|
|
425
|
+
region.getAttribute("hidden") === hold.authored && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.authored, hold.written, ...marker, ...beneath])
|
|
426
|
+
);
|
|
427
|
+
});
|
|
428
|
+
for (const [name, recorded] of records) {
|
|
429
|
+
if (region.getAttribute(name) === recorded) continue;
|
|
430
|
+
if (recorded === null) region.removeAttribute(name);
|
|
431
|
+
else region.setAttribute(name, recorded);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
#recorded(region, name) {
|
|
435
|
+
try {
|
|
436
|
+
const parsed = JSON.parse(region.getAttribute(name) ?? "null");
|
|
437
|
+
if (!Array.isArray(parsed)) return null;
|
|
438
|
+
const [authored, written, ...rest] = parsed;
|
|
439
|
+
if (authored !== null && typeof authored !== "string") return null;
|
|
440
|
+
const linked = rest[0] !== false;
|
|
441
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
442
|
+
if (!beneath.every((under) => typeof under === "string")) return null;
|
|
443
|
+
return typeof written === "boolean" ? { authored, written, linked, beneath } : null;
|
|
444
|
+
} catch {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
#inside(host, regions) {
|
|
449
|
+
return regions.filter((region) => host.contains(region));
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
function targetSelector(identifier, name) {
|
|
454
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
455
|
+
}
|
|
456
|
+
|
|
116
457
|
var ComboboxController = class extends Controller {
|
|
117
|
-
|
|
118
|
-
|
|
458
|
+
#moves = new MoveCounter();
|
|
459
|
+
#moveToken = 0;
|
|
460
|
+
static targets = ["input", "list", "option", "empty"];
|
|
461
|
+
static actions = ["close", "filter", "onKeydown", "open", "select"];
|
|
119
462
|
static events = ["selected"];
|
|
120
|
-
|
|
463
|
+
#emptyRegion = new StateRegions({ whenTrue: () => this.emptyTargets }, this.identifier);
|
|
121
464
|
#activeId = null;
|
|
122
465
|
#connected = false;
|
|
123
|
-
|
|
466
|
+
#list = null;
|
|
467
|
+
#listHidden = new AttributeLease("hidden", this.identifier);
|
|
468
|
+
#expanded = new AttributeLease("aria-expanded", this.identifier);
|
|
469
|
+
#activeDescendant = new AttributeLease(
|
|
470
|
+
"aria-activedescendant",
|
|
471
|
+
this.identifier
|
|
472
|
+
);
|
|
124
473
|
#reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());
|
|
125
|
-
/**
|
|
126
|
-
* Suppresses {@link open} for the duration of the programmatic re-focus in
|
|
127
|
-
* `#select`, so committing a value (which returns focus to the input)
|
|
128
|
-
* does not immediately re-open the listbox via a `focus`-bound action.
|
|
129
|
-
*/
|
|
130
474
|
#suppressOpen = false;
|
|
131
|
-
/** Owns IME lifecycle state; confirmed text re-filters the list once. */
|
|
132
475
|
#composition = new CompositionTracker({ onEnd: () => this.filter() });
|
|
133
|
-
/** Starts closed with no active option and registers the outside-click listener. */
|
|
134
476
|
connect() {
|
|
135
477
|
if (this.hasInputTarget) this.#composition.observe(this.inputTarget);
|
|
136
478
|
this.close();
|
|
479
|
+
this.#list = this.hasListTarget ? this.listTarget : null;
|
|
137
480
|
document.addEventListener("click", this.#onOutsideClick, true);
|
|
138
481
|
this.#connected = true;
|
|
139
482
|
this.#reconcile.activate();
|
|
140
483
|
}
|
|
141
|
-
/** Removes the document-level listener registered in {@link connect}. */
|
|
142
484
|
disconnect() {
|
|
143
485
|
this.#connected = false;
|
|
144
486
|
this.#reconcile.cancel();
|
|
145
487
|
this.#composition.disconnect();
|
|
146
488
|
document.removeEventListener("click", this.#onOutsideClick, true);
|
|
147
489
|
}
|
|
148
|
-
/** Tracks an input added initially or after connect. */
|
|
149
490
|
inputTargetConnected(input) {
|
|
150
491
|
this.#composition.observe(input);
|
|
151
492
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
152
493
|
}
|
|
153
|
-
/** Removes composition listeners when the active input is replaced or removed. */
|
|
154
494
|
inputTargetDisconnected(input) {
|
|
155
495
|
this.#composition.unobserve(input);
|
|
496
|
+
if (!this.inputTargets.includes(input)) {
|
|
497
|
+
this.#expanded.return(input);
|
|
498
|
+
this.#activeDescendant.return(input);
|
|
499
|
+
}
|
|
500
|
+
this.#queueOptionReconciliation();
|
|
501
|
+
}
|
|
502
|
+
listTargetConnected() {
|
|
503
|
+
this.#adoptList();
|
|
504
|
+
}
|
|
505
|
+
listTargetDisconnected(list) {
|
|
506
|
+
this.#adoptList();
|
|
507
|
+
if (!this.listTargets.includes(list)) this.#listHidden.return(list);
|
|
508
|
+
}
|
|
509
|
+
emptyTargetConnected() {
|
|
510
|
+
this.#reflectEmptyState();
|
|
156
511
|
}
|
|
157
|
-
/** Establishes the active-state baseline, then reconciles a runtime addition. */
|
|
158
512
|
optionTargetConnected(option) {
|
|
159
513
|
option.setAttribute("aria-selected", "false");
|
|
160
514
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
161
515
|
}
|
|
162
|
-
/** Clears the disconnected node's active state, then reconciles the survivors. */
|
|
163
516
|
optionTargetDisconnected(option) {
|
|
164
517
|
option.setAttribute("aria-selected", "false");
|
|
165
518
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
166
519
|
}
|
|
167
|
-
/** Filters confirmed input text and opens the listbox. */
|
|
168
520
|
filter(event) {
|
|
169
521
|
if (this.#composition.isComposing(event)) return;
|
|
170
522
|
this.open();
|
|
171
523
|
}
|
|
172
|
-
/**
|
|
173
|
-
* Opens the listbox, re-filtering the options against the current input value
|
|
174
|
-
* so the visible options and empty state always match what is typed (e.g.
|
|
175
|
-
* re-opening with a stale non-matching value still surfaces the empty state).
|
|
176
|
-
*/
|
|
177
524
|
open() {
|
|
178
525
|
if (!this.hasListTarget || !this.hasInputTarget || this.#suppressOpen) return;
|
|
179
526
|
this.#applyFilter();
|
|
180
|
-
this.listTarget
|
|
181
|
-
this.inputTarget
|
|
527
|
+
this.#listHidden.write(this.listTarget, null);
|
|
528
|
+
this.#expanded.write(this.inputTarget, "true");
|
|
182
529
|
this.#setActive(-1);
|
|
183
530
|
this.#reflectEmptyState();
|
|
184
531
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Hides options that don't match the current input value (case-insensitive
|
|
187
|
-
* substring). An empty query shows every option. Does not change open state.
|
|
188
|
-
*/
|
|
189
532
|
#applyFilter() {
|
|
190
533
|
const query = this.inputTarget.value.trim().toLowerCase();
|
|
191
534
|
for (const option of this.optionTargets) {
|
|
@@ -193,24 +536,13 @@ var ComboboxController = class extends Controller {
|
|
|
193
536
|
option.hidden = query.length > 0 && !text.includes(query);
|
|
194
537
|
}
|
|
195
538
|
}
|
|
196
|
-
/**
|
|
197
|
-
* Closes the listbox, clears the active option, and updates ARIA state.
|
|
198
|
-
*
|
|
199
|
-
* Survives a missing input in both directions. {@link inputTargetConnected}
|
|
200
|
-
* accepts an input added after connect, so dereferencing it here would abort
|
|
201
|
-
* `connect()` before it registers the outside-click listener, and nothing
|
|
202
|
-
* re-registers it later. Symmetrically, an input removed while the popup is
|
|
203
|
-
* open must still let the popup come down — so hiding the list is
|
|
204
|
-
* unconditional and only the ARIA half is guarded.
|
|
205
|
-
*/
|
|
206
539
|
close() {
|
|
207
540
|
if (!this.hasListTarget) return;
|
|
208
|
-
this.listTarget
|
|
209
|
-
this
|
|
210
|
-
if (this.hasInputTarget) this.inputTarget
|
|
541
|
+
this.#listHidden.write(this.listTarget, "");
|
|
542
|
+
this.#reflectEmptyState();
|
|
543
|
+
if (this.hasInputTarget) this.#expanded.write(this.inputTarget, "false");
|
|
211
544
|
this.#setActive(-1);
|
|
212
545
|
}
|
|
213
|
-
/** Routes keyboard interaction per the APG combobox model. */
|
|
214
546
|
onKeydown(event) {
|
|
215
547
|
if (this.#composition.isComposing(event)) return;
|
|
216
548
|
if (event.defaultPrevented) return;
|
|
@@ -283,85 +615,75 @@ var ComboboxController = class extends Controller {
|
|
|
283
615
|
break;
|
|
284
616
|
}
|
|
285
617
|
}
|
|
286
|
-
/**
|
|
287
|
-
* Closes the listbox when a click lands outside the combobox. Mirrors the menu
|
|
288
|
-
* button's outside-click behavior; clicks on an option are inside the element,
|
|
289
|
-
* so `#select` (not this handler) closes the popup after committing.
|
|
290
|
-
*/
|
|
291
618
|
#onOutsideClick = (event) => {
|
|
292
619
|
if (!this.#isClosed && !this.element.contains(event.target)) this.close();
|
|
293
620
|
};
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
* comes down and listeners still hear the choice, with only the field-bound
|
|
305
|
-
* half — the value write, the focus return, and the native `change` — skipped,
|
|
306
|
-
* because there is no field to carry them.
|
|
307
|
-
*/
|
|
308
|
-
#select(option) {
|
|
621
|
+
select(source) {
|
|
622
|
+
const { event, host, origin, reason } = actionSource(source);
|
|
623
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
624
|
+
return;
|
|
625
|
+
const option = host?.closest(targetSelector(this.identifier, "option"));
|
|
626
|
+
if (!option || !this.optionTargets.includes(option)) return;
|
|
627
|
+
const focus = event !== null || this.element.contains(document.activeElement);
|
|
628
|
+
this.#select(option, reason, focus);
|
|
629
|
+
}
|
|
630
|
+
#select(option, reason = "user", focus = true) {
|
|
309
631
|
const value = option.dataset.value ?? (option.textContent ?? "").trim();
|
|
310
632
|
if (!this.hasInputTarget) {
|
|
311
633
|
this.close();
|
|
312
|
-
this.dispatch("selected", { detail: { value } });
|
|
634
|
+
this.dispatch("selected", { detail: { value, reason } });
|
|
313
635
|
return;
|
|
314
636
|
}
|
|
315
637
|
const changed = this.inputTarget.value !== value;
|
|
638
|
+
if (changed) this.#moveToken = this.#moves.record();
|
|
639
|
+
const token = this.#moveToken;
|
|
316
640
|
this.inputTarget.value = value;
|
|
317
641
|
this.close();
|
|
318
642
|
this.#suppressOpen = true;
|
|
319
|
-
this.
|
|
643
|
+
if (focus && (reason !== "api" || this.element.contains(document.activeElement)))
|
|
644
|
+
this.inputTarget.focus();
|
|
320
645
|
this.#suppressOpen = false;
|
|
646
|
+
if (!this.#moves.isLatest(token)) return;
|
|
321
647
|
if (changed) {
|
|
322
|
-
this.inputTarget
|
|
648
|
+
commitField(this.inputTarget);
|
|
323
649
|
}
|
|
324
|
-
this.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
* Reflects whether the open listbox currently has zero matching options by
|
|
328
|
-
* toggling `data-stimeo--combobox-empty` on the root element. Behavior only:
|
|
329
|
-
* consumers decide how to present the empty state (hide the list, show a
|
|
330
|
-
* "no results" node, etc.) via CSS keyed off this attribute.
|
|
331
|
-
*/
|
|
650
|
+
if (!this.#moves.isLatest(token)) return;
|
|
651
|
+
this.dispatch("selected", { detail: { value, reason } });
|
|
652
|
+
}
|
|
332
653
|
#reflectEmptyState() {
|
|
333
654
|
const empty = !this.#isClosed && this.#visibleOptions().length === 0;
|
|
334
655
|
if (empty) {
|
|
335
|
-
this.element.setAttribute(
|
|
656
|
+
this.element.setAttribute(`data-${this.identifier}-empty`, "");
|
|
336
657
|
} else {
|
|
337
|
-
this.element.removeAttribute(
|
|
658
|
+
this.element.removeAttribute(`data-${this.identifier}-empty`);
|
|
338
659
|
}
|
|
660
|
+
this.#emptyRegion.reflect(this.element, empty);
|
|
339
661
|
}
|
|
340
|
-
/**
|
|
341
|
-
* Marks the visible option at `index` active via `aria-selected` and the
|
|
342
|
-
* input's `aria-activedescendant`. Pass `-1` to clear the active option.
|
|
343
|
-
*/
|
|
344
662
|
#setActive(index) {
|
|
345
663
|
const visible = this.#visibleOptions();
|
|
346
664
|
const active = index === -1 ? null : visible[index] ?? null;
|
|
347
665
|
syncActiveOption(this.optionTargets, active);
|
|
348
666
|
this.#activeId = active?.id || null;
|
|
349
|
-
if (this.hasInputTarget)
|
|
350
|
-
if (active?.id) {
|
|
351
|
-
this.inputTarget.setAttribute("aria-activedescendant", active.id);
|
|
352
|
-
} else {
|
|
353
|
-
this.inputTarget.removeAttribute("aria-activedescendant");
|
|
354
|
-
}
|
|
355
|
-
}
|
|
667
|
+
if (this.hasInputTarget) this.#activeDescendant.write(this.inputTarget, active?.id || null);
|
|
356
668
|
if (active && this.hasListTarget) scrollOptionIntoView(this.listTarget, active);
|
|
357
669
|
}
|
|
358
|
-
|
|
670
|
+
#adoptList() {
|
|
671
|
+
if (!this.#connected) return;
|
|
672
|
+
const list = this.hasListTarget ? this.listTarget : null;
|
|
673
|
+
const previous = this.#list;
|
|
674
|
+
if (list === previous) return;
|
|
675
|
+
this.#list = list;
|
|
676
|
+
if (list) this.#listHidden.write(list, previous?.hidden ?? true ? "" : null);
|
|
677
|
+
this.#queueOptionReconciliation();
|
|
678
|
+
}
|
|
359
679
|
#reconcileOptions() {
|
|
360
|
-
if (this.hasInputTarget
|
|
680
|
+
if (this.hasInputTarget) {
|
|
681
|
+
this.#expanded.write(this.inputTarget, String(!this.#isClosed));
|
|
682
|
+
if (!this.#isClosed) this.#applyFilter();
|
|
683
|
+
}
|
|
361
684
|
this.#reconcileActive();
|
|
362
685
|
this.#reflectEmptyState();
|
|
363
686
|
}
|
|
364
|
-
/** Preserves a surviving active ID; disappearance deliberately clears active state. */
|
|
365
687
|
#reconcileActive() {
|
|
366
688
|
if (!this.hasInputTarget || this.#isClosed) {
|
|
367
689
|
this.#setActive(-1);
|
|
@@ -377,26 +699,21 @@ var ComboboxController = class extends Controller {
|
|
|
377
699
|
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;
|
|
378
700
|
if (!stateMatches) this.#setActive(index);
|
|
379
701
|
}
|
|
380
|
-
/** Resolves the stable ID, with `aria-selected` as the ID-less compatibility marker. */
|
|
381
702
|
#findActiveIndex(options) {
|
|
382
703
|
const activeId = (this.hasInputTarget ? this.inputTarget.getAttribute("aria-activedescendant") : null) ?? this.#activeId;
|
|
383
704
|
if (activeId) return options.findIndex((option) => option.id === activeId);
|
|
384
705
|
return options.findIndex((option) => option.getAttribute("aria-selected") === "true");
|
|
385
706
|
}
|
|
386
|
-
/** Coalesces all target callbacks from one MutationObserver batch. */
|
|
387
707
|
#queueOptionReconciliation() {
|
|
388
708
|
this.#reconcile.schedule();
|
|
389
709
|
}
|
|
390
|
-
/** The options currently shown (not filtered out). */
|
|
391
710
|
#visibleOptions() {
|
|
392
711
|
return this.optionTargets.filter((option) => !option.hidden);
|
|
393
712
|
}
|
|
394
|
-
/** Whether the listbox is currently hidden. */
|
|
395
713
|
get #isClosed() {
|
|
396
714
|
return !this.hasListTarget || this.listTarget.hidden !== false;
|
|
397
715
|
}
|
|
398
716
|
};
|
|
399
717
|
|
|
400
718
|
export { ComboboxController };
|
|
401
|
-
//# sourceMappingURL=combobox_controller.js.map
|
|
402
719
|
//# sourceMappingURL=combobox_controller.js.map
|