feat: add Menu component with sub-components
This commit is contained in:
107
src/components/ui/Menu.tsx
Normal file
107
src/components/ui/Menu.tsx
Normal 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 }
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user