import Button from '@components/atoms/buttons/button'; import Heading, { type HeadingLevel } from '@components/atoms/headings/heading'; import ProgressBar from '@components/atoms/loaders/progress-bar'; import Spinner from '@components/atoms/loaders/spinner'; import Pagination, { PaginationProps, } from '@components/molecules/nav/pagination'; import useIsMounted from '@utils/hooks/use-is-mounted'; import useSettings from '@utils/hooks/use-settings'; import { FC, Fragment, useRef } from 'react'; import { useIntl } from 'react-intl'; import styles from './posts-list.module.scss'; import Summary, { type SummaryProps } from './summary'; export type Post = SummaryProps & { /** * The post id. */ id: string | number; }; export type YearCollection = { [key: string]: Post[]; }; export type PostsListProps = Pick & { /** * True to display the posts by year. Default: false. */ byYear?: boolean; /** * Determine if the data is loading. */ isLoading?: boolean; /** * Load more button handler. */ loadMore?: () => void; /** * The current page number. Default: 1. */ pageNumber?: number; /** * The posts data. */ posts: Post[]; /** * Determine if the load more button should be visible. */ showLoadMoreBtn?: boolean; /** * The posts heading level (hn). */ titleLevel?: HeadingLevel; /** * The total posts number. */ total: number; }; /** * Create a collection of posts sorted by year. * * @param {Posts[]} data - A collection of posts. * @returns {YearCollection} The posts sorted by year. */ const sortPostsByYear = (data: Post[]): YearCollection => { const yearCollection: YearCollection = {}; data.forEach((post) => { const postYear = new Date(post.meta.dates.publication) .getFullYear() .toString(); yearCollection[postYear] = [...(yearCollection[postYear] || []), post]; }); return yearCollection; }; /** * PostsList component * * Render a list of post summaries. */ const PostsList: FC = ({ baseUrl, byYear = false, isLoading = false, loadMore, pageNumber = 1, posts, showLoadMoreBtn = false, siblings, titleLevel, total, }) => { const intl = useIntl(); const listRef = useRef(null); const lastPostRef = useRef(null); const isMounted = useIsMounted(listRef); const { blog } = useSettings(); /** * Retrieve the list of posts. * * @param {Posts[]} allPosts - A collection fo posts. * @param {HeadingLevel} [headingLevel] - The posts heading level (hn). * @returns {JSX.Element} The list of posts. */ const getList = ( allPosts: Post[], headingLevel: HeadingLevel = 2 ): JSX.Element => { const lastPostId = allPosts[allPosts.length - 1].id; return (
    {allPosts.map(({ id, ...post }) => (
  1. {id === lastPostId && (
  2. )}
    ))}
); }; /** * Retrieve the list of posts. * * @returns {JSX.Element | JSX.Element[]} The posts list. */ const getPosts = (): JSX.Element | JSX.Element[] => { const firstLevel = titleLevel || 2; if (!byYear) return getList(posts, firstLevel); const postsPerYear = sortPostsByYear(posts); const years = Object.keys(postsPerYear).reverse(); const nextLevel = (firstLevel + 1) as HeadingLevel; return years.map((year) => { return (
{year} {getList(postsPerYear[year], nextLevel)}
); }); }; const progressInfo = intl.formatMessage( { defaultMessage: '{articlesCount, plural, =0 {# loaded articles} one {# loaded article} other {# loaded articles}} out of a total of {total}', description: 'PostsList: loaded articles progress', id: '9MeLN3', }, { articlesCount: posts.length, total: total } ); const loadMoreBody = intl.formatMessage({ defaultMessage: 'Load more articles?', id: 'uaqd5F', description: 'PostsList: load more button', }); /** * Load more posts handler. */ const loadMorePosts = () => { if (lastPostRef.current) { lastPostRef.current.focus(); } loadMore && loadMore(); }; const getProgressBar = () => { return ( <> {showLoadMoreBtn && ( )} ); }; const getPagination = () => { return posts.length <= blog.postsPerPage ? ( ) : ( <> ); }; if (posts.length === 0) { return (

{intl.formatMessage({ defaultMessage: 'No results found.', description: 'PostsList: no results', id: 'vK7Sxv', })}

); } return ( <> {getPosts()} {isLoading && } {isMounted ? getProgressBar() : getPagination()} ); }; export default PostsList;