Merge pull request 'Scalable people directory' (#13) from people-directory into main
build-frontend / build (push) Successful in 1m20s
build-frontend / build (push) Successful in 1m20s
This commit was merged in pull request #13.
This commit is contained in:
@@ -268,6 +268,7 @@ export default function FamilyViewPage() {
|
||||
const matches = search
|
||||
? sorted.filter((p) => (p.primary_name ?? "").toLowerCase().includes(search.toLowerCase()))
|
||||
: sorted;
|
||||
const shown = matches.slice(0, 200); // cap DOM nodes; refine search to narrow
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
@@ -325,32 +326,44 @@ export default function FamilyViewPage() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Searchable index of everyone in the tree */}
|
||||
{/* Scrollable, searchable people directory (scales to large trees) */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h2 className="font-serif text-base font-semibold">All people ({people.length})</h2>
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<h2 className="font-serif text-base font-semibold">People ({people.length})</h2>
|
||||
<Input
|
||||
className="w-56"
|
||||
placeholder="Search…"
|
||||
className="w-64"
|
||||
placeholder="Search by name…"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{matches.map((p) => (
|
||||
<button
|
||||
key={p.id}
|
||||
onClick={() => setFocusId(p.id)}
|
||||
className={`rounded-full border px-3 py-1 text-sm transition-colors ${
|
||||
p.id === focusId
|
||||
? "border-bronze bg-bronze/[0.08] text-bronze"
|
||||
: "border-[var(--border)] hover:border-bronze/60"
|
||||
}`}
|
||||
>
|
||||
{p.primary_name ?? "Unnamed"}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<Card className="overflow-hidden">
|
||||
<div className="max-h-96 overflow-y-auto">
|
||||
{shown.length === 0 ? (
|
||||
<div className="px-4 py-6 text-sm text-[var(--muted)]">No matches.</div>
|
||||
) : (
|
||||
shown.map((p, i) => (
|
||||
<button
|
||||
key={p.id}
|
||||
onClick={() => setFocusId(p.id)}
|
||||
className={`flex w-full items-center justify-between gap-3 px-4 py-2.5 text-left text-sm transition-colors ${
|
||||
i > 0 ? "border-t border-[var(--border)]" : ""
|
||||
} ${p.id === focusId ? "bg-bronze/[0.08]" : "hover:bg-bronze/[0.05]"}`}
|
||||
>
|
||||
<span className="truncate font-medium">{p.primary_name ?? "Unnamed"}</span>
|
||||
<span className="shrink-0 text-xs text-[var(--muted)]">
|
||||
{years.get(p.id) ?? ""}
|
||||
</span>
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
{matches.length > shown.length && (
|
||||
<div className="border-t border-[var(--border)] bg-[var(--surface)] px-4 py-2 text-xs text-[var(--muted)]">
|
||||
Showing {shown.length} of {matches.length} — refine your search to narrow.
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user