"use client";
import * as React from "react";
import {
LazySelect,
LazySelectContent,
LazySelectTrigger,
useLazySelectOptions,
type LazyPage,
} from "@/components/ui/lazy-select";
interface User {
id: string;
name: string;
}
const PAGE_SIZE = 20;
const FIRST_NAMES = [
"Ava",
"Liam",
"Noah",
"Mia",
"Zoe",
"Ezra",
"Iris",
"Omar",
"Lena",
"Kai",
];
const LAST_NAMES = [
"Khan",
"Reyes",
"Okafor",
"Park",
"Vance",
"Sato",
"Diallo",
"Costa",
];
const ALL_USERS: User[] = Array.from({ length: 137 }, (_, i) => ({
id: `u-${i + 1}`,
name: `${FIRST_NAMES[i % 10]} ${LAST_NAMES[i % 8]} #${i + 1}`,
}));
const fetchUsers = async ({
query,
page,
}: {
query: string;
page: number;
}): Promise<LazyPage<User>> => {
await new Promise((r) => setTimeout(r, 450));
const filtered = query
? ALL_USERS.filter((u) =>
u.name.toLowerCase().includes(query.toLowerCase()),
)
: ALL_USERS;
const start = page * PAGE_SIZE;
const items = filtered.slice(start, start + PAGE_SIZE);
return { items, hasMore: start + PAGE_SIZE < filtered.length };
};
const mapUser = (u: User) => ({ value: u.id, label: u.name });
export default function LazySelectDemo() {
const [value, setValue] = React.useState<string | undefined>();
const [open, setOpen] = React.useState(false);
// `enabled: open` defers all network work until the dropdown is opened —
// the "lazy" half. `loadMore` appends the next page on scroll — the
// "autocomplete + paginate" half.
const { setQuery, options, loading, loadingMore, hasMore, loadMore } =
useLazySelectOptions(fetchUsers, mapUser, {
enabled: open,
});
return (
<div className="flex w-full max-w-md flex-col gap-2">
<label
htmlFor="lazy-assignee"
className="text-sm font-medium text-foreground"
>
Assignee · lazy-loaded on open, paged on scroll
</label>
<LazySelect
value={value}
onValueChange={setValue}
open={open}
onOpenChange={setOpen}
options={options}
loading={loading}
loadingMore={loadingMore}
hasMore={hasMore}
onSearchChange={setQuery}
onLoadMore={loadMore}
>
<LazySelectTrigger
id="lazy-assignee"
placeholder="Select a teammate…"
/>
<LazySelectContent
searchPlaceholder="Search teammates…"
emptyMessage="No teammates match."
loadingMessage="Fetching teammates…"
loadingMoreMessage="Loading more…"
endMessage="End of list"
/>
</LazySelect>
<p className="text-xs uppercase text-muted-foreground">
value = {value ? `"${value}"` : "-"} · loaded = {options.length}
</p>
</div>
);
}