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,27 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
|
|
4
|
+
var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
|
|
5
|
+
function stateReasonFor(event) {
|
|
6
|
+
if (!event) return "api";
|
|
7
|
+
if (FOCUS_EVENTS.has(event.type)) return "focus";
|
|
8
|
+
if (POINTER_EVENTS.has(event.type)) return "pointer";
|
|
9
|
+
return "user";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function actionSource(source) {
|
|
13
|
+
if (source instanceof HTMLElement) {
|
|
14
|
+
return { event: null, host: source, origin: source, reason: "api" };
|
|
15
|
+
}
|
|
16
|
+
const event = source ?? null;
|
|
17
|
+
return {
|
|
18
|
+
event,
|
|
19
|
+
host: event?.currentTarget instanceof Element ? event.currentTarget : null,
|
|
20
|
+
origin: event?.target instanceof Element ? event.target : null,
|
|
21
|
+
reason: stateReasonFor(event)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
4
24
|
|
|
5
|
-
// src/utils/announce.ts
|
|
6
25
|
function announce(message, options = {}) {
|
|
7
26
|
const text = message.trim();
|
|
8
27
|
if (text.length === 0) return;
|
|
@@ -13,36 +32,6 @@ function announce(message, options = {}) {
|
|
|
13
32
|
);
|
|
14
33
|
}
|
|
15
34
|
|
|
16
|
-
// src/utils/before_cache_reset.ts
|
|
17
|
-
var BeforeCacheReset = class _BeforeCacheReset {
|
|
18
|
-
/** Every subscribed instance, iterated by the one shared document listener. */
|
|
19
|
-
static #subscribers = /* @__PURE__ */ new Set();
|
|
20
|
-
/** The shared listener; installed while at least one instance is subscribed. */
|
|
21
|
-
static #onBeforeCache = () => {
|
|
22
|
-
for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
|
|
23
|
-
};
|
|
24
|
-
#rewind;
|
|
25
|
-
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
26
|
-
constructor(rewind) {
|
|
27
|
-
this.#rewind = rewind;
|
|
28
|
-
}
|
|
29
|
-
/** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
|
|
30
|
-
activate() {
|
|
31
|
-
const first = _BeforeCacheReset.#subscribers.size === 0;
|
|
32
|
-
_BeforeCacheReset.#subscribers.add(this);
|
|
33
|
-
if (first) {
|
|
34
|
-
document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
|
|
38
|
-
deactivate() {
|
|
39
|
-
_BeforeCacheReset.#subscribers.delete(this);
|
|
40
|
-
if (_BeforeCacheReset.#subscribers.size > 0) return;
|
|
41
|
-
document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// src/utils/event_owner.ts
|
|
46
35
|
function ownerIndex(candidates, node) {
|
|
47
36
|
if (!(node instanceof Node)) return -1;
|
|
48
37
|
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
@@ -51,43 +40,79 @@ function ownerOf(candidates, node) {
|
|
|
51
40
|
return candidates[ownerIndex(candidates, node)] ?? null;
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
|
|
43
|
+
var ListenerSet = class {
|
|
44
|
+
#abort = new AbortController();
|
|
45
|
+
add(target, type, handler, options) {
|
|
46
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
47
|
+
}
|
|
48
|
+
dispose() {
|
|
49
|
+
this.#abort.abort();
|
|
50
|
+
this.#abort = new AbortController();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
var HoverRecheck = class {
|
|
55
|
+
#document;
|
|
56
|
+
#release;
|
|
57
|
+
#listeners = new ListenerSet();
|
|
58
|
+
#pending = /* @__PURE__ */ new Set();
|
|
59
|
+
#tickets = /* @__PURE__ */ new WeakMap();
|
|
60
|
+
constructor(document2, release) {
|
|
61
|
+
this.#document = document2;
|
|
62
|
+
this.#release = release;
|
|
63
|
+
}
|
|
64
|
+
wait(element) {
|
|
65
|
+
this.#pending.add(element);
|
|
66
|
+
this.#tickets.set(element, /* @__PURE__ */ Symbol());
|
|
67
|
+
this.#listeners.add(this.#document, "pointermove", this.#onPointerMove, {
|
|
68
|
+
capture: true,
|
|
69
|
+
passive: true
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
cancel(element) {
|
|
73
|
+
this.#tickets.delete(element);
|
|
74
|
+
this.#pending.delete(element);
|
|
75
|
+
if (this.#pending.size === 0) this.#listeners.dispose();
|
|
76
|
+
}
|
|
77
|
+
dispose() {
|
|
78
|
+
this.#listeners.dispose();
|
|
79
|
+
this.#pending.clear();
|
|
80
|
+
this.#tickets = /* @__PURE__ */ new WeakMap();
|
|
81
|
+
}
|
|
82
|
+
#onPointerMove = () => {
|
|
83
|
+
this.#listeners.dispose();
|
|
84
|
+
const waiting = [...this.#pending].map(
|
|
85
|
+
(element) => [element, this.#tickets.get(element)]
|
|
86
|
+
);
|
|
87
|
+
this.#pending.clear();
|
|
88
|
+
for (const [element, ticket] of waiting) {
|
|
89
|
+
if (this.#tickets.get(element) !== ticket) continue;
|
|
90
|
+
if (element.matches(":hover")) continue;
|
|
91
|
+
this.#release(element);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
97
|
+
|
|
55
98
|
var TimerRegistry = class {
|
|
56
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
57
99
|
ids = /* @__PURE__ */ new Set();
|
|
58
|
-
/**
|
|
59
|
-
* Cancels a single tracked timer.
|
|
60
|
-
*
|
|
61
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
62
|
-
* registry), so callers can clear defensively without guarding.
|
|
63
|
-
*/
|
|
64
100
|
clear(id) {
|
|
65
101
|
if (this.ids.delete(id)) {
|
|
66
102
|
this.cancel(id);
|
|
67
103
|
}
|
|
68
104
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
71
|
-
* to guarantee no timer outlives the element.
|
|
72
|
-
*/
|
|
73
105
|
clearAll() {
|
|
74
106
|
for (const id of this.ids) {
|
|
75
107
|
this.cancel(id);
|
|
76
108
|
}
|
|
77
109
|
this.ids.clear();
|
|
78
110
|
}
|
|
79
|
-
/** Number of timers currently tracked (pending). */
|
|
80
111
|
get size() {
|
|
81
112
|
return this.ids.size;
|
|
82
113
|
}
|
|
83
114
|
};
|
|
84
115
|
var SafeTimeout = class extends TimerRegistry {
|
|
85
|
-
/**
|
|
86
|
-
* Schedules `callback` after `delay` ms and returns the timer id.
|
|
87
|
-
*
|
|
88
|
-
* The id is removed from the registry automatically when the timeout fires,
|
|
89
|
-
* so {@link TimerRegistry.size | size} reflects only still-pending timers.
|
|
90
|
-
*/
|
|
91
116
|
set(callback, delay) {
|
|
92
117
|
const id = this.schedule(() => {
|
|
93
118
|
this.ids.delete(id);
|
|
@@ -104,16 +129,9 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
104
129
|
}
|
|
105
130
|
};
|
|
106
131
|
|
|
107
|
-
// src/utils/keyed_timers.ts
|
|
108
132
|
var KeyedTimers = class {
|
|
109
133
|
#timers = new SafeTimeout();
|
|
110
|
-
/** The pending timer of each key; an entry lives exactly as long as its timer. */
|
|
111
134
|
#pending = /* @__PURE__ */ new Map();
|
|
112
|
-
/**
|
|
113
|
-
* Arms `callback` after `delay` ms for `key`, cancelling the timer `key` had
|
|
114
|
-
* pending. The entry is dropped before the callback runs, so the callback sees
|
|
115
|
-
* the key unarmed and may arm it again for the next round.
|
|
116
|
-
*/
|
|
117
135
|
set(key, callback, delay) {
|
|
118
136
|
this.clear(key);
|
|
119
137
|
const id = this.#timers.set(() => {
|
|
@@ -122,76 +140,38 @@ var KeyedTimers = class {
|
|
|
122
140
|
}, delay);
|
|
123
141
|
this.#pending.set(key, id);
|
|
124
142
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Cancels `key`'s pending timer, if it has one.
|
|
127
|
-
*
|
|
128
|
-
* A timer id is a positive integer, so `-1` stands for "nothing pending" and
|
|
129
|
-
* the registry ignores an id it does not own — the unarmed case needs no
|
|
130
|
-
* branch of its own, and no other key's timer can be reached from here.
|
|
131
|
-
*/
|
|
132
143
|
clear(key) {
|
|
133
144
|
this.#timers.clear(this.#pending.get(key) ?? -1);
|
|
134
145
|
this.#pending.delete(key);
|
|
135
146
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Cancels every pending timer and forgets every key. Call this from a
|
|
138
|
-
* controller's `disconnect()` so no timer, and no entry, outlives the element.
|
|
139
|
-
*/
|
|
140
147
|
clearAll() {
|
|
141
148
|
this.#timers.clearAll();
|
|
142
149
|
this.#pending.clear();
|
|
143
150
|
}
|
|
144
|
-
/** Whether `key` has a timer pending. */
|
|
145
151
|
has(key) {
|
|
146
152
|
return this.#pending.has(key);
|
|
147
153
|
}
|
|
148
154
|
};
|
|
149
155
|
|
|
150
|
-
// src/utils/listener_set.ts
|
|
151
|
-
var ListenerSet = class {
|
|
152
|
-
/** The generation every `add` joins until the next `dispose()`. */
|
|
153
|
-
#abort = new AbortController();
|
|
154
|
-
/**
|
|
155
|
-
* Attaches `handler` to the open generation, exactly as the caller spelled it.
|
|
156
|
-
*
|
|
157
|
-
* The set supplies the signal, so `options` carries everything else the DOM
|
|
158
|
-
* accepts — `capture` included, which has to match at release time and no
|
|
159
|
-
* longer has a second place to drift from.
|
|
160
|
-
*/
|
|
161
|
-
add(target, type, handler, options) {
|
|
162
|
-
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Releases every listener of the open generation, synchronously, and opens the
|
|
166
|
-
* next one. Idempotent, and safe before anything has been added.
|
|
167
|
-
*/
|
|
168
|
-
dispose() {
|
|
169
|
-
this.#abort.abort();
|
|
170
|
-
this.#abort = new AbortController();
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
// src/utils/microtask_coalescer.ts
|
|
175
156
|
var MicrotaskCoalescer = class {
|
|
176
157
|
#run;
|
|
177
158
|
#queued = false;
|
|
178
159
|
#active = false;
|
|
179
160
|
#generation = 0;
|
|
180
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
181
161
|
constructor(run) {
|
|
182
162
|
this.#run = run;
|
|
183
163
|
}
|
|
184
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
185
164
|
activate() {
|
|
186
165
|
this.#active = true;
|
|
187
166
|
}
|
|
188
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
189
167
|
cancel() {
|
|
190
168
|
this.#active = false;
|
|
191
169
|
this.#queued = false;
|
|
192
170
|
this.#generation += 1;
|
|
193
171
|
}
|
|
194
|
-
|
|
172
|
+
get pending() {
|
|
173
|
+
return this.#queued;
|
|
174
|
+
}
|
|
195
175
|
schedule() {
|
|
196
176
|
if (!this.#active || this.#queued) return;
|
|
197
177
|
this.#queued = true;
|
|
@@ -204,27 +184,88 @@ var MicrotaskCoalescer = class {
|
|
|
204
184
|
}
|
|
205
185
|
};
|
|
206
186
|
|
|
207
|
-
|
|
187
|
+
var MorphRenderWatcher = class {
|
|
188
|
+
#listeners = new ListenerSet();
|
|
189
|
+
#pass;
|
|
190
|
+
#element = null;
|
|
191
|
+
#onMorph = () => this.#pass.schedule();
|
|
192
|
+
constructor(run) {
|
|
193
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
194
|
+
}
|
|
195
|
+
observe(element) {
|
|
196
|
+
if (this.#element === element) return;
|
|
197
|
+
this.disconnect();
|
|
198
|
+
this.#element = element;
|
|
199
|
+
this.#pass.activate();
|
|
200
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
201
|
+
}
|
|
202
|
+
schedule() {
|
|
203
|
+
this.#pass.schedule();
|
|
204
|
+
}
|
|
205
|
+
get pending() {
|
|
206
|
+
return this.#pass.pending;
|
|
207
|
+
}
|
|
208
|
+
disconnect() {
|
|
209
|
+
this.#listeners.dispose();
|
|
210
|
+
this.#pass.cancel();
|
|
211
|
+
this.#element = null;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
var NUMBER_BOUNDS = {
|
|
216
|
+
finite: { finite: true },
|
|
217
|
+
timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
|
|
218
|
+
function matchesNumberBounds(value, bounds) {
|
|
219
|
+
if (!Number.isFinite(value)) {
|
|
220
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
221
|
+
if (direction === null) return false;
|
|
222
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
223
|
+
}
|
|
224
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
225
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
226
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
227
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
228
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function readNumber(raw, fallback, bounds) {
|
|
233
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
var NumberValueReader = class {
|
|
237
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
238
|
+
read(owner, name, raw, fallback, bounds) {
|
|
239
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
240
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
241
|
+
this.#lastRejected.delete(name);
|
|
242
|
+
return resolved;
|
|
243
|
+
}
|
|
244
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
245
|
+
const literal = owner.element.getAttribute(attribute);
|
|
246
|
+
if (literal === null) {
|
|
247
|
+
this.#lastRejected.delete(name);
|
|
248
|
+
return resolved;
|
|
249
|
+
}
|
|
250
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
251
|
+
this.#lastRejected.set(name, literal);
|
|
252
|
+
console.warn(
|
|
253
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return resolved;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
208
260
|
var PausableTimers = class {
|
|
209
261
|
#timers = new KeyedTimers();
|
|
210
|
-
/** Every key armed or held; an entry goes as its timer fires, or with the key. */
|
|
211
262
|
#entries = /* @__PURE__ */ new Map();
|
|
212
|
-
/**
|
|
213
|
-
* Arms `callback` after `delay` ms for `key`, replacing whatever `key` had.
|
|
214
|
-
* A held key stays held and banks `delay` for its resume.
|
|
215
|
-
*/
|
|
216
263
|
set(key, callback, delay) {
|
|
217
264
|
const reasons = this.#entries.get(key)?.reasons ?? /* @__PURE__ */ new Set();
|
|
218
265
|
const entry = { callback, startedAt: 0, remaining: delay, reasons };
|
|
219
266
|
this.#entries.set(key, entry);
|
|
220
267
|
if (reasons.size === 0) this.#arm(key, entry, callback);
|
|
221
268
|
}
|
|
222
|
-
/**
|
|
223
|
-
* Holds `key` for `reason` and banks the time left, with a floor of one
|
|
224
|
-
* millisecond. A key with no timer is held all the same. Reports whether this
|
|
225
|
-
* call is the one that stopped a running timer, so the caller can write its
|
|
226
|
-
* held-state hook exactly once.
|
|
227
|
-
*/
|
|
228
269
|
pause(key, reason) {
|
|
229
270
|
let entry = this.#entries.get(key);
|
|
230
271
|
if (entry === void 0) {
|
|
@@ -237,11 +278,6 @@ var PausableTimers = class {
|
|
|
237
278
|
entry.remaining = Math.max(1, entry.remaining - (Date.now() - entry.startedAt));
|
|
238
279
|
return true;
|
|
239
280
|
}
|
|
240
|
-
/**
|
|
241
|
-
* Releases `reason` on `key`, arming the banked time again once no reason is
|
|
242
|
-
* left; a key released with no timer is forgotten. Reports whether this call
|
|
243
|
-
* is the one that started the timer again.
|
|
244
|
-
*/
|
|
245
281
|
resume(key, reason) {
|
|
246
282
|
const entry = this.#entries.get(key);
|
|
247
283
|
if (entry === void 0) return false;
|
|
@@ -255,38 +291,20 @@ var PausableTimers = class {
|
|
|
255
291
|
this.#arm(key, entry, callback);
|
|
256
292
|
return true;
|
|
257
293
|
}
|
|
258
|
-
/** Whether any reason holds `key`, with or without a timer. */
|
|
259
294
|
isHeld(key) {
|
|
260
295
|
return (this.#entries.get(key)?.reasons.size ?? 0) > 0;
|
|
261
296
|
}
|
|
262
|
-
/** Whether `key` is armed or held — that is, whether this registry drives it at all. */
|
|
263
297
|
tracks(key) {
|
|
264
298
|
return this.#entries.has(key);
|
|
265
299
|
}
|
|
266
|
-
/**
|
|
267
|
-
* Cancels `key`'s timer and keeps its hold, so a release arms nothing; a key
|
|
268
|
-
* nothing holds is forgotten.
|
|
269
|
-
*/
|
|
270
|
-
disarm(key) {
|
|
271
|
-
this.#timers.clear(key);
|
|
272
|
-
const entry = this.#entries.get(key);
|
|
273
|
-
if (entry?.reasons.size) entry.callback = null;
|
|
274
|
-
else this.#entries.delete(key);
|
|
275
|
-
}
|
|
276
|
-
/** Cancels `key`'s timer and drops its hold. */
|
|
277
300
|
clear(key) {
|
|
278
301
|
this.#timers.clear(key);
|
|
279
302
|
this.#entries.delete(key);
|
|
280
303
|
}
|
|
281
|
-
/**
|
|
282
|
-
* Cancels every timer and forgets every key. Call this from a controller's
|
|
283
|
-
* `disconnect()` so neither a timer nor a hold outlives the element.
|
|
284
|
-
*/
|
|
285
304
|
clearAll() {
|
|
286
305
|
this.#timers.clearAll();
|
|
287
306
|
this.#entries.clear();
|
|
288
307
|
}
|
|
289
|
-
/** Starts `entry`'s banked time running for `key`, and drops it as `callback` fires. */
|
|
290
308
|
#arm(key, entry, callback) {
|
|
291
309
|
entry.startedAt = Date.now();
|
|
292
310
|
this.#timers.set(
|
|
@@ -300,12 +318,10 @@ var PausableTimers = class {
|
|
|
300
318
|
}
|
|
301
319
|
};
|
|
302
320
|
|
|
303
|
-
// src/utils/target_selector.ts
|
|
304
321
|
function targetSelector(identifier, name) {
|
|
305
322
|
return `[data-${identifier}-target~="${name}"]`;
|
|
306
323
|
}
|
|
307
324
|
|
|
308
|
-
// src/utils/transition_completion.ts
|
|
309
325
|
function timeMs(value) {
|
|
310
326
|
const trimmed = value.trim();
|
|
311
327
|
const amount = Number.parseFloat(trimmed);
|
|
@@ -339,11 +355,11 @@ function maxTransitionTotalMs(style) {
|
|
|
339
355
|
return maxTotalMs(transitionTimings(style));
|
|
340
356
|
}
|
|
341
357
|
|
|
342
|
-
// src/controllers/flash_controller.ts
|
|
343
358
|
var ASSERTIVE_TYPES = /* @__PURE__ */ new Set(["alert", "error"]);
|
|
344
359
|
var MESSAGE_PART = "message";
|
|
345
|
-
var
|
|
346
|
-
|
|
360
|
+
var SHOWN_ATTRIBUTE = "shown";
|
|
361
|
+
var FlashController = class _FlashController extends Controller {
|
|
362
|
+
#numbers = new NumberValueReader();
|
|
347
363
|
get #messageSelector() {
|
|
348
364
|
return targetSelector(this.identifier, MESSAGE_PART);
|
|
349
365
|
}
|
|
@@ -353,54 +369,31 @@ var FlashController = class extends Controller {
|
|
|
353
369
|
pauseOnHover: { type: Boolean, default: true },
|
|
354
370
|
max: { type: Number, default: 0 }
|
|
355
371
|
};
|
|
372
|
+
static valueConstraints = {
|
|
373
|
+
duration: NUMBER_BOUNDS.timer,
|
|
374
|
+
max: NUMBER_BOUNDS.finite
|
|
375
|
+
};
|
|
356
376
|
static actions = ["dismiss"];
|
|
357
377
|
static events = ["show", "dismiss", "reconcile"];
|
|
358
|
-
/** Removal timers for the leaving transition; the auto-dismiss ones live below. */
|
|
359
378
|
#timers = new SafeTimeout();
|
|
360
|
-
/**
|
|
361
|
-
* Per-message auto-dismiss held open while the message is hovered or focused, and
|
|
362
|
-
* the one record of which messages hover or focus holds, which the cap reads.
|
|
363
|
-
*/
|
|
364
379
|
#dismiss = new PausableTimers();
|
|
365
|
-
/**
|
|
366
|
-
* Auto-dismiss of the messages taken on while `pauseOnHover` is off. It runs on
|
|
367
|
-
* whatever holds the message; the hold itself still keeps the cap away.
|
|
368
|
-
*/
|
|
369
380
|
#fixedDismiss = new KeyedTimers();
|
|
370
|
-
|
|
371
|
-
#reapply = new MicrotaskCoalescer(() => this.#reapplyCap());
|
|
372
|
-
/** Messages the pointer or focus moved into since the cap was last applied again. */
|
|
381
|
+
#reapply = new MorphRenderWatcher(() => this.#reapplyCap());
|
|
373
382
|
#spared = [];
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
383
|
+
#pointer = new HoverRecheck(
|
|
384
|
+
this.element.ownerDocument,
|
|
385
|
+
(message) => this.#resume(message, "hover", null)
|
|
386
|
+
);
|
|
378
387
|
#observer = null;
|
|
379
|
-
/** Whether the controller is between `connect()` and `disconnect()`. */
|
|
380
388
|
#connected = false;
|
|
381
|
-
|
|
382
|
-
* The `max` the stack was last held to, or `null` before the first time. `connect()`
|
|
383
|
-
* applies the cap only when `max` is not that one, so a reconnect of the same instance
|
|
384
|
-
* leaves the stack as it was unless `max` changed while it was away.
|
|
385
|
-
*/
|
|
389
|
+
#arrivals = /* @__PURE__ */ new Set();
|
|
386
390
|
#appliedMax = null;
|
|
387
|
-
/** Messages already processed, in insertion order, to enforce `max` and avoid double work. */
|
|
388
391
|
#order = [];
|
|
389
|
-
/**
|
|
390
|
-
* Messages between `leaving` and their removal. {@link FlashController.#beginDismiss}
|
|
391
|
-
* releases the bookkeeping above *before* the transition wait, so for that window the
|
|
392
|
-
* element is in the DOM but in neither collection — without this set a re-scan would
|
|
393
|
-
* read it as a brand-new flash and show it a second time.
|
|
394
|
-
*/
|
|
395
392
|
#leaving = /* @__PURE__ */ new Set();
|
|
396
|
-
#
|
|
393
|
+
get #shownAttribute() {
|
|
394
|
+
return `data-${this.identifier}-${SHOWN_ATTRIBUTE}`;
|
|
395
|
+
}
|
|
397
396
|
#onEnter = (event) => this.#pause(event.currentTarget, event.type === "focusin" ? "focus" : "hover");
|
|
398
|
-
/**
|
|
399
|
-
* Releases hover or focus on a message. Focus moving between two of the message's
|
|
400
|
-
* own controls is no release, since `focusout` names a control still inside it; and
|
|
401
|
-
* the message the pointer or focus is moving into is spared when the release
|
|
402
|
-
* applies the cap again, because its own hold only arrives after this event.
|
|
403
|
-
*/
|
|
404
397
|
#onLeave = (event) => {
|
|
405
398
|
const message = event.currentTarget;
|
|
406
399
|
const next = ownerOf(this.#order, event.relatedTarget);
|
|
@@ -409,92 +402,58 @@ var FlashController = class extends Controller {
|
|
|
409
402
|
};
|
|
410
403
|
connect() {
|
|
411
404
|
this.#connected = true;
|
|
412
|
-
this
|
|
405
|
+
const current = this.messageTargets;
|
|
406
|
+
for (const item of this.#arrivals) {
|
|
407
|
+
if (!current.includes(item)) this.#arrivals.delete(item);
|
|
408
|
+
}
|
|
409
|
+
this.#discardInherited();
|
|
410
|
+
this.#reapply.observe(this.element);
|
|
413
411
|
for (const message of this.messageTargets) {
|
|
414
412
|
if (this.#owns(message)) this.#process(message, true);
|
|
415
413
|
}
|
|
416
|
-
if (!Object.is(this.#appliedMax, this
|
|
417
|
-
this.#appliedMax = this
|
|
414
|
+
if (!Object.is(this.#appliedMax, this.#safeMax)) {
|
|
415
|
+
this.#appliedMax = this.#safeMax;
|
|
418
416
|
this.#enforceMax();
|
|
419
417
|
}
|
|
420
418
|
this.#syncObservation();
|
|
421
|
-
this.#beforeCache.activate();
|
|
422
419
|
}
|
|
423
420
|
disconnect() {
|
|
424
421
|
this.#connected = false;
|
|
425
|
-
this.#beforeCache.deactivate();
|
|
426
422
|
this.#stopObserving();
|
|
427
423
|
this.#timers.clearAll();
|
|
428
424
|
this.#dismiss.clearAll();
|
|
429
425
|
this.#fixedDismiss.clearAll();
|
|
430
|
-
this.#reapply.
|
|
426
|
+
this.#reapply.disconnect();
|
|
431
427
|
this.#spared = [];
|
|
432
428
|
this.#pointer.dispose();
|
|
433
|
-
this.#awaitingPointer.clear();
|
|
434
429
|
for (const message of this.#order) this.#unbindPause(message);
|
|
435
430
|
this.#order.length = 0;
|
|
436
431
|
this.#leaving.clear();
|
|
437
432
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
* too — that value governs the timer, not what belongs in a cached page. Removal
|
|
444
|
-
* only: `dismiss` reports a dismissal, and freezing the page is not one.
|
|
445
|
-
*/
|
|
446
|
-
#rewindForCache() {
|
|
447
|
-
const removed = this.#order.length;
|
|
448
|
-
for (const message of [...this.#order]) {
|
|
433
|
+
#discardInherited() {
|
|
434
|
+
let removed = 0;
|
|
435
|
+
for (const message of this.messageTargets) {
|
|
436
|
+
if (!message.hasAttribute(this.#shownAttribute) || this.#arrivals.has(message)) continue;
|
|
437
|
+
if (!this.#owns(message)) continue;
|
|
449
438
|
message.remove();
|
|
450
|
-
|
|
439
|
+
removed += 1;
|
|
451
440
|
}
|
|
452
|
-
for (const message of this.#leaving) message.remove();
|
|
453
|
-
this.#leaving.clear();
|
|
454
441
|
if (removed > 0) this.dispatch("reconcile", { detail: { removed } });
|
|
455
442
|
}
|
|
456
|
-
/** Follows a `region` element swapped in — or arriving — at runtime (Turbo Stream). */
|
|
457
443
|
regionTargetConnected() {
|
|
458
444
|
this.#resync();
|
|
459
445
|
}
|
|
460
|
-
/** Releases the observation when the `region` element leaves the target set. */
|
|
461
446
|
regionTargetDisconnected() {
|
|
462
447
|
this.#resync();
|
|
463
448
|
}
|
|
464
|
-
/**
|
|
465
|
-
* Holds the stack to a changed `max`: lowering it below the count dismisses the
|
|
466
|
-
* oldest messages hover and focus do not hold, with reason `limit`, as an arrival
|
|
467
|
-
* past the cap does, while raising it or setting `0` dismisses nothing. Stimulus can
|
|
468
|
-
* also call this ahead of `connect()` — for an attribute that changed while the
|
|
469
|
-
* controller was away, and on every connect for an undeclared `max`, with its default;
|
|
470
|
-
* `connect()` decides then.
|
|
471
|
-
*/
|
|
472
449
|
maxValueChanged() {
|
|
473
450
|
if (!this.#connected) return;
|
|
474
|
-
this.#appliedMax = this
|
|
451
|
+
this.#appliedMax = this.#safeMax;
|
|
475
452
|
this.#enforceMax();
|
|
476
453
|
}
|
|
477
|
-
/**
|
|
478
|
-
* Whether this controller owns `message`. Ownership is the current `region`'s
|
|
479
|
-
* subtree: a message target anywhere else in the controller's scope is the
|
|
480
|
-
* consumer's, and so is one in a region that has gone away. The initial scan, a
|
|
481
|
-
* re-scan after a `region` swap, and a departure from the target set all resolve
|
|
482
|
-
* ownership through this one test; the observation gets it structurally, by watching
|
|
483
|
-
* that subtree and nothing else.
|
|
484
|
-
*/
|
|
485
454
|
#owns(message) {
|
|
486
455
|
return this.hasRegionTarget && this.regionTarget.contains(message);
|
|
487
456
|
}
|
|
488
|
-
/**
|
|
489
|
-
* Releases a message that left the target set (a Turbo Stream `remove`, the consumer
|
|
490
|
-
* detaching the node, or a morph that rewrote the target attribute in place): it
|
|
491
|
-
* stops occupying a `max` slot, and both its pending auto-dismiss and an already
|
|
492
|
-
* scheduled removal are cancelled. A move *within* the region keeps all of them —
|
|
493
|
-
* which is why the element must still be a message to be treated as one: ownership
|
|
494
|
-
* alone reads an in-place attribute rewrite as a move, and a node outside the target
|
|
495
|
-
* set belongs to the consumer, so nothing here may dismiss it. A move may still have
|
|
496
|
-
* ended a hold, so those are read again.
|
|
497
|
-
*/
|
|
498
457
|
messageTargetDisconnected(message) {
|
|
499
458
|
if (!this.#connected) return;
|
|
500
459
|
const moved = this.#owns(message) && message.matches(this.#messageSelector);
|
|
@@ -503,15 +462,9 @@ var FlashController = class extends Controller {
|
|
|
503
462
|
return;
|
|
504
463
|
}
|
|
505
464
|
this.#forget(message);
|
|
465
|
+
this.#arrivals.delete(message);
|
|
506
466
|
this.#leaving.delete(message);
|
|
507
467
|
}
|
|
508
|
-
/**
|
|
509
|
-
* Re-points the observation after a `region` swap and picks up the messages the
|
|
510
|
-
* new element brought with it (dynamic inserts, so their own `role` announces
|
|
511
|
-
* them). The `#connected` guard is load-bearing: Stimulus runs target callbacks
|
|
512
|
-
* for the initial markup *before* `connect()` and again during teardown *after*
|
|
513
|
-
* `disconnect()`, and re-observing there would outlive the controller.
|
|
514
|
-
*/
|
|
515
468
|
#resync() {
|
|
516
469
|
if (!this.#connected) return;
|
|
517
470
|
this.#syncObservation();
|
|
@@ -519,11 +472,6 @@ var FlashController = class extends Controller {
|
|
|
519
472
|
if (this.#owns(message)) this.#process(message, false);
|
|
520
473
|
}
|
|
521
474
|
}
|
|
522
|
-
/**
|
|
523
|
-
* Points the mutation observation at the current `region` target, re-resolved on
|
|
524
|
-
* every sync rather than captured at connect, so an element swapped in at runtime
|
|
525
|
-
* is observed instead of the detached original.
|
|
526
|
-
*/
|
|
527
475
|
#syncObservation() {
|
|
528
476
|
this.#stopObserving();
|
|
529
477
|
if (!this.hasRegionTarget || typeof MutationObserver === "undefined") return;
|
|
@@ -534,11 +482,6 @@ var FlashController = class extends Controller {
|
|
|
534
482
|
this.#observer?.disconnect();
|
|
535
483
|
this.#observer = null;
|
|
536
484
|
}
|
|
537
|
-
/**
|
|
538
|
-
* Hover and focus listeners, bound and unbound as a pair so the two sides stay in
|
|
539
|
-
* sync. Every message gets them, whatever `pauseOnHover` says, because the cap reads
|
|
540
|
-
* the holds they record; unbinding is idempotent (a no-op when nothing was bound).
|
|
541
|
-
*/
|
|
542
485
|
#bindPause(message) {
|
|
543
486
|
message.addEventListener("mouseenter", this.#onEnter);
|
|
544
487
|
message.addEventListener("mouseleave", this.#onLeave);
|
|
@@ -551,13 +494,14 @@ var FlashController = class extends Controller {
|
|
|
551
494
|
message.removeEventListener("focusin", this.#onEnter);
|
|
552
495
|
message.removeEventListener("focusout", this.#onLeave);
|
|
553
496
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
497
|
+
dismiss(input) {
|
|
498
|
+
const source = actionSource(input);
|
|
499
|
+
if (source.origin && source.origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
500
|
+
return;
|
|
501
|
+
const message = (source.origin ?? source.host)?.closest(this.#messageSelector);
|
|
502
|
+
if (message && this.messageTargets.includes(message))
|
|
503
|
+
this.#beginDismiss(message, source.reason);
|
|
559
504
|
}
|
|
560
|
-
/** Processes messages added after connect (Turbo Stream); their own role announces them. */
|
|
561
505
|
#onMutations(mutations) {
|
|
562
506
|
for (const mutation of mutations) {
|
|
563
507
|
for (const node of mutation.addedNodes) {
|
|
@@ -569,14 +513,6 @@ var FlashController = class extends Controller {
|
|
|
569
513
|
}
|
|
570
514
|
}
|
|
571
515
|
}
|
|
572
|
-
/**
|
|
573
|
-
* Applies role/state, wires pause listeners, takes the holds the message already has,
|
|
574
|
-
* and schedules auto-dismiss. A message `connect()` takes on (`atConnect`) is bridged
|
|
575
|
-
* to the Announcer and meets no cap here, `connect()` holding the stack to it; a later
|
|
576
|
-
* insert announces through its own role and meets the cap as it arrives. Idempotent
|
|
577
|
-
* per message, and a node that has left the region by the time its insertion is
|
|
578
|
-
* reported is not taken on.
|
|
579
|
-
*/
|
|
580
516
|
#process(message, atConnect) {
|
|
581
517
|
if (this.#order.includes(message) || this.#leaving.has(message) || !this.#owns(message)) {
|
|
582
518
|
return;
|
|
@@ -587,134 +523,74 @@ var FlashController = class extends Controller {
|
|
|
587
523
|
message.setAttribute("role", assertive ? "alert" : "status");
|
|
588
524
|
}
|
|
589
525
|
message.setAttribute("data-flash-state", "visible");
|
|
526
|
+
message.setAttribute(this.#shownAttribute, "");
|
|
527
|
+
const known = this.#arrivals.has(message);
|
|
590
528
|
this.#order.push(message);
|
|
529
|
+
this.#arrivals.add(message);
|
|
591
530
|
this.#bindPause(message);
|
|
592
531
|
this.#readHolds(message);
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
532
|
+
if (!known) {
|
|
533
|
+
const text = message.textContent?.trim() ?? "";
|
|
534
|
+
this.dispatch("show", { target: message, detail: { type, message: text } });
|
|
535
|
+
if (atConnect) announce(text, { assertive });
|
|
536
|
+
}
|
|
596
537
|
this.#startTimer(message);
|
|
597
538
|
if (!atConnect) this.#enforceMax([message]);
|
|
598
539
|
}
|
|
599
|
-
/**
|
|
600
|
-
* Dismisses the oldest messages with reason `limit` while more than `max` are shown
|
|
601
|
-
* (0 or less, or not a finite number, means no cap), passing over every message hover
|
|
602
|
-
* or focus holds and those in `spared` — the message just taken on, or the ones the
|
|
603
|
-
* pointer or focus moved into. Only messages still in the region count or go: one a
|
|
604
|
-
* script has just taken out stays on the stack until its removal is reported. With
|
|
605
|
-
* nothing else left to go the stack stays over the cap. The loop walks a copy and
|
|
606
|
-
* re-reads the count on every step, because a `dismiss` listener may already have
|
|
607
|
-
* changed both.
|
|
608
|
-
*
|
|
609
|
-
* @stimeoRenderRoot
|
|
610
|
-
*/
|
|
611
540
|
#enforceMax(spared = []) {
|
|
612
|
-
if (
|
|
613
|
-
for (const message of [...this.#
|
|
614
|
-
if (this.#shownCount() <= this
|
|
541
|
+
if (this.#safeMax <= 0) return;
|
|
542
|
+
for (const message of [...this.#arrivals]) {
|
|
543
|
+
if (this.#shownCount() <= this.#safeMax) return;
|
|
615
544
|
if (this.#owns(message) && !spared.includes(message) && !this.#dismiss.isHeld(message)) {
|
|
616
545
|
this.#beginDismiss(message, "limit");
|
|
617
546
|
}
|
|
618
547
|
}
|
|
619
548
|
}
|
|
620
|
-
/** How many messages the stack shows: the ones taken on that are still in the region. */
|
|
621
549
|
#shownCount() {
|
|
622
550
|
return this.#order.filter((message) => this.#owns(message)).length;
|
|
623
551
|
}
|
|
624
|
-
/**
|
|
625
|
-
* Arms a message's auto-dismiss; a non-positive `duration` means it never expires.
|
|
626
|
-
* The timer waits out hover and focus when `pauseOnHover` is on, and runs on
|
|
627
|
-
* regardless when it is off.
|
|
628
|
-
*
|
|
629
|
-
* @stimeoRuntimeOnly `duration` is the delay of the one dismissal timer this call arms,
|
|
630
|
-
* and `pauseOnHover` whether hover and focus hold that timer.
|
|
631
|
-
*/
|
|
632
552
|
#startTimer(message) {
|
|
633
|
-
if (this
|
|
553
|
+
if (this.#safeDuration <= 0) return;
|
|
634
554
|
const dismiss = () => this.#beginDismiss(message, "timeout");
|
|
635
|
-
if (this.pauseOnHoverValue) this.#dismiss.set(message, dismiss, this
|
|
636
|
-
else this.#fixedDismiss.set(message, dismiss, this
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* Records hover or focus on a message. Hover and focus are independent reasons:
|
|
640
|
-
* the registry banks the time left on the first of them and waits for the last
|
|
641
|
-
* (WCAG 2.2 2.2.1), and the cap passes over the message while either is held.
|
|
642
|
-
*/
|
|
555
|
+
if (this.pauseOnHoverValue) this.#dismiss.set(message, dismiss, this.#safeDuration);
|
|
556
|
+
else this.#fixedDismiss.set(message, dismiss, this.#safeDuration);
|
|
557
|
+
}
|
|
643
558
|
#pause(message, reason) {
|
|
644
559
|
this.#dismiss.pause(message, reason);
|
|
645
560
|
}
|
|
646
|
-
/**
|
|
647
|
-
* Releases one reason, resuming the banked time once no reason is left. Releasing
|
|
648
|
-
* the last one applies the cap again, sparing `spare`.
|
|
649
|
-
*/
|
|
650
561
|
#resume(message, reason, spare) {
|
|
651
562
|
const held = this.#dismiss.isHeld(message);
|
|
652
563
|
this.#dismiss.resume(message, reason);
|
|
653
564
|
if (held && !this.#dismiss.isHeld(message)) this.#capLater(spare);
|
|
654
565
|
}
|
|
655
|
-
/**
|
|
656
|
-
* Applies the cap again once the current event is over, sparing `spare`. A hold can
|
|
657
|
-
* end in a `focusout` the engine fires while it is itself taking the node out for a
|
|
658
|
-
* script's move or removal, and dismissing the message inside that event would pull
|
|
659
|
-
* the node from under the caller's own call.
|
|
660
|
-
*/
|
|
661
566
|
#capLater(spare) {
|
|
662
567
|
if (spare) this.#spared.push(spare);
|
|
663
568
|
this.#reapply.schedule();
|
|
664
569
|
}
|
|
665
|
-
/** Applies the cap again, sparing every message gathered since the last pass. */
|
|
666
570
|
#reapplyCap() {
|
|
571
|
+
for (const message of [...this.#order, ...this.#leaving]) {
|
|
572
|
+
if (!this.#owns(message)) continue;
|
|
573
|
+
message.setAttribute("data-flash-state", this.#leaving.has(message) ? "leaving" : "visible");
|
|
574
|
+
message.setAttribute(this.#shownAttribute, "");
|
|
575
|
+
if (!message.hasAttribute("role")) {
|
|
576
|
+
const assertive = ASSERTIVE_TYPES.has(message.getAttribute("data-flash-type") ?? "");
|
|
577
|
+
message.setAttribute("role", assertive ? "alert" : "status");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
667
580
|
const spared = this.#spared;
|
|
668
581
|
this.#spared = [];
|
|
669
582
|
this.#enforceMax(spared);
|
|
670
583
|
}
|
|
671
|
-
/**
|
|
672
|
-
* Reads the holds of a message that moved within the region. Focus is read at once:
|
|
673
|
-
* an ordinary move ends it with a `focusout` and `moveBefore` keeps it. Where the
|
|
674
|
-
* pointer is, nothing says until it moves again: no `mouseleave` reaches a moved node,
|
|
675
|
-
* and `:hover` can answer from before the move. A message still held waits for that
|
|
676
|
-
* movement.
|
|
677
|
-
*/
|
|
678
584
|
#afterMove(message) {
|
|
679
585
|
if (!message.contains(document.activeElement)) this.#resume(message, "focus", null);
|
|
680
|
-
if (this.#dismiss.isHeld(message)) this.#
|
|
586
|
+
if (this.#dismiss.isHeld(message)) this.#pointer.wait(message);
|
|
681
587
|
}
|
|
682
|
-
/**
|
|
683
|
-
* Takes the holds a message already has as it is taken on: focus inside it, and the
|
|
684
|
-
* pointer over it by `:hover`. That answer can date from before the message came here,
|
|
685
|
-
* so a hover hold taken on it waits for the next pointer movement to be confirmed.
|
|
686
|
-
*/
|
|
687
588
|
#readHolds(message) {
|
|
688
589
|
if (message.contains(document.activeElement)) this.#pause(message, "focus");
|
|
689
590
|
if (!message.matches(":hover")) return;
|
|
690
591
|
this.#pause(message, "hover");
|
|
691
|
-
this.#
|
|
592
|
+
this.#pointer.wait(message);
|
|
692
593
|
}
|
|
693
|
-
/** Leaves `message`'s hover hold to be confirmed or let go on the next pointer movement. */
|
|
694
|
-
#awaitPointer(message) {
|
|
695
|
-
this.#awaitingPointer.add(message);
|
|
696
|
-
this.#pointer.add(document, "pointermove", this.#onPointerMove, {
|
|
697
|
-
capture: true,
|
|
698
|
-
passive: true
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Reads `:hover` once the pointer has moved, when the answer is current, and lets go of
|
|
703
|
-
* the hover hold of every waiting message the pointer is not over. Listens once.
|
|
704
|
-
*/
|
|
705
|
-
#onPointerMove = () => {
|
|
706
|
-
this.#pointer.dispose();
|
|
707
|
-
const waiting = [...this.#awaitingPointer];
|
|
708
|
-
this.#awaitingPointer.clear();
|
|
709
|
-
for (const message of waiting) {
|
|
710
|
-
if (!message.matches(":hover")) this.#resume(message, "hover", null);
|
|
711
|
-
}
|
|
712
|
-
};
|
|
713
|
-
/**
|
|
714
|
-
* Releases every per-message resource: timers, hold, stacking slot, pause listeners,
|
|
715
|
-
* and the wait on the pointer. A message something still held counts as released: the
|
|
716
|
-
* cap is applied again.
|
|
717
|
-
*/
|
|
718
594
|
#forget(message) {
|
|
719
595
|
if (this.#dismiss.isHeld(message)) this.#capLater(null);
|
|
720
596
|
this.#dismiss.clear(message);
|
|
@@ -722,10 +598,8 @@ var FlashController = class extends Controller {
|
|
|
722
598
|
const index = this.#order.indexOf(message);
|
|
723
599
|
if (index !== -1) this.#order.splice(index, 1);
|
|
724
600
|
this.#unbindPause(message);
|
|
725
|
-
this.#
|
|
726
|
-
if (this.#awaitingPointer.size === 0) this.#pointer.dispose();
|
|
601
|
+
this.#pointer.cancel(message);
|
|
727
602
|
}
|
|
728
|
-
/** Marks a message leaving, then removes it after its CSS transition and emits dismiss. */
|
|
729
603
|
#beginDismiss(message, reason) {
|
|
730
604
|
if (!this.#order.includes(message)) return;
|
|
731
605
|
this.#forget(message);
|
|
@@ -734,6 +608,7 @@ var FlashController = class extends Controller {
|
|
|
734
608
|
const finalize = () => {
|
|
735
609
|
if (!this.#leaving.delete(message)) return;
|
|
736
610
|
message.remove();
|
|
611
|
+
this.#arrivals.delete(message);
|
|
737
612
|
this.dispatch("dismiss", { detail: { element: message, reason } });
|
|
738
613
|
};
|
|
739
614
|
const transition = this.#transitionMs(message);
|
|
@@ -743,13 +618,29 @@ var FlashController = class extends Controller {
|
|
|
743
618
|
finalize();
|
|
744
619
|
}
|
|
745
620
|
}
|
|
746
|
-
/** Maximum transition total (duration + delay) of `el` in ms (0 when none / unsupported). */
|
|
747
621
|
#transitionMs(el) {
|
|
748
622
|
if (typeof window.getComputedStyle !== "function") return 0;
|
|
749
623
|
return maxTransitionTotalMs(window.getComputedStyle(el));
|
|
750
624
|
}
|
|
625
|
+
get #safeDuration() {
|
|
626
|
+
return this.#numbers.read(
|
|
627
|
+
this,
|
|
628
|
+
"duration",
|
|
629
|
+
this.durationValue,
|
|
630
|
+
_FlashController.values.duration.default,
|
|
631
|
+
_FlashController.valueConstraints.duration
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
get #safeMax() {
|
|
635
|
+
return this.#numbers.read(
|
|
636
|
+
this,
|
|
637
|
+
"max",
|
|
638
|
+
this.maxValue,
|
|
639
|
+
_FlashController.values.max.default,
|
|
640
|
+
_FlashController.valueConstraints.max
|
|
641
|
+
);
|
|
642
|
+
}
|
|
751
643
|
};
|
|
752
644
|
|
|
753
645
|
export { FlashController };
|
|
754
|
-
//# sourceMappingURL=flash_controller.js.map
|
|
755
646
|
//# sourceMappingURL=flash_controller.js.map
|