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
@@ -5,7 +5,7 @@ import { isElement, isFocusVisible } from "../../internal/is.js";
5
5
  import { createBitsAttrs, boolToEmptyStrOrUndef } from "../../internal/attrs.js";
6
6
  import { TimeoutFn } from "../../internal/timeout-fn.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
  export const tooltipAttrs = createBitsAttrs({
10
10
  component: "tooltip",
11
11
  parts: ["content", "trigger"],
@@ -72,6 +72,7 @@ export class TooltipRootState {
72
72
  ignoreNonKeyboardFocus = $derived.by(() => this.opts.ignoreNonKeyboardFocus.current ??
73
73
  this.provider.opts.ignoreNonKeyboardFocus.current);
74
74
  contentNode = $state(null);
75
+ contentPresence;
75
76
  triggerNode = $state(null);
76
77
  #wasOpenDelayed = $state(false);
77
78
  #timerFn;
@@ -87,7 +88,7 @@ export class TooltipRootState {
87
88
  this.#wasOpenDelayed = true;
88
89
  this.opts.open.current = true;
89
90
  }, this.delayDuration ?? 0);
90
- new OpenChangeComplete({
91
+ this.contentPresence = new PresenceManager({
91
92
  open: this.opts.open,
92
93
  ref: boxWith(() => this.contentNode),
93
94
  onComplete: () => {
@@ -294,6 +295,9 @@ export class TooltipContentState {
294
295
  onCloseAutoFocus = (e) => {
295
296
  e.preventDefault();
296
297
  };
298
+ get shouldRender() {
299
+ return this.root.contentPresence.shouldRender;
300
+ }
297
301
  snippetProps = $derived.by(() => ({ open: this.root.opts.open.current }));
298
302
  props = $derived.by(() => ({
299
303
  id: this.opts.id.current,
@@ -47,7 +47,7 @@
47
47
  ref,
48
48
  tooltip = false,
49
49
  ...restProps
50
- }: Omit<PopperLayerImplProps, "open" | "children"> & {
50
+ }: Omit<PopperLayerImplProps, "open" | "children" | "shouldRender"> & {
51
51
  enabled: boolean;
52
52
  } = $props();
53
53
  </script>
@@ -1,5 +1,5 @@
1
1
  import type { PopperLayerImplProps } from "./types.js";
2
- type $$ComponentProps = Omit<PopperLayerImplProps, "open" | "children"> & {
2
+ type $$ComponentProps = Omit<PopperLayerImplProps, "open" | "children" | "shouldRender"> & {
3
3
  enabled: boolean;
4
4
  };
5
5
  declare const PopperLayerInner: import("svelte").Component<$$ComponentProps, {}, "">;
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { PopperLayerImplProps } from "./types.js";
3
3
  import PopperLayerInner from "./popper-layer-inner.svelte";
4
- import PresenceLayer from "../presence-layer/presence-layer.svelte";
5
4
 
6
5
  let {
7
6
  popper,
@@ -40,51 +39,50 @@
40
39
  customAnchor = null,
41
40
  isStatic = false,
42
41
  ref,
42
+ shouldRender,
43
43
  ...restProps
44
44
  }: PopperLayerImplProps = $props();
45
45
  </script>
46
46
 
47
- <PresenceLayer {open} {ref}>
48
- {#snippet presence()}
49
- <PopperLayerInner
50
- {popper}
51
- {onEscapeKeydown}
52
- {escapeKeydownBehavior}
53
- {preventOverflowTextSelection}
54
- {id}
55
- {onPointerDown}
56
- {onPointerUp}
57
- {side}
58
- {sideOffset}
59
- {align}
60
- {alignOffset}
61
- {arrowPadding}
62
- {avoidCollisions}
63
- {collisionBoundary}
64
- {collisionPadding}
65
- {sticky}
66
- {hideWhenDetached}
67
- {updatePositionStrategy}
68
- {strategy}
69
- {dir}
70
- {preventScroll}
71
- {wrapperId}
72
- {style}
73
- {onPlaced}
74
- {customAnchor}
75
- {isStatic}
76
- enabled={open}
77
- {onInteractOutside}
78
- {onCloseAutoFocus}
79
- {onOpenAutoFocus}
80
- {interactOutsideBehavior}
81
- {loop}
82
- {trapFocus}
83
- {isValidEvent}
84
- {onFocusOutside}
85
- forceMount={false}
86
- {ref}
87
- {...restProps}
88
- />
89
- {/snippet}
90
- </PresenceLayer>
47
+ {#if shouldRender}
48
+ <PopperLayerInner
49
+ {popper}
50
+ {onEscapeKeydown}
51
+ {escapeKeydownBehavior}
52
+ {preventOverflowTextSelection}
53
+ {id}
54
+ {onPointerDown}
55
+ {onPointerUp}
56
+ {side}
57
+ {sideOffset}
58
+ {align}
59
+ {alignOffset}
60
+ {arrowPadding}
61
+ {avoidCollisions}
62
+ {collisionBoundary}
63
+ {collisionPadding}
64
+ {sticky}
65
+ {hideWhenDetached}
66
+ {updatePositionStrategy}
67
+ {strategy}
68
+ {dir}
69
+ {preventScroll}
70
+ {wrapperId}
71
+ {style}
72
+ {onPlaced}
73
+ {customAnchor}
74
+ {isStatic}
75
+ enabled={open}
76
+ {onInteractOutside}
77
+ {onCloseAutoFocus}
78
+ {onOpenAutoFocus}
79
+ {interactOutsideBehavior}
80
+ {loop}
81
+ {trapFocus}
82
+ {isValidEvent}
83
+ {onFocusOutside}
84
+ forceMount={false}
85
+ {ref}
86
+ {...restProps}
87
+ />
88
+ {/if}
@@ -31,4 +31,8 @@ export type PopperLayerImplProps = Omit<EscapeLayerImplProps & DismissibleLayerI
31
31
  * symbol so that conflicts don't occur.
32
32
  */
33
33
  tooltip?: boolean;
34
+ /**
35
+ * Whether the popper layer should be rendered.
36
+ */
37
+ shouldRender: boolean;
34
38
  }, "enabled">;
@@ -1,30 +1,23 @@
1
1
  import { afterTick, onDestroyEffect } from "svelte-toolbelt";
2
2
  export class AnimationsComplete {
3
3
  #opts;
4
- #currentFrame = undefined;
5
- #isRunning = false;
4
+ #currentFrame = null;
6
5
  constructor(opts) {
7
6
  this.#opts = opts;
8
7
  onDestroyEffect(() => this.#cleanup());
9
8
  }
10
9
  #cleanup() {
11
- if (this.#currentFrame) {
12
- window.cancelAnimationFrame(this.#currentFrame);
13
- this.#currentFrame = undefined;
14
- }
15
- this.#isRunning = false;
10
+ if (!this.#currentFrame)
11
+ return;
12
+ window.cancelAnimationFrame(this.#currentFrame);
13
+ this.#currentFrame = null;
16
14
  }
17
15
  run(fn) {
18
- // prevent multiple concurrent runs
19
- if (this.#isRunning)
20
- return;
16
+ // if already running, cleanup and restart
21
17
  this.#cleanup();
22
- this.#isRunning = true;
23
18
  const node = this.#opts.ref.current;
24
- if (!node) {
25
- this.#isRunning = false;
19
+ if (!node)
26
20
  return;
27
- }
28
21
  if (typeof node.getAnimations !== "function") {
29
22
  this.#executeCallback(fn);
30
23
  return;
@@ -43,7 +36,6 @@ export class AnimationsComplete {
43
36
  #executeCallback(fn) {
44
37
  const execute = () => {
45
38
  fn();
46
- this.#isRunning = false;
47
39
  };
48
40
  if (this.#opts.afterTick) {
49
41
  afterTick(execute);
@@ -60,7 +60,9 @@ function createContentObj(props) {
60
60
  * If we're operating in a 12 hour clock and the part is an hour, we handle
61
61
  * the conversion to 12 hour format with 2 digit hours and leading zeros here.
62
62
  */
63
- if (part === "hour" && "dayPeriod" in segmentValues && props.hourCycle !== 24) {
63
+ const is12HourMode = props.hourCycle === 12 ||
64
+ (props.hourCycle === undefined && getDefaultHourCycle(locale) === 12);
65
+ if (part === "hour" && is12HourMode) {
64
66
  /**
65
67
  * If the value is over 12, we convert to 12 hour format and add leading
66
68
  * zeroes if the value is less than 10.
@@ -6,6 +6,7 @@ import { styleToString } from "svelte-toolbelt";
6
6
  import { useId } from "../../use-id.js";
7
7
  import { getPlaceholder } from "../placeholders.js";
8
8
  import { isZonedDateTime } from "../utils.js";
9
+ import { getDefaultHourCycle } from "./helpers.js";
9
10
  export function initializeSegmentValues() {
10
11
  const initialParts = EDITABLE_TIME_SEGMENT_PARTS.map((part) => {
11
12
  if (part === "dayPeriod") {
@@ -50,7 +51,9 @@ function createTimeContentObj(props) {
50
51
  * If we're operating in a 12 hour clock and the part is an hour, we handle
51
52
  * the conversion to 12 hour format with 2 digit hours and leading zeros here.
52
53
  */
53
- if (part === "hour" && "dayPeriod" in segmentValues && props.hourCycle !== 24) {
54
+ const is12HourMode = props.hourCycle === 12 ||
55
+ (props.hourCycle === undefined && getDefaultHourCycle(locale) === 12);
56
+ if (part === "hour" && is12HourMode) {
54
57
  /**
55
58
  * If the value is over 12, we convert to 12 hour format and add leading
56
59
  * zeroes if the value is less than 10.
@@ -0,0 +1,14 @@
1
+ import type { ReadableBoxedValues } from "svelte-toolbelt";
2
+ interface PresenceManagerOpts extends ReadableBoxedValues<{
3
+ open: boolean;
4
+ ref: HTMLElement | null;
5
+ }> {
6
+ onComplete?: () => void;
7
+ enabled?: boolean;
8
+ }
9
+ export declare class PresenceManager {
10
+ #private;
11
+ constructor(opts: PresenceManagerOpts);
12
+ get shouldRender(): boolean;
13
+ }
14
+ export {};
@@ -0,0 +1,34 @@
1
+ import { watch } from "runed";
2
+ import { AnimationsComplete } from "./animations-complete.js";
3
+ export class PresenceManager {
4
+ #opts;
5
+ #enabled;
6
+ #afterAnimations;
7
+ #shouldRender = $state(false);
8
+ constructor(opts) {
9
+ this.#opts = opts;
10
+ this.#shouldRender = opts.open.current;
11
+ this.#enabled = opts.enabled ?? true;
12
+ this.#afterAnimations = new AnimationsComplete({
13
+ ref: this.#opts.ref,
14
+ afterTick: this.#opts.open,
15
+ });
16
+ watch(() => this.#opts.open.current, (isOpen) => {
17
+ if (isOpen)
18
+ this.#shouldRender = true;
19
+ if (!this.#enabled)
20
+ return;
21
+ this.#afterAnimations.run(() => {
22
+ if (isOpen === this.#opts.open.current) {
23
+ if (!this.#opts.open.current) {
24
+ this.#shouldRender = false;
25
+ }
26
+ this.#opts.onComplete?.();
27
+ }
28
+ });
29
+ });
30
+ }
31
+ get shouldRender() {
32
+ return this.#shouldRender;
33
+ }
34
+ }
@@ -1,5 +1,4 @@
1
- export declare function shouldEnableFocusTrap({ forceMount, present, open, }: {
1
+ export declare function shouldEnableFocusTrap({ forceMount, open, }: {
2
2
  forceMount: boolean;
3
- present: boolean;
4
3
  open: boolean;
5
4
  }): boolean;
@@ -1,5 +1,5 @@
1
- export function shouldEnableFocusTrap({ forceMount, present, open, }) {
1
+ export function shouldEnableFocusTrap({ forceMount, open, }) {
2
2
  if (forceMount)
3
3
  return open;
4
- return present && open;
4
+ return open;
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bits-ui",
3
- "version": "2.14.0",
3
+ "version": "2.14.1",
4
4
  "license": "MIT",
5
5
  "repository": "github:huntabyte/bits-ui",
6
6
  "funding": "https://github.com/sponsors/huntabyte",
@@ -28,7 +28,7 @@
28
28
  "csstype": "^3.1.3",
29
29
  "jsdom": "^24.1.3",
30
30
  "publint": "^0.2.12",
31
- "svelte": "^5.38.10",
31
+ "svelte": "5.38.1",
32
32
  "svelte-check": "^4.3.1",
33
33
  "typescript": "^5.9.2",
34
34
  "vite": "^7.1.5",
@@ -1,13 +0,0 @@
1
- import type { ReadableBoxedValues } from "svelte-toolbelt";
2
- interface OpenChangeCompleteOpts extends ReadableBoxedValues<{
3
- open: boolean;
4
- ref: HTMLElement | null;
5
- }> {
6
- onComplete: () => void;
7
- enabled?: boolean;
8
- }
9
- export declare class OpenChangeComplete {
10
- #private;
11
- constructor(opts: OpenChangeCompleteOpts);
12
- }
13
- export {};
@@ -1,24 +0,0 @@
1
- import { watch } from "runed";
2
- import { AnimationsComplete } from "./animations-complete.js";
3
- export class OpenChangeComplete {
4
- #opts;
5
- #enabled;
6
- #afterAnimations;
7
- constructor(opts) {
8
- this.#opts = opts;
9
- this.#enabled = opts.enabled ?? true;
10
- this.#afterAnimations = new AnimationsComplete({
11
- ref: this.#opts.ref,
12
- afterTick: this.#opts.open,
13
- });
14
- watch([() => this.#opts.open.current], ([open]) => {
15
- if (!this.#enabled)
16
- return;
17
- this.#afterAnimations.run(() => {
18
- if (open === this.#opts.open.current) {
19
- this.#opts.onComplete();
20
- }
21
- });
22
- });
23
- }
24
- }