stimeo-ui 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -0
- package/README.md +27 -11
- package/dist/cable/index.d.ts +88 -31
- package/dist/cable/index.js +384 -449
- package/dist/cable/index.js.map +1 -1
- package/dist/coerce-JtZVNG5h.d.ts +20 -0
- package/dist/controllers/accordion_controller.d.ts +4 -2
- package/dist/controllers/accordion_controller.js +174 -55
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +12 -2
- package/dist/controllers/alert_dialog_controller.js +903 -235
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +37 -5
- package/dist/controllers/announcer_controller.js +238 -205
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.js +0 -10
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +30 -6
- package/dist/controllers/auto_submit_controller.js +414 -145
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +185 -107
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.js +0 -130
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +21 -3
- package/dist/controllers/bulk_select_controller.js +282 -58
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +38 -16
- package/dist/controllers/calendar_controller.js +228 -260
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +43 -12
- package/dist/controllers/carousel_controller.js +536 -436
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +44 -7
- package/dist/controllers/character_counter_controller.js +493 -207
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +7 -2
- package/dist/controllers/checkbox_controller.js +38 -74
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +16 -5
- package/dist/controllers/clipboard_controller.js +261 -149
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +17 -5
- package/dist/controllers/collapsible_controller.js +304 -120
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +18 -6
- package/dist/controllers/color_picker_controller.js +178 -146
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +24 -6
- package/dist/controllers/combobox_controller.js +522 -156
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +51 -20
- package/dist/controllers/command_palette_controller.js +1156 -395
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +15 -2
- package/dist/controllers/conditional_fields_controller.js +300 -171
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +49 -12
- package/dist/controllers/confirm_controller.js +980 -262
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +80 -18
- package/dist/controllers/context_menu_controller.js +346 -153
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +13 -1
- package/dist/controllers/count_up_controller.js +73 -21
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +47 -3
- package/dist/controllers/countdown_controller.js +281 -158
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +93 -10
- package/dist/controllers/currency_input_controller.js +575 -225
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +25 -12
- package/dist/controllers/data_grid_controller.js +248 -214
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +42 -14
- package/dist/controllers/date_range_picker_controller.js +338 -235
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +17 -6
- package/dist/controllers/dialog_controller.js +899 -237
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +25 -8
- package/dist/controllers/direct_upload_controller.js +97 -167
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +6 -1
- package/dist/controllers/dirty_form_controller.js +72 -69
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +19 -0
- package/dist/controllers/dismissible_controller.js +196 -24
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +39 -11
- package/dist/controllers/drawer_controller.js +974 -355
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +31 -1
- package/dist/controllers/dropdown_controller.js +339 -96
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +315 -88
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +79 -58
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +63 -18
- package/dist/controllers/file_dropzone_controller.js +322 -211
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.js +77 -42
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +35 -12
- package/dist/controllers/flash_controller.js +238 -347
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +15 -6
- package/dist/controllers/focus_controller.js +679 -240
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +156 -132
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.d.ts +3 -3
- package/dist/controllers/form_validation_controller.js +31 -97
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +47 -14
- package/dist/controllers/frame_loading_controller.js +275 -217
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +27 -6
- package/dist/controllers/highlight_controller.js +107 -75
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +60 -4
- package/dist/controllers/hover_card_controller.js +327 -130
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +48 -6
- package/dist/controllers/idle_controller.js +271 -101
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +30 -13
- package/dist/controllers/input_mask_controller.js +483 -97
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.d.ts +32 -13
- package/dist/controllers/intersection_controller.js +150 -106
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +19 -2
- package/dist/controllers/lazy_frame_controller.js +24 -68
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +44 -9
- package/dist/controllers/listbox_controller.js +523 -240
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +9 -7
- package/dist/controllers/local_time_controller.js +102 -77
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +11 -3
- package/dist/controllers/masonry_controller.js +80 -96
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +55 -11
- package/dist/controllers/menu_controller.js +302 -188
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +51 -9
- package/dist/controllers/menubar_controller.js +232 -345
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +33 -20
- package/dist/controllers/meter_controller.js +417 -87
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +48 -18
- package/dist/controllers/multi_select_controller.js +785 -393
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +27 -6
- package/dist/controllers/navigation_menu_controller.js +190 -250
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +10 -0
- package/dist/controllers/nested_form_controller.js +396 -192
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +23 -0
- package/dist/controllers/network_status_controller.js +268 -62
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +50 -8
- package/dist/controllers/number_input_controller.js +610 -303
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.d.ts +8 -5
- package/dist/controllers/optimistic_controller.js +45 -87
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +55 -8
- package/dist/controllers/otp_controller.js +658 -255
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
- package/dist/controllers/overflow_indicator_controller.js +122 -113
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +58 -20
- package/dist/controllers/overflow_menu_controller.js +470 -295
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +37 -7
- package/dist/controllers/pagination_controller.js +332 -123
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +46 -8
- package/dist/controllers/password_reveal_controller.js +392 -121
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +15 -6
- package/dist/controllers/password_strength_controller.js +111 -158
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +11 -4
- package/dist/controllers/persist_controller.js +165 -111
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +35 -12
- package/dist/controllers/pointer_drag_controller.js +434 -188
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +41 -5
- package/dist/controllers/popover_controller.js +269 -113
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +215 -76
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +10 -4
- package/dist/controllers/preview_guard_controller.js +243 -116
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +20 -19
- package/dist/controllers/progress_controller.js +391 -78
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +21 -9
- package/dist/controllers/radio_group_controller.js +278 -211
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +47 -6
- package/dist/controllers/range_slider_controller.js +220 -113
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +20 -4
- package/dist/controllers/rating_controller.js +458 -211
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +2 -0
- package/dist/controllers/read_more_controller.js +138 -63
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +1 -1
- package/dist/controllers/reading_progress_controller.js +166 -137
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +20 -6
- package/dist/controllers/relative_time_controller.js +211 -112
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
- package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +31 -2
- package/dist/controllers/resizable_controller.js +299 -150
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +3 -3
- package/dist/controllers/roving_controller.js +49 -88
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -3
- package/dist/controllers/scroll_area_controller.js +490 -246
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.d.ts +3 -3
- package/dist/controllers/scroll_restore_controller.js +14 -55
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
- package/dist/controllers/scroll_visibility_controller.js +432 -149
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +6 -1
- package/dist/controllers/scrollspy_controller.js +193 -248
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +20 -5
- package/dist/controllers/separator_controller.js +285 -142
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +65 -8
- package/dist/controllers/sidebar_controller.js +1069 -404
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +259 -101
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +29 -2
- package/dist/controllers/slider_controller.js +187 -86
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +22 -4
- package/dist/controllers/smart_sticky_header_controller.js +173 -66
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.d.ts +4 -4
- package/dist/controllers/sortable_controller.js +26 -125
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +288 -173
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +9 -4
- package/dist/controllers/step_indicator_controller.js +105 -46
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +14 -6
- package/dist/controllers/stepper_controller.js +120 -73
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +9 -1
- package/dist/controllers/stick_to_bottom_controller.js +275 -140
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +11 -0
- package/dist/controllers/sticky_observer_controller.js +220 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +24 -8
- package/dist/controllers/submit_once_controller.js +376 -227
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +18 -4
- package/dist/controllers/switch_controller.js +201 -84
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +9 -6
- package/dist/controllers/tabs_controller.js +41 -30
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +16 -3
- package/dist/controllers/tags_input_controller.js +293 -173
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +11 -1
- package/dist/controllers/textarea_autosize_controller.js +108 -74
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +24 -11
- package/dist/controllers/theme_controller.js +140 -116
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +55 -5
- package/dist/controllers/time_picker_controller.js +331 -127
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +40 -16
- package/dist/controllers/toast_controller.js +244 -324
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +14 -6
- package/dist/controllers/toggle_group_controller.js +252 -191
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.d.ts +8 -6
- package/dist/controllers/toolbar_controller.js +219 -120
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +49 -4
- package/dist/controllers/tooltip_controller.js +158 -124
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +22 -4
- package/dist/controllers/transition_controller.js +123 -119
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +22 -12
- package/dist/controllers/tree_view_controller.js +283 -369
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +8573 -10410
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +38 -14
- package/dist/inspector/cli.js +108 -40
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +145 -64
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +20 -20
- package/dist/inspector/manifest.json +802 -101
- package/dist/positioning/index.d.ts +15 -7
- package/dist/positioning/index.js +154 -47
- package/dist/positioning/index.js.map +1 -1
- package/package.json +11 -10
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function fillTemplate(template, values) {
|
|
4
|
+
return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
|
|
5
|
+
const replacement = Object.hasOwn(values, name) ? values[name] : void 0;
|
|
6
|
+
return replacement === void 0 ? match : String(replacement);
|
|
7
|
+
});
|
|
8
|
+
}
|
|
4
9
|
|
|
5
|
-
// src/utils/announce.ts
|
|
6
10
|
function announce(message, options = {}) {
|
|
7
11
|
const text = message.trim();
|
|
8
12
|
if (text.length === 0) return;
|
|
@@ -12,19 +16,11 @@ function announce(message, options = {}) {
|
|
|
12
16
|
})
|
|
13
17
|
);
|
|
14
18
|
}
|
|
15
|
-
function fillTemplate(template, values) {
|
|
16
|
-
return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
|
|
17
|
-
const replacement = values[name];
|
|
18
|
-
return replacement === void 0 ? match : String(replacement);
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
19
|
|
|
22
|
-
// src/utils/logical_scroll.ts
|
|
23
20
|
function isRtl(element) {
|
|
24
21
|
return window.getComputedStyle(element).direction === "rtl";
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
// src/utils/arrow_step.ts
|
|
28
24
|
function logicalArrowKey(key, element) {
|
|
29
25
|
if (key !== "ArrowRight" && key !== "ArrowLeft") return key;
|
|
30
26
|
if (!isRtl(element)) return key;
|
|
@@ -35,7 +31,6 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
35
31
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
// src/utils/element_part.ts
|
|
39
34
|
function matchingPart(root, selector) {
|
|
40
35
|
return root.matches(selector) ? root : root.querySelector(selector);
|
|
41
36
|
}
|
|
@@ -54,31 +49,14 @@ function writeLabel(slot, text) {
|
|
|
54
49
|
else slot.prepend(text);
|
|
55
50
|
}
|
|
56
51
|
|
|
57
|
-
// src/utils/roving_tabindex.ts
|
|
58
52
|
var RovingTabindex = class {
|
|
59
|
-
/** Returns the current ordered item elements; called on every operation. */
|
|
60
53
|
#getItems;
|
|
61
|
-
/**
|
|
62
|
-
* @param getItems - Returns the current ordered item elements. Called on every
|
|
63
|
-
* operation so the live target list is always used.
|
|
64
|
-
*/
|
|
65
54
|
constructor(getItems) {
|
|
66
55
|
this.#getItems = getItems;
|
|
67
56
|
}
|
|
68
|
-
/** Index of the currently tabbable item (`tabindex="0"`), or `-1` if none. */
|
|
69
57
|
get activeIndex() {
|
|
70
58
|
return this.#getItems().findIndex((item) => item.tabIndex === 0);
|
|
71
59
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Makes exactly the item at `index` tabbable (`tabindex="0"`) and removes every
|
|
74
|
-
* other item from the Tab sequence (`tabindex="-1"`). An out-of-range `index`
|
|
75
|
-
* (e.g. `-1`) leaves all items at `-1`, which a controller can use to express
|
|
76
|
-
* "nothing is currently tabbable".
|
|
77
|
-
*
|
|
78
|
-
* @param index - Position of the item to make tabbable.
|
|
79
|
-
* @param options - Pass `{ focus: true }` to also move DOM focus to that item,
|
|
80
|
-
* and `items` to reuse an event-scoped collection snapshot.
|
|
81
|
-
*/
|
|
82
60
|
setActive(index, options = {}) {
|
|
83
61
|
const { focus = false } = options;
|
|
84
62
|
const items = options.items ?? this.#getItems();
|
|
@@ -89,9 +67,8 @@ var RovingTabindex = class {
|
|
|
89
67
|
}
|
|
90
68
|
};
|
|
91
69
|
|
|
92
|
-
// src/utils/chip_row.ts
|
|
93
70
|
var ChipRow = class {
|
|
94
|
-
#
|
|
71
|
+
#getDirectionElement;
|
|
95
72
|
#getItems;
|
|
96
73
|
#getButton;
|
|
97
74
|
#onRemove;
|
|
@@ -99,16 +76,12 @@ var ChipRow = class {
|
|
|
99
76
|
#roving = new RovingTabindex(() => this.buttons);
|
|
100
77
|
#container = null;
|
|
101
78
|
constructor(options) {
|
|
102
|
-
this.#
|
|
79
|
+
this.#getDirectionElement = options.getDirectionElement;
|
|
103
80
|
this.#getItems = options.getItems;
|
|
104
81
|
this.#getButton = options.getButton ?? ((item) => matchingPart(item, "button"));
|
|
105
82
|
this.#onRemove = options.onRemove;
|
|
106
83
|
this.#focusAfterEnd = options.focusAfterEnd;
|
|
107
84
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Binds delegation to `container`; any row currently bound is released first.
|
|
110
|
-
* Idempotent for the same node, so target callbacks may call it freely.
|
|
111
|
-
*/
|
|
112
85
|
connect(container) {
|
|
113
86
|
if (this.#container === container) return;
|
|
114
87
|
this.disconnect();
|
|
@@ -116,10 +89,6 @@ var ChipRow = class {
|
|
|
116
89
|
container.addEventListener("click", this.#onClick);
|
|
117
90
|
container.addEventListener("keydown", this.#onKeydown);
|
|
118
91
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Releases the current row. When `container` is supplied, a stale disconnect
|
|
121
|
-
* callback cannot tear listeners off a newer replacement target.
|
|
122
|
-
*/
|
|
123
92
|
disconnect(container) {
|
|
124
93
|
const current = this.#container;
|
|
125
94
|
if (!current || container !== void 0 && current !== container) return;
|
|
@@ -127,19 +96,15 @@ var ChipRow = class {
|
|
|
127
96
|
current.removeEventListener("keydown", this.#onKeydown);
|
|
128
97
|
this.#container = null;
|
|
129
98
|
}
|
|
130
|
-
/** Current remove buttons, at most one consumer-resolved button per chip item. */
|
|
131
99
|
get buttons() {
|
|
132
100
|
return this.#entries.map(({ button }) => button);
|
|
133
101
|
}
|
|
134
|
-
/** Number of currently navigable remove buttons. */
|
|
135
102
|
get length() {
|
|
136
103
|
return this.buttons.length;
|
|
137
104
|
}
|
|
138
|
-
/** Declared-item index of the last navigable chip, or `-1` for an empty row. */
|
|
139
105
|
get lastIndex() {
|
|
140
106
|
return this.#entries.at(-1)?.itemIndex ?? -1;
|
|
141
107
|
}
|
|
142
|
-
/** Keeps exactly one button tabbable, preferring the first authored Tab stop. */
|
|
143
108
|
ensureTabStop() {
|
|
144
109
|
const buttons = this.#entries.map(({ button }) => button);
|
|
145
110
|
const active = buttons.findIndex((button) => button.tabIndex === 0);
|
|
@@ -148,7 +113,6 @@ var ChipRow = class {
|
|
|
148
113
|
this.#roving.setActive(active === -1 ? 0 : active, { items: buttons });
|
|
149
114
|
}
|
|
150
115
|
}
|
|
151
|
-
/** Focuses the last chip, returning false for an empty row. */
|
|
152
116
|
focusLast() {
|
|
153
117
|
const buttons = this.#entries.map(({ button }) => button);
|
|
154
118
|
const last = buttons.length - 1;
|
|
@@ -156,10 +120,6 @@ var ChipRow = class {
|
|
|
156
120
|
this.#roving.setActive(last, { focus: true, items: buttons });
|
|
157
121
|
return true;
|
|
158
122
|
}
|
|
159
|
-
/**
|
|
160
|
-
* Focuses the chip that followed a removed index, or the new last chip.
|
|
161
|
-
* Returns false when removal emptied the row so the consumer can rescue focus.
|
|
162
|
-
*/
|
|
163
123
|
focusAfterRemoval(index) {
|
|
164
124
|
const entries = this.#entries;
|
|
165
125
|
if (entries.length === 0) return false;
|
|
@@ -170,14 +130,12 @@ var ChipRow = class {
|
|
|
170
130
|
});
|
|
171
131
|
return true;
|
|
172
132
|
}
|
|
173
|
-
/** Current remove-button entries, retaining each button's declared item index. */
|
|
174
133
|
get #entries() {
|
|
175
134
|
return this.#getItems().flatMap((item, itemIndex) => {
|
|
176
135
|
const button = this.#getButton(item);
|
|
177
136
|
return button ? [{ button, itemIndex }] : [];
|
|
178
137
|
});
|
|
179
138
|
}
|
|
180
|
-
/** Resolves a delegated event to the remove button owned by a declared chip. */
|
|
181
139
|
#entry(event) {
|
|
182
140
|
const target = event.target;
|
|
183
141
|
if (!(target instanceof Element)) return null;
|
|
@@ -188,20 +146,18 @@ var ChipRow = class {
|
|
|
188
146
|
const entry = entries[buttonIndex];
|
|
189
147
|
return { ...entry, buttonIndex, buttons: entries.map(({ button: button2 }) => button2) };
|
|
190
148
|
}
|
|
191
|
-
/** Delegates removal clicks without waiting for Stimulus to wire a new chip. */
|
|
192
149
|
#onClick = (event) => {
|
|
193
150
|
const entry = this.#entry(event);
|
|
194
151
|
if (!entry) return;
|
|
195
152
|
this.#onRemove(entry.itemIndex);
|
|
196
153
|
};
|
|
197
|
-
/** Applies the shared logical-arrow and removal policy within the chip row. */
|
|
198
154
|
#onKeydown = (event) => {
|
|
199
155
|
if (event.defaultPrevented || isReservedArrowChord(event)) return;
|
|
200
156
|
const entry = this.#entry(event);
|
|
201
157
|
if (!entry) return;
|
|
202
158
|
const buttons = entry.buttons;
|
|
203
159
|
const index = entry.buttonIndex;
|
|
204
|
-
switch (logicalArrowKey(event.key, this.#
|
|
160
|
+
switch (logicalArrowKey(event.key, this.#getDirectionElement())) {
|
|
205
161
|
case "ArrowLeft":
|
|
206
162
|
if (index > 0) {
|
|
207
163
|
event.preventDefault();
|
|
@@ -225,86 +181,191 @@ var ChipRow = class {
|
|
|
225
181
|
};
|
|
226
182
|
};
|
|
227
183
|
|
|
228
|
-
// src/utils/composition_tracker.ts
|
|
229
184
|
var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
230
185
|
"insertCompositionText",
|
|
231
186
|
"insertFromComposition",
|
|
232
187
|
"deleteCompositionText",
|
|
233
188
|
"deleteByComposition"
|
|
234
189
|
]);
|
|
190
|
+
function isCompositionInputType(inputType) {
|
|
191
|
+
return inputType !== void 0 && COMPOSITION_INPUT_TYPES.has(inputType);
|
|
192
|
+
}
|
|
193
|
+
var BEFORE_MORPH_ATTRIBUTE = "turbo:before-morph-attribute";
|
|
194
|
+
var textOf = (element) => element.value;
|
|
195
|
+
var holdsFocus = (element) => element instanceof Node && element.getRootNode().activeElement === element;
|
|
235
196
|
var CompositionTracker = class {
|
|
236
197
|
#observedTargets = /* @__PURE__ */ new Set();
|
|
237
|
-
#
|
|
198
|
+
#composition = null;
|
|
238
199
|
#onStart;
|
|
239
200
|
#onEnd;
|
|
240
|
-
|
|
201
|
+
#onAbandon;
|
|
241
202
|
#confirmedTarget = null;
|
|
203
|
+
#keyComposing = false;
|
|
204
|
+
#connected = false;
|
|
205
|
+
#abandoned = null;
|
|
242
206
|
constructor(options = {}) {
|
|
243
207
|
this.#onStart = options.onStart;
|
|
244
208
|
this.#onEnd = options.onEnd;
|
|
209
|
+
this.#onAbandon = options.onAbandon;
|
|
245
210
|
}
|
|
246
|
-
/** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
|
|
247
211
|
observe(target) {
|
|
212
|
+
this.#connected = true;
|
|
248
213
|
if (this.#observedTargets.has(target)) return;
|
|
249
214
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
250
215
|
target.addEventListener("compositionend", this.#handleEnd);
|
|
251
|
-
target.addEventListener("keydown", this.#handleKeydown);
|
|
216
|
+
target.addEventListener("keydown", this.#handleKeydown, true);
|
|
217
|
+
target.addEventListener("beforeinput", this.#handleBeforeInput, true);
|
|
218
|
+
target.addEventListener("input", this.#handleInput, true);
|
|
219
|
+
target.addEventListener("blur", this.#handleBlur, true);
|
|
220
|
+
target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
|
|
252
221
|
this.#observedTargets.add(target);
|
|
253
222
|
}
|
|
254
|
-
/** Stops tracking one target and clears any active composition it owned. */
|
|
255
223
|
unobserve(target) {
|
|
256
224
|
if (!this.#observedTargets.delete(target)) return;
|
|
257
|
-
|
|
258
|
-
target
|
|
259
|
-
target.removeEventListener("keydown", this.#handleKeydown);
|
|
260
|
-
this.#activeTargets.delete(target);
|
|
225
|
+
this.#release(target);
|
|
226
|
+
if (this.#composition?.target === target) this.#letGo();
|
|
261
227
|
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
262
228
|
}
|
|
263
|
-
/** Releases every listener and clears state so reconnect starts cleanly. */
|
|
264
229
|
disconnect() {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
target.removeEventListener("compositionend", this.#handleEnd);
|
|
268
|
-
target.removeEventListener("keydown", this.#handleKeydown);
|
|
269
|
-
}
|
|
230
|
+
this.#connected = false;
|
|
231
|
+
for (const target of this.#observedTargets) this.#release(target);
|
|
270
232
|
this.#observedTargets.clear();
|
|
271
|
-
this.#
|
|
233
|
+
this.#letGo();
|
|
272
234
|
this.#confirmedTarget = null;
|
|
235
|
+
this.#keyComposing = false;
|
|
236
|
+
this.#abandoned = null;
|
|
273
237
|
}
|
|
274
|
-
/** True when lifecycle tracking or the current event reports composition. */
|
|
275
238
|
isComposing(event) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
239
|
+
this.#settle();
|
|
240
|
+
return event?.isComposing === true || this.#composition !== null;
|
|
241
|
+
}
|
|
242
|
+
composingElement() {
|
|
243
|
+
this.#settle();
|
|
244
|
+
return this.#composition?.element ?? null;
|
|
245
|
+
}
|
|
246
|
+
owns(event) {
|
|
247
|
+
const element = this.composingElement();
|
|
248
|
+
return event.isComposing === true || element !== null && element === event.target;
|
|
249
|
+
}
|
|
284
250
|
consumesConfirmedInput(event) {
|
|
285
251
|
const confirmed = this.#confirmedTarget;
|
|
286
252
|
this.#confirmedTarget = null;
|
|
287
253
|
if (confirmed === null || confirmed !== event.target) return false;
|
|
288
254
|
const inputType = event.inputType;
|
|
289
|
-
return !inputType ||
|
|
255
|
+
return !inputType || isCompositionInputType(inputType);
|
|
290
256
|
}
|
|
257
|
+
#release(target) {
|
|
258
|
+
target.removeEventListener("compositionstart", this.#handleStart);
|
|
259
|
+
target.removeEventListener("compositionend", this.#handleEnd);
|
|
260
|
+
target.removeEventListener("keydown", this.#handleKeydown, true);
|
|
261
|
+
target.removeEventListener("beforeinput", this.#handleBeforeInput, true);
|
|
262
|
+
target.removeEventListener("input", this.#handleInput, true);
|
|
263
|
+
target.removeEventListener("blur", this.#handleBlur, true);
|
|
264
|
+
target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
|
|
265
|
+
}
|
|
266
|
+
#letGo() {
|
|
267
|
+
const composition = this.#composition;
|
|
268
|
+
if (composition === null) return;
|
|
269
|
+
if (!holdsFocus(composition.element)) {
|
|
270
|
+
this.#composition = null;
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
queueMicrotask(() => {
|
|
274
|
+
if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.#composition = null;
|
|
278
|
+
if (!this.#connected) return;
|
|
279
|
+
this.#abandoned = { element: composition.element, inPlace: true };
|
|
280
|
+
this.#runAbandon();
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
#settle() {
|
|
284
|
+
const composition = this.#composition;
|
|
285
|
+
if (composition === null) return;
|
|
286
|
+
const { target, element, text } = composition;
|
|
287
|
+
if (target instanceof Node && element instanceof Node && !target.contains(element)) {
|
|
288
|
+
this.#composition = null;
|
|
289
|
+
} else if (textOf(element) !== text) {
|
|
290
|
+
this.#abandon(false);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
#abandon(inPlace) {
|
|
294
|
+
this.#owe(inPlace);
|
|
295
|
+
this.#composition = null;
|
|
296
|
+
queueMicrotask(this.#runAbandon);
|
|
297
|
+
}
|
|
298
|
+
#owe(inPlace) {
|
|
299
|
+
const composition = this.#composition;
|
|
300
|
+
if (composition !== null) this.#abandoned = { element: composition.element, inPlace };
|
|
301
|
+
}
|
|
302
|
+
#runAbandon = () => {
|
|
303
|
+
const abandoned = this.#abandoned;
|
|
304
|
+
if (abandoned === null) return;
|
|
305
|
+
this.#abandoned = null;
|
|
306
|
+
this.#onAbandon?.(abandoned.element, abandoned.inPlace);
|
|
307
|
+
};
|
|
291
308
|
#handleStart = (event) => {
|
|
292
309
|
this.#confirmedTarget = null;
|
|
293
|
-
|
|
310
|
+
this.#settle();
|
|
311
|
+
this.#owe(true);
|
|
312
|
+
const target = event.currentTarget;
|
|
313
|
+
const element = event.target;
|
|
314
|
+
this.#composition = { target, element, text: textOf(element) };
|
|
315
|
+
this.#runAbandon();
|
|
294
316
|
this.#onStart?.(event);
|
|
295
317
|
};
|
|
296
318
|
#handleEnd = (event) => {
|
|
297
|
-
|
|
319
|
+
this.#runAbandon();
|
|
320
|
+
if (this.#composition?.target === event.currentTarget) this.#composition = null;
|
|
298
321
|
this.#confirmedTarget = event.target;
|
|
299
322
|
this.#onEnd?.(event);
|
|
300
323
|
};
|
|
301
|
-
|
|
302
|
-
|
|
324
|
+
#handleKeydown = (event) => {
|
|
325
|
+
this.#settle();
|
|
326
|
+
this.#runAbandon();
|
|
303
327
|
this.#confirmedTarget = null;
|
|
328
|
+
this.#keyComposing = event.isComposing === true;
|
|
329
|
+
};
|
|
330
|
+
#handleBeforeInput = (event) => {
|
|
331
|
+
const keyComposing = this.#keyComposing;
|
|
332
|
+
this.#keyComposing = false;
|
|
333
|
+
this.#settle();
|
|
334
|
+
const { isComposing, inputType } = event;
|
|
335
|
+
if (this.#composition !== null && !keyComposing && !isComposing && inputType && !isCompositionInputType(inputType)) {
|
|
336
|
+
this.#abandon(true);
|
|
337
|
+
}
|
|
338
|
+
this.#runAbandon();
|
|
339
|
+
};
|
|
340
|
+
#handleInput = (event) => {
|
|
341
|
+
const composition = this.#composition;
|
|
342
|
+
const { isTrusted, isComposing, inputType } = event;
|
|
343
|
+
if (composition !== null && event.target === composition.element && (isTrusted || isComposing || isCompositionInputType(inputType))) {
|
|
344
|
+
composition.text = textOf(composition.element);
|
|
345
|
+
} else {
|
|
346
|
+
this.#settle();
|
|
347
|
+
}
|
|
348
|
+
this.#runAbandon();
|
|
349
|
+
};
|
|
350
|
+
#handleBlur = (event) => {
|
|
351
|
+
this.#settle();
|
|
352
|
+
this.#runAbandon();
|
|
353
|
+
const composition = this.#composition;
|
|
354
|
+
if (composition === null || composition.target === composition.element) return;
|
|
355
|
+
const { element } = composition;
|
|
356
|
+
if (!(element instanceof Node) || event.target !== element) return;
|
|
357
|
+
const root = element.getRootNode();
|
|
358
|
+
if (root.activeElement !== element) this.#composition = null;
|
|
359
|
+
};
|
|
360
|
+
#handleBeforeMorphAttribute = (event) => {
|
|
361
|
+
this.#settle();
|
|
362
|
+
const composition = this.#composition;
|
|
363
|
+
if (composition?.target !== event.currentTarget || event.target !== composition.element) return;
|
|
364
|
+
const detail = event.detail;
|
|
365
|
+
if (detail?.attributeName === "value") event.preventDefault();
|
|
304
366
|
};
|
|
305
367
|
};
|
|
306
368
|
|
|
307
|
-
// src/utils/field_mirror.ts
|
|
308
369
|
function writeFields(container, values, { name, form = "" }) {
|
|
309
370
|
const current = [...container.children];
|
|
310
371
|
const submits = current.length === values.length && current.every(
|
|
@@ -327,27 +388,36 @@ function commitField(target) {
|
|
|
327
388
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
328
389
|
}
|
|
329
390
|
|
|
330
|
-
|
|
391
|
+
var ListenerSet = class {
|
|
392
|
+
#abort = new AbortController();
|
|
393
|
+
add(target, type, handler, options) {
|
|
394
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
395
|
+
}
|
|
396
|
+
dispose() {
|
|
397
|
+
this.#abort.abort();
|
|
398
|
+
this.#abort = new AbortController();
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
331
402
|
var MicrotaskCoalescer = class {
|
|
332
403
|
#run;
|
|
333
404
|
#queued = false;
|
|
334
405
|
#active = false;
|
|
335
406
|
#generation = 0;
|
|
336
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
337
407
|
constructor(run) {
|
|
338
408
|
this.#run = run;
|
|
339
409
|
}
|
|
340
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
341
410
|
activate() {
|
|
342
411
|
this.#active = true;
|
|
343
412
|
}
|
|
344
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
345
413
|
cancel() {
|
|
346
414
|
this.#active = false;
|
|
347
415
|
this.#queued = false;
|
|
348
416
|
this.#generation += 1;
|
|
349
417
|
}
|
|
350
|
-
|
|
418
|
+
get pending() {
|
|
419
|
+
return this.#queued;
|
|
420
|
+
}
|
|
351
421
|
schedule() {
|
|
352
422
|
if (!this.#active || this.#queued) return;
|
|
353
423
|
this.#queued = true;
|
|
@@ -360,12 +430,93 @@ var MicrotaskCoalescer = class {
|
|
|
360
430
|
}
|
|
361
431
|
};
|
|
362
432
|
|
|
363
|
-
|
|
433
|
+
var MorphRenderWatcher = class {
|
|
434
|
+
#listeners = new ListenerSet();
|
|
435
|
+
#pass;
|
|
436
|
+
#element = null;
|
|
437
|
+
#onMorph = () => this.#pass.schedule();
|
|
438
|
+
constructor(run) {
|
|
439
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
440
|
+
}
|
|
441
|
+
observe(element) {
|
|
442
|
+
if (this.#element === element) return;
|
|
443
|
+
this.disconnect();
|
|
444
|
+
this.#element = element;
|
|
445
|
+
this.#pass.activate();
|
|
446
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
447
|
+
}
|
|
448
|
+
schedule() {
|
|
449
|
+
this.#pass.schedule();
|
|
450
|
+
}
|
|
451
|
+
get pending() {
|
|
452
|
+
return this.#pass.pending;
|
|
453
|
+
}
|
|
454
|
+
disconnect() {
|
|
455
|
+
this.#listeners.dispose();
|
|
456
|
+
this.#pass.cancel();
|
|
457
|
+
this.#element = null;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
var MoveCounter = class {
|
|
462
|
+
#sequence = 0;
|
|
463
|
+
record() {
|
|
464
|
+
this.#sequence += 1;
|
|
465
|
+
return this.#sequence;
|
|
466
|
+
}
|
|
467
|
+
isLatest(token) {
|
|
468
|
+
return token === this.#sequence;
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
var NUMBER_BOUNDS = {
|
|
473
|
+
nonNegative: { finite: true, min: 0 }};
|
|
474
|
+
function matchesNumberBounds(value, bounds) {
|
|
475
|
+
if (!Number.isFinite(value)) {
|
|
476
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
477
|
+
if (direction === null) return false;
|
|
478
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
479
|
+
}
|
|
480
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
481
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
482
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
483
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
484
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function readNumber(raw, fallback, bounds) {
|
|
489
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
var NumberValueReader = class {
|
|
493
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
494
|
+
read(owner, name, raw, fallback, bounds) {
|
|
495
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
496
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
497
|
+
this.#lastRejected.delete(name);
|
|
498
|
+
return resolved;
|
|
499
|
+
}
|
|
500
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
501
|
+
const literal = owner.element.getAttribute(attribute);
|
|
502
|
+
if (literal === null) {
|
|
503
|
+
this.#lastRejected.delete(name);
|
|
504
|
+
return resolved;
|
|
505
|
+
}
|
|
506
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
507
|
+
this.#lastRejected.set(name, literal);
|
|
508
|
+
console.warn(
|
|
509
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
return resolved;
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
364
516
|
function targetSelector(identifier, name) {
|
|
365
517
|
return `[data-${identifier}-target~="${name}"]`;
|
|
366
518
|
}
|
|
367
519
|
|
|
368
|
-
// src/utils/template_row.ts
|
|
369
520
|
function cloneTemplateRoot(template) {
|
|
370
521
|
const root = template.content.firstElementChild;
|
|
371
522
|
return root instanceof HTMLElement ? root.cloneNode(true) : null;
|
|
@@ -376,22 +527,15 @@ var TemplateRow = class {
|
|
|
376
527
|
constructor(options) {
|
|
377
528
|
this.#options = options;
|
|
378
529
|
}
|
|
379
|
-
/** The attribute selector for one declared part, in this controller's namespace. */
|
|
380
530
|
selector(name) {
|
|
381
531
|
return targetSelector(this.#options.identifier, name);
|
|
382
532
|
}
|
|
383
|
-
/** Re-arms the once-per-connection diagnostic. */
|
|
384
533
|
connect() {
|
|
385
534
|
this.#warned = false;
|
|
386
535
|
}
|
|
387
|
-
/**
|
|
388
|
-
* Names one missing part on the console, at most once per connection, and
|
|
389
|
-
* returns `null` so a caller can hand it straight back.
|
|
390
|
-
*/
|
|
391
536
|
report(missing) {
|
|
392
537
|
return this.#say(`lacks ${missing}`);
|
|
393
538
|
}
|
|
394
|
-
/** Writes one diagnostic per connection, saying what the template got wrong. */
|
|
395
539
|
#say(problem) {
|
|
396
540
|
if (this.#warned) return null;
|
|
397
541
|
this.#warned = true;
|
|
@@ -399,10 +543,6 @@ var TemplateRow = class {
|
|
|
399
543
|
console.warn(`Stimeo UI: "${identifier}" ${outcome} because its ${noun} ${problem}.`);
|
|
400
544
|
return null;
|
|
401
545
|
}
|
|
402
|
-
/**
|
|
403
|
-
* Clones the row and resolves its parts, or names what the template got wrong
|
|
404
|
-
* and returns `null`. `values` fills the button's authored `aria-label`.
|
|
405
|
-
*/
|
|
406
546
|
instantiate(template, values) {
|
|
407
547
|
const { root: rootName, required, optional, button: buttonName } = this.#options;
|
|
408
548
|
const root = cloneTemplateRoot(template);
|
|
@@ -430,7 +570,6 @@ var TemplateRow = class {
|
|
|
430
570
|
button.setAttribute("aria-label", fillTemplate(name, values));
|
|
431
571
|
return { root, slots, button };
|
|
432
572
|
}
|
|
433
|
-
/** The row may be its own part; otherwise the first descendant carrying the target. */
|
|
434
573
|
#resolve(root, name) {
|
|
435
574
|
return matchingPart(root, this.selector(name));
|
|
436
575
|
}
|
|
@@ -439,8 +578,9 @@ var TemplateRow = class {
|
|
|
439
578
|
}
|
|
440
579
|
};
|
|
441
580
|
|
|
442
|
-
|
|
443
|
-
|
|
581
|
+
var TagsInputController = class _TagsInputController extends Controller {
|
|
582
|
+
#numbers = new NumberValueReader();
|
|
583
|
+
#moves = new MoveCounter();
|
|
444
584
|
static targets = ["input", "tags", "tag", "tagTemplate", "label", "remove", "fields"];
|
|
445
585
|
static values = {
|
|
446
586
|
delimiter: { type: String, default: "," },
|
|
@@ -451,11 +591,12 @@ var TagsInputController = class extends Controller {
|
|
|
451
591
|
announceText: { type: String, default: "" },
|
|
452
592
|
announceRemovedText: { type: String, default: "" }
|
|
453
593
|
};
|
|
594
|
+
static valueConstraints = {
|
|
595
|
+
max: NUMBER_BOUNDS.nonNegative
|
|
596
|
+
};
|
|
454
597
|
static actions = ["onKeydown"];
|
|
455
598
|
static events = ["change", "reconcile", "reject"];
|
|
456
|
-
/** Last reconciled tag order, separating user edits from DOM/Turbo repair. */
|
|
457
599
|
#tagValues = [];
|
|
458
|
-
/** Builds one chip from the authored template and owns its diagnostic. */
|
|
459
600
|
#rows = new TemplateRow({
|
|
460
601
|
identifier: this.identifier,
|
|
461
602
|
root: "tag",
|
|
@@ -464,18 +605,15 @@ var TagsInputController = class extends Controller {
|
|
|
464
605
|
outcome: "added no tag",
|
|
465
606
|
noun: "chip template"
|
|
466
607
|
});
|
|
467
|
-
|
|
468
|
-
#reconcile = new MicrotaskCoalescer(() => this.#reconcileTags());
|
|
608
|
+
#reconcile = new MorphRenderWatcher(() => this.#reconcileTags());
|
|
469
609
|
#chipRow = new ChipRow({
|
|
470
|
-
|
|
610
|
+
getDirectionElement: () => this.#chipDirectionElement,
|
|
471
611
|
getItems: () => this.tagTargets,
|
|
472
612
|
getButton: (tag) => matchingPart(tag, `button${this.#rows.selector("remove")}`),
|
|
473
613
|
onRemove: (index) => this.#removeAt(index),
|
|
474
614
|
focusAfterEnd: () => this.#focusInput()
|
|
475
615
|
});
|
|
476
|
-
/** Owns IME lifecycle state for commit-key guarding. */
|
|
477
616
|
#composition = new CompositionTracker();
|
|
478
|
-
/** Wires tag-list keyboard navigation and removal, and seeds the single Tab stop. */
|
|
479
617
|
connect() {
|
|
480
618
|
this.#rows.connect();
|
|
481
619
|
if (this.hasInputTarget) this.#composition.observe(this.inputTarget);
|
|
@@ -483,56 +621,47 @@ var TagsInputController = class extends Controller {
|
|
|
483
621
|
const tags = this.#values;
|
|
484
622
|
this.#syncState(tags);
|
|
485
623
|
this.#tagValues = tags;
|
|
486
|
-
this.#reconcile.
|
|
624
|
+
this.#reconcile.observe(this.element);
|
|
487
625
|
}
|
|
488
|
-
/** Releases the delegated listeners so no handler outlives the element. */
|
|
489
626
|
disconnect() {
|
|
490
|
-
this.#reconcile.
|
|
627
|
+
this.#reconcile.disconnect();
|
|
491
628
|
this.#composition.disconnect();
|
|
492
629
|
this.#chipRow.disconnect();
|
|
493
630
|
}
|
|
494
|
-
/** Tracks an input added initially or after connect. */
|
|
495
631
|
inputTargetConnected(input) {
|
|
496
632
|
this.#composition.observe(input);
|
|
497
633
|
}
|
|
498
|
-
/** Removes composition listeners when the active input is replaced or removed. */
|
|
499
634
|
inputTargetDisconnected(input) {
|
|
500
635
|
this.#composition.unobserve(input);
|
|
501
636
|
}
|
|
502
|
-
/** Rebinds delegation and derived state when the tags container is replaced. */
|
|
503
637
|
tagsTargetConnected(_tags) {
|
|
504
638
|
this.#reconcile.schedule();
|
|
505
639
|
}
|
|
506
|
-
/** Releases only the row that disconnected, leaving a newer target intact. */
|
|
507
640
|
tagsTargetDisconnected(tags) {
|
|
508
641
|
this.#chipRow.disconnect(tags);
|
|
509
642
|
this.#reconcile.schedule();
|
|
510
643
|
}
|
|
511
|
-
/** Reconciles a tag inserted by Turbo, a server render, or another controller. */
|
|
512
644
|
tagTargetConnected(_tag) {
|
|
513
645
|
this.#reconcile.schedule();
|
|
514
646
|
}
|
|
515
|
-
/** Reconciles a tag removed by Turbo, a server render, or another controller. */
|
|
516
647
|
tagTargetDisconnected(_tag) {
|
|
517
648
|
this.#reconcile.schedule();
|
|
518
649
|
}
|
|
519
|
-
/** Seeds a fields target inserted after connect. */
|
|
520
650
|
fieldsTargetConnected() {
|
|
521
651
|
this.#reconcile.schedule();
|
|
522
652
|
}
|
|
523
|
-
|
|
653
|
+
fieldsTargetDisconnected() {
|
|
654
|
+
this.#reconcile.schedule();
|
|
655
|
+
}
|
|
524
656
|
nameValueChanged() {
|
|
525
657
|
this.#reconcile.schedule();
|
|
526
658
|
}
|
|
527
|
-
/** Repoints submitted fields when the owning form changes at runtime. */
|
|
528
659
|
formValueChanged() {
|
|
529
660
|
this.#reconcile.schedule();
|
|
530
661
|
}
|
|
531
|
-
/** Recomputes the full hook when the cap changes at runtime. */
|
|
532
662
|
maxValueChanged() {
|
|
533
663
|
this.#reconcile.schedule();
|
|
534
664
|
}
|
|
535
|
-
/** Commits on `Enter`/delimiter and deletes the last tag on empty `Backspace`. */
|
|
536
665
|
onKeydown(event) {
|
|
537
666
|
if (event.defaultPrevented) return;
|
|
538
667
|
if (isReservedArrowChord(event)) return;
|
|
@@ -551,25 +680,20 @@ var TagsInputController = class extends Controller {
|
|
|
551
680
|
}
|
|
552
681
|
return;
|
|
553
682
|
}
|
|
554
|
-
if (logicalArrowKey(event.key, this
|
|
683
|
+
if (logicalArrowKey(event.key, this.#chipDirectionElement) === "ArrowLeft" && this.inputTarget.value === "") {
|
|
555
684
|
if (this.#chipRow.length > 0) {
|
|
556
685
|
event.preventDefault();
|
|
557
686
|
this.#chipRow.focusLast();
|
|
558
687
|
}
|
|
559
688
|
}
|
|
560
689
|
}
|
|
561
|
-
/**
|
|
562
|
-
* Validates and adds the current input value as a tag, then clears the input.
|
|
563
|
-
*
|
|
564
|
-
* @stimeoRuntimeOnly `max` and `allowDuplicates` decide whether this one entry is taken.
|
|
565
|
-
*/
|
|
566
690
|
#commitInput() {
|
|
567
691
|
const value = this.inputTarget.value.trim();
|
|
568
692
|
if (value === "") {
|
|
569
693
|
this.#reject(value, "empty");
|
|
570
694
|
return;
|
|
571
695
|
}
|
|
572
|
-
if (this
|
|
696
|
+
if (this.#safeMax > 0 && this.tagTargets.length >= this.#safeMax) {
|
|
573
697
|
this.#reject(value, "max");
|
|
574
698
|
return;
|
|
575
699
|
}
|
|
@@ -580,18 +704,8 @@ var TagsInputController = class extends Controller {
|
|
|
580
704
|
if (!this.#appendTag(value)) return;
|
|
581
705
|
this.inputTarget.value = "";
|
|
582
706
|
const tags = this.#values;
|
|
583
|
-
this.#
|
|
584
|
-
this.#tagValues = tags;
|
|
585
|
-
this.#announceTransition(true, value, tags.length);
|
|
586
|
-
this.dispatch("change", { detail: { tags } });
|
|
707
|
+
this.#publishTags(tags, true, value);
|
|
587
708
|
}
|
|
588
|
-
/**
|
|
589
|
-
* Builds one chip from the template and appends it to the tag list.
|
|
590
|
-
*
|
|
591
|
-
* A refused row leaves the commit a no-op — nothing about the input, the tag
|
|
592
|
-
* set, the hidden fields, the announcement, or the events changes — and the
|
|
593
|
-
* row reports why on the console once per connection.
|
|
594
|
-
*/
|
|
595
709
|
#appendTag(value) {
|
|
596
710
|
if (!this.hasTagsTarget) return false;
|
|
597
711
|
if (!this.hasTagTemplateTarget) {
|
|
@@ -606,76 +720,82 @@ var TagsInputController = class extends Controller {
|
|
|
606
720
|
this.tagsTarget.appendChild(row.root);
|
|
607
721
|
return true;
|
|
608
722
|
}
|
|
609
|
-
/** Removes the tag at `index`, then applies the interaction-origin focus policy. */
|
|
610
723
|
#removeAt(index, focus = "neighbor") {
|
|
611
724
|
const tag = this.tagTargets[index];
|
|
612
725
|
if (!tag) return;
|
|
613
726
|
const value = tag.dataset.value ?? "";
|
|
614
727
|
tag.remove();
|
|
615
728
|
const tags = this.#values;
|
|
616
|
-
this.#
|
|
617
|
-
this.#tagValues = tags;
|
|
618
|
-
this.#announceTransition(false, value, tags.length);
|
|
619
|
-
this.dispatch("change", { detail: { tags } });
|
|
729
|
+
if (!this.#publishTags(tags, false, value)) return;
|
|
620
730
|
if (focus === "input") {
|
|
621
731
|
this.#focusInput();
|
|
622
732
|
return;
|
|
623
733
|
}
|
|
624
734
|
if (!this.#chipRow.focusAfterRemoval(index)) this.#focusInput();
|
|
625
735
|
}
|
|
626
|
-
|
|
736
|
+
get #chipDirectionElement() {
|
|
737
|
+
return this.hasTagsTarget ? this.tagsTarget : this.element;
|
|
738
|
+
}
|
|
627
739
|
#focusInput() {
|
|
628
740
|
if (this.hasInputTarget) this.inputTarget.focus();
|
|
629
741
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
*
|
|
633
|
-
* @stimeoRenderRoot
|
|
634
|
-
*/
|
|
635
|
-
#syncState(values, notify = false) {
|
|
742
|
+
#syncState(values) {
|
|
743
|
+
let fieldChanged = false;
|
|
636
744
|
if (this.hasFieldsTarget) {
|
|
637
745
|
const options = { name: this.nameValue, form: this.formValue };
|
|
638
|
-
|
|
639
|
-
commitField(this.fieldsTarget);
|
|
640
|
-
}
|
|
746
|
+
fieldChanged = writeFields(this.fieldsTarget, values, options);
|
|
641
747
|
}
|
|
642
|
-
const full = this
|
|
748
|
+
const full = this.#safeMax > 0 && values.length >= this.#safeMax;
|
|
643
749
|
this.element.toggleAttribute(`data-${this.identifier}-full`, full);
|
|
644
750
|
this.#chipRow.ensureTabStop();
|
|
751
|
+
return fieldChanged;
|
|
752
|
+
}
|
|
753
|
+
#publishTags(tags, added, value) {
|
|
754
|
+
const changed = !this.#sameTags(this.#tagValues, tags);
|
|
755
|
+
const fieldChanged = this.#syncState(tags);
|
|
756
|
+
this.#tagValues = tags;
|
|
757
|
+
if (!changed) return true;
|
|
758
|
+
const token = this.#moves.record();
|
|
759
|
+
if (fieldChanged) commitField(this.fieldsTarget);
|
|
760
|
+
if (!this.#moves.isLatest(token)) return false;
|
|
761
|
+
this.#announceTransition(added, value, tags.length);
|
|
762
|
+
if (!this.#moves.isLatest(token)) return false;
|
|
763
|
+
this.dispatch("change", { detail: { tags } });
|
|
764
|
+
return this.#moves.isLatest(token);
|
|
645
765
|
}
|
|
646
|
-
/** Repairs derived state after DOM/Turbo changes and reports a changed tag order. */
|
|
647
766
|
#reconcileTags() {
|
|
648
767
|
if (this.hasTagsTarget) this.#chipRow.connect(this.tagsTarget);
|
|
649
|
-
else this.#chipRow.disconnect();
|
|
650
768
|
const tags = this.#values;
|
|
651
769
|
this.#syncState(tags);
|
|
652
770
|
const changed = !this.#sameTags(this.#tagValues, tags);
|
|
653
771
|
this.#tagValues = tags;
|
|
654
|
-
if (changed)
|
|
772
|
+
if (changed) {
|
|
773
|
+
this.dispatch("reconcile", { detail: { tags } });
|
|
774
|
+
}
|
|
655
775
|
}
|
|
656
|
-
/**
|
|
657
|
-
* Sends one localized tag transition through the page's shared announcer.
|
|
658
|
-
*
|
|
659
|
-
* @stimeoRuntimeOnly The texts word the one announcement of this change.
|
|
660
|
-
*/
|
|
661
776
|
#announceTransition(added, value, count) {
|
|
662
777
|
const template = added ? this.announceTextValue : this.announceRemovedTextValue;
|
|
663
778
|
announce(fillTemplate(template, { label: value, value, count }));
|
|
664
779
|
}
|
|
665
|
-
/** Reports rejection with `{ value, reason: "duplicate" | "empty" | "max" }`. */
|
|
666
780
|
#reject(value, reason) {
|
|
667
781
|
this.dispatch("reject", { detail: { value, reason } });
|
|
668
782
|
}
|
|
669
|
-
/** Current tag values in order. */
|
|
670
783
|
get #values() {
|
|
671
784
|
return this.tagTargets.map((tag) => tag.dataset.value ?? "");
|
|
672
785
|
}
|
|
673
|
-
/** Whether two arrays carry the same values in the same submitted order. */
|
|
674
786
|
#sameTags(left, right) {
|
|
675
787
|
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
676
788
|
}
|
|
789
|
+
get #safeMax() {
|
|
790
|
+
return this.#numbers.read(
|
|
791
|
+
this,
|
|
792
|
+
"max",
|
|
793
|
+
this.maxValue,
|
|
794
|
+
_TagsInputController.values.max.default,
|
|
795
|
+
_TagsInputController.valueConstraints.max
|
|
796
|
+
);
|
|
797
|
+
}
|
|
677
798
|
};
|
|
678
799
|
|
|
679
800
|
export { TagsInputController };
|
|
680
|
-
//# sourceMappingURL=tags_input_controller.js.map
|
|
681
801
|
//# sourceMappingURL=tags_input_controller.js.map
|