UNPKG

@kmenu/react

Version:

React adapter for kmenu

293 lines (288 loc) 10.4 kB
import React, { HTMLAttributes, ReactNode, InputHTMLAttributes } from 'react'; /** * Describes an actionable or navigable option in the command menu. */ interface CommandOption$1<T = any> { id?: string; label: string; keywords?: string[]; disabled?: boolean; group?: string; data?: T; children?: CommandOption$1<T>[]; parent?: string; action?: () => void | Promise<void> | CommandOption$1<T>[]; } /** * Breadcrumb segment representing the current submenu path. */ interface Breadcrumb { id: string; label: string; } /** * Complete immutable snapshot of the command system state. */ interface CommandState<T = any> { open: boolean; input: string; activeId?: string; activeIndex: number; filtered: CommandOption$1<T>[]; options: CommandOption$1<T>[]; groups: Map<string, CommandOption$1<T>[]>; menuStack: string[]; currentLevel: number; breadcrumbs: Breadcrumb[]; allOptions: CommandOption$1<T>[]; currentOptions: CommandOption$1<T>[]; } /** * Discriminated union of events emitted by the command core. */ type CommandEvent<T = any> = { type: "open"; } | { type: "close"; } | { type: "change"; input: string; } | { type: "select"; option: CommandOption$1<T>; } | { type: "navigate"; activeId: string | undefined; activeIndex: number; } | { type: "submenu"; option: CommandOption$1<T>; level: number; } | { type: "back"; level: number; }; /** Handler for a specific command event type. */ type EventHandler<T = any> = (event: CommandEvent<T>) => void; /** Function that filters `options` using a text `query`. */ type FilterFunction<T = any> = (options: CommandOption$1<T>[], query: string) => CommandOption$1<T>[]; /** Callback to unsubscribe an event handler. */ type Unsubscribe = () => void; /** * Configuration for the `CommandCore` behavior and callbacks. */ interface CommandCoreConfig<T = any> { filter?: FilterFunction<T>; onOpen?: () => void; onClose?: () => void; onChange?: (input: string) => void; onSelect?: (option: CommandOption$1<T>) => void; } /** * Headless command menu engine with filtering, navigation, and submenu support. * Provides ARIA props and emits events for UI integration. */ declare class CommandCore<T = any> { private stateMachine; private state; private listeners; private filter; private keydownHandler?; private globalKeyHandler?; private inputElement?; private listElement?; private optionElements; private destroyed; private lastScrollTime; constructor(config?: CommandCoreConfig<T>); private setupStateMachine; private setupGlobalKeyboardShortcut; private defaultFilter; /** Open the command menu if not destroyed. */ open(): void; /** Close the command menu if not destroyed. */ close(): void; /** Toggle the command menu open/closed state. */ toggle(): void; /** * Set the input query and update the filtered options. * Emits a `change` event with the latest input. */ setInput(value: string): void; /** * Replace the registered options and rebuild all derived state. */ registerOptions(options: CommandOption$1<T>[]): void; private processOptionsWithIds; private ensureUniqueId; private rebuildGroups; private reorderByGroups; private flattenOptions; /** * Set the active option by filtered index. Optionally provide the * navigation direction to optimize scroll behavior. */ setActiveByIndex(index: number, direction?: "up" | "down"): void; private scrollActiveIntoView; /** Set the active option by option id. */ setActiveById(id: string): void; /** Move the active selection to the previous enabled option. */ navigateUp(): void; /** Move the active selection to the next enabled option. */ navigateDown(): void; /** Subscribe to a command event. Returns an unsubscribe function. */ on(event: CommandEvent<T>["type"], handler: EventHandler<T>): Unsubscribe; /** Get a shallow-copied snapshot of the current state. */ getState(): CommandState<T>; private emit; private updateFiltered; private selectFirstAvailableOption; /** Enter a submenu using the given parent `option` with children. */ enterSubmenu(option: CommandOption$1<T>): void; /** Go back one submenu level. Returns `false` if already at root. */ goBack(): boolean; private findOptionById; private updateAriaActiveDescendant; /** * Return ARIA attributes for the combobox container element. */ getComboboxProps(): { role: "combobox"; "aria-expanded": boolean; "aria-haspopup": "listbox"; "aria-controls": string; }; /** * Return ARIA attributes and event handlers for the input element. */ getInputProps(): { ref: (el: HTMLInputElement | null) => void; role: "combobox"; "aria-autocomplete": "list"; "aria-expanded": boolean; "aria-controls": string; "aria-activedescendant": string | undefined; value: string; onInput: (e: Event) => void; onKeyDown: (e: KeyboardEvent) => void; }; /** Return ARIA attributes for the listbox element. */ getListboxProps(): { ref: (el: HTMLElement | null) => void; id: string; role: "listbox"; "aria-label": string; tabIndex: number; }; /** * Return ARIA attributes and event handlers for an option element by id. */ getOptionProps(id: string): { ref: (el: HTMLElement | null) => void; id: string; role: "option"; "aria-selected": boolean; "aria-disabled": boolean | undefined; tabIndex: number; onClick: () => void; onMouseEnter: () => void; }; private handleKeyDown; /** Select the active option or enter its submenu if it has children. */ selectActive(): void; /** Cleanup event listeners and internal references. Safe to call multiple times. */ destroy(): void; } /** * Root command component props. */ interface CommandProps<T = any> extends Omit<HTMLAttributes<HTMLDivElement>, "onChange" | "onSelect"> { children?: ReactNode; value?: string; onValueChange?: (value: string) => void; open?: boolean; onOpenChange?: (open: boolean) => void; options?: CommandOption$1<T>[]; filter?: FilterFunction<T>; onSelect?: (option: CommandOption$1<T>) => void; shouldFilter?: boolean; } /** * Ref handle exposed by `Command`. */ interface CommandRef<T = any> { command: CommandCore<T> | null; } /** * Root component that wires the headless core to React and provides context. */ declare const Command: React.ForwardRefExoticComponent<CommandProps<any> & React.RefAttributes<CommandRef<any>>>; /** Props for the command input element. */ interface CommandInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange"> { value?: string; onValueChange?: (value: string) => void; } declare const CommandInput: React.ForwardRefExoticComponent<CommandInputProps & React.RefAttributes<HTMLInputElement>>; /** Props for the listbox container. */ interface CommandListProps extends HTMLAttributes<HTMLDivElement> { children?: ReactNode; indicatorOffsetY?: number; } declare const CommandList: React.ForwardRefExoticComponent<CommandListProps & React.RefAttributes<HTMLDivElement>>; /** Props for an individual command option element. */ interface CommandOptionProps<T = any> extends HTMLAttributes<HTMLDivElement> { value: CommandOption$1<T>; disabled?: boolean; children?: ReactNode; } declare const CommandOption: React.ForwardRefExoticComponent<CommandOptionProps<any> & React.RefAttributes<HTMLDivElement>>; /** Props for a visual grouping of options. */ interface CommandGroupProps extends HTMLAttributes<HTMLDivElement> { heading?: ReactNode; children?: ReactNode; } declare const CommandGroup: React.ForwardRefExoticComponent<CommandGroupProps & React.RefAttributes<HTMLDivElement>>; /** Props for the empty state container. */ interface CommandEmptyProps extends HTMLAttributes<HTMLDivElement> { children?: ReactNode; } declare const CommandEmpty: React.ForwardRefExoticComponent<CommandEmptyProps & React.RefAttributes<HTMLDivElement>>; /** Props for the breadcrumb navigation container. */ interface CommandBreadcrumbsProps extends HTMLAttributes<HTMLDivElement> { children?: ReactNode; } declare const CommandBreadcrumbs: React.ForwardRefExoticComponent<CommandBreadcrumbsProps & React.RefAttributes<HTMLDivElement>>; /** Props for the loading state container. */ interface CommandLoadingProps extends HTMLAttributes<HTMLDivElement> { children?: ReactNode; } declare const CommandLoading: React.ForwardRefExoticComponent<CommandLoadingProps & React.RefAttributes<HTMLDivElement>>; /** Props for a separator between list sections. */ interface CommandSeparatorProps extends HTMLAttributes<HTMLDivElement> { } declare const CommandSeparator: React.ForwardRefExoticComponent<CommandSeparatorProps & React.RefAttributes<HTMLDivElement>>; /** * Context value shared by command components. */ interface CommandContextValue<T = any> { command: CommandCore<T> | null; state: { open: boolean; input: string; activeId?: string; activeIndex: number; filtered: CommandOption$1<T>[]; options: CommandOption$1<T>[]; menuStack: string[]; currentLevel: number; breadcrumbs: { id: string; label: string; }[]; }; } /** * Hook to access the command context. Throws if used outside of `Command`. */ declare function useCommand<T = any>(): CommandContextValue<T>; export { Command, CommandBreadcrumbs, type CommandBreadcrumbsProps, type CommandContextValue, CommandEmpty, type CommandEmptyProps, type CommandEvent as CommandEventType, CommandGroup, type CommandGroupProps, CommandInput, type CommandInputProps, CommandList, type CommandListProps, CommandLoading, type CommandLoadingProps, CommandOption, type CommandOptionProps, type CommandOption$1 as CommandOptionType, type CommandProps, type CommandRef, CommandSeparator, type CommandSeparatorProps, type CommandState as CommandStateType, type FilterFunction as FilterFunctionType, useCommand };