bits-ui 2.16.5 → 2.17.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.
- package/dist/bits/accordion/accordion.svelte.d.ts +4 -2
- package/dist/bits/accordion/accordion.svelte.js +2 -1
- package/dist/bits/collapsible/collapsible.svelte.d.ts +3 -1
- package/dist/bits/collapsible/collapsible.svelte.js +2 -1
- package/dist/bits/context-menu/components/context-menu.svelte +3 -1
- package/dist/bits/dialog/dialog.svelte.d.ts +4 -0
- package/dist/bits/dialog/dialog.svelte.js +3 -1
- package/dist/bits/link-preview/link-preview.svelte.d.ts +5 -3
- package/dist/bits/link-preview/link-preview.svelte.js +2 -1
- package/dist/bits/menu/components/menu-sub-content-static.svelte +12 -3
- package/dist/bits/menu/components/menu-sub-content.svelte +12 -3
- package/dist/bits/menu/components/menu-sub-trigger.svelte +1 -1
- package/dist/bits/menu/components/menu.svelte +5 -0
- package/dist/bits/menu/components/menu.svelte.d.ts +1 -0
- package/dist/bits/menu/menu.svelte.d.ts +8 -4
- package/dist/bits/menu/menu.svelte.js +642 -13
- package/dist/bits/menubar/components/menubar-menu.svelte +3 -0
- package/dist/bits/menubar/menubar.svelte.d.ts +2 -0
- package/dist/bits/menubar/menubar.svelte.js +22 -2
- package/dist/bits/navigation-menu/components/navigation-menu-content.svelte +7 -2
- package/dist/bits/navigation-menu/components/navigation-menu-indicator.svelte +9 -2
- package/dist/bits/navigation-menu/components/navigation-menu-viewport.svelte +5 -3
- package/dist/bits/popover/popover.svelte.d.ts +7 -3
- package/dist/bits/popover/popover.svelte.js +3 -1
- package/dist/bits/select/select.svelte.d.ts +6 -4
- package/dist/bits/select/select.svelte.js +2 -1
- package/dist/bits/tooltip/tooltip.svelte.d.ts +5 -3
- package/dist/bits/tooltip/tooltip.svelte.js +2 -1
- package/dist/bits/utilities/floating-layer/use-floating-layer.svelte.d.ts +5 -5
- package/dist/bits/utilities/presence-layer/presence-layer.svelte +5 -1
- package/dist/bits/utilities/presence-layer/presence.svelte.d.ts +3 -38
- package/dist/bits/utilities/presence-layer/presence.svelte.js +49 -146
- package/dist/bits/utilities/presence-layer/types.d.ts +7 -3
- package/dist/internal/animations-complete.js +64 -9
- package/dist/internal/attrs.d.ts +5 -0
- package/dist/internal/attrs.js +8 -2
- package/dist/internal/presence-manager.svelte.d.ts +4 -1
- package/dist/internal/presence-manager.svelte.js +42 -1
- package/package.json +1 -1
|
@@ -2,18 +2,22 @@ import { afterTick, onDestroyEffect } from "svelte-toolbelt";
|
|
|
2
2
|
export class AnimationsComplete {
|
|
3
3
|
#opts;
|
|
4
4
|
#currentFrame = null;
|
|
5
|
+
#observer = null;
|
|
6
|
+
#runId = 0;
|
|
5
7
|
constructor(opts) {
|
|
6
8
|
this.#opts = opts;
|
|
7
9
|
onDestroyEffect(() => this.#cleanup());
|
|
8
10
|
}
|
|
9
11
|
#cleanup() {
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (this.#currentFrame !== null) {
|
|
13
|
+
window.cancelAnimationFrame(this.#currentFrame);
|
|
14
|
+
this.#currentFrame = null;
|
|
15
|
+
}
|
|
16
|
+
this.#observer?.disconnect();
|
|
17
|
+
this.#observer = null;
|
|
18
|
+
this.#runId++;
|
|
14
19
|
}
|
|
15
20
|
run(fn) {
|
|
16
|
-
// if already running, cleanup and restart
|
|
17
21
|
this.#cleanup();
|
|
18
22
|
const node = this.#opts.ref.current;
|
|
19
23
|
if (!node)
|
|
@@ -22,14 +26,65 @@ export class AnimationsComplete {
|
|
|
22
26
|
this.#executeCallback(fn);
|
|
23
27
|
return;
|
|
24
28
|
}
|
|
25
|
-
|
|
29
|
+
const runId = this.#runId;
|
|
30
|
+
const executeIfCurrent = () => {
|
|
31
|
+
if (runId !== this.#runId)
|
|
32
|
+
return;
|
|
33
|
+
this.#executeCallback(fn);
|
|
34
|
+
};
|
|
35
|
+
const waitForAnimations = () => {
|
|
36
|
+
if (runId !== this.#runId)
|
|
37
|
+
return;
|
|
26
38
|
const animations = node.getAnimations();
|
|
27
39
|
if (animations.length === 0) {
|
|
28
|
-
|
|
40
|
+
executeIfCurrent();
|
|
29
41
|
return;
|
|
30
42
|
}
|
|
31
|
-
Promise.
|
|
32
|
-
|
|
43
|
+
Promise.all(animations.map((animation) => animation.finished))
|
|
44
|
+
.then(() => {
|
|
45
|
+
executeIfCurrent();
|
|
46
|
+
})
|
|
47
|
+
.catch(() => {
|
|
48
|
+
if (runId !== this.#runId)
|
|
49
|
+
return;
|
|
50
|
+
const currentAnimations = node.getAnimations();
|
|
51
|
+
const hasRunningAnimations = currentAnimations.some((animation) => animation.pending || animation.playState !== "finished");
|
|
52
|
+
if (hasRunningAnimations) {
|
|
53
|
+
waitForAnimations();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
executeIfCurrent();
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const requestWaitForAnimations = () => {
|
|
60
|
+
this.#currentFrame = window.requestAnimationFrame(() => {
|
|
61
|
+
this.#currentFrame = null;
|
|
62
|
+
waitForAnimations();
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
if (!this.#opts.afterTick.current) {
|
|
66
|
+
requestWaitForAnimations();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
this.#currentFrame = window.requestAnimationFrame(() => {
|
|
70
|
+
this.#currentFrame = null;
|
|
71
|
+
const startingStyleAttr = "data-starting-style";
|
|
72
|
+
if (!node.hasAttribute(startingStyleAttr)) {
|
|
73
|
+
requestWaitForAnimations();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.#observer = new MutationObserver(() => {
|
|
77
|
+
if (runId !== this.#runId)
|
|
78
|
+
return;
|
|
79
|
+
if (node.hasAttribute(startingStyleAttr))
|
|
80
|
+
return;
|
|
81
|
+
this.#observer?.disconnect();
|
|
82
|
+
this.#observer = null;
|
|
83
|
+
requestWaitForAnimations();
|
|
84
|
+
});
|
|
85
|
+
this.#observer.observe(node, {
|
|
86
|
+
attributes: true,
|
|
87
|
+
attributeFilter: [startingStyleAttr],
|
|
33
88
|
});
|
|
34
89
|
});
|
|
35
90
|
}
|
package/dist/internal/attrs.d.ts
CHANGED
|
@@ -4,6 +4,11 @@ export declare function boolToEmptyStrOrUndef(condition: boolean): "" | undefine
|
|
|
4
4
|
export declare function boolToTrueOrUndef(condition: boolean): true | undefined;
|
|
5
5
|
export declare function getDataOpenClosed(condition: boolean): "open" | "closed";
|
|
6
6
|
export declare function getDataChecked(condition: boolean): "checked" | "unchecked";
|
|
7
|
+
export type TransitionState = "starting" | "ending" | "idle" | undefined;
|
|
8
|
+
export declare function getDataTransitionAttrs(state: TransitionState): {
|
|
9
|
+
"data-starting-style"?: "";
|
|
10
|
+
"data-ending-style"?: "";
|
|
11
|
+
};
|
|
7
12
|
export declare function getAriaChecked(checked: boolean, indeterminate: boolean): "true" | "false" | "mixed";
|
|
8
13
|
export type BitsAttrsConfig<T extends readonly string[]> = {
|
|
9
14
|
component: string;
|
package/dist/internal/attrs.js
CHANGED
|
@@ -16,10 +16,16 @@ export function getDataOpenClosed(condition) {
|
|
|
16
16
|
export function getDataChecked(condition) {
|
|
17
17
|
return condition ? "checked" : "unchecked";
|
|
18
18
|
}
|
|
19
|
+
export function getDataTransitionAttrs(state) {
|
|
20
|
+
if (state === "starting")
|
|
21
|
+
return { "data-starting-style": "" };
|
|
22
|
+
if (state === "ending")
|
|
23
|
+
return { "data-ending-style": "" };
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
19
26
|
export function getAriaChecked(checked, indeterminate) {
|
|
20
|
-
if (indeterminate)
|
|
27
|
+
if (indeterminate)
|
|
21
28
|
return "mixed";
|
|
22
|
-
}
|
|
23
29
|
return checked ? "true" : "false";
|
|
24
30
|
}
|
|
25
31
|
export class BitsAttrs {
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReadableBoxedValues } from "svelte-toolbelt";
|
|
2
|
+
import type { TransitionState } from "./attrs.js";
|
|
2
3
|
interface PresenceManagerOpts extends ReadableBoxedValues<{
|
|
3
4
|
open: boolean;
|
|
4
5
|
ref: HTMLElement | null;
|
|
5
6
|
}> {
|
|
6
7
|
onComplete?: () => void;
|
|
7
8
|
enabled?: boolean;
|
|
9
|
+
shouldSkipExitAnimation?: () => boolean;
|
|
8
10
|
}
|
|
9
11
|
export declare class PresenceManager {
|
|
10
12
|
#private;
|
|
11
13
|
constructor(opts: PresenceManagerOpts);
|
|
12
14
|
get shouldRender(): boolean;
|
|
15
|
+
get transitionStatus(): TransitionState;
|
|
13
16
|
}
|
|
14
17
|
export {};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { watch } from "runed";
|
|
2
|
+
import { onDestroyEffect } from "svelte-toolbelt";
|
|
2
3
|
import { AnimationsComplete } from "./animations-complete.js";
|
|
3
4
|
export class PresenceManager {
|
|
4
5
|
#opts;
|
|
5
6
|
#enabled;
|
|
6
7
|
#afterAnimations;
|
|
7
8
|
#shouldRender = $state(false);
|
|
9
|
+
#transitionStatus = $state(undefined);
|
|
10
|
+
#hasMounted = false;
|
|
11
|
+
#transitionFrame = null;
|
|
8
12
|
constructor(opts) {
|
|
9
13
|
this.#opts = opts;
|
|
10
14
|
this.#shouldRender = opts.open.current;
|
|
@@ -13,16 +17,44 @@ export class PresenceManager {
|
|
|
13
17
|
ref: this.#opts.ref,
|
|
14
18
|
afterTick: this.#opts.open,
|
|
15
19
|
});
|
|
20
|
+
onDestroyEffect(() => this.#clearTransitionFrame());
|
|
16
21
|
watch(() => this.#opts.open.current, (isOpen) => {
|
|
22
|
+
if (!this.#hasMounted) {
|
|
23
|
+
this.#hasMounted = true;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.#clearTransitionFrame();
|
|
27
|
+
if (!isOpen && this.#opts.shouldSkipExitAnimation?.()) {
|
|
28
|
+
this.#shouldRender = false;
|
|
29
|
+
this.#transitionStatus = undefined;
|
|
30
|
+
this.#opts.onComplete?.();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
17
33
|
if (isOpen)
|
|
18
34
|
this.#shouldRender = true;
|
|
19
|
-
|
|
35
|
+
this.#transitionStatus = isOpen ? "starting" : "ending";
|
|
36
|
+
if (isOpen) {
|
|
37
|
+
this.#transitionFrame = window.requestAnimationFrame(() => {
|
|
38
|
+
this.#transitionFrame = null;
|
|
39
|
+
if (this.#opts.open.current) {
|
|
40
|
+
this.#transitionStatus = undefined;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (!this.#enabled) {
|
|
45
|
+
if (!isOpen) {
|
|
46
|
+
this.#shouldRender = false;
|
|
47
|
+
}
|
|
48
|
+
this.#transitionStatus = undefined;
|
|
49
|
+
this.#opts.onComplete?.();
|
|
20
50
|
return;
|
|
51
|
+
}
|
|
21
52
|
this.#afterAnimations.run(() => {
|
|
22
53
|
if (isOpen === this.#opts.open.current) {
|
|
23
54
|
if (!this.#opts.open.current) {
|
|
24
55
|
this.#shouldRender = false;
|
|
25
56
|
}
|
|
57
|
+
this.#transitionStatus = undefined;
|
|
26
58
|
this.#opts.onComplete?.();
|
|
27
59
|
}
|
|
28
60
|
});
|
|
@@ -31,4 +63,13 @@ export class PresenceManager {
|
|
|
31
63
|
get shouldRender() {
|
|
32
64
|
return this.#shouldRender;
|
|
33
65
|
}
|
|
66
|
+
get transitionStatus() {
|
|
67
|
+
return this.#transitionStatus;
|
|
68
|
+
}
|
|
69
|
+
#clearTransitionFrame() {
|
|
70
|
+
if (this.#transitionFrame === null)
|
|
71
|
+
return;
|
|
72
|
+
window.cancelAnimationFrame(this.#transitionFrame);
|
|
73
|
+
this.#transitionFrame = null;
|
|
74
|
+
}
|
|
34
75
|
}
|