import type { TextMatchRange } from "@charcuterie/logic" import type { ControlSize } from "@charcuterie/tokens" import type { MouseEvent as ReactMouseEvent, ReactNode, } from "react" import { PANEL_ITEM_SIZE_CLASS } from "../controlStyles.ts" import { UnstyledLink } from "../RouterLink/UnstyledLink.tsx" import { toClassName } from "../toClassName.ts" import type { CommandPaletteItem } from "./commandPaletteSections.ts" export type CommandPaletteOptionProps = { /** The DOM id `aria-activedescendant` points at. */ id: string isActive: boolean item: CommandPaletteItem itemSize: ControlSize /** A click on the row: the link has navigated (or will), so close and run `onSelect`. */ onChoose: () => void /** The pointer moved over the row. */ onPoint: () => void titleRanges: readonly TextMatchRange[] } const ICON_SIZE_CLASS: Record = { lg: "size-7 [&>img]:size-7 [&>svg]:size-5", md: "size-6 [&>img]:size-6 [&>svg]:size-4", sm: "size-5 [&>img]:size-5 [&>svg]:size-4", } /** * The title with each matched range wrapped in a ``. Plain * text in, so nothing the caller typed is ever read as markup. */ const HighlightedTitle = ({ ranges, title, }: { ranges: readonly TextMatchRange[] title: string }): ReactNode => { if (ranges.length === 0) { return title } const pieces: ReactNode[] = [] let cursor = 0 for (const { end, start } of ranges) { if (start > cursor) { pieces.push(title.slice(cursor, start)) } pieces.push( {title.slice(start, end)} , ) cursor = end } if (cursor < title.length) { pieces.push(title.slice(cursor)) } return pieces } /** * One row of the palette's listbox. * * **Focus never comes here.** The input keeps it, and the row the * arrows have reached is announced through the input's * `aria-activedescendant` — the APG combobox pattern `Combobox` uses * too. So the row is `tabIndex={-1}`, and a mouse press is prevented * from moving focus (`onMouseDown`), which keeps the caret in the * input while the click still lands. * * **An `href` item is a real ``.** It goes through * `UnstyledLink`, so an app's injected router link handles a plain * click, and middle-click, ctrl-click and "copy link address" all * work, because it is a link. Enter in the input clicks this same * element, so the keyboard and the mouse take one path. An item * without one is a ` ) }