stimeo-ui 0.16.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 +31 -0
- package/README.md +8 -1
- package/dist/cable/index.d.ts +79 -23
- package/dist/cable/index.js +363 -436
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +4 -2
- package/dist/controllers/accordion_controller.js +166 -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 +862 -221
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +35 -5
- package/dist/controllers/announcer_controller.js +135 -198
- 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 +271 -127
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +171 -105
- 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 +18 -0
- package/dist/controllers/bulk_select_controller.js +262 -52
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +24 -5
- package/dist/controllers/calendar_controller.js +175 -209
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +34 -7
- package/dist/controllers/carousel_controller.js +461 -399
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +17 -1
- package/dist/controllers/character_counter_controller.js +246 -156
- 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 +32 -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 +168 -147
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +16 -4
- package/dist/controllers/collapsible_controller.js +301 -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 +149 -139
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +23 -5
- package/dist/controllers/combobox_controller.js +360 -133
- 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 +970 -358
- 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 +166 -124
- 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 -248
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +31 -4
- package/dist/controllers/context_menu_controller.js +256 -128
- 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 +45 -1
- package/dist/controllers/countdown_controller.js +183 -148
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +21 -2
- package/dist/controllers/currency_input_controller.js +134 -158
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +15 -4
- package/dist/controllers/data_grid_controller.js +114 -186
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +30 -5
- package/dist/controllers/date_range_picker_controller.js +288 -206
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +13 -1
- package/dist/controllers/dialog_controller.js +858 -223
- 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 +73 -153
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.js +14 -48
- 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 -21
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +39 -11
- package/dist/controllers/drawer_controller.js +933 -341
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +31 -1
- package/dist/controllers/dropdown_controller.js +312 -82
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +158 -68
- 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 +311 -207
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.js +67 -39
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +31 -8
- package/dist/controllers/flash_controller.js +172 -314
- 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 -240
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +147 -132
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +13 -86
- 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 +264 -213
- 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 +107 -75
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +56 -3
- package/dist/controllers/hover_card_controller.js +309 -118
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +42 -1
- package/dist/controllers/idle_controller.js +267 -101
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.js +71 -72
- 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 -106
- 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 -65
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +34 -2
- package/dist/controllers/listbox_controller.js +306 -204
- 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 -93
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +32 -7
- package/dist/controllers/menu_controller.js +214 -156
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +4 -4
- package/dist/controllers/menubar_controller.js +170 -329
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +20 -0
- package/dist/controllers/meter_controller.js +151 -67
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +32 -4
- package/dist/controllers/multi_select_controller.js +593 -358
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +8 -1
- package/dist/controllers/navigation_menu_controller.js +124 -232
- 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 +352 -182
- 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 +242 -54
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +26 -2
- package/dist/controllers/number_input_controller.js +320 -263
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +16 -76
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +20 -4
- package/dist/controllers/otp_controller.js +198 -193
- 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 +116 -113
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +48 -11
- package/dist/controllers/overflow_menu_controller.js +343 -283
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +35 -6
- package/dist/controllers/pagination_controller.js +295 -119
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +45 -8
- package/dist/controllers/password_reveal_controller.js +389 -121
- 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 +237 -103
- 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 +185 -165
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +38 -2
- package/dist/controllers/popover_controller.js +236 -97
- 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 -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 +232 -116
- 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 +125 -56
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +14 -5
- package/dist/controllers/radio_group_controller.js +117 -137
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +43 -3
- package/dist/controllers/range_slider_controller.js +192 -105
- 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 +418 -207
- 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 +134 -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 +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 +28 -2
- package/dist/controllers/resizable_controller.js +255 -139
- 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 -225
- 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 -145
- 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 +181 -242
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +19 -4
- package/dist/controllers/separator_controller.js +267 -140
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +64 -7
- package/dist/controllers/sidebar_controller.js +1023 -386
- 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 -97
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +27 -1
- package/dist/controllers/slider_controller.js +159 -79
- 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 +162 -63
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +0 -112
- 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 -169
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +5 -0
- package/dist/controllers/step_indicator_controller.js +99 -46
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +12 -3
- package/dist/controllers/stepper_controller.js +114 -73
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +6 -0
- package/dist/controllers/stick_to_bottom_controller.js +262 -138
- 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 +23 -8
- package/dist/controllers/submit_once_controller.js +294 -205
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +12 -1
- package/dist/controllers/switch_controller.js +33 -60
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +2 -2
- package/dist/controllers/tabs_controller.js +35 -29
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +12 -0
- package/dist/controllers/tags_input_controller.js +136 -145
- 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 +21 -9
- package/dist/controllers/theme_controller.js +130 -112
- 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 +146 -80
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +36 -11
- package/dist/controllers/toast_controller.js +186 -297
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +12 -3
- package/dist/controllers/toggle_group_controller.js +95 -136
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +68 -98
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +45 -3
- 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 +21 -11
- package/dist/controllers/tree_view_controller.js +150 -345
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6370 -9346
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +23 -3
- package/dist/inspector/cli.js +92 -37
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +92 -56
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +8 -8
- package/dist/inspector/manifest.json +787 -100
- package/dist/positioning/index.d.ts +8 -0
- package/dist/positioning/index.js +148 -47
- package/dist/positioning/index.js.map +1 -1
- package/package.json +6 -6
- 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,13 +33,158 @@ 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
|
+
|
|
24
188
|
var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
25
189
|
"insertCompositionText",
|
|
26
190
|
"insertFromComposition",
|
|
@@ -32,13 +196,11 @@ var CompositionTracker = class {
|
|
|
32
196
|
#activeTargets = /* @__PURE__ */ new Set();
|
|
33
197
|
#onStart;
|
|
34
198
|
#onEnd;
|
|
35
|
-
/** The field whose confirming `input` is still owed, while the window is open. */
|
|
36
199
|
#confirmedTarget = null;
|
|
37
200
|
constructor(options = {}) {
|
|
38
201
|
this.#onStart = options.onStart;
|
|
39
202
|
this.#onEnd = options.onEnd;
|
|
40
203
|
}
|
|
41
|
-
/** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
|
|
42
204
|
observe(target) {
|
|
43
205
|
if (this.#observedTargets.has(target)) return;
|
|
44
206
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
@@ -46,7 +208,6 @@ var CompositionTracker = class {
|
|
|
46
208
|
target.addEventListener("keydown", this.#handleKeydown);
|
|
47
209
|
this.#observedTargets.add(target);
|
|
48
210
|
}
|
|
49
|
-
/** Stops tracking one target and clears any active composition it owned. */
|
|
50
211
|
unobserve(target) {
|
|
51
212
|
if (!this.#observedTargets.delete(target)) return;
|
|
52
213
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
@@ -55,7 +216,6 @@ var CompositionTracker = class {
|
|
|
55
216
|
this.#activeTargets.delete(target);
|
|
56
217
|
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
57
218
|
}
|
|
58
|
-
/** Releases every listener and clears state so reconnect starts cleanly. */
|
|
59
219
|
disconnect() {
|
|
60
220
|
for (const target of this.#observedTargets) {
|
|
61
221
|
target.removeEventListener("compositionstart", this.#handleStart);
|
|
@@ -66,16 +226,9 @@ var CompositionTracker = class {
|
|
|
66
226
|
this.#activeTargets.clear();
|
|
67
227
|
this.#confirmedTarget = null;
|
|
68
228
|
}
|
|
69
|
-
/** True when lifecycle tracking or the current event reports composition. */
|
|
70
229
|
isComposing(event) {
|
|
71
230
|
return this.#activeTargets.size > 0 || event?.isComposing === true;
|
|
72
231
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Whether `event` is the `input` echoing the composition just confirmed.
|
|
75
|
-
*
|
|
76
|
-
* Asking closes the window either way, so one confirmation is folded at most
|
|
77
|
-
* once and a consumer asks once per `input`.
|
|
78
|
-
*/
|
|
79
232
|
consumesConfirmedInput(event) {
|
|
80
233
|
const confirmed = this.#confirmedTarget;
|
|
81
234
|
this.#confirmedTarget = null;
|
|
@@ -93,38 +246,31 @@ var CompositionTracker = class {
|
|
|
93
246
|
this.#confirmedTarget = event.target;
|
|
94
247
|
this.#onEnd?.(event);
|
|
95
248
|
};
|
|
96
|
-
/** A key on an observed field opens an edit of its own, so no echo is owed. */
|
|
97
249
|
#handleKeydown = () => {
|
|
98
250
|
this.#confirmedTarget = null;
|
|
99
251
|
};
|
|
100
252
|
};
|
|
101
253
|
|
|
102
|
-
// src/utils/field_mirror.ts
|
|
103
254
|
function commitField(target) {
|
|
104
255
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
105
256
|
}
|
|
106
257
|
|
|
107
|
-
// src/utils/microtask_coalescer.ts
|
|
108
258
|
var MicrotaskCoalescer = class {
|
|
109
259
|
#run;
|
|
110
260
|
#queued = false;
|
|
111
261
|
#active = false;
|
|
112
262
|
#generation = 0;
|
|
113
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
114
263
|
constructor(run) {
|
|
115
264
|
this.#run = run;
|
|
116
265
|
}
|
|
117
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
118
266
|
activate() {
|
|
119
267
|
this.#active = true;
|
|
120
268
|
}
|
|
121
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
122
269
|
cancel() {
|
|
123
270
|
this.#active = false;
|
|
124
271
|
this.#queued = false;
|
|
125
272
|
this.#generation += 1;
|
|
126
273
|
}
|
|
127
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
128
274
|
schedule() {
|
|
129
275
|
if (!this.#active || this.#queued) return;
|
|
130
276
|
this.#queued = true;
|
|
@@ -137,7 +283,17 @@ var MicrotaskCoalescer = class {
|
|
|
137
283
|
}
|
|
138
284
|
};
|
|
139
285
|
|
|
140
|
-
|
|
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
|
+
|
|
141
297
|
function scrollOptionIntoView(list, option) {
|
|
142
298
|
if (list.scrollHeight <= list.clientHeight) return;
|
|
143
299
|
const listRect = list.getBoundingClientRect();
|
|
@@ -149,16 +305,20 @@ function scrollOptionIntoView(list, option) {
|
|
|
149
305
|
}
|
|
150
306
|
}
|
|
151
307
|
|
|
152
|
-
|
|
153
|
-
|
|
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
|
+
);
|
|
154
313
|
#whenTrue;
|
|
155
314
|
#whenFalse;
|
|
156
|
-
#
|
|
157
|
-
constructor(options) {
|
|
315
|
+
#record;
|
|
316
|
+
constructor(options, owner) {
|
|
158
317
|
this.#whenTrue = options.whenTrue;
|
|
159
318
|
this.#whenFalse = options.whenFalse ?? null;
|
|
319
|
+
this.#record = `data-${owner}-hidden-region`;
|
|
320
|
+
_StateRegions.#registry.names.add(this.#record);
|
|
160
321
|
}
|
|
161
|
-
/** Shows the regions inside `host` that belong to `isTrue` and hides the others. */
|
|
162
322
|
reflect(host, isTrue) {
|
|
163
323
|
const shown = this.#inside(host, this.#whenTrue());
|
|
164
324
|
if (!this.#whenFalse) {
|
|
@@ -174,103 +334,201 @@ var StateRegions = class {
|
|
|
174
334
|
for (const region of shown) this.#write(region, !isTrue);
|
|
175
335
|
for (const region of hiddenSide) this.#write(region, isTrue);
|
|
176
336
|
}
|
|
177
|
-
|
|
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
|
+
}
|
|
178
360
|
#write(region, hidden) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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);
|
|
182
367
|
}
|
|
183
|
-
/** Returns a region this instance hid; one it never hid keeps what it carries. */
|
|
184
368
|
#give(region) {
|
|
185
|
-
|
|
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
|
+
}
|
|
186
447
|
}
|
|
187
|
-
/** The declared regions that sit within `host`, which may be the host itself. */
|
|
188
448
|
#inside(host, regions) {
|
|
189
449
|
return regions.filter((region) => host.contains(region));
|
|
190
450
|
}
|
|
191
451
|
};
|
|
192
452
|
|
|
193
|
-
|
|
453
|
+
function targetSelector(identifier, name) {
|
|
454
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
455
|
+
}
|
|
456
|
+
|
|
194
457
|
var ComboboxController = class extends Controller {
|
|
458
|
+
#moves = new MoveCounter();
|
|
459
|
+
#moveToken = 0;
|
|
195
460
|
static targets = ["input", "list", "option", "empty"];
|
|
196
|
-
static actions = ["close", "filter", "onKeydown", "open", "
|
|
461
|
+
static actions = ["close", "filter", "onKeydown", "open", "select"];
|
|
197
462
|
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. */
|
|
463
|
+
#emptyRegion = new StateRegions({ whenTrue: () => this.emptyTargets }, this.identifier);
|
|
201
464
|
#activeId = null;
|
|
202
465
|
#connected = false;
|
|
203
|
-
|
|
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
|
+
);
|
|
204
473
|
#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
474
|
#suppressOpen = false;
|
|
212
|
-
/** Owns IME lifecycle state; confirmed text re-filters the list once. */
|
|
213
475
|
#composition = new CompositionTracker({ onEnd: () => this.filter() });
|
|
214
|
-
/** Starts closed with no active option and registers the outside-click listener. */
|
|
215
476
|
connect() {
|
|
216
477
|
if (this.hasInputTarget) this.#composition.observe(this.inputTarget);
|
|
217
478
|
this.close();
|
|
479
|
+
this.#list = this.hasListTarget ? this.listTarget : null;
|
|
218
480
|
document.addEventListener("click", this.#onOutsideClick, true);
|
|
219
481
|
this.#connected = true;
|
|
220
482
|
this.#reconcile.activate();
|
|
221
483
|
}
|
|
222
|
-
/** Removes the document-level listener registered in {@link connect}. */
|
|
223
484
|
disconnect() {
|
|
224
485
|
this.#connected = false;
|
|
225
486
|
this.#reconcile.cancel();
|
|
226
487
|
this.#composition.disconnect();
|
|
227
488
|
document.removeEventListener("click", this.#onOutsideClick, true);
|
|
228
489
|
}
|
|
229
|
-
/** Tracks an input added initially or after connect. */
|
|
230
490
|
inputTargetConnected(input) {
|
|
231
491
|
this.#composition.observe(input);
|
|
232
492
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
233
493
|
}
|
|
234
|
-
/** Removes composition listeners when the active input is replaced or removed. */
|
|
235
494
|
inputTargetDisconnected(input) {
|
|
236
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);
|
|
237
508
|
}
|
|
238
|
-
/** Settles a connecting region on the side the empty state is on. */
|
|
239
509
|
emptyTargetConnected() {
|
|
240
510
|
this.#reflectEmptyState();
|
|
241
511
|
}
|
|
242
|
-
/** Establishes the active-state baseline, then reconciles a runtime addition. */
|
|
243
512
|
optionTargetConnected(option) {
|
|
244
513
|
option.setAttribute("aria-selected", "false");
|
|
245
514
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
246
515
|
}
|
|
247
|
-
/** Clears the disconnected node's active state, then reconciles the survivors. */
|
|
248
516
|
optionTargetDisconnected(option) {
|
|
249
517
|
option.setAttribute("aria-selected", "false");
|
|
250
518
|
if (this.#connected) this.#queueOptionReconciliation();
|
|
251
519
|
}
|
|
252
|
-
/** Filters confirmed input text and opens the listbox. */
|
|
253
520
|
filter(event) {
|
|
254
521
|
if (this.#composition.isComposing(event)) return;
|
|
255
522
|
this.open();
|
|
256
523
|
}
|
|
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
524
|
open() {
|
|
263
525
|
if (!this.hasListTarget || !this.hasInputTarget || this.#suppressOpen) return;
|
|
264
526
|
this.#applyFilter();
|
|
265
|
-
this.listTarget
|
|
266
|
-
this.inputTarget
|
|
527
|
+
this.#listHidden.write(this.listTarget, null);
|
|
528
|
+
this.#expanded.write(this.inputTarget, "true");
|
|
267
529
|
this.#setActive(-1);
|
|
268
530
|
this.#reflectEmptyState();
|
|
269
531
|
}
|
|
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
532
|
#applyFilter() {
|
|
275
533
|
const query = this.inputTarget.value.trim().toLowerCase();
|
|
276
534
|
for (const option of this.optionTargets) {
|
|
@@ -278,24 +536,13 @@ var ComboboxController = class extends Controller {
|
|
|
278
536
|
option.hidden = query.length > 0 && !text.includes(query);
|
|
279
537
|
}
|
|
280
538
|
}
|
|
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
539
|
close() {
|
|
292
540
|
if (!this.hasListTarget) return;
|
|
293
|
-
this.listTarget
|
|
541
|
+
this.#listHidden.write(this.listTarget, "");
|
|
294
542
|
this.#reflectEmptyState();
|
|
295
|
-
if (this.hasInputTarget) this.inputTarget
|
|
543
|
+
if (this.hasInputTarget) this.#expanded.write(this.inputTarget, "false");
|
|
296
544
|
this.#setActive(-1);
|
|
297
545
|
}
|
|
298
|
-
/** Routes keyboard interaction per the APG combobox model. */
|
|
299
546
|
onKeydown(event) {
|
|
300
547
|
if (this.#composition.isComposing(event)) return;
|
|
301
548
|
if (event.defaultPrevented) return;
|
|
@@ -368,56 +615,41 @@ var ComboboxController = class extends Controller {
|
|
|
368
615
|
break;
|
|
369
616
|
}
|
|
370
617
|
}
|
|
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
618
|
#onOutsideClick = (event) => {
|
|
377
619
|
if (!this.#isClosed && !this.element.contains(event.target)) this.close();
|
|
378
620
|
};
|
|
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) {
|
|
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) {
|
|
394
631
|
const value = option.dataset.value ?? (option.textContent ?? "").trim();
|
|
395
632
|
if (!this.hasInputTarget) {
|
|
396
633
|
this.close();
|
|
397
|
-
this.dispatch("selected", { detail: { value } });
|
|
634
|
+
this.dispatch("selected", { detail: { value, reason } });
|
|
398
635
|
return;
|
|
399
636
|
}
|
|
400
637
|
const changed = this.inputTarget.value !== value;
|
|
638
|
+
if (changed) this.#moveToken = this.#moves.record();
|
|
639
|
+
const token = this.#moveToken;
|
|
401
640
|
this.inputTarget.value = value;
|
|
402
641
|
this.close();
|
|
403
642
|
this.#suppressOpen = true;
|
|
404
|
-
this.
|
|
643
|
+
if (focus && (reason !== "api" || this.element.contains(document.activeElement)))
|
|
644
|
+
this.inputTarget.focus();
|
|
405
645
|
this.#suppressOpen = false;
|
|
646
|
+
if (!this.#moves.isLatest(token)) return;
|
|
406
647
|
if (changed) {
|
|
407
648
|
commitField(this.inputTarget);
|
|
408
649
|
}
|
|
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
|
-
*/
|
|
650
|
+
if (!this.#moves.isLatest(token)) return;
|
|
651
|
+
this.dispatch("selected", { detail: { value, reason } });
|
|
652
|
+
}
|
|
421
653
|
#reflectEmptyState() {
|
|
422
654
|
const empty = !this.#isClosed && this.#visibleOptions().length === 0;
|
|
423
655
|
if (empty) {
|
|
@@ -427,31 +659,31 @@ var ComboboxController = class extends Controller {
|
|
|
427
659
|
}
|
|
428
660
|
this.#emptyRegion.reflect(this.element, empty);
|
|
429
661
|
}
|
|
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
662
|
#setActive(index) {
|
|
435
663
|
const visible = this.#visibleOptions();
|
|
436
664
|
const active = index === -1 ? null : visible[index] ?? null;
|
|
437
665
|
syncActiveOption(this.optionTargets, active);
|
|
438
666
|
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
|
-
}
|
|
667
|
+
if (this.hasInputTarget) this.#activeDescendant.write(this.inputTarget, active?.id || null);
|
|
446
668
|
if (active && this.hasListTarget) scrollOptionIntoView(this.listTarget, active);
|
|
447
669
|
}
|
|
448
|
-
|
|
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
|
+
}
|
|
449
679
|
#reconcileOptions() {
|
|
450
|
-
if (this.hasInputTarget
|
|
680
|
+
if (this.hasInputTarget) {
|
|
681
|
+
this.#expanded.write(this.inputTarget, String(!this.#isClosed));
|
|
682
|
+
if (!this.#isClosed) this.#applyFilter();
|
|
683
|
+
}
|
|
451
684
|
this.#reconcileActive();
|
|
452
685
|
this.#reflectEmptyState();
|
|
453
686
|
}
|
|
454
|
-
/** Preserves a surviving active ID; disappearance deliberately clears active state. */
|
|
455
687
|
#reconcileActive() {
|
|
456
688
|
if (!this.hasInputTarget || this.#isClosed) {
|
|
457
689
|
this.#setActive(-1);
|
|
@@ -467,26 +699,21 @@ var ComboboxController = class extends Controller {
|
|
|
467
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;
|
|
468
700
|
if (!stateMatches) this.#setActive(index);
|
|
469
701
|
}
|
|
470
|
-
/** Resolves the stable ID, with `aria-selected` as the ID-less compatibility marker. */
|
|
471
702
|
#findActiveIndex(options) {
|
|
472
703
|
const activeId = (this.hasInputTarget ? this.inputTarget.getAttribute("aria-activedescendant") : null) ?? this.#activeId;
|
|
473
704
|
if (activeId) return options.findIndex((option) => option.id === activeId);
|
|
474
705
|
return options.findIndex((option) => option.getAttribute("aria-selected") === "true");
|
|
475
706
|
}
|
|
476
|
-
/** Coalesces all target callbacks from one MutationObserver batch. */
|
|
477
707
|
#queueOptionReconciliation() {
|
|
478
708
|
this.#reconcile.schedule();
|
|
479
709
|
}
|
|
480
|
-
/** The options currently shown (not filtered out). */
|
|
481
710
|
#visibleOptions() {
|
|
482
711
|
return this.optionTargets.filter((option) => !option.hidden);
|
|
483
712
|
}
|
|
484
|
-
/** Whether the listbox is currently hidden. */
|
|
485
713
|
get #isClosed() {
|
|
486
714
|
return !this.hasListTarget || this.listTarget.hidden !== false;
|
|
487
715
|
}
|
|
488
716
|
};
|
|
489
717
|
|
|
490
718
|
export { ComboboxController };
|
|
491
|
-
//# sourceMappingURL=combobox_controller.js.map
|
|
492
719
|
//# sourceMappingURL=combobox_controller.js.map
|