'use client'; import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; import { cva } from 'class-variance-authority'; import * as React from 'react'; import { cn, ComponentAnatomy, defineStyleAnatomy } from '../core/styling'; /* ------------------------------------------------------------------------------------------------- * Anatomy * -----------------------------------------------------------------------------------------------*/ export const DropdownMenuAnatomy = defineStyleAnatomy({ subTrigger: cva([ 'UI-DropdownMenu__subTrigger', 'focus:bg-[--subtle] data-[state=open]:bg-[--subtle]', ]), subContent: cva([ 'UI-DropdownMenu__subContent', 'z-50 min-w-[12rem] overflow-hidden rounded-[--radius] border bg-[--background] p-2 text-[--foreground] shadow-sm', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-100 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2', 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', ]), root: cva([ 'UI-DropdownMenu__root', 'z-50 min-w-[15rem] overflow-hidden rounded-[--radius] border bg-[--background] p-2 text-[--foreground] shadow-sm', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-100 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2', 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', ]), item: cva([ 'UI-DropdownMenu__item', 'relative flex cursor-default select-none items-center rounded-[--radius] cursor-pointer px-2 py-2 text-sm outline-none transition-colors', 'focus:bg-[--subtle] data-[disabled]:pointer-events-none', 'data-[disabled]:opacity-50', '[&>svg]:mr-2 [&>svg]:text-lg', ]), group: cva(['UI-DropdownMenu__group']), label: cva([ 'UI-DropdownMenu__label', 'px-2 py-1.5 text-sm font-semibold text-[--muted]', ]), separator: cva([ 'UI-DropdownMenu__separator', '-mx-1 my-2 h-px bg-[--border]', ]), shortcut: cva([ 'UI-DropdownMenu__shortcut', 'ml-auto text-xs tracking-widest opacity-60', ]), }); /* ------------------------------------------------------------------------------------------------- * DropdownMenu * -----------------------------------------------------------------------------------------------*/ const __DropdownMenuAnatomyContext = React.createContext< ComponentAnatomy >({}); export type DropdownMenuProps = ComponentAnatomy & Pick< React.ComponentPropsWithoutRef, 'defaultOpen' | 'open' | 'onOpenChange' | 'dir' > & React.ComponentPropsWithoutRef & { /** * Interaction with outside elements will be enabled and other elements will be visible to screen readers. */ allowOutsideInteraction?: boolean; /** * The trigger element that is always visible and is used to open the menu. */ trigger?: React.ReactNode; }; export const DropdownMenu = React.forwardRef( (props, ref) => { const { children, trigger, // Root defaultOpen, open, onOpenChange, dir, allowOutsideInteraction, // Content sideOffset = 4, className, subContentClass, subTriggerClass, shortcutClass, itemClass, labelClass, separatorClass, groupClass, ...rest } = props; return ( <__DropdownMenuAnatomyContext.Provider value={{ subContentClass, subTriggerClass, shortcutClass, itemClass, labelClass, separatorClass, groupClass, }} > {trigger} {children} ); } ); DropdownMenu.displayName = 'DropdownMenu'; /* ------------------------------------------------------------------------------------------------- * DropdownMenuGroup * -----------------------------------------------------------------------------------------------*/ export type DropdownMenuGroupProps = React.ComponentPropsWithoutRef< typeof DropdownMenuPrimitive.Group >; export const DropdownMenuGroup = React.forwardRef< HTMLDivElement, DropdownMenuGroupProps >((props, ref) => { const { className, ...rest } = props; const { groupClass } = React.useContext(__DropdownMenuAnatomyContext); return ( ); }); DropdownMenuGroup.displayName = 'DropdownMenuGroup'; /* ------------------------------------------------------------------------------------------------- * DropdownMenuSub * -----------------------------------------------------------------------------------------------*/ export type DropdownMenuSubProps = Pick< ComponentAnatomy, 'subTriggerClass' > & Pick< React.ComponentPropsWithoutRef, 'defaultOpen' | 'open' | 'onOpenChange' > & React.ComponentPropsWithoutRef & { /** * The content of the default trigger element that will open the sub menu. * * By default, the trigger will be an item with a right chevron icon. */ triggerContent?: React.ReactNode; /** * Props to pass to the default trigger element that will open the sub menu. */ triggerProps?: React.ComponentPropsWithoutRef< typeof DropdownMenuPrimitive.SubTrigger >; triggerInset?: boolean; }; export const DropdownMenuSub = React.forwardRef< HTMLDivElement, DropdownMenuSubProps >((props, ref) => { const { children, triggerContent, triggerProps, triggerInset, // Sub defaultOpen, open, onOpenChange, // SubContent sideOffset = 8, className, subTriggerClass, ...rest } = props; const { subTriggerClass: _subTriggerClass, subContentClass } = React.useContext(__DropdownMenuAnatomyContext); return ( {triggerContent} {children} ); }); DropdownMenuSub.displayName = 'DropdownMenuSub'; /* ------------------------------------------------------------------------------------------------- * DropdownMenuItem * -----------------------------------------------------------------------------------------------*/ export type DropdownMenuItemProps = React.ComponentPropsWithoutRef< typeof DropdownMenuPrimitive.Item > & { inset?: boolean; }; export const DropdownMenuItem = React.forwardRef< HTMLDivElement, DropdownMenuItemProps >((props, ref) => { const { className, inset, ...rest } = props; const { itemClass } = React.useContext(__DropdownMenuAnatomyContext); return ( ); }); DropdownMenuItem.displayName = 'DropdownMenuItem'; /* ------------------------------------------------------------------------------------------------- * DropdownMenuLabel * -----------------------------------------------------------------------------------------------*/ export type DropdownMenuLabelProps = React.ComponentPropsWithoutRef< typeof DropdownMenuPrimitive.Label > & { inset?: boolean; }; export const DropdownMenuLabel = React.forwardRef< HTMLDivElement, DropdownMenuLabelProps >((props, ref) => { const { className, inset, ...rest } = props; const { labelClass } = React.useContext(__DropdownMenuAnatomyContext); return ( ); }); DropdownMenuLabel.displayName = 'DropdownMenuLabel'; /* ------------------------------------------------------------------------------------------------- * DropdownMenuSeparator * -----------------------------------------------------------------------------------------------*/ export type DropdownMenuSeparatorProps = React.ComponentPropsWithoutRef< typeof DropdownMenuPrimitive.Separator >; export const DropdownMenuSeparator = React.forwardRef< HTMLDivElement, DropdownMenuSeparatorProps >((props, ref) => { const { className, ...rest } = props; const { separatorClass } = React.useContext(__DropdownMenuAnatomyContext); return ( ); }); DropdownMenuSeparator.displayName = 'DropdownMenuSeparator'; /* ------------------------------------------------------------------------------------------------- * DropdownMenuShortcut * -----------------------------------------------------------------------------------------------*/ export type DropdownMenuShortcutProps = React.HTMLAttributes; export const DropdownMenuShortcut = React.forwardRef< HTMLSpanElement, DropdownMenuShortcutProps >((props, ref) => { const { className, ...rest } = props; const { shortcutClass } = React.useContext(__DropdownMenuAnatomyContext); return ( ); }); DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';