summaryrefslogtreecommitdiffstats
path: root/src/components/organisms/layout/summary.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/organisms/layout/summary.tsx')
-rw-r--r--src/components/organisms/layout/summary.tsx136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/components/organisms/layout/summary.tsx b/src/components/organisms/layout/summary.tsx
new file mode 100644
index 0000000..8807878
--- /dev/null
+++ b/src/components/organisms/layout/summary.tsx
@@ -0,0 +1,136 @@
+import ButtonLink from '@components/atoms/buttons/button-link';
+import Heading, { type HeadingLevel } from '@components/atoms/headings/heading';
+import Arrow from '@components/atoms/icons/arrow';
+import Link from '@components/atoms/links/link';
+import ResponsiveImage, {
+ type ResponsiveImageProps,
+} from '@components/molecules/images/responsive-image';
+import Meta, { type MetaData } from '@components/molecules/layout/meta';
+import { type Article, type Meta as MetaType } from '@ts/types/app';
+import useReadingTime from '@utils/hooks/use-reading-time';
+import { FC, ReactNode } from 'react';
+import { useIntl } from 'react-intl';
+import styles from './summary.module.scss';
+
+export type Cover = Pick<
+ ResponsiveImageProps,
+ 'alt' | 'src' | 'width' | 'height'
+>;
+
+export type SummaryMeta = Pick<
+ MetaType<'article'>,
+ | 'author'
+ | 'commentsCount'
+ | 'cover'
+ | 'dates'
+ | 'thematics'
+ | 'topics'
+ | 'wordsCount'
+>;
+
+export type SummaryProps = Pick<Article, 'intro' | 'title'> & {
+ /**
+ * The post metadata.
+ */
+ meta: SummaryMeta;
+ /**
+ * The heading level (hn).
+ */
+ titleLevel?: HeadingLevel;
+ /**
+ * The post url.
+ */
+ url: string;
+};
+
+/**
+ * Summary component
+ *
+ * Render a page summary.
+ */
+const Summary: FC<SummaryProps> = ({
+ intro,
+ meta,
+ title,
+ titleLevel = 2,
+ url,
+}) => {
+ const intl = useIntl();
+ const readMore = intl.formatMessage(
+ {
+ defaultMessage: 'Read more<a11y> about {title}</a11y>',
+ description: 'Summary: read more link',
+ id: 'Zpgv+f',
+ },
+ {
+ title,
+ a11y: (chunks: ReactNode) => (
+ <span className="screen-reader-text">{chunks}</span>
+ ),
+ }
+ );
+ const { author, commentsCount, cover, dates, thematics, topics, wordsCount } =
+ meta;
+ const readingTime = useReadingTime(wordsCount, true);
+
+ const getMeta = (): MetaData => {
+ return {
+ author: author?.name,
+ publication: { date: dates.publication },
+ update:
+ dates.update && dates.publication !== dates.update
+ ? { date: dates.update }
+ : undefined,
+ readingTime,
+ thematics: thematics?.map((thematic) => (
+ <Link key={thematic.id} href={thematic.url}>
+ {thematic.name}
+ </Link>
+ )),
+ topics: topics?.map((topic) => (
+ <Link key={topic.id} href={topic.url}>
+ {topic.name}
+ </Link>
+ )),
+ comments: {
+ about: title,
+ count: commentsCount || 0,
+ target: `${url}#comments`,
+ },
+ };
+ };
+
+ return (
+ <article className={styles.wrapper}>
+ {cover && <ResponsiveImage className={styles.cover} {...cover} />}
+ <header className={styles.header}>
+ <Link href={url} className={styles.link}>
+ <Heading level={titleLevel} className={styles.title}>
+ {title}
+ </Heading>
+ </Link>
+ </header>
+ <div className={styles.body}>
+ <div dangerouslySetInnerHTML={{ __html: intro }} />
+ <ButtonLink target={url} className={styles['read-more']}>
+ <>
+ {readMore}
+ <Arrow direction="right" className={styles.icon} />
+ </>
+ </ButtonLink>
+ </div>
+ <footer className={styles.footer}>
+ <Meta
+ data={getMeta()}
+ layout="column"
+ itemsLayout="stacked"
+ withSeparator={false}
+ className={styles.meta}
+ groupClassName={styles.meta__item}
+ />
+ </footer>
+ </article>
+ );
+};
+
+export default Summary;