bits-ui 2.14.0 → 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.
Files changed (53) hide show
  1. package/dist/bits/accordion/accordion.svelte.d.ts +5 -1
  2. package/dist/bits/accordion/accordion.svelte.js +21 -2
  3. package/dist/bits/accordion/components/accordion-content.svelte +12 -24
  4. package/dist/bits/alert-dialog/components/alert-dialog-content.svelte +50 -62
  5. package/dist/bits/collapsible/collapsible.svelte.d.ts +4 -1
  6. package/dist/bits/collapsible/collapsible.svelte.js +15 -2
  7. package/dist/bits/collapsible/components/collapsible-content.svelte +12 -24
  8. package/dist/bits/context-menu/components/context-menu-content-static.svelte +2 -0
  9. package/dist/bits/context-menu/components/context-menu-content.svelte +2 -0
  10. package/dist/bits/date-field/date-field.svelte.js +5 -3
  11. package/dist/bits/dialog/components/dialog-content.svelte +44 -57
  12. package/dist/bits/dialog/components/dialog-overlay.svelte +9 -12
  13. package/dist/bits/dialog/dialog.svelte.d.ts +6 -0
  14. package/dist/bits/dialog/dialog.svelte.js +17 -3
  15. package/dist/bits/dropdown-menu/components/dropdown-menu-content-static.svelte +2 -0
  16. package/dist/bits/dropdown-menu/components/dropdown-menu-content.svelte +2 -0
  17. package/dist/bits/link-preview/components/link-preview-content-static.svelte +2 -0
  18. package/dist/bits/link-preview/components/link-preview-content.svelte +2 -0
  19. package/dist/bits/link-preview/link-preview.svelte.d.ts +3 -0
  20. package/dist/bits/link-preview/link-preview.svelte.js +6 -2
  21. package/dist/bits/menu/components/menu-content-static.svelte +2 -0
  22. package/dist/bits/menu/components/menu-content.svelte +2 -0
  23. package/dist/bits/menu/components/menu-sub-content-static.svelte +2 -0
  24. package/dist/bits/menu/components/menu-sub-content.svelte +2 -0
  25. package/dist/bits/menu/menu.svelte.d.ts +3 -0
  26. package/dist/bits/menu/menu.svelte.js +6 -2
  27. package/dist/bits/popover/components/popover-content-static.svelte +2 -0
  28. package/dist/bits/popover/components/popover-content.svelte +2 -0
  29. package/dist/bits/popover/components/popover-overlay.svelte +9 -12
  30. package/dist/bits/popover/popover.svelte.d.ts +6 -0
  31. package/dist/bits/popover/popover.svelte.js +16 -3
  32. package/dist/bits/select/components/select-content-static.svelte +2 -0
  33. package/dist/bits/select/components/select-content.svelte +2 -0
  34. package/dist/bits/select/select.svelte.d.ts +3 -0
  35. package/dist/bits/select/select.svelte.js +6 -2
  36. package/dist/bits/tooltip/components/tooltip-content-static.svelte +2 -0
  37. package/dist/bits/tooltip/components/tooltip-content.svelte +2 -0
  38. package/dist/bits/tooltip/tooltip.svelte.d.ts +3 -0
  39. package/dist/bits/tooltip/tooltip.svelte.js +6 -2
  40. package/dist/bits/utilities/popper-layer/popper-layer-inner.svelte +1 -1
  41. package/dist/bits/utilities/popper-layer/popper-layer-inner.svelte.d.ts +1 -1
  42. package/dist/bits/utilities/popper-layer/popper-layer.svelte +43 -45
  43. package/dist/bits/utilities/popper-layer/types.d.ts +4 -0
  44. package/dist/internal/animations-complete.js +7 -15
  45. package/dist/internal/date-time/field/helpers.js +3 -1
  46. package/dist/internal/date-time/field/time-helpers.js +4 -1
  47. package/dist/internal/presence-manager.svelte.d.ts +14 -0
  48. package/dist/internal/presence-manager.svelte.js +34 -0
  49. package/dist/internal/should-enable-focus-trap.d.ts +1 -2
  50. package/dist/internal/should-enable-focus-trap.js +2 -2
  51. package/package.json +2 -2
  52. package/dist/internal/open-change-complete.d.ts +0 -13
  53. 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 { OpenChangeComplete } from "../../internal/open-change-complete.js";
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 OpenChangeComplete({
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 { OpenChangeComplete } from "../../internal/open-change-complete.js";
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 OpenChangeComplete({
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 { OpenChangeComplete } from "../../internal/open-change-complete.js";
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 OpenChangeComplete({
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, {
@@ -3,7 +3,6 @@
3
3
  import { PopoverOverlayState } from "../popover.svelte.js";
4
4
  import type { PopoverOverlayProps } from "../types.js";
5
5
  import { createId } from "../../../internal/create-id.js";
6
- import PresenceLayer from "../../utilities/presence-layer/presence-layer.svelte";
7
6
 
8
7
  const uid = $props.id();
9
8
 
@@ -27,14 +26,12 @@
27
26
  const mergedProps = $derived(mergeProps(restProps, overlayState.props));
28
27
  </script>
29
28
 
30
- <PresenceLayer open={overlayState.root.opts.open.current || forceMount} ref={overlayState.opts.ref}>
31
- {#snippet presence()}
32
- {#if child}
33
- {@render child({ props: mergeProps(mergedProps), ...overlayState.snippetProps })}
34
- {:else}
35
- <div {...mergeProps(mergedProps)}>
36
- {@render children?.(overlayState.snippetProps)}
37
- </div>
38
- {/if}
39
- {/snippet}
40
- </PresenceLayer>
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}
@@ -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
  };
@@ -95,6 +100,7 @@ export declare class PopoverOverlayState {
95
100
  readonly root: PopoverRootState;
96
101
  readonly attachment: RefAttachment;
97
102
  constructor(opts: PopoverOverlayStateOpts, root: PopoverRootState);
103
+ get shouldRender(): boolean;
98
104
  readonly snippetProps: {
99
105
  open: boolean;
100
106
  };
@@ -3,7 +3,7 @@ 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 { OpenChangeComplete } from "../../internal/open-change-complete.js";
6
+ import { PresenceManager } from "../../internal/presence-manager.svelte.js";
7
7
  const popoverAttrs = createBitsAttrs({
8
8
  component: "popover",
9
9
  parts: ["root", "trigger", "content", "close", "overlay"],
@@ -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 OpenChangeComplete({
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,
@@ -182,7 +192,10 @@ export class PopoverOverlayState {
182
192
  constructor(opts, root) {
183
193
  this.opts = opts;
184
194
  this.root = root;
185
- this.attachment = attachRef(this.opts.ref);
195
+ this.attachment = attachRef(this.opts.ref, (v) => (this.root.overlayNode = v));
196
+ }
197
+ get shouldRender() {
198
+ return this.root.overlayPresence.shouldRender;
186
199
  }
187
200
  snippetProps = $derived.by(() => ({ open: this.root.opts.open.current }));
188
201
  props = $derived.by(() => ({
@@ -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
  };
@@ -10,7 +10,7 @@ import { createBitsAttrs } from "../../internal/attrs.js";
10
10
  import { getFloatingContentCSSVars } from "../../internal/floating-svelte/floating-utils.svelte.js";
11
11
  import { DataTypeahead } from "../../internal/data-typeahead.svelte.js";
12
12
  import { DOMTypeahead } from "../../internal/dom-typeahead.svelte.js";
13
- import { OpenChangeComplete } from "../../internal/open-change-complete.js";
13
+ import { PresenceManager } from "../../internal/presence-manager.svelte.js";
14
14
  // prettier-ignore
15
15
  export const INTERACTION_KEYS = [kbd.ARROW_LEFT, kbd.ESCAPE, kbd.ARROW_RIGHT, kbd.SHIFT, kbd.CAPS_LOCK, kbd.CONTROL, kbd.ALT, kbd.META, kbd.ENTER, kbd.F1, kbd.F2, kbd.F3, kbd.F4, kbd.F5, kbd.F6, kbd.F7, kbd.F8, kbd.F9, kbd.F10, kbd.F11, kbd.F12];
16
16
  export const FIRST_KEYS = [kbd.ARROW_DOWN, kbd.PAGE_UP, kbd.HOME];
@@ -45,6 +45,7 @@ class SelectBaseRootState {
45
45
  touchedInput = $state(false);
46
46
  inputNode = $state(null);
47
47
  contentNode = $state(null);
48
+ contentPresence;
48
49
  viewportNode = $state(null);
49
50
  triggerNode = $state(null);
50
51
  valueId = $state("");
@@ -70,7 +71,7 @@ class SelectBaseRootState {
70
71
  constructor(opts) {
71
72
  this.opts = opts;
72
73
  this.isCombobox = opts.isCombobox;
73
- new OpenChangeComplete({
74
+ this.contentPresence = new PresenceManager({
74
75
  ref: boxWith(() => this.contentNode),
75
76
  open: this.opts.open,
76
77
  onComplete: () => {
@@ -749,6 +750,9 @@ export class SelectContentState {
749
750
  onCloseAutoFocus = (e) => {
750
751
  e.preventDefault();
751
752
  };
753
+ get shouldRender() {
754
+ return this.root.contentPresence.shouldRender;
755
+ }
752
756
  snippetProps = $derived.by(() => ({ open: this.root.opts.open.current }));
753
757
  props = $derived.by(() => ({
754
758
  id: this.opts.id.current,
@@ -47,6 +47,7 @@
47
47
  forceMount={true}
48
48
  ref={contentState.opts.ref}
49
49
  tooltip={true}
50
+ shouldRender={contentState.shouldRender}
50
51
  >
51
52
  {#snippet popper({ props })}
52
53
  {@const mergedProps = mergeProps(props, {
@@ -74,6 +75,7 @@
74
75
  preventScroll={false}
75
76
  forceMount={false}
76
77
  ref={contentState.opts.ref}
78
+ shouldRender={contentState.shouldRender}
77
79
  >
78
80
  {#snippet popper({ props })}
79
81
  {@const mergedProps = mergeProps(props, {
@@ -67,6 +67,7 @@
67
67
  forceMount={true}
68
68
  ref={contentState.opts.ref}
69
69
  tooltip={true}
70
+ shouldRender={contentState.shouldRender}
70
71
  >
71
72
  {#snippet popper({ props, wrapperProps })}
72
73
  {@const mergedProps = mergeProps(props, {
@@ -95,6 +96,7 @@
95
96
  forceMount={false}
96
97
  ref={contentState.opts.ref}
97
98
  tooltip={true}
99
+ shouldRender={contentState.shouldRender}
98
100
  >
99
101
  {#snippet popper({ props, wrapperProps })}
100
102
  {@const mergedProps = mergeProps(props, {
@@ -1,6 +1,7 @@
1
1
  import { DOMContext, type WritableBoxedValues, type ReadableBoxedValues } from "svelte-toolbelt";
2
2
  import type { OnChangeFn, RefAttachment, WithRefOpts } from "../../internal/types.js";
3
3
  import type { FocusEventHandler, MouseEventHandler, PointerEventHandler } from "svelte/elements";
4
+ import { PresenceManager } from "../../internal/presence-manager.svelte.js";
4
5
  export declare const tooltipAttrs: import("../../internal/attrs.js").CreateBitsAttrsReturn<readonly ["content", "trigger"]>;
5
6
  interface TooltipProviderStateOpts extends ReadableBoxedValues<{
6
7
  delayDuration: number;
@@ -44,6 +45,7 @@ export declare class TooltipRootState {
44
45
  readonly disabled: boolean;
45
46
  readonly ignoreNonKeyboardFocus: boolean;
46
47
  contentNode: HTMLElement | null;
48
+ contentPresence: PresenceManager;
47
49
  triggerNode: HTMLElement | null;
48
50
  readonly stateAttr: string;
49
51
  constructor(opts: TooltipRootStateOpts, provider: TooltipProviderState);
@@ -97,6 +99,7 @@ export declare class TooltipContentState {
97
99
  onEscapeKeydown: (e: KeyboardEvent) => void;
98
100
  onOpenAutoFocus: (e: Event) => void;
99
101
  onCloseAutoFocus: (e: Event) => void;
102
+ get shouldRender(): boolean;
100
103
  readonly snippetProps: {
101
104
  open: boolean;
102
105
  };