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,34 +1,215 @@
|
|
|
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/arrow_step.ts
|
|
6
25
|
function isReservedArrowChord(event, allow = []) {
|
|
7
26
|
if (!event.key.startsWith("Arrow")) return false;
|
|
8
27
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
9
28
|
}
|
|
10
29
|
|
|
11
|
-
|
|
30
|
+
function sharedRegistry(key, create) {
|
|
31
|
+
const symbol = Symbol.for(key);
|
|
32
|
+
const scope = globalThis;
|
|
33
|
+
const existing = scope[symbol];
|
|
34
|
+
if (existing !== void 0) return existing;
|
|
35
|
+
const registry = create();
|
|
36
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
37
|
+
return registry;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isAttributeValue(value) {
|
|
41
|
+
return value === null || typeof value === "string";
|
|
42
|
+
}
|
|
43
|
+
function readLeaseRecord(element, record) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
46
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
47
|
+
const [original, written, ...rest] = parsed;
|
|
48
|
+
const linked = rest[0] !== false;
|
|
49
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
50
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
51
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
52
|
+
} catch {
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
var AttributeLease = class _AttributeLease {
|
|
57
|
+
static #registry = sharedRegistry(
|
|
58
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
59
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
60
|
+
);
|
|
61
|
+
#attribute;
|
|
62
|
+
#record;
|
|
63
|
+
#base;
|
|
64
|
+
#held = /* @__PURE__ */ new Set();
|
|
65
|
+
constructor(attribute, owner, options = {}) {
|
|
66
|
+
this.#attribute = attribute;
|
|
67
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
68
|
+
this.#base = options.base === true;
|
|
69
|
+
const names = _AttributeLease.#registry.names;
|
|
70
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
71
|
+
}
|
|
72
|
+
write(element, value) {
|
|
73
|
+
const stack = this.#stack(element);
|
|
74
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
75
|
+
this.#held.add(element);
|
|
76
|
+
const shows = !this.#covered(element, stack, hold);
|
|
77
|
+
hold.written = value;
|
|
78
|
+
this.#mark(element, stack);
|
|
79
|
+
if (shows) this.#reflect(element, value);
|
|
80
|
+
}
|
|
81
|
+
return(element) {
|
|
82
|
+
const held = this.#held.delete(element);
|
|
83
|
+
const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
|
|
84
|
+
const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
|
|
85
|
+
const hold = this.#take(stack);
|
|
86
|
+
if (!hold) return false;
|
|
87
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
88
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
89
|
+
const index = stack.indexOf(hold);
|
|
90
|
+
stack.splice(index, 1);
|
|
91
|
+
const above = stack[index];
|
|
92
|
+
if (above) above.linked &&= hold.linked;
|
|
93
|
+
this.#mark(element, stack);
|
|
94
|
+
if (!owns) return true;
|
|
95
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
96
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
returnAll() {
|
|
100
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
101
|
+
}
|
|
102
|
+
#stack(element) {
|
|
103
|
+
const stacks = _AttributeLease.#registry.stacks;
|
|
104
|
+
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
105
|
+
stacks.set(element, byAttribute);
|
|
106
|
+
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
107
|
+
byAttribute.set(this.#attribute, stack);
|
|
108
|
+
return stack;
|
|
109
|
+
}
|
|
110
|
+
#seed(element) {
|
|
111
|
+
const found = [];
|
|
112
|
+
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
113
|
+
const read = readLeaseRecord(element, record);
|
|
114
|
+
if (read) found.push({ ...read, record });
|
|
115
|
+
}
|
|
116
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
117
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
118
|
+
lease: null,
|
|
119
|
+
record,
|
|
120
|
+
written,
|
|
121
|
+
original,
|
|
122
|
+
linked: linked && index > 0
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
#take(stack) {
|
|
126
|
+
const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
|
|
127
|
+
if (hold) hold.lease = this;
|
|
128
|
+
return hold;
|
|
129
|
+
}
|
|
130
|
+
#acquire(element, stack) {
|
|
131
|
+
const current = element.getAttribute(this.#attribute);
|
|
132
|
+
const top = stack.at(-1);
|
|
133
|
+
const bottom = stack[0];
|
|
134
|
+
const over = top?.written === current ? top : void 0;
|
|
135
|
+
const from = this.#base ? bottom : over;
|
|
136
|
+
const hold = {
|
|
137
|
+
lease: this,
|
|
138
|
+
record: this.#record,
|
|
139
|
+
written: current,
|
|
140
|
+
original: from ? from.original : current,
|
|
141
|
+
linked: !this.#base && over !== void 0
|
|
142
|
+
};
|
|
143
|
+
if (this.#base && bottom) {
|
|
144
|
+
bottom.linked = true;
|
|
145
|
+
stack.unshift(hold);
|
|
146
|
+
} else stack.push(hold);
|
|
147
|
+
return hold;
|
|
148
|
+
}
|
|
149
|
+
#covered(element, stack, hold) {
|
|
150
|
+
const current = element.getAttribute(this.#attribute);
|
|
151
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
|
|
152
|
+
}
|
|
153
|
+
#mark(element, stack) {
|
|
154
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
155
|
+
stack.forEach((hold, index) => {
|
|
156
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
157
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
158
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
159
|
+
records.set(
|
|
160
|
+
hold.record,
|
|
161
|
+
hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
for (const [name, recorded] of records) {
|
|
165
|
+
if (element.getAttribute(name) === recorded) continue;
|
|
166
|
+
if (recorded === null) element.removeAttribute(name);
|
|
167
|
+
else element.setAttribute(name, recorded);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
#reflect(element, value) {
|
|
171
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
172
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
173
|
+
else element.setAttribute(this.#attribute, value);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
function writeField(field, value) {
|
|
178
|
+
if (field.value === value) return false;
|
|
179
|
+
field.value = value;
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
function commitField(target) {
|
|
183
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
var ListenerSet = class {
|
|
187
|
+
#abort = new AbortController();
|
|
188
|
+
add(target, type, handler, options) {
|
|
189
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
190
|
+
}
|
|
191
|
+
dispose() {
|
|
192
|
+
this.#abort.abort();
|
|
193
|
+
this.#abort = new AbortController();
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
12
197
|
var MicrotaskCoalescer = class {
|
|
13
198
|
#run;
|
|
14
199
|
#queued = false;
|
|
15
200
|
#active = false;
|
|
16
201
|
#generation = 0;
|
|
17
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
18
202
|
constructor(run) {
|
|
19
203
|
this.#run = run;
|
|
20
204
|
}
|
|
21
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
22
205
|
activate() {
|
|
23
206
|
this.#active = true;
|
|
24
207
|
}
|
|
25
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
26
208
|
cancel() {
|
|
27
209
|
this.#active = false;
|
|
28
210
|
this.#queued = false;
|
|
29
211
|
this.#generation += 1;
|
|
30
212
|
}
|
|
31
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
32
213
|
schedule() {
|
|
33
214
|
if (!this.#active || this.#queued) return;
|
|
34
215
|
this.#queued = true;
|
|
@@ -41,7 +222,42 @@ var MicrotaskCoalescer = class {
|
|
|
41
222
|
}
|
|
42
223
|
};
|
|
43
224
|
|
|
44
|
-
|
|
225
|
+
var MorphRenderWatcher = class {
|
|
226
|
+
#listeners = new ListenerSet();
|
|
227
|
+
#pass;
|
|
228
|
+
#element = null;
|
|
229
|
+
#onMorph = () => this.#pass.schedule();
|
|
230
|
+
constructor(run) {
|
|
231
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
232
|
+
}
|
|
233
|
+
observe(element) {
|
|
234
|
+
if (this.#element === element) return;
|
|
235
|
+
this.disconnect();
|
|
236
|
+
this.#element = element;
|
|
237
|
+
this.#pass.activate();
|
|
238
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
239
|
+
}
|
|
240
|
+
schedule() {
|
|
241
|
+
this.#pass.schedule();
|
|
242
|
+
}
|
|
243
|
+
disconnect() {
|
|
244
|
+
this.#listeners.dispose();
|
|
245
|
+
this.#pass.cancel();
|
|
246
|
+
this.#element = null;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
var MoveCounter = class {
|
|
251
|
+
#sequence = 0;
|
|
252
|
+
record() {
|
|
253
|
+
this.#sequence += 1;
|
|
254
|
+
return this.#sequence;
|
|
255
|
+
}
|
|
256
|
+
isLatest(token) {
|
|
257
|
+
return token === this.#sequence;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
45
261
|
function scrollOptionIntoView(list, option) {
|
|
46
262
|
if (list.scrollHeight <= list.clientHeight) return;
|
|
47
263
|
const listRect = list.getBoundingClientRect();
|
|
@@ -53,43 +269,28 @@ function scrollOptionIntoView(list, option) {
|
|
|
53
269
|
}
|
|
54
270
|
}
|
|
55
271
|
|
|
56
|
-
|
|
272
|
+
function targetSelector(identifier, name) {
|
|
273
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
274
|
+
}
|
|
275
|
+
|
|
57
276
|
var TimerRegistry = class {
|
|
58
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
59
277
|
ids = /* @__PURE__ */ new Set();
|
|
60
|
-
/**
|
|
61
|
-
* Cancels a single tracked timer.
|
|
62
|
-
*
|
|
63
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
64
|
-
* registry), so callers can clear defensively without guarding.
|
|
65
|
-
*/
|
|
66
278
|
clear(id) {
|
|
67
279
|
if (this.ids.delete(id)) {
|
|
68
280
|
this.cancel(id);
|
|
69
281
|
}
|
|
70
282
|
}
|
|
71
|
-
/**
|
|
72
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
73
|
-
* to guarantee no timer outlives the element.
|
|
74
|
-
*/
|
|
75
283
|
clearAll() {
|
|
76
284
|
for (const id of this.ids) {
|
|
77
285
|
this.cancel(id);
|
|
78
286
|
}
|
|
79
287
|
this.ids.clear();
|
|
80
288
|
}
|
|
81
|
-
/** Number of timers currently tracked (pending). */
|
|
82
289
|
get size() {
|
|
83
290
|
return this.ids.size;
|
|
84
291
|
}
|
|
85
292
|
};
|
|
86
293
|
var SafeTimeout = class extends TimerRegistry {
|
|
87
|
-
/**
|
|
88
|
-
* Schedules `callback` after `delay` ms and returns the timer id.
|
|
89
|
-
*
|
|
90
|
-
* The id is removed from the registry automatically when the timeout fires,
|
|
91
|
-
* so {@link TimerRegistry.size | size} reflects only still-pending timers.
|
|
92
|
-
*/
|
|
93
294
|
set(callback, delay) {
|
|
94
295
|
const id = this.schedule(() => {
|
|
95
296
|
this.ids.delete(id);
|
|
@@ -106,29 +307,18 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
106
307
|
}
|
|
107
308
|
};
|
|
108
309
|
|
|
109
|
-
// src/utils/typeahead.ts
|
|
110
310
|
var TYPEAHEAD_RESET_MS = 500;
|
|
111
311
|
var Typeahead = class {
|
|
112
|
-
/** Timer registry for the pending idle reset; private so `reset()` is the only exit. */
|
|
113
312
|
#timers = new SafeTimeout();
|
|
114
|
-
/** Idle window before the query resets, in milliseconds. */
|
|
115
313
|
#resetMs;
|
|
116
|
-
/** The accumulated lowercase query, empty when idle. */
|
|
117
314
|
#query = "";
|
|
118
|
-
/** Id of the pending reset timer, `0` when none is scheduled. */
|
|
119
315
|
#timerId = 0;
|
|
120
|
-
/** @param options - Overrides for the idle window. */
|
|
121
316
|
constructor({ resetMs = TYPEAHEAD_RESET_MS } = {}) {
|
|
122
317
|
this.#resetMs = resetMs;
|
|
123
318
|
}
|
|
124
|
-
/** The query a search would currently run with; empty while idle. */
|
|
125
319
|
get query() {
|
|
126
320
|
return this.#query;
|
|
127
321
|
}
|
|
128
|
-
/**
|
|
129
|
-
* Folds `key` into the query, restarts the idle window, and returns the query to
|
|
130
|
-
* search with. A repeated character collapses the query to that one character.
|
|
131
|
-
*/
|
|
132
322
|
push(key) {
|
|
133
323
|
const char = key.toLowerCase();
|
|
134
324
|
const repeated = this.#query.length > 0 && [...this.#query].every((c) => c === char);
|
|
@@ -137,7 +327,6 @@ var Typeahead = class {
|
|
|
137
327
|
this.#timerId = this.#timers.set(() => this.reset(), this.#resetMs);
|
|
138
328
|
return this.#query;
|
|
139
329
|
}
|
|
140
|
-
/** Clears the query and cancels the pending idle reset. */
|
|
141
330
|
reset() {
|
|
142
331
|
this.#query = "";
|
|
143
332
|
this.#timers.clear(this.#timerId);
|
|
@@ -164,83 +353,129 @@ function findTypeaheadMatch(items, from, query, label = (item) => typeaheadLabel
|
|
|
164
353
|
return -1;
|
|
165
354
|
}
|
|
166
355
|
|
|
167
|
-
|
|
356
|
+
var OBSERVED_ATTRIBUTES = ["aria-selected", "data-value"];
|
|
357
|
+
function setAttributeIfChanged(element, name, value) {
|
|
358
|
+
if (element.getAttribute(name) !== value) element.setAttribute(name, value);
|
|
359
|
+
}
|
|
168
360
|
var ListboxController = class extends Controller {
|
|
361
|
+
#moves = new MoveCounter();
|
|
169
362
|
static targets = ["trigger", "value", "list", "option", "field"];
|
|
363
|
+
static values = {
|
|
364
|
+
placeholder: { type: String, default: "" }
|
|
365
|
+
};
|
|
170
366
|
static actions = ["close", "onTriggerKeydown", "open", "select", "toggle"];
|
|
171
|
-
static events = ["change"];
|
|
172
|
-
/** Stable ID of the active option; DOM targets are resolved afresh before use. */
|
|
367
|
+
static events = ["change", "reconcile"];
|
|
173
368
|
#activeId = null;
|
|
174
|
-
/** Target ID order captured while an option is active, used only for removal fallback. */
|
|
175
369
|
#activeOrder = [];
|
|
176
|
-
#
|
|
177
|
-
|
|
178
|
-
#
|
|
179
|
-
|
|
370
|
+
#reconcile = new MorphRenderWatcher(() => this.#reconcileOptions());
|
|
371
|
+
#observer = null;
|
|
372
|
+
#settled = { option: null, value: "" };
|
|
373
|
+
#shownList = null;
|
|
374
|
+
#listHidden = new AttributeLease("hidden", this.identifier);
|
|
375
|
+
#expanded = new AttributeLease("aria-expanded", this.identifier);
|
|
376
|
+
#activeDescendant = new AttributeLease(
|
|
377
|
+
"aria-activedescendant",
|
|
378
|
+
this.identifier
|
|
379
|
+
);
|
|
180
380
|
#typeahead = new Typeahead();
|
|
181
|
-
/** Establishes the ARIA baseline, starts closed, and listens for outside clicks. */
|
|
182
381
|
connect() {
|
|
183
382
|
this.#normalizeSelection();
|
|
184
383
|
this.close();
|
|
185
384
|
document.addEventListener("click", this.#onOutsideClick, true);
|
|
186
|
-
this.#
|
|
187
|
-
this.#reconcile.
|
|
385
|
+
this.#settled = this.#selection();
|
|
386
|
+
this.#reconcile.observe(this.element);
|
|
387
|
+
this.#observeOptions();
|
|
188
388
|
}
|
|
189
|
-
/**
|
|
190
|
-
* Establishes an inactive baseline for a late option, re-resolves active
|
|
191
|
-
* identity, and re-applies the selection baseline.
|
|
192
|
-
*/
|
|
193
389
|
optionTargetConnected(option) {
|
|
194
390
|
option.removeAttribute("data-active");
|
|
195
|
-
|
|
196
|
-
this.#normalizeSelection();
|
|
197
|
-
this.#queueOptionReconciliation();
|
|
391
|
+
this.#reconcile.schedule();
|
|
198
392
|
}
|
|
199
|
-
/** Removes controller-owned active state and reconciles the surviving targets. */
|
|
200
393
|
optionTargetDisconnected(option) {
|
|
201
394
|
option.removeAttribute("data-active");
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
395
|
+
this.#reconcile.schedule();
|
|
396
|
+
}
|
|
397
|
+
triggerTargetConnected() {
|
|
398
|
+
this.#reconcile.schedule();
|
|
399
|
+
}
|
|
400
|
+
triggerTargetDisconnected(trigger) {
|
|
401
|
+
if (!this.triggerTargets.includes(trigger)) {
|
|
402
|
+
this.#expanded.return(trigger);
|
|
403
|
+
this.#activeDescendant.return(trigger);
|
|
404
|
+
}
|
|
405
|
+
this.#reconcile.schedule();
|
|
406
|
+
}
|
|
407
|
+
listTargetConnected() {
|
|
408
|
+
this.#reconcile.schedule();
|
|
409
|
+
}
|
|
410
|
+
listTargetDisconnected(list) {
|
|
411
|
+
if (!this.listTargets.includes(list)) {
|
|
412
|
+
if (list === this.#shownList) this.#adoptPageClose();
|
|
413
|
+
this.#listHidden.return(list);
|
|
414
|
+
}
|
|
415
|
+
this.#reconcile.schedule();
|
|
416
|
+
}
|
|
417
|
+
fieldTargetConnected() {
|
|
418
|
+
this.#reconcile.schedule();
|
|
419
|
+
}
|
|
420
|
+
fieldTargetDisconnected() {
|
|
421
|
+
this.#reconcile.schedule();
|
|
422
|
+
}
|
|
423
|
+
valueTargetConnected() {
|
|
424
|
+
this.#reconcile.schedule();
|
|
425
|
+
}
|
|
426
|
+
valueTargetDisconnected() {
|
|
427
|
+
this.#reconcile.schedule();
|
|
428
|
+
}
|
|
429
|
+
placeholderValueChanged() {
|
|
430
|
+
this.#reconcile.schedule();
|
|
431
|
+
}
|
|
224
432
|
#normalizeSelection() {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
433
|
+
const selected = this.optionTargets.find(
|
|
434
|
+
(option) => option.getAttribute("aria-selected") === "true"
|
|
435
|
+
);
|
|
436
|
+
this.#applySelection(selected);
|
|
437
|
+
}
|
|
438
|
+
#reconcileOptions() {
|
|
439
|
+
this.#normalizeSelection();
|
|
440
|
+
this.#reconcileOpenState();
|
|
441
|
+
this.#reconcileActive();
|
|
442
|
+
this.#reportMove();
|
|
443
|
+
}
|
|
444
|
+
#reconcileOpenState() {
|
|
445
|
+
if (!this.hasListTarget) {
|
|
446
|
+
this.#shownList = null;
|
|
447
|
+
return;
|
|
229
448
|
}
|
|
230
|
-
if (
|
|
449
|
+
if (this.#shownList && this.listTargets.includes(this.#shownList)) this.#adoptPageClose();
|
|
450
|
+
const list = this.listTarget;
|
|
451
|
+
if (this.#shownList !== null && list !== this.#shownList) {
|
|
452
|
+
this.#listHidden.write(list, null);
|
|
453
|
+
this.#shownList = list;
|
|
454
|
+
}
|
|
455
|
+
if (this.hasTriggerTarget) {
|
|
456
|
+
this.#expanded.write(this.triggerTarget, list.hidden ? "false" : "true");
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
#adoptPageClose() {
|
|
460
|
+
if (this.#shownList?.hidden) this.#shownList = null;
|
|
461
|
+
}
|
|
462
|
+
#reportMove() {
|
|
463
|
+
const { option, value } = this.#selection();
|
|
464
|
+
if (option === this.#settled.option && value === this.#settled.value) return;
|
|
465
|
+
this.#settled = { option, value };
|
|
466
|
+
this.dispatch("reconcile", { detail: { value, option } });
|
|
467
|
+
}
|
|
468
|
+
#selection() {
|
|
469
|
+
const option = this.optionTargets.find((candidate) => candidate.getAttribute("aria-selected") === "true") ?? null;
|
|
470
|
+
return { option, value: option ? this.#optionValue(option) : "" };
|
|
231
471
|
}
|
|
232
|
-
/** Removes the document listener and clears the typeahead timer. */
|
|
233
472
|
disconnect() {
|
|
234
|
-
this.#
|
|
235
|
-
this.#
|
|
473
|
+
this.#reconcile.disconnect();
|
|
474
|
+
this.#observer?.disconnect();
|
|
475
|
+
this.#observer = null;
|
|
236
476
|
document.removeEventListener("click", this.#onOutsideClick, true);
|
|
237
477
|
this.#typeahead.reset();
|
|
238
478
|
}
|
|
239
|
-
/**
|
|
240
|
-
* Toggles the list on a real mouse click. Keyboard activation of the
|
|
241
|
-
* `<button>` also fires a click (`detail === 0`); the keydown handler already
|
|
242
|
-
* drives that, so the synthetic click is ignored to avoid double-toggling.
|
|
243
|
-
*/
|
|
244
479
|
toggle(event) {
|
|
245
480
|
if (event.detail === 0) return;
|
|
246
481
|
if (this.#isClosed) {
|
|
@@ -249,7 +484,6 @@ var ListboxController = class extends Controller {
|
|
|
249
484
|
this.close();
|
|
250
485
|
}
|
|
251
486
|
}
|
|
252
|
-
/** Yields claimed keys; otherwise routes the APG select-only keyboard model. */
|
|
253
487
|
onTriggerKeydown(event) {
|
|
254
488
|
if (event.defaultPrevented) return;
|
|
255
489
|
if (isReservedArrowChord(event)) return;
|
|
@@ -275,7 +509,7 @@ var ListboxController = class extends Controller {
|
|
|
275
509
|
switch (event.key) {
|
|
276
510
|
case "ArrowDown":
|
|
277
511
|
event.preventDefault();
|
|
278
|
-
this.#setActive(
|
|
512
|
+
this.#setActive((activeIndex + 1) % length);
|
|
279
513
|
break;
|
|
280
514
|
case "ArrowUp":
|
|
281
515
|
event.preventDefault();
|
|
@@ -311,19 +545,24 @@ var ListboxController = class extends Controller {
|
|
|
311
545
|
break;
|
|
312
546
|
}
|
|
313
547
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
548
|
+
select(source) {
|
|
549
|
+
const { event, host, origin, reason } = actionSource(source);
|
|
550
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
551
|
+
return;
|
|
552
|
+
const option = host?.closest(targetSelector(this.identifier, "option"));
|
|
317
553
|
if (!option || !this.optionTargets.includes(option)) return;
|
|
318
|
-
this
|
|
554
|
+
const focus = event !== null || this.element.contains(document.activeElement);
|
|
555
|
+
if (!this.#selectOption(option, reason)) return;
|
|
319
556
|
this.close();
|
|
320
|
-
this.
|
|
557
|
+
if (focus && (event !== null || this.element.contains(document.activeElement))) {
|
|
558
|
+
this.triggerTarget.focus();
|
|
559
|
+
}
|
|
321
560
|
}
|
|
322
|
-
/** Opens the list and activates the selected option (else the first). */
|
|
323
561
|
open() {
|
|
324
562
|
if (!this.hasListTarget) return;
|
|
325
|
-
this.listTarget
|
|
326
|
-
this.
|
|
563
|
+
this.#listHidden.write(this.listTarget, null);
|
|
564
|
+
this.#shownList = this.listTarget;
|
|
565
|
+
this.#expanded.write(this.triggerTarget, "true");
|
|
327
566
|
if (this.optionTargets.length === 0) {
|
|
328
567
|
this.#setActive(-1);
|
|
329
568
|
return;
|
|
@@ -333,56 +572,94 @@ var ListboxController = class extends Controller {
|
|
|
333
572
|
);
|
|
334
573
|
this.#setActive(selected === -1 ? 0 : selected);
|
|
335
574
|
}
|
|
336
|
-
/** Closes the list, clears the active option, and resets the typeahead buffer. */
|
|
337
575
|
close() {
|
|
338
576
|
if (!this.hasListTarget) return;
|
|
339
|
-
this.listTarget
|
|
340
|
-
this
|
|
577
|
+
this.#listHidden.write(this.listTarget, "");
|
|
578
|
+
this.#shownList = null;
|
|
579
|
+
this.#expanded.write(this.triggerTarget, "false");
|
|
341
580
|
this.#setActive(-1);
|
|
342
581
|
this.#typeahead.reset();
|
|
343
582
|
}
|
|
344
|
-
/** Commits the active option (keyboard) and closes, returning focus. */
|
|
345
583
|
#commitActive() {
|
|
346
|
-
this.#reconcileActive();
|
|
347
584
|
const options = this.optionTargets;
|
|
348
585
|
const activeIndex = this.#findActiveIndex(options);
|
|
349
|
-
const option =
|
|
586
|
+
const option = options[activeIndex];
|
|
350
587
|
if (option) this.#selectOption(option);
|
|
351
588
|
this.close();
|
|
352
589
|
this.triggerTarget.focus();
|
|
353
590
|
}
|
|
354
|
-
|
|
355
|
-
#selectOption(option) {
|
|
591
|
+
#selectOption(option, reason = "user") {
|
|
356
592
|
const { value, fieldChanged } = this.#applySelection(option);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
this.
|
|
593
|
+
const changed = option !== this.#settled.option || value !== this.#settled.value;
|
|
594
|
+
this.#settled = { option, value };
|
|
595
|
+
if (!changed) return true;
|
|
596
|
+
const token = this.#moves.record();
|
|
597
|
+
if (fieldChanged) commitField(this.fieldTarget);
|
|
598
|
+
if (!this.#moves.isLatest(token)) return false;
|
|
599
|
+
this.dispatch("change", { detail: { value, option, reason } });
|
|
600
|
+
return this.#moves.isLatest(token);
|
|
361
601
|
}
|
|
362
|
-
/**
|
|
363
|
-
* Writes `option` into `aria-selected`, the trigger label and the hidden field.
|
|
364
|
-
* Emits nothing — {@link #normalizeSelection} reuses this at connect, where the
|
|
365
|
-
* state is being described rather than changed.
|
|
366
|
-
*/
|
|
367
602
|
#applySelection(option) {
|
|
368
603
|
for (const candidate of this.optionTargets) {
|
|
369
|
-
candidate
|
|
604
|
+
setAttributeIfChanged(candidate, "aria-selected", candidate === option ? "true" : "false");
|
|
370
605
|
}
|
|
371
|
-
|
|
372
|
-
const value = option
|
|
373
|
-
|
|
374
|
-
const fieldChanged = this.hasFieldTarget && this.fieldTarget
|
|
375
|
-
if (fieldChanged) this.fieldTarget.value = value;
|
|
606
|
+
this.#dropOwnRecords();
|
|
607
|
+
const value = option ? this.#optionValue(option) : "";
|
|
608
|
+
this.#renderLabel(option);
|
|
609
|
+
const fieldChanged = this.hasFieldTarget && writeField(this.fieldTarget, value);
|
|
376
610
|
return { value, fieldChanged };
|
|
377
611
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
612
|
+
#renderLabel(option) {
|
|
613
|
+
if (!this.hasValueTarget) return;
|
|
614
|
+
const label = this.valueTarget;
|
|
615
|
+
const ownsLabel = `data-${this.identifier}-owns-label`;
|
|
616
|
+
const originalLabel = `data-${this.identifier}-original-label`;
|
|
617
|
+
const owned = label.getAttribute(ownsLabel) === label.textContent && label.childElementCount === 0;
|
|
618
|
+
if (option === void 0 && !owned && label.hasAttribute(ownsLabel)) return;
|
|
619
|
+
const text = option ? this.#optionLabel(option) : this.placeholderValue || null;
|
|
620
|
+
if (text === null) {
|
|
621
|
+
if (!owned) return;
|
|
622
|
+
label.textContent = label.getAttribute(originalLabel) ?? "";
|
|
623
|
+
label.removeAttribute(ownsLabel);
|
|
624
|
+
label.removeAttribute(originalLabel);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (!owned) {
|
|
628
|
+
const current = (label.textContent ?? "").trim();
|
|
629
|
+
if (option === void 0 || current !== text) {
|
|
630
|
+
setAttributeIfChanged(label, originalLabel, current);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
if (label.textContent !== text) label.textContent = text;
|
|
634
|
+
setAttributeIfChanged(label, ownsLabel, text);
|
|
635
|
+
}
|
|
636
|
+
#optionLabel(option) {
|
|
637
|
+
return (option.textContent ?? "").trim();
|
|
638
|
+
}
|
|
639
|
+
#optionValue(option) {
|
|
640
|
+
return option.dataset.value ?? this.#optionLabel(option);
|
|
641
|
+
}
|
|
642
|
+
#observeOptions() {
|
|
643
|
+
const observer = new MutationObserver((records) => {
|
|
644
|
+
if (this.#concernsOptions(records)) this.#reconcile.schedule();
|
|
645
|
+
});
|
|
646
|
+
observer.observe(this.element, {
|
|
647
|
+
subtree: true,
|
|
648
|
+
attributes: true,
|
|
649
|
+
attributeFilter: OBSERVED_ATTRIBUTES
|
|
650
|
+
});
|
|
651
|
+
this.#observer = observer;
|
|
652
|
+
}
|
|
653
|
+
#concernsOptions(records) {
|
|
654
|
+
const options = new Set(this.optionTargets);
|
|
655
|
+
return records.some((record) => options.has(record.target));
|
|
656
|
+
}
|
|
657
|
+
#dropOwnRecords() {
|
|
658
|
+
this.#observer?.takeRecords();
|
|
659
|
+
}
|
|
383
660
|
#setActive(index) {
|
|
384
661
|
const options = this.optionTargets;
|
|
385
|
-
const active =
|
|
662
|
+
const active = options[index] ?? null;
|
|
386
663
|
for (const option of options) {
|
|
387
664
|
const marked = option.hasAttribute("data-active");
|
|
388
665
|
if (option === active) {
|
|
@@ -393,15 +670,14 @@ var ListboxController = class extends Controller {
|
|
|
393
670
|
}
|
|
394
671
|
if (active?.id) {
|
|
395
672
|
this.#activeId = active.id;
|
|
396
|
-
this.triggerTarget
|
|
673
|
+
this.#activeDescendant.write(this.triggerTarget, active.id);
|
|
397
674
|
} else {
|
|
398
675
|
this.#activeId = null;
|
|
399
|
-
this.triggerTarget
|
|
676
|
+
this.#activeDescendant.write(this.triggerTarget, null);
|
|
400
677
|
}
|
|
401
678
|
this.#activeOrder = active ? options.map((option) => option.id).filter(Boolean) : [];
|
|
402
679
|
if (active && this.hasListTarget) scrollOptionIntoView(this.listTarget, active);
|
|
403
680
|
}
|
|
404
|
-
/** Resolves active state against the current target collection. */
|
|
405
681
|
#reconcileActive() {
|
|
406
682
|
if (!this.hasTriggerTarget || this.#isClosed) {
|
|
407
683
|
if (this.hasTriggerTarget) this.#setActive(-1);
|
|
@@ -421,17 +697,15 @@ var ListboxController = class extends Controller {
|
|
|
421
697
|
return;
|
|
422
698
|
}
|
|
423
699
|
const activeId = this.triggerTarget.getAttribute("aria-activedescendant") ?? this.#activeId;
|
|
424
|
-
this.#setActive(
|
|
700
|
+
this.#setActive(this.#findFallbackIndex(options, activeId));
|
|
425
701
|
}
|
|
426
|
-
/** Finds the live target carrying the stable ID, or the active marker for an ID-less option. */
|
|
427
702
|
#findActiveIndex(options) {
|
|
428
703
|
const activeId = (this.hasTriggerTarget ? this.triggerTarget.getAttribute("aria-activedescendant") : null) ?? this.#activeId;
|
|
429
704
|
if (activeId) return options.findIndex((option) => option.id === activeId);
|
|
430
705
|
return options.findIndex((option) => option.hasAttribute("data-active"));
|
|
431
706
|
}
|
|
432
|
-
/** Chooses a surviving former successor, then a former predecessor. */
|
|
433
707
|
#findFallbackIndex(options, activeId) {
|
|
434
|
-
const oldIndex = this.#activeOrder.indexOf(activeId);
|
|
708
|
+
const oldIndex = this.#activeOrder.indexOf(activeId ?? "");
|
|
435
709
|
if (oldIndex < 0) return -1;
|
|
436
710
|
const indexesById = new Map(options.map((option, index) => [option.id, index]));
|
|
437
711
|
for (let index = oldIndex + 1; index < this.#activeOrder.length; index += 1) {
|
|
@@ -444,31 +718,17 @@ var ListboxController = class extends Controller {
|
|
|
444
718
|
}
|
|
445
719
|
return -1;
|
|
446
720
|
}
|
|
447
|
-
/** Coalesces all target callbacks from one MutationObserver batch. */
|
|
448
|
-
#queueOptionReconciliation() {
|
|
449
|
-
this.#reconcile.schedule();
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Advances the typeahead query and activates the next matching option.
|
|
453
|
-
*
|
|
454
|
-
* The search resumes just after the active option so repeating a character
|
|
455
|
-
* cycles through the options starting with it, rather than re-activating the
|
|
456
|
-
* same first match on every press.
|
|
457
|
-
*/
|
|
458
721
|
#typeaheadTo(options, activeIndex, char) {
|
|
459
722
|
const index = findTypeaheadMatch(options, activeIndex, this.#typeahead.push(char));
|
|
460
723
|
if (index !== -1) this.#setActive(index);
|
|
461
724
|
}
|
|
462
|
-
/** Closes on an outside click before an inside handler can detach its target. */
|
|
463
725
|
#onOutsideClick = (event) => {
|
|
464
726
|
if (!this.#isClosed && !this.element.contains(event.target)) this.close();
|
|
465
727
|
};
|
|
466
|
-
/** Whether the list is currently hidden. */
|
|
467
728
|
get #isClosed() {
|
|
468
729
|
return !this.hasListTarget || this.listTarget.hidden !== false;
|
|
469
730
|
}
|
|
470
731
|
};
|
|
471
732
|
|
|
472
733
|
export { ListboxController };
|
|
473
|
-
//# sourceMappingURL=listbox_controller.js.map
|
|
474
734
|
//# sourceMappingURL=listbox_controller.js.map
|