From 6ae13debc74f38b69d974204182ce09c531c60f3 Mon Sep 17 00:00:00 2001 From: Mike Decker Date: Wed, 3 Jul 2024 14:37:04 -0700 Subject: [PATCH] Improved paged list loader --- package.json | 14 +- src/components/elements/paged-list.tsx | 90 +++++---- .../stanford-lists/list-paragraph.tsx | 32 +-- src/lib/hooks/usePagination.tsx | 21 +- yarn.lock | 186 ++++++++++-------- 5 files changed, 193 insertions(+), 150 deletions(-) diff --git a/package.json b/package.json index c0557a6c..facddd2f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@formkit/auto-animate": "^0.8.2", "@heroicons/react": "^2.1.4", "@js-temporal/polyfill": "^0.4.4", - "@mui/base": "^5.0.0-dev.20240529-082515-213b5e33ab", + "@mui/base": "5.0.0-beta.40", "@next/third-parties": "^14.2.4", "@tailwindcss/container-queries": "^0.1.1", "@types/node": "^20.14.9", @@ -38,19 +38,19 @@ "html-react-parser": "^5.1.10", "next": "^14.2.4", "next-drupal": "^1.6.0", - "postcss": "^8.4.38", - "qs": "^6.12.1", + "postcss": "^8.4.39", + "qs": "^6.12.2", "react": "^18.3.1", "react-dom": "^18.3.1", "react-focus-lock": "^2.12.1", - "react-instantsearch": "^7.11.4", - "react-instantsearch-nextjs": "^0.3.4", + "react-instantsearch": "^7.12.0", + "react-instantsearch-nextjs": "^0.3.5", "react-slick": "^0.30.2", "react-tiny-oembed": "^1.1.0", "sharp": "^0.33.4", "tailwind-merge": "^2.3.0", "tailwindcss": "^3.4.4", - "typescript": "^5.5.2", + "typescript": "^5.5.3", "usehooks-ts": "^3.1.0" }, "devDependencies": { @@ -58,7 +58,7 @@ "@graphql-codegen/cli": "^5.0.2", "@graphql-codegen/import-types-preset": "^3.0.0", "@graphql-codegen/typescript-graphql-request": "^6.2.0", - "@graphql-codegen/typescript-operations": "^4.2.1", + "@graphql-codegen/typescript-operations": "^4.2.3", "@next/bundle-analyzer": "^14.2.4", "@storybook/addon-essentials": "^8.1.11", "@storybook/addon-interactions": "^8.1.11", diff --git a/src/components/elements/paged-list.tsx b/src/components/elements/paged-list.tsx index 85ec7a38..281345c5 100644 --- a/src/components/elements/paged-list.tsx +++ b/src/components/elements/paged-list.tsx @@ -1,11 +1,12 @@ "use client" -import {useLayoutEffect, useRef, HtmlHTMLAttributes, useEffect, useId, JSX, useState} from "react" +import {useLayoutEffect, useRef, HtmlHTMLAttributes, useEffect, JSX, useState, useCallback} from "react" import {useAutoAnimate} from "@formkit/auto-animate/react" import {useBoolean, useCounter} from "usehooks-ts" import {useRouter, useSearchParams} from "next/navigation" import usePagination from "@lib/hooks/usePagination" import useFocusOnRender from "@lib/hooks/useFocusOnRender" +import {ArrowLongLeftIcon, ArrowLongRightIcon} from "@heroicons/react/20/solid" type Props = HtmlHTMLAttributes & { /** @@ -35,29 +36,35 @@ type Props = HtmlHTMLAttributes & { } const PagedList = ({children, ulProps, liProps, pageKey = "page", totalPages, pagerSiblingCount = 2, loadPage, ...props}: Props) => { - const id = useId() + const ref = useRef(false) const [items, setItems] = useState(Array.isArray(children) ? children : [children]) + const router = useRouter() const searchParams = useSearchParams() // Use the GET param for page, but make sure that it is between 1 and the last page. If it's a string or a number // outside the range, fix the value, so it works as expected. const {count: currentPage, setCount: setPage} = useCounter(Math.max(1, parseInt(searchParams.get(pageKey || "") || "") || 1)) - const {value: focusOnElement, setTrue: enableFocusElement, setFalse: disableFocusElement} = useBoolean(false) const focusItemRef = useRef(null) const [animationParent] = useAutoAnimate() - const goToPage = async (page: number) => { - if (loadPage) { - const newView = await loadPage(page - 1) - setItems(newView.props.children) - } - - enableFocusElement() - setPage(page) - } + const goToPage = useCallback( + async (page: number, doNotFocusOnResults?: boolean) => { + if (loadPage) { + loadPage(page - 1) + .then(response => { + setItems(response.props.fallback ? response.props.children.props.children : response.props.children) + + if (!doNotFocusOnResults) enableFocusElement() + setPage(page) + }) + .catch(() => console.warn("An error occurred fetching more results.")) + } + }, + [enableFocusElement, setPage, loadPage] + ) const setFocusOnItem = useFocusOnRender(focusItemRef, false) @@ -80,16 +87,10 @@ const PagedList = ({children, ulProps, liProps, pageKey = "page", totalPages, pa }, [loadPage, router, currentPage, pageKey, searchParams]) useEffect(() => { - const updateInitialContents = async (initialPage: number) => { - if (loadPage) { - const newView = await loadPage(initialPage - 1) - setItems(newView.props.children) - } - } - const initialPage = parseInt(searchParams.get(pageKey || "") || "") - if (initialPage > 1) updateInitialContents(initialPage) - }, [searchParams, pageKey, loadPage]) + if (initialPage > 1 && !ref.current) goToPage(initialPage, true) + ref.current = true + }, [searchParams, pageKey, loadPage, goToPage]) const paginationButtons = usePagination(totalPages * items.length, currentPage, items.length, pagerSiblingCount) @@ -101,7 +102,7 @@ const PagedList = ({children, ulProps, liProps, pageKey = "page", totalPages, pa > {items.map((item, i) => (
  • 1 && (