import HighlightMatch from "@/components/ui/highlight-match";
import { useState } from "react";
const PEOPLE: [string, string][] = [
["Zoë Laurent", "Head of Design · Montréal"],
["José Álvarez", "Staff Engineer · Sevilla"],
["Søren Østergaard", "Finance Lead · København"],
["Anna Nováková", "Support · Brno"],
["Joanna Price", "Account Executive · New York"],
["Chloé Dubois", "Product Manager · Lyon"],
["Renée O’Connor", "Growth · Dublin"],
];
function initials(name: string) {
return name
.split(" ")
.map((part) => part[0])
.join("")
.slice(0, 2)
.toUpperCase();
}
function fold(value: string) {
return value.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase();
}
function highlightMatchFilter(text: string, words: string[]) {
const haystack = fold(text);
return words.every((word) => haystack.includes(fold(word)));
}
export default function HighlightMatchDemo() {
const [query, setQuery] = useState("zoe montreal");
const [wordStart, setWordStart] = useState<"anywhere" | "start">("anywhere");
const words = query.trim().split(/\s+/).filter(Boolean);
const results = PEOPLE.filter(
([name, role]) =>
words.length === 0 || highlightMatchFilter(`${name} ${role}`, words),
);
return (
<div className="flex w-full items-center justify-center bg-background p-8">
<div className="flex w-full max-w-md flex-col gap-3">
<label className="flex flex-col gap-1 text-sm font-medium text-foreground">
Search people
<span className="flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground shadow-sm">
<span aria-hidden>🔍</span>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder='Try "an", "cafe", "("'
className="w-full bg-transparent outline-none placeholder:text-muted-foreground"
/>
</span>
</label>
<div className="flex self-start overflow-hidden rounded-md border border-border text-xs font-medium">
{(["anywhere", "start"] as const).map((value) => (
<button
key={value}
type="button"
onClick={() => setWordStart(value)}
className={
"px-3 py-1.5 transition-colors " +
(wordStart === value
? "bg-foreground text-background"
: "bg-card text-muted-foreground hover:bg-accent")
}
>
{value === "anywhere" ? "Anywhere" : "Word start"}
</button>
))}
</div>
<ul
aria-label="People"
className="flex flex-col divide-y divide-border rounded-xl border border-border bg-card"
>
{results.map(([name, role]) => (
<li key={name} className="flex items-center gap-3 px-4 py-2.5">
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-semibold text-muted-foreground">
{initials(name)}
</span>
<div className="flex min-w-0 flex-col gap-0.5">
<span className="text-[13px] font-bold text-foreground">
<HighlightMatch
text={name}
query={query}
wordStart={wordStart === "start"}
/>
</span>
<span className="text-[12px] font-medium text-muted-foreground">
<HighlightMatch
text={role}
query={query}
wordStart={wordStart === "start"}
/>
</span>
</div>
</li>
))}
{results.length === 0 && (
<li className="px-4 py-6 text-center text-sm text-muted-foreground">
No matches
</li>
)}
</ul>
</div>
</div>
);
}