Skip to content

Commit

Permalink
next(breaking): Floating Content child Snippet (#994)
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte authored Dec 10, 2024
1 parent 273a731 commit cf0cb2f
Show file tree
Hide file tree
Showing 63 changed files with 1,107 additions and 1,079 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-poems-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"bits-ui": patch
---

BREAKING: Update `child` snippet behavior of Floating UI-based `Content` components
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { box, mergeProps } from "svelte-toolbelt";
import type { ContextMenuContentProps } from "../types.js";
import type { ContextMenuContentStaticProps } from "../types.js";
import { CONTEXT_MENU_TRIGGER_ATTR, useMenuContent } from "$lib/bits/menu/menu.svelte.js";
import { useId } from "$lib/internal/use-id.js";
import { noop } from "$lib/internal/noop.js";
Expand All @@ -22,7 +22,7 @@
onEscapeKeydown = noop,
forceMount = false,
...restProps
}: ContextMenuContentProps = $props();
}: ContextMenuContentStaticProps = $props();
let isMounted = $state(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,17 @@
{loop}
{id}
>
{#snippet popper({ props })}
{#snippet popper({ props, wrapperProps })}
{@const finalProps = mergeProps(props, {
style: getFloatingContentCSSVars("context-menu"),
})}
{#if child}
{@render child({ props: finalProps, ...contentState.snippetProps })}
{@render child({ props: finalProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...finalProps}>
{@render children?.()}
<div {...wrapperProps}>
<div {...finalProps}>
{@render children?.()}
</div>
</div>
{/if}
<Mounted bind:isMounted />
Expand All @@ -107,15 +109,17 @@
{loop}
{id}
>
{#snippet popper({ props })}
{#snippet popper({ props, wrapperProps })}
{@const finalProps = mergeProps(props, {
style: getFloatingContentCSSVars("context-menu"),
})}
{#if child}
{@render child({ props: finalProps, ...contentState.snippetProps })}
{@render child({ props: finalProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...finalProps}>
{@render children?.()}
<div {...wrapperProps}>
<div {...finalProps}>
{@render children?.()}
</div>
</div>
{/if}
<Mounted bind:isMounted />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { box, mergeProps } from "svelte-toolbelt";
import type { DropdownMenuContentProps } from "../types.js";
import type { DropdownMenuContentStaticProps } from "../types.js";
import { useMenuContent } from "$lib/bits/menu/menu.svelte.js";
import { useId } from "$lib/internal/use-id.js";
import { noop } from "$lib/internal/noop.js";
Expand All @@ -19,7 +19,7 @@
onEscapeKeydown = noop,
forceMount = false,
...restProps
}: DropdownMenuContentProps = $props();
}: DropdownMenuContentStaticProps = $props();
let isMounted = $state(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,17 @@
forceMount={true}
{id}
>
{#snippet popper({ props })}
{#snippet popper({ props, wrapperProps })}
{@const finalProps = mergeProps(props, {
style: getFloatingContentCSSVars("dropdown-menu"),
})}
{#if child}
{@render child({ props: finalProps, ...contentState.snippetProps })}
{@render child({ props: finalProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...finalProps}>
{@render children?.()}
<div {...wrapperProps}>
<div {...finalProps}>
{@render children?.()}
</div>
</div>
{/if}
<Mounted bind:isMounted />
Expand All @@ -85,15 +87,17 @@
forceMount={false}
{id}
>
{#snippet popper({ props })}
{#snippet popper({ props, wrapperProps })}
{@const finalProps = mergeProps(props, {
style: getFloatingContentCSSVars("dropdown-menu"),
})}
{#if child}
{@render child({ props: finalProps, ...contentState.snippetProps })}
{@render child({ props: finalProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...finalProps}>
{@render children?.()}
<div {...wrapperProps}>
<div {...finalProps}>
{@render children?.()}
</div>
</div>
{/if}
<Mounted bind:isMounted />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { box, mergeProps } from "svelte-toolbelt";
import type { LinkPreviewContentProps } from "../types.js";
import type { LinkPreviewContentStaticProps } from "../types.js";
import { useLinkPreviewContent } from "../link-preview.svelte.js";
import { useId } from "$lib/internal/use-id.js";
import PopperLayer from "$lib/bits/utilities/popper-layer/popper-layer.svelte";
Expand All @@ -12,19 +12,11 @@
child,
id = useId(),
ref = $bindable(null),
side = "top",
sideOffset = 0,
align = "center",
avoidCollisions = true,
arrowPadding = 0,
sticky = "partial",
hideWhenDetached = false,
collisionPadding = 0,
onInteractOutside,
onEscapeKeydown,
forceMount = false,
...restProps
}: LinkPreviewContentProps = $props();
}: LinkPreviewContentStaticProps = $props();
const contentState = useLinkPreviewContent({
id: box.with(() => id),
Expand All @@ -34,18 +26,7 @@
),
});
const floatingProps = $derived({
side,
sideOffset,
align,
avoidCollisions,
arrowPadding,
sticky,
hideWhenDetached,
collisionPadding,
});
const mergedProps = $derived(mergeProps(restProps, floatingProps, contentState.props));
const mergedProps = $derived(mergeProps(restProps, contentState.props));
function handleInteractOutside(e: PointerEvent) {
onInteractOutside?.(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,17 @@
preventScroll={false}
forceMount={true}
>
{#snippet popper({ props })}
{#snippet popper({ props, wrapperProps })}
{@const mergedProps = mergeProps(props, {
style: getFloatingContentCSSVars("link-preview"),
})}
{#if child}
{@render child({ props: mergedProps, ...contentState.snippetProps })}
{@render child({ props: mergedProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
<div {...wrapperProps}>
<div {...mergedProps}>
{@render children?.()}
</div>
</div>
{/if}
{/snippet}
Expand All @@ -102,15 +104,17 @@
preventScroll={false}
forceMount={false}
>
{#snippet popper({ props })}
{#snippet popper({ props, wrapperProps })}
{@const mergedProps = mergeProps(props, {
style: getFloatingContentCSSVars("link-preview"),
})}
{#if child}
{@render child({ props: mergedProps, ...contentState.snippetProps })}
{@render child({ props: mergedProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
<div {...wrapperProps}>
<div {...mergedProps}>
{@render children?.()}
</div>
</div>
{/if}
<Mounted onMountedChange={(m) => (contentState.root.contentMounted = m)} />
Expand Down
15 changes: 4 additions & 11 deletions packages/bits-ui/src/lib/bits/link-preview/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
WithChildren,
Without,
} from "$lib/internal/types.js";
import type { FloatingContentSnippetProps, StaticContentSnippetProps } from "$lib/shared/types.js";

export type LinkPreviewRootPropsWithoutHTML = WithChildren<{
/**
Expand Down Expand Up @@ -69,14 +70,6 @@ export type LinkPreviewRootPropsWithoutHTML = WithChildren<{

export type LinkPreviewRootProps = LinkPreviewRootPropsWithoutHTML;

export type LinkPreviewContentSnippetProps = {
/**
* Whether the content is open or closed. Used alongside the `forceMount` prop to
* conditionally render the content using Svelte transitions.
*/
open: boolean;
};

export type LinkPreviewContentPropsWithoutHTML = WithChildNoChildrenSnippetProps<
Pick<
FloatingLayerContentProps,
Expand All @@ -101,13 +94,13 @@ export type LinkPreviewContentPropsWithoutHTML = WithChildNoChildrenSnippetProps
*/
forceMount?: boolean;
},
LinkPreviewContentSnippetProps
FloatingContentSnippetProps
>;

export type LinkPreviewContentProps = LinkPreviewContentPropsWithoutHTML &
Without<BitsPrimitiveDivAttributes, LinkPreviewContentPropsWithoutHTML>;

export type LinkPreviewContentStaticPropsWithoutHTML = WithChild<
export type LinkPreviewContentStaticPropsWithoutHTML = WithChildNoChildrenSnippetProps<
Pick<FloatingLayerContentProps, "dir"> &
Omit<DismissibleLayerProps, "onInteractOutsideStart"> &
EscapeLayerProps & {
Expand All @@ -118,7 +111,7 @@ export type LinkPreviewContentStaticPropsWithoutHTML = WithChild<
*/
forceMount?: boolean;
},
LinkPreviewContentSnippetProps
StaticContentSnippetProps
>;

export type LinkPreviewContentStaticProps = LinkPreviewContentStaticPropsWithoutHTML &
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { box, mergeProps } from "svelte-toolbelt";
import type { MenuContentProps } from "../types.js";
import type { MenuContentStaticProps } from "../types.js";
import { useMenuContent } from "../menu.svelte.js";
import { useId } from "$lib/internal/use-id.js";
import { noop } from "$lib/internal/noop.js";
Expand All @@ -19,7 +19,7 @@
onEscapeKeydown = noop,
forceMount = false,
...restProps
}: MenuContentProps = $props();
}: MenuContentStaticProps = $props();
let isMounted = $state(false);
Expand Down
20 changes: 12 additions & 8 deletions packages/bits-ui/src/lib/bits/menu/components/menu-content.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,20 @@
forceMount={true}
{id}
>
{#snippet popper({ props })}
{#snippet popper({ props, wrapperProps })}
{@const finalProps = mergeProps(props, {
style: {
outline: "none",
...getFloatingContentCSSVars("menu"),
},
})}
{#if child}
{@render child({ props: finalProps, ...contentState.snippetProps })}
{@render child({ props: finalProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...finalProps}>
{@render children?.()}
<div {...wrapperProps}>
<div {...finalProps}>
{@render children?.()}
</div>
</div>
{/if}
<Mounted bind:isMounted />
Expand All @@ -91,18 +93,20 @@
forceMount={false}
{id}
>
{#snippet popper({ props })}
{#snippet popper({ props, wrapperProps })}
{@const finalProps = mergeProps(props, {
style: {
outline: "none",
...getFloatingContentCSSVars("menu"),
},
})}
{#if child}
{@render child({ props: finalProps, ...contentState.snippetProps })}
{@render child({ props: finalProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...finalProps}>
{@render children?.()}
<div {...wrapperProps}>
<div {...finalProps}>
{@render children?.()}
</div>
</div>
{/if}
<Mounted bind:isMounted />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { afterTick, box, mergeProps } from "svelte-toolbelt";
import type { MenuSubContentProps } from "../types.js";
import type { MenuSubContentStaticProps } from "../types.js";
import { useMenuContent } from "../menu.svelte.js";
import { SUB_CLOSE_KEYS } from "../utils.js";
import { useId } from "$lib/internal/use-id.js";
Expand All @@ -25,9 +25,8 @@
onOpenAutoFocus: onOpenAutoFocusProp = noop,
onCloseAutoFocus: onCloseAutoFocusProp = noop,
onFocusOutside = noop,
side = "right",
...restProps
}: MenuSubContentProps = $props();
}: MenuSubContentStaticProps = $props();
let isMounted = $state(false);
Expand Down Expand Up @@ -58,7 +57,6 @@
const mergedProps = $derived(
mergeProps(restProps, subContentState.props, {
side,
onkeydown,
[dataAttr]: "",
})
Expand All @@ -69,7 +67,7 @@
if (e.defaultPrevented) return;
afterTick(() => {
e.preventDefault();
if (subContentState.parentMenu.root.isUsingKeyboard.current) {
if (subContentState.parentMenu.root.isUsingKeyboard) {
const subContentEl = subContentState.parentMenu.contentNode;
subContentEl?.focus();
}
Expand Down
Loading

0 comments on commit cf0cb2f

Please sign in to comment.