feat: add Menu component with sub-components

This commit is contained in:
2026-03-23 10:29:16 -06:00
parent b0617d89e8
commit 4187c3bd80

107
src/components/ui/Menu.tsx Normal file
View File

@@ -0,0 +1,107 @@
import { useRender } from '@base-ui/react/use-render'
import { cx } from '@styled-system/css'
import { type MenuVariantProps, menu } from '@styled-system/recipes/menu'
import type { FC, ReactNode } from 'react'
import type { MergeOmitting } from '@/types/helpers'
export type MenuProps = MergeOmitting<
useRender.ComponentProps<'div'>,
MenuVariantProps
>
const Menu: FC<MenuProps> = ({ render, className, ...props }) => {
const [menuProps, allOther] = menu.splitVariantProps(props)
return useRender({
defaultTagName: 'div',
render,
props: { className: cx(menu(menuProps).container, className), ...allOther }
})
}
export default Menu
export type MenuItemProps = MergeOmitting<
useRender.ComponentProps<'button'>,
MenuVariantProps
>
export const MenuItem: FC<MenuItemProps> = ({
render,
className,
...props
}) => {
const [menuProps, allOther] = menu.splitVariantProps(props)
return useRender({
defaultTagName: 'button',
render,
props: { className: cx(menu(menuProps).item, className), ...allOther }
})
}
export type MenuLabelProps = MergeOmitting<
useRender.ComponentProps<'span'>,
MenuVariantProps
>
export const MenuLabel: FC<MenuLabelProps> = ({
render,
className,
...props
}) => {
const [menuProps, allOther] = menu.splitVariantProps(props)
return useRender({
defaultTagName: 'span',
render,
props: { className: cx(menu(menuProps).label, className), ...allOther }
})
}
export type MenuGroupProps = MergeOmitting<
useRender.ComponentProps<'div'>,
MenuVariantProps & { label: ReactNode }
>
export const MenuGroup: FC<MenuGroupProps> = ({
render,
children,
className,
label,
...props
}) => {
const [menuProps, allOther] = menu.splitVariantProps(props)
return useRender({
defaultTagName: 'div',
render,
props: {
className: cx(menu(menuProps).group, className),
children: (
<>
<span className={cx(menu(menuProps).label)}>{label}</span>
{children}
</>
),
...allOther
}
})
}
export type MenuSeparatorProps = MergeOmitting<
Omit<useRender.ComponentProps<'div'>, 'children'>,
MenuVariantProps
>
export const MenuSeparator: FC<MenuSeparatorProps> = ({
render,
className,
...props
}) => {
const [menuProps, allOther] = menu.splitVariantProps(props)
return useRender({
defaultTagName: 'div',
render,
props: { className: cx(menu(menuProps).separator, className), ...allOther }
})
}