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/event_owner.ts
|
|
6
25
|
function ownerIndex(candidates, node) {
|
|
7
26
|
if (!(node instanceof Node)) return -1;
|
|
8
27
|
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
@@ -11,51 +30,78 @@ function ownerOf(candidates, node) {
|
|
|
11
30
|
return candidates[ownerIndex(candidates, node)] ?? null;
|
|
12
31
|
}
|
|
13
32
|
|
|
14
|
-
// src/utils/listener_set.ts
|
|
15
33
|
var ListenerSet = class {
|
|
16
|
-
/** The generation every `add` joins until the next `dispose()`. */
|
|
17
34
|
#abort = new AbortController();
|
|
18
|
-
/**
|
|
19
|
-
* Attaches `handler` to the open generation, exactly as the caller spelled it.
|
|
20
|
-
*
|
|
21
|
-
* The set supplies the signal, so `options` carries everything else the DOM
|
|
22
|
-
* accepts — `capture` included, which has to match at release time and no
|
|
23
|
-
* longer has a second place to drift from.
|
|
24
|
-
*/
|
|
25
35
|
add(target, type, handler, options) {
|
|
26
36
|
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
27
37
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Releases every listener of the open generation, synchronously, and opens the
|
|
30
|
-
* next one. Idempotent, and safe before anything has been added.
|
|
31
|
-
*/
|
|
32
38
|
dispose() {
|
|
33
39
|
this.#abort.abort();
|
|
34
40
|
this.#abort = new AbortController();
|
|
35
41
|
}
|
|
36
42
|
};
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
var HoverRecheck = class {
|
|
45
|
+
#document;
|
|
46
|
+
#release;
|
|
47
|
+
#listeners = new ListenerSet();
|
|
48
|
+
#pending = /* @__PURE__ */ new Set();
|
|
49
|
+
#tickets = /* @__PURE__ */ new WeakMap();
|
|
50
|
+
constructor(document2, release) {
|
|
51
|
+
this.#document = document2;
|
|
52
|
+
this.#release = release;
|
|
53
|
+
}
|
|
54
|
+
wait(element) {
|
|
55
|
+
this.#pending.add(element);
|
|
56
|
+
this.#tickets.set(element, /* @__PURE__ */ Symbol());
|
|
57
|
+
this.#listeners.add(this.#document, "pointermove", this.#onPointerMove, {
|
|
58
|
+
capture: true,
|
|
59
|
+
passive: true
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
cancel(element) {
|
|
63
|
+
this.#tickets.delete(element);
|
|
64
|
+
this.#pending.delete(element);
|
|
65
|
+
if (this.#pending.size === 0) this.#listeners.dispose();
|
|
66
|
+
}
|
|
67
|
+
dispose() {
|
|
68
|
+
this.#listeners.dispose();
|
|
69
|
+
this.#pending.clear();
|
|
70
|
+
this.#tickets = /* @__PURE__ */ new WeakMap();
|
|
71
|
+
}
|
|
72
|
+
#onPointerMove = () => {
|
|
73
|
+
this.#listeners.dispose();
|
|
74
|
+
const waiting = [...this.#pending].map(
|
|
75
|
+
(element) => [element, this.#tickets.get(element)]
|
|
76
|
+
);
|
|
77
|
+
this.#pending.clear();
|
|
78
|
+
for (const [element, ticket] of waiting) {
|
|
79
|
+
if (this.#tickets.get(element) !== ticket) continue;
|
|
80
|
+
if (element.matches(":hover")) continue;
|
|
81
|
+
this.#release(element);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
39
86
|
var MicrotaskCoalescer = class {
|
|
40
87
|
#run;
|
|
41
88
|
#queued = false;
|
|
42
89
|
#active = false;
|
|
43
90
|
#generation = 0;
|
|
44
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
45
91
|
constructor(run) {
|
|
46
92
|
this.#run = run;
|
|
47
93
|
}
|
|
48
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
49
94
|
activate() {
|
|
50
95
|
this.#active = true;
|
|
51
96
|
}
|
|
52
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
53
97
|
cancel() {
|
|
54
98
|
this.#active = false;
|
|
55
99
|
this.#queued = false;
|
|
56
100
|
this.#generation += 1;
|
|
57
101
|
}
|
|
58
|
-
|
|
102
|
+
get pending() {
|
|
103
|
+
return this.#queued;
|
|
104
|
+
}
|
|
59
105
|
schedule() {
|
|
60
106
|
if (!this.#active || this.#queued) return;
|
|
61
107
|
this.#queued = true;
|
|
@@ -68,43 +114,99 @@ var MicrotaskCoalescer = class {
|
|
|
68
114
|
}
|
|
69
115
|
};
|
|
70
116
|
|
|
71
|
-
|
|
117
|
+
var MorphRenderWatcher = class {
|
|
118
|
+
#listeners = new ListenerSet();
|
|
119
|
+
#pass;
|
|
120
|
+
#element = null;
|
|
121
|
+
#onMorph = () => this.#pass.schedule();
|
|
122
|
+
constructor(run) {
|
|
123
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
124
|
+
}
|
|
125
|
+
observe(element) {
|
|
126
|
+
if (this.#element === element) return;
|
|
127
|
+
this.disconnect();
|
|
128
|
+
this.#element = element;
|
|
129
|
+
this.#pass.activate();
|
|
130
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
131
|
+
}
|
|
132
|
+
schedule() {
|
|
133
|
+
this.#pass.schedule();
|
|
134
|
+
}
|
|
135
|
+
get pending() {
|
|
136
|
+
return this.#pass.pending;
|
|
137
|
+
}
|
|
138
|
+
disconnect() {
|
|
139
|
+
this.#listeners.dispose();
|
|
140
|
+
this.#pass.cancel();
|
|
141
|
+
this.#element = null;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
146
|
+
|
|
147
|
+
var NUMBER_BOUNDS = {
|
|
148
|
+
finite: { finite: true },
|
|
149
|
+
timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
|
|
150
|
+
function matchesNumberBounds(value, bounds) {
|
|
151
|
+
if (!Number.isFinite(value)) {
|
|
152
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
153
|
+
if (direction === null) return false;
|
|
154
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
155
|
+
}
|
|
156
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
157
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
158
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
159
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
160
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function readNumber(raw, fallback, bounds) {
|
|
165
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
var NumberValueReader = class {
|
|
169
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
170
|
+
read(owner, name, raw, fallback, bounds) {
|
|
171
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
172
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
173
|
+
this.#lastRejected.delete(name);
|
|
174
|
+
return resolved;
|
|
175
|
+
}
|
|
176
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
177
|
+
const literal = owner.element.getAttribute(attribute);
|
|
178
|
+
if (literal === null) {
|
|
179
|
+
this.#lastRejected.delete(name);
|
|
180
|
+
return resolved;
|
|
181
|
+
}
|
|
182
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
183
|
+
this.#lastRejected.set(name, literal);
|
|
184
|
+
console.warn(
|
|
185
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
return resolved;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
72
192
|
var TimerRegistry = class {
|
|
73
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
74
193
|
ids = /* @__PURE__ */ new Set();
|
|
75
|
-
/**
|
|
76
|
-
* Cancels a single tracked timer.
|
|
77
|
-
*
|
|
78
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
79
|
-
* registry), so callers can clear defensively without guarding.
|
|
80
|
-
*/
|
|
81
194
|
clear(id) {
|
|
82
195
|
if (this.ids.delete(id)) {
|
|
83
196
|
this.cancel(id);
|
|
84
197
|
}
|
|
85
198
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
88
|
-
* to guarantee no timer outlives the element.
|
|
89
|
-
*/
|
|
90
199
|
clearAll() {
|
|
91
200
|
for (const id of this.ids) {
|
|
92
201
|
this.cancel(id);
|
|
93
202
|
}
|
|
94
203
|
this.ids.clear();
|
|
95
204
|
}
|
|
96
|
-
/** Number of timers currently tracked (pending). */
|
|
97
205
|
get size() {
|
|
98
206
|
return this.ids.size;
|
|
99
207
|
}
|
|
100
208
|
};
|
|
101
209
|
var SafeTimeout = class extends TimerRegistry {
|
|
102
|
-
/**
|
|
103
|
-
* Schedules `callback` after `delay` ms and returns the timer id.
|
|
104
|
-
*
|
|
105
|
-
* The id is removed from the registry automatically when the timeout fires,
|
|
106
|
-
* so {@link TimerRegistry.size | size} reflects only still-pending timers.
|
|
107
|
-
*/
|
|
108
210
|
set(callback, delay) {
|
|
109
211
|
const id = this.schedule(() => {
|
|
110
212
|
this.ids.delete(id);
|
|
@@ -121,16 +223,9 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
121
223
|
}
|
|
122
224
|
};
|
|
123
225
|
|
|
124
|
-
// src/utils/keyed_timers.ts
|
|
125
226
|
var KeyedTimers = class {
|
|
126
227
|
#timers = new SafeTimeout();
|
|
127
|
-
/** The pending timer of each key; an entry lives exactly as long as its timer. */
|
|
128
228
|
#pending = /* @__PURE__ */ new Map();
|
|
129
|
-
/**
|
|
130
|
-
* Arms `callback` after `delay` ms for `key`, cancelling the timer `key` had
|
|
131
|
-
* pending. The entry is dropped before the callback runs, so the callback sees
|
|
132
|
-
* the key unarmed and may arm it again for the next round.
|
|
133
|
-
*/
|
|
134
229
|
set(key, callback, delay) {
|
|
135
230
|
this.clear(key);
|
|
136
231
|
const id = this.#timers.set(() => {
|
|
@@ -139,52 +234,28 @@ var KeyedTimers = class {
|
|
|
139
234
|
}, delay);
|
|
140
235
|
this.#pending.set(key, id);
|
|
141
236
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Cancels `key`'s pending timer, if it has one.
|
|
144
|
-
*
|
|
145
|
-
* A timer id is a positive integer, so `-1` stands for "nothing pending" and
|
|
146
|
-
* the registry ignores an id it does not own — the unarmed case needs no
|
|
147
|
-
* branch of its own, and no other key's timer can be reached from here.
|
|
148
|
-
*/
|
|
149
237
|
clear(key) {
|
|
150
238
|
this.#timers.clear(this.#pending.get(key) ?? -1);
|
|
151
239
|
this.#pending.delete(key);
|
|
152
240
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Cancels every pending timer and forgets every key. Call this from a
|
|
155
|
-
* controller's `disconnect()` so no timer, and no entry, outlives the element.
|
|
156
|
-
*/
|
|
157
241
|
clearAll() {
|
|
158
242
|
this.#timers.clearAll();
|
|
159
243
|
this.#pending.clear();
|
|
160
244
|
}
|
|
161
|
-
/** Whether `key` has a timer pending. */
|
|
162
245
|
has(key) {
|
|
163
246
|
return this.#pending.has(key);
|
|
164
247
|
}
|
|
165
248
|
};
|
|
166
249
|
|
|
167
|
-
// src/utils/pausable_timers.ts
|
|
168
250
|
var PausableTimers = class {
|
|
169
251
|
#timers = new KeyedTimers();
|
|
170
|
-
/** Every key armed or held; an entry goes as its timer fires, or with the key. */
|
|
171
252
|
#entries = /* @__PURE__ */ new Map();
|
|
172
|
-
/**
|
|
173
|
-
* Arms `callback` after `delay` ms for `key`, replacing whatever `key` had.
|
|
174
|
-
* A held key stays held and banks `delay` for its resume.
|
|
175
|
-
*/
|
|
176
253
|
set(key, callback, delay) {
|
|
177
254
|
const reasons = this.#entries.get(key)?.reasons ?? /* @__PURE__ */ new Set();
|
|
178
255
|
const entry = { callback, startedAt: 0, remaining: delay, reasons };
|
|
179
256
|
this.#entries.set(key, entry);
|
|
180
257
|
if (reasons.size === 0) this.#arm(key, entry, callback);
|
|
181
258
|
}
|
|
182
|
-
/**
|
|
183
|
-
* Holds `key` for `reason` and banks the time left, with a floor of one
|
|
184
|
-
* millisecond. A key with no timer is held all the same. Reports whether this
|
|
185
|
-
* call is the one that stopped a running timer, so the caller can write its
|
|
186
|
-
* held-state hook exactly once.
|
|
187
|
-
*/
|
|
188
259
|
pause(key, reason) {
|
|
189
260
|
let entry = this.#entries.get(key);
|
|
190
261
|
if (entry === void 0) {
|
|
@@ -197,11 +268,6 @@ var PausableTimers = class {
|
|
|
197
268
|
entry.remaining = Math.max(1, entry.remaining - (Date.now() - entry.startedAt));
|
|
198
269
|
return true;
|
|
199
270
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Releases `reason` on `key`, arming the banked time again once no reason is
|
|
202
|
-
* left; a key released with no timer is forgotten. Reports whether this call
|
|
203
|
-
* is the one that started the timer again.
|
|
204
|
-
*/
|
|
205
271
|
resume(key, reason) {
|
|
206
272
|
const entry = this.#entries.get(key);
|
|
207
273
|
if (entry === void 0) return false;
|
|
@@ -215,38 +281,20 @@ var PausableTimers = class {
|
|
|
215
281
|
this.#arm(key, entry, callback);
|
|
216
282
|
return true;
|
|
217
283
|
}
|
|
218
|
-
/** Whether any reason holds `key`, with or without a timer. */
|
|
219
284
|
isHeld(key) {
|
|
220
285
|
return (this.#entries.get(key)?.reasons.size ?? 0) > 0;
|
|
221
286
|
}
|
|
222
|
-
/** Whether `key` is armed or held — that is, whether this registry drives it at all. */
|
|
223
287
|
tracks(key) {
|
|
224
288
|
return this.#entries.has(key);
|
|
225
289
|
}
|
|
226
|
-
/**
|
|
227
|
-
* Cancels `key`'s timer and keeps its hold, so a release arms nothing; a key
|
|
228
|
-
* nothing holds is forgotten.
|
|
229
|
-
*/
|
|
230
|
-
disarm(key) {
|
|
231
|
-
this.#timers.clear(key);
|
|
232
|
-
const entry = this.#entries.get(key);
|
|
233
|
-
if (entry?.reasons.size) entry.callback = null;
|
|
234
|
-
else this.#entries.delete(key);
|
|
235
|
-
}
|
|
236
|
-
/** Cancels `key`'s timer and drops its hold. */
|
|
237
290
|
clear(key) {
|
|
238
291
|
this.#timers.clear(key);
|
|
239
292
|
this.#entries.delete(key);
|
|
240
293
|
}
|
|
241
|
-
/**
|
|
242
|
-
* Cancels every timer and forgets every key. Call this from a controller's
|
|
243
|
-
* `disconnect()` so neither a timer nor a hold outlives the element.
|
|
244
|
-
*/
|
|
245
294
|
clearAll() {
|
|
246
295
|
this.#timers.clearAll();
|
|
247
296
|
this.#entries.clear();
|
|
248
297
|
}
|
|
249
|
-
/** Starts `entry`'s banked time running for `key`, and drops it as `callback` fires. */
|
|
250
298
|
#arm(key, entry, callback) {
|
|
251
299
|
entry.startedAt = Date.now();
|
|
252
300
|
this.#timers.set(
|
|
@@ -260,18 +308,15 @@ var PausableTimers = class {
|
|
|
260
308
|
}
|
|
261
309
|
};
|
|
262
310
|
|
|
263
|
-
// src/utils/target_selector.ts
|
|
264
311
|
function targetSelector(identifier, name) {
|
|
265
312
|
return `[data-${identifier}-target~="${name}"]`;
|
|
266
313
|
}
|
|
267
314
|
|
|
268
|
-
// src/utils/template_row.ts
|
|
269
315
|
function cloneTemplateRoot(template) {
|
|
270
316
|
const root = template.content.firstElementChild;
|
|
271
317
|
return root instanceof HTMLElement ? root.cloneNode(true) : null;
|
|
272
318
|
}
|
|
273
319
|
|
|
274
|
-
// src/utils/transition_completion.ts
|
|
275
320
|
function timeMs(value) {
|
|
276
321
|
const trimmed = value.trim();
|
|
277
322
|
const amount = Number.parseFloat(trimmed);
|
|
@@ -305,62 +350,52 @@ function maxTransitionTotalMs(style) {
|
|
|
305
350
|
return maxTotalMs(transitionTimings(style));
|
|
306
351
|
}
|
|
307
352
|
|
|
308
|
-
// src/controllers/toast_controller.ts
|
|
309
353
|
var DELEGATED_EVENTS = ["click", "focusin", "focusout", "keydown", "mouseover", "mouseout"];
|
|
310
|
-
var ToastController = class extends Controller {
|
|
354
|
+
var ToastController = class _ToastController extends Controller {
|
|
355
|
+
#numbers = new NumberValueReader();
|
|
311
356
|
static targets = ["list", "template", "item"];
|
|
312
357
|
static values = {
|
|
313
358
|
duration: { type: Number, default: 0 },
|
|
314
359
|
max: { type: Number, default: 3 }
|
|
315
360
|
};
|
|
361
|
+
static valueConstraints = {
|
|
362
|
+
duration: NUMBER_BOUNDS.timer,
|
|
363
|
+
max: NUMBER_BOUNDS.finite
|
|
364
|
+
};
|
|
316
365
|
static actions = ["dismiss", "onKeydown", "pause", "resume", "show"];
|
|
317
366
|
static events = ["dismiss", "show"];
|
|
318
|
-
/** Removal timers for the leaving transition; the auto-dismiss ones live below. */
|
|
319
367
|
#timers = new SafeTimeout();
|
|
320
|
-
/** Per-toast auto-dismiss, held open while the toast is hovered or focused. */
|
|
321
368
|
#dismiss = new PausableTimers();
|
|
322
|
-
/** Toasts taken on and not let go; a move within the list keeps a toast here. */
|
|
323
369
|
#taken = /* @__PURE__ */ new Set();
|
|
324
|
-
|
|
325
|
-
#
|
|
326
|
-
|
|
370
|
+
#outside = /* @__PURE__ */ new WeakSet();
|
|
371
|
+
#phases = /* @__PURE__ */ new WeakMap();
|
|
372
|
+
#reapply = new MorphRenderWatcher(() => this.#reapplyLimit());
|
|
327
373
|
#spared = [];
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Pending one-shot `requestAnimationFrame` handles (the entering→visible flip).
|
|
334
|
-
* Tracked so {@link disconnect} can cancel any that have not fired, preventing a
|
|
335
|
-
* detached element from being mutated after it leaves the DOM (Turbo).
|
|
336
|
-
*/
|
|
374
|
+
#pointer = new HoverRecheck(
|
|
375
|
+
this.element.ownerDocument,
|
|
376
|
+
(item) => this.#release(item, "hover", null)
|
|
377
|
+
);
|
|
337
378
|
#rafHandles = /* @__PURE__ */ new Map();
|
|
338
|
-
/** The stable list that owns delegated listeners for dynamically added items. */
|
|
339
379
|
#delegatedList = null;
|
|
340
|
-
|
|
380
|
+
#listListeners = new ListenerSet();
|
|
341
381
|
#connected = false;
|
|
342
|
-
|
|
343
|
-
* The `max` the list was last held to, or `null` before the first time. `connect()`
|
|
344
|
-
* applies the limit only when `max` is not that one, so a reconnect of the same instance
|
|
345
|
-
* leaves the list as it was unless `max` changed while it was away.
|
|
346
|
-
*/
|
|
382
|
+
#arrivals = /* @__PURE__ */ new Set();
|
|
347
383
|
#appliedMax = null;
|
|
348
|
-
/**
|
|
349
|
-
* Stimulus reports every toast the list already holds before this runs, and each is
|
|
350
|
-
* taken on then with its holds, meeting no limit. The limit is applied here once, over
|
|
351
|
-
* all of them, unless the list was already held to the current `max`.
|
|
352
|
-
*/
|
|
353
384
|
connect() {
|
|
354
385
|
this.#connected = true;
|
|
355
|
-
this
|
|
386
|
+
const current = this.itemTargets;
|
|
387
|
+
for (const item of this.#arrivals) {
|
|
388
|
+
if (!current.includes(item)) this.#arrivals.delete(item);
|
|
389
|
+
}
|
|
390
|
+
this.#reapply.observe(this.element);
|
|
356
391
|
this.#connectDelegatedEvents();
|
|
357
392
|
for (const item of this.itemTargets) {
|
|
358
393
|
if (!this.#dismiss.tracks(item) && item.dataset.state !== "leaving") {
|
|
359
394
|
this.#startTimer(item);
|
|
360
395
|
}
|
|
361
396
|
}
|
|
362
|
-
if (!Object.is(this.#appliedMax, this
|
|
363
|
-
this.#appliedMax = this
|
|
397
|
+
if (!Object.is(this.#appliedMax, this.#safeMax)) {
|
|
398
|
+
this.#appliedMax = this.#safeMax;
|
|
364
399
|
this.#enforceMax();
|
|
365
400
|
}
|
|
366
401
|
}
|
|
@@ -370,75 +405,52 @@ var ToastController = class extends Controller {
|
|
|
370
405
|
this.#timers.clearAll();
|
|
371
406
|
this.#dismiss.clearAll();
|
|
372
407
|
this.#taken.clear();
|
|
373
|
-
this.#reapply.
|
|
408
|
+
this.#reapply.disconnect();
|
|
374
409
|
this.#spared = [];
|
|
375
410
|
this.#pointer.dispose();
|
|
376
|
-
this.#awaitingPointer.clear();
|
|
377
411
|
for (const handle of this.#rafHandles.values()) {
|
|
378
412
|
window.cancelAnimationFrame(handle);
|
|
379
413
|
}
|
|
380
414
|
this.#rafHandles.clear();
|
|
381
415
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (this.#delegatedList !== element) this.#connectDelegatedEvents(element);
|
|
416
|
+
listTargetConnected() {
|
|
417
|
+
if (this.#delegatedList !== this.listTarget) this.#connectDelegatedEvents();
|
|
385
418
|
}
|
|
386
|
-
/** Releases delegation only when the removed target is its current owner. */
|
|
387
419
|
listTargetDisconnected(element) {
|
|
388
420
|
if (this.#delegatedList === element) this.#disconnectDelegatedEvents();
|
|
389
|
-
|
|
390
|
-
|
|
421
|
+
if (!this.#connected || !this.hasListTarget || this.listTarget === element) return;
|
|
422
|
+
if (this.#delegatedList !== this.listTarget) this.#connectDelegatedEvents();
|
|
391
423
|
for (const item of this.itemTargets) {
|
|
392
|
-
if (item
|
|
393
|
-
if (this.durationValue <= 0) {
|
|
394
|
-
this.#dismiss.disarm(item);
|
|
395
|
-
item.removeAttribute("data-paused");
|
|
396
|
-
} else {
|
|
397
|
-
this.#startTimer(item);
|
|
398
|
-
}
|
|
424
|
+
if (this.#outside.has(item)) this.#takeIn(item);
|
|
399
425
|
}
|
|
400
426
|
}
|
|
401
|
-
/**
|
|
402
|
-
* Holds the list to a changed `max`. Stimulus can also call this ahead of `connect()` —
|
|
403
|
-
* for an attribute that changed while the controller was away, and on every connect
|
|
404
|
-
* for an undeclared `max`, with its default; `connect()` decides then.
|
|
405
|
-
*/
|
|
406
427
|
maxValueChanged() {
|
|
407
428
|
if (!this.#connected) return;
|
|
408
|
-
this.#appliedMax = this
|
|
429
|
+
this.#appliedMax = this.#safeMax;
|
|
409
430
|
this.enforceMaxLimit();
|
|
410
431
|
}
|
|
411
|
-
/**
|
|
412
|
-
* Stimulus lifecycle callback triggered automatically when a new item target
|
|
413
|
-
* enters the DOM, from a client-side injection or a Turbo Stream append alike, and for
|
|
414
|
-
* every toast the list already holds ahead of `connect()`. An item already leaving,
|
|
415
|
-
* parented outside `list`, or already taken on (a move within the list) is skipped; any
|
|
416
|
-
* other is taken on with the holds it already has. On a connected controller the `max`
|
|
417
|
-
* limit is then applied, never to the arrival itself; the toasts reported ahead of
|
|
418
|
-
* `connect()` are left to it.
|
|
419
|
-
*/
|
|
420
432
|
itemTargetConnected(element) {
|
|
421
|
-
if (this.#
|
|
422
|
-
|
|
433
|
+
if (this.#takeIn(element) && this.#connected) this.#enforceMax([element]);
|
|
434
|
+
}
|
|
435
|
+
#takeIn(element) {
|
|
436
|
+
if (this.#taken.has(element) || element.dataset.state === "leaving") return false;
|
|
437
|
+
if (element.parentNode !== this.listTarget) {
|
|
438
|
+
this.#outside.add(element);
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
423
441
|
this.#taken.add(element);
|
|
442
|
+
this.#arrivals.add(element);
|
|
424
443
|
this.#readHolds(element);
|
|
425
444
|
this.#startTimer(element);
|
|
426
|
-
element
|
|
427
|
-
this.#cancelAnimation(element);
|
|
445
|
+
this.#setPhase(element, "entering");
|
|
428
446
|
const handle = window.requestAnimationFrame(() => {
|
|
429
447
|
this.#rafHandles.delete(element);
|
|
430
448
|
if (element.parentNode !== this.listTarget || element.dataset.state === "leaving") return;
|
|
431
|
-
element
|
|
449
|
+
this.#setPhase(element, "visible");
|
|
432
450
|
});
|
|
433
451
|
this.#rafHandles.set(element, handle);
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Lets go of a toast that left the list or stopped being an item: its timer, holds,
|
|
438
|
-
* `data-paused` and pending frame. After a move within the list the node is already
|
|
439
|
-
* back, still an item, when Stimulus reports it, so it keeps all of those, and only the
|
|
440
|
-
* holds the move may have ended are read again.
|
|
441
|
-
*/
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
442
454
|
itemTargetDisconnected(element) {
|
|
443
455
|
const moved = this.#isShown(element) && element.matches(targetSelector(this.identifier, "item"));
|
|
444
456
|
if (moved) {
|
|
@@ -446,26 +458,12 @@ var ToastController = class extends Controller {
|
|
|
446
458
|
return;
|
|
447
459
|
}
|
|
448
460
|
this.#taken.delete(element);
|
|
461
|
+
this.#phases.delete(element);
|
|
462
|
+
if (this.#connected) this.#arrivals.delete(element);
|
|
449
463
|
element.removeAttribute("data-paused");
|
|
450
464
|
this.#letGo(element);
|
|
451
465
|
this.#cancelAnimation(element);
|
|
452
466
|
}
|
|
453
|
-
/**
|
|
454
|
-
* Shows a new toast. Accepts its content from either a Stimulus action param
|
|
455
|
-
* (attribute-only trigger) or a programmatic `show` CustomEvent `detail`
|
|
456
|
-
* (remote / Turbo trigger); the action param wins when both are present.
|
|
457
|
-
*
|
|
458
|
-
* <button data-action="click->stimeo--toast#show"
|
|
459
|
-
* data-stimeo--toast-message-param="Saved"
|
|
460
|
-
* data-stimeo--toast-type-param="status">Show</button>
|
|
461
|
-
*
|
|
462
|
-
* element.dispatchEvent(new CustomEvent("show", { detail: { message, type } }))
|
|
463
|
-
*
|
|
464
|
-
* Clones the template slot, interpolates the message, and appends to the list.
|
|
465
|
-
* `message` is the wording key the rest of the library dispatches under, so a
|
|
466
|
-
* part that already carries its own wording — `stimeo--clipboard:copy`,
|
|
467
|
-
* `stimeo--auto-submit:done` — wires straight into this action.
|
|
468
|
-
*/
|
|
469
467
|
show(event) {
|
|
470
468
|
if (!this.hasTemplateTarget || !this.hasListTarget) return;
|
|
471
469
|
const message = this.#readField(event, "message");
|
|
@@ -484,11 +482,6 @@ var ToastController = class extends Controller {
|
|
|
484
482
|
this.listTarget.appendChild(item);
|
|
485
483
|
this.dispatch("show", { detail: { item } });
|
|
486
484
|
}
|
|
487
|
-
/**
|
|
488
|
-
* Reads a string field from a Stimulus action param or a CustomEvent `detail`,
|
|
489
|
-
* preferring the action param. Returns null unless a non-empty string is found,
|
|
490
|
-
* so untrusted runtime payloads cannot inject non-string values.
|
|
491
|
-
*/
|
|
492
485
|
#readField(event, key) {
|
|
493
486
|
const params = event.params;
|
|
494
487
|
const fromParams = params?.[key];
|
|
@@ -500,153 +493,100 @@ var ToastController = class extends Controller {
|
|
|
500
493
|
}
|
|
501
494
|
return null;
|
|
502
495
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const item = this.#
|
|
496
|
+
dismiss(input) {
|
|
497
|
+
const source = actionSource(input);
|
|
498
|
+
const item = this.#itemFromSource(source);
|
|
506
499
|
if (!item) return;
|
|
507
|
-
this.#removeWithTransition(item,
|
|
500
|
+
this.#removeWithTransition(item, source.reason);
|
|
508
501
|
}
|
|
509
|
-
/** Dismisses the focused toast when Escape is pressed. */
|
|
510
502
|
onKeydown(event) {
|
|
511
503
|
if (event.key === "Escape") {
|
|
512
504
|
if (event.defaultPrevented || event.isComposing) return;
|
|
513
|
-
const item = this.#
|
|
505
|
+
const item = this.#itemFromSource(actionSource(event));
|
|
514
506
|
if (!item) return;
|
|
515
507
|
event.preventDefault();
|
|
516
508
|
this.#removeWithTransition(item, "user");
|
|
517
509
|
}
|
|
518
510
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
* snapshotted on the first active reason, and only resumed once *every*
|
|
524
|
-
* reason has been released (see {@link resume}). This keeps a toast paused
|
|
525
|
-
* while it is still hovered *or* focused, per WCAG 2.2.1. The hold is recorded
|
|
526
|
-
* whether or not a timer runs, so the `max` limit passes over the toast either
|
|
527
|
-
* way; `data-paused` marks a timer that stopped, and only that.
|
|
528
|
-
*/
|
|
529
|
-
pause(event) {
|
|
530
|
-
const item = this.#itemFromEvent(event);
|
|
531
|
-
if (item && this.#dismiss.pause(item, this.#pauseReason(event))) {
|
|
511
|
+
pause(input) {
|
|
512
|
+
const source = actionSource(input);
|
|
513
|
+
const item = this.#itemFromSource(source);
|
|
514
|
+
if (item && this.#dismiss.pause(item, this.#pauseReason(source.event))) {
|
|
532
515
|
item.setAttribute("data-paused", "true");
|
|
533
516
|
}
|
|
534
517
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
* moving into: its own hold only arrives after this event.
|
|
539
|
-
*/
|
|
540
|
-
resume(event) {
|
|
541
|
-
const item = this.#itemFromEvent(event);
|
|
518
|
+
resume(input) {
|
|
519
|
+
const source = actionSource(input);
|
|
520
|
+
const item = this.#itemFromSource(source);
|
|
542
521
|
if (item) {
|
|
543
522
|
this.#release(
|
|
544
523
|
item,
|
|
545
|
-
this.#pauseReason(event),
|
|
546
|
-
ownerOf(this.itemTargets, event
|
|
524
|
+
this.#pauseReason(source.event),
|
|
525
|
+
ownerOf(this.itemTargets, source.event?.relatedTarget)
|
|
547
526
|
);
|
|
548
527
|
}
|
|
549
528
|
}
|
|
550
|
-
/** Releases one reason on `item`; releasing the last one applies the limit again. */
|
|
551
529
|
#release(item, reason, spare) {
|
|
552
530
|
const held = this.#dismiss.isHeld(item);
|
|
553
531
|
if (this.#dismiss.resume(item, reason)) item.removeAttribute("data-paused");
|
|
554
532
|
if (held && !this.#dismiss.isHeld(item)) this.#limitLater(spare);
|
|
555
533
|
}
|
|
556
|
-
/**
|
|
557
|
-
* Applies the limit again once the current event is over, sparing `spare`. A hold can
|
|
558
|
-
* end in a `focusout` the engine fires while it is itself taking the node out for a
|
|
559
|
-
* script's move or removal, and taking the toast away inside that event would pull
|
|
560
|
-
* the node from under the caller's own call.
|
|
561
|
-
*/
|
|
562
534
|
#limitLater(spare) {
|
|
563
535
|
if (spare) this.#spared.push(spare);
|
|
564
536
|
this.#reapply.schedule();
|
|
565
537
|
}
|
|
566
|
-
|
|
538
|
+
#setPhase(item, phase) {
|
|
539
|
+
this.#phases.set(item, phase);
|
|
540
|
+
item.setAttribute("data-state", phase);
|
|
541
|
+
}
|
|
567
542
|
#reapplyLimit() {
|
|
543
|
+
for (const item of this.itemTargets) {
|
|
544
|
+
if (!this.#taken.has(item) || !this.hasListTarget || item.parentNode !== this.listTarget)
|
|
545
|
+
continue;
|
|
546
|
+
const phase = this.#phases.get(item);
|
|
547
|
+
if (item.dataset.state === "leaving") this.#phases.set(item, "leaving");
|
|
548
|
+
else if (phase) item.setAttribute("data-state", phase);
|
|
549
|
+
if (this.#dismiss.isHeld(item) && this.#dismiss.tracks(item))
|
|
550
|
+
item.setAttribute("data-paused", "true");
|
|
551
|
+
else item.removeAttribute("data-paused");
|
|
552
|
+
}
|
|
568
553
|
const spared = this.#spared;
|
|
569
554
|
this.#spared = [];
|
|
570
555
|
this.#enforceMax(spared);
|
|
571
556
|
}
|
|
572
|
-
/**
|
|
573
|
-
* Reads the holds of a toast that moved within the list. Focus is read at once: an
|
|
574
|
-
* ordinary move ends it with a `focusout` and `moveBefore` keeps it. Where the pointer
|
|
575
|
-
* is, nothing says until it moves again: no `mouseout` reaches a moved node, and
|
|
576
|
-
* `:hover` can answer from before the move. A toast still held waits for that movement.
|
|
577
|
-
*/
|
|
578
557
|
#afterMove(item) {
|
|
579
558
|
if (!item.contains(document.activeElement)) this.#release(item, "focus", null);
|
|
580
|
-
if (this.#dismiss.isHeld(item)) this.#
|
|
559
|
+
if (this.#dismiss.isHeld(item)) this.#pointer.wait(item);
|
|
581
560
|
}
|
|
582
|
-
/**
|
|
583
|
-
* Takes the holds a toast already has as it is taken on: focus inside it, and the pointer
|
|
584
|
-
* over it by `:hover`. That answer can date from before the toast came here, so a hover
|
|
585
|
-
* hold taken on it waits for the next pointer movement to be confirmed.
|
|
586
|
-
*/
|
|
587
561
|
#readHolds(item) {
|
|
588
562
|
if (item.contains(document.activeElement)) this.#dismiss.pause(item, "focus");
|
|
589
563
|
if (!item.matches(":hover")) return;
|
|
590
564
|
this.#dismiss.pause(item, "hover");
|
|
591
|
-
this.#
|
|
565
|
+
this.#pointer.wait(item);
|
|
592
566
|
}
|
|
593
|
-
/** Leaves `item`'s hover hold to be confirmed or let go on the next pointer movement. */
|
|
594
|
-
#awaitPointer(item) {
|
|
595
|
-
this.#awaitingPointer.add(item);
|
|
596
|
-
this.#pointer.add(document, "pointermove", this.#onPointerMove, {
|
|
597
|
-
capture: true,
|
|
598
|
-
passive: true
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
/**
|
|
602
|
-
* Reads `:hover` once the pointer has moved, when the answer is current, and lets go of
|
|
603
|
-
* the hover hold of every waiting toast the pointer is not over. Listens once.
|
|
604
|
-
*/
|
|
605
|
-
#onPointerMove = () => {
|
|
606
|
-
this.#pointer.dispose();
|
|
607
|
-
const waiting = [...this.#awaitingPointer];
|
|
608
|
-
this.#awaitingPointer.clear();
|
|
609
|
-
for (const item of waiting) {
|
|
610
|
-
if (!item.matches(":hover")) this.#release(item, "hover", null);
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
/**
|
|
614
|
-
* Drops a toast's timer and holds as it leaves, and stops waiting on the pointer for it.
|
|
615
|
-
* A toast something still held counts as released: the limit is applied again.
|
|
616
|
-
*/
|
|
617
567
|
#letGo(item) {
|
|
618
568
|
if (this.#dismiss.isHeld(item)) this.#limitLater(null);
|
|
619
569
|
this.#dismiss.clear(item);
|
|
620
|
-
this.#
|
|
621
|
-
if (this.#awaitingPointer.size === 0) this.#pointer.dispose();
|
|
570
|
+
this.#pointer.cancel(item);
|
|
622
571
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if (
|
|
572
|
+
#itemFromSource(source) {
|
|
573
|
+
const target = source.origin ?? source.host;
|
|
574
|
+
if (!target || !this.hasListTarget) return null;
|
|
575
|
+
if (target.closest(`[data-controller~="${this.identifier}"]`) !== this.element) return null;
|
|
627
576
|
const item = target.closest(targetSelector(this.identifier, "item"));
|
|
628
|
-
return item && this.listTarget.contains(item) ? item : null;
|
|
577
|
+
return item && this.itemTargets.includes(item) && this.listTarget.contains(item) ? item : null;
|
|
629
578
|
}
|
|
630
|
-
/** Classifies a pause/resume event as a hover or focus reason. */
|
|
631
579
|
#pauseReason(event) {
|
|
632
|
-
return event
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Arms a toast's auto-dismiss; a non-positive `duration` means it never expires.
|
|
636
|
-
* On a toast hover or focus already holds, the timer waits for the release.
|
|
637
|
-
* `data-paused` is written from that state, so a mark the toast carried in with it,
|
|
638
|
-
* from a restored page, does not outlive it.
|
|
639
|
-
*
|
|
640
|
-
* @stimeoRuntimeOnly `duration` is the delay of the one dismissal timer this call arms.
|
|
641
|
-
*/
|
|
580
|
+
return event?.type === "focusin" || event?.type === "focusout" ? "focus" : "hover";
|
|
581
|
+
}
|
|
642
582
|
#startTimer(element) {
|
|
643
583
|
if (element.dataset.state === "leaving" || element.parentNode !== this.listTarget) return;
|
|
644
|
-
const armed = this
|
|
584
|
+
const armed = this.#safeDuration > 0;
|
|
645
585
|
if (armed) {
|
|
646
586
|
this.#dismiss.set(
|
|
647
587
|
element,
|
|
648
588
|
() => this.#removeWithTransition(element, "timeout"),
|
|
649
|
-
this
|
|
589
|
+
this.#safeDuration
|
|
650
590
|
);
|
|
651
591
|
}
|
|
652
592
|
if (armed && this.#dismiss.isHeld(element)) element.setAttribute("data-paused", "true");
|
|
@@ -656,7 +596,7 @@ var ToastController = class extends Controller {
|
|
|
656
596
|
if (element.dataset.state === "leaving" || element.parentNode !== this.listTarget) return;
|
|
657
597
|
this.#letGo(element);
|
|
658
598
|
this.#cancelAnimation(element);
|
|
659
|
-
element
|
|
599
|
+
this.#setPhase(element, "leaving");
|
|
660
600
|
const finalize = () => {
|
|
661
601
|
if (element.parentNode !== this.listTarget) return;
|
|
662
602
|
this.listTarget.removeChild(element);
|
|
@@ -669,65 +609,36 @@ var ToastController = class extends Controller {
|
|
|
669
609
|
finalize();
|
|
670
610
|
}
|
|
671
611
|
}
|
|
672
|
-
/**
|
|
673
|
-
* Removes the oldest toasts while the list shows more than `maxValue`, passing
|
|
674
|
-
* over every toast hover or focus holds.
|
|
675
|
-
*
|
|
676
|
-
* Public (not `#private`) as a deterministic seam: enforcement normally runs
|
|
677
|
-
* as each toast is taken on in `itemTargetConnected`, a Stimulus callback delivered
|
|
678
|
-
* through a MutationObserver, which a DOM-only environment does not reliably fire —
|
|
679
|
-
* so it can also be invoked directly. It counts only the toasts taken on. It is not a
|
|
680
|
-
* user-wired action.
|
|
681
|
-
*/
|
|
682
612
|
enforceMaxLimit() {
|
|
683
613
|
this.#enforceMax();
|
|
684
614
|
}
|
|
685
|
-
/**
|
|
686
|
-
* Removes the oldest toasts with reason `limit` while the list shows more than `max`
|
|
687
|
-
* (`0` or less, or not a finite number, means no limit), passing over every toast hover
|
|
688
|
-
* or focus holds and those in `spared` — the toast just taken on, or the ones the
|
|
689
|
-
* pointer or focus moved into. Only toasts taken on count or go, so one Stimulus has
|
|
690
|
-
* yet to report is neither; a toast already leaving does not count. With nothing else
|
|
691
|
-
* left to go the list stays over the limit. The count is read again on every step,
|
|
692
|
-
* because a `dismiss` listener may already have changed it.
|
|
693
|
-
*
|
|
694
|
-
* @stimeoRenderRoot
|
|
695
|
-
*/
|
|
696
615
|
#enforceMax(spared = []) {
|
|
697
|
-
if (
|
|
698
|
-
for (const item of this
|
|
699
|
-
if (this.#shownCount() <= this
|
|
616
|
+
if (this.#safeMax <= 0) return;
|
|
617
|
+
for (const item of [...this.#arrivals]) {
|
|
618
|
+
if (this.#shownCount() <= this.#safeMax) return;
|
|
700
619
|
if (this.#isShown(item) && !spared.includes(item) && !this.#dismiss.isHeld(item)) {
|
|
701
620
|
this.#removeWithTransition(item, "limit");
|
|
702
621
|
}
|
|
703
622
|
}
|
|
704
623
|
}
|
|
705
|
-
/** How many toasts the list shows: the ones taken on that are still shown. */
|
|
706
624
|
#shownCount() {
|
|
707
625
|
return this.itemTargets.filter((item) => this.#isShown(item)).length;
|
|
708
626
|
}
|
|
709
|
-
/** Whether the list shows `item`: a toast taken on, a child of `list`, not leaving. */
|
|
710
627
|
#isShown(item) {
|
|
711
628
|
return this.#taken.has(item) && this.hasListTarget && item.parentNode === this.listTarget && item.dataset.state !== "leaving";
|
|
712
629
|
}
|
|
713
|
-
/** Wires stable-container delegation so newly appended items work immediately. */
|
|
714
630
|
#connectDelegatedEvents(list = this.hasListTarget ? this.listTarget : null) {
|
|
715
631
|
this.#disconnectDelegatedEvents();
|
|
716
632
|
if (!list) return;
|
|
717
633
|
this.#delegatedList = list;
|
|
718
634
|
for (const type of DELEGATED_EVENTS) {
|
|
719
|
-
this.#
|
|
635
|
+
this.#listListeners.add(list, type, this.#onListEvent);
|
|
720
636
|
}
|
|
721
637
|
}
|
|
722
|
-
/** Releases every delegated listener from the exact list that owns it. */
|
|
723
638
|
#disconnectDelegatedEvents() {
|
|
724
|
-
|
|
725
|
-
for (const type of DELEGATED_EVENTS) {
|
|
726
|
-
this.#delegatedList.removeEventListener(type, this.#onListEvent);
|
|
727
|
-
}
|
|
639
|
+
this.#listListeners.dispose();
|
|
728
640
|
this.#delegatedList = null;
|
|
729
641
|
}
|
|
730
|
-
/** Routes every delegated item interaction from the stable list. */
|
|
731
642
|
#onListEvent = (event) => {
|
|
732
643
|
if (event.type === "click") {
|
|
733
644
|
const target = event.target instanceof Element ? event.target : null;
|
|
@@ -740,22 +651,31 @@ var ToastController = class extends Controller {
|
|
|
740
651
|
else this.resume(event);
|
|
741
652
|
}
|
|
742
653
|
};
|
|
743
|
-
/** Whether a bubbling focus/pointer event enters or leaves a toast boundary. */
|
|
744
654
|
#crossesItemBoundary(event) {
|
|
745
|
-
const item = this.#
|
|
655
|
+
const item = this.#itemFromSource(actionSource(event));
|
|
746
656
|
if (!item) return false;
|
|
747
657
|
const related = "relatedTarget" in event ? event.relatedTarget : null;
|
|
748
658
|
return !(related instanceof Node && item.contains(related));
|
|
749
659
|
}
|
|
750
|
-
/** Cancels the pending entering-to-visible frame owned by one item. */
|
|
751
660
|
#cancelAnimation(element) {
|
|
752
661
|
const handle = this.#rafHandles.get(element);
|
|
753
662
|
if (handle === void 0) return;
|
|
754
663
|
window.cancelAnimationFrame(handle);
|
|
755
664
|
this.#rafHandles.delete(element);
|
|
756
665
|
}
|
|
666
|
+
get #safeDuration() {
|
|
667
|
+
return this.#numbers.read(
|
|
668
|
+
this,
|
|
669
|
+
"duration",
|
|
670
|
+
this.durationValue,
|
|
671
|
+
_ToastController.values.duration.default,
|
|
672
|
+
_ToastController.valueConstraints.duration
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
get #safeMax() {
|
|
676
|
+
return this.#numbers.read(this, "max", this.maxValue, 0, _ToastController.valueConstraints.max);
|
|
677
|
+
}
|
|
757
678
|
};
|
|
758
679
|
|
|
759
680
|
export { ToastController };
|
|
760
|
-
//# sourceMappingURL=toast_controller.js.map
|
|
761
681
|
//# sourceMappingURL=toast_controller.js.map
|