diff --git a/packages/bits-ui/src/lib/bits/dialog/types.ts b/packages/bits-ui/src/lib/bits/dialog/types.ts index 4310ca3f4..abe798512 100644 --- a/packages/bits-ui/src/lib/bits/dialog/types.ts +++ b/packages/bits-ui/src/lib/bits/dialog/types.ts @@ -1,113 +1,16 @@ -import type { HTMLButtonAttributes } from "svelte/elements"; -import type { CreateDialogProps as MeltDialogProps } from "@melt-ui/svelte"; -import type { - DOMElement, - Expand, - HTMLDivAttributes, - HTMLHeadingAttributes, - OmitOpen, - OnChangeFn, - SvelteEvent, - Transition, - TransitionProps, -} from "$lib/internal/index.js"; -import type { CustomEventHandler } from "$lib/index.js"; +import type { Snippet } from "svelte"; +import type { OnChangeFn, WithAsChild } from "$lib/internal/types.js"; -import type { FocusProp } from "$lib/shared/index.js"; +export type DialogRootPropsWithoutHTML = { + /** + * The open state of the dialog. + */ + open?: boolean; -export type DialogPropsWithoutHTML = Expand< - OmitOpen< - Omit - > & { - /** - * The open state of the dialog. - * You can bind this to a boolean value to programmatically control the open state. - * - * @defaultValue false - */ - open?: MeltDialogProps["defaultOpen"] & {}; + /** + * A callback that is called when the popover's open state changes. + */ + onOpenChange?: OnChangeFn; - /** - * A callback function called when the open state changes. - */ - onOpenChange?: OnChangeFn; - - /** - * Override the default autofocus behavior of the dialog when it opens - */ - openFocus?: FocusProp; - - /** - * Override the default autofocus behavior of the dialog after close - */ - closeFocus?: FocusProp; - } ->; - -export type DialogTriggerPropsWithoutHTML = DOMElement; - -export type DialogClosePropsWithoutHTML = DialogTriggerPropsWithoutHTML; - -export type DialogContentPropsWithoutHTML< - T extends Transition = Transition, - In extends Transition = Transition, - Out extends Transition = Transition, -> = Expand & DOMElement>; - -export type DialogDescriptionPropsWithoutHTML = DOMElement; - -export type DialogOverlayPropsWithoutHTML< - T extends Transition = Transition, - In extends Transition = Transition, - Out extends Transition = Transition, -> = Expand & DOMElement>; - -export type DialogPortalPropsWithoutHTML = DOMElement; - -export type DialogTitlePropsWithoutHTML = Expand< - { - level?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; - } & DOMElement ->; - -export type DialogProps = DialogPropsWithoutHTML; - -export type DialogTriggerProps = DialogTriggerPropsWithoutHTML & HTMLButtonAttributes; -export type DialogCloseProps = DialogTriggerProps; - -export type DialogContentProps< - T extends Transition = Transition, - In extends Transition = Transition, - Out extends Transition = Transition, -> = DialogContentPropsWithoutHTML & HTMLDivAttributes; - -export type DialogDescriptionProps = DialogDescriptionPropsWithoutHTML & HTMLDivAttributes; - -export type DialogOverlayProps< - T extends Transition = Transition, - In extends Transition = Transition, - Out extends Transition = Transition, -> = DialogOverlayPropsWithoutHTML & HTMLDivAttributes; - -export type DialogPortalProps = DialogPortalPropsWithoutHTML & HTMLDivAttributes; -export type DialogTitleProps = DialogTitlePropsWithoutHTML & HTMLHeadingAttributes; - -export type DialogOverlayEvents = { - mouseup: SvelteEvent; -}; - -export type DialogContentEvents = { - pointerdown: SvelteEvent; - pointerup: SvelteEvent; - pointermove: SvelteEvent; - touchend: SvelteEvent; - touchstart: SvelteEvent; - touchcancel: SvelteEvent; - touchmove: SvelteEvent; -}; - -export type DialogTriggerEvents = { - click: CustomEventHandler; - keydown: CustomEventHandler; + children?: Snippet; }; -export type DialogCloseEvents = DialogTriggerEvents; 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 119f168f2..7c694f59a 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 @@ -12,6 +12,9 @@ id = useId(), forceMount = false, onDestroyAutoFocus = noop, + onEscapeKeydown = noop, + onInteractOutside = noop, + loop = true, ...restProps }: ContentProps = $props(); @@ -24,8 +27,16 @@ {...restProps} present={state.root.open.value || forceMount} {id} - onInteractOutside={state.root.close} - onEscape={state.root.close} + onInteractOutside={(e) => { + onInteractOutside(e); + if (e.defaultPrevented) return; + state.root.close(); + }} + onEscapeKeydown={(e) => { + // TODO: users should be able to cancel this + onEscapeKeydown(e); + state.root.close(); + }} onDestroyAutoFocus={(e) => { onDestroyAutoFocus(e); if (e.defaultPrevented) return; @@ -33,7 +44,7 @@ state.root.triggerNode?.value?.focus(); }} trapped - loop + {loop} > {#snippet popper({ props })} {@const mergedProps = mergeProps(restProps, state.props, props)} diff --git a/packages/bits-ui/src/lib/bits/popover/types.ts b/packages/bits-ui/src/lib/bits/popover/types.ts index bae972d88..42111a4db 100644 --- a/packages/bits-ui/src/lib/bits/popover/types.ts +++ b/packages/bits-ui/src/lib/bits/popover/types.ts @@ -1,5 +1,6 @@ import type { Snippet } from "svelte"; import type { ArrowProps, ArrowPropsWithoutHTML } from "../utilities/arrow/types.js"; +import type { PopperLayerProps } from "../utilities/popper-layer/types.js"; import type { EventCallback, OnChangeFn, @@ -8,7 +9,6 @@ import type { WithAsChild, } from "$lib/internal/index.js"; import type { CustomEventHandler } from "$lib/index.js"; -import type { FloatingLayer } from "$lib/bits/utilities/floating-layer/index.js"; export type PopoverRootPropsWithoutHTML = { /** @@ -27,23 +27,9 @@ export type PopoverRootPropsWithoutHTML = { children?: Snippet; }; -export type PopoverRootProps = PopoverRootPropsWithoutHTML & PrimitiveDivAttributes; +export type PopoverRootProps = PopoverRootPropsWithoutHTML; -export type PopoverContentPropsWithoutHTML = WithAsChild< - Partial> & { - forceMount?: boolean; - } & { - onMountAutoFocus?: EventCallback; - onDestroyAutoFocus?: EventCallback; - } & { - /** - * Whether to prevent scrolling the body when the popover is open or not. - * - * @defaultValue true - */ - preventScroll?: boolean; - } ->; +export type PopoverContentPropsWithoutHTML = WithAsChild; export type PopoverContentProps = PopoverContentPropsWithoutHTML & PrimitiveDivAttributes; diff --git a/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte b/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte index a7ae166f0..3d615c9b1 100644 --- a/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte @@ -1,20 +1,20 @@ diff --git a/packages/bits-ui/src/lib/bits/utilities/escape-layer/types.ts b/packages/bits-ui/src/lib/bits/utilities/escape-layer/types.ts index f0dd2e5b4..39090c132 100644 --- a/packages/bits-ui/src/lib/bits/utilities/escape-layer/types.ts +++ b/packages/bits-ui/src/lib/bits/utilities/escape-layer/types.ts @@ -7,12 +7,10 @@ export type EscapeBehaviorType = | "ignore"; export type EscapeLayerProps = { - children?: Snippet; - /** * Callback fired when escape is pressed. */ - onEscape?: (e: KeyboardEvent) => void; + onEscapeKeydown?: (e: KeyboardEvent) => void; /** * Escape behavior type. @@ -24,10 +22,15 @@ export type EscapeLayerProps = { * @defaultValue `close` */ behaviorType?: EscapeBehaviorType; +}; +// internal props not exposed to the user but used in the implementation +export type EscapeLayerImplProps = { /** * Whether the layer is enabled. Currently, we determine this with the * `presence` returned from the `presence` layer. */ present: boolean; -}; + + children?: Snippet; +} & EscapeLayerProps; diff --git a/packages/bits-ui/src/lib/bits/utilities/escape-layer/useEscapeLayer.svelte.ts b/packages/bits-ui/src/lib/bits/utilities/escape-layer/useEscapeLayer.svelte.ts index 4c2414d8b..71395df82 100644 --- a/packages/bits-ui/src/lib/bits/utilities/escape-layer/useEscapeLayer.svelte.ts +++ b/packages/bits-ui/src/lib/bits/utilities/escape-layer/useEscapeLayer.svelte.ts @@ -1,5 +1,5 @@ import { untrack } from "svelte"; -import type { EscapeBehaviorType, EscapeLayerProps } from "./types.js"; +import type { EscapeBehaviorType, EscapeLayerImplProps } from "./types.js"; import type { ReadonlyBox, ReadonlyBoxedValues } from "$lib/internal/box.svelte.js"; import { type EventCallback, addEventListener } from "$lib/internal/events.js"; import { kbd } from "$lib/internal/kbd.js"; @@ -7,7 +7,7 @@ import { noop } from "$lib/internal/callbacks.js"; const layers = new Map>(); -type EscapeLayerStateProps = ReadonlyBoxedValues>>; +type EscapeLayerStateProps = ReadonlyBoxedValues>>; export class EscapeLayerState { #onEscapeProp: ReadonlyBox>; @@ -16,7 +16,7 @@ export class EscapeLayerState { constructor(props: EscapeLayerStateProps) { this.#behaviorType = props.behaviorType; - this.#onEscapeProp = props.onEscape; + this.#onEscapeProp = props.onEscapeKeydown; this.#present = props.present; let unsubEvents = noop; 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 aba68ce22..4f6eb2572 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 @@ -1,6 +1,6 @@
diff --git a/packages/bits-ui/src/lib/bits/utilities/floating-layer/components/index.ts b/packages/bits-ui/src/lib/bits/utilities/floating-layer/components/index.ts index c64cdb147..8e3d3eba0 100644 --- a/packages/bits-ui/src/lib/bits/utilities/floating-layer/components/index.ts +++ b/packages/bits-ui/src/lib/bits/utilities/floating-layer/components/index.ts @@ -4,6 +4,7 @@ export { default as Content } from "./floating-layer-content.svelte"; export { default as Root } from "./floating-layer.svelte"; export type { + FloatingLayerContentImplProps as ContentImplProps, FloatingLayerContentProps as ContentProps, FloatingLayerAnchorProps as AnchorProps, } from "../types.js"; 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 b9f227ae7..84c9ee3ae 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 @@ -4,7 +4,6 @@ import type { Arrayable } from "$lib/internal/types.js"; import type { Direction, StyleProperties } from "$lib/shared/index.js"; export type FloatingLayerContentProps = { - id: string; /** * The preferred side of the anchor to render against when open. * Will be reversed when collisions occur. @@ -76,11 +75,6 @@ export type FloatingLayerContentProps = { updatePositionStrategy?: "optimized" | "always"; - /** - * Callback that is called when the floating element is placed. - */ - onPlaced?: () => void; - content?: Snippet<[{ props: Record }]>; /** @@ -95,10 +89,13 @@ export type FloatingLayerContentProps = { dir?: Direction; /** - * The style properties to apply to the content. + * Whether to prevent scrolling the body when the content is open. */ - style?: StyleProperties; + preventScroll?: boolean; +}; +export type FloatingLayerContentImplProps = { + id: string; /** * Whether the floating layer is present. */ @@ -108,7 +105,17 @@ export type FloatingLayerContentProps = { * The ID of the content wrapper element. */ wrapperId?: string; -}; + + /** + * The style properties to apply to the content. + */ + style?: StyleProperties; + + /** + * Callback that is called when the floating element is placed. + */ + onPlaced?: () => void; +} & FloatingLayerContentProps; export type FloatingLayerAnchorProps = { id: string; diff --git a/packages/bits-ui/src/lib/bits/utilities/focus-scope/focus-scope.svelte b/packages/bits-ui/src/lib/bits/utilities/focus-scope/focus-scope.svelte index d8da1be80..0dbb1d81e 100644 --- a/packages/bits-ui/src/lib/bits/utilities/focus-scope/focus-scope.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/focus-scope/focus-scope.svelte @@ -1,5 +1,5 @@ diff --git a/packages/bits-ui/src/lib/bits/utilities/popper-layer/types.ts b/packages/bits-ui/src/lib/bits/utilities/popper-layer/types.ts index d91730df2..15b68c356 100644 --- a/packages/bits-ui/src/lib/bits/utilities/popper-layer/types.ts +++ b/packages/bits-ui/src/lib/bits/utilities/popper-layer/types.ts @@ -1,15 +1,32 @@ import type { Snippet } from "svelte"; -import type { EscapeLayerProps } from "../escape-layer/types.js"; -import type { DismissableLayerProps } from "../dismissable-layer/types.js"; -import type { FloatingLayerContentProps } from "../floating-layer/types.js"; -import type { TextSelectionLayerProps } from "../text-selection-layer/types.js"; -import type { PresenceLayerProps } from "../presence-layer/types.js"; -import type { FocusScopeProps } from "../focus-scope/types.js"; +import type { EscapeLayerImplProps, EscapeLayerProps } from "../escape-layer/types.js"; +import type { + DismissableLayerImplProps, + DismissableLayerProps, +} from "../dismissable-layer/types.js"; +import type { + FloatingLayerContentImplProps, + FloatingLayerContentProps, +} from "../floating-layer/types.js"; +import type { + TextSelectionLayerImplProps, + TextSelectionLayerProps, +} from "../text-selection-layer/types.js"; +import type { PresenceLayerImplProps, PresenceLayerProps } from "../presence-layer/types.js"; +import type { FocusScopeImplProps, FocusScopeProps } from "../focus-scope/types.js"; export type PopperLayerProps = EscapeLayerProps & DismissableLayerProps & FloatingLayerContentProps & PresenceLayerProps & - TextSelectionLayerProps & { + TextSelectionLayerProps & + FocusScopeProps; + +export type PopperLayerImplProps = EscapeLayerImplProps & + DismissableLayerImplProps & + FloatingLayerContentImplProps & + PresenceLayerImplProps & + TextSelectionLayerImplProps & + FocusScopeImplProps & { popper: Snippet<[{ props: Record }]>; - } & FocusScopeProps; + }; diff --git a/packages/bits-ui/src/lib/bits/utilities/presence-layer/presence-layer.svelte b/packages/bits-ui/src/lib/bits/utilities/presence-layer/presence-layer.svelte index 1382a7801..dc39a2be4 100644 --- a/packages/bits-ui/src/lib/bits/utilities/presence-layer/presence-layer.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/presence-layer/presence-layer.svelte @@ -1,16 +1,9 @@ -{#if forceMount.value || present || isPresent.value} +{#if forceMount || present || isPresent.value} {@render presence?.({ present: isPresent })} {/if} diff --git a/packages/bits-ui/src/lib/bits/utilities/presence-layer/types.ts b/packages/bits-ui/src/lib/bits/utilities/presence-layer/types.ts index 18ee8b8c3..8bd5041d6 100644 --- a/packages/bits-ui/src/lib/bits/utilities/presence-layer/types.ts +++ b/packages/bits-ui/src/lib/bits/utilities/presence-layer/types.ts @@ -2,16 +2,17 @@ import type { Snippet } from "svelte"; export type PresenceLayerProps = { /** - * The presence status. + * Whether to force mount the component. */ - present: boolean; + forceMount?: boolean; +}; +export type PresenceLayerImplProps = PresenceLayerProps & { + id: string; /** - * Whether to force mount the component. + * The presence status. */ - forceMount?: boolean; + present: boolean; presence?: Snippet<[{ present: { value: boolean } }]>; - - id: string; }; diff --git a/packages/bits-ui/src/lib/bits/utilities/text-selection-layer/text-selection-layer.svelte b/packages/bits-ui/src/lib/bits/utilities/text-selection-layer/text-selection-layer.svelte index 885c3c6bf..881bfebea 100644 --- a/packages/bits-ui/src/lib/bits/utilities/text-selection-layer/text-selection-layer.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/text-selection-layer/text-selection-layer.svelte @@ -1,20 +1,20 @@