mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
* chore(root): menu and select components in progress * chore(root): merged with main * feat(menu): stories created * feat(dropdown): menu migrated to menu component * feat(select): base implementation in progress * feat(select): listbox implemented * feat(select): scroll overflow and auto scroll added * feat(select): colors & variants improved, inside placement complete, outside pending * feat(scroll-shadow): new component in progress * feat(select): scroll shadow integrated * fix(select): popover implementation, more scroll shadow stories added * feat(listbox): custom styles example added to stories * chore(listbox): overflow hidden removed * feat(select): variants and styles completed * chore(select): description story added * feat(select): more stories added * fix(select): dynamic collections inference, styles improved, more stories added * fix(select): auto scroll to selected item, scroll padding added * chore(select): scroll behavior changed to nearest * feat(select): custom item story added * fix(select): autoscroll fixed * feat(select): multi select support added * feat(select): more examples added, clean-package modified to remove dev packages * chore(modal): useImperativeHandle removed * feat(select): render value story improved * feat(docs): listbox & scroll shadow docs done * feat(docs): select documentation in progress * fix: select aria label (#1425) * feat(docs): more examples added * feat(docs): select multiple added, storybook favicon changed * fix: added value to aria label of select (#1431) * feat(select): more examples added, storybook favicon changed * fix(popover): dialog role removed * feat(select): api added, async loading exampled added * fix: fixed list box on click not working with sr click (#1439) * feat(select): async items loading support added and documented * feat(root): input styles updated to be as the select ones * chore(root): listbox and scroll shadow readme updated * feat(select): ts examples added, banner updated * fix(popover): voiceover closes when focusing an item fixed * chore(select): focus scope removed * fix(popover): free solo popover added to use without a trigger * feat(select): blog post added * chore(docs): search meta generated, multi controlled onchange example fixed * chore(root): changeset added --------- Co-authored-by: Jakob Guddas <github@jguddas.de>
114 lines
2.8 KiB
TypeScript
114 lines
2.8 KiB
TypeScript
/* eslint-disable no-console */
|
|
"use client";
|
|
|
|
import {Select, SelectItem} from "@nextui-org/react";
|
|
import {useEffect, useState} from "react";
|
|
import {useInfiniteScroll} from "@nextui-org/use-infinite-scroll";
|
|
|
|
type Pokemon = {
|
|
name: string;
|
|
url: string;
|
|
};
|
|
|
|
type UsePokemonListProps = {
|
|
/** Delay to wait before fetching more items */
|
|
fetchDelay?: number;
|
|
};
|
|
|
|
function usePokemonList({fetchDelay = 0}: UsePokemonListProps = {}) {
|
|
const [items, setItems] = useState<Pokemon[]>([]);
|
|
const [hasMore, setHasMore] = useState(true);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [offset, setOffset] = useState(0);
|
|
const limit = 10; // Number of items per page, adjust as necessary
|
|
|
|
const loadPokemon = async (currentOffset: number) => {
|
|
const controller = new AbortController();
|
|
const {signal} = controller;
|
|
|
|
try {
|
|
setIsLoading(true);
|
|
|
|
if (offset > 0) {
|
|
// Delay to simulate network latency
|
|
await new Promise((resolve) => setTimeout(resolve, fetchDelay));
|
|
}
|
|
|
|
let res = await fetch(
|
|
`https://pokeapi.co/api/v2/pokemon?offset=${currentOffset}&limit=${limit}`,
|
|
{signal},
|
|
);
|
|
|
|
if (!res.ok) {
|
|
throw new Error("Network response was not ok");
|
|
}
|
|
|
|
let json = await res.json();
|
|
|
|
setHasMore(json.next !== null);
|
|
// Append new results to existing ones
|
|
setItems((prevItems) => [...prevItems, ...json.results]);
|
|
} catch (error) {
|
|
// @ts-ignore
|
|
if (error.name === "AbortError") {
|
|
console.log("Fetch aborted");
|
|
} else {
|
|
console.error("There was an error with the fetch operation:", error);
|
|
}
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
loadPokemon(offset);
|
|
}, []);
|
|
|
|
const onLoadMore = () => {
|
|
const newOffset = offset + limit;
|
|
|
|
setOffset(newOffset);
|
|
loadPokemon(newOffset);
|
|
};
|
|
|
|
return {
|
|
items,
|
|
hasMore,
|
|
isLoading,
|
|
onLoadMore,
|
|
};
|
|
}
|
|
|
|
export default function Page() {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const {items, hasMore, isLoading, onLoadMore} = usePokemonList({fetchDelay: 1500});
|
|
|
|
const [, scrollerRef] = useInfiniteScroll({
|
|
hasMore,
|
|
isEnabled: isOpen,
|
|
shouldUseLoader: false, // We don't want to show the loader at the bottom of the list
|
|
onLoadMore,
|
|
});
|
|
|
|
return (
|
|
<div className="p-6">
|
|
<Select
|
|
className="max-w-xs"
|
|
isLoading={isLoading}
|
|
items={items}
|
|
label="Pick a Pokemon"
|
|
placeholder="Select a Pokemon"
|
|
scrollRef={scrollerRef}
|
|
selectionMode="single"
|
|
onOpenChange={setIsOpen}
|
|
>
|
|
{(item) => (
|
|
<SelectItem key={item.name} className="capitalize">
|
|
{item.name}
|
|
</SelectItem>
|
|
)}
|
|
</Select>
|
|
</div>
|
|
);
|
|
}
|