stimeo-ui 0.14.0 → 0.15.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/dist/cable/index.js +60 -14
- data/dist/controllers/announcer_controller.js +88 -61
- data/dist/controllers/auto_submit_controller.js +40 -5
- data/dist/controllers/breadcrumb_controller.js +29 -3
- data/dist/controllers/calendar_controller.js +20 -3
- data/dist/controllers/checkbox_controller.js +27 -2
- data/dist/controllers/conditional_fields_controller.js +29 -6
- data/dist/controllers/currency_input_controller.js +16 -9
- data/dist/controllers/date_range_picker_controller.js +12 -3
- data/dist/controllers/dirty_form_controller.js +32 -12
- data/dist/controllers/flash_controller.js +145 -43
- data/dist/controllers/frame_loading_controller.js +29 -6
- data/dist/controllers/highlight_controller.js +63 -23
- data/dist/controllers/local_time_controller.js +10 -5
- data/dist/controllers/masonry_controller.js +31 -13
- data/dist/controllers/optimistic_controller.js +28 -4
- data/dist/controllers/overflow_indicator_controller.js +31 -8
- data/dist/controllers/overflow_menu_controller.js +29 -3
- data/dist/controllers/read_more_controller.js +31 -5
- data/dist/controllers/reading_progress_controller.js +65 -19
- data/dist/controllers/relative_time_controller.js +10 -5
- data/dist/controllers/scroll_area_controller.js +70 -26
- data/dist/controllers/scroll_restore_controller.js +37 -16
- data/dist/controllers/scroll_visibility_controller.js +49 -29
- data/dist/controllers/scrollspy_controller.js +65 -26
- data/dist/controllers/smart_sticky_header_controller.js +49 -25
- data/dist/controllers/stick_to_bottom_controller.js +29 -3
- data/dist/controllers/submit_once_controller.js +29 -6
- data/dist/controllers/textarea_autosize_controller.js +29 -3
- data/dist/controllers/toast_controller.js +136 -73
- data/dist/index.js +473 -435
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +2 -2
|
@@ -8,10 +8,15 @@ var LayoutObserver = class {
|
|
|
8
8
|
#resizeObserverFactory;
|
|
9
9
|
#resizeObserver = null;
|
|
10
10
|
#observingViewport = false;
|
|
11
|
+
#loadContainer = null;
|
|
11
12
|
/** Stable bound handler so add/removeEventListener target the same reference. */
|
|
12
13
|
#handleViewportResize = () => {
|
|
13
14
|
this.#callback();
|
|
14
15
|
};
|
|
16
|
+
/** Stable bound handler for the capture-phase `load`; see {@link observeDescendantLoads}. */
|
|
17
|
+
#handleDescendantLoad = () => {
|
|
18
|
+
this.#callback();
|
|
19
|
+
};
|
|
15
20
|
constructor(callback, options = {}) {
|
|
16
21
|
this.#callback = callback;
|
|
17
22
|
this.#resizeObserverFactory = options.resizeObserverFactory ?? (typeof ResizeObserver === "undefined" ? null : (cb) => new ResizeObserver(cb));
|
|
@@ -47,14 +52,35 @@ var LayoutObserver = class {
|
|
|
47
52
|
window.removeEventListener("resize", this.#handleViewportResize);
|
|
48
53
|
}
|
|
49
54
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
55
|
+
* Starts reporting a `load` from anywhere inside `container` — an image or a
|
|
56
|
+
* frame settling changes the box it sits in, and it measures as zero high until
|
|
57
|
+
* then. `load` does not bubble, so the subscription is a capture-phase listener
|
|
58
|
+
* on the container itself and nothing the caller spells.
|
|
59
|
+
*
|
|
60
|
+
* **One container at a time.** A further call moves the observation, so a widget
|
|
61
|
+
* whose content element is swapped at runtime releases the element it let go by
|
|
62
|
+
* naming the new one — there is no second place for the release to drift from.
|
|
63
|
+
*/
|
|
64
|
+
observeDescendantLoads(container) {
|
|
65
|
+
this.unobserveDescendantLoads();
|
|
66
|
+
this.#loadContainer = container;
|
|
67
|
+
container.addEventListener("load", this.#handleDescendantLoad, true);
|
|
68
|
+
}
|
|
69
|
+
/** Stops reporting descendant loads without affecting element or viewport observation. */
|
|
70
|
+
unobserveDescendantLoads() {
|
|
71
|
+
this.#loadContainer?.removeEventListener("load", this.#handleDescendantLoad, true);
|
|
72
|
+
this.#loadContainer = null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Releases every observation: disconnects the {@link ResizeObserver} and removes
|
|
76
|
+
* the viewport and descendant-load listeners. Safe to call multiple times. Call
|
|
77
|
+
* this from a controller's `disconnect()`.
|
|
53
78
|
*/
|
|
54
79
|
disconnect() {
|
|
55
80
|
this.#resizeObserver?.disconnect();
|
|
56
81
|
this.#resizeObserver = null;
|
|
57
82
|
this.unobserveViewport();
|
|
83
|
+
this.unobserveDescendantLoads();
|
|
58
84
|
}
|
|
59
85
|
};
|
|
60
86
|
|
|
@@ -138,6 +138,30 @@ var DetachGate = class _DetachGate {
|
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
+
// src/utils/listener_set.ts
|
|
142
|
+
var ListenerSet = class {
|
|
143
|
+
/** The generation every `add` joins until the next `dispose()`. */
|
|
144
|
+
#abort = new AbortController();
|
|
145
|
+
/**
|
|
146
|
+
* Attaches `handler` to the open generation, exactly as the caller spelled it.
|
|
147
|
+
*
|
|
148
|
+
* The set supplies the signal, so `options` carries everything else the DOM
|
|
149
|
+
* accepts — `capture` included, which has to match at release time and no
|
|
150
|
+
* longer has a second place to drift from.
|
|
151
|
+
*/
|
|
152
|
+
add(target, type, handler, options) {
|
|
153
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Releases every listener of the open generation, synchronously, and opens the
|
|
157
|
+
* next one. Idempotent, and safe before anything has been added.
|
|
158
|
+
*/
|
|
159
|
+
dispose() {
|
|
160
|
+
this.#abort.abort();
|
|
161
|
+
this.#abort = new AbortController();
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
141
165
|
// src/utils/safe_timeout.ts
|
|
142
166
|
var TimerRegistry = class {
|
|
143
167
|
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
@@ -206,6 +230,7 @@ var SubmitOnceController = class extends Controller {
|
|
|
206
230
|
static events = ["start", "end", "reconcile"];
|
|
207
231
|
#timers = new SafeTimeout();
|
|
208
232
|
#gate = new DetachGate();
|
|
233
|
+
#listeners = new ListenerSet();
|
|
209
234
|
#beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
|
|
210
235
|
#sessions = /* @__PURE__ */ new Map();
|
|
211
236
|
#disabled = new AttributeLease("disabled");
|
|
@@ -230,14 +255,12 @@ var SubmitOnceController = class extends Controller {
|
|
|
230
255
|
connect() {
|
|
231
256
|
this.#gate.cancel();
|
|
232
257
|
this.#beforeCache.activate();
|
|
233
|
-
this.element
|
|
234
|
-
this.element
|
|
235
|
-
this.element
|
|
258
|
+
this.#listeners.add(this.element, "submit", this.#onNativeSubmit, { capture: true });
|
|
259
|
+
this.#listeners.add(this.element, "turbo:submit-start", this.#onSubmitStart);
|
|
260
|
+
this.#listeners.add(this.element, "turbo:submit-end", this.#onSubmitEnd);
|
|
236
261
|
}
|
|
237
262
|
disconnect() {
|
|
238
|
-
this.
|
|
239
|
-
this.element.removeEventListener("turbo:submit-start", this.#onSubmitStart);
|
|
240
|
-
this.element.removeEventListener("turbo:submit-end", this.#onSubmitEnd);
|
|
263
|
+
this.#listeners.dispose();
|
|
241
264
|
this.#beforeCache.deactivate();
|
|
242
265
|
this.#gate.disconnected(this, () => this.#teardown());
|
|
243
266
|
}
|
|
@@ -8,10 +8,15 @@ var LayoutObserver = class {
|
|
|
8
8
|
#resizeObserverFactory;
|
|
9
9
|
#resizeObserver = null;
|
|
10
10
|
#observingViewport = false;
|
|
11
|
+
#loadContainer = null;
|
|
11
12
|
/** Stable bound handler so add/removeEventListener target the same reference. */
|
|
12
13
|
#handleViewportResize = () => {
|
|
13
14
|
this.#callback();
|
|
14
15
|
};
|
|
16
|
+
/** Stable bound handler for the capture-phase `load`; see {@link observeDescendantLoads}. */
|
|
17
|
+
#handleDescendantLoad = () => {
|
|
18
|
+
this.#callback();
|
|
19
|
+
};
|
|
15
20
|
constructor(callback, options = {}) {
|
|
16
21
|
this.#callback = callback;
|
|
17
22
|
this.#resizeObserverFactory = options.resizeObserverFactory ?? (typeof ResizeObserver === "undefined" ? null : (cb) => new ResizeObserver(cb));
|
|
@@ -47,14 +52,35 @@ var LayoutObserver = class {
|
|
|
47
52
|
window.removeEventListener("resize", this.#handleViewportResize);
|
|
48
53
|
}
|
|
49
54
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
55
|
+
* Starts reporting a `load` from anywhere inside `container` — an image or a
|
|
56
|
+
* frame settling changes the box it sits in, and it measures as zero high until
|
|
57
|
+
* then. `load` does not bubble, so the subscription is a capture-phase listener
|
|
58
|
+
* on the container itself and nothing the caller spells.
|
|
59
|
+
*
|
|
60
|
+
* **One container at a time.** A further call moves the observation, so a widget
|
|
61
|
+
* whose content element is swapped at runtime releases the element it let go by
|
|
62
|
+
* naming the new one — there is no second place for the release to drift from.
|
|
63
|
+
*/
|
|
64
|
+
observeDescendantLoads(container) {
|
|
65
|
+
this.unobserveDescendantLoads();
|
|
66
|
+
this.#loadContainer = container;
|
|
67
|
+
container.addEventListener("load", this.#handleDescendantLoad, true);
|
|
68
|
+
}
|
|
69
|
+
/** Stops reporting descendant loads without affecting element or viewport observation. */
|
|
70
|
+
unobserveDescendantLoads() {
|
|
71
|
+
this.#loadContainer?.removeEventListener("load", this.#handleDescendantLoad, true);
|
|
72
|
+
this.#loadContainer = null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Releases every observation: disconnects the {@link ResizeObserver} and removes
|
|
76
|
+
* the viewport and descendant-load listeners. Safe to call multiple times. Call
|
|
77
|
+
* this from a controller's `disconnect()`.
|
|
53
78
|
*/
|
|
54
79
|
disconnect() {
|
|
55
80
|
this.#resizeObserver?.disconnect();
|
|
56
81
|
this.#resizeObserver = null;
|
|
57
82
|
this.unobserveViewport();
|
|
83
|
+
this.unobserveDescendantLoads();
|
|
58
84
|
}
|
|
59
85
|
};
|
|
60
86
|
|
|
@@ -55,6 +55,121 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
// src/utils/keyed_timers.ts
|
|
59
|
+
var KeyedTimers = class {
|
|
60
|
+
#timers = new SafeTimeout();
|
|
61
|
+
/** The pending timer of each key; an entry lives exactly as long as its timer. */
|
|
62
|
+
#pending = /* @__PURE__ */ new Map();
|
|
63
|
+
/**
|
|
64
|
+
* Arms `callback` after `delay` ms for `key`, cancelling the timer `key` had
|
|
65
|
+
* pending. The entry is dropped before the callback runs, so the callback sees
|
|
66
|
+
* the key unarmed and may arm it again for the next round.
|
|
67
|
+
*/
|
|
68
|
+
set(key, callback, delay) {
|
|
69
|
+
this.clear(key);
|
|
70
|
+
const id = this.#timers.set(() => {
|
|
71
|
+
this.#pending.delete(key);
|
|
72
|
+
callback();
|
|
73
|
+
}, delay);
|
|
74
|
+
this.#pending.set(key, id);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Cancels `key`'s pending timer, if it has one.
|
|
78
|
+
*
|
|
79
|
+
* A timer id is a positive integer, so `-1` stands for "nothing pending" and
|
|
80
|
+
* the registry ignores an id it does not own — the unarmed case needs no
|
|
81
|
+
* branch of its own, and no other key's timer can be reached from here.
|
|
82
|
+
*/
|
|
83
|
+
clear(key) {
|
|
84
|
+
this.#timers.clear(this.#pending.get(key) ?? -1);
|
|
85
|
+
this.#pending.delete(key);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Cancels every pending timer and forgets every key. Call this from a
|
|
89
|
+
* controller's `disconnect()` so no timer, and no entry, outlives the element.
|
|
90
|
+
*/
|
|
91
|
+
clearAll() {
|
|
92
|
+
this.#timers.clearAll();
|
|
93
|
+
this.#pending.clear();
|
|
94
|
+
}
|
|
95
|
+
/** Whether `key` has a timer pending. */
|
|
96
|
+
has(key) {
|
|
97
|
+
return this.#pending.has(key);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/utils/pausable_timers.ts
|
|
102
|
+
var PausableTimers = class {
|
|
103
|
+
#timers = new KeyedTimers();
|
|
104
|
+
/** Every key armed or held; an entry goes as its timer fires, or with the key. */
|
|
105
|
+
#entries = /* @__PURE__ */ new Map();
|
|
106
|
+
/**
|
|
107
|
+
* Arms `callback` after `delay` ms for `key`, replacing whatever `key` had.
|
|
108
|
+
* A held key stays held and banks `delay` for its resume.
|
|
109
|
+
*/
|
|
110
|
+
set(key, callback, delay) {
|
|
111
|
+
const reasons = this.#entries.get(key)?.reasons ?? /* @__PURE__ */ new Set();
|
|
112
|
+
const entry = { callback, startedAt: 0, remaining: delay, reasons };
|
|
113
|
+
this.#entries.set(key, entry);
|
|
114
|
+
if (reasons.size === 0) this.#arm(key, entry);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Holds `key` for `reason` and banks the time left, with a floor of one
|
|
118
|
+
* millisecond. Reports whether this call is the one that stopped a running
|
|
119
|
+
* timer, so the caller can write its held-state hook exactly once.
|
|
120
|
+
*/
|
|
121
|
+
pause(key, reason) {
|
|
122
|
+
const entry = this.#entries.get(key);
|
|
123
|
+
if (entry === void 0) return false;
|
|
124
|
+
entry.reasons.add(reason);
|
|
125
|
+
if (!this.#timers.has(key)) return false;
|
|
126
|
+
this.#timers.clear(key);
|
|
127
|
+
entry.remaining = Math.max(1, entry.remaining - (Date.now() - entry.startedAt));
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Releases `reason` on `key`, arming the banked time again once no reason is
|
|
132
|
+
* left. Reports whether this call is the one that started the timer again.
|
|
133
|
+
*/
|
|
134
|
+
resume(key, reason) {
|
|
135
|
+
const entry = this.#entries.get(key);
|
|
136
|
+
if (entry === void 0) return false;
|
|
137
|
+
entry.reasons.delete(reason);
|
|
138
|
+
if (entry.reasons.size > 0 || this.#timers.has(key)) return false;
|
|
139
|
+
this.#arm(key, entry);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
/** Whether `key` is armed or held — that is, whether this registry drives it at all. */
|
|
143
|
+
tracks(key) {
|
|
144
|
+
return this.#entries.has(key);
|
|
145
|
+
}
|
|
146
|
+
/** Cancels `key`'s timer and drops its hold. */
|
|
147
|
+
clear(key) {
|
|
148
|
+
this.#timers.clear(key);
|
|
149
|
+
this.#entries.delete(key);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Cancels every timer and forgets every key. Call this from a controller's
|
|
153
|
+
* `disconnect()` so neither a timer nor a hold outlives the element.
|
|
154
|
+
*/
|
|
155
|
+
clearAll() {
|
|
156
|
+
this.#timers.clearAll();
|
|
157
|
+
this.#entries.clear();
|
|
158
|
+
}
|
|
159
|
+
/** Starts `entry`'s banked time running for `key`, and drops it as it fires. */
|
|
160
|
+
#arm(key, entry) {
|
|
161
|
+
entry.startedAt = Date.now();
|
|
162
|
+
this.#timers.set(
|
|
163
|
+
key,
|
|
164
|
+
() => {
|
|
165
|
+
this.#entries.delete(key);
|
|
166
|
+
entry.callback();
|
|
167
|
+
},
|
|
168
|
+
entry.remaining
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
58
173
|
// src/utils/transition_completion.ts
|
|
59
174
|
function timeMs(value) {
|
|
60
175
|
const trimmed = value.trim();
|
|
@@ -99,30 +214,23 @@ var ToastController = class extends Controller {
|
|
|
99
214
|
};
|
|
100
215
|
static actions = ["dismiss", "onKeydown", "pause", "resume", "show"];
|
|
101
216
|
static events = ["dismiss", "show"];
|
|
102
|
-
/**
|
|
103
|
-
* Registry for every auto-dismiss and transition-finalize timer the controller
|
|
104
|
-
* schedules. `SafeTimeout` owns *registration and teardown only*; the
|
|
105
|
-
* pause/resume remaining-time accounting stays in `#activeTimeouts` so the
|
|
106
|
-
* per-widget WCAG 2.2.1 semantics are not flattened into the helper.
|
|
107
|
-
*/
|
|
217
|
+
/** Removal timers for the leaving transition; the auto-dismiss ones live below. */
|
|
108
218
|
#timers = new SafeTimeout();
|
|
219
|
+
/** Per-toast auto-dismiss, held open while the toast is hovered or focused. */
|
|
220
|
+
#dismiss = new PausableTimers();
|
|
109
221
|
/**
|
|
110
222
|
* Pending one-shot `requestAnimationFrame` handles (the entering→visible flip).
|
|
111
223
|
* Tracked so {@link disconnect} can cancel any that have not fired, preventing a
|
|
112
224
|
* detached element from being mutated after it leaves the DOM (Turbo).
|
|
113
225
|
*/
|
|
114
226
|
#rafHandles = /* @__PURE__ */ new Map();
|
|
115
|
-
/** Track active timeouts mapped by each toast element for safe cancellation. */
|
|
116
|
-
#activeTimeouts = /* @__PURE__ */ new Map();
|
|
117
|
-
/** Track active pause reasons (hover/focus) per toast for WCAG 2.2.1 pause/resume. */
|
|
118
|
-
#pauseReasons = /* @__PURE__ */ new Map();
|
|
119
227
|
/** The stable list that owns delegated listeners for dynamically added items. */
|
|
120
228
|
#delegatedList = null;
|
|
121
229
|
connect() {
|
|
122
230
|
this.#connectDelegatedEvents();
|
|
123
231
|
this.enforceMaxLimit();
|
|
124
232
|
for (const item of this.itemTargets) {
|
|
125
|
-
if (!this.#
|
|
233
|
+
if (!this.#dismiss.tracks(item) && item.dataset.state !== "leaving") {
|
|
126
234
|
this.#startTimer(item);
|
|
127
235
|
}
|
|
128
236
|
}
|
|
@@ -130,12 +238,11 @@ var ToastController = class extends Controller {
|
|
|
130
238
|
disconnect() {
|
|
131
239
|
this.#disconnectDelegatedEvents();
|
|
132
240
|
this.#timers.clearAll();
|
|
241
|
+
this.#dismiss.clearAll();
|
|
133
242
|
for (const handle of this.#rafHandles.values()) {
|
|
134
243
|
window.cancelAnimationFrame(handle);
|
|
135
244
|
}
|
|
136
245
|
this.#rafHandles.clear();
|
|
137
|
-
this.#activeTimeouts.clear();
|
|
138
|
-
this.#pauseReasons.clear();
|
|
139
246
|
}
|
|
140
247
|
/** Rebinds delegated interaction when Turbo replaces the list target in place. */
|
|
141
248
|
listTargetConnected(element) {
|
|
@@ -148,18 +255,9 @@ var ToastController = class extends Controller {
|
|
|
148
255
|
durationValueChanged() {
|
|
149
256
|
for (const item of this.itemTargets) {
|
|
150
257
|
if (item.dataset.state === "leaving") continue;
|
|
151
|
-
const pauseReasons = this.#pauseReasons.get(item);
|
|
152
|
-
this.#clearTimer(item);
|
|
153
258
|
if (this.durationValue <= 0) {
|
|
259
|
+
this.#dismiss.clear(item);
|
|
154
260
|
item.removeAttribute("data-paused");
|
|
155
|
-
} else if (pauseReasons && pauseReasons.size > 0) {
|
|
156
|
-
this.#pauseReasons.set(item, pauseReasons);
|
|
157
|
-
this.#activeTimeouts.set(item, {
|
|
158
|
-
id: 0,
|
|
159
|
-
startedAt: 0,
|
|
160
|
-
remaining: this.durationValue
|
|
161
|
-
});
|
|
162
|
-
item.setAttribute("data-paused", "true");
|
|
163
261
|
} else {
|
|
164
262
|
this.#startTimer(item);
|
|
165
263
|
}
|
|
@@ -188,7 +286,7 @@ var ToastController = class extends Controller {
|
|
|
188
286
|
}
|
|
189
287
|
/** Clears any active timer when a toast is removed from the DOM. */
|
|
190
288
|
itemTargetDisconnected(element) {
|
|
191
|
-
this.#
|
|
289
|
+
this.#dismiss.clear(element);
|
|
192
290
|
this.#cancelAnimation(element);
|
|
193
291
|
}
|
|
194
292
|
/**
|
|
@@ -260,35 +358,16 @@ var ToastController = class extends Controller {
|
|
|
260
358
|
*/
|
|
261
359
|
pause(event) {
|
|
262
360
|
const item = this.#itemFromEvent(event);
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
if (!timeout) return;
|
|
266
|
-
const reasons = this.#pauseReasonsFor(item);
|
|
267
|
-
const wasActive = reasons.size > 0;
|
|
268
|
-
reasons.add(this.#pauseReason(event));
|
|
269
|
-
if (wasActive || timeout.id === 0) return;
|
|
270
|
-
this.#timers.clear(timeout.id);
|
|
271
|
-
const elapsed = Date.now() - timeout.startedAt;
|
|
272
|
-
const remaining = Math.max(0, timeout.remaining - elapsed);
|
|
273
|
-
if (remaining <= 0) {
|
|
274
|
-
this.#removeWithTransition(item, "timeout");
|
|
275
|
-
return;
|
|
361
|
+
if (item && this.#dismiss.pause(item, this.#pauseReason(event))) {
|
|
362
|
+
item.setAttribute("data-paused", "true");
|
|
276
363
|
}
|
|
277
|
-
this.#activeTimeouts.set(item, { id: 0, startedAt: 0, remaining });
|
|
278
|
-
item.setAttribute("data-paused", "true");
|
|
279
364
|
}
|
|
280
365
|
/** Resumes the auto-dismiss timer once both hover and focus have been released. */
|
|
281
366
|
resume(event) {
|
|
282
367
|
const item = this.#itemFromEvent(event);
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (reasons.size > 0) return;
|
|
287
|
-
const timeout = this.#activeTimeouts.get(item);
|
|
288
|
-
if (!timeout) return;
|
|
289
|
-
if (timeout.id !== 0 || timeout.remaining <= 0) return;
|
|
290
|
-
item.removeAttribute("data-paused");
|
|
291
|
-
this.#startTimer(item, timeout.remaining);
|
|
368
|
+
if (item && this.#dismiss.resume(item, this.#pauseReason(event))) {
|
|
369
|
+
item.removeAttribute("data-paused");
|
|
370
|
+
}
|
|
292
371
|
}
|
|
293
372
|
/** Resolves the toast item element a pause/resume event targets. */
|
|
294
373
|
#itemFromEvent(event) {
|
|
@@ -301,36 +380,20 @@ var ToastController = class extends Controller {
|
|
|
301
380
|
#pauseReason(event) {
|
|
302
381
|
return event.type === "focusin" || event.type === "focusout" ? "focus" : "hover";
|
|
303
382
|
}
|
|
304
|
-
/**
|
|
305
|
-
#
|
|
306
|
-
|
|
307
|
-
if (!reasons) {
|
|
308
|
-
reasons = /* @__PURE__ */ new Set();
|
|
309
|
-
this.#pauseReasons.set(item, reasons);
|
|
310
|
-
}
|
|
311
|
-
return reasons;
|
|
312
|
-
}
|
|
313
|
-
#startTimer(element, duration = this.durationValue) {
|
|
314
|
-
if (duration <= 0 || element.dataset.state === "leaving" || element.parentNode !== this.listTarget) {
|
|
383
|
+
/** Arms a toast's auto-dismiss; a non-positive `duration` means it never expires. */
|
|
384
|
+
#startTimer(element) {
|
|
385
|
+
if (this.durationValue <= 0 || element.dataset.state === "leaving" || element.parentNode !== this.listTarget) {
|
|
315
386
|
return;
|
|
316
387
|
}
|
|
317
|
-
this.#
|
|
318
|
-
|
|
319
|
-
this.#removeWithTransition(element, "timeout")
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
#clearTimer(element) {
|
|
324
|
-
const timeout = this.#activeTimeouts.get(element);
|
|
325
|
-
if (timeout) {
|
|
326
|
-
if (timeout.id) this.#timers.clear(timeout.id);
|
|
327
|
-
this.#activeTimeouts.delete(element);
|
|
328
|
-
}
|
|
329
|
-
this.#pauseReasons.delete(element);
|
|
388
|
+
this.#dismiss.set(
|
|
389
|
+
element,
|
|
390
|
+
() => this.#removeWithTransition(element, "timeout"),
|
|
391
|
+
this.durationValue
|
|
392
|
+
);
|
|
330
393
|
}
|
|
331
394
|
#removeWithTransition(element, reason) {
|
|
332
395
|
if (element.dataset.state === "leaving" || element.parentNode !== this.listTarget) return;
|
|
333
|
-
this.#
|
|
396
|
+
this.#dismiss.clear(element);
|
|
334
397
|
this.#cancelAnimation(element);
|
|
335
398
|
element.setAttribute("data-state", "leaving");
|
|
336
399
|
const finalize = () => {
|