stimeo-ui 0.14.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 +212 -0
- package/README.md +120 -0
- package/dist/cable/index.js +123 -29
- 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 +96 -62
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +83 -7
- 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 +38 -11
- 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 +101 -23
- package/dist/controllers/calendar_controller.js +340 -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 +81 -12
- 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 +85 -17
- 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/count_up_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 +77 -16
- package/dist/controllers/currency_input_controller.js +221 -67
- 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 +41 -6
- package/dist/controllers/date_range_picker_controller.js +151 -30
- 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 +46 -13
- 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 +432 -71
- 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 +45 -8
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js +82 -25
- 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/local_time_controller.js +10 -5
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +3 -3
- package/dist/controllers/masonry_controller.js +31 -15
- 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 +42 -5
- 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 +115 -21
- 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 +141 -44
- 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 +100 -7
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.js +65 -19
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.js +10 -5
- package/dist/controllers/relative_time_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 +75 -27
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +37 -16
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +49 -30
- 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 +71 -26
- 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 +60 -26
- 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 +104 -8
- 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 +45 -9
- 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/textarea_autosize_controller.js +29 -3
- package/dist/controllers/textarea_autosize_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 +55 -15
- package/dist/controllers/toast_controller.js +451 -105
- 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 +5002 -1911
- 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 +529 -61
- package/dist/positioning/index.js +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -90,12 +90,91 @@ var BeforeCacheReset = class _BeforeCacheReset {
|
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
+
// src/utils/detach_gate.ts
|
|
94
|
+
var DetachGate = class _DetachGate {
|
|
95
|
+
/** Set while a probe is queued, waiting for a reconnect to cancel it. */
|
|
96
|
+
#pending = false;
|
|
97
|
+
/**
|
|
98
|
+
* True while a probe is queued — the last disconnect was ambiguous and no
|
|
99
|
+
* reconnect has cancelled it yet. Read it from `connect()` to tell the
|
|
100
|
+
* reconnect half of an in-page move from a first connect: a controller whose
|
|
101
|
+
* initialisation restarts a measurement (a min-duration floor, an elapsed
|
|
102
|
+
* counter) must skip it for the move, where nothing actually restarted.
|
|
103
|
+
*/
|
|
104
|
+
get pending() {
|
|
105
|
+
return this.#pending;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* True when the disconnect is definitely a real detach — the element left
|
|
109
|
+
* the document, or `data-controller` no longer lists the identifier. False
|
|
110
|
+
* means ambiguous (in-page move or observed-root exit), NOT "alive".
|
|
111
|
+
*/
|
|
112
|
+
static isDetached(host) {
|
|
113
|
+
if (!host.element.isConnected) return true;
|
|
114
|
+
const tokens = (host.element.getAttribute("data-controller") ?? "").split(/\s+/);
|
|
115
|
+
return !tokens.includes(host.identifier);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Call from `disconnect()`: runs `teardown` synchronously on a definite
|
|
119
|
+
* detach (fast path), otherwise defers it one microtask — a reconnect
|
|
120
|
+
* ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.
|
|
121
|
+
* One microtask is the whole probe window: Stimulus reconnects a moved
|
|
122
|
+
* element within the same mutation batch, before the checkpoint drains.
|
|
123
|
+
*/
|
|
124
|
+
disconnected(host, teardown) {
|
|
125
|
+
if (_DetachGate.isDetached(host)) {
|
|
126
|
+
this.#pending = false;
|
|
127
|
+
teardown();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
this.#pending = true;
|
|
131
|
+
queueMicrotask(() => {
|
|
132
|
+
if (!this.#pending) return;
|
|
133
|
+
this.#pending = false;
|
|
134
|
+
teardown();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Disarms a pending probe. Call from `connect()` (the reconnect that proves
|
|
139
|
+
* an in-page move) and from the head of any teardown path not routed through
|
|
140
|
+
* {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can
|
|
141
|
+
* never run the teardown a second time.
|
|
142
|
+
*/
|
|
143
|
+
cancel() {
|
|
144
|
+
this.#pending = false;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
93
148
|
// src/utils/event_owner.ts
|
|
94
149
|
function ownerIndex(candidates, node) {
|
|
95
150
|
if (!(node instanceof Node)) return -1;
|
|
96
151
|
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
97
152
|
}
|
|
98
153
|
|
|
154
|
+
// src/utils/listener_set.ts
|
|
155
|
+
var ListenerSet = class {
|
|
156
|
+
/** The generation every `add` joins until the next `dispose()`. */
|
|
157
|
+
#abort = new AbortController();
|
|
158
|
+
/**
|
|
159
|
+
* Attaches `handler` to the open generation, exactly as the caller spelled it.
|
|
160
|
+
*
|
|
161
|
+
* The set supplies the signal, so `options` carries everything else the DOM
|
|
162
|
+
* accepts — `capture` included, which has to match at release time and no
|
|
163
|
+
* longer has a second place to drift from.
|
|
164
|
+
*/
|
|
165
|
+
add(target, type, handler, options) {
|
|
166
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Releases every listener of the open generation, synchronously, and opens the
|
|
170
|
+
* next one. Idempotent, and safe before anything has been added.
|
|
171
|
+
*/
|
|
172
|
+
dispose() {
|
|
173
|
+
this.#abort.abort();
|
|
174
|
+
this.#abort = new AbortController();
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
99
178
|
// src/utils/microtask_coalescer.ts
|
|
100
179
|
var MicrotaskCoalescer = class {
|
|
101
180
|
#run;
|
|
@@ -219,11 +298,61 @@ var SafeInterval = class extends TimerRegistry {
|
|
|
219
298
|
}
|
|
220
299
|
};
|
|
221
300
|
|
|
301
|
+
// src/utils/state_regions.ts
|
|
302
|
+
var StateRegions = class {
|
|
303
|
+
#whenTrue;
|
|
304
|
+
#whenFalse;
|
|
305
|
+
#taken = /* @__PURE__ */ new WeakSet();
|
|
306
|
+
constructor(options) {
|
|
307
|
+
this.#whenTrue = options.whenTrue;
|
|
308
|
+
this.#whenFalse = options.whenFalse ?? null;
|
|
309
|
+
}
|
|
310
|
+
/** Shows the regions inside `host` that belong to `isTrue` and hides the others. */
|
|
311
|
+
reflect(host, isTrue) {
|
|
312
|
+
const shown = this.#inside(host, this.#whenTrue());
|
|
313
|
+
if (!this.#whenFalse) {
|
|
314
|
+
for (const region of shown) this.#write(region, !isTrue);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const hiddenSide = this.#inside(host, this.#whenFalse());
|
|
318
|
+
if (shown.length === 0 || hiddenSide.length === 0) {
|
|
319
|
+
for (const region of shown) this.#give(region);
|
|
320
|
+
for (const region of hiddenSide) this.#give(region);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
for (const region of shown) this.#write(region, !isTrue);
|
|
324
|
+
for (const region of hiddenSide) this.#write(region, isTrue);
|
|
325
|
+
}
|
|
326
|
+
/** Writes `hidden` where it moves, noting which regions it takes out of view. */
|
|
327
|
+
#write(region, hidden) {
|
|
328
|
+
if (hidden) this.#taken.add(region);
|
|
329
|
+
else this.#taken.delete(region);
|
|
330
|
+
if (region.hidden !== hidden) region.hidden = hidden;
|
|
331
|
+
}
|
|
332
|
+
/** Returns a region this instance hid; one it never hid keeps what it carries. */
|
|
333
|
+
#give(region) {
|
|
334
|
+
if (this.#taken.delete(region)) region.hidden = false;
|
|
335
|
+
}
|
|
336
|
+
/** The declared regions that sit within `host`, which may be the host itself. */
|
|
337
|
+
#inside(host, regions) {
|
|
338
|
+
return regions.filter((region) => host.contains(region));
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
222
342
|
// src/controllers/carousel_controller.ts
|
|
223
343
|
var DEFAULT_INTERVAL = 5e3;
|
|
224
344
|
var OBSERVED_ATTRIBUTES = ["aria-selected", "data-state", "hidden"];
|
|
225
345
|
var CarouselController = class extends Controller {
|
|
226
|
-
static targets = [
|
|
346
|
+
static targets = [
|
|
347
|
+
"slide",
|
|
348
|
+
"viewport",
|
|
349
|
+
"prev",
|
|
350
|
+
"next",
|
|
351
|
+
"picker",
|
|
352
|
+
"playToggle",
|
|
353
|
+
"onLabel",
|
|
354
|
+
"offLabel"
|
|
355
|
+
];
|
|
227
356
|
static values = {
|
|
228
357
|
autoplay: { type: Boolean, default: false },
|
|
229
358
|
interval: { type: Number, default: DEFAULT_INTERVAL },
|
|
@@ -248,7 +377,16 @@ var CarouselController = class extends Controller {
|
|
|
248
377
|
#ariaLive = new AttributeLease("aria-live");
|
|
249
378
|
/** Pairs with {@link CarouselController.#ariaLive}: only the changed slide is read. */
|
|
250
379
|
#ariaAtomic = new AttributeLease("aria-atomic");
|
|
380
|
+
/** The label pair a play toggle may carry, one half per rotation state. */
|
|
381
|
+
#labels = new StateRegions({
|
|
382
|
+
whenTrue: () => this.onLabelTargets,
|
|
383
|
+
whenFalse: () => this.offLabelTargets
|
|
384
|
+
});
|
|
251
385
|
#beforeCache = new BeforeCacheReset(() => this.#returnLeases());
|
|
386
|
+
/** Tells an in-page move, which keeps the rotation, from a real detach. */
|
|
387
|
+
#gate = new DetachGate();
|
|
388
|
+
/** The next pointer movement, listened for while a pointer suspension waits on it. */
|
|
389
|
+
#pointer = new ListenerSet();
|
|
252
390
|
/**
|
|
253
391
|
* Events an authored action binding already took. The delegated listener runs
|
|
254
392
|
* later — it sits on the controller element, above every control — so it can
|
|
@@ -268,12 +406,17 @@ var CarouselController = class extends Controller {
|
|
|
268
406
|
#activeSlide = null;
|
|
269
407
|
/** Slide count at the last resolved state, so a changed total is reportable. */
|
|
270
408
|
#total = 0;
|
|
271
|
-
/**
|
|
409
|
+
/**
|
|
410
|
+
* Pointer rests on the carousel: a suspension that lifts on `mouseleave`, or on the
|
|
411
|
+
* pointer movement that confirms a suspension a move or a connect left unconfirmed.
|
|
412
|
+
*/
|
|
272
413
|
#pointerPaused = false;
|
|
273
414
|
/** Focus is inside the carousel: a suspension that lifts when it leaves. */
|
|
274
415
|
#focusPaused = false;
|
|
275
416
|
/** The tab is in the background: a suspension that lifts when it returns. */
|
|
276
417
|
#hiddenPaused = false;
|
|
418
|
+
/** Reduced motion holds back the intent declared at connect, until a press or a new one. */
|
|
419
|
+
#motionHold = false;
|
|
277
420
|
/** Id of the live autoplay interval, or null when stopped. */
|
|
278
421
|
#timerId = null;
|
|
279
422
|
/** Delay the live interval was armed with, so an `interval` change re-arms it. */
|
|
@@ -289,17 +432,24 @@ var CarouselController = class extends Controller {
|
|
|
289
432
|
* Renders the initial slide, wires the delegated listeners, and starts autoplay
|
|
290
433
|
* when requested.
|
|
291
434
|
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
435
|
+
* Focus, the tab's visibility and the reduced-motion hold are read afresh. The
|
|
436
|
+
* pointer is not something the document can answer yet: no `mouseleave` reaches a
|
|
437
|
+
* node that moved, and `:hover` can answer from before the move until the pointer
|
|
438
|
+
* moves again. So an in-page move — which Stimulus delivers to the *same* controller
|
|
439
|
+
* instance as `disconnect()` then `connect()` — keeps the pointer suspension it had,
|
|
440
|
+
* `:hover` reading true adds one, and either waits for the next pointer movement to
|
|
441
|
+
* be confirmed or lifted. A rotation the move carried is still running, so the edges
|
|
442
|
+
* are judged against it. The attribute observer starts after the first render so the
|
|
443
|
+
* controller's own opening writes are not fed back to it.
|
|
297
444
|
*/
|
|
298
445
|
connect() {
|
|
299
|
-
|
|
446
|
+
const moved = this.#gate.pending;
|
|
447
|
+
this.#gate.cancel();
|
|
448
|
+
this.#pointerPaused = moved && this.#pointerPaused || this.element.matches(":hover");
|
|
449
|
+
if (this.#pointerPaused) this.#awaitPointer();
|
|
300
450
|
this.#focusPaused = this.element.contains(document.activeElement);
|
|
301
451
|
this.#hiddenPaused = document.visibilityState === "hidden";
|
|
302
|
-
|
|
452
|
+
this.#motionHold = prefersReducedMotion();
|
|
303
453
|
this.#index = this.#resolveIndex();
|
|
304
454
|
this.#render({ focus: false });
|
|
305
455
|
this.#total = this.slideTargets.length;
|
|
@@ -321,9 +471,15 @@ var CarouselController = class extends Controller {
|
|
|
321
471
|
attributeFilter: OBSERVED_ATTRIBUTES
|
|
322
472
|
});
|
|
323
473
|
}
|
|
324
|
-
/**
|
|
474
|
+
/**
|
|
475
|
+
* Releases every listener and observer, returns the leased ARIA, drops the focus
|
|
476
|
+
* and tab suspensions. The rotation goes through the detach gate: a reconnect in
|
|
477
|
+
* the same batch — an in-page move — keeps it, and the pointer suspension with it;
|
|
478
|
+
* anything else ends it.
|
|
479
|
+
*/
|
|
325
480
|
disconnect() {
|
|
326
481
|
this.#connected = false;
|
|
482
|
+
this.#pointer.dispose();
|
|
327
483
|
this.element.removeEventListener("click", this.#onClick);
|
|
328
484
|
this.element.removeEventListener("keydown", this.#onKeydown);
|
|
329
485
|
this.element.removeEventListener("focusin", this.#onFocusin);
|
|
@@ -334,14 +490,12 @@ var CarouselController = class extends Controller {
|
|
|
334
490
|
this.#observer?.disconnect();
|
|
335
491
|
this.#observer = null;
|
|
336
492
|
this.#reconcileTargets.cancel();
|
|
337
|
-
this.#intervals.clearAll();
|
|
338
|
-
this.#timerId = null;
|
|
339
|
-
this.#pointerPaused = false;
|
|
340
493
|
this.#focusPaused = false;
|
|
341
494
|
this.#hiddenPaused = false;
|
|
342
495
|
this.#activeSlide = null;
|
|
343
496
|
this.#returnLeases();
|
|
344
497
|
this.#beforeCache.deactivate();
|
|
498
|
+
this.#gate.disconnected(this, () => this.#endRun());
|
|
345
499
|
}
|
|
346
500
|
/**
|
|
347
501
|
* Re-establishes the single selected picker when one is added after connect.
|
|
@@ -368,19 +522,46 @@ var CarouselController = class extends Controller {
|
|
|
368
522
|
slideTargetDisconnected() {
|
|
369
523
|
this.#reconcileTargets.schedule();
|
|
370
524
|
}
|
|
371
|
-
/**
|
|
372
|
-
|
|
525
|
+
/**
|
|
526
|
+
* Publishes the rotation state on a toggle that joins a connected carousel.
|
|
527
|
+
*
|
|
528
|
+
* A toggle the markup already carries connects ahead of `connect()`, where the
|
|
529
|
+
* suspensions and the reduced-motion hold are not settled yet; the opening pass
|
|
530
|
+
* covers it.
|
|
531
|
+
*/
|
|
532
|
+
playToggleTargetConnected() {
|
|
373
533
|
if (this.#connected) this.#syncTimer();
|
|
374
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* Publishes the rotation state on a label half that joins a connected toggle.
|
|
537
|
+
*
|
|
538
|
+
* A half arriving changes the toggle's subtree, not its attributes, so the
|
|
539
|
+
* attribute observer never sees it; where the arrival completes a pair that was
|
|
540
|
+
* standing alone, this is what makes that pair whole and reflectable.
|
|
541
|
+
*/
|
|
542
|
+
onLabelTargetConnected() {
|
|
543
|
+
if (this.#connected) this.#syncTimer();
|
|
544
|
+
}
|
|
545
|
+
/** Publishes the rotation state on a label half that joins a connected toggle. */
|
|
546
|
+
offLabelTargetConnected() {
|
|
547
|
+
if (this.#connected) this.#syncTimer();
|
|
548
|
+
}
|
|
549
|
+
/** Follows a rotation intent the page changed at runtime, lifting the reduced-motion hold. */
|
|
550
|
+
autoplayValueChanged() {
|
|
551
|
+
if (!this.#connected) return;
|
|
552
|
+
this.#motionHold = false;
|
|
553
|
+
this.#syncTimer();
|
|
554
|
+
}
|
|
375
555
|
/** Re-arms the live interval at the new delay without reporting a state change. */
|
|
376
556
|
intervalValueChanged() {
|
|
377
557
|
if (this.#connected) this.#syncTimer();
|
|
378
558
|
}
|
|
379
|
-
/**
|
|
559
|
+
/**
|
|
560
|
+
* Re-publishes the step controls and re-evaluates the non-looping end through the
|
|
561
|
+
* reconcile pass, which reports a move only when the position changed.
|
|
562
|
+
*/
|
|
380
563
|
loopValueChanged() {
|
|
381
|
-
|
|
382
|
-
this.#render({ focus: false });
|
|
383
|
-
this.#syncTimer();
|
|
564
|
+
this.#reconcileTargets.schedule();
|
|
384
565
|
}
|
|
385
566
|
/** Advances to the next slide. Delegated; `data-action` wiring is optional. */
|
|
386
567
|
next(event) {
|
|
@@ -402,11 +583,11 @@ var CarouselController = class extends Controller {
|
|
|
402
583
|
if (index !== -1) this.#select(index, { focus: false });
|
|
403
584
|
}
|
|
404
585
|
/**
|
|
405
|
-
* Flips the rotation intent on the user's explicit request.
|
|
586
|
+
* Flips the rotation intent the toggle shows, on the user's explicit request.
|
|
406
587
|
*
|
|
407
|
-
* The
|
|
408
|
-
*
|
|
409
|
-
*
|
|
588
|
+
* The result is written to the `autoplay` Value, which is where every other path
|
|
589
|
+
* reads it from. A carousel with nothing to advance to has no intent to flip: the
|
|
590
|
+
* toggle is marked `aria-disabled` and does nothing.
|
|
410
591
|
*/
|
|
411
592
|
togglePlay(event) {
|
|
412
593
|
if (event?.defaultPrevented) return;
|
|
@@ -422,7 +603,7 @@ var CarouselController = class extends Controller {
|
|
|
422
603
|
pause(event) {
|
|
423
604
|
this.#markHandled(event);
|
|
424
605
|
if (isFocusEvent(event)) this.#focusPaused = true;
|
|
425
|
-
else this.#
|
|
606
|
+
else this.#setPointerPaused(true);
|
|
426
607
|
this.#syncTimer();
|
|
427
608
|
}
|
|
428
609
|
/**
|
|
@@ -437,7 +618,7 @@ var CarouselController = class extends Controller {
|
|
|
437
618
|
if (this.#focusStaysInside(event)) return;
|
|
438
619
|
this.#focusPaused = false;
|
|
439
620
|
} else {
|
|
440
|
-
this.#
|
|
621
|
+
this.#setPointerPaused(false);
|
|
441
622
|
}
|
|
442
623
|
this.#syncTimer();
|
|
443
624
|
}
|
|
@@ -479,14 +660,36 @@ var CarouselController = class extends Controller {
|
|
|
479
660
|
});
|
|
480
661
|
/** Pointer entry suspends the rotation. */
|
|
481
662
|
#onMouseenter = (event) => this.#delegate(event, () => {
|
|
482
|
-
this.#
|
|
663
|
+
this.#setPointerPaused(true);
|
|
483
664
|
this.#syncTimer();
|
|
484
665
|
});
|
|
485
666
|
/** Pointer exit lifts the suspension. */
|
|
486
667
|
#onMouseleave = (event) => this.#delegate(event, () => {
|
|
487
|
-
this.#
|
|
668
|
+
this.#setPointerPaused(false);
|
|
488
669
|
this.#syncTimer();
|
|
489
670
|
});
|
|
671
|
+
/**
|
|
672
|
+
* Reads `:hover` once the pointer has moved, when the answer is current, and lifts
|
|
673
|
+
* the pointer suspension if the pointer is not over the carousel. Listens once.
|
|
674
|
+
*/
|
|
675
|
+
#onPointerMove = () => {
|
|
676
|
+
this.#pointer.dispose();
|
|
677
|
+
if (this.element.matches(":hover")) return;
|
|
678
|
+
this.#pointerPaused = false;
|
|
679
|
+
this.#syncTimer();
|
|
680
|
+
};
|
|
681
|
+
/** Leaves the pointer suspension to be confirmed or lifted on the next pointer movement. */
|
|
682
|
+
#awaitPointer() {
|
|
683
|
+
this.#pointer.add(document, "pointermove", this.#onPointerMove, {
|
|
684
|
+
capture: true,
|
|
685
|
+
passive: true
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
/** Records the pointer suspension; a newer answer ends any wait on the next movement. */
|
|
689
|
+
#setPointerPaused(paused) {
|
|
690
|
+
this.#pointer.dispose();
|
|
691
|
+
this.#pointerPaused = paused;
|
|
692
|
+
}
|
|
490
693
|
/**
|
|
491
694
|
* Schedules one reconciliation when a state attribute is rewritten in place —
|
|
492
695
|
* the only shape of change no target callback reports.
|
|
@@ -523,10 +726,12 @@ var CarouselController = class extends Controller {
|
|
|
523
726
|
const next = event.relatedTarget;
|
|
524
727
|
return next instanceof Node && this.element.contains(next);
|
|
525
728
|
}
|
|
526
|
-
/** Flips the rotation intent
|
|
729
|
+
/** Flips the rotation intent unless there is nothing to rotate to; lifts the motion hold. */
|
|
527
730
|
#togglePlay() {
|
|
528
731
|
if (!this.#canAutoplay()) return;
|
|
529
|
-
|
|
732
|
+
const next = !this.#intent;
|
|
733
|
+
this.#motionHold = false;
|
|
734
|
+
this.autoplayValue = next;
|
|
530
735
|
this.#syncTimer();
|
|
531
736
|
}
|
|
532
737
|
/** Applies the APG picker key map: arrows, Home, and End all move focus only. */
|
|
@@ -585,8 +790,7 @@ var CarouselController = class extends Controller {
|
|
|
585
790
|
/**
|
|
586
791
|
* Changes the active slide, updates state hooks, and emits `change` — but only
|
|
587
792
|
* when the index actually changes, so a `next`/`prev` clamped at the end (or an
|
|
588
|
-
* autoplay tick at a non-looping boundary) re-renders without a spurious event
|
|
589
|
-
* (matching the "emit on real change" policy of flash/masonry/bulk-select).
|
|
793
|
+
* autoplay tick at a non-looping boundary) re-renders without a spurious event.
|
|
590
794
|
*/
|
|
591
795
|
#select(index, { focus }) {
|
|
592
796
|
const target = this.#clampToSlides(index);
|
|
@@ -673,6 +877,10 @@ var CarouselController = class extends Controller {
|
|
|
673
877
|
this.dispatch("reconcile", { detail: { index: this.#index, total: this.#total } });
|
|
674
878
|
}
|
|
675
879
|
}
|
|
880
|
+
/** The rotation intent in effect: `autoplay`, unless reduced motion holds it back. */
|
|
881
|
+
get #intent() {
|
|
882
|
+
return this.autoplayValue && !this.#motionHold;
|
|
883
|
+
}
|
|
676
884
|
/** Whether autoplay has anywhere left to advance to. */
|
|
677
885
|
#canAutoplay() {
|
|
678
886
|
const total = this.slideTargets.length;
|
|
@@ -688,15 +896,18 @@ var CarouselController = class extends Controller {
|
|
|
688
896
|
* Drives the autoplay interval toward the desired state and publishes it.
|
|
689
897
|
*
|
|
690
898
|
* Rotation runs when the intent is on, nothing suspends it, and a slide is left
|
|
691
|
-
* to advance to.
|
|
692
|
-
* `aria-pressed` never claims a rotation that cannot happen.
|
|
693
|
-
*
|
|
694
|
-
*
|
|
899
|
+
* to advance to. The toggle shows the intent only while a slide is left, so
|
|
900
|
+
* `aria-pressed` never claims a rotation that cannot happen. This body reads
|
|
901
|
+
* `autoplay` and never writes it, so the declared intent returns once a slide
|
|
902
|
+
* lies ahead again. Crossing the run/stop boundary emits `play`/`pause`;
|
|
903
|
+
* re-arming at a new `interval` is the same state and stays silent.
|
|
904
|
+
*
|
|
905
|
+
* @stimeoRenderRoot
|
|
695
906
|
*/
|
|
696
907
|
#syncTimer() {
|
|
697
908
|
const canAutoplay = this.#canAutoplay();
|
|
698
|
-
|
|
699
|
-
const shouldRun =
|
|
909
|
+
const pressed = this.#intent && canAutoplay;
|
|
910
|
+
const shouldRun = pressed && !this.#pointerPaused && !this.#focusPaused && !this.#hiddenPaused;
|
|
700
911
|
const wasRunning = this.#timerId !== null;
|
|
701
912
|
const interval = this.#interval;
|
|
702
913
|
if (this.#timerId !== null && (!shouldRun || interval !== this.#timerInterval)) {
|
|
@@ -713,14 +924,28 @@ var CarouselController = class extends Controller {
|
|
|
713
924
|
else this.dispatch("pause");
|
|
714
925
|
}
|
|
715
926
|
for (const toggle of this.playToggleTargets) {
|
|
716
|
-
toggle.setAttribute("aria-pressed",
|
|
927
|
+
toggle.setAttribute("aria-pressed", pressed ? "true" : "false");
|
|
717
928
|
this.#ariaDisabled.write(toggle, canAutoplay ? null : "true");
|
|
929
|
+
this.#labels.reflect(toggle, pressed);
|
|
718
930
|
}
|
|
719
931
|
if (this.hasViewportTarget) {
|
|
720
932
|
this.#ariaLive.write(this.viewportTarget, shouldRun ? "off" : "polite");
|
|
721
933
|
this.#ariaAtomic.write(this.viewportTarget, "false");
|
|
722
934
|
}
|
|
723
935
|
}
|
|
936
|
+
/**
|
|
937
|
+
* Ends the rotation once the controller has really left. A carousel still in
|
|
938
|
+
* the document hears the end — `pause`, and `data-state="paused"` — so no `play`
|
|
939
|
+
* it reported is left without one; a detached tree keeps the markup it had.
|
|
940
|
+
*/
|
|
941
|
+
#endRun() {
|
|
942
|
+
const wasRunning = this.#timerId !== null;
|
|
943
|
+
this.#intervals.clearAll();
|
|
944
|
+
this.#timerId = null;
|
|
945
|
+
if (!wasRunning || !this.element.isConnected) return;
|
|
946
|
+
setAttributeIfChanged(this.element, "data-state", "paused");
|
|
947
|
+
this.dispatch("pause");
|
|
948
|
+
}
|
|
724
949
|
/** Hands every leased attribute back to the value the consumer authored. */
|
|
725
950
|
#returnLeases() {
|
|
726
951
|
this.#ariaDisabled.returnAll();
|