import React, { useState, useEffect } from "react"; import { Squares2X2Icon, Bars3Icon } from "@heroicons/react/24/solid"; import type { CursorObj } from "../../api"; export type ListViewProps = { fetchItems: (cursor: string, limit: number) => Promise<{ items: T[]; cursor: CursorObj}>; renderItem: (item: T, layout: "square" | "horizontal") => React.ReactNode; pageSize?: number; searchPlaceholder?: string; setSearch: any; }; export function ListView({ fetchItems, renderItem, pageSize = 20, searchPlaceholder = "Search...", }: ListViewProps) { const [items, setItems] = useState([]); const [cursorObj, setCursorObj] = useState(undefined); const [loading, setLoading] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [search, setSearch] = useState(""); const [layout, setLayout] = useState<"square" | "horizontal">("horizontal"); const [error, setError] = useState(null); const loadItems = async (reset: boolean = false) => { try { if (reset) { setLoading(true); setCursorObj(undefined); } else { setLoadingMore(true); } const cursorStr = cursorObj ? btoa(JSON.stringify(cursorObj)) : "" console.log("encoded cursor: " + cursorStr) const result = await fetchItems(cursorStr, pageSize); if (reset) setItems(result.items); else setItems(prev => [...prev, ...result.items]); setCursorObj(result.cursor); setError(null); } catch (err: any) { console.error(err); setError("Failed to fetch items."); } finally { setLoading(false); setLoadingMore(false); } }; useEffect(() => { loadItems(true); }, [search]); return (
setSearch(e.target.value)} className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-black" />
{error &&
{error}
}
{items.map(item => renderItem(item, layout))}
{cursorObj && (
)} {loading &&
Loading...
}
); }