mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-07 08:23:55 +00:00
96 lines
3.2 KiB
TypeScript
96 lines
3.2 KiB
TypeScript
import { PropsWithChildren } from 'react';
|
|
import { IconType } from 'react-icons';
|
|
import { Menu as BaseMenu } from '@base-ui/react/menu';
|
|
|
|
import style from './DropdownMenu.module.scss';
|
|
|
|
type DropdownMenuItemDivider = { type: 'divider' };
|
|
type DropdownMenuItem = {
|
|
type: 'item' | 'destructive';
|
|
label: string;
|
|
icon?: IconType;
|
|
disabled?: boolean;
|
|
onClick: () => void;
|
|
shortcut?: string;
|
|
};
|
|
|
|
export type DropdownMenuOption = DropdownMenuItemDivider | DropdownMenuItem;
|
|
|
|
interface DropdownMenuProps extends BaseMenu.Trigger.Props {
|
|
items: DropdownMenuOption[];
|
|
}
|
|
|
|
export function DropdownMenu({ items, children, ...triggerProps }: PropsWithChildren<DropdownMenuProps>) {
|
|
return (
|
|
<BaseMenu.Root>
|
|
<BaseMenu.Trigger {...triggerProps}>{children}</BaseMenu.Trigger>
|
|
<BaseMenu.Portal>
|
|
<BaseMenu.Positioner className={style.positioner} align='start' sideOffset={8}>
|
|
<BaseMenu.Popup className={style.popup}>
|
|
{items.map((item, index) => {
|
|
if (item.type === 'divider') {
|
|
return <BaseMenu.Separator key={index} className={style.separator} />;
|
|
}
|
|
return (
|
|
<BaseMenu.Item
|
|
key={index}
|
|
className={style.item}
|
|
onClick={item.onClick}
|
|
disabled={item.disabled}
|
|
data-type={item.type}
|
|
>
|
|
<span className={style.label}>
|
|
{item.icon && <item.icon />}
|
|
{item.label}
|
|
</span>
|
|
{item.shortcut && <span className={style.shortcut}>{item.shortcut}</span>}
|
|
</BaseMenu.Item>
|
|
);
|
|
})}
|
|
</BaseMenu.Popup>
|
|
</BaseMenu.Positioner>
|
|
</BaseMenu.Portal>
|
|
</BaseMenu.Root>
|
|
);
|
|
}
|
|
|
|
interface PositionedDropdownMenuProps {
|
|
items: Array<DropdownMenuItemDivider | DropdownMenuItem>;
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
position: { x: number; y: number };
|
|
}
|
|
|
|
export function PositionedDropdownMenu({ items, isOpen, position, onClose }: PositionedDropdownMenuProps) {
|
|
return (
|
|
<BaseMenu.Root
|
|
open={isOpen}
|
|
onOpenChange={(open) => {
|
|
if (!open) onClose();
|
|
}}
|
|
>
|
|
<BaseMenu.Trigger style={{ position: 'fixed', left: position.x, top: position.y }} aria-hidden />
|
|
<BaseMenu.Portal>
|
|
<BaseMenu.Positioner className={style.positioner} align='start' sideOffset={8} alignOffset={8}>
|
|
<BaseMenu.Popup className={style.popup}>
|
|
{items.map((item, index) => {
|
|
if (item.type === 'divider') {
|
|
return <BaseMenu.Separator key={index} className={style.separator} />;
|
|
}
|
|
return (
|
|
<BaseMenu.Item key={index} className={style.item} onClick={item.onClick} disabled={item.disabled}>
|
|
<span className={style.label}>
|
|
{item.icon && <item.icon />}
|
|
{item.label}
|
|
</span>
|
|
{item.shortcut && <span className={style.shortcut}>{item.shortcut}</span>}
|
|
</BaseMenu.Item>
|
|
);
|
|
})}
|
|
</BaseMenu.Popup>
|
|
</BaseMenu.Positioner>
|
|
</BaseMenu.Portal>
|
|
</BaseMenu.Root>
|
|
);
|
|
}
|