import { TNode, Value, Renderable } from '@tempots/dom'; import { Placement } from '@tempots/ui'; import { FlyoutTrigger, FlyoutTriggerFunction } from './flyout'; /** * Trigger mode for the {@link Menu} component. * Alias for {@link FlyoutTrigger} -- controls how the menu is shown (e.g., `'click'`, `'hover'`). */ export type MenuTrigger = FlyoutTrigger; /** * Configuration options for the {@link Menu} component. */ export interface MenuOptions { /** * Factory function returning the array of menu items (created via {@link MenuItem}, * {@link MenuSeparator}, or custom nodes with `role="menuitem"`). */ items: () => TNode[]; /** * Placement of the menu relative to the trigger element. * @default 'bottom-start' */ placement?: Value; /** * Delay in milliseconds before showing the menu after trigger activation. * @default 0 */ showDelay?: Value; /** * Delay in milliseconds before hiding the menu after trigger deactivation. * @default 100 */ hideDelay?: Value; /** * Offset in pixels from the main axis. * @default 4 */ mainAxisOffset?: Value; /** * Offset in pixels from the cross axis. * @default 0 */ crossAxisOffset?: Value; /** * How the menu is triggered to show and hide. * Accepts a built-in {@link MenuTrigger} string or a custom {@link FlyoutTriggerFunction}. * @default 'click' */ showOn?: Value | FlyoutTriggerFunction; /** * Whether the menu can be closed with the Escape key or by clicking outside. * @default true */ closable?: Value; /** * Callback invoked when the menu is closed (via Escape, click outside, or after item activation). */ onClose?: () => void; /** * Callback invoked when a menu item with a `key` is activated (via click or keyboard). * Receives the menu item's `key` as an argument. */ onAction?: (key: string) => void; /** * Accessible label for the menu container (`aria-label`). */ ariaLabel?: Value; /** * ID of the element that labels the menu (`aria-labelledby`). */ ariaLabelledBy?: Value; } /** * Configuration options for the {@link MenuItem} component. */ export interface MenuItemOptions { /** * Unique identifier for the menu item. Used by the `onAction` callback in {@link MenuOptions}. * When not provided, a unique session ID is generated automatically. */ key?: Value; /** * Primary text or node content displayed as the menu item label. */ content: TNode; /** * Optional content displayed before the menu item label, * typically used for icons or selection indicators. */ before?: TNode; /** * Optional content displayed after the menu item label, * typically used for keyboard shortcuts or badges. */ after?: TNode; /** * Whether the menu item is disabled. Disabled items are skipped by keyboard * navigation and cannot be activated. * @default false */ disabled?: Value; /** * Callback invoked when the menu item is clicked or activated via keyboard. */ onClick?: () => void; /** * Accessible label for the menu item (`aria-label`). */ ariaLabel?: Value; /** * Factory function returning submenu items for nested menus. * When provided, the menu item gains a hover-triggered submenu. */ submenu?: () => TNode[]; /** * Placement of the submenu relative to the parent menu item. * @default 'right-start' */ submenuPlacement?: Value; } /** * Configuration options for the {@link MenuSeparator} component. */ export interface MenuSeparatorOptions { /** * Optional label displayed alongside the separator line, * useful for grouping related menu items under a heading. */ label?: TNode; } /** * Dropdown menu component that provides a list of actions or options. * * Built on top of the {@link Flyout} component for positioning and overlay behavior. * Follows the WAI-ARIA menu pattern with full keyboard navigation support: * - Arrow Up/Down to navigate items * - Home/End to jump to first/last item * - Enter/Space to activate the focused item * - Escape to close * - Arrow Right to open submenus, Arrow Left to close them * * Focus is restored to the trigger element when the menu closes. * * @param options - Configuration options for menu behavior, positioning, and accessibility * @returns A renderable node to be placed inside the trigger element * * @example * ```typescript * Button( * { variant: 'outline' }, * 'Actions', * Menu({ * items: () => [ * MenuItem({ content: 'Edit', key: 'edit', onClick: () => console.log('edit') }), * MenuItem({ content: 'Duplicate', key: 'dup' }), * MenuSeparator(), * MenuItem({ content: 'Delete', key: 'delete', disabled: true }), * ], * onAction: (key) => console.log('Selected:', key), * placement: 'bottom-start', * }) * ) * ``` */ export declare function Menu(options: MenuOptions): Renderable; /** * Individual menu item component with optional start/end content and submenu support. * * Renders a `role="menuitem"` element with proper ARIA attributes for disabled state, * selection state, and submenu expansion. Disabled items prevent click propagation * and are skipped by keyboard navigation. * * @param options - Configuration options for the menu item's content, behavior, and accessibility * @returns A renderable node * * @example * ```typescript * MenuItem({ * key: 'edit', * content: 'Edit', * before: Icon({ icon: 'edit', size: 'sm' }), * after: html.span(attr.class('shortcut'), 'Ctrl+E'), * onClick: () => console.log('edit clicked'), * }) * ``` * * @example * ```typescript * // Menu item with a submenu * MenuItem({ * content: 'Export as...', * submenu: () => [ * MenuItem({ content: 'PDF', onClick: () => exportAs('pdf') }), * MenuItem({ content: 'CSV', onClick: () => exportAs('csv') }), * ], * }) * ``` */ export declare function MenuItem(options: MenuItemOptions): Renderable; /** * Visual separator for grouping related menu items within a {@link Menu}. * * Renders a `role="separator"` element with horizontal orientation. An optional label * can be provided to act as a section heading for the items below. * * @param options - Optional configuration with a label for the separator * @returns A renderable node * * @example * ```typescript * Menu({ * items: () => [ * MenuItem({ content: 'Cut' }), * MenuItem({ content: 'Copy' }), * MenuSeparator(), * MenuItem({ content: 'Paste' }), * ] * }) * ``` * * @example * ```typescript * // With a labeled section * MenuSeparator({ label: 'Advanced' }) * ``` */ export declare function MenuSeparator(options?: MenuSeparatorOptions): Renderable;