// components/ui/Dropdown.tsx 'use client'; import * as React from 'react'; import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'; import { ChevronDownIcon, EllipsisVerticalIcon, } from '@heroicons/react/20/solid'; import clsx from 'clsx'; type DropdownTone = 'default' | 'danger'; export type DropdownItem = { id?: string; label: string; href?: string; onClick?: () => void; icon?: React.ReactNode; tone?: DropdownTone; disabled?: boolean; }; export type DropdownSection = { id?: string; items: DropdownItem[]; }; export type DropdownTriggerVariant = 'button' | 'icon'; export interface DropdownProps { /** Button-Label (für Trigger "button") */ label?: string; /** aria-label für Trigger "icon" */ ariaLabel?: string; /** Ausrichtung des Menüs */ align?: 'left' | 'right'; /** Darstellung des Triggers (normaler Button oder nur Icon) */ triggerVariant?: DropdownTriggerVariant; /** Optionaler Header im Dropdown (z.B. "Signed in as …") */ header?: React.ReactNode; /** Sektionen (werden bei >1 Sektion automatisch mit Divider getrennt) */ sections: DropdownSection[]; /** Optional: zusätzliche Klassen für den Trigger-Button */ triggerClassName?: string; /** Optional: zusätzliche Klassen für das MenuItems-Panel */ menuClassName?: string; } /* ───────── interne Helfer ───────── */ const itemBaseClasses = 'block px-4 py-2 text-sm text-gray-700 ' + 'data-focus:bg-gray-100 data-focus:text-gray-900 data-focus:outline-hidden ' + 'dark:text-gray-300 dark:data-focus:bg-white/5 dark:data-focus:text-white'; const itemWithIconClasses = 'group flex items-center gap-x-3 px-4 py-2 text-sm text-gray-700 ' + 'data-focus:bg-gray-100 data-focus:text-gray-900 data-focus:outline-hidden ' + 'dark:text-gray-300 dark:data-focus:bg-white/5 dark:data-focus:text-white'; const iconClasses = 'flex size-5 shrink-0 items-center justify-center text-gray-400 group-data-focus:text-gray-500 ' + 'dark:text-gray-500 dark:group-data-focus:text-white'; const toneClasses: Record = { default: '', danger: 'text-rose-600 data-focus:text-rose-700 dark:text-rose-400 dark:data-focus:text-rose-300', }; function renderItemContent(item: DropdownItem) { const hasIcon = !!item.icon; if (hasIcon) { return ( {item.label} ); } return ( {item.label} ); } /* ───────── Dropdown-Komponente ───────── */ export function Dropdown({ label = 'Options', ariaLabel = 'Open options', align = 'right', triggerVariant = 'button', header, sections, triggerClassName, menuClassName, }: DropdownProps) { const hasDividers = sections.length > 1; const alignmentClasses = align === 'left' ? 'left-0 origin-top-left' : 'right-0 origin-top-right'; const triggerIsButton = triggerVariant === 'button'; return ( {triggerIsButton ? ( <> {label} {/* Optionaler Header (z.B. "Signed in as …") */} {header && (
{header}
)} {/* Sektionen mit/ohne Divider */} {sections.map((section, sectionIndex) => (
{section.items.map((item, itemIndex) => { const key = item.id ?? `${sectionIndex}-${itemIndex}`; const commonProps = { className: itemBaseClasses, }; return ( {item.href ? ( {renderItemContent(item)} ) : ( )} ); })}
))}
); } export default Dropdown;