Skip to content

Commit

Permalink
Updated load more list and focus on render
Browse files Browse the repository at this point in the history
  • Loading branch information
pookmish committed Aug 8, 2024
1 parent 13c0821 commit 663a1d8
Show file tree
Hide file tree
Showing 18 changed files with 169 additions and 72 deletions.
31 changes: 22 additions & 9 deletions src/components/elements/load-more-list.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"use client"

import {useLayoutEffect, useRef, HtmlHTMLAttributes, JSX, useId, useState} from "react"
import Button from "@components/elements/button"
import {useAutoAnimate} from "@formkit/auto-animate/react"
import {useBoolean, useCounter} from "usehooks-ts"
import useFocusOnRender from "@lib/hooks/useFocusOnRender"
import useServerAction from "@lib/hooks/useServerAction"
import {twMerge} from "tailwind-merge"
import {ArrowPathIcon} from "@heroicons/react/20/solid"
import Button from "@components/elements/button"

type Props = HtmlHTMLAttributes<HTMLDivElement> & {
/**
Expand All @@ -31,23 +34,26 @@ type Props = HtmlHTMLAttributes<HTMLDivElement> & {
* Server action callback to fetch the next "page" contents.
*/
loadPage?: (_page: number) => Promise<JSX.Element>
/**
* Count of the total number of items of all pages.
*/
totalItems: number
}

const LoadMoreList = ({buttonText, children, ulProps, liProps, loadPage, ...props}: Props) => {
const LoadMoreList = ({buttonText, children, ulProps, liProps, totalItems, loadPage, ...props}: Props) => {
const id = useId()
const {count: page, increment: incrementPage} = useCounter(0)
const [items, setItems] = useState<JSX.Element[]>(children)
const {value: hasMore, setValue: setHasMore} = useBoolean(!!loadPage)
const {value: focusOnElement, setTrue: enableFocusElement, setFalse: disableFocusElement} = useBoolean(false)
const [runLoadPage, isPending] = useServerAction(loadPage)

const focusItemRef = useRef<HTMLLIElement>(null)
const [animationParent] = useAutoAnimate<HTMLUListElement>()

const showMoreItems = async () => {
if (loadPage) {
const results = await loadPage(page + 1)
if (results.props.children.length < 30) setHasMore(false)
setItems([...items, ...results.props.children])
const results = await runLoadPage(page + 1)
setItems([...items, ...results?.props.children])
}

enableFocusElement()
Expand All @@ -61,7 +67,14 @@ const LoadMoreList = ({buttonText, children, ulProps, liProps, loadPage, ...prop
}, [focusOnElement, setFocusOnItem])

return (
<div {...props}>
<div {...props} className={twMerge("relative", props.className)}>
{isPending && (
<div className="absolute left-0 top-0 z-20 h-full w-full bg-black-30 bg-opacity-80">
<div className="absolute bottom-20 left-1/2 -translate-x-[25px]">
<ArrowPathIcon className="animate-spin" width={50} />
</div>
</div>
)}
<ul {...ulProps} ref={animationParent}>
{items.map((item, i) => (
<li
Expand All @@ -79,8 +92,8 @@ const LoadMoreList = ({buttonText, children, ulProps, liProps, loadPage, ...prop
Showing {items.length} items.
</span>

{hasMore && (
<Button centered onClick={showMoreItems}>
{items.length < totalItems && (
<Button buttonElem centered onClick={showMoreItems}>
{buttonText ? buttonText : "Load More"}
</Button>
)}
Expand Down
40 changes: 21 additions & 19 deletions src/components/paragraphs/stanford-lists/list-paragraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Wysiwyg from "@components/elements/wysiwyg"
import Button from "@components/elements/button"
import View from "@components/views/view"
import {H2} from "@components/elements/headers"
import {cache, ElementType, HtmlHTMLAttributes, JSX} from "react"
import {cache, ElementType, HtmlHTMLAttributes, JSX, Suspense} from "react"
import {
Maybe,
NodeStanfordCourse,
Expand Down Expand Up @@ -79,24 +79,26 @@ const ListParagraph = async ({paragraph, ...props}: Props) => {
<Wysiwyg html={paragraph.suListDescription?.processed} />

{viewId && displayId && viewItems && (
<View
viewId={viewId}
displayId={displayId}
items={viewItems}
headingLevel={paragraph.suListHeadline ? "h3" : "h2"}
loadPage={
addLoadMore
? loadPage.bind(
null,
viewId,
displayId,
paragraph.suListView?.contextualFilter || [],
!!paragraph.suListHeadline
)
: undefined
}
totalItems={totalItems}
/>
<Suspense>
<View
viewId={viewId}
displayId={displayId}
items={viewItems}
headingLevel={paragraph.suListHeadline ? "h3" : "h2"}
loadPage={
addLoadMore
? loadPage.bind(
null,
viewId,
displayId,
paragraph.suListView?.contextualFilter || [],
!!paragraph.suListHeadline
)
: undefined
}
totalItems={totalItems}
/>
</Suspense>
)}

{viewItems.length === 0 && behaviors.list_paragraph?.empty_message && (
Expand Down
7 changes: 6 additions & 1 deletion src/components/views/card-view-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ type Props = {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items on all pages.
*/
totalItems: number
}

const CardViewGrid = ({items, headingLevel}: Props) => {
const CardViewGrid = ({items, totalItems, headingLevel}: Props) => {
return (
<LoadMoreList
ulProps={{className: "list-unstyled grid @4xl:grid-cols-2 @7xl:grid-cols-3 gap-20 mb-20"}}
liProps={{className: ""}}
totalItems={totalItems}
>
{items.map(item => (
<NodeCard node={item} key={item.id} headingLevel={headingLevel} />
Expand Down
8 changes: 6 additions & 2 deletions src/components/views/shared-tags/shared-tags-card-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items to build the pager.
*/
totalItems: number
}

const SharedTagsCardView = async ({items = [], headingLevel}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} />
const SharedTagsCardView = async ({items = [], totalItems, headingLevel}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} totalItems={totalItems} />
}
export default SharedTagsCardView
8 changes: 6 additions & 2 deletions src/components/views/stanford-courses/course-card-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items to build the pager.
*/
totalItems: number
}

const CourseCardView = async ({items = [], headingLevel}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} />
const CourseCardView = async ({items = [], headingLevel, totalItems}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} totalItems={totalItems} />
}
export default CourseCardView
7 changes: 6 additions & 1 deletion src/components/views/stanford-courses/course-list-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items on all pages.
*/
totalItems: number
}

const CourseListView = async ({items = [], headingLevel}: Props) => {
const CourseListView = async ({items = [], totalItems, headingLevel}: Props) => {
return (
<LoadMoreList
buttonText={
Expand All @@ -25,6 +29,7 @@ const CourseListView = async ({items = [], headingLevel}: Props) => {
liProps={{
className: "border-b border-black-20 last-of-type:border-0 pb-10 last:pb-0 pt-10 first:pt-0",
}}
totalItems={totalItems}
>
{items.map(item => (
<StanfordCourseListItem key={item.id} node={item} headingLevel={headingLevel} />
Expand Down
8 changes: 6 additions & 2 deletions src/components/views/stanford-events/events-card-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items to build the pager.
*/
totalItems: number
}

const EventsCardView = async ({items = [], headingLevel}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} />
const EventsCardView = async ({items = [], headingLevel, totalItems}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} totalItems={totalItems} />
}
export default EventsCardView
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ const getTopicOptions = (
return topicOptions.sort((a, b) => (a.label < b.label ? -1 : a.label > b.label ? 1 : 0))
}

const EventsFilteredListView = ({items, topics}: {items: NodeStanfordEvent[]; topics: TermStanfordEventType[]}) => {
const EventsFilteredListView = ({
items,
totalItems,
topics,
}: {
items: NodeStanfordEvent[]
totalItems: number
topics: TermStanfordEventType[]
}) => {
const [chosenTopic, setChosenTopic] = useState<string>("")
const [displayedEvents, setDisplayedEvents] = useState<NodeStanfordEvent[]>(items)

Expand Down Expand Up @@ -88,6 +96,7 @@ const EventsFilteredListView = ({items, topics}: {items: NodeStanfordEvent[]; to
className: "border-b border-black-20 last-of-type:border-0 pb-10 last:pb-0 pt-10 first:pt-0",
}}
itemsPerPage={3}
totalItems={totalItems}
>
{displayedEvents.map(event => (
<StanfordEventListItem key={event.id} node={event} />
Expand Down
9 changes: 7 additions & 2 deletions src/components/views/stanford-events/events-list-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items on all pages.
*/
totalItems: number
}

const EventsListView = async ({items = [], headingLevel}: Props) => {
const EventsListView = async ({items = [], headingLevel, totalItems}: Props) => {
if (items.length >= 5) {
const topics: TermStanfordEventType[] = []
items.map(event => event.suEventType?.map(topic => topics.push(topic)))
const uniqueTopics = [...new Map(topics.map(t => [t.id, t])).values()]

if (uniqueTopics.length > 1) {
return <EventsFilteredListView items={items} topics={uniqueTopics} />
return <EventsFilteredListView items={items} topics={uniqueTopics} totalItems={totalItems} />
}
}

Expand All @@ -36,6 +40,7 @@ const EventsListView = async ({items = [], headingLevel}: Props) => {
liProps={{
className: "border-b border-black-20 last-of-type:border-0 pb-10 last:pb-0 pt-10 first:pt-0",
}}
totalItems={totalItems}
>
{items.map(item => (
<StanfordEventListItem key={item.id} node={item} headingLevel={headingLevel} />
Expand Down
8 changes: 6 additions & 2 deletions src/components/views/stanford-news/news-card-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items to build the pager.
*/
totalItems: number
}

const NewsCardView = async ({items = [], headingLevel}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} />
const NewsCardView = async ({items = [], headingLevel, totalItems}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} totalItems={totalItems} />
}
export default NewsCardView
2 changes: 1 addition & 1 deletion src/components/views/stanford-news/news-list-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface Props {
loadPage?: (_page: number) => Promise<JSX.Element>
}

const NewsListView = async ({items, totalItems, headingLevel, loadPage}: Props) => {
const NewsListView = async ({items, headingLevel, totalItems, loadPage}: Props) => {
return (
<PagedList
ulProps={{className: "list-unstyled mb-20"}}
Expand Down
8 changes: 6 additions & 2 deletions src/components/views/stanford-page/page-card-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items to build the pager.
*/
totalItems: number
}

const PageCardView = async ({items = [], headingLevel}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} />
const PageCardView = async ({items = [], headingLevel, totalItems}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} totalItems={totalItems} />
}
export default PageCardView
7 changes: 6 additions & 1 deletion src/components/views/stanford-page/page-list-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items on all pages.
*/
totalItems: number
}

const PageListView = async ({items = [], headingLevel}: Props) => {
const PageListView = async ({items = [], headingLevel, totalItems}: Props) => {
return (
<LoadMoreList
ulProps={{className: "list-unstyled mb-20"}}
liProps={{
className: "border-b border-black-20 last-of-type:border-0 pb-10 last:pb-0 pt-10 first:pt-0",
}}
totalItems={totalItems}
>
{items.map(item => (
<StanfordPageListItem key={item.id} node={item} headingLevel={headingLevel} />
Expand Down
8 changes: 6 additions & 2 deletions src/components/views/stanford-person/person-card-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items to build the pager.
*/
totalItems: number
}

const PersonCardView = async ({items = [], headingLevel}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} />
const PersonCardView = async ({items = [], headingLevel, totalItems}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} totalItems={totalItems} />
}
export default PersonCardView
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items to build the pager.
*/
totalItems: number
}

const PublicationsApaView = async ({items = [], headingLevel}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} />
const PublicationsApaView = async ({items = [], headingLevel, totalItems}: Props) => {
return <CardViewGrid items={items} headingLevel={headingLevel} totalItems={totalItems} />
}
export default PublicationsApaView
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ interface Props {
* If those nodes titles should display as <h2> or <h3>
*/
headingLevel?: "h2" | "h3"
/**
* Total number of items on all pages.
*/
totalItems: number
}

const PublicationsChicagoView = async ({items = [], headingLevel}: Props) => {
const PublicationsChicagoView = async ({items = [], headingLevel, totalItems}: Props) => {
return (
<LoadMoreList
ulProps={{className: "list-unstyled mb-20"}}
liProps={{
className: "border-b border-black-20 last-of-type:border-0 pb-10 last:pb-0 pt-10 first:pt-0",
}}
totalItems={totalItems}
>
{items.map(item => (
<StanfordPublicationListItem key={item.id} node={item} headingLevel={headingLevel} />
Expand Down
Loading

0 comments on commit 663a1d8

Please sign in to comment.