stimeo-ui 0.15.0 → 0.16.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 +178 -0
- package/README.md +120 -0
- package/dist/cable/index.js +63 -15
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +30 -3
- package/dist/controllers/accordion_controller.js +98 -9
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +8 -7
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +3 -2
- package/dist/controllers/announcer_controller.js +8 -1
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +43 -2
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +1 -1
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +9 -8
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +4 -4
- package/dist/controllers/bulk_select_controller.js +7 -6
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +85 -23
- package/dist/controllers/calendar_controller.js +323 -123
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +67 -22
- package/dist/controllers/carousel_controller.js +263 -38
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +1 -1
- package/dist/controllers/character_counter_controller.js +40 -2
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.js +54 -10
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +28 -2
- package/dist/controllers/clipboard_controller.js +63 -5
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +25 -1
- package/dist/controllers/collapsible_controller.js +99 -14
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +29 -8
- package/dist/controllers/color_picker_controller.js +80 -34
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +10 -1
- package/dist/controllers/combobox_controller.js +106 -16
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +3 -3
- package/dist/controllers/command_palette_controller.js +35 -3
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.js +56 -11
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js +3 -0
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +6 -0
- package/dist/controllers/context_menu_controller.js +32 -12
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +30 -1
- package/dist/controllers/countdown_controller.js +129 -26
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +74 -15
- package/dist/controllers/currency_input_controller.js +205 -58
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +63 -18
- package/dist/controllers/data_grid_controller.js +195 -29
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +34 -6
- package/dist/controllers/date_range_picker_controller.js +139 -27
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +10 -3
- package/dist/controllers/dialog_controller.js +35 -8
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.js +22 -4
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +2 -2
- package/dist/controllers/dirty_form_controller.js +14 -1
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js +1 -0
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +18 -10
- package/dist/controllers/drawer_controller.js +54 -19
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +9 -3
- package/dist/controllers/dropdown_controller.js +36 -9
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +34 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.js +144 -51
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +10 -4
- package/dist/controllers/filter_controller.js +20 -6
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +26 -6
- package/dist/controllers/flash_controller.js +305 -46
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.js +1 -0
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +7 -5
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +19 -13
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.js +16 -2
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js +19 -2
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +10 -2
- package/dist/controllers/hover_card_controller.js +40 -14
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +16 -3
- package/dist/controllers/idle_controller.js +90 -5
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +5 -2
- package/dist/controllers/input_mask_controller.js +65 -9
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +3 -0
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +11 -2
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +50 -8
- package/dist/controllers/listbox_controller.js +203 -45
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.js +0 -2
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +9 -3
- package/dist/controllers/menu_controller.js +45 -16
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +11 -0
- package/dist/controllers/menubar_controller.js +58 -24
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js +9 -5
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +18 -3
- package/dist/controllers/multi_select_controller.js +278 -104
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +11 -0
- package/dist/controllers/navigation_menu_controller.js +48 -15
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.js +37 -8
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.js +9 -1
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +36 -8
- package/dist/controllers/number_input_controller.js +124 -21
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +14 -1
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +22 -7
- package/dist/controllers/otp_controller.js +198 -55
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +8 -12
- package/dist/controllers/overflow_indicator_controller.js +84 -13
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +26 -6
- package/dist/controllers/overflow_menu_controller.js +112 -41
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +19 -11
- package/dist/controllers/pagination_controller.js +74 -28
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +15 -1
- package/dist/controllers/password_reveal_controller.js +59 -2
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +30 -8
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.js +131 -52
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +9 -3
- package/dist/controllers/popover_controller.js +45 -11
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +1 -1
- package/dist/controllers/portal_controller.js +6 -2
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.js +16 -1
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.js +8 -4
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +6 -4
- package/dist/controllers/radio_group_controller.js +42 -17
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +49 -1
- package/dist/controllers/range_slider_controller.js +88 -42
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +14 -3
- package/dist/controllers/rating_controller.js +39 -15
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +24 -2
- package/dist/controllers/read_more_controller.js +69 -2
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +16 -2
- package/dist/controllers/resizable_controller.js +82 -22
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +5 -1
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +0 -1
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +3 -2
- package/dist/controllers/scrollspy_controller.js +6 -0
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +41 -14
- package/dist/controllers/separator_controller.js +66 -37
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +20 -3
- package/dist/controllers/sidebar_controller.js +77 -18
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +6 -1
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +45 -7
- package/dist/controllers/slider_controller.js +82 -47
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.js +11 -1
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +17 -2
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +10 -2
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +19 -17
- package/dist/controllers/step_indicator_controller.js +18 -17
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +34 -9
- package/dist/controllers/stepper_controller.js +101 -19
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +19 -0
- package/dist/controllers/stick_to_bottom_controller.js +75 -5
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +3 -2
- package/dist/controllers/submit_once_controller.js +16 -3
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +29 -7
- package/dist/controllers/switch_controller.js +101 -10
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +12 -0
- package/dist/controllers/tabs_controller.js +21 -2
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +15 -3
- package/dist/controllers/tags_input_controller.js +209 -59
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +20 -3
- package/dist/controllers/theme_controller.js +64 -14
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.js +23 -8
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +52 -15
- package/dist/controllers/toast_controller.js +337 -54
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +49 -7
- package/dist/controllers/toggle_group_controller.js +159 -23
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +32 -0
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +8 -0
- package/dist/controllers/tooltip_controller.js +39 -13
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.js +4 -0
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +39 -8
- package/dist/controllers/tree_view_controller.js +169 -16
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4588 -1535
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +72 -6
- package/dist/inspector/cli.js +262 -51
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +309 -51
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +40 -40
- package/dist/inspector/manifest.json +525 -59
- package/dist/positioning/index.js +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -42,6 +42,15 @@ var BeforeCacheReset = class _BeforeCacheReset {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
// src/utils/event_owner.ts
|
|
46
|
+
function ownerIndex(candidates, node) {
|
|
47
|
+
if (!(node instanceof Node)) return -1;
|
|
48
|
+
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
49
|
+
}
|
|
50
|
+
function ownerOf(candidates, node) {
|
|
51
|
+
return candidates[ownerIndex(candidates, node)] ?? null;
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
// src/utils/safe_timeout.ts
|
|
46
55
|
var TimerRegistry = class {
|
|
47
56
|
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
@@ -138,6 +147,63 @@ var KeyedTimers = class {
|
|
|
138
147
|
}
|
|
139
148
|
};
|
|
140
149
|
|
|
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
|
+
var MicrotaskCoalescer = class {
|
|
176
|
+
#run;
|
|
177
|
+
#queued = false;
|
|
178
|
+
#active = false;
|
|
179
|
+
#generation = 0;
|
|
180
|
+
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
181
|
+
constructor(run) {
|
|
182
|
+
this.#run = run;
|
|
183
|
+
}
|
|
184
|
+
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
185
|
+
activate() {
|
|
186
|
+
this.#active = true;
|
|
187
|
+
}
|
|
188
|
+
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
189
|
+
cancel() {
|
|
190
|
+
this.#active = false;
|
|
191
|
+
this.#queued = false;
|
|
192
|
+
this.#generation += 1;
|
|
193
|
+
}
|
|
194
|
+
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
195
|
+
schedule() {
|
|
196
|
+
if (!this.#active || this.#queued) return;
|
|
197
|
+
this.#queued = true;
|
|
198
|
+
const generation = this.#generation;
|
|
199
|
+
queueMicrotask(() => {
|
|
200
|
+
if (generation !== this.#generation || !this.#queued || !this.#active) return;
|
|
201
|
+
this.#queued = false;
|
|
202
|
+
this.#run();
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
141
207
|
// src/utils/pausable_timers.ts
|
|
142
208
|
var PausableTimers = class {
|
|
143
209
|
#timers = new KeyedTimers();
|
|
@@ -151,16 +217,20 @@ var PausableTimers = class {
|
|
|
151
217
|
const reasons = this.#entries.get(key)?.reasons ?? /* @__PURE__ */ new Set();
|
|
152
218
|
const entry = { callback, startedAt: 0, remaining: delay, reasons };
|
|
153
219
|
this.#entries.set(key, entry);
|
|
154
|
-
if (reasons.size === 0) this.#arm(key, entry);
|
|
220
|
+
if (reasons.size === 0) this.#arm(key, entry, callback);
|
|
155
221
|
}
|
|
156
222
|
/**
|
|
157
223
|
* Holds `key` for `reason` and banks the time left, with a floor of one
|
|
158
|
-
* millisecond.
|
|
159
|
-
* timer, so the caller can write its
|
|
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.
|
|
160
227
|
*/
|
|
161
228
|
pause(key, reason) {
|
|
162
|
-
|
|
163
|
-
if (entry === void 0)
|
|
229
|
+
let entry = this.#entries.get(key);
|
|
230
|
+
if (entry === void 0) {
|
|
231
|
+
entry = { callback: null, startedAt: 0, remaining: 0, reasons: /* @__PURE__ */ new Set() };
|
|
232
|
+
this.#entries.set(key, entry);
|
|
233
|
+
}
|
|
164
234
|
entry.reasons.add(reason);
|
|
165
235
|
if (!this.#timers.has(key)) return false;
|
|
166
236
|
this.#timers.clear(key);
|
|
@@ -169,20 +239,40 @@ var PausableTimers = class {
|
|
|
169
239
|
}
|
|
170
240
|
/**
|
|
171
241
|
* Releases `reason` on `key`, arming the banked time again once no reason is
|
|
172
|
-
* left
|
|
242
|
+
* left; a key released with no timer is forgotten. Reports whether this call
|
|
243
|
+
* is the one that started the timer again.
|
|
173
244
|
*/
|
|
174
245
|
resume(key, reason) {
|
|
175
246
|
const entry = this.#entries.get(key);
|
|
176
247
|
if (entry === void 0) return false;
|
|
177
248
|
entry.reasons.delete(reason);
|
|
178
249
|
if (entry.reasons.size > 0 || this.#timers.has(key)) return false;
|
|
179
|
-
|
|
250
|
+
const { callback } = entry;
|
|
251
|
+
if (callback === null) {
|
|
252
|
+
this.#entries.delete(key);
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
this.#arm(key, entry, callback);
|
|
180
256
|
return true;
|
|
181
257
|
}
|
|
258
|
+
/** Whether any reason holds `key`, with or without a timer. */
|
|
259
|
+
isHeld(key) {
|
|
260
|
+
return (this.#entries.get(key)?.reasons.size ?? 0) > 0;
|
|
261
|
+
}
|
|
182
262
|
/** Whether `key` is armed or held — that is, whether this registry drives it at all. */
|
|
183
263
|
tracks(key) {
|
|
184
264
|
return this.#entries.has(key);
|
|
185
265
|
}
|
|
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
|
+
}
|
|
186
276
|
/** Cancels `key`'s timer and drops its hold. */
|
|
187
277
|
clear(key) {
|
|
188
278
|
this.#timers.clear(key);
|
|
@@ -196,20 +286,25 @@ var PausableTimers = class {
|
|
|
196
286
|
this.#timers.clearAll();
|
|
197
287
|
this.#entries.clear();
|
|
198
288
|
}
|
|
199
|
-
/** Starts `entry`'s banked time running for `key`, and drops it as
|
|
200
|
-
#arm(key, entry) {
|
|
289
|
+
/** Starts `entry`'s banked time running for `key`, and drops it as `callback` fires. */
|
|
290
|
+
#arm(key, entry, callback) {
|
|
201
291
|
entry.startedAt = Date.now();
|
|
202
292
|
this.#timers.set(
|
|
203
293
|
key,
|
|
204
294
|
() => {
|
|
205
295
|
this.#entries.delete(key);
|
|
206
|
-
|
|
296
|
+
callback();
|
|
207
297
|
},
|
|
208
298
|
entry.remaining
|
|
209
299
|
);
|
|
210
300
|
}
|
|
211
301
|
};
|
|
212
302
|
|
|
303
|
+
// src/utils/target_selector.ts
|
|
304
|
+
function targetSelector(identifier, name) {
|
|
305
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
306
|
+
}
|
|
307
|
+
|
|
213
308
|
// src/utils/transition_completion.ts
|
|
214
309
|
function timeMs(value) {
|
|
215
310
|
const trimmed = value.trim();
|
|
@@ -246,8 +341,12 @@ function maxTransitionTotalMs(style) {
|
|
|
246
341
|
|
|
247
342
|
// src/controllers/flash_controller.ts
|
|
248
343
|
var ASSERTIVE_TYPES = /* @__PURE__ */ new Set(["alert", "error"]);
|
|
249
|
-
var
|
|
344
|
+
var MESSAGE_PART = "message";
|
|
250
345
|
var FlashController = class extends Controller {
|
|
346
|
+
/** Selects the message parts, in the namespace this controller is registered under. */
|
|
347
|
+
get #messageSelector() {
|
|
348
|
+
return targetSelector(this.identifier, MESSAGE_PART);
|
|
349
|
+
}
|
|
251
350
|
static targets = ["region", "message"];
|
|
252
351
|
static values = {
|
|
253
352
|
duration: { type: Number, default: 5e3 },
|
|
@@ -258,11 +357,33 @@ var FlashController = class extends Controller {
|
|
|
258
357
|
static events = ["show", "dismiss", "reconcile"];
|
|
259
358
|
/** Removal timers for the leaving transition; the auto-dismiss ones live below. */
|
|
260
359
|
#timers = new SafeTimeout();
|
|
261
|
-
/**
|
|
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
|
+
*/
|
|
262
364
|
#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
|
+
#fixedDismiss = new KeyedTimers();
|
|
370
|
+
/** Applies the cap again once the event that released a hold has run its course. */
|
|
371
|
+
#reapply = new MicrotaskCoalescer(() => this.#reapplyCap());
|
|
372
|
+
/** Messages the pointer or focus moved into since the cap was last applied again. */
|
|
373
|
+
#spared = [];
|
|
374
|
+
/** The next pointer movement, listened for while a hover hold waits on it. */
|
|
375
|
+
#pointer = new ListenerSet();
|
|
376
|
+
/** Messages whose hover hold waits for the next pointer movement to be confirmed. */
|
|
377
|
+
#awaitingPointer = /* @__PURE__ */ new Set();
|
|
263
378
|
#observer = null;
|
|
264
379
|
/** Whether the controller is between `connect()` and `disconnect()`. */
|
|
265
380
|
#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
|
+
*/
|
|
386
|
+
#appliedMax = null;
|
|
266
387
|
/** Messages already processed, in insertion order, to enforce `max` and avoid double work. */
|
|
267
388
|
#order = [];
|
|
268
389
|
/**
|
|
@@ -274,12 +395,28 @@ var FlashController = class extends Controller {
|
|
|
274
395
|
#leaving = /* @__PURE__ */ new Set();
|
|
275
396
|
#beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
|
|
276
397
|
#onEnter = (event) => this.#pause(event.currentTarget, event.type === "focusin" ? "focus" : "hover");
|
|
277
|
-
|
|
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
|
+
#onLeave = (event) => {
|
|
405
|
+
const message = event.currentTarget;
|
|
406
|
+
const next = ownerOf(this.#order, event.relatedTarget);
|
|
407
|
+
if (next === message) return;
|
|
408
|
+
this.#resume(message, event.type === "focusout" ? "focus" : "hover", next);
|
|
409
|
+
};
|
|
278
410
|
connect() {
|
|
279
411
|
this.#connected = true;
|
|
412
|
+
this.#reapply.activate();
|
|
280
413
|
for (const message of this.messageTargets) {
|
|
281
414
|
if (this.#owns(message)) this.#process(message, true);
|
|
282
415
|
}
|
|
416
|
+
if (!Object.is(this.#appliedMax, this.maxValue)) {
|
|
417
|
+
this.#appliedMax = this.maxValue;
|
|
418
|
+
this.#enforceMax();
|
|
419
|
+
}
|
|
283
420
|
this.#syncObservation();
|
|
284
421
|
this.#beforeCache.activate();
|
|
285
422
|
}
|
|
@@ -289,6 +426,11 @@ var FlashController = class extends Controller {
|
|
|
289
426
|
this.#stopObserving();
|
|
290
427
|
this.#timers.clearAll();
|
|
291
428
|
this.#dismiss.clearAll();
|
|
429
|
+
this.#fixedDismiss.clearAll();
|
|
430
|
+
this.#reapply.cancel();
|
|
431
|
+
this.#spared = [];
|
|
432
|
+
this.#pointer.dispose();
|
|
433
|
+
this.#awaitingPointer.clear();
|
|
292
434
|
for (const message of this.#order) this.#unbindPause(message);
|
|
293
435
|
this.#order.length = 0;
|
|
294
436
|
this.#leaving.clear();
|
|
@@ -319,6 +461,19 @@ var FlashController = class extends Controller {
|
|
|
319
461
|
regionTargetDisconnected() {
|
|
320
462
|
this.#resync();
|
|
321
463
|
}
|
|
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
|
+
maxValueChanged() {
|
|
473
|
+
if (!this.#connected) return;
|
|
474
|
+
this.#appliedMax = this.maxValue;
|
|
475
|
+
this.#enforceMax();
|
|
476
|
+
}
|
|
322
477
|
/**
|
|
323
478
|
* Whether this controller owns `message`. Ownership is the current `region`'s
|
|
324
479
|
* subtree: a message target anywhere else in the controller's scope is the
|
|
@@ -337,12 +492,16 @@ var FlashController = class extends Controller {
|
|
|
337
492
|
* scheduled removal are cancelled. A move *within* the region keeps all of them —
|
|
338
493
|
* which is why the element must still be a message to be treated as one: ownership
|
|
339
494
|
* alone reads an in-place attribute rewrite as a move, and a node outside the target
|
|
340
|
-
* set belongs to the consumer, so nothing here may dismiss it.
|
|
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.
|
|
341
497
|
*/
|
|
342
498
|
messageTargetDisconnected(message) {
|
|
343
499
|
if (!this.#connected) return;
|
|
344
|
-
const moved = this.#owns(message) && message.matches(
|
|
345
|
-
if (moved)
|
|
500
|
+
const moved = this.#owns(message) && message.matches(this.#messageSelector);
|
|
501
|
+
if (moved) {
|
|
502
|
+
this.#afterMove(message);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
346
505
|
this.#forget(message);
|
|
347
506
|
this.#leaving.delete(message);
|
|
348
507
|
}
|
|
@@ -376,13 +535,11 @@ var FlashController = class extends Controller {
|
|
|
376
535
|
this.#observer = null;
|
|
377
536
|
}
|
|
378
537
|
/**
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
* even if `pauseOnHover` were ever toggled during a message's life.
|
|
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).
|
|
383
541
|
*/
|
|
384
542
|
#bindPause(message) {
|
|
385
|
-
if (!this.pauseOnHoverValue) return;
|
|
386
543
|
message.addEventListener("mouseenter", this.#onEnter);
|
|
387
544
|
message.addEventListener("mouseleave", this.#onLeave);
|
|
388
545
|
message.addEventListener("focusin", this.#onEnter);
|
|
@@ -397,7 +554,7 @@ var FlashController = class extends Controller {
|
|
|
397
554
|
/** Dismisses the flash whose close control fired the event. */
|
|
398
555
|
dismiss(event) {
|
|
399
556
|
const target = event.currentTarget || event.target;
|
|
400
|
-
const message = target?.closest(
|
|
557
|
+
const message = target?.closest(this.#messageSelector);
|
|
401
558
|
if (message) this.#beginDismiss(message, "user");
|
|
402
559
|
}
|
|
403
560
|
/** Processes messages added after connect (Turbo Stream); their own role announces them. */
|
|
@@ -405,20 +562,25 @@ var FlashController = class extends Controller {
|
|
|
405
562
|
for (const mutation of mutations) {
|
|
406
563
|
for (const node of mutation.addedNodes) {
|
|
407
564
|
if (!(node instanceof HTMLElement)) continue;
|
|
408
|
-
if (node.matches(
|
|
409
|
-
for (const message of node.querySelectorAll(
|
|
565
|
+
if (node.matches(this.#messageSelector)) this.#process(node, false);
|
|
566
|
+
for (const message of node.querySelectorAll(this.#messageSelector)) {
|
|
410
567
|
this.#process(message, false);
|
|
411
568
|
}
|
|
412
569
|
}
|
|
413
570
|
}
|
|
414
571
|
}
|
|
415
572
|
/**
|
|
416
|
-
* Applies role/state, wires pause listeners,
|
|
417
|
-
*
|
|
418
|
-
*
|
|
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.
|
|
419
579
|
*/
|
|
420
|
-
#process(message,
|
|
421
|
-
if (this.#order.includes(message) || this.#leaving.has(message))
|
|
580
|
+
#process(message, atConnect) {
|
|
581
|
+
if (this.#order.includes(message) || this.#leaving.has(message) || !this.#owns(message)) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
422
584
|
const type = message.getAttribute("data-flash-type") ?? "";
|
|
423
585
|
const assertive = ASSERTIVE_TYPES.has(type);
|
|
424
586
|
if (!message.hasAttribute("role")) {
|
|
@@ -427,44 +589,141 @@ var FlashController = class extends Controller {
|
|
|
427
589
|
message.setAttribute("data-flash-state", "visible");
|
|
428
590
|
this.#order.push(message);
|
|
429
591
|
this.#bindPause(message);
|
|
592
|
+
this.#readHolds(message);
|
|
430
593
|
const text = message.textContent?.trim() ?? "";
|
|
431
594
|
this.dispatch("show", { target: message, detail: { type, message: text } });
|
|
432
|
-
if (
|
|
595
|
+
if (atConnect) announce(text, { assertive });
|
|
433
596
|
this.#startTimer(message);
|
|
434
|
-
this.#enforceMax();
|
|
597
|
+
if (!atConnect) this.#enforceMax([message]);
|
|
435
598
|
}
|
|
436
|
-
/**
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
+
#enforceMax(spared = []) {
|
|
612
|
+
if (!Number.isFinite(this.maxValue) || this.maxValue <= 0) return;
|
|
613
|
+
for (const message of [...this.#order]) {
|
|
614
|
+
if (this.#shownCount() <= this.maxValue) return;
|
|
615
|
+
if (this.#owns(message) && !spared.includes(message) && !this.#dismiss.isHeld(message)) {
|
|
616
|
+
this.#beginDismiss(message, "limit");
|
|
617
|
+
}
|
|
443
618
|
}
|
|
444
619
|
}
|
|
445
|
-
/**
|
|
620
|
+
/** How many messages the stack shows: the ones taken on that are still in the region. */
|
|
621
|
+
#shownCount() {
|
|
622
|
+
return this.#order.filter((message) => this.#owns(message)).length;
|
|
623
|
+
}
|
|
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
|
+
*/
|
|
446
632
|
#startTimer(message) {
|
|
447
633
|
if (this.durationValue <= 0) return;
|
|
448
|
-
|
|
634
|
+
const dismiss = () => this.#beginDismiss(message, "timeout");
|
|
635
|
+
if (this.pauseOnHoverValue) this.#dismiss.set(message, dismiss, this.durationValue);
|
|
636
|
+
else this.#fixedDismiss.set(message, dismiss, this.durationValue);
|
|
449
637
|
}
|
|
450
638
|
/**
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
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.
|
|
454
642
|
*/
|
|
455
643
|
#pause(message, reason) {
|
|
456
644
|
this.#dismiss.pause(message, reason);
|
|
457
645
|
}
|
|
458
|
-
/**
|
|
459
|
-
|
|
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
|
+
#resume(message, reason, spare) {
|
|
651
|
+
const held = this.#dismiss.isHeld(message);
|
|
460
652
|
this.#dismiss.resume(message, reason);
|
|
653
|
+
if (held && !this.#dismiss.isHeld(message)) this.#capLater(spare);
|
|
654
|
+
}
|
|
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
|
+
#capLater(spare) {
|
|
662
|
+
if (spare) this.#spared.push(spare);
|
|
663
|
+
this.#reapply.schedule();
|
|
664
|
+
}
|
|
665
|
+
/** Applies the cap again, sparing every message gathered since the last pass. */
|
|
666
|
+
#reapplyCap() {
|
|
667
|
+
const spared = this.#spared;
|
|
668
|
+
this.#spared = [];
|
|
669
|
+
this.#enforceMax(spared);
|
|
670
|
+
}
|
|
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
|
+
#afterMove(message) {
|
|
679
|
+
if (!message.contains(document.activeElement)) this.#resume(message, "focus", null);
|
|
680
|
+
if (this.#dismiss.isHeld(message)) this.#awaitPointer(message);
|
|
461
681
|
}
|
|
462
|
-
/**
|
|
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
|
+
#readHolds(message) {
|
|
688
|
+
if (message.contains(document.activeElement)) this.#pause(message, "focus");
|
|
689
|
+
if (!message.matches(":hover")) return;
|
|
690
|
+
this.#pause(message, "hover");
|
|
691
|
+
this.#awaitPointer(message);
|
|
692
|
+
}
|
|
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
|
+
*/
|
|
463
718
|
#forget(message) {
|
|
719
|
+
if (this.#dismiss.isHeld(message)) this.#capLater(null);
|
|
464
720
|
this.#dismiss.clear(message);
|
|
721
|
+
this.#fixedDismiss.clear(message);
|
|
465
722
|
const index = this.#order.indexOf(message);
|
|
466
723
|
if (index !== -1) this.#order.splice(index, 1);
|
|
467
724
|
this.#unbindPause(message);
|
|
725
|
+
this.#awaitingPointer.delete(message);
|
|
726
|
+
if (this.#awaitingPointer.size === 0) this.#pointer.dispose();
|
|
468
727
|
}
|
|
469
728
|
/** Marks a message leaving, then removes it after its CSS transition and emits dismiss. */
|
|
470
729
|
#beginDismiss(message, reason) {
|