Spaces:
Build error
Build error
'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<typeof DropdownMenuAnatomy> | |
>({}); | |
export type DropdownMenuProps = ComponentAnatomy<typeof DropdownMenuAnatomy> & | |
Pick< | |
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root>, | |
'defaultOpen' | 'open' | 'onOpenChange' | 'dir' | |
> & | |
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & { | |
/** | |
* 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<HTMLDivElement, DropdownMenuProps>( | |
(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, | |
}} | |
> | |
<DropdownMenuPrimitive.Root | |
defaultOpen={defaultOpen} | |
open={open} | |
onOpenChange={onOpenChange} | |
dir={dir} | |
modal={!allowOutsideInteraction} | |
{...rest} | |
> | |
<DropdownMenuPrimitive.Trigger asChild> | |
{trigger} | |
</DropdownMenuPrimitive.Trigger> | |
<DropdownMenuPrimitive.Portal> | |
<DropdownMenuPrimitive.Content | |
ref={ref} | |
sideOffset={sideOffset} | |
className={cn(DropdownMenuAnatomy.root(), className)} | |
{...rest} | |
> | |
{children} | |
</DropdownMenuPrimitive.Content> | |
</DropdownMenuPrimitive.Portal> | |
</DropdownMenuPrimitive.Root> | |
</__DropdownMenuAnatomyContext.Provider> | |
); | |
} | |
); | |
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 ( | |
<DropdownMenuPrimitive.Group | |
ref={ref} | |
className={cn(DropdownMenuAnatomy.group(), groupClass, className)} | |
{...rest} | |
/> | |
); | |
}); | |
DropdownMenuGroup.displayName = 'DropdownMenuGroup'; | |
/* ------------------------------------------------------------------------------------------------- | |
* DropdownMenuSub | |
* -----------------------------------------------------------------------------------------------*/ | |
export type DropdownMenuSubProps = Pick< | |
ComponentAnatomy<typeof DropdownMenuAnatomy>, | |
'subTriggerClass' | |
> & | |
Pick< | |
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Sub>, | |
'defaultOpen' | 'open' | 'onOpenChange' | |
> & | |
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> & { | |
/** | |
* 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 ( | |
<DropdownMenuPrimitive.Sub {...rest}> | |
<DropdownMenuPrimitive.SubTrigger | |
className={cn( | |
DropdownMenuAnatomy.item(), | |
DropdownMenuAnatomy.subTrigger(), | |
triggerInset && 'pl-8', | |
_subTriggerClass, | |
subTriggerClass, | |
className | |
)} | |
{...triggerProps} | |
> | |
{triggerContent} | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
viewBox="0 0 24 24" | |
fill="none" | |
stroke="currentColor" | |
strokeWidth="2" | |
strokeLinecap="round" | |
strokeLinejoin="round" | |
className={cn(DropdownMenuAnatomy.shortcut(), 'w-4 h-4 ml-auto')} | |
> | |
<path d="m9 18 6-6-6-6" /> | |
</svg> | |
</DropdownMenuPrimitive.SubTrigger> | |
<DropdownMenuPrimitive.Portal> | |
<DropdownMenuPrimitive.SubContent | |
ref={ref} | |
sideOffset={sideOffset} | |
className={cn( | |
DropdownMenuAnatomy.subContent(), | |
subContentClass, | |
className | |
)} | |
{...rest} | |
> | |
{children} | |
</DropdownMenuPrimitive.SubContent> | |
</DropdownMenuPrimitive.Portal> | |
</DropdownMenuPrimitive.Sub> | |
); | |
}); | |
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 ( | |
<DropdownMenuPrimitive.Item | |
ref={ref} | |
className={cn( | |
DropdownMenuAnatomy.item(), | |
inset && 'pl-8', | |
itemClass, | |
className | |
)} | |
{...rest} | |
/> | |
); | |
}); | |
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 ( | |
<DropdownMenuPrimitive.Label | |
ref={ref} | |
className={cn( | |
DropdownMenuAnatomy.label(), | |
inset && 'pl-8', | |
labelClass, | |
className | |
)} | |
{...rest} | |
/> | |
); | |
}); | |
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 ( | |
<DropdownMenuPrimitive.Separator | |
ref={ref} | |
className={cn(DropdownMenuAnatomy.separator(), separatorClass, className)} | |
{...rest} | |
/> | |
); | |
}); | |
DropdownMenuSeparator.displayName = 'DropdownMenuSeparator'; | |
/* ------------------------------------------------------------------------------------------------- | |
* DropdownMenuShortcut | |
* -----------------------------------------------------------------------------------------------*/ | |
export type DropdownMenuShortcutProps = React.HTMLAttributes<HTMLSpanElement>; | |
export const DropdownMenuShortcut = React.forwardRef< | |
HTMLSpanElement, | |
DropdownMenuShortcutProps | |
>((props, ref) => { | |
const { className, ...rest } = props; | |
const { shortcutClass } = React.useContext(__DropdownMenuAnatomyContext); | |
return ( | |
<span | |
ref={ref} | |
className={cn(DropdownMenuAnatomy.shortcut(), shortcutClass, className)} | |
{...rest} | |
/> | |
); | |
}); | |
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'; | |