aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-05-17 18:37:38 +0200
committerArmand Philippot <git@armandphilippot.com>2022-05-17 18:37:38 +0200
commit7d9f874364ec6e255e62eb3027011bfed267904c (patch)
tree711e3ce9fba99abdff89016f15b9bc763d61f03f /src
parent9a186b357b32325cf77fdce39a6c6c9aff76d8a5 (diff)
chore: adjust articles styles
* change animation on article card hover * change comments section alignment
Diffstat (limited to 'src')
-rw-r--r--src/components/atoms/buttons/buttons.module.scss2
-rw-r--r--src/components/atoms/headings/heading.module.scss16
-rw-r--r--src/components/atoms/headings/heading.stories.tsx26
-rw-r--r--src/components/atoms/headings/heading.tsx58
-rw-r--r--src/components/molecules/buttons/heading-button.module.scss15
-rw-r--r--src/components/molecules/buttons/heading-button.stories.tsx73
-rw-r--r--src/components/organisms/forms/comment-form.tsx14
-rw-r--r--src/components/organisms/layout/summary.module.scss7
-rw-r--r--src/components/templates/page/page-layout.module.scss9
-rw-r--r--src/components/templates/page/page-layout.tsx85
-rw-r--r--src/pages/article/[slug].tsx18
-rw-r--r--src/styles/pages/article.module.scss1
12 files changed, 211 insertions, 113 deletions
diff --git a/src/components/atoms/buttons/buttons.module.scss b/src/components/atoms/buttons/buttons.module.scss
index 36c66b6..2444bb1 100644
--- a/src/components/atoms/buttons/buttons.module.scss
+++ b/src/components/atoms/buttons/buttons.module.scss
@@ -16,7 +16,7 @@
}
&--rectangle {
- padding: var(--spacing-2xs) var(--spacing-md);
+ padding: var(--spacing-2xs) var(--spacing-sm);
}
&--square,
diff --git a/src/components/atoms/headings/heading.module.scss b/src/components/atoms/headings/heading.module.scss
index 8620f6f..a420bc1 100644
--- a/src/components/atoms/headings/heading.module.scss
+++ b/src/components/atoms/headings/heading.module.scss
@@ -6,11 +6,23 @@
letter-spacing: 0.01ex;
&--regular {
- margin: 0;
+ margin-bottom: 0;
+ margin-top: 0;
+ }
+
+ &--left {
+ text-align: left;
+ }
+
+ &--center {
+ width: fit-content;
+ margin-left: auto;
+ margin-right: auto;
}
&--margin {
- margin: 0 0 var(--spacing-sm);
+ margin-top: 0;
+ margin-bottom: var(--spacing-sm);
& + & {
margin-top: var(--spacing-md);
diff --git a/src/components/atoms/headings/heading.stories.tsx b/src/components/atoms/headings/heading.stories.tsx
index da5a718..0e3885d 100644
--- a/src/components/atoms/headings/heading.stories.tsx
+++ b/src/components/atoms/headings/heading.stories.tsx
@@ -8,10 +8,26 @@ export default {
title: 'Atoms/Typography/Headings',
component: Heading,
args: {
+ alignment: 'left',
isFake: false,
withMargin: true,
},
argTypes: {
+ alignment: {
+ control: {
+ type: 'select',
+ },
+ description: 'The title alignment.',
+ options: ['center', 'left'],
+ table: {
+ category: 'Options',
+ defaultValue: { summary: 'left' },
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
className: {
control: {
type: 'text',
@@ -32,6 +48,16 @@ export default {
required: true,
},
},
+ id: {
+ control: {
+ type: 'text',
+ },
+ description: 'An unique id.',
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
isFake: {
control: {
type: 'boolean',
diff --git a/src/components/atoms/headings/heading.tsx b/src/components/atoms/headings/heading.tsx
index c5bf4ca..e385249 100644
--- a/src/components/atoms/headings/heading.tsx
+++ b/src/components/atoms/headings/heading.tsx
@@ -1,10 +1,20 @@
-import { FC, ReactNode } from 'react';
+import {
+ createElement,
+ ForwardedRef,
+ forwardRef,
+ ForwardRefRenderFunction,
+ ReactNode,
+} from 'react';
import styles from './heading.module.scss';
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
export type HeadingProps = {
/**
+ * The title alignment. Default: left;
+ */
+ alignment?: 'center' | 'left';
+ /**
* The heading body.
*/
children: ReactNode;
@@ -30,31 +40,55 @@ export type HeadingProps = {
withMargin?: boolean;
};
+type TitleTagProps = Pick<HeadingProps, 'children' | 'className' | 'id'> & {
+ tagName: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p';
+};
+
+const TitleTag = forwardRef<
+ HTMLHeadingElement | HTMLParagraphElement,
+ TitleTagProps
+>(
+ (
+ { children, tagName, ...props },
+ ref: ForwardedRef<HTMLHeadingElement | HTMLParagraphElement>
+ ) => {
+ return createElement(tagName, { ...props, ref }, children);
+ }
+);
+TitleTag.displayName = 'TitleTag';
+
/**
* Heading component.
*
* Render an HTML heading element or a paragraph with heading styles.
*/
-const Heading: FC<HeadingProps> = ({
- children,
- className,
- id,
- isFake = false,
- level,
- withMargin = true,
-}) => {
- const TitleTag = isFake ? `p` : (`h${level}` as keyof JSX.IntrinsicElements);
+const Heading: ForwardRefRenderFunction<HTMLDivElement, HeadingProps> = (
+ {
+ alignment = 'left',
+ children,
+ className,
+ id,
+ isFake = false,
+ level,
+ withMargin = true,
+ },
+ ref: ForwardedRef<HTMLHeadingElement | HTMLParagraphElement>
+) => {
+ const tagName = isFake ? 'p' : (`h${level}` as TitleTagProps['tagName']);
const levelClass = `heading--${level}`;
+ const alignmentClass = `heading--${alignment}`;
const marginClass = withMargin ? 'heading--margin' : 'heading--regular';
return (
<TitleTag
- className={`${styles.heading} ${styles[levelClass]} ${styles[marginClass]} ${className}`}
+ tagName={tagName}
+ className={`${styles.heading} ${styles[levelClass]} ${styles[alignmentClass]} ${styles[marginClass]} ${className}`}
id={id}
+ ref={ref}
>
{children}
</TitleTag>
);
};
-export default Heading;
+export default forwardRef(Heading);
diff --git a/src/components/molecules/buttons/heading-button.module.scss b/src/components/molecules/buttons/heading-button.module.scss
index 9c278e4..3c69221 100644
--- a/src/components/molecules/buttons/heading-button.module.scss
+++ b/src/components/molecules/buttons/heading-button.module.scss
@@ -20,6 +20,14 @@
border-bottom: fun.convert-px(2) solid var(--color-primary-dark);
cursor: pointer;
+ .heading {
+ padding: var(--spacing-2xs) 0;
+ background: none;
+ font-size: var(--font-size-xl);
+ font-weight: 500;
+ text-align: left;
+ }
+
&:hover,
&:focus {
.icon {
@@ -34,10 +42,3 @@
}
}
}
-
-.heading {
- padding: var(--spacing-2xs) 0;
- background: none;
- font-size: var(--font-size-xl);
- text-align: left;
-}
diff --git a/src/components/molecules/buttons/heading-button.stories.tsx b/src/components/molecules/buttons/heading-button.stories.tsx
index b0e1465..d1b5ed4 100644
--- a/src/components/molecules/buttons/heading-button.stories.tsx
+++ b/src/components/molecules/buttons/heading-button.stories.tsx
@@ -1,6 +1,5 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { useState } from 'react';
-import { IntlProvider } from 'react-intl';
import HeadingButtonComponent from './heading-button';
/**
@@ -10,6 +9,19 @@ export default {
title: 'Molecules/Buttons/HeadingButton',
component: HeadingButtonComponent,
argTypes: {
+ className: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the button.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
expanded: {
control: {
type: null,
@@ -53,13 +65,6 @@ export default {
},
},
},
- decorators: [
- (Story) => (
- <IntlProvider locale="en">
- <Story />
- </IntlProvider>
- ),
- ],
} as ComponentMeta<typeof HeadingButtonComponent>;
const Template: ComponentStory<typeof HeadingButtonComponent> = ({
@@ -79,55 +84,21 @@ const Template: ComponentStory<typeof HeadingButtonComponent> = ({
};
/**
- * Heading Button Stories - Level 1
+ * Heading Button Stories - Expanded
*/
-export const Level1 = Template.bind({});
-Level1.args = {
- level: 1,
- title: 'Your title',
-};
-
-/**
- * Heading Button Stories - Level 2
- */
-export const Level2 = Template.bind({});
-Level2.args = {
+export const Expanded = Template.bind({});
+Expanded.args = {
+ expanded: true,
level: 2,
title: 'Your title',
};
/**
- * Heading Button Stories - Level 3
+ * Heading Button Stories - Collapsed
*/
-export const Level3 = Template.bind({});
-Level3.args = {
- level: 3,
- title: 'Your title',
-};
-
-/**
- * Heading Button Stories - Level 4
- */
-export const Level4 = Template.bind({});
-Level4.args = {
- level: 4,
- title: 'Your title',
-};
-
-/**
- * Heading Button Stories - Level 5
- */
-export const Level5 = Template.bind({});
-Level5.args = {
- level: 5,
- title: 'Your title',
-};
-
-/**
- * Heading Button Stories - Level 6
- */
-export const Level6 = Template.bind({});
-Level6.args = {
- level: 6,
+export const Collapsed = Template.bind({});
+Collapsed.args = {
+ expanded: false,
+ level: 2,
title: 'Your title',
};
diff --git a/src/components/organisms/forms/comment-form.tsx b/src/components/organisms/forms/comment-form.tsx
index 5ff4ea4..b2c725f 100644
--- a/src/components/organisms/forms/comment-form.tsx
+++ b/src/components/organisms/forms/comment-form.tsx
@@ -1,6 +1,9 @@
import Button from '@components/atoms/buttons/button';
import Form, { type FormProps } from '@components/atoms/forms/form';
-import Heading, { type HeadingLevel } from '@components/atoms/headings/heading';
+import Heading, {
+ type HeadingProps,
+ type HeadingLevel,
+} from '@components/atoms/headings/heading';
import Spinner from '@components/atoms/loaders/spinner';
import LabelledField from '@components/molecules/forms/labelled-field';
import { FC, ReactNode, useState } from 'react';
@@ -34,7 +37,11 @@ export type CommentFormProps = Pick<FormProps, 'className'> & {
*/
title?: string;
/**
- * The title level.
+ * The form title alignment. Default: left.
+ */
+ titleAlignment?: HeadingProps['alignment'];
+ /**
+ * The title level. Default: 2.
*/
titleLevel?: HeadingLevel;
};
@@ -44,6 +51,7 @@ const CommentForm: FC<CommentFormProps> = ({
parentId,
saveComment,
title,
+ titleAlignment,
titleLevel = 2,
...props
}) => {
@@ -117,7 +125,7 @@ const CommentForm: FC<CommentFormProps> = ({
{...props}
>
{title && (
- <Heading id={formId} level={titleLevel}>
+ <Heading id={formId} level={titleLevel} alignment={titleAlignment}>
{title}
</Heading>
)}
diff --git a/src/components/organisms/layout/summary.module.scss b/src/components/organisms/layout/summary.module.scss
index 7e86dd2..62dfc0e 100644
--- a/src/components/organisms/layout/summary.module.scss
+++ b/src/components/organisms/layout/summary.module.scss
@@ -29,8 +29,11 @@
&:hover {
.icon {
- transform: scaleX(1.4);
- transform-origin: left;
+ --icon-size: #{fun.convert-px(35)};
+
+ :global {
+ animation: pulse 1.5s ease-in-out 0.2s infinite;
+ }
}
}
}
diff --git a/src/components/templates/page/page-layout.module.scss b/src/components/templates/page/page-layout.module.scss
index 83e5a80..c6b4e8d 100644
--- a/src/components/templates/page/page-layout.module.scss
+++ b/src/components/templates/page/page-layout.module.scss
@@ -86,7 +86,14 @@
&__section {
grid-column: 2;
- margin: var(--spacing-md) 0 0;
+
+ &:first-child {
+ margin: var(--spacing-md) 0 0;
+ }
+ }
+
+ &__no-comments {
+ text-align: center;
}
&__form {
diff --git a/src/components/templates/page/page-layout.tsx b/src/components/templates/page/page-layout.tsx
index f3f3ea8..2ff2084 100644
--- a/src/components/templates/page/page-layout.tsx
+++ b/src/components/templates/page/page-layout.tsx
@@ -1,6 +1,7 @@
import Heading from '@components/atoms/headings/heading';
import Notice, { type NoticeKind } from '@components/atoms/layout/notice';
import Sidebar from '@components/atoms/layout/sidebar';
+import { MetaData } from '@components/molecules/layout/meta';
import PageFooter, {
type PageFooterProps,
} from '@components/molecules/layout/page-footer';
@@ -118,10 +119,10 @@ const PageLayout: FC<PageLayoutProps> = ({
const isMounted = useIsMounted(bodyRef);
const hasComments = Array.isArray(comments) && comments.length > 0;
- const hasCommentsSection = hasComments || allowComments;
- const articleModifier = hasCommentsSection
- ? 'article--has-comments'
- : 'article--no-comments';
+ const articleModifier =
+ hasComments || allowComments
+ ? 'article--has-comments'
+ : 'article--no-comments';
const [status, setStatus] = useState<NoticeKind>('info');
const [statusMessage, setStatusMessage] = useState<string>('');
@@ -175,6 +176,15 @@ const PageLayout: FC<PageLayoutProps> = ({
}
};
+ /**
+ * Check if meta properties are defined.
+ *
+ * @param {MetaData} meta - The metadata.
+ */
+ const hasMeta = (meta: MetaData) => {
+ return Object.values(meta).every((value) => value === null);
+ };
+
return (
<Layout
breadcrumbSchema={breadcrumbSchema}
@@ -218,7 +228,9 @@ const PageLayout: FC<PageLayoutProps> = ({
{children}
</div>
)}
- <PageFooter meta={footerMeta} className={styles.footer} />
+ {footerMeta && hasMeta(footerMeta) && (
+ <PageFooter meta={footerMeta} className={styles.footer} />
+ )}
<Sidebar
className={`${styles.sidebar} ${styles['sidebar--last']}`}
aria-label={intl.formatMessage({
@@ -229,45 +241,54 @@ const PageLayout: FC<PageLayoutProps> = ({
>
{widgets}
</Sidebar>
- {hasCommentsSection && (
- <div className={styles.comments}>
- {hasComments && (
- <section className={styles.comments__section}>
- <Heading level={2}>{commentsTitle}</Heading>
+ {allowComments && (
+ <div className={styles.comments} id="comments">
+ <section className={styles.comments__section}>
+ <Heading level={2} alignment="center">
+ {commentsTitle}
+ </Heading>
+ {hasComments ? (
<CommentsList
comments={comments}
depth={1}
Notice={
- isReplyRef.current === true ? (
+ isReplyRef.current === true && (
<Notice
kind={status}
message={statusMessage}
className={styles.notice}
/>
- ) : undefined
+ )
}
saveComment={saveComment}
/>
- </section>
- )}
- {allowComments && (
- <section className={styles.comments__section}>
- <CommentForm
- className={styles.comments__form}
- saveComment={saveComment}
- title={commentFormTitle}
- Notice={
- isReplyRef.current === false ? (
- <Notice
- kind={status}
- message={statusMessage}
- className={styles.notice}
- />
- ) : undefined
- }
- />
- </section>
- )}
+ ) : (
+ <p className={styles['comments__no-comments']}>
+ {intl.formatMessage({
+ defaultMessage: 'No comments.',
+ id: 'sBwfCy',
+ description: 'PageLayout: no comments text',
+ })}
+ </p>
+ )}
+ </section>
+ <section className={styles.comments__section}>
+ <CommentForm
+ className={styles.comments__form}
+ saveComment={saveComment}
+ title={commentFormTitle}
+ titleAlignment="center"
+ Notice={
+ isReplyRef.current === false && (
+ <Notice
+ kind={status}
+ message={statusMessage}
+ className={styles.notice}
+ />
+ )
+ }
+ />
+ </section>
</div>
)}
</Layout>
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx
index 995e3a9..a0fb7fc 100644
--- a/src/pages/article/[slug].tsx
+++ b/src/pages/article/[slug].tsx
@@ -15,7 +15,10 @@ import { type Article, type Comment } from '@ts/types/app';
import { loadTranslation, type Messages } from '@utils/helpers/i18n';
import useAddPrismClassAttr from '@utils/hooks/use-add-prism-class-attr';
import useBreadcrumb from '@utils/hooks/use-breadcrumb';
-import usePrismPlugins, { PrismPlugin } from '@utils/hooks/use-prism-plugins';
+import usePrismPlugins, {
+ type PrismPlugin,
+} from '@utils/hooks/use-prism-plugins';
+import useReadingTime from '@utils/hooks/use-reading-time';
import useSettings from '@utils/hooks/use-settings';
import { GetStaticPaths, GetStaticProps, NextPage } from 'next';
import Head from 'next/head';
@@ -38,7 +41,16 @@ type ArticlePageProps = {
*/
const ArticlePage: NextPage<ArticlePageProps> = ({ comments, post }) => {
const { content, id, intro, meta, slug, title } = post;
- const { author, commentsCount, cover, dates, seo, thematics, topics } = meta;
+ const {
+ author,
+ commentsCount,
+ cover,
+ dates,
+ seo,
+ thematics,
+ topics,
+ wordsCount,
+ } = meta;
const { data } = useSWR(() => id, getPostComments, {
fallbackData: comments,
});
@@ -46,11 +58,13 @@ const ArticlePage: NextPage<ArticlePageProps> = ({ comments, post }) => {
title,
url: `/article/${slug}`,
});
+ const readingTime = useReadingTime(wordsCount || 0, true);
const headerMeta: PageLayoutProps['headerMeta'] = {
author: author?.name,
publication: { date: dates.publication },
update: dates.update ? { date: dates.update } : undefined,
+ readingTime,
thematics:
thematics &&
thematics.map((thematic) => (
diff --git a/src/styles/pages/article.module.scss b/src/styles/pages/article.module.scss
index a42c633..04acad9 100644
--- a/src/styles/pages/article.module.scss
+++ b/src/styles/pages/article.module.scss
@@ -31,6 +31,7 @@
@include mix.media("screen") {
@include mix.dimensions("md") {
width: min-content;
+ gap: var(--spacing-2xs);
}
}
}