71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
// packages/core/src/components/popover/popover-root.tsx
|
|
import type { Middleware, Placement } from "@floating-ui/dom";
|
|
import { flip, offset, shift } from "@floating-ui/dom";
|
|
import type { Accessor, JSX } from "solid-js";
|
|
import { createSignal, createUniqueId } from "solid-js";
|
|
import {
|
|
type CreateDisclosureStateOptions,
|
|
createDisclosureState,
|
|
} from "../../primitives/create-disclosure-state";
|
|
import { createFloating } from "../../primitives/create-floating";
|
|
import {
|
|
InternalPopoverContextProvider,
|
|
PopoverPublicContextProvider,
|
|
type InternalPopoverContextValue,
|
|
} from "./popover-context";
|
|
|
|
import type { PopoverRootProps } from "./popover.props";
|
|
export type { PopoverRootProps } from "./popover.props";
|
|
|
|
/**
|
|
* Root component for Popover. Manages open state, floating positioning,
|
|
* and provides context to all Popover parts. Renders no DOM elements.
|
|
*/
|
|
export function PopoverRoot(props: PopoverRootProps): JSX.Element {
|
|
const disclosure = createDisclosureState({
|
|
get open() {
|
|
return props.open;
|
|
},
|
|
get defaultOpen() {
|
|
return props.defaultOpen;
|
|
},
|
|
get onOpenChange() {
|
|
return props.onOpenChange;
|
|
},
|
|
} as CreateDisclosureStateOptions);
|
|
|
|
const contentId = createUniqueId();
|
|
const [triggerRef, setTriggerRef] = createSignal<HTMLElement | null>(null);
|
|
const [contentRef, setContentRef] = createSignal<HTMLElement | null>(null);
|
|
|
|
const floating = createFloating({
|
|
anchor: triggerRef,
|
|
floating: contentRef,
|
|
placement: (() => props.placement ?? "bottom") as Accessor<Placement>,
|
|
middleware: (() => [offset(8), flip(), shift({ padding: 8 })]) as Accessor<Middleware[]>,
|
|
open: disclosure.isOpen,
|
|
});
|
|
|
|
const internalCtx: InternalPopoverContextValue = {
|
|
isOpen: disclosure.isOpen,
|
|
setOpen: (open) => (open ? disclosure.open() : disclosure.close()),
|
|
modal: () => props.modal ?? false,
|
|
contentId: () => contentId,
|
|
triggerRef,
|
|
setTriggerRef,
|
|
contentRef,
|
|
setContentRef,
|
|
floatingStyle: floating.style,
|
|
};
|
|
|
|
return (
|
|
<InternalPopoverContextProvider value={internalCtx}>
|
|
<PopoverPublicContextProvider
|
|
value={{ open: disclosure.isOpen, modal: () => props.modal ?? false }}
|
|
>
|
|
{props.children}
|
|
</PopoverPublicContextProvider>
|
|
</InternalPopoverContextProvider>
|
|
);
|
|
}
|