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,84 +16,160 @@ function announce(message, options = {}) {
|
|
|
12
16
|
})
|
|
13
17
|
);
|
|
14
18
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
21
|
+
if (element.getAttribute(name) === value) return false;
|
|
22
|
+
if (value === null) element.removeAttribute(name);
|
|
23
|
+
else element.setAttribute(name, value);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sharedRegistry(key, create) {
|
|
28
|
+
const symbol = Symbol.for(key);
|
|
29
|
+
const scope = globalThis;
|
|
30
|
+
const existing = scope[symbol];
|
|
31
|
+
if (existing !== void 0) return existing;
|
|
32
|
+
const registry2 = create();
|
|
33
|
+
Object.defineProperty(globalThis, symbol, { value: registry2 });
|
|
34
|
+
return registry2;
|
|
20
35
|
}
|
|
21
36
|
|
|
22
|
-
|
|
37
|
+
var registry = sharedRegistry(
|
|
38
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
39
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
40
|
+
);
|
|
41
|
+
function isAttributeValue(value) {
|
|
42
|
+
return value === null || typeof value === "string";
|
|
43
|
+
}
|
|
44
|
+
function readLeaseRecord(element, record) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
47
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
48
|
+
const [original, written, ...rest] = parsed;
|
|
49
|
+
const linked = rest[0] !== false;
|
|
50
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
51
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
52
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
53
|
+
} catch {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function attributeStack(element, attribute) {
|
|
58
|
+
const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
59
|
+
registry.stacks.set(element, byAttribute);
|
|
60
|
+
const known = byAttribute.get(attribute);
|
|
61
|
+
if (known) return known;
|
|
62
|
+
const found = [];
|
|
63
|
+
for (const record of registry.names.get(attribute) ?? []) {
|
|
64
|
+
const read = readLeaseRecord(element, record);
|
|
65
|
+
if (read) found.push({ ...read, record });
|
|
66
|
+
}
|
|
67
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
68
|
+
const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
69
|
+
lease: null,
|
|
70
|
+
record,
|
|
71
|
+
written,
|
|
72
|
+
original,
|
|
73
|
+
linked: linked && index > 0
|
|
74
|
+
}));
|
|
75
|
+
byAttribute.set(attribute, stack);
|
|
76
|
+
return stack;
|
|
77
|
+
}
|
|
78
|
+
function findHold(stack, lease, record) {
|
|
79
|
+
return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
|
|
80
|
+
}
|
|
23
81
|
var AttributeLease = class {
|
|
24
82
|
#attribute;
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
83
|
+
#record;
|
|
84
|
+
#base;
|
|
85
|
+
#preserveAuthor;
|
|
86
|
+
#held = /* @__PURE__ */ new Set();
|
|
87
|
+
constructor(attribute, owner, options = {}) {
|
|
28
88
|
this.#attribute = attribute;
|
|
89
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
90
|
+
this.#base = options.base === true;
|
|
91
|
+
this.#preserveAuthor = options.preserveAuthor === true;
|
|
92
|
+
const names = registry.names;
|
|
93
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
29
94
|
}
|
|
30
|
-
/** Writes or removes the leased attribute while preserving its authored value. */
|
|
31
95
|
write(element, value) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
this.#reflect(element, value);
|
|
96
|
+
const stack = attributeStack(element, this.#attribute);
|
|
97
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
98
|
+
this.#held.add(element);
|
|
99
|
+
const shows = !this.#covered(element, stack, hold);
|
|
100
|
+
hold.written = value;
|
|
101
|
+
this.#mark(element, stack);
|
|
102
|
+
if (shows) this.#reflect(element, value);
|
|
42
103
|
}
|
|
43
|
-
/** Returns one lease without overwriting a value subsequently authored by a consumer. */
|
|
44
104
|
return(element) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
this.#
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
105
|
+
const held = this.#held.delete(element);
|
|
106
|
+
const known = registry.stacks.get(element)?.has(this.#attribute);
|
|
107
|
+
const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
|
|
108
|
+
const hold = this.#take(stack);
|
|
109
|
+
if (!hold) return false;
|
|
110
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
111
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
112
|
+
const index = stack.indexOf(hold);
|
|
113
|
+
stack.splice(index, 1);
|
|
114
|
+
const above = stack[index];
|
|
115
|
+
if (above) above.linked &&= hold.linked;
|
|
116
|
+
this.#mark(element, stack);
|
|
117
|
+
if (!owns) return true;
|
|
118
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
119
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
120
|
+
return true;
|
|
56
121
|
}
|
|
57
|
-
/** Returns every outstanding lease using the same ownership check as {@link return}. */
|
|
58
122
|
returnAll() {
|
|
59
|
-
for (const element of Array.from(this.#
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
124
|
+
}
|
|
125
|
+
#take(stack) {
|
|
126
|
+
const hold = findHold(stack, this, 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 ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : 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
|
+
setAttributeIfChanged(element, name, recorded);
|
|
82
166
|
}
|
|
83
167
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
_BeforeCacheReset.#subscribers.delete(this);
|
|
87
|
-
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
88
|
-
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
168
|
+
#reflect(element, value) {
|
|
169
|
+
setAttributeIfChanged(element, this.#attribute, value);
|
|
89
170
|
}
|
|
90
171
|
};
|
|
91
172
|
|
|
92
|
-
// src/utils/focus_candidate.ts
|
|
93
173
|
function inheritsFieldsetDisabled(control) {
|
|
94
174
|
let fieldset = control.closest("fieldset[disabled]");
|
|
95
175
|
while (fieldset) {
|
|
@@ -99,8 +179,19 @@ function inheritsFieldsetDisabled(control) {
|
|
|
99
179
|
}
|
|
100
180
|
return false;
|
|
101
181
|
}
|
|
182
|
+
function unslotted(element) {
|
|
183
|
+
return element.assignedSlot === null && element.parentElement?.shadowRoot != null;
|
|
184
|
+
}
|
|
185
|
+
function flatTreeParent(element) {
|
|
186
|
+
if (unslotted(element)) return null;
|
|
187
|
+
const parent = element.assignedSlot ?? element.parentNode;
|
|
188
|
+
if (parent instanceof ShadowRoot) return parent.host;
|
|
189
|
+
return parent instanceof Element ? parent : null;
|
|
190
|
+
}
|
|
102
191
|
function canTakeFocus(element) {
|
|
103
|
-
|
|
192
|
+
for (let node = element; node; node = flatTreeParent(node)) {
|
|
193
|
+
if (unslotted(node) || node.matches("[hidden], [inert]")) return false;
|
|
194
|
+
}
|
|
104
195
|
if (element instanceof HTMLInputElement && element.type === "hidden") return false;
|
|
105
196
|
if (!("disabled" in element)) return true;
|
|
106
197
|
if (element.disabled) return false;
|
|
@@ -163,28 +254,42 @@ function firstTabStop(root) {
|
|
|
163
254
|
}
|
|
164
255
|
return null;
|
|
165
256
|
}
|
|
257
|
+
function deepActiveElement(ownerDocument) {
|
|
258
|
+
let active = ownerDocument.activeElement;
|
|
259
|
+
while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
|
|
260
|
+
return active;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
var ListenerSet = class {
|
|
264
|
+
#abort = new AbortController();
|
|
265
|
+
add(target, type, handler, options) {
|
|
266
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
267
|
+
}
|
|
268
|
+
dispose() {
|
|
269
|
+
this.#abort.abort();
|
|
270
|
+
this.#abort = new AbortController();
|
|
271
|
+
}
|
|
272
|
+
};
|
|
166
273
|
|
|
167
|
-
// src/utils/microtask_coalescer.ts
|
|
168
274
|
var MicrotaskCoalescer = class {
|
|
169
275
|
#run;
|
|
170
276
|
#queued = false;
|
|
171
277
|
#active = false;
|
|
172
278
|
#generation = 0;
|
|
173
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
174
279
|
constructor(run) {
|
|
175
280
|
this.#run = run;
|
|
176
281
|
}
|
|
177
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
178
282
|
activate() {
|
|
179
283
|
this.#active = true;
|
|
180
284
|
}
|
|
181
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
182
285
|
cancel() {
|
|
183
286
|
this.#active = false;
|
|
184
287
|
this.#queued = false;
|
|
185
288
|
this.#generation += 1;
|
|
186
289
|
}
|
|
187
|
-
|
|
290
|
+
get pending() {
|
|
291
|
+
return this.#queued;
|
|
292
|
+
}
|
|
188
293
|
schedule() {
|
|
189
294
|
if (!this.#active || this.#queued) return;
|
|
190
295
|
this.#queued = true;
|
|
@@ -197,56 +302,213 @@ var MicrotaskCoalescer = class {
|
|
|
197
302
|
}
|
|
198
303
|
};
|
|
199
304
|
|
|
200
|
-
|
|
201
|
-
|
|
305
|
+
var MorphRenderWatcher = class {
|
|
306
|
+
#listeners = new ListenerSet();
|
|
307
|
+
#pass;
|
|
308
|
+
#element = null;
|
|
309
|
+
#onMorph = () => this.#pass.schedule();
|
|
310
|
+
constructor(run) {
|
|
311
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
312
|
+
}
|
|
313
|
+
observe(element) {
|
|
314
|
+
if (this.#element === element) return;
|
|
315
|
+
this.disconnect();
|
|
316
|
+
this.#element = element;
|
|
317
|
+
this.#pass.activate();
|
|
318
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
319
|
+
}
|
|
320
|
+
schedule() {
|
|
321
|
+
this.#pass.schedule();
|
|
322
|
+
}
|
|
323
|
+
get pending() {
|
|
324
|
+
return this.#pass.pending;
|
|
325
|
+
}
|
|
326
|
+
disconnect() {
|
|
327
|
+
this.#listeners.dispose();
|
|
328
|
+
this.#pass.cancel();
|
|
329
|
+
this.#element = null;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
var NUMBER_BOUNDS = {
|
|
334
|
+
nonNegative: { finite: true, min: 0 }};
|
|
335
|
+
function matchesNumberBounds(value, bounds) {
|
|
336
|
+
if (!Number.isFinite(value)) {
|
|
337
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
338
|
+
if (direction === null) return false;
|
|
339
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
340
|
+
}
|
|
341
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
342
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
343
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
344
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
345
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function readNumber(raw, fallback, bounds) {
|
|
350
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
var NumberValueReader = class {
|
|
354
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
355
|
+
read(owner, name, raw, fallback, bounds) {
|
|
356
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
357
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
358
|
+
this.#lastRejected.delete(name);
|
|
359
|
+
return resolved;
|
|
360
|
+
}
|
|
361
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
362
|
+
const literal = owner.element.getAttribute(attribute);
|
|
363
|
+
if (literal === null) {
|
|
364
|
+
this.#lastRejected.delete(name);
|
|
365
|
+
return resolved;
|
|
366
|
+
}
|
|
367
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
368
|
+
this.#lastRejected.set(name, literal);
|
|
369
|
+
console.warn(
|
|
370
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
return resolved;
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
function queryOpenDescendants(root, selector) {
|
|
378
|
+
const matches = [];
|
|
379
|
+
const roots = [root];
|
|
380
|
+
for (const scope of roots) {
|
|
381
|
+
matches.push(...scope.querySelectorAll(selector));
|
|
382
|
+
if (scope.nodeType === Node.ELEMENT_NODE) {
|
|
383
|
+
const shadow = scope.shadowRoot;
|
|
384
|
+
if (shadow) roots.push(shadow);
|
|
385
|
+
}
|
|
386
|
+
for (const element of scope.querySelectorAll("*")) {
|
|
387
|
+
if (element.shadowRoot) roots.push(element.shadowRoot);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return matches;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function holdsFocus(element) {
|
|
394
|
+
return element.matches(":focus") || deepActiveElement(element.ownerDocument) === element;
|
|
395
|
+
}
|
|
396
|
+
var TabindexLoan = class _TabindexLoan {
|
|
397
|
+
static #registry = sharedRegistry(
|
|
398
|
+
"stimeo-ui.tabindex-loan.registry.v1",
|
|
399
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
|
|
400
|
+
);
|
|
202
401
|
#value;
|
|
402
|
+
#record;
|
|
403
|
+
#preserveFocus;
|
|
203
404
|
#lent = /* @__PURE__ */ new Set();
|
|
204
|
-
|
|
205
|
-
#beforeCache = new BeforeCacheReset(() => this.returnAll());
|
|
206
|
-
/**
|
|
207
|
-
* @param value - the `tabindex` to lend. `"-1"` (the default) is
|
|
208
|
-
* programmatically focusable but not a Tab stop; `"0"` is a real Tab stop,
|
|
209
|
-
* which a scroll region with no focusable content of its own needs.
|
|
210
|
-
*/
|
|
211
|
-
constructor(value = "-1") {
|
|
405
|
+
constructor(value, owner, options = {}) {
|
|
212
406
|
this.#value = value;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
407
|
+
this.#record = `data-${owner}-tabindex-loan`;
|
|
408
|
+
this.#preserveFocus = options.preserveFocus === true;
|
|
409
|
+
_TabindexLoan.#registry.names.add(this.#record);
|
|
410
|
+
}
|
|
411
|
+
lend(element, options = {}) {
|
|
412
|
+
const stack = this.#stack(element);
|
|
413
|
+
const current = element.getAttribute("tabindex");
|
|
414
|
+
const held = stack.find((hold) => hold.loan === this) ?? stack.find(
|
|
415
|
+
(hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
|
|
416
|
+
);
|
|
417
|
+
const linked = stack.at(-1)?.value === current;
|
|
418
|
+
if (!held && options.onlyIfUnfocusable && (current !== null || element.tabIndex >= 0)) return;
|
|
419
|
+
if (!held && current !== null && !linked) return;
|
|
218
420
|
this.#lent.add(element);
|
|
219
|
-
|
|
421
|
+
if (held) held.loan = this;
|
|
422
|
+
else stack.push({ loan: this, record: this.#record, value: this.#value, linked });
|
|
423
|
+
this.#mark(element, stack);
|
|
424
|
+
if (current === null || !held && current !== this.#value) {
|
|
425
|
+
element.setAttribute("tabindex", this.#value);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return(element) {
|
|
429
|
+
const stack = this.#stack(element);
|
|
430
|
+
const hold = stack.find((held) => held.loan === this) ?? stack.find(
|
|
431
|
+
(held) => held.loan === null && held.record === this.#record && held.value === this.#value
|
|
432
|
+
);
|
|
433
|
+
if (!hold) return false;
|
|
434
|
+
if (!this.#release(element, stack, hold, this.#preserveFocus)) {
|
|
435
|
+
hold.loan = this;
|
|
436
|
+
this.#lent.add(element);
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
this.#lent.delete(element);
|
|
440
|
+
return true;
|
|
220
441
|
}
|
|
221
|
-
/** Takes back every loan whose value is still the one that was lent. */
|
|
222
442
|
returnAll() {
|
|
223
|
-
for (const element of this.#lent)
|
|
224
|
-
|
|
443
|
+
for (const element of Array.from(this.#lent)) this.return(element);
|
|
444
|
+
}
|
|
445
|
+
reclaim(element) {
|
|
446
|
+
if (element.getAttribute(this.#record) !== this.#value) return;
|
|
447
|
+
const stack = this.#stack(element);
|
|
448
|
+
const hold = stack.find((held) => held.loan === null && held.record === this.#record);
|
|
449
|
+
if (hold && !holdsFocus(element)) this.#release(element, stack, hold);
|
|
450
|
+
}
|
|
451
|
+
reclaimWithin(root) {
|
|
452
|
+
for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
|
|
453
|
+
this.reclaim(element);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
#stack(element) {
|
|
457
|
+
const stacks = _TabindexLoan.#registry.stacks;
|
|
458
|
+
const stack = stacks.get(element) ?? this.#seed(element);
|
|
459
|
+
stacks.set(element, stack);
|
|
460
|
+
return stack;
|
|
461
|
+
}
|
|
462
|
+
#seed(element) {
|
|
463
|
+
const current = element.getAttribute("tabindex");
|
|
464
|
+
const holds = [];
|
|
465
|
+
for (const record of _TabindexLoan.#registry.names) {
|
|
466
|
+
const value = element.getAttribute(record);
|
|
467
|
+
if (value !== null) holds.push({ loan: null, record, value, linked: true });
|
|
468
|
+
}
|
|
469
|
+
holds.sort((one, other) => Number(one.value === current) - Number(other.value === current));
|
|
470
|
+
for (const [index, hold] of holds.entries()) hold.linked = index > 0;
|
|
471
|
+
return holds;
|
|
472
|
+
}
|
|
473
|
+
#release(element, stack, hold, preserveFocus = false) {
|
|
474
|
+
const current = element.getAttribute("tabindex");
|
|
475
|
+
const index = stack.indexOf(hold);
|
|
476
|
+
const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
|
|
477
|
+
if (owns && preserveFocus && holdsFocus(element)) return false;
|
|
478
|
+
stack.splice(index, 1);
|
|
479
|
+
const above = stack[index];
|
|
480
|
+
if (above) above.linked &&= hold.linked;
|
|
481
|
+
this.#mark(element, stack);
|
|
482
|
+
if (!owns) return true;
|
|
483
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
484
|
+
if (!below) element.removeAttribute("tabindex");
|
|
485
|
+
else if (below.value !== current) element.setAttribute("tabindex", below.value);
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
#mark(element, stack) {
|
|
489
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
490
|
+
for (const hold of stack) records.set(hold.record, hold.value);
|
|
491
|
+
for (const [name, recorded] of records) {
|
|
492
|
+
setAttributeIfChanged(element, name, recorded);
|
|
225
493
|
}
|
|
226
|
-
this.#lent.clear();
|
|
227
|
-
this.#beforeCache.deactivate();
|
|
228
494
|
}
|
|
229
495
|
};
|
|
230
496
|
|
|
231
|
-
// src/utils/target_selector.ts
|
|
232
497
|
function targetSelector(identifier, name) {
|
|
233
498
|
return `[data-${identifier}-target~="${name}"]`;
|
|
234
499
|
}
|
|
235
500
|
|
|
236
|
-
// src/controllers/nested_form_controller.ts
|
|
237
501
|
var REMOVE_PART = "remove";
|
|
238
502
|
var DESTROY_FLAG_PART = "destroyFlag";
|
|
239
503
|
var DESTROYED_VALUES = /* @__PURE__ */ new Set(["1", "true"]);
|
|
240
|
-
var NestedFormController = class extends Controller {
|
|
241
|
-
|
|
504
|
+
var NestedFormController = class _NestedFormController extends Controller {
|
|
505
|
+
#numbers = new NumberValueReader();
|
|
242
506
|
get #rootSelector() {
|
|
243
507
|
return `[data-controller~="${this.identifier}"]`;
|
|
244
508
|
}
|
|
245
|
-
/** Selects per-row remove buttons, in the namespace this controller is registered under. */
|
|
246
509
|
get #removeSelector() {
|
|
247
510
|
return targetSelector(this.identifier, REMOVE_PART);
|
|
248
511
|
}
|
|
249
|
-
/** Selects the hidden `_destroy` input a persisted row carries. */
|
|
250
512
|
get #destroyFlagSelector() {
|
|
251
513
|
return targetSelector(this.identifier, DESTROY_FLAG_PART);
|
|
252
514
|
}
|
|
@@ -258,27 +520,20 @@ var NestedFormController = class extends Controller {
|
|
|
258
520
|
announce: { type: Boolean, default: true },
|
|
259
521
|
countMessage: { type: String, default: "" }
|
|
260
522
|
};
|
|
523
|
+
static valueConstraints = {
|
|
524
|
+
min: NUMBER_BOUNDS.nonNegative,
|
|
525
|
+
max: NUMBER_BOUNDS.nonNegative
|
|
526
|
+
};
|
|
261
527
|
static actions = ["add"];
|
|
262
528
|
static events = ["add", "remove", "reconcile"];
|
|
263
|
-
/** Monotonic source for unique row indices; never a row-state counter. */
|
|
264
529
|
#lastIndex = 0;
|
|
265
530
|
#warnedMissing = false;
|
|
266
531
|
#warnedTemplate = false;
|
|
267
|
-
/** The state last written to the hooks; reconciliation reports only real moves. */
|
|
268
532
|
#published = null;
|
|
269
|
-
/** Watches the list for row changes the controller did not perform itself. */
|
|
270
533
|
#observer = null;
|
|
271
|
-
#reconcile = new
|
|
272
|
-
|
|
273
|
-
#
|
|
274
|
-
#beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
|
|
275
|
-
/** Makes the root a programmatic focus destination when no other candidate survives. */
|
|
276
|
-
#tabindex = new TabindexLoan();
|
|
277
|
-
/**
|
|
278
|
-
* Delegated click handler for the per-row remove buttons (dynamic-safe). Only
|
|
279
|
-
* buttons whose nearest nested-form root is this instance are acted on, so a
|
|
280
|
-
* nested inner form's buttons never remove an outer row.
|
|
281
|
-
*/
|
|
534
|
+
#reconcile = new MorphRenderWatcher(() => this.#reconcileNow());
|
|
535
|
+
#addDisabled = new AttributeLease("disabled", this.identifier);
|
|
536
|
+
#tabindex = new TabindexLoan("-1", this.identifier);
|
|
282
537
|
#onClick = (event) => {
|
|
283
538
|
const target = event.target;
|
|
284
539
|
const button = target?.closest(this.#removeSelector);
|
|
@@ -286,41 +541,33 @@ var NestedFormController = class extends Controller {
|
|
|
286
541
|
const row = this.#rowContaining(button);
|
|
287
542
|
if (row) this.#removeRow(row);
|
|
288
543
|
};
|
|
544
|
+
#listeners = new ListenerSet();
|
|
289
545
|
connect() {
|
|
546
|
+
this.#tabindex.reclaim(this.element);
|
|
290
547
|
this.#warnedMissing = false;
|
|
291
548
|
this.#warnedTemplate = false;
|
|
292
|
-
this.element
|
|
293
|
-
this.#reconcile.
|
|
294
|
-
this.#beforeCache.activate();
|
|
549
|
+
this.#listeners.add(this.element, "click", this.#onClick);
|
|
550
|
+
this.#reconcile.observe(this.element);
|
|
295
551
|
if (!this.hasListTarget || !this.hasTemplateTarget) this.#warnMissing();
|
|
296
552
|
this.#refresh();
|
|
297
553
|
}
|
|
298
554
|
disconnect() {
|
|
299
|
-
this.
|
|
555
|
+
this.#listeners.dispose();
|
|
300
556
|
this.#observer?.disconnect();
|
|
301
557
|
this.#observer = null;
|
|
302
|
-
this.#reconcile.
|
|
303
|
-
this.#
|
|
304
|
-
this.#rewindForCache();
|
|
558
|
+
this.#reconcile.disconnect();
|
|
559
|
+
this.#addDisabled.returnAll();
|
|
305
560
|
this.#tabindex.returnAll();
|
|
306
561
|
this.#published = null;
|
|
307
562
|
}
|
|
308
|
-
/** Follows an arriving or swapped-in list: rebind to the primary, then reconcile. */
|
|
309
563
|
listTargetConnected() {
|
|
310
564
|
this.#rebindObserver();
|
|
311
565
|
this.#reconcile.schedule();
|
|
312
566
|
}
|
|
313
|
-
/** Follows a departing list the same way — the primary may have changed. */
|
|
314
567
|
listTargetDisconnected() {
|
|
315
568
|
this.#rebindObserver();
|
|
316
569
|
this.#reconcile.schedule();
|
|
317
570
|
}
|
|
318
|
-
/**
|
|
319
|
-
* Points the observer at the current primary list. Re-deriving on every list
|
|
320
|
-
* arrival and departure makes the binding independent of the order Stimulus
|
|
321
|
-
* reports an overlapping swap in — a staggered swap (successor appended before
|
|
322
|
-
* the old list leaves) ends observed and reconciled either way.
|
|
323
|
-
*/
|
|
324
571
|
#rebindObserver() {
|
|
325
572
|
this.#observer?.disconnect();
|
|
326
573
|
this.#observer = null;
|
|
@@ -333,7 +580,6 @@ var NestedFormController = class extends Controller {
|
|
|
333
580
|
attributeFilter: ["value"]
|
|
334
581
|
});
|
|
335
582
|
}
|
|
336
|
-
/** Returns the lease with a departing add button; a new one re-arms on refresh. */
|
|
337
583
|
addTargetDisconnected(target) {
|
|
338
584
|
this.#addDisabled.return(target);
|
|
339
585
|
this.#reconcile.schedule();
|
|
@@ -341,21 +587,12 @@ var NestedFormController = class extends Controller {
|
|
|
341
587
|
addTargetConnected() {
|
|
342
588
|
this.#reconcile.schedule();
|
|
343
589
|
}
|
|
344
|
-
/** Re-clamps when application code or a Turbo morph changes `min`. */
|
|
345
590
|
minValueChanged() {
|
|
346
591
|
this.#reconcile.schedule();
|
|
347
592
|
}
|
|
348
|
-
/** Re-clamps when application code or a Turbo morph changes `max`. */
|
|
349
593
|
maxValueChanged() {
|
|
350
594
|
this.#reconcile.schedule();
|
|
351
595
|
}
|
|
352
|
-
/**
|
|
353
|
-
* Clones the template row, replaces the index placeholder with a unique value,
|
|
354
|
-
* appends it, focuses its first tab stop, and announces the new count. No-ops at
|
|
355
|
-
* `max`, when the required targets are missing (named on the console once per
|
|
356
|
-
* connection), or when the template does not produce exactly one root element
|
|
357
|
-
* (also named once; the insertion is rolled back so nothing accumulates).
|
|
358
|
-
*/
|
|
359
596
|
add() {
|
|
360
597
|
if (!this.hasListTarget || !this.hasTemplateTarget) {
|
|
361
598
|
this.#warnMissing();
|
|
@@ -386,13 +623,6 @@ var NestedFormController = class extends Controller {
|
|
|
386
623
|
this.dispatch("add", { detail: { index, element: row } });
|
|
387
624
|
this.#announce();
|
|
388
625
|
}
|
|
389
|
-
/**
|
|
390
|
-
* Removes a row: a persisted row (one carrying its own `destroyFlag`) has the
|
|
391
|
-
* flag set to `1` and is hidden so Rails destroys it on submit; an unsaved row
|
|
392
|
-
* is dropped from the DOM. Returns focus to a surviving row. No-ops at `min`.
|
|
393
|
-
*
|
|
394
|
-
* @stimeoRuntimeOnly `min` decides whether this one removal is allowed.
|
|
395
|
-
*/
|
|
396
626
|
#removeRow(row) {
|
|
397
627
|
const rows = this.#effectiveRows;
|
|
398
628
|
if (this.#destroyed(row)) {
|
|
@@ -400,7 +630,7 @@ var NestedFormController = class extends Controller {
|
|
|
400
630
|
this.#focusAfterRemove(this.#positionAmong(rows, row));
|
|
401
631
|
return;
|
|
402
632
|
}
|
|
403
|
-
if (rows.length <= this
|
|
633
|
+
if (rows.length <= this.#safeMin) return;
|
|
404
634
|
const position = rows.indexOf(row);
|
|
405
635
|
const flag = this.#destroyFlagOf(row);
|
|
406
636
|
const persisted = flag !== null;
|
|
@@ -415,21 +645,11 @@ var NestedFormController = class extends Controller {
|
|
|
415
645
|
this.dispatch("remove", { detail: { element: row, persisted } });
|
|
416
646
|
this.#announce();
|
|
417
647
|
}
|
|
418
|
-
/**
|
|
419
|
-
* The index of the first effective row following `row` in document order.
|
|
420
|
-
* -1 (no following row) feeds the focus slices as a negative index, which
|
|
421
|
-
* yields the same fully-reversed nearest-first order as `rows.length` would.
|
|
422
|
-
*/
|
|
423
648
|
#positionAmong(rows, row) {
|
|
424
649
|
return rows.findIndex(
|
|
425
650
|
(candidate) => (row.compareDocumentPosition(candidate) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0
|
|
426
651
|
);
|
|
427
652
|
}
|
|
428
|
-
/**
|
|
429
|
-
* Moves focus to the first tab stop of the nearest surviving row — following
|
|
430
|
-
* rows first, then preceding ones — falling back to the add button and finally
|
|
431
|
-
* to the root via a temporary `tabindex`.
|
|
432
|
-
*/
|
|
433
653
|
#focusAfterRemove(position) {
|
|
434
654
|
const rows = this.#effectiveRows;
|
|
435
655
|
const ordered = [...rows.slice(position), ...rows.slice(0, position).reverse()];
|
|
@@ -447,31 +667,20 @@ var NestedFormController = class extends Controller {
|
|
|
447
667
|
this.#tabindex.lend(this.element);
|
|
448
668
|
this.element.focus();
|
|
449
669
|
}
|
|
450
|
-
/**
|
|
451
|
-
* Recomputes the count and min/max hooks from the DOM and records them as published.
|
|
452
|
-
*
|
|
453
|
-
* @stimeoRenderRoot
|
|
454
|
-
*/
|
|
455
670
|
#refresh() {
|
|
456
671
|
if (!this.hasListTarget) return;
|
|
457
672
|
const count = this.#effectiveRows.length;
|
|
458
|
-
const atMin = count <= this
|
|
459
|
-
const atMax = this
|
|
673
|
+
const atMin = count <= this.#safeMin;
|
|
674
|
+
const atMax = this.#safeMax > 0 && count >= this.#safeMax;
|
|
460
675
|
this.element.setAttribute("data-nested-count", String(count));
|
|
461
676
|
this.#reflect("data-nested-at-max", atMax);
|
|
462
677
|
this.#reflect("data-nested-at-min", atMin);
|
|
463
678
|
if (this.hasAddTarget) {
|
|
464
|
-
if (this
|
|
679
|
+
if (this.#safeMax > 0) this.#addDisabled.write(this.addTarget, atMax ? "" : null);
|
|
465
680
|
else this.#addDisabled.return(this.addTarget);
|
|
466
681
|
}
|
|
467
682
|
this.#published = { count, atMin, atMax };
|
|
468
683
|
}
|
|
469
|
-
/**
|
|
470
|
-
* Applies row changes the controller did not perform itself (Turbo Streams,
|
|
471
|
-
* morphs, runtime Value changes): refreshes the hooks and reports a moved
|
|
472
|
-
* public state as `reconcile`. The controller's own operations refresh
|
|
473
|
-
* synchronously first, so their observer echo arrives here as a no-move.
|
|
474
|
-
*/
|
|
475
684
|
#reconcileNow() {
|
|
476
685
|
const previous = this.#published;
|
|
477
686
|
this.#refresh();
|
|
@@ -480,25 +689,14 @@ var NestedFormController = class extends Controller {
|
|
|
480
689
|
const moved = previous.count !== current.count || previous.atMin !== current.atMin || previous.atMax !== current.atMax;
|
|
481
690
|
if (moved) this.dispatch("reconcile", { detail: { ...current } });
|
|
482
691
|
}
|
|
483
|
-
/**
|
|
484
|
-
* Bridges the count change to the shared announcer when configured.
|
|
485
|
-
*
|
|
486
|
-
* @stimeoRuntimeOnly `announce` and `countMessage` word the one announcement of this change.
|
|
487
|
-
*/
|
|
488
692
|
#announce() {
|
|
489
693
|
if (!this.announceValue || this.countMessageValue === "") return;
|
|
490
694
|
announce(fillTemplate(this.countMessageValue, { count: this.#effectiveRows.length }));
|
|
491
695
|
}
|
|
492
|
-
/** Sets `attribute` to `"true"` when `on`, else removes it. */
|
|
493
696
|
#reflect(attribute, on) {
|
|
494
697
|
if (on) this.element.setAttribute(attribute, "true");
|
|
495
698
|
else this.element.removeAttribute(attribute);
|
|
496
699
|
}
|
|
497
|
-
/** Returns the disabled lease so an authored value never leaks into a snapshot. */
|
|
498
|
-
#rewindForCache() {
|
|
499
|
-
if (this.hasAddTarget) this.#addDisabled.return(this.addTarget);
|
|
500
|
-
}
|
|
501
|
-
/** Names the missing required target(s) once per connection. */
|
|
502
700
|
#warnMissing() {
|
|
503
701
|
if (this.#warnedMissing) return;
|
|
504
702
|
this.#warnedMissing = true;
|
|
@@ -510,49 +708,38 @@ var NestedFormController = class extends Controller {
|
|
|
510
708
|
`Stimeo UI: "${this.identifier}" cannot manage rows because its markup lacks ${missing}.`
|
|
511
709
|
);
|
|
512
710
|
}
|
|
513
|
-
/** Names a template that does not produce exactly one element, once per connection. */
|
|
514
711
|
#warnBadTemplate(produced) {
|
|
515
712
|
if (this.#warnedTemplate) return;
|
|
516
713
|
this.#warnedTemplate = true;
|
|
517
714
|
const reason = produced === 0 ? "produces no element" : "must produce exactly one root element";
|
|
518
715
|
console.warn(`Stimeo UI: "${this.identifier}" added no row because its template ${reason}.`);
|
|
519
716
|
}
|
|
520
|
-
/** A strictly-increasing unique index (collision-free even on rapid adds). */
|
|
521
717
|
#nextIndex() {
|
|
522
718
|
const index = Math.max(Date.now(), this.#lastIndex + 1);
|
|
523
719
|
this.#lastIndex = index;
|
|
524
720
|
return index;
|
|
525
721
|
}
|
|
526
722
|
get #atMax() {
|
|
527
|
-
return this
|
|
723
|
+
return this.#safeMax > 0 && this.#effectiveRows.length >= this.#safeMax;
|
|
528
724
|
}
|
|
529
|
-
/**
|
|
530
|
-
* Direct child rows of the list whose own destroy flag is not set. Callers
|
|
531
|
-
* reach this only behind a list-presence gate (`add`, `#refresh`, and the
|
|
532
|
-
* click path through `#rowContaining`).
|
|
533
|
-
*/
|
|
534
725
|
get #effectiveRows() {
|
|
535
726
|
return Array.from(this.listTarget.children).filter(
|
|
536
727
|
(row) => !this.#destroyed(row)
|
|
537
728
|
);
|
|
538
729
|
}
|
|
539
|
-
/** Whether `row` is flagged for destruction; the flag value is the truth source. */
|
|
540
730
|
#destroyed(row) {
|
|
541
731
|
const flag = this.#destroyFlagOf(row);
|
|
542
732
|
return flag !== null && DESTROYED_VALUES.has(flag.value);
|
|
543
733
|
}
|
|
544
|
-
/** The row's own destroy flag, skipping flags owned by a nested inner form. */
|
|
545
734
|
#destroyFlagOf(row) {
|
|
546
735
|
for (const flag of row.querySelectorAll(this.#destroyFlagSelector)) {
|
|
547
736
|
if (this.#ownerOf(flag) === this.element) return flag;
|
|
548
737
|
}
|
|
549
738
|
return null;
|
|
550
739
|
}
|
|
551
|
-
/** The nearest nested-form root that owns `el`. */
|
|
552
740
|
#ownerOf(el) {
|
|
553
741
|
return el.closest(this.#rootSelector);
|
|
554
742
|
}
|
|
555
|
-
/** The nearest ancestor of `el` that is a direct child of the list, else null. */
|
|
556
743
|
#rowContaining(el) {
|
|
557
744
|
if (!this.hasListTarget) return null;
|
|
558
745
|
let node = el;
|
|
@@ -561,8 +748,25 @@ var NestedFormController = class extends Controller {
|
|
|
561
748
|
}
|
|
562
749
|
return node;
|
|
563
750
|
}
|
|
751
|
+
get #safeMin() {
|
|
752
|
+
return this.#numbers.read(
|
|
753
|
+
this,
|
|
754
|
+
"min",
|
|
755
|
+
this.minValue,
|
|
756
|
+
_NestedFormController.values.min.default,
|
|
757
|
+
_NestedFormController.valueConstraints.min
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
get #safeMax() {
|
|
761
|
+
return this.#numbers.read(
|
|
762
|
+
this,
|
|
763
|
+
"max",
|
|
764
|
+
this.maxValue,
|
|
765
|
+
_NestedFormController.values.max.default,
|
|
766
|
+
_NestedFormController.valueConstraints.max
|
|
767
|
+
);
|
|
768
|
+
}
|
|
564
769
|
};
|
|
565
770
|
|
|
566
771
|
export { NestedFormController };
|
|
567
|
-
//# sourceMappingURL=nested_form_controller.js.map
|
|
568
772
|
//# sourceMappingURL=nested_form_controller.js.map
|