diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-11-20 12:27:46 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-20 19:32:09 +0100 |
| commit | 70b4f633a6fbedb58c8b9134ac64ede854d489de (patch) | |
| tree | c757bb12ad9a588e23b25cdb8b46710ac14dbcb1 /src/components/templates/page/page-header.tsx | |
| parent | 9a481f066e1427d53a06cf7aeec525a745abf03f (diff) | |
refactor(components): replace PageLayout template with Page
* split pages in smaller components (it is both easier to maintain and
more readable, we avoid the use of fragments in pages directory)
* extract breadcrumbs from article tag (the navigation is not related
to the page contents)
* remove useReadingTime hook
* remove layout options except `isHome`
Diffstat (limited to 'src/components/templates/page/page-header.tsx')
| -rw-r--r-- | src/components/templates/page/page-header.tsx | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/components/templates/page/page-header.tsx b/src/components/templates/page/page-header.tsx new file mode 100644 index 0000000..6effc9e --- /dev/null +++ b/src/components/templates/page/page-header.tsx @@ -0,0 +1,172 @@ +import { + type ForwardRefRenderFunction, + forwardRef, + type ReactNode, +} from 'react'; +import { useIntl } from 'react-intl'; +import type { PageLink } from '../../../types'; +import { getReadingTimeFrom } from '../../../utils/helpers'; +import { Header, Heading, type HeaderProps, Link, Time } from '../../atoms'; +import { MetaList, MetaItem } from '../../molecules'; +import styles from './page.module.scss'; + +export type PageHeaderMetaData = { + author: string; + publicationDate: string; + thematics: PageLink[]; + total: number; + updateDate: string; + website: string; + wordsCount: number; +}; + +export type PageHeaderProps = Omit<HeaderProps, 'children'> & { + /** + * The page main title. + */ + heading: ReactNode; + /** + * The page introduction. + */ + intro?: ReactNode; + /** + * The page meta. + */ + meta?: Partial<PageHeaderMetaData>; +}; + +const PageHeaderWithRef: ForwardRefRenderFunction< + HTMLElement, + PageHeaderProps +> = ({ className = '', heading, intro, meta, ...props }, ref) => { + const headerClass = `${styles.header} ${className}`; + const intl = useIntl(); + + return ( + <Header {...props} className={headerClass} ref={ref}> + <div className={styles.header__body}> + <Heading className={styles.heading} level={1}> + {heading} + </Heading> + {meta ? ( + <MetaList className={styles.meta}> + {meta.author ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Written by:', + description: 'PageHeader: author meta label', + id: '/unaGZ', + })} + value={meta.author} + /> + ) : null} + {meta.publicationDate ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'PageHeader: publication date label', + id: 'pUBhKy', + })} + value={<Time date={meta.publicationDate} />} + /> + ) : null} + {meta.updateDate && meta.updateDate !== meta.publicationDate ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'PageHeader: update date label', + id: 'sR5hah', + })} + value={<Time date={meta.updateDate} />} + /> + ) : null} + {meta.wordsCount ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Reading time:', + description: 'PageHeader: reading time label', + id: 'jJm8wd', + })} + value={intl.formatMessage( + { + defaultMessage: + '{minutesCount, plural, =0 {Less than one minute} one {# minute} other {# minutes}}', + description: 'PageHeader: rounded minutes count', + id: 'NNDqRg', + }, + { + minutesCount: getReadingTimeFrom( + meta.wordsCount + ).inMinutes(), + } + )} + /> + ) : null} + {meta.thematics ? ( + <MetaItem + isInline + label={intl.formatMessage( + { + defaultMessage: + '{thematicsCount, plural, =0 {Thematics:} one {Thematic:} other {Thematics:}}', + description: 'PageHeader: thematics label', + id: 'ODwkBI', + }, + { thematicsCount: meta.thematics.length } + )} + value={meta.thematics.map((thematic) => { + return { + id: `thematic-${thematic.id}`, + value: <Link href={thematic.url}>{thematic.name}</Link>, + }; + })} + /> + ) : null} + {meta.total ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Total:', + description: 'PageHeader: total meta label', + id: 'a6DzIj', + })} + value={intl.formatMessage( + { + defaultMessage: + '{postsCount, plural, =0 {No posts} one {# post} other {# posts}}', + description: 'PageHeader: total meta value', + id: 'bAXtMT', + }, + { postsCount: meta.total } + )} + /> + ) : null} + {meta.website ? ( + <MetaItem + isInline + label={intl.formatMessage({ + defaultMessage: 'Website:', + description: 'PageHeader: website meta label', + id: '9jh0r2', + })} + value={meta.website} + /> + ) : null} + </MetaList> + ) : null} + {typeof intro === 'string' ? ( + // eslint-disable-next-line react/no-danger -- Intro can contain tags. + <div dangerouslySetInnerHTML={{ __html: intro }} /> + ) : ( + intro + )} + </div> + </Header> + ); +}; + +export const PageHeader = forwardRef(PageHeaderWithRef); |
