diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-03-01 22:05:08 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-03-01 22:05:08 +0100 |
| commit | 8bd9784acdee6871ad70e86d0d7120299bf76969 (patch) | |
| tree | 9b81e0cd3ff881b2cbeb81f9f96b52b510d67646 /src/components | |
| parent | 21c228600a7a69cfea3b7d8af6838bcfda1d7399 (diff) | |
refactor: split posts meta into smaller components
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/Layouts/Layout.tsx | 3 | ||||
| -rw-r--r-- | src/components/MetaItems/Author/Author.tsx | 20 | ||||
| -rw-r--r-- | src/components/MetaItems/CommentsCount/CommentsCount.tsx | 41 | ||||
| -rw-r--r-- | src/components/MetaItems/Dates/Dates.tsx | 56 | ||||
| -rw-r--r-- | src/components/MetaItems/MetaItem/MetaItem.module.scss | 18 | ||||
| -rw-r--r-- | src/components/MetaItems/MetaItem/MetaItem.tsx | 36 | ||||
| -rw-r--r-- | src/components/MetaItems/PostsCount/PostsCount.tsx | 27 | ||||
| -rw-r--r-- | src/components/MetaItems/ReadingTime/ReadingTime.tsx | 55 | ||||
| -rw-r--r-- | src/components/MetaItems/Thematics/Thematics.tsx | 42 | ||||
| -rw-r--r-- | src/components/MetaItems/Topics/Topics.tsx | 36 | ||||
| -rw-r--r-- | src/components/MetaItems/Website/Website.tsx | 20 | ||||
| -rw-r--r-- | src/components/MetaItems/index.tsx | 21 | ||||
| -rw-r--r-- | src/components/PostHeader/PostHeader.tsx | 16 | ||||
| -rw-r--r-- | src/components/PostMeta/PostMeta.module.scss | 21 | ||||
| -rw-r--r-- | src/components/PostMeta/PostMeta.tsx | 236 |
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> ); |
