aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-03-01 22:05:08 +0100
committerArmand Philippot <git@armandphilippot.com>2022-03-01 22:05:08 +0100
commit8bd9784acdee6871ad70e86d0d7120299bf76969 (patch)
tree9b81e0cd3ff881b2cbeb81f9f96b52b510d67646 /src/components
parent21c228600a7a69cfea3b7d8af6838bcfda1d7399 (diff)
refactor: split posts meta into smaller components
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Layouts/Layout.tsx3
-rw-r--r--src/components/MetaItems/Author/Author.tsx20
-rw-r--r--src/components/MetaItems/CommentsCount/CommentsCount.tsx41
-rw-r--r--src/components/MetaItems/Dates/Dates.tsx56
-rw-r--r--src/components/MetaItems/MetaItem/MetaItem.module.scss18
-rw-r--r--src/components/MetaItems/MetaItem/MetaItem.tsx36
-rw-r--r--src/components/MetaItems/PostsCount/PostsCount.tsx27
-rw-r--r--src/components/MetaItems/ReadingTime/ReadingTime.tsx55
-rw-r--r--src/components/MetaItems/Thematics/Thematics.tsx42
-rw-r--r--src/components/MetaItems/Topics/Topics.tsx36
-rw-r--r--src/components/MetaItems/Website/Website.tsx20
-rw-r--r--src/components/MetaItems/index.tsx21
-rw-r--r--src/components/PostHeader/PostHeader.tsx16
-rw-r--r--src/components/PostMeta/PostMeta.module.scss21
-rw-r--r--src/components/PostMeta/PostMeta.tsx236
15 files changed, 405 insertions, 243 deletions
diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx
index 845d6fa..23c1d0e 100644
--- a/src/components/Layouts/Layout.tsx
+++ b/src/components/Layouts/Layout.tsx
@@ -19,9 +19,8 @@ const Layout = ({
isHome?: boolean;
}) => {
const intl = useIntl();
- const { locale } = useRouter();
+ const { asPath, locale } = useRouter();
const ref = useRef<HTMLSpanElement>(null);
- const { asPath } = useRouter();
useEffect(() => {
ref.current?.focus();
diff --git a/src/components/MetaItems/Author/Author.tsx b/src/components/MetaItems/Author/Author.tsx
new file mode 100644
index 0000000..c3d78c2
--- /dev/null
+++ b/src/components/MetaItems/Author/Author.tsx
@@ -0,0 +1,20 @@
+import { MetaKind } from '@ts/types/app';
+import { useIntl } from 'react-intl';
+import { MetaItem } from '..';
+
+const Author = ({ name, kind }: { name: string; kind: MetaKind }) => {
+ const intl = useIntl();
+
+ return (
+ <MetaItem
+ title={intl.formatMessage({
+ defaultMessage: 'Written by:',
+ description: 'Author: article author meta label',
+ })}
+ value={name}
+ kind={kind}
+ />
+ );
+};
+
+export default Author;
diff --git a/src/components/MetaItems/CommentsCount/CommentsCount.tsx b/src/components/MetaItems/CommentsCount/CommentsCount.tsx
new file mode 100644
index 0000000..bd1990d
--- /dev/null
+++ b/src/components/MetaItems/CommentsCount/CommentsCount.tsx
@@ -0,0 +1,41 @@
+import { MetaKind } from '@ts/types/app';
+import { useRouter } from 'next/router';
+import { useIntl } from 'react-intl';
+import { MetaItem } from '..';
+
+const CommentsCount = ({ total, kind }: { total: number; kind: MetaKind }) => {
+ const intl = useIntl();
+ const { asPath } = useRouter();
+
+ const isArticle = () => asPath.includes('/article/');
+
+ const getCommentsCount = () => {
+ return intl.formatMessage(
+ {
+ defaultMessage:
+ '{total, plural, =0 {No comments} one {# comment} other {# comments}}',
+ description: 'CommentsCount: comment count value',
+ },
+ { total }
+ );
+ };
+
+ return (
+ <MetaItem
+ title={intl.formatMessage({
+ defaultMessage: 'Comments:',
+ description: 'CommentsCount: comment count meta label',
+ })}
+ value={
+ isArticle() ? (
+ <a href="#comments">{getCommentsCount()}</a>
+ ) : (
+ getCommentsCount()
+ )
+ }
+ kind={kind}
+ />
+ );
+};
+
+export default CommentsCount;
diff --git a/src/components/MetaItems/Dates/Dates.tsx b/src/components/MetaItems/Dates/Dates.tsx
new file mode 100644
index 0000000..04dff3a
--- /dev/null
+++ b/src/components/MetaItems/Dates/Dates.tsx
@@ -0,0 +1,56 @@
+import { MetaKind } from '@ts/types/app';
+import { settings } from '@utils/config';
+import { getFormattedDate } from '@utils/helpers/format';
+import { useRouter } from 'next/router';
+import { useIntl } from 'react-intl';
+import { MetaItem } from '..';
+
+const Dates = ({
+ publication,
+ update,
+ kind,
+}: {
+ publication: string;
+ update: string;
+ kind: MetaKind;
+}) => {
+ const intl = useIntl();
+ const { locale } = useRouter();
+ const validLocale = locale ? locale : settings.locales.defaultLocale;
+
+ const publicationDate = getFormattedDate(publication, validLocale);
+ const updateDate = getFormattedDate(update, validLocale);
+
+ return (
+ <>
+ <MetaItem
+ title={intl.formatMessage({
+ defaultMessage: 'Published on:',
+ description: 'Dates: publication date meta label',
+ })}
+ values={[
+ <time key={publication} dateTime={publication}>
+ {publicationDate}
+ </time>,
+ ]}
+ kind={kind}
+ />
+ {publicationDate !== updateDate && (
+ <MetaItem
+ title={intl.formatMessage({
+ defaultMessage: 'Updated on:',
+ description: 'Dates: update date meta label',
+ })}
+ values={[
+ <time key={update} dateTime={update}>
+ {updateDate}
+ </time>,
+ ]}
+ kind={kind}
+ />
+ )}
+ </>
+ );
+};
+
+export default Dates;
diff --git a/src/components/MetaItems/MetaItem/MetaItem.module.scss b/src/components/MetaItems/MetaItem/MetaItem.module.scss
new file mode 100644
index 0000000..0b159ca
--- /dev/null
+++ b/src/components/MetaItems/MetaItem/MetaItem.module.scss
@@ -0,0 +1,18 @@
+.wrapper--article {
+ display: flex;
+ flex-flow: row wrap;
+}
+
+.title--article {
+ margin-right: var(--spacing-2xs);
+ color: var(--color-fg-light);
+}
+
+.body--article {
+ &:not(:first-of-type) {
+ &::before {
+ content: "/";
+ margin: 0 var(--spacing-2xs);
+ }
+ }
+}
diff --git a/src/components/MetaItems/MetaItem/MetaItem.tsx b/src/components/MetaItems/MetaItem/MetaItem.tsx
new file mode 100644
index 0000000..5c51283
--- /dev/null
+++ b/src/components/MetaItems/MetaItem/MetaItem.tsx
@@ -0,0 +1,36 @@
+import { MetaKind } from '@ts/types/app';
+import { ReactElement } from 'react';
+import styles from './MetaItem.module.scss';
+
+const MetaItem = ({
+ title,
+ value,
+ values,
+ info,
+ kind = 'list',
+}: {
+ title: string;
+ value?: ReactElement | string;
+ values?: ReactElement[] | string[];
+ info?: string;
+ kind: MetaKind;
+}) => {
+ return (
+ <div className={styles[`wrapper--${kind}`]}>
+ <dt className={styles[`title--${kind}`]}>{title}</dt>
+ {value && (
+ <dd className={styles[`body--${kind}`]} title={info}>
+ {value}
+ </dd>
+ )}
+ {values &&
+ values.map((currentValue, index) => (
+ <dd key={index} className={styles[`body--${kind}`]} title={info}>
+ {currentValue}
+ </dd>
+ ))}
+ </div>
+ );
+};
+
+export default MetaItem;
diff --git a/src/components/MetaItems/PostsCount/PostsCount.tsx b/src/components/MetaItems/PostsCount/PostsCount.tsx
new file mode 100644
index 0000000..9fb1784
--- /dev/null
+++ b/src/components/MetaItems/PostsCount/PostsCount.tsx
@@ -0,0 +1,27 @@
+import { MetaKind } from '@ts/types/app';
+import { useIntl } from 'react-intl';
+import { MetaItem } from '..';
+
+const PostsCount = ({ total, kind }: { total: number; kind: MetaKind }) => {
+ const intl = useIntl();
+
+ return (
+ <MetaItem
+ title={intl.formatMessage({
+ defaultMessage: 'Total:',
+ description: 'PostCount: total found articles meta label',
+ })}
+ value={intl.formatMessage(
+ {
+ defaultMessage:
+ '{total, plural, =0 {No articles} one {# article} other {# articles}}',
+ description: 'PostCount: total found articles',
+ },
+ { total }
+ )}
+ kind={kind}
+ />
+ );
+};
+
+export default PostsCount;
diff --git a/src/components/MetaItems/ReadingTime/ReadingTime.tsx b/src/components/MetaItems/ReadingTime/ReadingTime.tsx
new file mode 100644
index 0000000..94215b3
--- /dev/null
+++ b/src/components/MetaItems/ReadingTime/ReadingTime.tsx
@@ -0,0 +1,55 @@
+import { MetaKind } from '@ts/types/app';
+import { useRouter } from 'next/router';
+import { useIntl } from 'react-intl';
+import { MetaItem } from '..';
+
+const ReadingTime = ({
+ time,
+ words,
+ kind,
+}: {
+ time: number;
+ words: number;
+ kind: MetaKind;
+}) => {
+ const intl = useIntl();
+ const { locale } = useRouter();
+
+ const getEstimation = () => {
+ if (time < 0) {
+ return intl.formatMessage({
+ defaultMessage: 'less than 1 minute',
+ description: 'ReadingTime: Reading time value',
+ });
+ }
+
+ return intl.formatMessage(
+ {
+ defaultMessage:
+ '{time, plural, =0 {# minutes} one {# minute} other {# minutes}}',
+ description: 'ReadingTime: reading time value',
+ },
+ { time }
+ );
+ };
+
+ return (
+ <MetaItem
+ title={intl.formatMessage({
+ defaultMessage: 'Reading time:',
+ description: 'ReadingTime: reading time meta label',
+ })}
+ value={getEstimation()}
+ info={intl.formatMessage(
+ {
+ defaultMessage: `Approximately {number} words`,
+ description: 'ReadingTime: number of words',
+ },
+ { number: words.toLocaleString(locale) }
+ )}
+ kind={kind}
+ />
+ );
+};
+
+export default ReadingTime;
diff --git a/src/components/MetaItems/Thematics/Thematics.tsx b/src/components/MetaItems/Thematics/Thematics.tsx
new file mode 100644
index 0000000..a127715
--- /dev/null
+++ b/src/components/MetaItems/Thematics/Thematics.tsx
@@ -0,0 +1,42 @@
+import { MetaKind } from '@ts/types/app';
+import { ThematicPreview } from '@ts/types/taxonomies';
+import Link from 'next/link';
+import { useIntl } from 'react-intl';
+import { MetaItem } from '..';
+
+const Thematics = ({
+ list,
+ kind,
+}: {
+ list: ThematicPreview[];
+ kind: MetaKind;
+}) => {
+ const intl = useIntl();
+
+ const getThematics = () => {
+ return list.map((thematic) => {
+ return (
+ <Link key={thematic.databaseId} href={`/thematique/${thematic.slug}`}>
+ <a>{thematic.title}</a>
+ </Link>
+ );
+ });
+ };
+
+ return (
+ <MetaItem
+ title={intl.formatMessage(
+ {
+ defaultMessage:
+ '{thematicsCount, plural, =0 {Thematics:} one {Thematic:} other {Thematics:}}',
+ description: 'Thematics: thematics list meta label',
+ },
+ { thematicsCount: list.length }
+ )}
+ values={getThematics()}
+ kind={kind}
+ />
+ );
+};
+
+export default Thematics;
diff --git a/src/components/MetaItems/Topics/Topics.tsx b/src/components/MetaItems/Topics/Topics.tsx
new file mode 100644
index 0000000..4f2dc1f
--- /dev/null
+++ b/src/components/MetaItems/Topics/Topics.tsx
@@ -0,0 +1,36 @@
+import { MetaKind } from '@ts/types/app';
+import { TopicPreview } from '@ts/types/taxonomies';
+import Link from 'next/link';
+import { useIntl } from 'react-intl';
+import { MetaItem } from '..';
+
+const Topics = ({ list, kind }: { list: TopicPreview[]; kind: MetaKind }) => {
+ const intl = useIntl();
+
+ const getTopics = () => {
+ return list.map((topic) => {
+ return (
+ <Link key={topic.databaseId} href={`/sujet/${topic.slug}`}>
+ <a>{topic.title}</a>
+ </Link>
+ );
+ });
+ };
+
+ return (
+ <MetaItem
+ title={intl.formatMessage(
+ {
+ defaultMessage:
+ '{topicsCount, plural, =0 {Topics:} one {Topic:} other {Topics:}}',
+ description: 'Topics: topics list meta label',
+ },
+ { topicsCount: list.length }
+ )}
+ values={getTopics()}
+ kind={kind}
+ />
+ );
+};
+
+export default Topics;
diff --git a/src/components/MetaItems/Website/Website.tsx b/src/components/MetaItems/Website/Website.tsx
new file mode 100644
index 0000000..bcf3fc8
--- /dev/null
+++ b/src/components/MetaItems/Website/Website.tsx
@@ -0,0 +1,20 @@
+import { MetaKind } from '@ts/types/app';
+import { useIntl } from 'react-intl';
+import { MetaItem } from '..';
+
+const Website = ({ url, kind }: { url: string; kind: MetaKind }) => {
+ const intl = useIntl();
+
+ return (
+ <MetaItem
+ title={intl.formatMessage({
+ defaultMessage: 'Website:',
+ description: 'Website: website meta label',
+ })}
+ value={<a href={url}>{url}</a>}
+ kind={kind}
+ />
+ );
+};
+
+export default Website;
diff --git a/src/components/MetaItems/index.tsx b/src/components/MetaItems/index.tsx
new file mode 100644
index 0000000..e90d5a6
--- /dev/null
+++ b/src/components/MetaItems/index.tsx
@@ -0,0 +1,21 @@
+import Author from './Author/Author';
+import CommentsCount from './CommentsCount/CommentsCount';
+import Dates from './Dates/Dates';
+import MetaItem from './MetaItem/MetaItem';
+import PostsCount from './PostsCount/PostsCount';
+import ReadingTime from './ReadingTime/ReadingTime';
+import Thematics from './Thematics/Thematics';
+import Topics from './Topics/Topics';
+import Website from './Website/Website';
+
+export {
+ Author,
+ CommentsCount,
+ Dates,
+ MetaItem,
+ PostsCount,
+ ReadingTime,
+ Thematics,
+ Topics,
+ Website,
+};
diff --git a/src/components/PostHeader/PostHeader.tsx b/src/components/PostHeader/PostHeader.tsx
index f070583..c0a6b68 100644
--- a/src/components/PostHeader/PostHeader.tsx
+++ b/src/components/PostHeader/PostHeader.tsx
@@ -16,19 +16,6 @@ const PostHeader = ({
meta?: ArticleMeta;
title: string;
}) => {
- const hasMeta = () => {
- return (
- meta?.author ||
- meta?.commentCount ||
- meta?.dates ||
- meta?.readingTime ||
- meta?.results ||
- meta?.thematics ||
- meta?.website ||
- meta?.wordsCount
- );
- };
-
const getIntro = () => {
if (React.isValidElement(intro)) {
const Intro = () => intro;
@@ -38,6 +25,7 @@ const PostHeader = ({
</div>
);
}
+
return (
intro && (
<div
@@ -59,7 +47,7 @@ const PostHeader = ({
)}
{title}
</h1>
- {meta && hasMeta() && <PostMeta mode="single" meta={meta} />}
+ {meta && <PostMeta kind="article" meta={meta} />}
{getIntro()}
</div>
</header>
diff --git a/src/components/PostMeta/PostMeta.module.scss b/src/components/PostMeta/PostMeta.module.scss
index 6f8e1c2..d438635 100644
--- a/src/components/PostMeta/PostMeta.module.scss
+++ b/src/components/PostMeta/PostMeta.module.scss
@@ -18,7 +18,7 @@
}
}
- &--single {
+ &--article {
flex-flow: column wrap;
margin: var(--spacing-sm) 0 0;
@@ -27,24 +27,5 @@
font-size: var(--font-size-sm);
}
}
-
- .item {
- display: flex;
- flex-flow: row wrap;
- }
-
- .term {
- margin-right: var(--spacing-2xs);
- color: var(--color-fg-light);
- }
-
- .description {
- &:not(:first-of-type) {
- &::before {
- content: "/";
- margin: 0 var(--spacing-2xs);
- }
- }
- }
}
}
diff --git a/src/components/PostMeta/PostMeta.tsx b/src/components/PostMeta/PostMeta.tsx
index b951c44..e89e0e2 100644
--- a/src/components/PostMeta/PostMeta.tsx
+++ b/src/components/PostMeta/PostMeta.tsx
@@ -1,19 +1,24 @@
+import {
+ Author,
+ CommentsCount,
+ Dates,
+ PostsCount,
+ ReadingTime,
+ Thematics,
+ Topics,
+ Website,
+} from '@components/MetaItems';
+import { MetaKind } from '@ts/types/app';
import { ArticleMeta } from '@ts/types/articles';
-import { settings } from '@utils/config';
-import { getFormattedDate } from '@utils/helpers/format';
-import Link from 'next/link';
import { useRouter } from 'next/router';
-import { useIntl } from 'react-intl';
import styles from './PostMeta.module.scss';
-type PostMetaMode = 'list' | 'single';
-
const PostMeta = ({
meta,
- mode = 'list',
+ kind = 'list',
}: {
meta: ArticleMeta;
- mode?: PostMetaMode;
+ kind?: MetaKind;
}) => {
const {
author,
@@ -26,217 +31,34 @@ const PostMeta = ({
website,
wordsCount,
} = meta;
- const intl = useIntl();
- const router = useRouter();
- const locale = router.locale ? router.locale : settings.locales.defaultLocale;
- const isThematic = () => router.asPath.includes('/thematique/');
- const isArticle = () => router.asPath.includes('/article/');
-
- const getTopics = () => {
- return (
- topics &&
- topics.map((topic) => {
- return (
- <dd key={topic.id} className={styles.description}>
- <Link href={`/sujet/${topic.slug}`}>
- <a>{topic.title}</a>
- </Link>
- </dd>
- );
- })
- );
- };
-
- const getThematics = () => {
- return (
- thematics &&
- thematics.map((thematic) => {
- return (
- <dd key={thematic.id} className={styles.description}>
- <Link href={`/thematique/${thematic.slug}`}>
- <a>{thematic.title}</a>
- </Link>
- </dd>
- );
- })
- );
- };
-
- const getCommentsCount = () => {
- return intl.formatMessage(
- {
- defaultMessage:
- '{commentCount, plural, =0 {No comments} one {# comment} other {# comments}}',
- description: 'PostMeta: comment count value',
- },
- { commentCount }
- );
- };
-
- const getReadingTime = () => {
- if (!readingTime) return;
- if (readingTime < 0)
- return intl.formatMessage({
- defaultMessage: 'less than 1 minute',
- description: 'PostMeta: Reading time value',
- });
- return intl.formatMessage(
- {
- defaultMessage:
- '{readingTime, plural, =0 {# minutes} one {# minute} other {# minutes}}',
- description: 'PostMeta: reading time value',
- },
- { readingTime }
- );
- };
+ const { asPath } = useRouter();
+ const isThematic = () => asPath.includes('/thematique/');
- const getDates = () => {
- if (!dates) return <></>;
-
- const publicationDate = getFormattedDate(dates.publication, locale);
- const updateDate = getFormattedDate(dates.update, locale);
-
- return (
- <>
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage({
- defaultMessage: 'Published on:',
- description: 'PostMeta: publication date label',
- })}
- </dt>
- <dd className={styles.description}>
- <time dateTime={dates.publication}>{publicationDate}</time>
- </dd>
- </div>
- {publicationDate !== updateDate && (
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage({
- defaultMessage: 'Updated on:',
- description: 'PostMeta: update date label',
- })}
- </dt>
- <dd className={styles.description}>
- <time dateTime={dates.update}>{updateDate}</time>
- </dd>
- </div>
- )}
- </>
- );
- };
-
- const wrapperClass = styles[`wrapper--${mode}`];
+ const wrapperClass = styles[`wrapper--${kind}`];
return (
<dl className={wrapperClass}>
- {author && (
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage({
- defaultMessage: 'Written by:',
- description: 'Article meta',
- })}
- </dt>
- <dd className={styles.description}>{author.name}</dd>
- </div>
+ {author && <Author name={author.name} kind={kind} />}
+ {dates && (
+ <Dates
+ publication={dates.publication}
+ update={dates.update}
+ kind={kind}
+ />
)}
- {getDates()}
{readingTime !== undefined && wordsCount !== undefined && (
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage({
- defaultMessage: 'Reading time:',
- description: 'Article meta',
- })}
- </dt>
- <dd
- className={styles.description}
- title={`Approximately ${wordsCount.toLocaleString(locale)} words`}
- >
- {getReadingTime()}
- </dd>
- </div>
- )}
- {results && (
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage({
- defaultMessage: 'Total:',
- description: 'Article meta',
- })}
- </dt>
- <dd className={styles.description}>
- {intl.formatMessage(
- {
- defaultMessage:
- '{results, plural, =0 {No articles} one {# article} other {# articles}}',
- description: 'PostMeta: total found articles',
- },
- { results }
- )}
- </dd>
- </div>
+ <ReadingTime time={readingTime} words={wordsCount} kind={kind} />
)}
+ {results && <PostsCount total={results} kind={kind} />}
{!isThematic() && thematics && thematics.length > 0 && (
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage(
- {
- defaultMessage:
- '{thematicsCount, plural, =0 {Thematics:} one {Thematic:} other {Thematics:}}',
- description: 'PostMeta: thematics list label',
- },
- { thematicsCount: thematics.length }
- )}
- </dt>
- {getThematics()}
- </div>
+ <Thematics list={thematics} kind={kind} />
)}
{isThematic() && topics && topics.length > 0 && (
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage(
- {
- defaultMessage:
- '{topicsCount, plural, =0 {Topics:} one {Topic:} other {Topics:}}',
- description: 'PostMeta: topics list label',
- },
- { topicsCount: topics.length }
- )}
- </dt>
- {getTopics()}
- </div>
- )}
- {website && (
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage({
- defaultMessage: 'Website:',
- description: 'PostMeta: website label',
- })}
- </dt>
- <dd className={styles.description}>
- <a href={website}>{website}</a>
- </dd>
- </div>
+ <Topics list={topics} kind={kind} />
)}
+ {website && <Website url={website} kind={kind} />}
{commentCount !== undefined && (
- <div className={styles.item}>
- <dt className={styles.term}>
- {intl.formatMessage({
- defaultMessage: 'Comments:',
- description: 'PostMeta: comment count label',
- })}
- </dt>
- <dd className={styles.description}>
- {isArticle() ? (
- <a href="#comments">{getCommentsCount()}</a>
- ) : (
- getCommentsCount()
- )}
- </dd>
- </div>
+ <CommentsCount total={commentCount} kind={kind} />
)}
</dl>
);