bits-ui 1.6.0 → 1.6.1
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.
|
@@ -5,6 +5,7 @@ import { kbd } from "../../internal/kbd.js";
|
|
|
5
5
|
import { getAriaDisabled, getAriaExpanded, getAriaSelected, getDataDisabled, getDataSelected, } from "../../internal/attrs.js";
|
|
6
6
|
import { getFirstNonCommentChild } from "../../internal/dom.js";
|
|
7
7
|
import { computeCommandScore } from "./index.js";
|
|
8
|
+
import cssesc from "css.escape";
|
|
8
9
|
// attributes
|
|
9
10
|
const COMMAND_ROOT_ATTR = "data-command-root";
|
|
10
11
|
const COMMAND_LIST_ATTR = "data-command-list";
|
|
@@ -178,7 +179,7 @@ class CommandRootState {
|
|
|
178
179
|
}
|
|
179
180
|
const sortedGroups = groups.sort((a, b) => b[1] - a[1]);
|
|
180
181
|
for (const group of sortedGroups) {
|
|
181
|
-
const element = listInsertionElement?.querySelector(`${COMMAND_GROUP_SELECTOR}[${COMMAND_VALUE_ATTR}="${
|
|
182
|
+
const element = listInsertionElement?.querySelector(`${COMMAND_GROUP_SELECTOR}[${COMMAND_VALUE_ATTR}="${cssesc(group[0])}"]`);
|
|
182
183
|
element?.parentElement?.appendChild(element);
|
|
183
184
|
}
|
|
184
185
|
this.#selectFirstItem();
|
|
@@ -683,7 +684,7 @@ class CommandInputState {
|
|
|
683
684
|
opts;
|
|
684
685
|
root;
|
|
685
686
|
#selectedItemId = $derived.by(() => {
|
|
686
|
-
const item = this.root.viewportNode?.querySelector(`${COMMAND_ITEM_SELECTOR}[${COMMAND_VALUE_ATTR}="${
|
|
687
|
+
const item = this.root.viewportNode?.querySelector(`${COMMAND_ITEM_SELECTOR}[${COMMAND_VALUE_ATTR}="${cssesc(this.root.opts.value.current)}"]`);
|
|
687
688
|
if (!item)
|
|
688
689
|
return;
|
|
689
690
|
return item?.getAttribute("id") ?? undefined;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
|
|
2
2
|
import type { WithRefProps } from "../../internal/types.js";
|
|
3
|
-
import {
|
|
4
|
-
export declare const TooltipOpenEvent: CustomEventDispatcher<unknown>;
|
|
3
|
+
import type { PointerEventHandler } from "svelte/elements";
|
|
5
4
|
type TooltipProviderStateProps = ReadableBoxedValues<{
|
|
6
5
|
delayDuration: number;
|
|
7
6
|
disableHoverableContent: boolean;
|
|
@@ -16,8 +15,9 @@ declare class TooltipProviderState {
|
|
|
16
15
|
isOpenDelayed: boolean;
|
|
17
16
|
isPointerInTransit: import("svelte-toolbelt").WritableBox<boolean>;
|
|
18
17
|
constructor(opts: TooltipProviderStateProps);
|
|
19
|
-
onOpen: () => void;
|
|
20
|
-
onClose: () => void;
|
|
18
|
+
onOpen: (tooltip: TooltipRootState) => void;
|
|
19
|
+
onClose: (tooltip: TooltipRootState) => void;
|
|
20
|
+
isTooltipOpen: (tooltip: TooltipRootState) => boolean;
|
|
21
21
|
}
|
|
22
22
|
type TooltipRootStateProps = ReadableBoxedValues<{
|
|
23
23
|
delayDuration: number | undefined;
|
|
@@ -66,7 +66,7 @@ declare class TooltipTriggerState {
|
|
|
66
66
|
disabled: boolean;
|
|
67
67
|
onpointerup: () => void;
|
|
68
68
|
onpointerdown: () => void;
|
|
69
|
-
onpointermove:
|
|
69
|
+
onpointermove: PointerEventHandler<HTMLElement>;
|
|
70
70
|
onpointerleave: () => void;
|
|
71
71
|
onfocus: (e: FocusEvent & {
|
|
72
72
|
currentTarget: HTMLElement;
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import { box,
|
|
1
|
+
import { box, onMountEffect, useRefById } from "svelte-toolbelt";
|
|
2
2
|
import { on } from "svelte/events";
|
|
3
3
|
import { Context, watch } from "runed";
|
|
4
4
|
import { useTimeoutFn } from "../../internal/use-timeout-fn.svelte.js";
|
|
5
5
|
import { isElement, isFocusVisible } from "../../internal/is.js";
|
|
6
6
|
import { useGraceArea } from "../../internal/use-grace-area.svelte.js";
|
|
7
7
|
import { getDataDisabled } from "../../internal/attrs.js";
|
|
8
|
-
import { CustomEventDispatcher } from "../../internal/events.js";
|
|
9
8
|
const TOOLTIP_CONTENT_ATTR = "data-tooltip-content";
|
|
10
9
|
const TOOLTIP_TRIGGER_ATTR = "data-tooltip-trigger";
|
|
11
|
-
export const TooltipOpenEvent = new CustomEventDispatcher("bits.tooltip.open", {
|
|
12
|
-
bubbles: false,
|
|
13
|
-
cancelable: false,
|
|
14
|
-
});
|
|
15
10
|
class TooltipProviderState {
|
|
16
11
|
opts;
|
|
17
12
|
isOpenDelayed = $state(true);
|
|
18
13
|
isPointerInTransit = box(false);
|
|
19
14
|
#timerFn;
|
|
15
|
+
#openTooltip = $state(null);
|
|
20
16
|
constructor(opts) {
|
|
21
17
|
this.opts = opts;
|
|
22
18
|
this.#timerFn = useTimeoutFn(() => {
|
|
@@ -24,18 +20,34 @@ class TooltipProviderState {
|
|
|
24
20
|
}, this.opts.skipDelayDuration.current, { immediate: false });
|
|
25
21
|
}
|
|
26
22
|
#startTimer = () => {
|
|
27
|
-
this
|
|
23
|
+
const skipDuration = this.opts.skipDelayDuration.current;
|
|
24
|
+
if (skipDuration === 0) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.#timerFn.start();
|
|
29
|
+
}
|
|
28
30
|
};
|
|
29
31
|
#clearTimer = () => {
|
|
30
32
|
this.#timerFn.stop();
|
|
31
33
|
};
|
|
32
|
-
onOpen = () => {
|
|
34
|
+
onOpen = (tooltip) => {
|
|
35
|
+
if (this.#openTooltip && this.#openTooltip !== tooltip) {
|
|
36
|
+
this.#openTooltip.handleClose();
|
|
37
|
+
}
|
|
33
38
|
this.#clearTimer();
|
|
34
39
|
this.isOpenDelayed = false;
|
|
40
|
+
this.#openTooltip = tooltip;
|
|
35
41
|
};
|
|
36
|
-
onClose = () => {
|
|
42
|
+
onClose = (tooltip) => {
|
|
43
|
+
if (this.#openTooltip === tooltip) {
|
|
44
|
+
this.#openTooltip = null;
|
|
45
|
+
}
|
|
37
46
|
this.#startTimer();
|
|
38
47
|
};
|
|
48
|
+
isTooltipOpen = (tooltip) => {
|
|
49
|
+
return this.#openTooltip === tooltip;
|
|
50
|
+
};
|
|
39
51
|
}
|
|
40
52
|
class TooltipRootState {
|
|
41
53
|
opts;
|
|
@@ -73,14 +85,11 @@ class TooltipRootState {
|
|
|
73
85
|
}, this.delayDuration, { immediate: false });
|
|
74
86
|
});
|
|
75
87
|
watch(() => this.opts.open.current, (isOpen) => {
|
|
76
|
-
if (!this.provider.onClose)
|
|
77
|
-
return;
|
|
78
88
|
if (isOpen) {
|
|
79
|
-
this.provider.onOpen();
|
|
80
|
-
TooltipOpenEvent.dispatch(document);
|
|
89
|
+
this.provider.onOpen(this);
|
|
81
90
|
}
|
|
82
91
|
else {
|
|
83
|
-
this.provider.onClose();
|
|
92
|
+
this.provider.onClose(this);
|
|
84
93
|
}
|
|
85
94
|
});
|
|
86
95
|
}
|
|
@@ -94,7 +103,19 @@ class TooltipRootState {
|
|
|
94
103
|
this.opts.open.current = false;
|
|
95
104
|
};
|
|
96
105
|
#handleDelayedOpen = () => {
|
|
97
|
-
this.#timerFn.
|
|
106
|
+
this.#timerFn.stop();
|
|
107
|
+
const shouldSkipDelay = !this.provider.isOpenDelayed;
|
|
108
|
+
const delayDuration = this.delayDuration ?? 0;
|
|
109
|
+
// if no delay needed (either skip delay active or delay is 0), open immediately
|
|
110
|
+
if (shouldSkipDelay || delayDuration === 0) {
|
|
111
|
+
// set wasOpenDelayed based on whether we actually had a delay
|
|
112
|
+
this.#wasOpenDelayed = delayDuration > 0 && shouldSkipDelay;
|
|
113
|
+
this.opts.open.current = true;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// use timer for actual delays
|
|
117
|
+
this.#timerFn.start();
|
|
118
|
+
}
|
|
98
119
|
};
|
|
99
120
|
onTriggerEnter = () => {
|
|
100
121
|
this.#handleDelayedOpen();
|
|
@@ -145,7 +166,9 @@ class TooltipTriggerState {
|
|
|
145
166
|
return;
|
|
146
167
|
if (e.pointerType === "touch")
|
|
147
168
|
return;
|
|
148
|
-
if (this.#hasPointerMoveOpened
|
|
169
|
+
if (this.#hasPointerMoveOpened)
|
|
170
|
+
return;
|
|
171
|
+
if (this.root.provider.isPointerInTransit.current)
|
|
149
172
|
return;
|
|
150
173
|
this.root.onTriggerEnter();
|
|
151
174
|
this.#hasPointerMoveOpened = true;
|
|
@@ -209,20 +232,23 @@ class TooltipContentState {
|
|
|
209
232
|
contentNode: () => this.root.contentNode,
|
|
210
233
|
enabled: () => this.root.opts.open.current && !this.root.disableHoverableContent,
|
|
211
234
|
onPointerExit: () => {
|
|
212
|
-
this.root.
|
|
235
|
+
if (this.root.provider.isTooltipOpen(this.root)) {
|
|
236
|
+
this.root.handleClose();
|
|
237
|
+
}
|
|
213
238
|
},
|
|
214
239
|
setIsPointerInTransit: (value) => {
|
|
215
240
|
this.root.provider.isPointerInTransit.current = value;
|
|
216
241
|
},
|
|
242
|
+
transitTimeout: this.root.provider.opts.skipDelayDuration.current,
|
|
217
243
|
});
|
|
218
|
-
onMountEffect(() =>
|
|
244
|
+
onMountEffect(() => on(window, "scroll", (e) => {
|
|
219
245
|
const target = e.target;
|
|
220
246
|
if (!target)
|
|
221
247
|
return;
|
|
222
248
|
if (target.contains(this.root.triggerNode)) {
|
|
223
249
|
this.root.handleClose();
|
|
224
250
|
}
|
|
225
|
-
})
|
|
251
|
+
}));
|
|
226
252
|
}
|
|
227
253
|
onInteractOutside = (e) => {
|
|
228
254
|
if (isElement(e.target) &&
|
|
@@ -5,6 +5,7 @@ interface UseGraceAreaOpts {
|
|
|
5
5
|
contentNode: Getter<HTMLElement | null>;
|
|
6
6
|
onPointerExit: () => void;
|
|
7
7
|
setIsPointerInTransit?: (value: boolean) => void;
|
|
8
|
+
transitTimeout?: number;
|
|
8
9
|
}
|
|
9
10
|
export declare function useGraceArea(opts: UseGraceAreaOpts): {
|
|
10
11
|
isPointerInTransit: import("svelte-toolbelt").WritableBox<boolean>;
|
|
@@ -5,7 +5,7 @@ import { boxAutoReset } from "./box-auto-reset.svelte.js";
|
|
|
5
5
|
import { isElement, isHTMLElement } from "./is.js";
|
|
6
6
|
export function useGraceArea(opts) {
|
|
7
7
|
const enabled = $derived(opts.enabled());
|
|
8
|
-
const isPointerInTransit = boxAutoReset(false, 300, (value) => {
|
|
8
|
+
const isPointerInTransit = boxAutoReset(false, opts.transitTimeout ?? 300, (value) => {
|
|
9
9
|
if (enabled) {
|
|
10
10
|
opts.setIsPointerInTransit?.(value);
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bits-ui",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": "github:huntabyte/bits-ui",
|
|
6
6
|
"funding": "https://github.com/sponsors/huntabyte",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@sveltejs/kit": "^2.16.1",
|
|
22
22
|
"@sveltejs/package": "^2.3.9",
|
|
23
23
|
"@sveltejs/vite-plugin-svelte": "4.0.0",
|
|
24
|
+
"@types/css.escape": "^1.5.2",
|
|
24
25
|
"@types/node": "^20.17.6",
|
|
25
26
|
"@types/resize-observer-browser": "^0.1.11",
|
|
26
27
|
"csstype": "^3.1.3",
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
"@floating-ui/core": "^1.6.4",
|
|
42
43
|
"@floating-ui/dom": "^1.6.7",
|
|
43
44
|
"@internationalized/date": "^3.5.6",
|
|
45
|
+
"css.escape": "^1.5.1",
|
|
44
46
|
"esm-env": "^1.1.2",
|
|
45
47
|
"runed": "^0.23.2",
|
|
46
48
|
"svelte-toolbelt": "^0.7.1",
|