diff --git a/packages/bits-ui/src/lib/bits/popover/components/popover-content.svelte b/packages/bits-ui/src/lib/bits/popover/components/popover-content.svelte index 86bd67109..c1f822292 100644 --- a/packages/bits-ui/src/lib/bits/popover/components/popover-content.svelte +++ b/packages/bits-ui/src/lib/bits/popover/components/popover-content.svelte @@ -22,19 +22,19 @@ {#snippet popper({ props })} {@const mergedProps = { - ...props, - ...state.props, ...restProps, + ...state.props, + ...props, }} {#if asChild} {@render child?.({ props: mergedProps })} diff --git a/packages/bits-ui/src/lib/bits/popover/popover.svelte.ts b/packages/bits-ui/src/lib/bits/popover/popover.svelte.ts index 7e2b383cd..eac37c7cb 100644 --- a/packages/bits-ui/src/lib/bits/popover/popover.svelte.ts +++ b/packages/bits-ui/src/lib/bits/popover/popover.svelte.ts @@ -87,6 +87,7 @@ class PopoverTriggerState { #onkeydown = (e: KeyboardEvent) => { if (!(e.key === kbd.ENTER || e.key === kbd.SPACE)) return; + e.preventDefault(); this.root.toggleOpen(); }; } diff --git a/packages/bits-ui/src/lib/bits/utilities/floating-layer/components/floating-layer-content.svelte b/packages/bits-ui/src/lib/bits/utilities/floating-layer/components/floating-layer-content.svelte index a1b6abec4..1597dc258 100644 --- a/packages/bits-ui/src/lib/bits/utilities/floating-layer/components/floating-layer-content.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/floating-layer/components/floating-layer-content.svelte @@ -21,6 +21,7 @@ strategy = "fixed", dir = "ltr", style = {}, + present, }: ContentProps = $props(); const state = setFloatingContentState({ @@ -40,6 +41,7 @@ strategy: readonlyBox(() => strategy), dir: readonlyBox(() => dir), style: readonlyBox(() => style), + present: readonlyBox(() => present), }); diff --git a/packages/bits-ui/src/lib/bits/utilities/floating-layer/floating-layer.svelte.ts b/packages/bits-ui/src/lib/bits/utilities/floating-layer/floating-layer.svelte.ts index 08672a9fd..9c0b555d5 100644 --- a/packages/bits-ui/src/lib/bits/utilities/floating-layer/floating-layer.svelte.ts +++ b/packages/bits-ui/src/lib/bits/utilities/floating-layer/floating-layer.svelte.ts @@ -16,6 +16,7 @@ import { type Box, type ReadonlyBox, type ReadonlyBoxedValues, + afterTick, boxedState, generateId, styleToString, @@ -82,6 +83,7 @@ export type FloatingContentStateProps = ReadonlyBoxedValues<{ onPlaced: () => void; dir: TextDirection; style: StyleProperties; + present: boolean; }>; class FloatingContentState { @@ -103,6 +105,7 @@ class FloatingContentState { updatePositionStrategy = undefined as unknown as FloatingContentStateProps["updatePositionStrategy"]; onPlaced = undefined as unknown as FloatingContentStateProps["onPlaced"]; + present = undefined as unknown as FloatingContentStateProps["present"]; arrowSize: { readonly value: | { @@ -200,7 +203,7 @@ class FloatingContentState { ...this.style.value, // if the FloatingContent hasn't been placed yet (not all measurements done) // we prevent animations so that users's animation don't kick in too early referring wrong sides - animation: !this.floating.isPositioned ? "none" : undefined, + // animation: !this.floating.isPositioned ? "none" : undefined, }), }); @@ -222,6 +225,9 @@ class FloatingContentState { this.dir = props.dir; this.style = props.style; this.root = root; + this.present = props.present; + this.arrowSize = useSize(this.root.arrowNode); + this.root.contentNode = useNodeById(this.id); this.floating = useFloating({ strategy: () => this.strategy.value, placement: () => this.desiredPlacement, @@ -233,12 +239,9 @@ class FloatingContentState { }); return cleanup; }, + open: () => this.present.value, }); - this.arrowSize = useSize(this.root.arrowNode); - - this.root.contentNode = useNodeById(this.id); - $effect(() => { if (this.floating.isPositioned) { this.onPlaced?.value(); diff --git a/packages/bits-ui/src/lib/bits/utilities/floating-layer/types.ts b/packages/bits-ui/src/lib/bits/utilities/floating-layer/types.ts index 749d418a3..ec964d05f 100644 --- a/packages/bits-ui/src/lib/bits/utilities/floating-layer/types.ts +++ b/packages/bits-ui/src/lib/bits/utilities/floating-layer/types.ts @@ -98,6 +98,11 @@ export type FloatingLayerContentProps = { * The style properties to apply to the content. */ style?: StyleProperties; + + /** + * Whether the floating layer is present. + */ + present: boolean; }; export type FloatingLayerArrowProps = { diff --git a/packages/bits-ui/src/lib/bits/utilities/popper-layer/popper-layer.svelte b/packages/bits-ui/src/lib/bits/utilities/popper-layer/popper-layer.svelte index 89569bf4f..741a3cc3c 100644 --- a/packages/bits-ui/src/lib/bits/utilities/popper-layer/popper-layer.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/popper-layer/popper-layer.svelte @@ -13,7 +13,7 @@ {#snippet presence({ present })} - + {#snippet content({ props })} diff --git a/packages/bits-ui/src/lib/internal/events.ts b/packages/bits-ui/src/lib/internal/events.ts index 8de5a309d..37939412c 100644 --- a/packages/bits-ui/src/lib/internal/events.ts +++ b/packages/bits-ui/src/lib/internal/events.ts @@ -97,7 +97,6 @@ export function addEventListener( // Return a function that removes the event listener from the target element(s). return () => { - console.log("removing event listeners"); events.forEach((_event) => target.removeEventListener(_event, handler, options)); }; } diff --git a/packages/bits-ui/src/lib/internal/floating-svelte/use-floating.svelte.ts b/packages/bits-ui/src/lib/internal/floating-svelte/use-floating.svelte.ts index ef727f5ac..b8a56c932 100644 --- a/packages/bits-ui/src/lib/internal/floating-svelte/use-floating.svelte.ts +++ b/packages/bits-ui/src/lib/internal/floating-svelte/use-floating.svelte.ts @@ -1,5 +1,6 @@ import { computePosition } from "@floating-ui/dom"; import { box, boxedState } from "../box.svelte.js"; +import { afterTick } from "../after-tick.js"; import type { UseFloatingOptions, UseFloatingReturn } from "./types.js"; import { get, getDPR, roundByDPR } from "./utils.js"; @@ -59,7 +60,6 @@ export function useFloating(options: UseFloatingOptions): UseFloatingReturn { function update() { if (reference.value === null || floating.value === null) return; - computePosition(reference.value, floating.value, { middleware: middlewareOption, placement: placementOption, diff --git a/packages/bits-ui/src/lib/internal/use-presence.svelte.ts b/packages/bits-ui/src/lib/internal/use-presence.svelte.ts index 89bee80c6..fa39c07c0 100644 --- a/packages/bits-ui/src/lib/internal/use-presence.svelte.ts +++ b/packages/bits-ui/src/lib/internal/use-presence.svelte.ts @@ -1,6 +1,6 @@ -import { onDestroy } from "svelte"; +import { onDestroy, untrack } from "svelte"; import { type Box, type ReadonlyBox, boxedState, watch } from "./box.svelte.js"; -import { useNodeById, useStateMachine } from "$lib/internal/index.js"; +import { afterTick, useNodeById, useStateMachine } from "$lib/internal/index.js"; export function usePresence(present: ReadonlyBox, id: ReadonlyBox) { const styles = boxedState({}) as unknown as Box; @@ -34,7 +34,7 @@ export function usePresence(present: ReadonlyBox, id: ReadonlyBox, id: ReadonlyBox, id: ReadonlyBox, id: ReadonlyBox { - if (currNode) { - styles.value = getComputedStyle(currNode); - currNode.addEventListener("animationstart", handleAnimationStart); - currNode.addEventListener("animationcancel", handleAnimationEnd); - currNode.addEventListener("animationend", handleAnimationEnd); - } else { - dispatch("ANIMATION_END"); - prevNode?.removeEventListener("animationstart", handleAnimationStart); - prevNode?.removeEventListener("animationcancel", handleAnimationEnd); - prevNode?.removeEventListener("animationend", handleAnimationEnd); - } - }, - { immediate: true } - ); + const watcher = watch(node, (currNode, prevNode) => { + if (currNode) { + styles.value = getComputedStyle(currNode); + currNode.addEventListener("animationstart", handleAnimationStart); + currNode.addEventListener("animationcancel", handleAnimationEnd); + currNode.addEventListener("animationend", handleAnimationEnd); + } else { + dispatch("ANIMATION_END"); + prevNode?.removeEventListener("animationstart", handleAnimationStart); + prevNode?.removeEventListener("animationcancel", handleAnimationEnd); + prevNode?.removeEventListener("animationend", handleAnimationEnd); + } + }); onDestroy(() => { watcher();