import { bottomSheetHandle } from "@seed-design/css/recipes/bottom-sheet-handle"; import { menuSheet, type MenuSheetVariantProps } from "@seed-design/css/recipes/menu-sheet"; import { menuSheetItem, type MenuSheetItemVariantProps, } from "@seed-design/css/recipes/menu-sheet-item"; import { Drawer } from "@seed-design/react-drawer"; import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive"; import clsx from "clsx"; import * as React from "react"; import { createSlotRecipeContext } from "../../utils/createSlotRecipeContext"; const { withContext, useClassNames, ClassNamesProvider } = createSlotRecipeContext(menuSheet); const { PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, ClassNamesProvider: ItemClassNamesProvider, } = createSlotRecipeContext(menuSheetItem); //////////////////////////////////////////////////////////////////////////////////// // Allowlist only the Drawer props that fit a bottom menu sheet, so the public surface // stays stable if Drawer changes or its implementation is swapped later. Intentionally // left out: `direction` (forced "bottom" below so the bottom-only recipe transform holds), // `dismissible` (no default close button → would trap users), and `modal` (omitted so it // stays at Drawer's default `true`, keeping the focus trap and aria-modal contract on the // action list). export interface SwipeableMenuSheetRootProps extends MenuSheetVariantProps, Pick< Drawer.RootProps, | "children" | "open" | "defaultOpen" | "onOpenChange" | "closeOnEscape" | "closeOnInteractOutside" | "lazyMount" | "unmountOnExit" | "onAnimationEnd" > {} // Forces `direction="bottom"` so the bottom-only recipe transform isn't broken. export function SwipeableMenuSheetRoot(props: SwipeableMenuSheetRootProps) { const [variantProps, otherProps] = menuSheet.splitVariantProps({ lazyMount: true, unmountOnExit: true, ...props, }); const classNames = menuSheet(variantProps); return ( ); } //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetTriggerProps extends Drawer.TriggerProps {} export const SwipeableMenuSheetTrigger = Drawer.Trigger; //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetPositionerProps extends Drawer.PositionerProps {} export const SwipeableMenuSheetPositioner = withContext< HTMLDivElement, SwipeableMenuSheetPositionerProps >(Drawer.Positioner, "positioner"); //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetBackdropProps extends Drawer.BackdropProps {} export const SwipeableMenuSheetBackdrop = withContext< HTMLDivElement, SwipeableMenuSheetBackdropProps >(Drawer.Backdrop, "backdrop"); //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetContentProps extends Drawer.ContentProps, Pick {} export const SwipeableMenuSheetContent = React.forwardRef< HTMLDivElement, SwipeableMenuSheetContentProps >(({ className, ...props }, ref) => { const [variantProps, otherProps] = menuSheetItem.splitVariantProps(props); const classNames = useClassNames(); return ( ); }); SwipeableMenuSheetContent.displayName = "SwipeableMenuSheetContent"; //////////////////////////////////////////////////////////////////////////////////// // `preventCycle` only applies when snap points are configured, and // SwipeableMenuSheet omits snap points from its Root API. export interface SwipeableMenuSheetHandleProps extends PrimitiveProps, Omit {} export const SwipeableMenuSheetHandle = React.forwardRef< HTMLDivElement, SwipeableMenuSheetHandleProps >(({ className, ...props }, ref) => { const classNames = bottomSheetHandle(); return ( ); }); SwipeableMenuSheetHandle.displayName = "SwipeableMenuSheetHandle"; //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetHeaderProps extends Drawer.HeaderProps {} export const SwipeableMenuSheetHeader = withContext( Drawer.Header, "header", ); //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetTitleProps extends Drawer.TitleProps {} export const SwipeableMenuSheetTitle = withContext< HTMLHeadingElement, SwipeableMenuSheetTitleProps >(Drawer.Title, "title"); SwipeableMenuSheetTitle.displayName = "SwipeableMenuSheetTitle"; //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetDescriptionProps extends Drawer.DescriptionProps {} export const SwipeableMenuSheetDescription = withContext< HTMLParagraphElement, SwipeableMenuSheetDescriptionProps >(Drawer.Description, "description"); //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetListProps extends PrimitiveProps, React.HTMLAttributes {} export const SwipeableMenuSheetList = withContext( Primitive.div, "list", ); //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetGroupProps extends PrimitiveProps, React.HTMLAttributes, Pick {} export const SwipeableMenuSheetGroup = React.forwardRef< HTMLDivElement, SwipeableMenuSheetGroupProps >(({ className, ...props }, ref) => { const [variantProps, otherProps] = menuSheetItem.splitVariantProps(props); const parentProps = useItemProps(); const classNames = useClassNames(); return ( ); }); SwipeableMenuSheetGroup.displayName = "SwipeableMenuSheetGroup"; //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetItemProps extends PrimitiveProps, MenuSheetItemVariantProps, React.HTMLAttributes {} export const SwipeableMenuSheetItem = React.forwardRef< HTMLButtonElement, SwipeableMenuSheetItemProps >(({ className: propClassName, ...props }, ref) => { const [variantProps, otherProps] = menuSheetItem.splitVariantProps(props); const parentProps = useItemProps(); const classNames = menuSheetItem({ ...parentProps, ...variantProps }); return ( ); }); SwipeableMenuSheetItem.displayName = "SwipeableMenuSheetItem"; export interface SwipeableMenuSheetItemContentProps extends PrimitiveProps, React.HTMLAttributes {} export const SwipeableMenuSheetItemContent = withItemContext< HTMLDivElement, SwipeableMenuSheetItemContentProps >(Primitive.div, "content"); export interface SwipeableMenuSheetItemLabelProps extends PrimitiveProps, React.HTMLAttributes {} export const SwipeableMenuSheetItemLabel = withItemContext< HTMLSpanElement, SwipeableMenuSheetItemLabelProps >(Primitive.span, "label"); export interface SwipeableMenuSheetItemDescriptionProps extends PrimitiveProps, React.HTMLAttributes {} export const SwipeableMenuSheetItemDescription = withItemContext< HTMLSpanElement, SwipeableMenuSheetItemDescriptionProps >(Primitive.span, "description"); //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetFooterProps extends PrimitiveProps, React.HTMLAttributes {} export const SwipeableMenuSheetFooter = withContext( Primitive.div, "footer", ); //////////////////////////////////////////////////////////////////////////////////// export interface SwipeableMenuSheetCloseButtonProps extends Drawer.CloseButtonProps {} /** * Visible button that closes the swipeable menu sheet. */ export const SwipeableMenuSheetCloseButton = withContext< HTMLButtonElement, SwipeableMenuSheetCloseButtonProps >(Drawer.CloseButton, "closeButton");