bits-ui 2.13.1 → 2.14.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 +5 -1
- package/dist/bits/accordion/accordion.svelte.js +21 -2
- package/dist/bits/accordion/components/accordion-content.svelte +12 -24
- package/dist/bits/alert-dialog/components/alert-dialog-content.svelte +50 -62
- package/dist/bits/collapsible/collapsible.svelte.d.ts +4 -1
- package/dist/bits/collapsible/collapsible.svelte.js +15 -2
- package/dist/bits/collapsible/components/collapsible-content.svelte +12 -24
- package/dist/bits/context-menu/components/context-menu-content-static.svelte +2 -0
- package/dist/bits/context-menu/components/context-menu-content.svelte +2 -0
- package/dist/bits/date-field/date-field.svelte.js +5 -3
- package/dist/bits/dialog/components/dialog-content.svelte +44 -57
- package/dist/bits/dialog/components/dialog-overlay.svelte +9 -12
- package/dist/bits/dialog/dialog.svelte.d.ts +6 -0
- package/dist/bits/dialog/dialog.svelte.js +17 -3
- package/dist/bits/dropdown-menu/components/dropdown-menu-content-static.svelte +2 -0
- package/dist/bits/dropdown-menu/components/dropdown-menu-content.svelte +2 -0
- package/dist/bits/link-preview/components/link-preview-content-static.svelte +2 -0
- package/dist/bits/link-preview/components/link-preview-content.svelte +2 -0
- package/dist/bits/link-preview/link-preview.svelte.d.ts +3 -0
- package/dist/bits/link-preview/link-preview.svelte.js +6 -2
- package/dist/bits/menu/components/menu-content-static.svelte +2 -0
- package/dist/bits/menu/components/menu-content.svelte +2 -0
- package/dist/bits/menu/components/menu-sub-content-static.svelte +2 -0
- package/dist/bits/menu/components/menu-sub-content.svelte +2 -0
- package/dist/bits/menu/menu.svelte.d.ts +3 -0
- package/dist/bits/menu/menu.svelte.js +6 -2
- package/dist/bits/popover/components/popover-content-static.svelte +2 -0
- package/dist/bits/popover/components/popover-content.svelte +2 -0
- package/dist/bits/popover/components/popover-overlay.svelte +37 -0
- package/dist/bits/popover/components/popover-overlay.svelte.d.ts +4 -0
- package/dist/bits/popover/exports.d.ts +2 -1
- package/dist/bits/popover/exports.js +1 -0
- package/dist/bits/popover/popover.svelte.d.ts +25 -0
- package/dist/bits/popover/popover.svelte.js +39 -3
- package/dist/bits/popover/types.d.ts +6 -0
- package/dist/bits/select/components/select-content-static.svelte +2 -0
- package/dist/bits/select/components/select-content.svelte +2 -0
- package/dist/bits/select/select.svelte.d.ts +3 -0
- package/dist/bits/select/select.svelte.js +6 -2
- package/dist/bits/tooltip/components/tooltip-content-static.svelte +2 -0
- package/dist/bits/tooltip/components/tooltip-content.svelte +2 -0
- package/dist/bits/tooltip/tooltip.svelte.d.ts +3 -0
- package/dist/bits/tooltip/tooltip.svelte.js +6 -2
- package/dist/bits/utilities/popper-layer/popper-layer-inner.svelte +1 -1
- package/dist/bits/utilities/popper-layer/popper-layer-inner.svelte.d.ts +1 -1
- package/dist/bits/utilities/popper-layer/popper-layer.svelte +43 -45
- package/dist/bits/utilities/popper-layer/types.d.ts +4 -0
- package/dist/internal/animations-complete.js +7 -15
- package/dist/internal/date-time/field/helpers.js +3 -1
- package/dist/internal/date-time/field/time-helpers.js +4 -1
- package/dist/internal/presence-manager.svelte.d.ts +14 -0
- package/dist/internal/presence-manager.svelte.js +34 -0
- package/dist/internal/should-enable-focus-trap.d.ts +1 -2
- package/dist/internal/should-enable-focus-trap.js +2 -2
- package/package.json +2 -2
- package/dist/internal/open-change-complete.d.ts +0 -13
- package/dist/internal/open-change-complete.js +0 -24
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReadableBoxedValues, type WritableBoxedValues } from "svelte-toolbelt";
|
|
2
2
|
import type { BitsKeyboardEvent, BitsMouseEvent, OnChangeFn, RefAttachment, WithRefOpts } from "../../internal/types.js";
|
|
3
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
3
4
|
type DialogVariant = "alert-dialog" | "dialog";
|
|
4
5
|
declare const dialogAttrs: import("../../internal/attrs.js").CreateBitsAttrsReturn<readonly ["content", "trigger", "overlay", "title", "description", "close", "cancel", "action"]>;
|
|
5
6
|
interface DialogRootStateOpts extends WritableBoxedValues<{
|
|
@@ -14,6 +15,7 @@ export declare class DialogRootState {
|
|
|
14
15
|
readonly opts: DialogRootStateOpts;
|
|
15
16
|
triggerNode: HTMLElement | null;
|
|
16
17
|
contentNode: HTMLElement | null;
|
|
18
|
+
overlayNode: HTMLElement | null;
|
|
17
19
|
descriptionNode: HTMLElement | null;
|
|
18
20
|
contentId: string | undefined;
|
|
19
21
|
titleId: string | undefined;
|
|
@@ -23,6 +25,8 @@ export declare class DialogRootState {
|
|
|
23
25
|
nestedOpenCount: number;
|
|
24
26
|
readonly depth: number;
|
|
25
27
|
readonly parent: DialogRootState | null;
|
|
28
|
+
contentPresence: PresenceManager;
|
|
29
|
+
overlayPresence: PresenceManager;
|
|
26
30
|
constructor(opts: DialogRootStateOpts, parent: DialogRootState | null);
|
|
27
31
|
handleOpen(): void;
|
|
28
32
|
handleClose(): void;
|
|
@@ -149,6 +153,7 @@ export declare class DialogContentState {
|
|
|
149
153
|
readonly "data-nested-open": "" | undefined;
|
|
150
154
|
readonly "data-nested": "" | undefined;
|
|
151
155
|
};
|
|
156
|
+
get shouldRender(): boolean;
|
|
152
157
|
}
|
|
153
158
|
interface DialogOverlayStateOpts extends WithRefOpts {
|
|
154
159
|
}
|
|
@@ -172,6 +177,7 @@ export declare class DialogOverlayState {
|
|
|
172
177
|
readonly "data-nested-open": "" | undefined;
|
|
173
178
|
readonly "data-nested": "" | undefined;
|
|
174
179
|
};
|
|
180
|
+
get shouldRender(): boolean;
|
|
175
181
|
}
|
|
176
182
|
interface AlertDialogCancelStateOpts extends WithRefOpts, ReadableBoxedValues<{
|
|
177
183
|
disabled: boolean;
|
|
@@ -2,7 +2,7 @@ import { attachRef, boxWith, onDestroyEffect, } from "svelte-toolbelt";
|
|
|
2
2
|
import { Context, watch } from "runed";
|
|
3
3
|
import { createBitsAttrs, boolToStr, getDataOpenClosed, boolToEmptyStrOrUndef, } from "../../internal/attrs.js";
|
|
4
4
|
import { kbd } from "../../internal/kbd.js";
|
|
5
|
-
import {
|
|
5
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
6
6
|
const dialogAttrs = createBitsAttrs({
|
|
7
7
|
component: "dialog",
|
|
8
8
|
parts: ["content", "trigger", "overlay", "title", "description", "close", "cancel", "action"],
|
|
@@ -16,6 +16,7 @@ export class DialogRootState {
|
|
|
16
16
|
opts;
|
|
17
17
|
triggerNode = $state(null);
|
|
18
18
|
contentNode = $state(null);
|
|
19
|
+
overlayNode = $state(null);
|
|
19
20
|
descriptionNode = $state(null);
|
|
20
21
|
contentId = $state(undefined);
|
|
21
22
|
titleId = $state(undefined);
|
|
@@ -25,13 +26,15 @@ export class DialogRootState {
|
|
|
25
26
|
nestedOpenCount = $state(0);
|
|
26
27
|
depth;
|
|
27
28
|
parent;
|
|
29
|
+
contentPresence;
|
|
30
|
+
overlayPresence;
|
|
28
31
|
constructor(opts, parent) {
|
|
29
32
|
this.opts = opts;
|
|
30
33
|
this.parent = parent;
|
|
31
34
|
this.depth = parent ? parent.depth + 1 : 0;
|
|
32
35
|
this.handleOpen = this.handleOpen.bind(this);
|
|
33
36
|
this.handleClose = this.handleClose.bind(this);
|
|
34
|
-
new
|
|
37
|
+
this.contentPresence = new PresenceManager({
|
|
35
38
|
ref: boxWith(() => this.contentNode),
|
|
36
39
|
open: this.opts.open,
|
|
37
40
|
enabled: true,
|
|
@@ -39,6 +42,11 @@ export class DialogRootState {
|
|
|
39
42
|
this.opts.onOpenChangeComplete.current(this.opts.open.current);
|
|
40
43
|
},
|
|
41
44
|
});
|
|
45
|
+
this.overlayPresence = new PresenceManager({
|
|
46
|
+
ref: boxWith(() => this.overlayNode),
|
|
47
|
+
open: this.opts.open,
|
|
48
|
+
enabled: true,
|
|
49
|
+
});
|
|
42
50
|
watch(() => this.opts.open.current, (isOpen) => {
|
|
43
51
|
if (!this.parent)
|
|
44
52
|
return;
|
|
@@ -271,6 +279,9 @@ export class DialogContentState {
|
|
|
271
279
|
...this.root.sharedProps,
|
|
272
280
|
...this.attachment,
|
|
273
281
|
}));
|
|
282
|
+
get shouldRender() {
|
|
283
|
+
return this.root.contentPresence.shouldRender;
|
|
284
|
+
}
|
|
274
285
|
}
|
|
275
286
|
export class DialogOverlayState {
|
|
276
287
|
static create(opts) {
|
|
@@ -282,7 +293,7 @@ export class DialogOverlayState {
|
|
|
282
293
|
constructor(opts, root) {
|
|
283
294
|
this.opts = opts;
|
|
284
295
|
this.root = root;
|
|
285
|
-
this.attachment = attachRef(this.opts.ref);
|
|
296
|
+
this.attachment = attachRef(this.opts.ref, (v) => (this.root.overlayNode = v));
|
|
286
297
|
}
|
|
287
298
|
snippetProps = $derived.by(() => ({ open: this.root.opts.open.current }));
|
|
288
299
|
props = $derived.by(() => ({
|
|
@@ -298,6 +309,9 @@ export class DialogOverlayState {
|
|
|
298
309
|
...this.root.sharedProps,
|
|
299
310
|
...this.attachment,
|
|
300
311
|
}));
|
|
312
|
+
get shouldRender() {
|
|
313
|
+
return this.root.overlayPresence.shouldRender;
|
|
314
|
+
}
|
|
301
315
|
}
|
|
302
316
|
export class AlertDialogCancelState {
|
|
303
317
|
static create(opts) {
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
forceMount={true}
|
|
63
63
|
isStatic
|
|
64
64
|
{id}
|
|
65
|
+
shouldRender={contentState.shouldRender}
|
|
65
66
|
>
|
|
66
67
|
{#snippet popper({ props })}
|
|
67
68
|
{@const finalProps = mergeProps(props, {
|
|
@@ -89,6 +90,7 @@
|
|
|
89
90
|
forceMount={false}
|
|
90
91
|
isStatic
|
|
91
92
|
{id}
|
|
93
|
+
shouldRender={contentState.shouldRender}
|
|
92
94
|
>
|
|
93
95
|
{#snippet popper({ props })}
|
|
94
96
|
{@const finalProps = mergeProps(props, {
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
{loop}
|
|
69
69
|
forceMount={true}
|
|
70
70
|
{id}
|
|
71
|
+
shouldRender={contentState.shouldRender}
|
|
71
72
|
>
|
|
72
73
|
{#snippet popper({ props, wrapperProps })}
|
|
73
74
|
{@const finalProps = mergeProps(props, {
|
|
@@ -96,6 +97,7 @@
|
|
|
96
97
|
{loop}
|
|
97
98
|
forceMount={false}
|
|
98
99
|
{id}
|
|
100
|
+
shouldRender={contentState.shouldRender}
|
|
99
101
|
>
|
|
100
102
|
{#snippet popper({ props, wrapperProps })}
|
|
101
103
|
{@const finalProps = mergeProps(props, {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
loop={false}
|
|
48
48
|
preventScroll={false}
|
|
49
49
|
forceMount={true}
|
|
50
|
+
shouldRender={contentState.shouldRender}
|
|
50
51
|
>
|
|
51
52
|
{#snippet popper({ props })}
|
|
52
53
|
{@const mergedProps = mergeProps(props, {
|
|
@@ -74,6 +75,7 @@
|
|
|
74
75
|
loop={false}
|
|
75
76
|
preventScroll={false}
|
|
76
77
|
forceMount={false}
|
|
78
|
+
shouldRender={contentState.shouldRender}
|
|
77
79
|
>
|
|
78
80
|
{#snippet popper({ props })}
|
|
79
81
|
{@const mergedProps = mergeProps(props, {
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
loop={false}
|
|
66
66
|
preventScroll={false}
|
|
67
67
|
forceMount={true}
|
|
68
|
+
shouldRender={contentState.shouldRender}
|
|
68
69
|
>
|
|
69
70
|
{#snippet popper({ props, wrapperProps })}
|
|
70
71
|
{@const mergedProps = mergeProps(props, {
|
|
@@ -93,6 +94,7 @@
|
|
|
93
94
|
loop={false}
|
|
94
95
|
preventScroll={false}
|
|
95
96
|
forceMount={false}
|
|
97
|
+
shouldRender={contentState.shouldRender}
|
|
96
98
|
>
|
|
97
99
|
{#snippet popper({ props, wrapperProps })}
|
|
98
100
|
{@const mergedProps = mergeProps(props, {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DOMContext, type ReadableBoxedValues, type WritableBoxedValues } from "svelte-toolbelt";
|
|
2
2
|
import type { BitsFocusEvent, BitsPointerEvent, OnChangeFn, RefAttachment, WithRefOpts } from "../../internal/types.js";
|
|
3
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
3
4
|
interface LinkPreviewRootStateOpts extends WritableBoxedValues<{
|
|
4
5
|
open: boolean;
|
|
5
6
|
}>, ReadableBoxedValues<{
|
|
@@ -18,6 +19,7 @@ export declare class LinkPreviewRootState {
|
|
|
18
19
|
timeout: number | null;
|
|
19
20
|
contentNode: HTMLElement | null;
|
|
20
21
|
contentMounted: boolean;
|
|
22
|
+
contentPresence: PresenceManager;
|
|
21
23
|
triggerNode: HTMLElement | null;
|
|
22
24
|
isOpening: boolean;
|
|
23
25
|
domContext: DOMContext;
|
|
@@ -70,6 +72,7 @@ export declare class LinkPreviewContentState {
|
|
|
70
72
|
onEscapeKeydown: (e: KeyboardEvent) => void;
|
|
71
73
|
onOpenAutoFocus: (e: Event) => void;
|
|
72
74
|
onCloseAutoFocus: (e: Event) => void;
|
|
75
|
+
get shouldRender(): boolean;
|
|
73
76
|
readonly snippetProps: {
|
|
74
77
|
open: boolean;
|
|
75
78
|
};
|
|
@@ -5,7 +5,7 @@ import { createBitsAttrs, boolToStr, getDataOpenClosed } from "../../internal/at
|
|
|
5
5
|
import { isElement, isFocusVisible, isTouch } from "../../internal/is.js";
|
|
6
6
|
import { getTabbableCandidates } from "../../internal/focus.js";
|
|
7
7
|
import { GraceArea } from "../../internal/grace-area.svelte.js";
|
|
8
|
-
import {
|
|
8
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
9
9
|
const linkPreviewAttrs = createBitsAttrs({
|
|
10
10
|
component: "link-preview",
|
|
11
11
|
parts: ["content", "trigger"],
|
|
@@ -22,12 +22,13 @@ export class LinkPreviewRootState {
|
|
|
22
22
|
timeout = null;
|
|
23
23
|
contentNode = $state(null);
|
|
24
24
|
contentMounted = $state(false);
|
|
25
|
+
contentPresence;
|
|
25
26
|
triggerNode = $state(null);
|
|
26
27
|
isOpening = false;
|
|
27
28
|
domContext = new DOMContext(() => null);
|
|
28
29
|
constructor(opts) {
|
|
29
30
|
this.opts = opts;
|
|
30
|
-
new
|
|
31
|
+
this.contentPresence = new PresenceManager({
|
|
31
32
|
ref: boxWith(() => this.contentNode),
|
|
32
33
|
open: this.opts.open,
|
|
33
34
|
onComplete: () => {
|
|
@@ -216,6 +217,9 @@ export class LinkPreviewContentState {
|
|
|
216
217
|
onCloseAutoFocus = (e) => {
|
|
217
218
|
e.preventDefault();
|
|
218
219
|
};
|
|
220
|
+
get shouldRender() {
|
|
221
|
+
return this.root.contentPresence.shouldRender;
|
|
222
|
+
}
|
|
219
223
|
snippetProps = $derived.by(() => ({ open: this.root.opts.open.current }));
|
|
220
224
|
props = $derived.by(() => ({
|
|
221
225
|
id: this.opts.id.current,
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
forceMount={true}
|
|
66
66
|
isStatic
|
|
67
67
|
{id}
|
|
68
|
+
shouldRender={contentState.shouldRender}
|
|
68
69
|
>
|
|
69
70
|
{#snippet popper({ props })}
|
|
70
71
|
{@const finalProps = mergeProps(props, {
|
|
@@ -95,6 +96,7 @@
|
|
|
95
96
|
forceMount={false}
|
|
96
97
|
isStatic
|
|
97
98
|
{id}
|
|
99
|
+
shouldRender={contentState.shouldRender}
|
|
98
100
|
>
|
|
99
101
|
{#snippet popper({ props })}
|
|
100
102
|
{@const finalProps = mergeProps(props, {
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
{loop}
|
|
70
70
|
forceMount={true}
|
|
71
71
|
{id}
|
|
72
|
+
shouldRender={contentState.shouldRender}
|
|
72
73
|
>
|
|
73
74
|
{#snippet popper({ props, wrapperProps })}
|
|
74
75
|
{@const finalProps = mergeProps(props, {
|
|
@@ -100,6 +101,7 @@
|
|
|
100
101
|
{loop}
|
|
101
102
|
forceMount={false}
|
|
102
103
|
{id}
|
|
104
|
+
shouldRender={contentState.shouldRender}
|
|
103
105
|
>
|
|
104
106
|
{#snippet popper({ props, wrapperProps })}
|
|
105
107
|
{@const finalProps = mergeProps(props, {
|
|
@@ -120,6 +120,7 @@
|
|
|
120
120
|
{loop}
|
|
121
121
|
{trapFocus}
|
|
122
122
|
isStatic
|
|
123
|
+
shouldRender={subContentState.shouldRender}
|
|
123
124
|
>
|
|
124
125
|
{#snippet popper({ props })}
|
|
125
126
|
{@const finalProps = mergeProps(props, mergedProps, {
|
|
@@ -150,6 +151,7 @@
|
|
|
150
151
|
{loop}
|
|
151
152
|
{trapFocus}
|
|
152
153
|
isStatic
|
|
154
|
+
shouldRender={subContentState.shouldRender}
|
|
153
155
|
>
|
|
154
156
|
{#snippet popper({ props })}
|
|
155
157
|
{@const finalProps = mergeProps(props, mergedProps, {
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
preventScroll={false}
|
|
122
122
|
{loop}
|
|
123
123
|
{trapFocus}
|
|
124
|
+
shouldRender={subContentState.shouldRender}
|
|
124
125
|
>
|
|
125
126
|
{#snippet popper({ props, wrapperProps })}
|
|
126
127
|
{@const finalProps = mergeProps(props, mergedProps, {
|
|
@@ -156,6 +157,7 @@
|
|
|
156
157
|
preventScroll={false}
|
|
157
158
|
{loop}
|
|
158
159
|
{trapFocus}
|
|
160
|
+
shouldRender={subContentState.shouldRender}
|
|
159
161
|
>
|
|
160
162
|
{#snippet popper({ props, wrapperProps })}
|
|
161
163
|
{@const finalProps = mergeProps(props, mergedProps, {
|
|
@@ -6,6 +6,7 @@ import type { Direction } from "../../shared/index.js";
|
|
|
6
6
|
import { IsUsingKeyboard } from "../../index.js";
|
|
7
7
|
import type { KeyboardEventHandler, PointerEventHandler, MouseEventHandler } from "svelte/elements";
|
|
8
8
|
import { RovingFocusGroup } from "../../internal/roving-focus-group.js";
|
|
9
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
9
10
|
export declare const CONTEXT_MENU_TRIGGER_ATTR = "data-context-menu-trigger";
|
|
10
11
|
export declare const CONTEXT_MENU_CONTENT_ATTR = "data-context-menu-content";
|
|
11
12
|
export declare const MenuCheckboxGroupContext: Context<MenuCheckboxGroupState>;
|
|
@@ -40,6 +41,7 @@ export declare class MenuMenuState {
|
|
|
40
41
|
readonly parentMenu: MenuMenuState | null;
|
|
41
42
|
contentId: ReadableBox<string>;
|
|
42
43
|
contentNode: HTMLElement | null;
|
|
44
|
+
contentPresence: PresenceManager;
|
|
43
45
|
triggerNode: HTMLElement | null;
|
|
44
46
|
constructor(opts: MenuMenuStateOpts, root: MenuRootState, parentMenu: MenuMenuState | null);
|
|
45
47
|
toggleOpen(): void;
|
|
@@ -72,6 +74,7 @@ export declare class MenuContentState {
|
|
|
72
74
|
onItemLeave(e: BitsPointerEvent): void;
|
|
73
75
|
onTriggerLeave(): boolean;
|
|
74
76
|
handleInteractOutside(e: PointerEvent): void;
|
|
77
|
+
get shouldRender(): boolean;
|
|
75
78
|
readonly snippetProps: {
|
|
76
79
|
open: boolean;
|
|
77
80
|
};
|
|
@@ -12,7 +12,7 @@ import { isTabbable } from "tabbable";
|
|
|
12
12
|
import { DOMTypeahead } from "../../internal/dom-typeahead.svelte.js";
|
|
13
13
|
import { RovingFocusGroup } from "../../internal/roving-focus-group.js";
|
|
14
14
|
import { GraceArea } from "../../internal/grace-area.svelte.js";
|
|
15
|
-
import {
|
|
15
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
16
16
|
export const CONTEXT_MENU_TRIGGER_ATTR = "data-context-menu-trigger";
|
|
17
17
|
export const CONTEXT_MENU_CONTENT_ATTR = "data-context-menu-content";
|
|
18
18
|
const MenuRootContext = new Context("Menu.Root");
|
|
@@ -68,12 +68,13 @@ export class MenuMenuState {
|
|
|
68
68
|
parentMenu;
|
|
69
69
|
contentId = boxWith(() => "");
|
|
70
70
|
contentNode = $state(null);
|
|
71
|
+
contentPresence;
|
|
71
72
|
triggerNode = $state(null);
|
|
72
73
|
constructor(opts, root, parentMenu) {
|
|
73
74
|
this.opts = opts;
|
|
74
75
|
this.root = root;
|
|
75
76
|
this.parentMenu = parentMenu;
|
|
76
|
-
new
|
|
77
|
+
this.contentPresence = new PresenceManager({
|
|
77
78
|
ref: boxWith(() => this.contentNode),
|
|
78
79
|
open: this.opts.open,
|
|
79
80
|
onComplete: () => {
|
|
@@ -306,6 +307,9 @@ export class MenuContentState {
|
|
|
306
307
|
e.preventDefault();
|
|
307
308
|
}
|
|
308
309
|
}
|
|
310
|
+
get shouldRender() {
|
|
311
|
+
return this.parentMenu.contentPresence.shouldRender;
|
|
312
|
+
}
|
|
309
313
|
snippetProps = $derived.by(() => ({ open: this.parentMenu.opts.open.current }));
|
|
310
314
|
props = $derived.by(() => ({
|
|
311
315
|
id: this.opts.id.current,
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
loop
|
|
52
52
|
forceMount={true}
|
|
53
53
|
{onCloseAutoFocus}
|
|
54
|
+
shouldRender={contentState.shouldRender}
|
|
54
55
|
>
|
|
55
56
|
{#snippet popper({ props })}
|
|
56
57
|
{@const finalProps = mergeProps(props, {
|
|
@@ -78,6 +79,7 @@
|
|
|
78
79
|
loop
|
|
79
80
|
forceMount={false}
|
|
80
81
|
{onCloseAutoFocus}
|
|
82
|
+
shouldRender={contentState.shouldRender}
|
|
81
83
|
>
|
|
82
84
|
{#snippet popper({ props })}
|
|
83
85
|
{@const finalProps = mergeProps(props, {
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
forceMount={true}
|
|
53
53
|
{customAnchor}
|
|
54
54
|
{onCloseAutoFocus}
|
|
55
|
+
shouldRender={contentState.shouldRender}
|
|
55
56
|
>
|
|
56
57
|
{#snippet popper({ props, wrapperProps })}
|
|
57
58
|
{@const finalProps = mergeProps(props, {
|
|
@@ -81,6 +82,7 @@
|
|
|
81
82
|
forceMount={false}
|
|
82
83
|
{customAnchor}
|
|
83
84
|
{onCloseAutoFocus}
|
|
85
|
+
shouldRender={contentState.shouldRender}
|
|
84
86
|
>
|
|
85
87
|
{#snippet popper({ props, wrapperProps })}
|
|
86
88
|
{@const finalProps = mergeProps(props, {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { boxWith, mergeProps } from "svelte-toolbelt";
|
|
3
|
+
import { PopoverOverlayState } from "../popover.svelte.js";
|
|
4
|
+
import type { PopoverOverlayProps } from "../types.js";
|
|
5
|
+
import { createId } from "../../../internal/create-id.js";
|
|
6
|
+
|
|
7
|
+
const uid = $props.id();
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
id = createId(uid),
|
|
11
|
+
forceMount = false,
|
|
12
|
+
child,
|
|
13
|
+
children,
|
|
14
|
+
ref = $bindable(null),
|
|
15
|
+
...restProps
|
|
16
|
+
}: PopoverOverlayProps = $props();
|
|
17
|
+
|
|
18
|
+
const overlayState = PopoverOverlayState.create({
|
|
19
|
+
id: boxWith(() => id),
|
|
20
|
+
ref: boxWith(
|
|
21
|
+
() => ref,
|
|
22
|
+
(v) => (ref = v)
|
|
23
|
+
),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const mergedProps = $derived(mergeProps(restProps, overlayState.props));
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
{#if overlayState.shouldRender || forceMount}
|
|
30
|
+
{#if child}
|
|
31
|
+
{@render child({ props: mergeProps(mergedProps), ...overlayState.snippetProps })}
|
|
32
|
+
{:else}
|
|
33
|
+
<div {...mergeProps(mergedProps)}>
|
|
34
|
+
{@render children?.(overlayState.snippetProps)}
|
|
35
|
+
</div>
|
|
36
|
+
{/if}
|
|
37
|
+
{/if}
|
|
@@ -5,4 +5,5 @@ export { default as ContentStatic } from "./components/popover-content-static.sv
|
|
|
5
5
|
export { default as Trigger } from "./components/popover-trigger.svelte";
|
|
6
6
|
export { default as Close } from "./components/popover-close.svelte";
|
|
7
7
|
export { default as Portal } from "../utilities/portal/portal.svelte";
|
|
8
|
-
export
|
|
8
|
+
export { default as Overlay } from "./components/popover-overlay.svelte";
|
|
9
|
+
export type { PopoverRootProps as RootProps, PopoverArrowProps as ArrowProps, PopoverContentProps as ContentProps, PopoverContentStaticProps as ContentStaticProps, PopoverTriggerProps as TriggerProps, PopoverCloseProps as CloseProps, PopoverPortalProps as PortalProps, PopoverOverlayProps as OverlayProps, } from "./types.js";
|
|
@@ -5,3 +5,4 @@ export { default as ContentStatic } from "./components/popover-content-static.sv
|
|
|
5
5
|
export { default as Trigger } from "./components/popover-trigger.svelte";
|
|
6
6
|
export { default as Close } from "./components/popover-close.svelte";
|
|
7
7
|
export { default as Portal } from "../utilities/portal/portal.svelte";
|
|
8
|
+
export { default as Overlay } from "./components/popover-overlay.svelte";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReadableBoxedValues, type WritableBoxedValues } from "svelte-toolbelt";
|
|
2
2
|
import type { BitsKeyboardEvent, BitsMouseEvent, BitsPointerEvent, OnChangeFn, RefAttachment, WithRefOpts } from "../../internal/types.js";
|
|
3
3
|
import type { Measurable } from "../../internal/floating-svelte/types.js";
|
|
4
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
4
5
|
interface PopoverRootStateOpts extends WritableBoxedValues<{
|
|
5
6
|
open: boolean;
|
|
6
7
|
}>, ReadableBoxedValues<{
|
|
@@ -11,7 +12,10 @@ export declare class PopoverRootState {
|
|
|
11
12
|
static create(opts: PopoverRootStateOpts): PopoverRootState;
|
|
12
13
|
readonly opts: PopoverRootStateOpts;
|
|
13
14
|
contentNode: HTMLElement | null;
|
|
15
|
+
contentPresence: PresenceManager;
|
|
14
16
|
triggerNode: HTMLElement | null;
|
|
17
|
+
overlayNode: HTMLElement | null;
|
|
18
|
+
overlayPresence: PresenceManager;
|
|
15
19
|
constructor(opts: PopoverRootStateOpts);
|
|
16
20
|
toggleOpen(): void;
|
|
17
21
|
handleClose(): void;
|
|
@@ -54,6 +58,7 @@ export declare class PopoverContentState {
|
|
|
54
58
|
constructor(opts: PopoverContentStateOpts, root: PopoverRootState);
|
|
55
59
|
onInteractOutside: (e: PointerEvent) => void;
|
|
56
60
|
onEscapeKeydown: (e: KeyboardEvent) => void;
|
|
61
|
+
get shouldRender(): boolean;
|
|
57
62
|
readonly snippetProps: {
|
|
58
63
|
open: boolean;
|
|
59
64
|
};
|
|
@@ -87,4 +92,24 @@ export declare class PopoverCloseState {
|
|
|
87
92
|
readonly type: "button";
|
|
88
93
|
};
|
|
89
94
|
}
|
|
95
|
+
interface PopoverOverlayStateOpts extends WithRefOpts {
|
|
96
|
+
}
|
|
97
|
+
export declare class PopoverOverlayState {
|
|
98
|
+
static create(opts: PopoverOverlayStateOpts): PopoverOverlayState;
|
|
99
|
+
readonly opts: PopoverOverlayStateOpts;
|
|
100
|
+
readonly root: PopoverRootState;
|
|
101
|
+
readonly attachment: RefAttachment;
|
|
102
|
+
constructor(opts: PopoverOverlayStateOpts, root: PopoverRootState);
|
|
103
|
+
get shouldRender(): boolean;
|
|
104
|
+
readonly snippetProps: {
|
|
105
|
+
open: boolean;
|
|
106
|
+
};
|
|
107
|
+
readonly props: {
|
|
108
|
+
readonly id: string;
|
|
109
|
+
readonly style: {
|
|
110
|
+
readonly pointerEvents: "auto";
|
|
111
|
+
};
|
|
112
|
+
readonly "data-state": "open" | "closed";
|
|
113
|
+
};
|
|
114
|
+
}
|
|
90
115
|
export {};
|
|
@@ -3,10 +3,10 @@ import { Context } from "runed";
|
|
|
3
3
|
import { kbd } from "../../internal/kbd.js";
|
|
4
4
|
import { createBitsAttrs, boolToStr, getDataOpenClosed } from "../../internal/attrs.js";
|
|
5
5
|
import { isElement } from "../../internal/is.js";
|
|
6
|
-
import {
|
|
6
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
7
7
|
const popoverAttrs = createBitsAttrs({
|
|
8
8
|
component: "popover",
|
|
9
|
-
parts: ["root", "trigger", "content", "close"],
|
|
9
|
+
parts: ["root", "trigger", "content", "close", "overlay"],
|
|
10
10
|
});
|
|
11
11
|
const PopoverRootContext = new Context("Popover.Root");
|
|
12
12
|
export class PopoverRootState {
|
|
@@ -15,16 +15,23 @@ export class PopoverRootState {
|
|
|
15
15
|
}
|
|
16
16
|
opts;
|
|
17
17
|
contentNode = $state(null);
|
|
18
|
+
contentPresence;
|
|
18
19
|
triggerNode = $state(null);
|
|
20
|
+
overlayNode = $state(null);
|
|
21
|
+
overlayPresence;
|
|
19
22
|
constructor(opts) {
|
|
20
23
|
this.opts = opts;
|
|
21
|
-
new
|
|
24
|
+
this.contentPresence = new PresenceManager({
|
|
22
25
|
ref: boxWith(() => this.contentNode),
|
|
23
26
|
open: this.opts.open,
|
|
24
27
|
onComplete: () => {
|
|
25
28
|
this.opts.onOpenChangeComplete.current(this.opts.open.current);
|
|
26
29
|
},
|
|
27
30
|
});
|
|
31
|
+
this.overlayPresence = new PresenceManager({
|
|
32
|
+
ref: boxWith(() => this.overlayNode),
|
|
33
|
+
open: this.opts.open,
|
|
34
|
+
});
|
|
28
35
|
}
|
|
29
36
|
toggleOpen() {
|
|
30
37
|
this.opts.open.current = !this.opts.open.current;
|
|
@@ -124,6 +131,9 @@ export class PopoverContentState {
|
|
|
124
131
|
return;
|
|
125
132
|
this.root.handleClose();
|
|
126
133
|
};
|
|
134
|
+
get shouldRender() {
|
|
135
|
+
return this.root.contentPresence.shouldRender;
|
|
136
|
+
}
|
|
127
137
|
snippetProps = $derived.by(() => ({ open: this.root.opts.open.current }));
|
|
128
138
|
props = $derived.by(() => ({
|
|
129
139
|
id: this.opts.id.current,
|
|
@@ -172,3 +182,29 @@ export class PopoverCloseState {
|
|
|
172
182
|
...this.attachment,
|
|
173
183
|
}));
|
|
174
184
|
}
|
|
185
|
+
export class PopoverOverlayState {
|
|
186
|
+
static create(opts) {
|
|
187
|
+
return new PopoverOverlayState(opts, PopoverRootContext.get());
|
|
188
|
+
}
|
|
189
|
+
opts;
|
|
190
|
+
root;
|
|
191
|
+
attachment;
|
|
192
|
+
constructor(opts, root) {
|
|
193
|
+
this.opts = opts;
|
|
194
|
+
this.root = root;
|
|
195
|
+
this.attachment = attachRef(this.opts.ref, (v) => (this.root.overlayNode = v));
|
|
196
|
+
}
|
|
197
|
+
get shouldRender() {
|
|
198
|
+
return this.root.overlayPresence.shouldRender;
|
|
199
|
+
}
|
|
200
|
+
snippetProps = $derived.by(() => ({ open: this.root.opts.open.current }));
|
|
201
|
+
props = $derived.by(() => ({
|
|
202
|
+
id: this.opts.id.current,
|
|
203
|
+
[popoverAttrs.overlay]: "",
|
|
204
|
+
style: {
|
|
205
|
+
pointerEvents: "auto",
|
|
206
|
+
},
|
|
207
|
+
"data-state": getDataOpenClosed(this.root.opts.open.current),
|
|
208
|
+
...this.attachment,
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
@@ -4,6 +4,7 @@ import type { OnChangeFn, WithChild, WithChildNoChildrenSnippetProps, WithChildr
|
|
|
4
4
|
import type { BitsPrimitiveButtonAttributes, BitsPrimitiveDivAttributes } from "../../shared/attributes.js";
|
|
5
5
|
import type { FloatingContentSnippetProps, StaticContentSnippetProps } from "../../shared/types.js";
|
|
6
6
|
import type { PortalProps } from "../../types.js";
|
|
7
|
+
import type { PresenceLayerProps } from "../utilities/presence-layer/types.js";
|
|
7
8
|
export type PopoverRootPropsWithoutHTML = WithChildren<{
|
|
8
9
|
/**
|
|
9
10
|
* The open state of the popover.
|
|
@@ -31,3 +32,8 @@ export type PopoverArrowPropsWithoutHTML = ArrowPropsWithoutHTML;
|
|
|
31
32
|
export type PopoverArrowProps = ArrowProps;
|
|
32
33
|
export type PopoverPortalPropsWithoutHTML = PortalProps;
|
|
33
34
|
export type PopoverPortalProps = PortalProps;
|
|
35
|
+
export type PopoverOverlaySnippetProps = {
|
|
36
|
+
open: boolean;
|
|
37
|
+
};
|
|
38
|
+
export type PopoverOverlayPropsWithoutHTML = WithChild<PresenceLayerProps, PopoverOverlaySnippetProps>;
|
|
39
|
+
export type PopoverOverlayProps = PopoverOverlayPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, PopoverOverlayPropsWithoutHTML>;
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
{id}
|
|
45
45
|
{preventScroll}
|
|
46
46
|
forceMount={true}
|
|
47
|
+
shouldRender={contentState.shouldRender}
|
|
47
48
|
>
|
|
48
49
|
{#snippet popper({ props })}
|
|
49
50
|
{@const finalProps = mergeProps(props, { style: contentState.props.style })}
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
{id}
|
|
67
68
|
{preventScroll}
|
|
68
69
|
forceMount={false}
|
|
70
|
+
shouldRender={contentState.shouldRender}
|
|
69
71
|
>
|
|
70
72
|
{#snippet popper({ props })}
|
|
71
73
|
{@const finalProps = mergeProps(props, { style: contentState.props.style })}
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
{id}
|
|
46
46
|
{preventScroll}
|
|
47
47
|
forceMount={true}
|
|
48
|
+
shouldRender={contentState.shouldRender}
|
|
48
49
|
>
|
|
49
50
|
{#snippet popper({ props, wrapperProps })}
|
|
50
51
|
{@const finalProps = mergeProps(props, { style: contentState.props.style })}
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
{id}
|
|
70
71
|
{preventScroll}
|
|
71
72
|
forceMount={false}
|
|
73
|
+
shouldRender={contentState.shouldRender}
|
|
72
74
|
>
|
|
73
75
|
{#snippet popper({ props, wrapperProps })}
|
|
74
76
|
{@const finalProps = mergeProps(props, { style: contentState.props.style })}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Previous } from "runed";
|
|
2
2
|
import { DOMContext, type ReadableBoxedValues, type WritableBoxedValues, type Box } from "svelte-toolbelt";
|
|
3
3
|
import type { BitsEvent, BitsFocusEvent, BitsKeyboardEvent, BitsMouseEvent, BitsPointerEvent, OnChangeFn, WithRefOpts, RefAttachment } from "../../internal/types.js";
|
|
4
|
+
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
|
|
4
5
|
export declare const INTERACTION_KEYS: string[];
|
|
5
6
|
export declare const FIRST_KEYS: string[];
|
|
6
7
|
export declare const LAST_KEYS: string[];
|
|
@@ -32,6 +33,7 @@ declare abstract class SelectBaseRootState {
|
|
|
32
33
|
touchedInput: boolean;
|
|
33
34
|
inputNode: HTMLElement | null;
|
|
34
35
|
contentNode: HTMLElement | null;
|
|
36
|
+
contentPresence: PresenceManager;
|
|
35
37
|
viewportNode: HTMLElement | null;
|
|
36
38
|
triggerNode: HTMLElement | null;
|
|
37
39
|
valueId: string;
|
|
@@ -204,6 +206,7 @@ export declare class SelectContentState {
|
|
|
204
206
|
onEscapeKeydown: (e: KeyboardEvent) => void;
|
|
205
207
|
onOpenAutoFocus: (e: Event) => void;
|
|
206
208
|
onCloseAutoFocus: (e: Event) => void;
|
|
209
|
+
get shouldRender(): boolean;
|
|
207
210
|
readonly snippetProps: {
|
|
208
211
|
open: boolean;
|
|
209
212
|
};
|