stimeo-ui 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +8 -1
- package/dist/cable/index.d.ts +79 -23
- package/dist/cable/index.js +363 -436
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +4 -2
- package/dist/controllers/accordion_controller.js +166 -55
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +12 -2
- package/dist/controllers/alert_dialog_controller.js +862 -221
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +35 -5
- package/dist/controllers/announcer_controller.js +135 -198
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.js +0 -10
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +30 -6
- package/dist/controllers/auto_submit_controller.js +271 -127
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +171 -105
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.js +0 -130
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +18 -0
- package/dist/controllers/bulk_select_controller.js +262 -52
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +24 -5
- package/dist/controllers/calendar_controller.js +175 -209
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +34 -7
- package/dist/controllers/carousel_controller.js +461 -399
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +17 -1
- package/dist/controllers/character_counter_controller.js +246 -156
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +7 -2
- package/dist/controllers/checkbox_controller.js +32 -74
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +16 -5
- package/dist/controllers/clipboard_controller.js +168 -147
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +16 -4
- package/dist/controllers/collapsible_controller.js +301 -120
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +18 -6
- package/dist/controllers/color_picker_controller.js +149 -139
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +23 -5
- package/dist/controllers/combobox_controller.js +360 -133
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +45 -14
- package/dist/controllers/command_palette_controller.js +970 -358
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +15 -2
- package/dist/controllers/conditional_fields_controller.js +166 -124
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +49 -12
- package/dist/controllers/confirm_controller.js +939 -248
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +31 -4
- package/dist/controllers/context_menu_controller.js +256 -128
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +13 -1
- package/dist/controllers/count_up_controller.js +73 -21
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +45 -1
- package/dist/controllers/countdown_controller.js +183 -148
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +21 -2
- package/dist/controllers/currency_input_controller.js +134 -158
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +15 -4
- package/dist/controllers/data_grid_controller.js +114 -186
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +30 -5
- package/dist/controllers/date_range_picker_controller.js +288 -206
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +13 -1
- package/dist/controllers/dialog_controller.js +858 -223
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +24 -7
- package/dist/controllers/direct_upload_controller.js +73 -153
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.js +14 -48
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +19 -0
- package/dist/controllers/dismissible_controller.js +175 -21
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +39 -11
- package/dist/controllers/drawer_controller.js +933 -341
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +31 -1
- package/dist/controllers/dropdown_controller.js +312 -82
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +158 -68
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +67 -53
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +60 -18
- package/dist/controllers/file_dropzone_controller.js +311 -207
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.js +67 -39
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +31 -8
- package/dist/controllers/flash_controller.js +172 -314
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +12 -3
- package/dist/controllers/focus_controller.js +654 -240
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +147 -132
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +13 -86
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +41 -9
- package/dist/controllers/frame_loading_controller.js +264 -213
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +22 -2
- package/dist/controllers/highlight_controller.js +107 -75
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +56 -3
- package/dist/controllers/hover_card_controller.js +309 -118
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +42 -1
- package/dist/controllers/idle_controller.js +267 -101
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.js +71 -72
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.d.ts +32 -13
- package/dist/controllers/intersection_controller.js +147 -106
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +16 -0
- package/dist/controllers/lazy_frame_controller.js +9 -65
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +34 -2
- package/dist/controllers/listbox_controller.js +306 -204
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +1 -1
- package/dist/controllers/local_time_controller.js +54 -64
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +11 -3
- package/dist/controllers/masonry_controller.js +70 -93
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +32 -7
- package/dist/controllers/menu_controller.js +214 -156
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +4 -4
- package/dist/controllers/menubar_controller.js +170 -329
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +20 -0
- package/dist/controllers/meter_controller.js +151 -67
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +32 -4
- package/dist/controllers/multi_select_controller.js +593 -358
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +8 -1
- package/dist/controllers/navigation_menu_controller.js +124 -232
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +10 -0
- package/dist/controllers/nested_form_controller.js +352 -182
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +23 -0
- package/dist/controllers/network_status_controller.js +242 -54
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +26 -2
- package/dist/controllers/number_input_controller.js +320 -263
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +16 -76
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +20 -4
- package/dist/controllers/otp_controller.js +198 -193
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
- package/dist/controllers/overflow_indicator_controller.js +116 -113
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +48 -11
- package/dist/controllers/overflow_menu_controller.js +343 -283
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +35 -6
- package/dist/controllers/pagination_controller.js +295 -119
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +45 -8
- package/dist/controllers/password_reveal_controller.js +389 -121
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +14 -5
- package/dist/controllers/password_strength_controller.js +99 -153
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +11 -4
- package/dist/controllers/persist_controller.js +237 -103
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +19 -7
- package/dist/controllers/pointer_drag_controller.js +185 -165
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +38 -2
- package/dist/controllers/popover_controller.js +236 -97
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +209 -76
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +10 -4
- package/dist/controllers/preview_guard_controller.js +232 -116
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +11 -0
- package/dist/controllers/progress_controller.js +125 -56
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +14 -5
- package/dist/controllers/radio_group_controller.js +117 -137
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +43 -3
- package/dist/controllers/range_slider_controller.js +192 -105
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +20 -4
- package/dist/controllers/rating_controller.js +418 -207
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +2 -0
- package/dist/controllers/read_more_controller.js +134 -63
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +1 -1
- package/dist/controllers/reading_progress_controller.js +147 -135
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +18 -4
- package/dist/controllers/relative_time_controller.js +145 -110
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
- package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +28 -2
- package/dist/controllers/resizable_controller.js +255 -139
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.js +39 -84
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -3
- package/dist/controllers/scroll_area_controller.js +428 -225
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +0 -53
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
- package/dist/controllers/scroll_visibility_controller.js +395 -145
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +6 -1
- package/dist/controllers/scrollspy_controller.js +181 -242
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +19 -4
- package/dist/controllers/separator_controller.js +267 -140
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +64 -7
- package/dist/controllers/sidebar_controller.js +1023 -386
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +248 -97
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +27 -1
- package/dist/controllers/slider_controller.js +159 -79
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
- package/dist/controllers/smart_sticky_header_controller.js +162 -63
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +0 -112
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +277 -169
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +5 -0
- package/dist/controllers/step_indicator_controller.js +99 -46
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +12 -3
- package/dist/controllers/stepper_controller.js +114 -73
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +6 -0
- package/dist/controllers/stick_to_bottom_controller.js +262 -138
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +11 -0
- package/dist/controllers/sticky_observer_controller.js +214 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +23 -8
- package/dist/controllers/submit_once_controller.js +294 -205
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +12 -1
- package/dist/controllers/switch_controller.js +33 -60
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +2 -2
- package/dist/controllers/tabs_controller.js +35 -29
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +12 -0
- package/dist/controllers/tags_input_controller.js +136 -145
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
- package/dist/controllers/textarea_autosize_controller.js +71 -58
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +21 -9
- package/dist/controllers/theme_controller.js +130 -112
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +18 -1
- package/dist/controllers/time_picker_controller.js +146 -80
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +36 -11
- package/dist/controllers/toast_controller.js +186 -297
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +12 -3
- package/dist/controllers/toggle_group_controller.js +95 -136
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +68 -98
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +45 -3
- package/dist/controllers/tooltip_controller.js +158 -124
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +22 -4
- package/dist/controllers/transition_controller.js +123 -119
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +21 -11
- package/dist/controllers/tree_view_controller.js +150 -345
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6370 -9346
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +23 -3
- package/dist/inspector/cli.js +92 -37
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +92 -56
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +8 -8
- package/dist/inspector/manifest.json +787 -100
- package/dist/positioning/index.d.ts +8 -0
- package/dist/positioning/index.js +148 -47
- package/dist/positioning/index.js.map +1 -1
- package/package.json +6 -6
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
// src/controllers/frame_loading_controller.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/announce.ts
|
|
6
3
|
function announce(message, options = {}) {
|
|
7
4
|
const text = message.trim();
|
|
8
5
|
if (text.length === 0) return;
|
|
@@ -19,141 +16,214 @@ function fillTemplate(template, values) {
|
|
|
19
16
|
});
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
19
|
+
function sharedRegistry(key, create) {
|
|
20
|
+
const symbol = Symbol.for(key);
|
|
21
|
+
const scope = globalThis;
|
|
22
|
+
const existing = scope[symbol];
|
|
23
|
+
if (existing !== void 0) return existing;
|
|
24
|
+
const registry = create();
|
|
25
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
26
|
+
return registry;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isAttributeValue(value) {
|
|
30
|
+
return value === null || typeof value === "string";
|
|
31
|
+
}
|
|
32
|
+
function readLeaseRecord(element, record) {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
35
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
36
|
+
const [original, written, ...rest] = parsed;
|
|
37
|
+
const linked = rest[0] !== false;
|
|
38
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
39
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
40
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
41
|
+
} catch {
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
var AttributeLease = class _AttributeLease {
|
|
46
|
+
static #registry = sharedRegistry(
|
|
47
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
48
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
49
|
+
);
|
|
50
|
+
#attribute;
|
|
51
|
+
#record;
|
|
52
|
+
#base;
|
|
53
|
+
#held = /* @__PURE__ */ new Set();
|
|
54
|
+
constructor(attribute, owner, options = {}) {
|
|
55
|
+
this.#attribute = attribute;
|
|
56
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
57
|
+
this.#base = options.base === true;
|
|
58
|
+
const names = _AttributeLease.#registry.names;
|
|
59
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
60
|
+
}
|
|
61
|
+
write(element, value) {
|
|
62
|
+
const stack = this.#stack(element);
|
|
63
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
64
|
+
this.#held.add(element);
|
|
65
|
+
const shows = !this.#covered(element, stack, hold);
|
|
66
|
+
hold.written = value;
|
|
67
|
+
this.#mark(element, stack);
|
|
68
|
+
if (shows) this.#reflect(element, value);
|
|
69
|
+
}
|
|
70
|
+
return(element) {
|
|
71
|
+
const held = this.#held.delete(element);
|
|
72
|
+
const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
|
|
73
|
+
const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
|
|
74
|
+
const hold = this.#take(stack);
|
|
75
|
+
if (!hold) return false;
|
|
76
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
77
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
78
|
+
const index = stack.indexOf(hold);
|
|
79
|
+
stack.splice(index, 1);
|
|
80
|
+
const above = stack[index];
|
|
81
|
+
if (above) above.linked &&= hold.linked;
|
|
82
|
+
this.#mark(element, stack);
|
|
83
|
+
if (!owns) return true;
|
|
84
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
85
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
returnAll() {
|
|
89
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
90
|
+
}
|
|
91
|
+
#stack(element) {
|
|
92
|
+
const stacks = _AttributeLease.#registry.stacks;
|
|
93
|
+
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
94
|
+
stacks.set(element, byAttribute);
|
|
95
|
+
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
96
|
+
byAttribute.set(this.#attribute, stack);
|
|
97
|
+
return stack;
|
|
98
|
+
}
|
|
99
|
+
#seed(element) {
|
|
100
|
+
const found = [];
|
|
101
|
+
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
102
|
+
const read = readLeaseRecord(element, record);
|
|
103
|
+
if (read) found.push({ ...read, record });
|
|
104
|
+
}
|
|
105
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
106
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
107
|
+
lease: null,
|
|
108
|
+
record,
|
|
109
|
+
written,
|
|
110
|
+
original,
|
|
111
|
+
linked: linked && index > 0
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
#take(stack) {
|
|
115
|
+
const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
|
|
116
|
+
if (hold) hold.lease = this;
|
|
117
|
+
return hold;
|
|
118
|
+
}
|
|
119
|
+
#acquire(element, stack) {
|
|
120
|
+
const current = element.getAttribute(this.#attribute);
|
|
121
|
+
const top = stack.at(-1);
|
|
122
|
+
const bottom = stack[0];
|
|
123
|
+
const over = top?.written === current ? top : void 0;
|
|
124
|
+
const from = this.#base ? bottom : over;
|
|
125
|
+
const hold = {
|
|
126
|
+
lease: this,
|
|
127
|
+
record: this.#record,
|
|
128
|
+
written: current,
|
|
129
|
+
original: from ? from.original : current,
|
|
130
|
+
linked: !this.#base && over !== void 0
|
|
131
|
+
};
|
|
132
|
+
if (this.#base && bottom) {
|
|
133
|
+
bottom.linked = true;
|
|
134
|
+
stack.unshift(hold);
|
|
135
|
+
} else stack.push(hold);
|
|
136
|
+
return hold;
|
|
137
|
+
}
|
|
138
|
+
#covered(element, stack, hold) {
|
|
139
|
+
const current = element.getAttribute(this.#attribute);
|
|
140
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
|
|
141
|
+
}
|
|
142
|
+
#mark(element, stack) {
|
|
143
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
144
|
+
stack.forEach((hold, index) => {
|
|
145
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
146
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
147
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
148
|
+
records.set(
|
|
149
|
+
hold.record,
|
|
150
|
+
hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
for (const [name, recorded] of records) {
|
|
154
|
+
if (element.getAttribute(name) === recorded) continue;
|
|
155
|
+
if (recorded === null) element.removeAttribute(name);
|
|
156
|
+
else element.setAttribute(name, recorded);
|
|
41
157
|
}
|
|
42
158
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
159
|
+
#reflect(element, value) {
|
|
160
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
161
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
162
|
+
else element.setAttribute(this.#attribute, value);
|
|
48
163
|
}
|
|
49
164
|
};
|
|
50
165
|
|
|
51
|
-
// src/utils/detach_gate.ts
|
|
52
166
|
var DetachGate = class _DetachGate {
|
|
53
|
-
/** Set while a probe is queued, waiting for a reconnect to cancel it. */
|
|
54
167
|
#pending = false;
|
|
55
|
-
/**
|
|
56
|
-
* True while a probe is queued — the last disconnect was ambiguous and no
|
|
57
|
-
* reconnect has cancelled it yet. Read it from `connect()` to tell the
|
|
58
|
-
* reconnect half of an in-page move from a first connect: a controller whose
|
|
59
|
-
* initialisation restarts a measurement (a min-duration floor, an elapsed
|
|
60
|
-
* counter) must skip it for the move, where nothing actually restarted.
|
|
61
|
-
*/
|
|
62
168
|
get pending() {
|
|
63
169
|
return this.#pending;
|
|
64
170
|
}
|
|
65
|
-
/**
|
|
66
|
-
* True when the disconnect is definitely a real detach — the element left
|
|
67
|
-
* the document, or `data-controller` no longer lists the identifier. False
|
|
68
|
-
* means ambiguous (in-page move or observed-root exit), NOT "alive".
|
|
69
|
-
*/
|
|
70
171
|
static isDetached(host) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return !tokens.includes(host.identifier);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Call from `disconnect()`: runs `teardown` synchronously on a definite
|
|
77
|
-
* detach (fast path), otherwise defers it one microtask — a reconnect
|
|
78
|
-
* ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.
|
|
79
|
-
* One microtask is the whole probe window: Stimulus reconnects a moved
|
|
80
|
-
* element within the same mutation batch, before the checkpoint drains.
|
|
81
|
-
*/
|
|
172
|
+
return !host.element.isConnected || !_DetachGate.#listed(host);
|
|
173
|
+
}
|
|
82
174
|
disconnected(host, teardown) {
|
|
83
|
-
|
|
175
|
+
const carried = _DetachGate.#carried(host);
|
|
176
|
+
if (!carried && _DetachGate.isDetached(host)) {
|
|
84
177
|
this.#pending = false;
|
|
85
178
|
teardown();
|
|
86
179
|
return;
|
|
87
180
|
}
|
|
88
181
|
this.#pending = true;
|
|
89
|
-
|
|
182
|
+
const probe = () => {
|
|
90
183
|
if (!this.#pending) return;
|
|
91
184
|
this.#pending = false;
|
|
92
185
|
teardown();
|
|
93
|
-
}
|
|
186
|
+
};
|
|
187
|
+
if (carried) setTimeout(probe, 0);
|
|
188
|
+
else queueMicrotask(probe);
|
|
189
|
+
}
|
|
190
|
+
static #carried(host) {
|
|
191
|
+
const element = host.element;
|
|
192
|
+
return !element.isConnected && element.closest("[data-turbo-permanent]") !== null && _DetachGate.#listed(host);
|
|
193
|
+
}
|
|
194
|
+
static #listed(host) {
|
|
195
|
+
const tokens = (host.element.getAttribute("data-controller") ?? "").split(/\s+/);
|
|
196
|
+
return tokens.includes(host.identifier);
|
|
94
197
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Disarms a pending probe. Call from `connect()` (the reconnect that proves
|
|
97
|
-
* an in-page move) and from the head of any teardown path not routed through
|
|
98
|
-
* {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can
|
|
99
|
-
* never run the teardown a second time.
|
|
100
|
-
*/
|
|
101
198
|
cancel() {
|
|
102
199
|
this.#pending = false;
|
|
103
200
|
}
|
|
104
201
|
};
|
|
105
202
|
|
|
106
|
-
// src/utils/listener_set.ts
|
|
107
203
|
var ListenerSet = class {
|
|
108
|
-
/** The generation every `add` joins until the next `dispose()`. */
|
|
109
204
|
#abort = new AbortController();
|
|
110
|
-
/**
|
|
111
|
-
* Attaches `handler` to the open generation, exactly as the caller spelled it.
|
|
112
|
-
*
|
|
113
|
-
* The set supplies the signal, so `options` carries everything else the DOM
|
|
114
|
-
* accepts — `capture` included, which has to match at release time and no
|
|
115
|
-
* longer has a second place to drift from.
|
|
116
|
-
*/
|
|
117
205
|
add(target, type, handler, options) {
|
|
118
206
|
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
119
207
|
}
|
|
120
|
-
/**
|
|
121
|
-
* Releases every listener of the open generation, synchronously, and opens the
|
|
122
|
-
* next one. Idempotent, and safe before anything has been added.
|
|
123
|
-
*/
|
|
124
208
|
dispose() {
|
|
125
209
|
this.#abort.abort();
|
|
126
210
|
this.#abort = new AbortController();
|
|
127
211
|
}
|
|
128
212
|
};
|
|
129
213
|
|
|
130
|
-
// src/utils/min_duration_floor.ts
|
|
131
214
|
var MinDurationFloor = class {
|
|
132
215
|
#timers;
|
|
133
|
-
/** Pending finish timer id, or `null` when nothing is held back. */
|
|
134
216
|
#timerId = null;
|
|
135
|
-
/** Epoch ms the floor is measured from. */
|
|
136
217
|
#since = 0;
|
|
137
|
-
/** @param timers - the controller's registry; the floor schedules into it. */
|
|
138
218
|
constructor(timers) {
|
|
139
219
|
this.#timers = timers;
|
|
140
220
|
}
|
|
141
|
-
/** Starts the floor: call when the state being held becomes visible. */
|
|
142
221
|
begin() {
|
|
143
222
|
this.#since = Date.now();
|
|
144
223
|
}
|
|
145
|
-
/** True while a finish is held back waiting for the floor to elapse. */
|
|
146
224
|
get pending() {
|
|
147
225
|
return this.#timerId !== null;
|
|
148
226
|
}
|
|
149
|
-
/**
|
|
150
|
-
* Runs `finish` once the floor has elapsed, immediately when it already has.
|
|
151
|
-
*
|
|
152
|
-
* A held-back finish is **replaced**, never stacked: only the most recently
|
|
153
|
-
* queued id is cancellable, so a second timer would outlive every cancel and
|
|
154
|
-
* end a state that has since restarted. Controllers that want the first signal
|
|
155
|
-
* to win guard on {@link pending} before calling.
|
|
156
|
-
*/
|
|
157
227
|
schedule(minDuration, finish) {
|
|
158
228
|
this.cancel();
|
|
159
229
|
const remaining = minDuration - (Date.now() - this.#since);
|
|
@@ -166,7 +236,6 @@ var MinDurationFloor = class {
|
|
|
166
236
|
finish();
|
|
167
237
|
}
|
|
168
238
|
}
|
|
169
|
-
/** Drops a held-back finish. Safe when none is queued, or after a bulk clear. */
|
|
170
239
|
cancel() {
|
|
171
240
|
if (this.#timerId !== null) {
|
|
172
241
|
this.#timers.clear(this.#timerId);
|
|
@@ -175,43 +244,70 @@ var MinDurationFloor = class {
|
|
|
175
244
|
}
|
|
176
245
|
};
|
|
177
246
|
|
|
178
|
-
|
|
247
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
248
|
+
|
|
249
|
+
var NUMBER_BOUNDS = {
|
|
250
|
+
timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
|
|
251
|
+
function matchesNumberBounds(value, bounds) {
|
|
252
|
+
if (!Number.isFinite(value)) {
|
|
253
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
254
|
+
if (direction === null) return false;
|
|
255
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
256
|
+
}
|
|
257
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
258
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
259
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
260
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
261
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function readNumber(raw, fallback, bounds) {
|
|
266
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
var NumberValueReader = class {
|
|
270
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
271
|
+
read(owner, name, raw, fallback, bounds) {
|
|
272
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
273
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
274
|
+
this.#lastRejected.delete(name);
|
|
275
|
+
return resolved;
|
|
276
|
+
}
|
|
277
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
278
|
+
const literal = owner.element.getAttribute(attribute);
|
|
279
|
+
if (literal === null) {
|
|
280
|
+
this.#lastRejected.delete(name);
|
|
281
|
+
return resolved;
|
|
282
|
+
}
|
|
283
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
284
|
+
this.#lastRejected.set(name, literal);
|
|
285
|
+
console.warn(
|
|
286
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
return resolved;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
179
293
|
var TimerRegistry = class {
|
|
180
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
181
294
|
ids = /* @__PURE__ */ new Set();
|
|
182
|
-
/**
|
|
183
|
-
* Cancels a single tracked timer.
|
|
184
|
-
*
|
|
185
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
186
|
-
* registry), so callers can clear defensively without guarding.
|
|
187
|
-
*/
|
|
188
295
|
clear(id) {
|
|
189
296
|
if (this.ids.delete(id)) {
|
|
190
297
|
this.cancel(id);
|
|
191
298
|
}
|
|
192
299
|
}
|
|
193
|
-
/**
|
|
194
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
195
|
-
* to guarantee no timer outlives the element.
|
|
196
|
-
*/
|
|
197
300
|
clearAll() {
|
|
198
301
|
for (const id of this.ids) {
|
|
199
302
|
this.cancel(id);
|
|
200
303
|
}
|
|
201
304
|
this.ids.clear();
|
|
202
305
|
}
|
|
203
|
-
/** Number of timers currently tracked (pending). */
|
|
204
306
|
get size() {
|
|
205
307
|
return this.ids.size;
|
|
206
308
|
}
|
|
207
309
|
};
|
|
208
310
|
var SafeTimeout = class extends TimerRegistry {
|
|
209
|
-
/**
|
|
210
|
-
* Schedules `callback` after `delay` ms and returns the timer id.
|
|
211
|
-
*
|
|
212
|
-
* The id is removed from the registry automatically when the timeout fires,
|
|
213
|
-
* so {@link TimerRegistry.size | size} reflects only still-pending timers.
|
|
214
|
-
*/
|
|
215
311
|
set(callback, delay) {
|
|
216
312
|
const id = this.schedule(() => {
|
|
217
313
|
this.ids.delete(id);
|
|
@@ -228,8 +324,8 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
228
324
|
}
|
|
229
325
|
};
|
|
230
326
|
|
|
231
|
-
|
|
232
|
-
|
|
327
|
+
var FrameLoadingController = class _FrameLoadingController extends Controller {
|
|
328
|
+
#numbers = new NumberValueReader();
|
|
233
329
|
static targets = ["content", "skeleton", "overlay"];
|
|
234
330
|
static values = {
|
|
235
331
|
announceText: { type: String, default: "" },
|
|
@@ -237,147 +333,109 @@ var FrameLoadingController = class extends Controller {
|
|
|
237
333
|
minDuration: { type: Number, default: 0 },
|
|
238
334
|
restoreFocus: { type: Boolean, default: true }
|
|
239
335
|
};
|
|
336
|
+
static valueConstraints = {
|
|
337
|
+
minDuration: NUMBER_BOUNDS.timer
|
|
338
|
+
};
|
|
240
339
|
static events = ["start", "end", "reconcile"];
|
|
241
340
|
#timeouts = new SafeTimeout();
|
|
242
341
|
#floor = new MinDurationFloor(this.#timeouts);
|
|
243
342
|
#gate = new DetachGate();
|
|
244
|
-
#beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
|
|
245
343
|
#listeners = new ListenerSet();
|
|
344
|
+
#busy = new AttributeLease("aria-busy", this.identifier);
|
|
345
|
+
#loadingHook = new AttributeLease("data-frame-loading", this.identifier);
|
|
346
|
+
#hidden = new AttributeLease("hidden", this.identifier);
|
|
347
|
+
#inert = new AttributeLease("inert", this.identifier);
|
|
246
348
|
#loading = false;
|
|
247
|
-
/**
|
|
248
|
-
* The optional targets this controller revealed, and the content it marked inert.
|
|
249
|
-
* Held as references rather than re-resolved on the way out: a detach that keeps
|
|
250
|
-
* the element takes the identifier off `data-controller` first, and a scope
|
|
251
|
-
* without its identifier stops resolving targets — the elements to tidy would be
|
|
252
|
-
* unreachable exactly when the tidying matters. They double as the ownership
|
|
253
|
-
* marker, so a `hidden` or an `inert` the consumer wrote is never taken over.
|
|
254
|
-
*/
|
|
255
349
|
#revealedSkeleton = null;
|
|
256
350
|
#revealedOverlay = null;
|
|
257
351
|
#inertTarget = null;
|
|
258
352
|
#previousFocus = null;
|
|
259
|
-
/** The id of the retreated element, used to re-find it if the load replaced it. */
|
|
260
353
|
#previousFocusId = "";
|
|
261
354
|
#onStart = () => {
|
|
262
355
|
this.#floor.cancel();
|
|
263
356
|
if (!this.#loading) this.#begin();
|
|
264
357
|
};
|
|
265
|
-
/**
|
|
266
|
-
* @stimeoRuntimeOnly `minDuration` holds the loading state at least this long for the one load
|
|
267
|
-
* that ended.
|
|
268
|
-
*/
|
|
269
358
|
#onEnd = () => {
|
|
270
359
|
if (!this.#loading) return;
|
|
271
|
-
this.#floor.schedule(this
|
|
360
|
+
this.#floor.schedule(this.#safeMinDuration, () => this.#finish());
|
|
272
361
|
};
|
|
273
362
|
connect() {
|
|
274
363
|
this.#gate.cancel();
|
|
275
|
-
this.#beforeCache.activate();
|
|
276
364
|
this.#listeners.add(this.element, "turbo:before-fetch-request", this.#onStart);
|
|
277
365
|
this.#listeners.add(this.element, "turbo:frame-load", this.#onEnd);
|
|
278
366
|
this.#listeners.add(this.element, "turbo:fetch-request-error", this.#onEnd);
|
|
367
|
+
if (this.#loading) return;
|
|
368
|
+
const stale = this.element.hasAttribute("data-frame-loading");
|
|
369
|
+
this.#returnCopied();
|
|
370
|
+
if (stale) this.dispatch("reconcile", { detail: {} });
|
|
371
|
+
}
|
|
372
|
+
#returnCopied() {
|
|
373
|
+
this.#busy.return(this.element);
|
|
374
|
+
this.#loadingHook.return(this.element);
|
|
375
|
+
for (const region of [...this.skeletonTargets, ...this.overlayTargets]) {
|
|
376
|
+
this.#hidden.return(region);
|
|
377
|
+
}
|
|
378
|
+
for (const content of this.contentTargets) this.#inert.return(content);
|
|
279
379
|
}
|
|
280
380
|
disconnect() {
|
|
281
381
|
this.#listeners.dispose();
|
|
282
|
-
this.#beforeCache.deactivate();
|
|
283
382
|
this.#gate.disconnected(this, () => this.#teardown());
|
|
284
383
|
}
|
|
285
|
-
/**
|
|
286
|
-
* Drops the held finish and the loading bookkeeping on a real detach, returning
|
|
287
|
-
* the frame to its idle form. No reconnect is coming, so nothing is left that
|
|
288
|
-
* could finish the load and clear the hooks — a detach that keeps the element
|
|
289
|
-
* (a morph dropping the identifier, an exit from a scoped observed root) would
|
|
290
|
-
* otherwise strand it busy and inert. Focus is left where it is: the element is
|
|
291
|
-
* leaving this controller's care, and moving it now would be an unexplained jump.
|
|
292
|
-
*/
|
|
293
384
|
#teardown() {
|
|
294
|
-
this.#gate.cancel();
|
|
295
385
|
this.#timeouts.clearAll();
|
|
296
|
-
this.#floor.cancel();
|
|
297
386
|
if (this.#loading) this.#rewindHooks();
|
|
298
387
|
this.#loading = false;
|
|
299
388
|
this.#previousFocus = null;
|
|
300
389
|
}
|
|
301
|
-
/**
|
|
302
|
-
* Returns the frame to its idle form for the snapshot Turbo is about to take, so
|
|
303
|
-
* a page reached with the Back button does not restore a frame that is busy and
|
|
304
|
-
* inert with nothing left to finish it. State only — no `end` event and no focus
|
|
305
|
-
* move, because the load did not actually complete.
|
|
306
|
-
*
|
|
307
|
-
* The load is abandoned rather than paused, so the flag and any finish the floor
|
|
308
|
-
* still holds drop along with the hooks. A kept finish would surface after the
|
|
309
|
-
* rewind as exactly the three things this pass exists to avoid — an `end`, a
|
|
310
|
-
* completion announcement, and a focus move — and a kept flag would leave the
|
|
311
|
-
* next fetch on a page that survives a cancelled visit skipping the loading
|
|
312
|
-
* state, its idempotence guard already satisfied.
|
|
313
|
-
*/
|
|
314
|
-
#rewindForCache() {
|
|
315
|
-
if (!this.#loading) return;
|
|
316
|
-
this.#loading = false;
|
|
317
|
-
this.#floor.cancel();
|
|
318
|
-
this.#rewindHooks();
|
|
319
|
-
this.dispatch("reconcile", { detail: {} });
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Clears every hook the loading state writes. Shared by the three ways a load can
|
|
323
|
-
* stop — completion, detach, snapshot — so none of them can drift into tidying
|
|
324
|
-
* only part of it.
|
|
325
|
-
*/
|
|
326
390
|
#rewindHooks() {
|
|
327
|
-
this.element
|
|
328
|
-
this.element
|
|
329
|
-
if (this.#revealedSkeleton) this.#revealedSkeleton
|
|
330
|
-
if (this.#revealedOverlay) this.#revealedOverlay
|
|
391
|
+
this.#busy.return(this.element);
|
|
392
|
+
this.#loadingHook.return(this.element);
|
|
393
|
+
if (this.#revealedSkeleton) this.#hidden.return(this.#revealedSkeleton);
|
|
394
|
+
if (this.#revealedOverlay) this.#hidden.return(this.#revealedOverlay);
|
|
331
395
|
this.#revealedSkeleton = null;
|
|
332
396
|
this.#revealedOverlay = null;
|
|
333
397
|
this.#clearInert();
|
|
334
398
|
}
|
|
335
|
-
/**
|
|
336
|
-
* Re-shows a `skeleton` that arrived mid-load. Turbo's frame renderer empties the
|
|
337
|
-
* frame and re-inserts the response's children, so a response's authored (hidden)
|
|
338
|
-
* skeleton can land while a later fetch is still running, and only the controller
|
|
339
|
-
* knows the frame is still busy.
|
|
340
|
-
*/
|
|
341
399
|
skeletonTargetConnected() {
|
|
342
400
|
if (this.#loading) this.#revealSkeleton();
|
|
343
401
|
}
|
|
344
|
-
|
|
402
|
+
skeletonTargetDisconnected(skeleton) {
|
|
403
|
+
if (this.#loading && !this.skeletonTargets.includes(skeleton)) this.#revealSkeleton();
|
|
404
|
+
}
|
|
345
405
|
overlayTargetConnected() {
|
|
346
406
|
if (this.#loading) this.#revealOverlay();
|
|
347
407
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
* an `inert` the replacement authored stays the consumer's.
|
|
352
|
-
*/
|
|
408
|
+
overlayTargetDisconnected(overlay) {
|
|
409
|
+
if (this.#loading && !this.overlayTargets.includes(overlay)) this.#revealOverlay();
|
|
410
|
+
}
|
|
353
411
|
contentTargetConnected() {
|
|
354
412
|
if (!this.#loading) return;
|
|
355
413
|
this.#clearInert();
|
|
356
414
|
this.#applyInert();
|
|
357
415
|
}
|
|
358
|
-
|
|
416
|
+
contentTargetDisconnected(content) {
|
|
417
|
+
if (!this.#loading || this.contentTargets.includes(content)) return;
|
|
418
|
+
this.#clearInert();
|
|
419
|
+
this.#applyInert();
|
|
420
|
+
}
|
|
359
421
|
#revealSkeleton() {
|
|
360
|
-
|
|
361
|
-
this.#revealedSkeleton = this
|
|
362
|
-
this.skeletonTarget.hidden = false;
|
|
422
|
+
const next = this.hasSkeletonTarget ? this.skeletonTarget : null;
|
|
423
|
+
this.#revealedSkeleton = this.#reveal(this.#revealedSkeleton, next);
|
|
363
424
|
}
|
|
364
|
-
/** Reveals the optional `overlay`, noting it as this controller's to hide again. */
|
|
365
425
|
#revealOverlay() {
|
|
366
|
-
|
|
367
|
-
this.#revealedOverlay = this
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
* decides whether focus leaves the frame it covers.
|
|
375
|
-
*/
|
|
426
|
+
const next = this.hasOverlayTarget ? this.overlayTarget : null;
|
|
427
|
+
this.#revealedOverlay = this.#reveal(this.#revealedOverlay, next);
|
|
428
|
+
}
|
|
429
|
+
#reveal(revealed, next) {
|
|
430
|
+
if (revealed && revealed !== next) this.#hidden.return(revealed);
|
|
431
|
+
if (next) this.#hidden.write(next, null);
|
|
432
|
+
return next;
|
|
433
|
+
}
|
|
376
434
|
#begin() {
|
|
377
435
|
this.#loading = true;
|
|
378
436
|
this.#floor.begin();
|
|
379
|
-
this.element
|
|
380
|
-
this.element
|
|
437
|
+
this.#busy.write(this.element, "true");
|
|
438
|
+
this.#loadingHook.write(this.element, "true");
|
|
381
439
|
this.#revealSkeleton();
|
|
382
440
|
this.#revealOverlay();
|
|
383
441
|
this.#applyInert();
|
|
@@ -385,12 +443,6 @@ var FrameLoadingController = class extends Controller {
|
|
|
385
443
|
this.dispatch("start", { detail: {} });
|
|
386
444
|
announce(fillTemplate(this.announceTextValue, {}));
|
|
387
445
|
}
|
|
388
|
-
/**
|
|
389
|
-
* Leaves the loading state: restore hooks, hide skeleton/overlay, restore focus.
|
|
390
|
-
*
|
|
391
|
-
* @stimeoRuntimeOnly `announceReadyText` words the one announcement of this finished load and
|
|
392
|
-
* `restoreFocus` decides whether focus goes back.
|
|
393
|
-
*/
|
|
394
446
|
#finish() {
|
|
395
447
|
this.#loading = false;
|
|
396
448
|
this.#rewindHooks();
|
|
@@ -398,17 +450,15 @@ var FrameLoadingController = class extends Controller {
|
|
|
398
450
|
this.dispatch("end", { detail: {} });
|
|
399
451
|
announce(fillTemplate(this.announceReadyTextValue, {}));
|
|
400
452
|
}
|
|
401
|
-
/** Marks the content inert to block double-submits while stale (if we own it). */
|
|
402
453
|
#applyInert() {
|
|
403
454
|
if (!this.hasContentTarget || this.contentTarget.hasAttribute("inert")) return;
|
|
404
|
-
this.contentTarget
|
|
455
|
+
this.#inert.write(this.contentTarget, "");
|
|
405
456
|
this.#inertTarget = this.contentTarget;
|
|
406
457
|
}
|
|
407
458
|
#clearInert() {
|
|
408
|
-
this.#inertTarget
|
|
459
|
+
if (this.#inertTarget) this.#inert.return(this.#inertTarget);
|
|
409
460
|
this.#inertTarget = null;
|
|
410
461
|
}
|
|
411
|
-
/** Saves and blurs focus if it sits inside the frame about to go stale. */
|
|
412
462
|
#retreatFocus() {
|
|
413
463
|
this.#previousFocus = null;
|
|
414
464
|
this.#previousFocusId = "";
|
|
@@ -420,13 +470,6 @@ var FrameLoadingController = class extends Controller {
|
|
|
420
470
|
active.blur();
|
|
421
471
|
}
|
|
422
472
|
}
|
|
423
|
-
/**
|
|
424
|
-
* Restores focus after the load. The same node when it survived (e.g. a
|
|
425
|
-
* non-replacing update), else the element re-rendered with the same id inside the
|
|
426
|
-
* frame — Turbo frames typically re-emit the same controls. When neither is present
|
|
427
|
-
* (an anonymous control was replaced) focus is left where the browser put it, to
|
|
428
|
-
* avoid an unexpected jump (WCAG 3.2.x).
|
|
429
|
-
*/
|
|
430
473
|
#restoreFocus() {
|
|
431
474
|
const target = this.#previousFocus;
|
|
432
475
|
const id = this.#previousFocusId;
|
|
@@ -442,8 +485,16 @@ var FrameLoadingController = class extends Controller {
|
|
|
442
485
|
if (replacement && this.element.contains(replacement)) replacement.focus();
|
|
443
486
|
}
|
|
444
487
|
}
|
|
488
|
+
get #safeMinDuration() {
|
|
489
|
+
return this.#numbers.read(
|
|
490
|
+
this,
|
|
491
|
+
"minDuration",
|
|
492
|
+
this.minDurationValue,
|
|
493
|
+
_FrameLoadingController.values.minDuration.default,
|
|
494
|
+
_FrameLoadingController.valueConstraints.minDuration
|
|
495
|
+
);
|
|
496
|
+
}
|
|
445
497
|
};
|
|
446
498
|
|
|
447
499
|
export { FrameLoadingController };
|
|
448
|
-
//# sourceMappingURL=frame_loading_controller.js.map
|
|
449
500
|
//# sourceMappingURL=frame_loading_controller.js.map
|