stimeo-ui 0.16.0 → 0.17.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 +31 -0
- data/README.md +8 -1
- data/dist/cable/index.js +363 -436
- data/dist/controllers/accordion_controller.js +166 -55
- data/dist/controllers/alert_dialog_controller.js +862 -221
- data/dist/controllers/announcer_controller.js +135 -198
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +271 -127
- data/dist/controllers/avatar_controller.js +171 -105
- data/dist/controllers/breadcrumb_controller.js +0 -130
- data/dist/controllers/bulk_select_controller.js +262 -52
- data/dist/controllers/calendar_controller.js +175 -209
- data/dist/controllers/carousel_controller.js +461 -399
- data/dist/controllers/character_counter_controller.js +246 -156
- data/dist/controllers/checkbox_controller.js +32 -74
- data/dist/controllers/clipboard_controller.js +168 -147
- data/dist/controllers/collapsible_controller.js +301 -120
- data/dist/controllers/color_picker_controller.js +149 -139
- data/dist/controllers/combobox_controller.js +360 -133
- data/dist/controllers/command_palette_controller.js +970 -358
- data/dist/controllers/conditional_fields_controller.js +166 -124
- data/dist/controllers/confirm_controller.js +939 -248
- data/dist/controllers/context_menu_controller.js +256 -128
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +183 -148
- data/dist/controllers/currency_input_controller.js +134 -158
- data/dist/controllers/data_grid_controller.js +114 -186
- data/dist/controllers/date_range_picker_controller.js +288 -206
- data/dist/controllers/dialog_controller.js +858 -223
- data/dist/controllers/direct_upload_controller.js +73 -153
- data/dist/controllers/dirty_form_controller.js +14 -48
- data/dist/controllers/dismissible_controller.js +175 -21
- data/dist/controllers/drawer_controller.js +933 -341
- data/dist/controllers/dropdown_controller.js +312 -82
- data/dist/controllers/editable_controller.js +158 -68
- data/dist/controllers/empty_state_controller.js +67 -53
- data/dist/controllers/file_dropzone_controller.js +311 -207
- data/dist/controllers/filter_controller.js +67 -39
- data/dist/controllers/flash_controller.js +172 -314
- data/dist/controllers/focus_controller.js +654 -240
- data/dist/controllers/form_field_controller.js +147 -132
- data/dist/controllers/form_validation_controller.js +13 -86
- data/dist/controllers/frame_loading_controller.js +264 -213
- data/dist/controllers/highlight_controller.js +107 -75
- data/dist/controllers/hover_card_controller.js +309 -118
- data/dist/controllers/idle_controller.js +267 -101
- data/dist/controllers/input_mask_controller.js +71 -72
- data/dist/controllers/intersection_controller.js +147 -106
- data/dist/controllers/lazy_frame_controller.js +9 -65
- data/dist/controllers/listbox_controller.js +306 -204
- data/dist/controllers/local_time_controller.js +54 -64
- data/dist/controllers/masonry_controller.js +70 -93
- data/dist/controllers/menu_controller.js +214 -156
- data/dist/controllers/menubar_controller.js +170 -329
- data/dist/controllers/meter_controller.js +151 -67
- data/dist/controllers/multi_select_controller.js +593 -358
- data/dist/controllers/navigation_menu_controller.js +124 -232
- data/dist/controllers/nested_form_controller.js +352 -182
- data/dist/controllers/network_status_controller.js +242 -54
- data/dist/controllers/number_input_controller.js +320 -263
- data/dist/controllers/optimistic_controller.js +16 -76
- data/dist/controllers/otp_controller.js +198 -193
- data/dist/controllers/overflow_indicator_controller.js +116 -113
- data/dist/controllers/overflow_menu_controller.js +343 -283
- data/dist/controllers/pagination_controller.js +295 -119
- data/dist/controllers/password_reveal_controller.js +389 -121
- data/dist/controllers/password_strength_controller.js +99 -153
- data/dist/controllers/persist_controller.js +237 -103
- data/dist/controllers/pointer_drag_controller.js +185 -165
- data/dist/controllers/popover_controller.js +236 -97
- data/dist/controllers/portal_controller.js +209 -76
- data/dist/controllers/preview_guard_controller.js +232 -116
- data/dist/controllers/progress_controller.js +125 -56
- data/dist/controllers/radio_group_controller.js +117 -137
- data/dist/controllers/range_slider_controller.js +192 -105
- data/dist/controllers/rating_controller.js +418 -207
- data/dist/controllers/read_more_controller.js +134 -63
- data/dist/controllers/reading_progress_controller.js +147 -135
- data/dist/controllers/relative_time_controller.js +145 -110
- data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
- data/dist/controllers/resizable_controller.js +255 -139
- data/dist/controllers/roving_controller.js +39 -84
- data/dist/controllers/scroll_area_controller.js +428 -225
- data/dist/controllers/scroll_restore_controller.js +0 -53
- data/dist/controllers/scroll_visibility_controller.js +395 -145
- data/dist/controllers/scrollspy_controller.js +181 -242
- data/dist/controllers/separator_controller.js +267 -140
- data/dist/controllers/sidebar_controller.js +1023 -386
- data/dist/controllers/skeleton_controller.js +248 -97
- data/dist/controllers/slider_controller.js +159 -79
- data/dist/controllers/smart_sticky_header_controller.js +162 -63
- data/dist/controllers/sortable_controller.js +0 -112
- data/dist/controllers/spinner_controller.js +277 -169
- data/dist/controllers/step_indicator_controller.js +99 -46
- data/dist/controllers/stepper_controller.js +114 -73
- data/dist/controllers/stick_to_bottom_controller.js +262 -138
- data/dist/controllers/sticky_observer_controller.js +214 -49
- data/dist/controllers/submit_once_controller.js +294 -205
- data/dist/controllers/switch_controller.js +33 -60
- data/dist/controllers/tabs_controller.js +35 -29
- data/dist/controllers/tags_input_controller.js +136 -145
- data/dist/controllers/textarea_autosize_controller.js +71 -58
- data/dist/controllers/theme_controller.js +130 -112
- data/dist/controllers/time_picker_controller.js +146 -80
- data/dist/controllers/toast_controller.js +186 -297
- data/dist/controllers/toggle_group_controller.js +95 -136
- data/dist/controllers/toolbar_controller.js +68 -98
- data/dist/controllers/tooltip_controller.js +158 -124
- data/dist/controllers/transition_controller.js +123 -119
- data/dist/controllers/tree_view_controller.js +150 -345
- data/dist/index.js +6370 -9346
- data/dist/positioning/index.js +148 -47
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +3 -3
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function sharedRegistry(key, create) {
|
|
4
|
+
const symbol = Symbol.for(key);
|
|
5
|
+
const scope = globalThis;
|
|
6
|
+
const existing = scope[symbol];
|
|
7
|
+
if (existing !== void 0) return existing;
|
|
8
|
+
const registry = create();
|
|
9
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
10
|
+
return registry;
|
|
11
|
+
}
|
|
4
12
|
|
|
5
|
-
// src/utils/escape_layer.ts
|
|
6
13
|
var EscapeLayer = class _EscapeLayer {
|
|
7
|
-
static #registries =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
#claims = null;
|
|
13
|
-
/**
|
|
14
|
-
* Activates this layer at the top of its document's Escape stack, installing
|
|
15
|
-
* the document's shared resolver listener if this is its first layer.
|
|
16
|
-
* Re-activating an already-active layer moves it to the top.
|
|
17
|
-
*/
|
|
14
|
+
static #registries = sharedRegistry(
|
|
15
|
+
"stimeo-ui.escape-layer.registry.v1",
|
|
16
|
+
() => /* @__PURE__ */ new WeakMap()
|
|
17
|
+
);
|
|
18
|
+
#active = null;
|
|
18
19
|
activate(ownerDocument = document, options) {
|
|
19
20
|
this.deactivate();
|
|
20
21
|
let registry = _EscapeLayer.#registries.get(ownerDocument);
|
|
@@ -23,44 +24,31 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
23
24
|
_EscapeLayer.#registries.set(ownerDocument, registry);
|
|
24
25
|
ownerDocument.addEventListener("keydown", registry.onKeydown);
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.#
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Removes this layer from its document's Escape stack, uninstalling the
|
|
33
|
-
* shared listener when the stack empties. Safe to call when inactive.
|
|
34
|
-
*/
|
|
27
|
+
const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
|
|
28
|
+
registry.stack.push(entry);
|
|
29
|
+
this.#active = { document: ownerDocument, entry };
|
|
30
|
+
}
|
|
35
31
|
deactivate() {
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
38
|
-
const registry = _EscapeLayer.#registries.get(
|
|
32
|
+
const active = this.#active;
|
|
33
|
+
if (!active) return;
|
|
34
|
+
const registry = _EscapeLayer.#registries.get(active.document);
|
|
39
35
|
if (registry) {
|
|
40
|
-
const index = registry.stack.lastIndexOf(
|
|
36
|
+
const index = registry.stack.lastIndexOf(active.entry);
|
|
41
37
|
if (index >= 0) registry.stack.splice(index, 1);
|
|
42
38
|
if (registry.stack.length === 0) {
|
|
43
|
-
|
|
44
|
-
_EscapeLayer.#registries.delete(
|
|
39
|
+
active.document.removeEventListener("keydown", registry.onKeydown);
|
|
40
|
+
_EscapeLayer.#registries.delete(active.document);
|
|
45
41
|
}
|
|
46
42
|
}
|
|
47
|
-
this.#
|
|
48
|
-
|
|
49
|
-
this.#claims = null;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Whether this active layer would own a press right now: it is the topmost
|
|
53
|
-
* layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests
|
|
54
|
-
* and diagnostics — production dismissal goes through the shared listener.
|
|
55
|
-
*/
|
|
43
|
+
this.#active = null;
|
|
44
|
+
}
|
|
56
45
|
get ownsEscape() {
|
|
57
|
-
const
|
|
58
|
-
if (!
|
|
59
|
-
const registry = _EscapeLayer.#registries.get(
|
|
46
|
+
const active = this.#active;
|
|
47
|
+
if (!active) return false;
|
|
48
|
+
const registry = _EscapeLayer.#registries.get(active.document);
|
|
60
49
|
if (!registry) return false;
|
|
61
|
-
return _EscapeLayer.#resolveOwner(registry.stack) ===
|
|
50
|
+
return _EscapeLayer.#resolveOwner(registry.stack) === active.entry;
|
|
62
51
|
}
|
|
63
|
-
/** Builds a document's registry with its shared resolver listener. */
|
|
64
52
|
static #createRegistry() {
|
|
65
53
|
const registry = {
|
|
66
54
|
stack: [],
|
|
@@ -69,60 +57,86 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
69
57
|
const owner = _EscapeLayer.#resolveOwner(registry.stack);
|
|
70
58
|
if (!owner) return;
|
|
71
59
|
event.preventDefault();
|
|
72
|
-
owner
|
|
60
|
+
owner.onDismiss();
|
|
73
61
|
}
|
|
74
62
|
};
|
|
75
63
|
return registry;
|
|
76
64
|
}
|
|
77
|
-
/** The topmost stack layer whose claims predicate passes, or `null`. */
|
|
78
65
|
static #resolveOwner(stack) {
|
|
79
66
|
for (let index = stack.length - 1; index >= 0; index--) {
|
|
80
67
|
const layer = stack[index];
|
|
81
68
|
if (!layer) continue;
|
|
82
|
-
if (layer
|
|
69
|
+
if (layer.claims && !layer.claims()) continue;
|
|
83
70
|
return layer;
|
|
84
71
|
}
|
|
85
72
|
return null;
|
|
86
73
|
}
|
|
87
74
|
};
|
|
88
75
|
|
|
89
|
-
|
|
76
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
77
|
+
|
|
78
|
+
var NUMBER_BOUNDS = {
|
|
79
|
+
timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
|
|
80
|
+
function matchesNumberBounds(value, bounds) {
|
|
81
|
+
if (!Number.isFinite(value)) {
|
|
82
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
83
|
+
if (direction === null) return false;
|
|
84
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
85
|
+
}
|
|
86
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
87
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
88
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
89
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
90
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readNumber(raw, fallback, bounds) {
|
|
95
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var NumberValueReader = class {
|
|
99
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
100
|
+
read(owner, name, raw, fallback, bounds) {
|
|
101
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
102
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
103
|
+
this.#lastRejected.delete(name);
|
|
104
|
+
return resolved;
|
|
105
|
+
}
|
|
106
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
107
|
+
const literal = owner.element.getAttribute(attribute);
|
|
108
|
+
if (literal === null) {
|
|
109
|
+
this.#lastRejected.delete(name);
|
|
110
|
+
return resolved;
|
|
111
|
+
}
|
|
112
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
113
|
+
this.#lastRejected.set(name, literal);
|
|
114
|
+
console.warn(
|
|
115
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return resolved;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
90
122
|
var TimerRegistry = class {
|
|
91
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
92
123
|
ids = /* @__PURE__ */ new Set();
|
|
93
|
-
/**
|
|
94
|
-
* Cancels a single tracked timer.
|
|
95
|
-
*
|
|
96
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
97
|
-
* registry), so callers can clear defensively without guarding.
|
|
98
|
-
*/
|
|
99
124
|
clear(id) {
|
|
100
125
|
if (this.ids.delete(id)) {
|
|
101
126
|
this.cancel(id);
|
|
102
127
|
}
|
|
103
128
|
}
|
|
104
|
-
/**
|
|
105
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
106
|
-
* to guarantee no timer outlives the element.
|
|
107
|
-
*/
|
|
108
129
|
clearAll() {
|
|
109
130
|
for (const id of this.ids) {
|
|
110
131
|
this.cancel(id);
|
|
111
132
|
}
|
|
112
133
|
this.ids.clear();
|
|
113
134
|
}
|
|
114
|
-
/** Number of timers currently tracked (pending). */
|
|
115
135
|
get size() {
|
|
116
136
|
return this.ids.size;
|
|
117
137
|
}
|
|
118
138
|
};
|
|
119
139
|
var SafeTimeout = class extends TimerRegistry {
|
|
120
|
-
/**
|
|
121
|
-
* Schedules `callback` after `delay` ms and returns the timer id.
|
|
122
|
-
*
|
|
123
|
-
* The id is removed from the registry automatically when the timeout fires,
|
|
124
|
-
* so {@link TimerRegistry.size | size} reflects only still-pending timers.
|
|
125
|
-
*/
|
|
126
140
|
set(callback, delay) {
|
|
127
141
|
const id = this.schedule(() => {
|
|
128
142
|
this.ids.delete(id);
|
|
@@ -139,7 +153,6 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
139
153
|
}
|
|
140
154
|
};
|
|
141
155
|
|
|
142
|
-
// src/utils/scroll_dismiss.ts
|
|
143
156
|
function observeScrollDismiss(element, onScroll) {
|
|
144
157
|
const targets = [...scrollParents(element), window];
|
|
145
158
|
const handler = () => onScroll();
|
|
@@ -164,7 +177,6 @@ function isScrollable(element) {
|
|
|
164
177
|
return /auto|scroll|overlay/.test(style.overflow + style.overflowX + style.overflowY);
|
|
165
178
|
}
|
|
166
179
|
|
|
167
|
-
// src/utils/state_reason.ts
|
|
168
180
|
var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
|
|
169
181
|
var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
|
|
170
182
|
function stateReasonFor(event) {
|
|
@@ -174,123 +186,126 @@ function stateReasonFor(event) {
|
|
|
174
186
|
return "user";
|
|
175
187
|
}
|
|
176
188
|
|
|
177
|
-
|
|
178
|
-
|
|
189
|
+
var TooltipController = class _TooltipController extends Controller {
|
|
190
|
+
#numbers = new NumberValueReader();
|
|
179
191
|
static targets = ["trigger", "content"];
|
|
180
192
|
static values = {
|
|
181
193
|
showDelay: { type: Number, default: 0 },
|
|
182
194
|
hideDelay: { type: Number, default: 0 },
|
|
183
195
|
closeOnScroll: { type: Boolean, default: false }
|
|
184
196
|
};
|
|
197
|
+
static valueConstraints = {
|
|
198
|
+
showDelay: NUMBER_BOUNDS.timer,
|
|
199
|
+
hideDelay: NUMBER_BOUNDS.timer
|
|
200
|
+
};
|
|
185
201
|
static actions = ["hide", "show"];
|
|
186
202
|
static events = ["close", "open"];
|
|
187
|
-
/** Registry whose pending timers are cancelled individually with their guard ids. */
|
|
188
203
|
#timers = new SafeTimeout();
|
|
189
|
-
/** Escape-stack membership while shown; the shared resolver dismisses via it. */
|
|
190
204
|
#escapeLayer = new EscapeLayer();
|
|
191
|
-
/** The id of the currently pending show timer, if any. */
|
|
192
205
|
#pendingShow = null;
|
|
193
|
-
/** The id of the currently pending hide timer, if any. */
|
|
194
206
|
#pendingHide = null;
|
|
195
|
-
/** Whether focus or the pointer currently requires the tooltip to persist. */
|
|
196
207
|
#focusActive = false;
|
|
197
208
|
#pointerActive = false;
|
|
198
|
-
/** Cleanup for the dismiss-on-scroll listeners while shown, or `null`. */
|
|
199
209
|
#stopScrollDismiss = null;
|
|
200
|
-
|
|
210
|
+
#shownContent = null;
|
|
201
211
|
#reporting = false;
|
|
202
|
-
/** Starts hidden with no stale timer or interaction state from a prior connection. */
|
|
203
212
|
connect() {
|
|
204
|
-
this.#
|
|
205
|
-
this.#cancelHide();
|
|
213
|
+
this.#cancelTimers();
|
|
206
214
|
this.#resetInteractionState();
|
|
207
215
|
this.#conceal("api");
|
|
208
216
|
this.#reporting = true;
|
|
209
217
|
}
|
|
210
|
-
/** Clears timers, the Escape-stack membership, and scroll listeners so nothing outlives the element. */
|
|
211
218
|
disconnect() {
|
|
212
219
|
this.#reporting = false;
|
|
213
|
-
this.#
|
|
214
|
-
this.#
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
this.#
|
|
218
|
-
|
|
220
|
+
this.#cancelTimers();
|
|
221
|
+
this.#release();
|
|
222
|
+
}
|
|
223
|
+
closeOnScrollValueChanged() {
|
|
224
|
+
if (this.#shownContent) this.#syncScrollDismiss();
|
|
225
|
+
}
|
|
226
|
+
contentTargetDisconnected(content) {
|
|
227
|
+
if (content !== this.#shownContent) return;
|
|
228
|
+
if (this.contentTargets.includes(content)) return;
|
|
229
|
+
this.#cancelTimers();
|
|
230
|
+
this.#release();
|
|
231
|
+
content.hidden = true;
|
|
232
|
+
content.setAttribute("data-state", "closed");
|
|
233
|
+
this.#reflectHidden();
|
|
219
234
|
}
|
|
220
|
-
/** Shows after `showDelay`, recording the focus/pointer reason supplied by an action event. */
|
|
221
235
|
show(event) {
|
|
222
236
|
const reason = stateReasonFor(event);
|
|
223
237
|
this.#activateInteraction(event);
|
|
224
238
|
this.#cancelHide();
|
|
225
239
|
if (this.#isVisible || this.#pendingShow !== null) return;
|
|
226
|
-
if (this
|
|
240
|
+
if (this.#safeShowDelay <= 0) {
|
|
227
241
|
this.#reveal(reason);
|
|
228
242
|
return;
|
|
229
243
|
}
|
|
230
244
|
this.#pendingShow = this.#timers.set(() => {
|
|
231
245
|
this.#pendingShow = null;
|
|
232
246
|
this.#reveal(reason);
|
|
233
|
-
}, this
|
|
247
|
+
}, this.#safeShowDelay);
|
|
234
248
|
}
|
|
235
|
-
/** Hides after `hideDelay` once no focus/pointer reason remains; eventless calls are explicit. */
|
|
236
249
|
hide(event) {
|
|
237
250
|
const reason = stateReasonFor(event);
|
|
238
251
|
const interactionEnded = this.#deactivateInteraction(event);
|
|
239
|
-
if (interactionEnded && this.#hasActiveInteraction)
|
|
240
|
-
this.#cancelHide();
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
252
|
+
if (interactionEnded && this.#hasActiveInteraction) return;
|
|
243
253
|
this.#cancelShow();
|
|
244
|
-
if (!this.#
|
|
245
|
-
if (this
|
|
254
|
+
if (!this.#hidable || this.#pendingHide !== null) return;
|
|
255
|
+
if (this.#safeHideDelay <= 0) {
|
|
246
256
|
this.#conceal(reason);
|
|
247
257
|
return;
|
|
248
258
|
}
|
|
249
259
|
this.#pendingHide = this.#timers.set(() => {
|
|
250
260
|
this.#pendingHide = null;
|
|
251
261
|
this.#conceal(reason);
|
|
252
|
-
}, this
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Reveals the content, reports a move, and joins the Escape stack / scroll watcher.
|
|
256
|
-
*
|
|
257
|
-
* @stimeoRuntimeOnly `closeOnScroll` decides whether this reveal wires the scroll dismissal; what
|
|
258
|
-
* is shown does not depend on it.
|
|
259
|
-
*/
|
|
262
|
+
}, this.#safeHideDelay);
|
|
263
|
+
}
|
|
260
264
|
#reveal(reason) {
|
|
261
265
|
if (!this.hasContentTarget) return;
|
|
266
|
+
const content = this.contentTarget;
|
|
262
267
|
const was = this.#isVisible;
|
|
263
|
-
|
|
264
|
-
|
|
268
|
+
content.hidden = false;
|
|
269
|
+
content.setAttribute("data-state", "open");
|
|
270
|
+
this.#shownContent = content;
|
|
265
271
|
if (!was && this.#reporting) this.dispatch("open", { detail: { reason }, cancelable: false });
|
|
272
|
+
if (this.#shownContent !== content) return;
|
|
266
273
|
this.#escapeLayer.activate(document, { onDismiss: () => this.#dismiss("escape") });
|
|
267
|
-
|
|
268
|
-
this.#stopScrollDismiss = observeScrollDismiss(this.element, () => this.#dismiss("scroll"));
|
|
269
|
-
}
|
|
274
|
+
this.#syncScrollDismiss();
|
|
270
275
|
}
|
|
271
|
-
/** Hides the content, reports a move, and leaves the Escape stack / scroll watcher. */
|
|
272
276
|
#conceal(reason) {
|
|
273
277
|
const was = this.#isVisible;
|
|
274
|
-
this.#
|
|
275
|
-
this.#
|
|
276
|
-
this.#
|
|
278
|
+
this.#release();
|
|
279
|
+
this.#reflectHidden();
|
|
280
|
+
if (was && this.#reporting) this.dispatch("close", { detail: { reason }, cancelable: false });
|
|
281
|
+
}
|
|
282
|
+
#reflectHidden() {
|
|
277
283
|
if (!this.hasContentTarget) return;
|
|
278
284
|
this.contentTarget.hidden = true;
|
|
279
285
|
this.contentTarget.setAttribute("data-state", "closed");
|
|
280
|
-
if (was && this.#reporting) this.dispatch("close", { detail: { reason }, cancelable: false });
|
|
281
286
|
}
|
|
282
|
-
|
|
287
|
+
#release() {
|
|
288
|
+
this.#shownContent = null;
|
|
289
|
+
this.#escapeLayer.deactivate();
|
|
290
|
+
this.#stopScrollDismiss?.();
|
|
291
|
+
this.#stopScrollDismiss = null;
|
|
292
|
+
}
|
|
293
|
+
#syncScrollDismiss() {
|
|
294
|
+
if (this.closeOnScrollValue) {
|
|
295
|
+
this.#stopScrollDismiss ??= observeScrollDismiss(this.element, () => this.#dismiss("scroll"));
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
this.#stopScrollDismiss?.();
|
|
299
|
+
this.#stopScrollDismiss = null;
|
|
300
|
+
}
|
|
283
301
|
#dismiss(reason) {
|
|
284
|
-
this.#
|
|
285
|
-
this.#cancelHide();
|
|
302
|
+
this.#cancelTimers();
|
|
286
303
|
this.#conceal(reason);
|
|
287
304
|
}
|
|
288
|
-
/** Records the modality whose enter/focus event requires the tooltip to stay visible. */
|
|
289
305
|
#activateInteraction(event) {
|
|
290
306
|
if (event?.type === "mouseenter") this.#pointerActive = true;
|
|
291
307
|
if (event?.type === "focusin") this.#focusActive = true;
|
|
292
308
|
}
|
|
293
|
-
/** Clears a modality on leave/blur and reports whether the event represented such a change. */
|
|
294
309
|
#deactivateInteraction(event) {
|
|
295
310
|
if (event?.type === "mouseleave") {
|
|
296
311
|
this.#pointerActive = false;
|
|
@@ -302,35 +317,54 @@ var TooltipController = class extends Controller {
|
|
|
302
317
|
}
|
|
303
318
|
return false;
|
|
304
319
|
}
|
|
305
|
-
/** Discards interaction reasons at a lifecycle boundary. */
|
|
306
320
|
#resetInteractionState() {
|
|
307
321
|
this.#focusActive = false;
|
|
308
322
|
this.#pointerActive = false;
|
|
309
323
|
}
|
|
310
|
-
/** Whether focus or pointer presence still requires a persistent tooltip. */
|
|
311
324
|
get #hasActiveInteraction() {
|
|
312
325
|
return this.#focusActive || this.#pointerActive;
|
|
313
326
|
}
|
|
314
|
-
|
|
327
|
+
#cancelTimers() {
|
|
328
|
+
this.#cancelShow();
|
|
329
|
+
this.#cancelHide();
|
|
330
|
+
}
|
|
315
331
|
#cancelShow() {
|
|
316
332
|
if (this.#pendingShow !== null) {
|
|
317
333
|
this.#timers.clear(this.#pendingShow);
|
|
318
334
|
this.#pendingShow = null;
|
|
319
335
|
}
|
|
320
336
|
}
|
|
321
|
-
/** Cancels any pending hide timer. */
|
|
322
337
|
#cancelHide() {
|
|
323
338
|
if (this.#pendingHide !== null) {
|
|
324
339
|
this.#timers.clear(this.#pendingHide);
|
|
325
340
|
this.#pendingHide = null;
|
|
326
341
|
}
|
|
327
342
|
}
|
|
328
|
-
/** Whether the tooltip is currently shown. */
|
|
329
343
|
get #isVisible() {
|
|
330
344
|
return this.hasContentTarget && !this.contentTarget.hidden;
|
|
331
345
|
}
|
|
346
|
+
get #hidable() {
|
|
347
|
+
return this.#isVisible || this.#shownContent !== null;
|
|
348
|
+
}
|
|
349
|
+
get #safeShowDelay() {
|
|
350
|
+
return this.#numbers.read(
|
|
351
|
+
this,
|
|
352
|
+
"showDelay",
|
|
353
|
+
this.showDelayValue,
|
|
354
|
+
_TooltipController.values.showDelay.default,
|
|
355
|
+
_TooltipController.valueConstraints.showDelay
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
get #safeHideDelay() {
|
|
359
|
+
return this.#numbers.read(
|
|
360
|
+
this,
|
|
361
|
+
"hideDelay",
|
|
362
|
+
this.hideDelayValue,
|
|
363
|
+
_TooltipController.values.hideDelay.default,
|
|
364
|
+
_TooltipController.valueConstraints.hideDelay
|
|
365
|
+
);
|
|
366
|
+
}
|
|
332
367
|
};
|
|
333
368
|
|
|
334
369
|
export { TooltipController };
|
|
335
|
-
//# sourceMappingURL=tooltip_controller.js.map
|
|
336
370
|
//# sourceMappingURL=tooltip_controller.js.map
|