import { type ReactNode, type Ref } from 'react'; import { type MenuItemProps } from './Menu'; /** * The list a text editor opens as you type — `/` for commands, `@` for people, * `[` for a reference. UIG-31: Peek's four type-ahead menus, made one part. * * **Why it is not a `Combobox` or a `Menu`.** Base UI's lists need a text field * of their own, and the text being completed is an editor, not an ``. * So focus never leaves the text: the editor hands its arrow keys and Enter to * this list through `handle`, and the list tells the editor which row is * active (`aria-activedescendant`), the pattern Base UI's `Combobox` builds. * If Base UI ever adds a list that can be driven from outside its own field, * this part moves onto it. * * **What it owns**, all of it written once here instead of in every menu: * * - the box: the package `Popover`, anchored to the caret, above it and below * when there is no room, clamped to the window, scrolling in the package's * `ScrollArea` past `maxHeight`; * - the highlighted row: ↑ and ↓ (wrapping at the ends), Enter to choose, the * pointer moving the highlight, a press choosing without taking focus from * the text, the highlight back on the first row whenever the list changes, * and the highlighted row scrolled into view; * - what a screen reader is told: the list is a `listbox`, each row an * `option`, and the editor says it completes into this list and which row * is active — all removed again when the list closes. * * What stays with the app: which rows exist, what choosing one does, and what * each row shows (its label, face, keys, hint) — `row` returns the package * `MenuItem`'s own props. With nothing to show there is no box at all. */ export interface SuggestionMenuRow { label: string; description?: string; /** After the label, muted — `MenuItem`'s. */ aside?: string; leading?: ReactNode; /** What you would type — drawn as keys at the right, e.g. `#` for a heading. */ shortcut?: string; /** Drawn at the right while the row is highlighted, e.g. ``. */ hint?: ReactNode; /** `tall` for a row with a face and a second line — `MenuItem`'s own sizes. */ size?: MenuItemProps['size']; } export interface SuggestionMenuSection { /** The section's heading. Without one, the rows stand alone. */ label?: string; items: readonly T[]; /** On the heading row, as `MenuSection`'s. */ className?: string; } /** What the editor calls while the list is open. */ export interface SuggestionMenuHandle { /** ↑, ↓ and Enter. `true` when the list used the key, so the editor must not. */ onKeyDown: (event: KeyboardEvent) => boolean; } export interface SuggestionMenuProps { /** The caret's rect, from the editor. `null` closes the box. */ rect: DOMRect | null; /** The editor's own element — told which row is active. Absent in a story. */ editorElement?: HTMLElement | null; /** Names the list — "Commands", "People". */ ariaLabel: string; /** The box's width, e.g. `w-[300px]`. */ width: string; /** The cap before the rows scroll, e.g. `max-h-[400px]`. */ maxHeight: string; sections: readonly SuggestionMenuSection[]; /** A stable key for a row. */ itemKey: (item: T) => string; /** What a row shows. */ row: (item: T) => SuggestionMenuRow; onSelect: (item: T) => void; handle?: Ref; } export declare function SuggestionMenu({ rect, editorElement, ariaLabel, width, maxHeight, sections, itemKey, row, onSelect, handle }: SuggestionMenuProps): import("react").JSX.Element | null; //# sourceMappingURL=SuggestionMenu.d.ts.map