sheer-ui 0.4.0 → 0.4.3
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/package.json +1 -1
- package/src/lib/components/accordion/components/accordion-item.svelte +3 -3
- package/src/lib/components/accordion/components/accordion.svelte +29 -13
- package/src/lib/components/accordion/types.ts +3 -3
- package/src/lib/components/avatar/avatar.svelte.ts +34 -37
- package/src/lib/components/avatar/components/avatar-fallback.svelte +9 -6
- package/src/lib/components/avatar/components/avatar-image.svelte +18 -9
- package/src/lib/components/avatar/components/avatar.svelte +20 -16
- package/src/lib/components/checkbox/checkbox.svelte.ts +45 -55
- package/src/lib/components/checkbox/components/checkbox-group-label.svelte +9 -6
- package/src/lib/components/checkbox/components/checkbox-group.svelte +25 -14
- package/src/lib/components/checkbox/components/checkbox.svelte +39 -21
- package/src/lib/components/combobox/components/combobox.svelte +2 -2
- package/src/lib/components/combobox/select/components/select.svelte +2 -2
- package/src/lib/components/context-menu/components/context-menu-trigger.svelte +1 -1
- package/src/lib/components/date-field/date-field.svelte.ts +2 -3
- package/src/lib/components/dialog/components/dialog-close.svelte +13 -8
- package/src/lib/components/dialog/components/dialog-content-headless.svelte +14 -11
- package/src/lib/components/dialog/components/dialog-description.svelte +9 -6
- package/src/lib/components/dialog/components/dialog-overlay.svelte +10 -7
- package/src/lib/components/dialog/components/dialog-title.svelte +12 -7
- package/src/lib/components/dialog/components/dialog-trigger.svelte +12 -7
- package/src/lib/components/dialog/components/dialog.svelte +10 -4
- package/src/lib/components/dialog/components/modal-surface.svelte +11 -8
- package/src/lib/components/dialog/dialog.svelte.ts +73 -61
- package/src/lib/components/drawer/drawer.svelte +8 -3
- package/src/lib/components/link-preview/components/link-preview-content.svelte +15 -8
- package/src/lib/components/link-preview/components/link-preview-trigger.svelte +9 -6
- package/src/lib/components/link-preview/components/link-preview.svelte +19 -11
- package/src/lib/components/link-preview/link-preview.svelte.ts +42 -43
- package/src/lib/components/menu/components/menu-checkbox-group.svelte +6 -4
- package/src/lib/components/menu/components/menu-checkbox-item.svelte +11 -7
- package/src/lib/components/menu/menu.svelte.ts +29 -19
- package/src/lib/components/menubar/menubar.svelte.ts +7 -6
- package/src/lib/components/meter/components/meter.svelte +18 -9
- package/src/lib/components/meter/meter.svelte.ts +15 -18
- package/src/lib/components/navigation-menu/components/navigation-menu-content.svelte +1 -1
- package/src/lib/components/navigation-menu/components/navigation-menu-indicator.svelte +2 -2
- package/src/lib/components/navigation-menu/components/navigation-menu-trigger.svelte +1 -1
- package/src/lib/components/navigation-menu/navigation-menu.svelte.ts +57 -60
- package/src/lib/components/pagination/components/pagination-next-button.svelte +12 -7
- package/src/lib/components/pagination/components/pagination-page.svelte +15 -8
- package/src/lib/components/pagination/components/pagination-prev-button.svelte +12 -7
- package/src/lib/components/pagination/components/pagination.svelte +31 -18
- package/src/lib/components/pagination/pagination.svelte.ts +48 -64
- package/src/lib/components/pin-input/pin-input.svelte.ts +7 -8
- package/src/lib/components/pin-input/usePasswordManager.svelte.ts +2 -2
- package/src/lib/components/popover/components/popover-close.svelte +9 -6
- package/src/lib/components/popover/components/popover-content.svelte +12 -7
- package/src/lib/components/popover/components/popover-trigger.svelte +21 -10
- package/src/lib/components/popover/components/popover.svelte +9 -8
- package/src/lib/components/popover/popover.svelte.ts +68 -74
- package/src/lib/components/progress/components/progress.svelte +18 -9
- package/src/lib/components/progress/progress.svelte.ts +14 -17
- package/src/lib/components/scroll-area/scroll-area.svelte.ts +9 -10
- package/src/lib/components/separator/components/separator.svelte +15 -8
- package/src/lib/components/separator/separator.svelte.ts +12 -15
- package/src/lib/components/sidebar/sidebar-mobile-surface.svelte +8 -4
- package/src/lib/components/slider/components/slider.svelte +2 -1
- package/src/lib/components/tabs/components/tabs-content.svelte +12 -7
- package/src/lib/components/tabs/components/tabs-list.svelte +9 -6
- package/src/lib/components/tabs/components/tabs-trigger.svelte +15 -8
- package/src/lib/components/tabs/components/tabs.svelte +28 -17
- package/src/lib/components/tabs/tabs.svelte.ts +51 -67
- package/src/lib/components/time-field/time-field.svelte.ts +2 -2
- package/src/lib/components/toggle/components/toggle.svelte +19 -14
- package/src/lib/components/toggle/toggle.svelte.ts +15 -20
- package/src/lib/components/toggle-group/components/toggle-group-item.svelte +15 -8
- package/src/lib/components/toggle-group/components/toggle-group.svelte +28 -16
- package/src/lib/components/toggle-group/toggle-group.svelte.ts +33 -35
- package/src/lib/components/toolbar/components/toolbar-group-item.svelte +15 -8
- package/src/lib/components/toolbar/components/toolbar-group.svelte +19 -14
- package/src/lib/components/toolbar/toolbar.svelte.ts +28 -30
- package/src/lib/components/tooltip/components/tooltip-content.svelte +15 -8
- package/src/lib/components/tooltip/components/tooltip-provider.svelte +18 -7
- package/src/lib/components/tooltip/components/tooltip-trigger.svelte +21 -10
- package/src/lib/components/tooltip/components/tooltip.svelte +34 -21
- package/src/lib/components/tooltip/tooltip.svelte.ts +101 -125
- package/src/lib/internal/dismissible-layer/use-dismissable-layer.svelte.ts +3 -4
- package/src/lib/internal/escape-layer/use-escape-layer.svelte.ts +13 -29
- package/src/lib/internal/floating-layer/use-floating-layer.svelte.ts +26 -30
- package/src/lib/internal/group-value.svelte.ts +11 -10
- package/src/lib/internal/native-popover.svelte.ts +6 -6
- package/src/lib/internal/{roving-focus-group.ts → roving-focus-group.svelte.ts} +17 -19
- package/src/lib/internal/roving-focus-item.svelte.ts +12 -17
- package/src/lib/internal/selection.svelte.ts +55 -41
- package/src/lib/internal/{state-machine.ts → state-machine.svelte.ts} +6 -8
- package/src/lib/internal/timeout-fn.svelte.ts +6 -4
- package/src/lib/internal/tools/box.svelte.ts +5 -114
- package/src/lib/internal/tools/index.ts +1 -11
- package/src/lib/internal/tools/types.ts +0 -3
- package/src/lib/internal/typeahead.svelte.ts +9 -13
- package/src/lib/internal/types.ts +6 -0
- package/src/lib/internal/vaul/use-drawer-content.svelte.ts +2 -2
- package/src/lib/internal/vaul/use-drawer-root.svelte.ts +12 -7
- package/src/lib/components/accordion/accordion.svelte.ts +0 -43
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { attachRef, DOMContext
|
|
1
|
+
import { attachRef, DOMContext } from '../../internal/tools/index.js';
|
|
2
2
|
import { on } from 'svelte/events';
|
|
3
3
|
import { createContext, onMount, untrack } from 'svelte';
|
|
4
4
|
import { isElement } from '../../internal/tools/utils/dom.js';
|
|
5
5
|
import { createBitsAttrs, boolToEmptyStrOrUndef } from '../../internal/attrs.js';
|
|
6
|
-
import type { OnChangeFn, RefAttachment,
|
|
6
|
+
import type { OnChangeFn, RefAttachment, RefOpts } from '../../internal/types.js';
|
|
7
7
|
import type { FocusEventHandler, MouseEventHandler, PointerEventHandler } from 'svelte/elements';
|
|
8
8
|
import { createEffectTimeout } from '../../internal/timeout-fn.svelte.js';
|
|
9
9
|
import { SafePolygon } from '../../internal/safe-polygon.svelte.js';
|
|
@@ -122,7 +122,7 @@ export class TooltipTether<Payload = never> {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
get isOpen() {
|
|
125
|
-
return this.#state.root?.opts.open
|
|
125
|
+
return this.#state.root?.opts.open ?? false;
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -130,14 +130,14 @@ export function createTooltipTether<Payload = never>() {
|
|
|
130
130
|
return new TooltipTether<Payload>();
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
interface TooltipProviderStateOpts
|
|
134
|
-
delayDuration: number;
|
|
135
|
-
disableHoverableContent: boolean;
|
|
136
|
-
disableCloseOnTriggerClick: boolean;
|
|
137
|
-
disabled: boolean;
|
|
138
|
-
ignoreNonKeyboardFocus: boolean;
|
|
139
|
-
skipDelayDuration: number;
|
|
140
|
-
}
|
|
133
|
+
interface TooltipProviderStateOpts {
|
|
134
|
+
readonly delayDuration: number;
|
|
135
|
+
readonly disableHoverableContent: boolean;
|
|
136
|
+
readonly disableCloseOnTriggerClick: boolean;
|
|
137
|
+
readonly disabled: boolean;
|
|
138
|
+
readonly ignoreNonKeyboardFocus: boolean;
|
|
139
|
+
readonly skipDelayDuration: number;
|
|
140
|
+
}
|
|
141
141
|
|
|
142
142
|
export class TooltipProviderState {
|
|
143
143
|
static create(opts: TooltipProviderStateOpts) {
|
|
@@ -145,7 +145,6 @@ export class TooltipProviderState {
|
|
|
145
145
|
}
|
|
146
146
|
readonly opts: TooltipProviderStateOpts;
|
|
147
147
|
isOpenDelayed = $state<boolean>(true);
|
|
148
|
-
isPointerInTransit = simpleBox(false);
|
|
149
148
|
readonly #timerFn: ReturnType<typeof createEffectTimeout<() => void>>;
|
|
150
149
|
#openTooltip = $state<TooltipRootState | null>(null);
|
|
151
150
|
|
|
@@ -155,7 +154,7 @@ export class TooltipProviderState {
|
|
|
155
154
|
() => {
|
|
156
155
|
this.isOpenDelayed = true;
|
|
157
156
|
},
|
|
158
|
-
() => this.opts.skipDelayDuration
|
|
157
|
+
() => this.opts.skipDelayDuration,
|
|
159
158
|
);
|
|
160
159
|
|
|
161
160
|
onMount(() =>
|
|
@@ -176,7 +175,7 @@ export class TooltipProviderState {
|
|
|
176
175
|
}
|
|
177
176
|
|
|
178
177
|
#startTimer = () => {
|
|
179
|
-
const skipDuration = this.opts.skipDelayDuration
|
|
178
|
+
const skipDuration = this.opts.skipDelayDuration;
|
|
180
179
|
|
|
181
180
|
if (skipDuration === 0) {
|
|
182
181
|
// no grace period — reset immediately so next trigger waits the full delay
|
|
@@ -213,21 +212,17 @@ export class TooltipProviderState {
|
|
|
213
212
|
};
|
|
214
213
|
}
|
|
215
214
|
|
|
216
|
-
interface TooltipRootStateOpts
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
WritableBoxedValues<{
|
|
228
|
-
open: boolean;
|
|
229
|
-
triggerId: string | null;
|
|
230
|
-
}> {}
|
|
215
|
+
interface TooltipRootStateOpts {
|
|
216
|
+
readonly delayDuration: number | undefined;
|
|
217
|
+
readonly disableHoverableContent: boolean | undefined;
|
|
218
|
+
readonly disableCloseOnTriggerClick: boolean | undefined;
|
|
219
|
+
readonly disabled: boolean | undefined;
|
|
220
|
+
readonly ignoreNonKeyboardFocus: boolean | undefined;
|
|
221
|
+
readonly onOpenChangeComplete: OnChangeFn<boolean>;
|
|
222
|
+
readonly tether: TooltipTether<unknown> | undefined;
|
|
223
|
+
open: boolean;
|
|
224
|
+
triggerId: string | null;
|
|
225
|
+
}
|
|
231
226
|
|
|
232
227
|
export class TooltipRootState {
|
|
233
228
|
static create(opts: TooltipRootStateOpts) {
|
|
@@ -235,16 +230,16 @@ export class TooltipRootState {
|
|
|
235
230
|
}
|
|
236
231
|
readonly opts: TooltipRootStateOpts;
|
|
237
232
|
readonly provider: TooltipProviderState;
|
|
238
|
-
readonly delayDuration = $derived.by(() => this.opts.delayDuration
|
|
233
|
+
readonly delayDuration = $derived.by(() => this.opts.delayDuration ?? this.provider.opts.delayDuration);
|
|
239
234
|
readonly disableHoverableContent = $derived.by(
|
|
240
|
-
() => this.opts.disableHoverableContent
|
|
235
|
+
() => this.opts.disableHoverableContent ?? this.provider.opts.disableHoverableContent,
|
|
241
236
|
);
|
|
242
237
|
readonly disableCloseOnTriggerClick = $derived.by(
|
|
243
|
-
() => this.opts.disableCloseOnTriggerClick
|
|
238
|
+
() => this.opts.disableCloseOnTriggerClick ?? this.provider.opts.disableCloseOnTriggerClick,
|
|
244
239
|
);
|
|
245
|
-
readonly disabled = $derived.by(() => this.opts.disabled
|
|
240
|
+
readonly disabled = $derived.by(() => this.opts.disabled ?? this.provider.opts.disabled);
|
|
246
241
|
readonly ignoreNonKeyboardFocus = $derived.by(
|
|
247
|
-
() => this.opts.ignoreNonKeyboardFocus
|
|
242
|
+
() => this.opts.ignoreNonKeyboardFocus ?? this.provider.opts.ignoreNonKeyboardFocus,
|
|
248
243
|
);
|
|
249
244
|
readonly registry: TooltipTriggerRegistryState;
|
|
250
245
|
readonly tether: TooltipTetherState | null;
|
|
@@ -252,19 +247,19 @@ export class TooltipRootState {
|
|
|
252
247
|
#wasOpenDelayed = $state(false);
|
|
253
248
|
readonly #timerFn: ReturnType<typeof createEffectTimeout<() => void>>;
|
|
254
249
|
readonly stateAttr = $derived.by(() => {
|
|
255
|
-
if (!this.opts.open
|
|
250
|
+
if (!this.opts.open) return 'closed';
|
|
256
251
|
return this.#wasOpenDelayed ? 'delayed-open' : 'instant-open';
|
|
257
252
|
});
|
|
258
253
|
|
|
259
254
|
constructor(opts: TooltipRootStateOpts, provider: TooltipProviderState) {
|
|
260
255
|
this.opts = opts;
|
|
261
256
|
this.provider = provider;
|
|
262
|
-
this.tether = opts.tether
|
|
257
|
+
this.tether = opts.tether?.state ?? null;
|
|
263
258
|
this.registry = this.tether?.registry ?? new TooltipTriggerRegistryState();
|
|
264
259
|
this.#timerFn = createEffectTimeout(
|
|
265
260
|
() => {
|
|
266
261
|
this.#wasOpenDelayed = true;
|
|
267
|
-
this.opts.open
|
|
262
|
+
this.opts.open = true;
|
|
268
263
|
},
|
|
269
264
|
() => this.delayDuration ?? 0,
|
|
270
265
|
);
|
|
@@ -282,7 +277,7 @@ export class TooltipRootState {
|
|
|
282
277
|
|
|
283
278
|
let openStarted = false;
|
|
284
279
|
$effect(() => {
|
|
285
|
-
const isOpen = this.opts.open
|
|
280
|
+
const isOpen = this.opts.open;
|
|
286
281
|
if (!openStarted) {
|
|
287
282
|
openStarted = true;
|
|
288
283
|
return;
|
|
@@ -298,7 +293,7 @@ export class TooltipRootState {
|
|
|
298
293
|
});
|
|
299
294
|
|
|
300
295
|
$effect(() => {
|
|
301
|
-
const triggerId = this.opts.triggerId
|
|
296
|
+
const triggerId = this.opts.triggerId;
|
|
302
297
|
untrack(() => {
|
|
303
298
|
if (triggerId === this.registry.activeTriggerId) return;
|
|
304
299
|
this.registry.setActiveTrigger(triggerId);
|
|
@@ -308,8 +303,8 @@ export class TooltipRootState {
|
|
|
308
303
|
$effect(() => {
|
|
309
304
|
const activeTriggerId = this.registry.activeTriggerId;
|
|
310
305
|
untrack(() => {
|
|
311
|
-
if (this.opts.triggerId
|
|
312
|
-
this.opts.triggerId
|
|
306
|
+
if (this.opts.triggerId === activeTriggerId) return;
|
|
307
|
+
this.opts.triggerId = activeTriggerId;
|
|
313
308
|
});
|
|
314
309
|
});
|
|
315
310
|
}
|
|
@@ -318,12 +313,12 @@ export class TooltipRootState {
|
|
|
318
313
|
this.#timerFn.stop();
|
|
319
314
|
this.#wasOpenDelayed = false;
|
|
320
315
|
this.ensureActiveTrigger();
|
|
321
|
-
this.opts.open
|
|
316
|
+
this.opts.open = true;
|
|
322
317
|
};
|
|
323
318
|
|
|
324
319
|
handleClose = () => {
|
|
325
320
|
this.#timerFn.stop();
|
|
326
|
-
this.opts.open
|
|
321
|
+
this.opts.open = false;
|
|
327
322
|
};
|
|
328
323
|
|
|
329
324
|
#handleDelayedOpen = () => {
|
|
@@ -335,7 +330,7 @@ export class TooltipRootState {
|
|
|
335
330
|
// if no delay needed (either skip delay active or delay is 0), open immediately
|
|
336
331
|
if (shouldSkipDelay || delayDuration === 0) {
|
|
337
332
|
this.#wasOpenDelayed = false;
|
|
338
|
-
this.opts.open
|
|
333
|
+
this.opts.open = true;
|
|
339
334
|
} else {
|
|
340
335
|
// use timer for actual delays
|
|
341
336
|
this.#timerFn.start();
|
|
@@ -360,8 +355,8 @@ export class TooltipRootState {
|
|
|
360
355
|
return;
|
|
361
356
|
}
|
|
362
357
|
|
|
363
|
-
if (this.opts.triggerId
|
|
364
|
-
this.registry.setActiveTrigger(this.opts.triggerId
|
|
358
|
+
if (this.opts.triggerId !== null && this.registry.has(this.opts.triggerId)) {
|
|
359
|
+
this.registry.setActiveTrigger(this.opts.triggerId);
|
|
365
360
|
return;
|
|
366
361
|
}
|
|
367
362
|
|
|
@@ -376,7 +371,7 @@ export class TooltipRootState {
|
|
|
376
371
|
registerTrigger = (trigger: TooltipTriggerRecord) => {
|
|
377
372
|
this.registry.register(trigger);
|
|
378
373
|
|
|
379
|
-
if (trigger.disabled && this.registry.activeTriggerId === trigger.id && this.opts.open
|
|
374
|
+
if (trigger.disabled && this.registry.activeTriggerId === trigger.id && this.opts.open) {
|
|
380
375
|
this.handleClose();
|
|
381
376
|
}
|
|
382
377
|
};
|
|
@@ -384,7 +379,7 @@ export class TooltipRootState {
|
|
|
384
379
|
updateTrigger = (trigger: TooltipTriggerRecord) => {
|
|
385
380
|
this.registry.update(trigger);
|
|
386
381
|
|
|
387
|
-
if (trigger.disabled && this.registry.activeTriggerId === trigger.id && this.opts.open
|
|
382
|
+
if (trigger.disabled && this.registry.activeTriggerId === trigger.id && this.opts.open) {
|
|
388
383
|
this.handleClose();
|
|
389
384
|
}
|
|
390
385
|
};
|
|
@@ -393,7 +388,7 @@ export class TooltipRootState {
|
|
|
393
388
|
const isActive = this.registry.activeTriggerId === id;
|
|
394
389
|
this.registry.unregister(id);
|
|
395
390
|
|
|
396
|
-
if (isActive && this.opts.open
|
|
391
|
+
if (isActive && this.opts.open) {
|
|
397
392
|
this.handleClose();
|
|
398
393
|
}
|
|
399
394
|
};
|
|
@@ -415,20 +410,18 @@ export class TooltipRootState {
|
|
|
415
410
|
}
|
|
416
411
|
}
|
|
417
412
|
|
|
418
|
-
interface TooltipTriggerStateOpts
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
payload: unknown;
|
|
425
|
-
tether: TooltipTether<unknown> | undefined;
|
|
426
|
-
}> {}
|
|
413
|
+
interface TooltipTriggerStateOpts extends RefOpts {
|
|
414
|
+
readonly disabled: boolean;
|
|
415
|
+
readonly tabindex: number;
|
|
416
|
+
readonly payload: unknown;
|
|
417
|
+
readonly tether: TooltipTether<unknown> | undefined;
|
|
418
|
+
}
|
|
427
419
|
|
|
428
420
|
export class TooltipTriggerState {
|
|
429
421
|
static create(opts: TooltipTriggerStateOpts) {
|
|
430
|
-
|
|
431
|
-
|
|
422
|
+
const tether = opts.tether;
|
|
423
|
+
if (tether) {
|
|
424
|
+
return new TooltipTriggerState(opts, null, tether.state);
|
|
432
425
|
}
|
|
433
426
|
return new TooltipTriggerState(opts, getTooltipRoot(), null);
|
|
434
427
|
}
|
|
@@ -436,18 +429,9 @@ export class TooltipTriggerState {
|
|
|
436
429
|
readonly root: TooltipRootState | null;
|
|
437
430
|
readonly tether: TooltipTetherState | null;
|
|
438
431
|
readonly attachment: RefAttachment;
|
|
439
|
-
#isPointerDown =
|
|
432
|
+
#isPointerDown = false;
|
|
440
433
|
#hasPointerMoveOpened = $state(false);
|
|
441
434
|
domContext: DOMContext;
|
|
442
|
-
// Fired on pointerenter while the pointer is in transit: if still in transit after the delay, the
|
|
443
|
-
// user is likely staying on this trigger.
|
|
444
|
-
#transitCheck = createEffectTimeout(() => {
|
|
445
|
-
const root = this.#getRoot();
|
|
446
|
-
if (!root || !root.provider.isPointerInTransit.current) return;
|
|
447
|
-
root.provider.isPointerInTransit.current = false;
|
|
448
|
-
root.onTriggerEnter(this.opts.id.current);
|
|
449
|
-
this.#hasPointerMoveOpened = true;
|
|
450
|
-
}, () => 250);
|
|
451
435
|
#mounted = false;
|
|
452
436
|
#lastRegisteredId: string | null = null;
|
|
453
437
|
|
|
@@ -455,31 +439,34 @@ export class TooltipTriggerState {
|
|
|
455
439
|
this.opts = opts;
|
|
456
440
|
this.root = root;
|
|
457
441
|
this.tether = tether;
|
|
458
|
-
this.domContext = new DOMContext(opts.ref);
|
|
459
|
-
this.attachment = attachRef(
|
|
442
|
+
this.domContext = new DOMContext(() => opts.ref);
|
|
443
|
+
this.attachment = attachRef<HTMLElement>(
|
|
444
|
+
(v) => (opts.ref = v),
|
|
445
|
+
(v) => this.#register(v),
|
|
446
|
+
);
|
|
460
447
|
|
|
461
448
|
$effect(() => {
|
|
462
|
-
const _id = this.opts.id
|
|
449
|
+
const _id = this.opts.id;
|
|
463
450
|
untrack(() => {
|
|
464
|
-
this.#register(this.opts.ref
|
|
451
|
+
this.#register(this.opts.ref);
|
|
465
452
|
});
|
|
466
453
|
});
|
|
467
454
|
$effect(() => {
|
|
468
|
-
const _payload = this.opts.payload
|
|
455
|
+
const _payload = this.opts.payload;
|
|
469
456
|
untrack(() => {
|
|
470
|
-
this.#register(this.opts.ref
|
|
457
|
+
this.#register(this.opts.ref);
|
|
471
458
|
});
|
|
472
459
|
});
|
|
473
460
|
$effect(() => {
|
|
474
|
-
const _disabled = this.opts.disabled
|
|
461
|
+
const _disabled = this.opts.disabled;
|
|
475
462
|
untrack(() => {
|
|
476
|
-
this.#register(this.opts.ref
|
|
463
|
+
this.#register(this.opts.ref);
|
|
477
464
|
});
|
|
478
465
|
});
|
|
479
466
|
|
|
480
467
|
onMount(() => {
|
|
481
468
|
this.#mounted = true;
|
|
482
|
-
this.#register(this.opts.ref
|
|
469
|
+
this.#register(this.opts.ref);
|
|
483
470
|
|
|
484
471
|
return () => {
|
|
485
472
|
const root = this.#getRoot();
|
|
@@ -503,14 +490,14 @@ export class TooltipTriggerState {
|
|
|
503
490
|
|
|
504
491
|
#isDisabled = () => {
|
|
505
492
|
const root = this.#getRoot();
|
|
506
|
-
return this.opts.disabled
|
|
493
|
+
return this.opts.disabled || Boolean(root?.disabled);
|
|
507
494
|
};
|
|
508
495
|
|
|
509
496
|
#register = (node: HTMLElement | null) => {
|
|
510
497
|
if (!this.#mounted) return;
|
|
511
|
-
const id = this.opts.id
|
|
512
|
-
const payload = this.opts.payload
|
|
513
|
-
const disabled = this.opts.disabled
|
|
498
|
+
const id = this.opts.id;
|
|
499
|
+
const payload = this.opts.payload;
|
|
500
|
+
const disabled = this.opts.disabled;
|
|
514
501
|
|
|
515
502
|
if (this.#lastRegisteredId && this.#lastRegisteredId !== id) {
|
|
516
503
|
const root = this.#getRoot();
|
|
@@ -536,7 +523,7 @@ export class TooltipTriggerState {
|
|
|
536
523
|
this.tether.registry.register(triggerRecord);
|
|
537
524
|
}
|
|
538
525
|
|
|
539
|
-
if (disabled && this.tether.registry.activeTriggerId === id && root?.opts.open
|
|
526
|
+
if (disabled && this.tether.registry.activeTriggerId === id && root?.opts.open) {
|
|
540
527
|
root.handleClose();
|
|
541
528
|
}
|
|
542
529
|
} else {
|
|
@@ -551,17 +538,17 @@ export class TooltipTriggerState {
|
|
|
551
538
|
};
|
|
552
539
|
|
|
553
540
|
handlePointerUp = () => {
|
|
554
|
-
this.#isPointerDown
|
|
541
|
+
this.#isPointerDown = false;
|
|
555
542
|
};
|
|
556
543
|
|
|
557
544
|
#onpointerup: PointerEventHandler<HTMLElement> = () => {
|
|
558
545
|
if (this.#isDisabled()) return;
|
|
559
|
-
this.#isPointerDown
|
|
546
|
+
this.#isPointerDown = false;
|
|
560
547
|
};
|
|
561
548
|
|
|
562
549
|
#onpointerdown: PointerEventHandler<HTMLElement> = () => {
|
|
563
550
|
if (this.#isDisabled()) return;
|
|
564
|
-
this.#isPointerDown
|
|
551
|
+
this.#isPointerDown = true;
|
|
565
552
|
|
|
566
553
|
on(
|
|
567
554
|
this.domContext.getDocument(),
|
|
@@ -577,20 +564,14 @@ export class TooltipTriggerState {
|
|
|
577
564
|
const root = this.#getRoot();
|
|
578
565
|
if (!root) return;
|
|
579
566
|
if (this.#isDisabled()) {
|
|
580
|
-
if (root.opts.open
|
|
567
|
+
if (root.opts.open) {
|
|
581
568
|
root.handleClose();
|
|
582
569
|
}
|
|
583
570
|
return;
|
|
584
571
|
}
|
|
585
572
|
if (e.pointerType === 'touch') return;
|
|
586
573
|
|
|
587
|
-
|
|
588
|
-
if (root.provider.isPointerInTransit.current) {
|
|
589
|
-
this.#transitCheck.start();
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
root.onTriggerEnter(this.opts.id.current);
|
|
574
|
+
root.onTriggerEnter(this.opts.id);
|
|
594
575
|
this.#hasPointerMoveOpened = true;
|
|
595
576
|
};
|
|
596
577
|
|
|
@@ -598,7 +579,7 @@ export class TooltipTriggerState {
|
|
|
598
579
|
const root = this.#getRoot();
|
|
599
580
|
if (!root) return;
|
|
600
581
|
if (this.#isDisabled()) {
|
|
601
|
-
if (root.opts.open
|
|
582
|
+
if (root.opts.open) {
|
|
602
583
|
root.handleClose();
|
|
603
584
|
}
|
|
604
585
|
return;
|
|
@@ -606,11 +587,7 @@ export class TooltipTriggerState {
|
|
|
606
587
|
if (e.pointerType === 'touch') return;
|
|
607
588
|
if (this.#hasPointerMoveOpened) return;
|
|
608
589
|
|
|
609
|
-
|
|
610
|
-
this.#transitCheck.stop();
|
|
611
|
-
root.provider.isPointerInTransit.current = false;
|
|
612
|
-
|
|
613
|
-
root.onTriggerEnter(this.opts.id.current);
|
|
590
|
+
root.onTriggerEnter(this.opts.id);
|
|
614
591
|
this.#hasPointerMoveOpened = true;
|
|
615
592
|
};
|
|
616
593
|
|
|
@@ -618,8 +595,7 @@ export class TooltipTriggerState {
|
|
|
618
595
|
const root = this.#getRoot();
|
|
619
596
|
if (!root) return;
|
|
620
597
|
if (this.#isDisabled()) return;
|
|
621
|
-
this
|
|
622
|
-
if (!root.isActiveTrigger(this.opts.id.current)) {
|
|
598
|
+
if (!root.isActiveTrigger(this.opts.id)) {
|
|
623
599
|
this.#hasPointerMoveOpened = false;
|
|
624
600
|
return;
|
|
625
601
|
}
|
|
@@ -632,7 +608,7 @@ export class TooltipTriggerState {
|
|
|
632
608
|
if (isElement(relatedTarget)) {
|
|
633
609
|
for (const record of root.registry.triggers.values()) {
|
|
634
610
|
if (record.node !== relatedTarget) continue;
|
|
635
|
-
if (root.provider.opts.skipDelayDuration
|
|
611
|
+
if (root.provider.opts.skipDelayDuration > 0) {
|
|
636
612
|
this.#hasPointerMoveOpened = false;
|
|
637
613
|
return;
|
|
638
614
|
}
|
|
@@ -649,16 +625,16 @@ export class TooltipTriggerState {
|
|
|
649
625
|
#onfocus: FocusEventHandler<HTMLElement> = (e) => {
|
|
650
626
|
const root = this.#getRoot();
|
|
651
627
|
if (!root) return;
|
|
652
|
-
if (this.#isPointerDown
|
|
628
|
+
if (this.#isPointerDown) return;
|
|
653
629
|
if (this.#isDisabled()) {
|
|
654
|
-
if (root.opts.open
|
|
630
|
+
if (root.opts.open) {
|
|
655
631
|
root.handleClose();
|
|
656
632
|
}
|
|
657
633
|
return;
|
|
658
634
|
}
|
|
659
635
|
|
|
660
636
|
if (root.ignoreNonKeyboardFocus && !e.currentTarget.matches(':focus-visible')) return;
|
|
661
|
-
root.setActiveTrigger(this.opts.id
|
|
637
|
+
root.setActiveTrigger(this.opts.id);
|
|
662
638
|
root.handleOpen();
|
|
663
639
|
};
|
|
664
640
|
|
|
@@ -676,18 +652,18 @@ export class TooltipTriggerState {
|
|
|
676
652
|
|
|
677
653
|
readonly props = $derived.by(() => {
|
|
678
654
|
const root = this.#getRoot();
|
|
679
|
-
const isOpenForTrigger = Boolean(root?.opts.open
|
|
655
|
+
const isOpenForTrigger = Boolean(root?.opts.open && root.isActiveTrigger(this.opts.id));
|
|
680
656
|
const isDisabled = this.#isDisabled();
|
|
681
657
|
|
|
682
658
|
return {
|
|
683
|
-
id: this.opts.id
|
|
659
|
+
id: this.opts.id,
|
|
684
660
|
'aria-describedby': isOpenForTrigger ? root?.contentNode?.id : undefined,
|
|
685
661
|
'data-state': isOpenForTrigger ? root?.stateAttr : 'closed',
|
|
686
662
|
'data-disabled': boolToEmptyStrOrUndef(isDisabled),
|
|
687
663
|
'data-delay-duration': `${root?.delayDuration ?? 0}`,
|
|
688
664
|
[tooltipAttrs.trigger]: '',
|
|
689
|
-
tabindex: isDisabled ? undefined : this.opts.tabindex
|
|
690
|
-
disabled: this.opts.disabled
|
|
665
|
+
tabindex: isDisabled ? undefined : this.opts.tabindex,
|
|
666
|
+
disabled: this.opts.disabled,
|
|
691
667
|
onpointerup: this.#onpointerup,
|
|
692
668
|
onpointerdown: this.#onpointerdown,
|
|
693
669
|
onpointerenter: this.#onpointerenter,
|
|
@@ -701,13 +677,10 @@ export class TooltipTriggerState {
|
|
|
701
677
|
});
|
|
702
678
|
}
|
|
703
679
|
|
|
704
|
-
interface TooltipContentStateOpts
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
onInteractOutside: (e: PointerEvent) => void;
|
|
709
|
-
onEscapeKeydown: (e: KeyboardEvent) => void;
|
|
710
|
-
}> {}
|
|
680
|
+
interface TooltipContentStateOpts extends RefOpts {
|
|
681
|
+
readonly onInteractOutside: (e: PointerEvent) => void;
|
|
682
|
+
readonly onEscapeKeydown: (e: KeyboardEvent) => void;
|
|
683
|
+
}
|
|
711
684
|
|
|
712
685
|
export class TooltipContentState {
|
|
713
686
|
static create(opts: TooltipContentStateOpts) {
|
|
@@ -719,17 +692,20 @@ export class TooltipContentState {
|
|
|
719
692
|
constructor(opts: TooltipContentStateOpts, root: TooltipRootState) {
|
|
720
693
|
this.opts = opts;
|
|
721
694
|
this.root = root;
|
|
722
|
-
this.attachment = attachRef(
|
|
695
|
+
this.attachment = attachRef<HTMLElement>(
|
|
696
|
+
(v) => (opts.ref = v),
|
|
697
|
+
(v) => (this.root.contentNode = v),
|
|
698
|
+
);
|
|
723
699
|
|
|
724
700
|
new SafePolygon({
|
|
725
701
|
triggerNode: () => this.root.triggerNode,
|
|
726
702
|
contentNode: () => this.root.contentNode,
|
|
727
|
-
enabled: () => this.root.opts.open
|
|
703
|
+
enabled: () => this.root.opts.open && !this.root.disableHoverableContent,
|
|
728
704
|
transitIntentTimeout: 180,
|
|
729
705
|
ignoredTargets: () => {
|
|
730
706
|
// only skip closing for sibling triggers when there's a skip-delay grace period;
|
|
731
707
|
// with skipDelayDuration=0 the close+reopen is intentional (full delay + re-animation)
|
|
732
|
-
if (this.root.provider.opts.skipDelayDuration
|
|
708
|
+
if (this.root.provider.opts.skipDelayDuration === 0) return [];
|
|
733
709
|
const nodes: HTMLElement[] = [];
|
|
734
710
|
const activeTriggerNode = this.root.triggerNode;
|
|
735
711
|
for (const record of this.root.registry.triggers.values()) {
|
|
@@ -752,13 +728,13 @@ export class TooltipContentState {
|
|
|
752
728
|
e.preventDefault();
|
|
753
729
|
return;
|
|
754
730
|
}
|
|
755
|
-
this.opts.onInteractOutside
|
|
731
|
+
this.opts.onInteractOutside(e);
|
|
756
732
|
if (e.defaultPrevented) return;
|
|
757
733
|
this.root.handleClose();
|
|
758
734
|
};
|
|
759
735
|
|
|
760
736
|
onEscapeKeydown = (e: KeyboardEvent) => {
|
|
761
|
-
this.opts.onEscapeKeydown
|
|
737
|
+
this.opts.onEscapeKeydown?.(e);
|
|
762
738
|
if (e.defaultPrevented) return;
|
|
763
739
|
this.root.handleClose();
|
|
764
740
|
};
|
|
@@ -771,12 +747,12 @@ export class TooltipContentState {
|
|
|
771
747
|
e.preventDefault();
|
|
772
748
|
};
|
|
773
749
|
|
|
774
|
-
readonly snippetProps = $derived.by(() => ({ open: this.root.opts.open
|
|
750
|
+
readonly snippetProps = $derived.by(() => ({ open: this.root.opts.open }));
|
|
775
751
|
|
|
776
752
|
readonly props = $derived.by(
|
|
777
753
|
() =>
|
|
778
754
|
({
|
|
779
|
-
id: this.opts.id
|
|
755
|
+
id: this.opts.id,
|
|
780
756
|
'data-state': this.root.stateAttr,
|
|
781
757
|
'data-disabled': boolToEmptyStrOrUndef(this.root.disabled),
|
|
782
758
|
style: {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
simpleBox,
|
|
3
2
|
boxWith,
|
|
4
3
|
attachRef,
|
|
5
4
|
type Getter,
|
|
@@ -219,7 +218,7 @@ export function interactOutsideAttachment(opts: {
|
|
|
219
218
|
enabled: Getter<boolean>;
|
|
220
219
|
isValidEvent?: Getter<((e: PointerEvent, node: HTMLElement) => boolean) | undefined>;
|
|
221
220
|
}): { props: { onfocuscapture: () => void; onblurcapture: () => void }; attachment: RefAttachment<HTMLElement> } {
|
|
222
|
-
|
|
221
|
+
let node = $state.raw<HTMLElement | null>(null);
|
|
223
222
|
const state = DismissibleLayerState.create({
|
|
224
223
|
id: boxWith(opts.id),
|
|
225
224
|
interactOutsideBehavior: boxWith(opts.interactOutsideBehavior),
|
|
@@ -227,9 +226,9 @@ export function interactOutsideAttachment(opts: {
|
|
|
227
226
|
enabled: boxWith(opts.enabled),
|
|
228
227
|
onFocusOutside: boxWith(() => opts.onFocusOutside?.()),
|
|
229
228
|
isValidEvent: boxWith(() => opts.isValidEvent?.()),
|
|
230
|
-
ref,
|
|
229
|
+
ref: boxWith(() => node, (v) => (node = v)),
|
|
231
230
|
});
|
|
232
|
-
return { props: state.props, attachment: attachRef(
|
|
231
|
+
return { props: state.props, attachment: attachRef((v) => (node = v)) };
|
|
233
232
|
}
|
|
234
233
|
|
|
235
234
|
function isValidEvent(e: PointerEvent, node: HTMLElement): boolean {
|
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DOMContext,
|
|
3
|
-
boxWith,
|
|
4
|
-
type Box,
|
|
5
|
-
type Getter,
|
|
6
|
-
type ReadableBox,
|
|
7
|
-
type ReadableBoxedValues,
|
|
8
|
-
type RefAttachment,
|
|
9
|
-
} from '../tools/index.js';
|
|
1
|
+
import { DOMContext, type Getter, type RefAttachment } from '../tools/index.js';
|
|
10
2
|
import { untrack } from 'svelte';
|
|
11
3
|
import { on } from 'svelte/events';
|
|
12
4
|
import { createAttachmentKey } from 'svelte/attachments';
|
|
13
|
-
import type { EscapeBehaviorType
|
|
5
|
+
import type { EscapeBehaviorType } from './types.js';
|
|
14
6
|
import { kbd } from '../kbd.js';
|
|
15
7
|
import { createLayerStack } from '../layer-stack.js';
|
|
16
8
|
import { globalSingleton } from '../global-singleton.js';
|
|
17
9
|
|
|
18
10
|
const escapeLayers = globalSingleton('bitsEscapeLayers', () =>
|
|
19
|
-
createLayerStack<EscapeLayerState,
|
|
20
|
-
(box) => box.current === 'close' || box.current === 'ignore',
|
|
21
|
-
),
|
|
11
|
+
createLayerStack<EscapeLayerState, Getter<EscapeBehaviorType>>((behavior) => behavior() === 'close' || behavior() === 'ignore'),
|
|
22
12
|
);
|
|
23
13
|
|
|
24
|
-
interface EscapeLayerStateOpts
|
|
25
|
-
|
|
14
|
+
interface EscapeLayerStateOpts {
|
|
15
|
+
escapeKeydownBehavior: Getter<EscapeBehaviorType>;
|
|
16
|
+
onEscapeKeydown: Getter<(e: KeyboardEvent) => void>;
|
|
17
|
+
enabled: Getter<boolean>;
|
|
18
|
+
ref: Getter<HTMLElement | null>;
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
export class EscapeLayerState {
|
|
@@ -34,10 +27,10 @@ export class EscapeLayerState {
|
|
|
34
27
|
|
|
35
28
|
constructor(opts: EscapeLayerStateOpts) {
|
|
36
29
|
this.opts = opts;
|
|
37
|
-
this.domContext = new DOMContext(
|
|
30
|
+
this.domContext = new DOMContext(opts.ref);
|
|
38
31
|
|
|
39
32
|
$effect(() => {
|
|
40
|
-
if (!opts.enabled
|
|
33
|
+
if (!opts.enabled()) return;
|
|
41
34
|
|
|
42
35
|
const unsubEvents = untrack(() => {
|
|
43
36
|
escapeLayers.register(this, opts.escapeKeydownBehavior);
|
|
@@ -55,9 +48,9 @@ export class EscapeLayerState {
|
|
|
55
48
|
if (e.key !== kbd.ESCAPE || !escapeLayers.isResponsible(this)) return;
|
|
56
49
|
const clonedEvent = new KeyboardEvent(e.type, e);
|
|
57
50
|
e.preventDefault();
|
|
58
|
-
const behaviorType = this.opts.escapeKeydownBehavior
|
|
51
|
+
const behaviorType = this.opts.escapeKeydownBehavior();
|
|
59
52
|
if (behaviorType !== 'close' && behaviorType !== 'defer-otherwise-close') return;
|
|
60
|
-
this.opts.onEscapeKeydown
|
|
53
|
+
this.opts.onEscapeKeydown()(clonedEvent);
|
|
61
54
|
};
|
|
62
55
|
}
|
|
63
56
|
|
|
@@ -76,16 +69,7 @@ export function escapeKeydownAttachment(opts: {
|
|
|
76
69
|
}): RefAttachment<HTMLElement> {
|
|
77
70
|
return {
|
|
78
71
|
[createAttachmentKey()]: (node) => {
|
|
79
|
-
EscapeLayerState.create({
|
|
80
|
-
escapeKeydownBehavior: boxWith(opts.escapeKeydownBehavior),
|
|
81
|
-
onEscapeKeydown: boxWith(opts.onEscapeKeydown),
|
|
82
|
-
enabled: boxWith(opts.enabled),
|
|
83
|
-
// The state only reads the ref (to resolve the owner document), so a no-op setter is fine.
|
|
84
|
-
ref: boxWith(
|
|
85
|
-
() => node,
|
|
86
|
-
() => {},
|
|
87
|
-
),
|
|
88
|
-
});
|
|
72
|
+
EscapeLayerState.create({ ...opts, ref: () => node });
|
|
89
73
|
},
|
|
90
74
|
};
|
|
91
75
|
}
|