bits-ui 2.16.5 → 2.17.0

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 (39) hide show
  1. package/dist/bits/accordion/accordion.svelte.d.ts +4 -2
  2. package/dist/bits/accordion/accordion.svelte.js +2 -1
  3. package/dist/bits/collapsible/collapsible.svelte.d.ts +3 -1
  4. package/dist/bits/collapsible/collapsible.svelte.js +2 -1
  5. package/dist/bits/context-menu/components/context-menu.svelte +3 -1
  6. package/dist/bits/dialog/dialog.svelte.d.ts +4 -0
  7. package/dist/bits/dialog/dialog.svelte.js +3 -1
  8. package/dist/bits/link-preview/link-preview.svelte.d.ts +5 -3
  9. package/dist/bits/link-preview/link-preview.svelte.js +2 -1
  10. package/dist/bits/menu/components/menu-sub-content-static.svelte +12 -3
  11. package/dist/bits/menu/components/menu-sub-content.svelte +12 -3
  12. package/dist/bits/menu/components/menu-sub-trigger.svelte +1 -1
  13. package/dist/bits/menu/components/menu.svelte +5 -0
  14. package/dist/bits/menu/components/menu.svelte.d.ts +1 -0
  15. package/dist/bits/menu/menu.svelte.d.ts +8 -4
  16. package/dist/bits/menu/menu.svelte.js +636 -12
  17. package/dist/bits/menubar/components/menubar-menu.svelte +3 -0
  18. package/dist/bits/menubar/menubar.svelte.d.ts +2 -0
  19. package/dist/bits/menubar/menubar.svelte.js +22 -2
  20. package/dist/bits/navigation-menu/components/navigation-menu-content.svelte +7 -2
  21. package/dist/bits/navigation-menu/components/navigation-menu-indicator.svelte +9 -2
  22. package/dist/bits/navigation-menu/components/navigation-menu-viewport.svelte +5 -3
  23. package/dist/bits/popover/popover.svelte.d.ts +7 -3
  24. package/dist/bits/popover/popover.svelte.js +3 -1
  25. package/dist/bits/select/select.svelte.d.ts +6 -4
  26. package/dist/bits/select/select.svelte.js +2 -1
  27. package/dist/bits/tooltip/tooltip.svelte.d.ts +5 -3
  28. package/dist/bits/tooltip/tooltip.svelte.js +2 -1
  29. package/dist/bits/utilities/floating-layer/use-floating-layer.svelte.d.ts +5 -5
  30. package/dist/bits/utilities/presence-layer/presence-layer.svelte +5 -1
  31. package/dist/bits/utilities/presence-layer/presence.svelte.d.ts +3 -38
  32. package/dist/bits/utilities/presence-layer/presence.svelte.js +49 -146
  33. package/dist/bits/utilities/presence-layer/types.d.ts +7 -3
  34. package/dist/internal/animations-complete.js +64 -9
  35. package/dist/internal/attrs.d.ts +5 -0
  36. package/dist/internal/attrs.js +8 -2
  37. package/dist/internal/presence-manager.svelte.d.ts +4 -1
  38. package/dist/internal/presence-manager.svelte.js +42 -1
  39. package/package.json +1 -1
@@ -131,14 +131,16 @@ export declare class AccordionContentState {
131
131
  };
132
132
  readonly props: {
133
133
  readonly hidden: boolean | "until-found" | undefined;
134
- readonly id: string;
135
- readonly "data-state": "open" | "closed";
136
134
  readonly "data-disabled": "" | undefined;
137
135
  readonly "data-orientation": Orientation;
138
136
  readonly style: {
139
137
  readonly "--bits-accordion-content-height": `${number}px`;
140
138
  readonly "--bits-accordion-content-width": `${number}px`;
141
139
  };
140
+ readonly "data-starting-style"?: "";
141
+ readonly "data-ending-style"?: "";
142
+ readonly id: string;
143
+ readonly "data-state": "open" | "closed";
142
144
  };
143
145
  }
144
146
  export declare class AccordionHeaderState {
@@ -1,6 +1,6 @@
1
1
  import { afterTick, attachRef, boxWith, } from "svelte-toolbelt";
2
2
  import { Context, watch } from "runed";
3
- import { boolToStr, boolToEmptyStrOrUndef, getDataOpenClosed } from "../../internal/attrs.js";
3
+ import { boolToStr, boolToEmptyStrOrUndef, getDataOpenClosed, getDataTransitionAttrs, } from "../../internal/attrs.js";
4
4
  import { kbd } from "../../internal/kbd.js";
5
5
  import { createBitsAttrs } from "../../internal/attrs.js";
6
6
  import { RovingFocusGroup } from "../../internal/roving-focus-group.js";
@@ -237,6 +237,7 @@ export class AccordionContentState {
237
237
  props = $derived.by(() => ({
238
238
  id: this.opts.id.current,
239
239
  "data-state": getDataOpenClosed(this.item.isActive),
240
+ ...getDataTransitionAttrs(this.item.contentPresence.transitionStatus),
240
241
  "data-disabled": boolToEmptyStrOrUndef(this.item.isDisabled),
241
242
  "data-orientation": this.item.root.opts.orientation.current,
242
243
  [accordionAttrs.content]: "",
@@ -42,13 +42,15 @@ export declare class CollapsibleContentState {
42
42
  };
43
43
  readonly props: {
44
44
  readonly hidden: boolean | "until-found" | undefined;
45
+ readonly "data-disabled": "" | undefined;
46
+ readonly "data-starting-style"?: "";
47
+ readonly "data-ending-style"?: "";
45
48
  readonly id: string;
46
49
  readonly style: {
47
50
  readonly "--bits-collapsible-content-height": string | undefined;
48
51
  readonly "--bits-collapsible-content-width": string | undefined;
49
52
  };
50
53
  readonly "data-state": "open" | "closed";
51
- readonly "data-disabled": "" | undefined;
52
54
  };
53
55
  }
54
56
  interface CollapsibleTriggerStateOpts extends WithRefOpts, ReadableBoxedValues<{
@@ -1,6 +1,6 @@
1
1
  import { afterTick, attachRef, boxWith, } from "svelte-toolbelt";
2
2
  import { Context, watch } from "runed";
3
- import { createBitsAttrs, boolToStr, boolToEmptyStrOrUndef, getDataOpenClosed, } from "../../internal/attrs.js";
3
+ import { createBitsAttrs, boolToStr, boolToEmptyStrOrUndef, getDataOpenClosed, getDataTransitionAttrs, } from "../../internal/attrs.js";
4
4
  import { kbd } from "../../internal/kbd.js";
5
5
  import { on } from "svelte/events";
6
6
  import { PresenceManager } from "../../internal/presence-manager.svelte.js";
@@ -135,6 +135,7 @@ export class CollapsibleContentState {
135
135
  ? "until-found"
136
136
  : undefined,
137
137
  "data-state": getDataOpenClosed(this.root.opts.open.current),
138
+ ...getDataTransitionAttrs(this.root.contentPresence.transitionStatus),
138
139
  "data-disabled": boolToEmptyStrOrUndef(this.root.opts.disabled.current),
139
140
  [collapsibleAttrs.content]: "",
140
141
  ...(this.opts.hiddenUntilFound.current && !this.shouldRender
@@ -8,14 +8,16 @@
8
8
  let {
9
9
  open = $bindable(false),
10
10
  dir = "ltr",
11
+ // debugMode = false,
11
12
  onOpenChange = noop,
12
13
  onOpenChangeComplete = noop,
13
14
  children,
14
15
  }: ContextMenuRootProps = $props();
15
16
 
16
17
  const root = MenuRootState.create({
17
- variant: boxWith(() => "context-menu"),
18
+ variant: boxWith(() => "context-menu" as const),
18
19
  dir: boxWith(() => dir),
20
+ // debugMode: boxWith(() => debugMode),
19
21
  onClose: () => {
20
22
  open = false;
21
23
  onOpenChange?.(false);
@@ -138,6 +138,8 @@ export declare class DialogContentState {
138
138
  };
139
139
  readonly props: {
140
140
  readonly "data-state": "open" | "closed";
141
+ readonly "data-starting-style"?: "";
142
+ readonly "data-ending-style"?: "";
141
143
  readonly id: string;
142
144
  readonly role: "dialog" | "alertdialog";
143
145
  readonly "aria-modal": "true";
@@ -169,6 +171,8 @@ export declare class DialogOverlayState {
169
171
  };
170
172
  readonly props: {
171
173
  readonly "data-state": "open" | "closed";
174
+ readonly "data-starting-style"?: "";
175
+ readonly "data-ending-style"?: "";
172
176
  readonly id: string;
173
177
  readonly style: {
174
178
  readonly pointerEvents: "auto";
@@ -1,6 +1,6 @@
1
1
  import { attachRef, boxWith, onDestroyEffect, } from "svelte-toolbelt";
2
2
  import { Context, watch } from "runed";
3
- import { createBitsAttrs, boolToStr, getDataOpenClosed, boolToEmptyStrOrUndef, } from "../../internal/attrs.js";
3
+ import { createBitsAttrs, boolToStr, getDataOpenClosed, boolToEmptyStrOrUndef, getDataTransitionAttrs, } from "../../internal/attrs.js";
4
4
  import { kbd } from "../../internal/kbd.js";
5
5
  import { PresenceManager } from "../../internal/presence-manager.svelte.js";
6
6
  const dialogAttrs = createBitsAttrs({
@@ -280,6 +280,7 @@ export class DialogContentState {
280
280
  tabindex: this.root.opts.variant.current === "alert-dialog" ? -1 : undefined,
281
281
  "data-nested-open": boolToEmptyStrOrUndef(this.root.nestedOpenCount > 0),
282
282
  "data-nested": boolToEmptyStrOrUndef(this.root.parent !== null),
283
+ ...getDataTransitionAttrs(this.root.contentPresence.transitionStatus),
283
284
  ...this.root.sharedProps,
284
285
  ...this.attachment,
285
286
  }));
@@ -310,6 +311,7 @@ export class DialogOverlayState {
310
311
  },
311
312
  "data-nested-open": boolToEmptyStrOrUndef(this.root.nestedOpenCount > 0),
312
313
  "data-nested": boolToEmptyStrOrUndef(this.root.parent !== null),
314
+ ...getDataTransitionAttrs(this.root.overlayPresence.transitionStatus),
313
315
  ...this.root.sharedProps,
314
316
  ...this.attachment,
315
317
  }));
@@ -77,12 +77,14 @@ export declare class LinkPreviewContentState {
77
77
  open: boolean;
78
78
  };
79
79
  readonly props: {
80
- readonly id: string;
81
- readonly tabindex: -1;
82
- readonly "data-state": "open" | "closed";
83
80
  readonly onpointerdown: (e: BitsPointerEvent) => void;
84
81
  readonly onpointerenter: (e: BitsPointerEvent) => void;
85
82
  readonly onfocusout: (e: BitsFocusEvent) => void;
83
+ readonly "data-starting-style"?: "";
84
+ readonly "data-ending-style"?: "";
85
+ readonly id: string;
86
+ readonly tabindex: -1;
87
+ readonly "data-state": "open" | "closed";
86
88
  };
87
89
  readonly popperProps: {
88
90
  onInteractOutside: (e: PointerEvent) => void;
@@ -1,7 +1,7 @@
1
1
  import { afterSleep, onDestroyEffect, attachRef, DOMContext, boxWith, } from "svelte-toolbelt";
2
2
  import { Context, watch } from "runed";
3
3
  import { on } from "svelte/events";
4
- import { createBitsAttrs, boolToStr, getDataOpenClosed } from "../../internal/attrs.js";
4
+ import { createBitsAttrs, boolToStr, getDataOpenClosed, getDataTransitionAttrs, } from "../../internal/attrs.js";
5
5
  import { isElement, isFocusVisible, isTouch } from "../../internal/is.js";
6
6
  import { getTabbableCandidates } from "../../internal/focus.js";
7
7
  import { SafePolygon } from "../../internal/safe-polygon.svelte.js";
@@ -225,6 +225,7 @@ export class LinkPreviewContentState {
225
225
  id: this.opts.id.current,
226
226
  tabindex: -1,
227
227
  "data-state": getDataOpenClosed(this.root.opts.open.current),
228
+ ...getDataTransitionAttrs(this.root.contentPresence.transitionStatus),
228
229
  [linkPreviewAttrs.content]: "",
229
230
  onpointerdown: this.onpointerdown,
230
231
  onpointerenter: this.onpointerenter,
@@ -97,12 +97,21 @@
97
97
  function handleOnFocusOutside(e: FocusEvent) {
98
98
  onFocusOutside(e);
99
99
  if (e.defaultPrevented) return;
100
- // We prevent closing when the trigger is focused to avoid triggering a re-open animation
101
- // on pointer interaction.
102
100
  if (!isHTMLElement(e.target)) return;
103
- if (e.target.id !== subContentState.parentMenu.triggerNode?.id) {
101
+ if (e.target.id === subContentState.parentMenu.triggerNode?.id) return;
102
+ const parentContent = subContentState.parentMenu.parentMenu?.contentNode;
103
+ if (parentContent?.contains(e.target)) {
104
104
  subContentState.parentMenu.onClose();
105
+ e.preventDefault();
106
+ return;
107
+ }
108
+ // focus moved to a descendant sub-content (rendered in a portal)
109
+ const subContentSelector = `[${subContentState.parentMenu.root.getBitsAttr("sub-content")}]`;
110
+ if (e.target.closest(subContentSelector)) {
111
+ e.preventDefault();
112
+ return;
105
113
  }
114
+ subContentState.parentMenu.onClose();
106
115
  }
107
116
  </script>
108
117
 
@@ -99,12 +99,21 @@
99
99
  function handleOnFocusOutside(e: FocusEvent) {
100
100
  onFocusOutside(e);
101
101
  if (e.defaultPrevented) return;
102
- // We prevent closing when the trigger is focused to avoid triggering a re-open animation
103
- // on pointer interaction.
104
102
  if (!isHTMLElement(e.target)) return;
105
- if (e.target.id !== subContentState.parentMenu.triggerNode?.id) {
103
+ if (e.target.id === subContentState.parentMenu.triggerNode?.id) return;
104
+ const parentContent = subContentState.parentMenu.parentMenu?.contentNode;
105
+ if (parentContent?.contains(e.target)) {
106
106
  subContentState.parentMenu.onClose();
107
+ e.preventDefault();
108
+ return;
109
+ }
110
+ // focus moved to a descendant sub-content rendered in a portal
111
+ const subContentSelector = `[${subContentState.parentMenu.root.getBitsAttr("sub-content")}]`;
112
+ if (e.target.closest(subContentSelector)) {
113
+ e.preventDefault();
114
+ return;
107
115
  }
116
+ subContentState.parentMenu.onClose();
108
117
  }
109
118
  </script>
110
119
 
@@ -15,7 +15,7 @@
15
15
  children,
16
16
  child,
17
17
  onSelect = noop,
18
- openDelay = 100,
18
+ openDelay = 0,
19
19
  ...restProps
20
20
  }: MenuSubTriggerProps = $props();
21
21
 
@@ -8,21 +8,26 @@
8
8
  let {
9
9
  open = $bindable(false),
10
10
  dir = "ltr",
11
+ // debugMode = false,
11
12
  onOpenChange = noop,
12
13
  onOpenChangeComplete = noop,
13
14
  _internal_variant: variant = "dropdown-menu",
15
+ _internal_should_skip_exit_animation: shouldSkipExitAnimation = undefined,
14
16
  children,
15
17
  }: MenuRootProps & {
16
18
  _internal_variant?: "context-menu" | "dropdown-menu" | "menubar";
19
+ _internal_should_skip_exit_animation?: () => boolean;
17
20
  } = $props();
18
21
 
19
22
  const root = MenuRootState.create({
20
23
  variant: boxWith(() => variant),
21
24
  dir: boxWith(() => dir),
25
+ // debugMode: boxWith(() => debugMode),
22
26
  onClose: () => {
23
27
  open = false;
24
28
  onOpenChange(false);
25
29
  },
30
+ shouldSkipExitAnimation: () => shouldSkipExitAnimation?.() ?? false,
26
31
  });
27
32
 
28
33
  MenuMenuState.create(
@@ -1,6 +1,7 @@
1
1
  import type { MenuRootProps } from "../types.js";
2
2
  type $$ComponentProps = MenuRootProps & {
3
3
  _internal_variant?: "context-menu" | "dropdown-menu" | "menubar";
4
+ _internal_should_skip_exit_animation?: () => boolean;
4
5
  };
5
6
  declare const Menu: import("svelte").Component<$$ComponentProps, {}, "open">;
6
7
  type Menu = ReturnType<typeof Menu>;
@@ -16,6 +16,8 @@ export interface MenuRootStateOpts extends ReadableBoxedValues<{
16
16
  variant: MenuVariant;
17
17
  }> {
18
18
  onClose: AnyFn;
19
+ /** When closing, if this returns true, exit animations are skipped (instant unmount). */
20
+ shouldSkipExitAnimation?: () => boolean;
19
21
  }
20
22
  export declare const MenuOpenEvent: CustomEventDispatcher<unknown>;
21
23
  export declare const menuAttrs: import("../../internal/attrs.js").CreateBitsAttrsReturn<readonly ["trigger", "content", "sub-trigger", "item", "group", "group-heading", "checkbox-group", "checkbox-item", "radio-group", "radio-item", "separator", "sub-content", "arrow"]>;
@@ -79,10 +81,6 @@ export declare class MenuContentState {
79
81
  open: boolean;
80
82
  };
81
83
  readonly props: {
82
- readonly id: string;
83
- readonly role: "menu";
84
- readonly "aria-orientation": "vertical";
85
- readonly "data-state": "open" | "closed";
86
84
  readonly onkeydown: (e: BitsKeyboardEvent) => void;
87
85
  readonly onblur: (e: BitsFocusEvent) => void;
88
86
  readonly onfocus: (_: BitsFocusEvent) => void;
@@ -91,6 +89,12 @@ export declare class MenuContentState {
91
89
  readonly pointerEvents: "auto";
92
90
  readonly contain: "layout style";
93
91
  };
92
+ readonly "data-starting-style"?: "";
93
+ readonly "data-ending-style"?: "";
94
+ readonly id: string;
95
+ readonly role: "menu";
96
+ readonly "aria-orientation": "vertical";
97
+ readonly "data-state": "open" | "closed";
94
98
  };
95
99
  readonly popperProps: {
96
100
  onCloseAutoFocus: (e: Event) => void;