diff options
Diffstat (limited to 'src/components')
18 files changed, 447 insertions, 263 deletions
| diff --git a/src/components/Comment/Comment.tsx b/src/components/Comment/Comment.tsx index 7215afe..0371288 100644 --- a/src/components/Comment/Comment.tsx +++ b/src/components/Comment/Comment.tsx @@ -1,17 +1,25 @@  import { Button } from '@components/Buttons'; -import CommentForm from '@components/CommentForm/CommentForm'; +import Spinner from '@components/Spinner/Spinner';  import { Comment as CommentData } from '@ts/types/comments';  import { settings } from '@utils/config';  import { getFormattedDate } from '@utils/helpers/format'; +import dynamic from 'next/dynamic';  import Image from 'next/image';  import Link from 'next/link';  import { useRouter } from 'next/router';  import Script from 'next/script'; -import { useEffect, useRef, useState } from 'react'; +import { useState } from 'react';  import { useIntl } from 'react-intl';  import { Comment as CommentSchema, WithContext } from 'schema-dts';  import styles from './Comment.module.scss'; +const DynamicCommentForm = dynamic( +  () => import('@components/CommentForm/CommentForm'), +  { +    loading: () => <Spinner />, +  } +); +  const Comment = ({    articleId,    comment, @@ -25,11 +33,6 @@ const Comment = ({    const router = useRouter();    const locale = router.locale ? router.locale : settings.locales.defaultLocale;    const [shouldOpenForm, setShouldOpenForm] = useState<boolean>(false); -  const firstFieldRef = useRef<HTMLInputElement>(null); - -  useEffect(() => { -    firstFieldRef.current && firstFieldRef.current.focus(); -  });    const getCommentAuthor = () => {      return comment.author.url ? ( @@ -113,8 +116,7 @@ const Comment = ({            )}          </article>          {shouldOpenForm && ( -          <CommentForm -            ref={firstFieldRef} +          <DynamicCommentForm              articleId={articleId}              parentId={comment.commentId}            /> diff --git a/src/components/CommentForm/CommentForm.tsx b/src/components/CommentForm/CommentForm.tsx index c409d04..eb7c100 100644 --- a/src/components/CommentForm/CommentForm.tsx +++ b/src/components/CommentForm/CommentForm.tsx @@ -4,14 +4,17 @@ import Notice from '@components/Notice/Notice';  import Spinner from '@components/Spinner/Spinner';  import { createComment } from '@services/graphql/mutations';  import { NoticeType } from '@ts/types/app'; -import { ForwardedRef, forwardRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react';  import { useIntl } from 'react-intl';  import styles from './CommentForm.module.scss'; -const CommentForm = ( -  { articleId, parentId = 0 }: { articleId: number; parentId?: number }, -  ref: ForwardedRef<HTMLInputElement> -) => { +const CommentForm = ({ +  articleId, +  parentId = 0, +}: { +  articleId: number; +  parentId?: number; +}) => {    const intl = useIntl();    const [name, setName] = useState<string>('');    const [email, setEmail] = useState<string>(''); @@ -20,6 +23,12 @@ const CommentForm = (    const [isSubmitting, setIsSubmitting] = useState<boolean>(false);    const [notice, setNotice] = useState<string>();    const [noticeType, setNoticeType] = useState<NoticeType>('success'); +  const nameFieldRef = useRef<HTMLInputElement>(null); + +  useEffect(() => { +    if (parentId === 0) return; +    nameFieldRef.current && nameFieldRef.current.focus(); +  });    const resetForm = () => {      setName(''); @@ -154,7 +163,7 @@ const CommentForm = (              value={name}              setValue={setName}              required={true} -            ref={ref} +            ref={nameFieldRef}            />          </FormItem>          <FormItem> @@ -216,4 +225,4 @@ const CommentForm = (    );  }; -export default forwardRef(CommentForm); +export default CommentForm; 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>    ); diff --git a/src/components/Toolbar/Toolbar.tsx b/src/components/Toolbar/Toolbar.tsx index 7b28757..17f9ef9 100644 --- a/src/components/Toolbar/Toolbar.tsx +++ b/src/components/Toolbar/Toolbar.tsx @@ -1,10 +1,21 @@  import { ButtonToolbar } from '@components/Buttons';  import MainNav from '@components/MainNav/MainNav'; -import SearchForm from '@components/SearchForm/SearchForm'; -import Settings from '@components/Settings/Settings'; +import Spinner from '@components/Spinner/Spinner'; +import dynamic from 'next/dynamic';  import { RefObject, useCallback, useEffect, useRef, useState } from 'react';  import styles from './Toolbar.module.scss'; +const DynamicSearchForm = dynamic( +  () => import('@components/SearchForm/SearchForm'), +  { +    loading: () => <Spinner />, +  } +); + +const DynamicSettings = dynamic(() => import('@components/Settings/Settings'), { +  loading: () => <Spinner />, +}); +  const Toolbar = () => {    const [isNavOpened, setIsNavOpened] = useState<boolean>(false);    const [isSearchOpened, setIsSearchOpened] = useState<boolean>(false); @@ -129,7 +140,7 @@ const Toolbar = () => {          setIsActivated={setIsSearchOpened}        />        <div id="search-modal" className={searchClasses} ref={searchModalRef}> -        <SearchForm isOpened={isSearchOpened} /> +        <DynamicSearchForm isOpened={isSearchOpened} />        </div>        <ButtonToolbar          ref={settingsBtnRef} @@ -142,7 +153,7 @@ const Toolbar = () => {          className={settingsClasses}          ref={settingsModalRef}        > -        <Settings /> +        <DynamicSettings />        </div>      </div>    ); | 
