UNPKG

instantdb-react-ui

Version:

Customizable react components for InstantDB (forms/lists/etc.)

124 lines (123 loc) 4.99 kB
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import { useEffect, useRef, useState } from 'react'; /** Standard list that loads all data at once */ const NormalList = (props) => { const { entity, render, skeleton, query, noResults, db } = props; const constructedQuery = query || { [entity]: {} }; const { isLoading, error, data: rawData } = db.useQuery(constructedQuery); // TODO: Bug in Instant, isLoading doesn't change to false when the query changes // Extract the array from the entity property const data = rawData ? rawData[entity] : null; if (isLoading) return skeleton || null; if (error || !data) return noResults || null; if (data.length === 0) return noResults || null; return (_jsx(_Fragment, { children: data.map(item => (_jsx("div", { children: render(item, item.id) }, item.id))) })); }; /** Infinite list that loads more data when you scroll to the end of the list */ const InfiniteList = (props) => { const { entity, render, skeleton, query, noResults, pageSize = 10, db } = props; const [limit, setLimit] = useState(pageSize); const loadMoreRef = useRef(null); const constructedQuery = { [entity]: { ...(query?.[entity] || {}), $: { ...(query?.[entity]?.$ || {}), limit, }, }, }; const { isLoading, error, data: rawData } = db.useQuery(constructedQuery); const items = rawData ? rawData[entity] : []; const hasMore = items.length === limit; useEffect(() => { const increaseLimit = () => setLimit(prev => prev + pageSize); const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting && hasMore) { increaseLimit(); } }, { threshold: 0.1 }); const currentRef = loadMoreRef.current; if (currentRef) { observer.observe(currentRef); // Additional check to handle cases where intersection observer might miss const checkVisibility = () => { const entry = observer.takeRecords()[0]; if (entry?.isIntersecting && hasMore) { increaseLimit(); } }; const timer = setInterval(checkVisibility, 100); return () => { observer.disconnect(); clearInterval(timer); }; } return () => observer.disconnect(); }, [hasMore, pageSize]); if (isLoading && items.length === 0) return skeleton || null; if (error) return noResults || null; if (items.length === 0) return noResults || null; return (_jsxs(_Fragment, { children: [items.map(item => (_jsx("div", { children: render(item, item.id) }, item.id))), isLoading && items.length > 0 && _jsx("div", { children: "Loading more..." }), _jsx("div", { ref: loadMoreRef, style: { height: '10px' } })] })); }; /** Hook to get pagination state for a paginated list */ export const useIDBPagination = (props) => { const db = props.db; const pageSize = props.pageSize || 10; const [page, setPage] = useState(1); // Get all items to count total const allItemsQuery = props.query || { [props.model]: {} }; const { data: allData } = db.useQuery(allItemsQuery); const totalItems = allData?.[props.model]?.length || 0; const totalPages = Math.ceil(totalItems / pageSize); // Get paginated items const itemQuery = { [props.model]: { ...(props.query?.[props.model] || {}), $: { ...(props.query?.[props.model]?.$ || {}), limit: pageSize, offset: (page - 1) * pageSize, }, }, }; const { data: itemData } = db.useQuery(itemQuery); const items = itemData?.[props.model] || []; const goToPage = (newPage) => { setPage(Math.max(1, Math.min(newPage, totalPages))); }; return { ...props, items: items, page, totalPages, totalItems, goToPage, }; }; /** Paginated list */ const PaginatedList = (props) => { const { render, skeleton, noResults, pagination } = props; if (!pagination || !pagination.items) return skeleton || null; if (pagination.items.length === 0) return noResults || null; return (_jsx(_Fragment, { children: pagination.items.map(item => (_jsx("div", { children: render(item, item.id) }, item.id))) })); }; /** instantdb-react-ui list component, with support for normal, infinite, and paginated modes */ export const IDBList = (props) => { const { mode } = props; if (mode === 'infinite') { return _jsx(InfiniteList, { ...props }); } else if (mode === 'paginated') { return _jsx(PaginatedList, { ...props }); } // Default to normal list return _jsx(NormalList, { ...props }); };