// The bar's result list: kind headers over rank-ordered rows // (docs/COMMAND-BAR.md, "Structure"). Headers are labels, never rows — the // highlight walks the rows alone, in the order they are rendered. import { useEffect, useRef } from "react"; import { keyCaps } from "../../commands.js"; import { highlightSegments, isActionCandidate, metaText, rowName, type BarGroup, type BarResult, } from "./model.js"; export interface ResultListProps { groups: BarGroup[]; /** Dropped while a sigil is in force or on the action page: one kind needs * no header. */ showHeaders: boolean; /** Index into the flattened rows, in the order they render. */ highlighted: number; onActivate(index: number): void; } function Highlighted({ text, ranges }: { text: string; ranges: BarResult["ranges"] }) { return ( <> {highlightSegments(text, ranges).map((segment, i) => segment.hit ? ( {segment.text} ) : ( {segment.text} ), )} > ); } function Row({ result, index, active, onActivate, }: { result: BarResult; index: number; active: boolean; onActivate(index: number): void; }) { const ref = useRef(null); const candidate = result.candidate; useEffect(() => { if (active) ref.current?.scrollIntoView({ block: "nearest" }); }, [active]); const name = rowName(candidate); const nameMatched = result.field === "name" || result.field === "label" || result.field === "path"; const meta = metaText(result); const dead = candidate.kind === "session" && candidate.session.status === "Dead"; const chord = candidate.kind === "command" && !isActionCandidate(candidate) ? candidate.command.keys[0] : undefined; return ( onActivate(index)} > {nameMatched ? : name} {chord !== undefined ? ( {keyCaps(chord).map((cap, i) => ( {cap} ))} ) : meta ? ( {result.field !== undefined && result.field !== "name" ? ( ) : ( meta )} ) : null} ); } export function ResultList({ groups, showHeaders, highlighted, onActivate }: ResultListProps) { if (groups.length === 0) { return ( No matches ); } let index = 0; return ( {groups.map((group) => ( {showHeaders ? ( {group.title} ) : null} {group.results.map((result) => { const at = index++; return ( ); })} ))} ); }