aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--__tests__/jest/components/Branding.test.tsx24
-rw-r--r--__tests__/jest/components/Copyright.test.tsx13
-rw-r--r--__tests__/jest/components/Header.test.tsx12
-rw-r--r--src/components/Branding/Branding.module.scss169
-rw-r--r--src/components/Branding/Branding.tsx107
-rw-r--r--src/components/Branding/Logo/Logo.module.scss23
-rw-r--r--src/components/Branding/Logo/Logo.tsx32
-rw-r--r--src/components/Breadcrumb/Breadcrumb.module.scss29
-rw-r--r--src/components/Breadcrumb/Breadcrumb.tsx155
-rw-r--r--src/components/Buttons/Button/Button.tsx35
-rw-r--r--src/components/Buttons/ButtonHelp/ButtonHelp.module.scss52
-rw-r--r--src/components/Buttons/ButtonHelp/ButtonHelp.tsx42
-rw-r--r--src/components/Buttons/ButtonLink/ButtonLink.tsx30
-rw-r--r--src/components/Buttons/ButtonSubmit/ButtonSubmit.tsx22
-rw-r--r--src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx72
-rw-r--r--src/components/Buttons/Buttons.module.scss289
-rw-r--r--src/components/Buttons/index.tsx7
-rw-r--r--src/components/Comment/Comment.module.scss99
-rw-r--r--src/components/Comment/Comment.tsx200
-rw-r--r--src/components/CommentForm/CommentForm.module.scss25
-rw-r--r--src/components/CommentForm/CommentForm.tsx240
-rw-r--r--src/components/CommentsList/CommentsList.module.scss14
-rw-r--r--src/components/CommentsList/CommentsList.tsx69
-rw-r--r--src/components/ContactForm/ContactForm.module.scss21
-rw-r--r--src/components/ContactForm/ContactForm.tsx180
-rw-r--r--src/components/Copyright/Copyright.module.scss33
-rw-r--r--src/components/Copyright/Copyright.tsx17
-rw-r--r--src/components/Footer/Footer.module.scss90
-rw-r--r--src/components/Footer/Footer.tsx54
-rw-r--r--src/components/FooterNav/FooterNav.module.scss20
-rw-r--r--src/components/FooterNav/FooterNav.tsx45
-rw-r--r--src/components/FormElements/Field/Field.module.scss53
-rw-r--r--src/components/FormElements/Field/Field.tsx106
-rw-r--r--src/components/FormElements/Form/Form.module.scss37
-rw-r--r--src/components/FormElements/Form/Form.tsx27
-rw-r--r--src/components/FormElements/FormItem/FormItem.module.scss4
-rw-r--r--src/components/FormElements/FormItem/FormItem.tsx7
-rw-r--r--src/components/FormElements/Label/Label.module.scss22
-rw-r--r--src/components/FormElements/Label/Label.tsx24
-rw-r--r--src/components/FormElements/Toggle/Toggle.module.scss75
-rw-r--r--src/components/FormElements/Toggle/Toggle.tsx46
-rw-r--r--src/components/FormElements/index.tsx7
-rw-r--r--src/components/Header/Header.module.scss22
-rw-r--r--src/components/Header/Header.tsx16
-rw-r--r--src/components/Icons/Arrow/Arrow.module.scss7
-rw-r--r--src/components/Icons/Arrow/Arrow.tsx81
-rw-r--r--src/components/Icons/Blog/Blog.module.scss23
-rw-r--r--src/components/Icons/Blog/Blog.tsx62
-rw-r--r--src/components/Icons/CV/CV.module.scss54
-rw-r--r--src/components/Icons/CV/CV.tsx58
-rw-r--r--src/components/Icons/Close/Close.module.scss13
-rw-r--r--src/components/Icons/Close/Close.tsx22
-rw-r--r--src/components/Icons/Cog/Cog.module.scss10
-rw-r--r--src/components/Icons/Cog/Cog.tsx16
-rw-r--r--src/components/Icons/Contact/Contact.module.scss29
-rw-r--r--src/components/Icons/Contact/Contact.tsx54
-rw-r--r--src/components/Icons/Copyright/Copyright.module.scss7
-rw-r--r--src/components/Icons/Copyright/Copyright.tsx23
-rw-r--r--src/components/Icons/Hamburger/Hamburger.module.scss56
-rw-r--r--src/components/Icons/Hamburger/Hamburger.tsx10
-rw-r--r--src/components/Icons/Home/Home.module.scss42
-rw-r--r--src/components/Icons/Home/Home.tsx42
-rw-r--r--src/components/Icons/Moon/Moon.module.scss8
-rw-r--r--src/components/Icons/Moon/Moon.tsx25
-rw-r--r--src/components/Icons/Projects/Projects.module.scss40
-rw-r--r--src/components/Icons/Projects/Projects.tsx66
-rw-r--r--src/components/Icons/Search/Search.module.scss31
-rw-r--r--src/components/Icons/Search/Search.tsx30
-rw-r--r--src/components/Icons/Sun/Sun.module.scss8
-rw-r--r--src/components/Icons/Sun/Sun.tsx25
-rw-r--r--src/components/Icons/index.tsx29
-rw-r--r--src/components/Layouts/Layout.module.scss20
-rw-r--r--src/components/Layouts/Layout.tsx155
-rw-r--r--src/components/MDX/CodeBlock/CodeBlock.tsx115
-rw-r--r--src/components/MDX/Gallery/Gallery.module.scss32
-rw-r--r--src/components/MDX/Gallery/Gallery.tsx22
-rw-r--r--src/components/MDX/Link/Link.tsx23
-rw-r--r--src/components/MDX/ResponsiveImage/ResponsiveImage.module.scss50
-rw-r--r--src/components/MDX/ResponsiveImage/ResponsiveImage.tsx40
-rw-r--r--src/components/MDX/index.tsx6
-rw-r--r--src/components/Main/Main.module.scss7
-rw-r--r--src/components/Main/Main.tsx12
-rw-r--r--src/components/MainNav/MainNav.module.scss242
-rw-r--r--src/components/MainNav/MainNav.tsx155
-rw-r--r--src/components/MetaItems/Author/Author.tsx21
-rw-r--r--src/components/MetaItems/CommentsCount/CommentsCount.tsx43
-rw-r--r--src/components/MetaItems/Dates/Dates.tsx58
-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.tsx29
-rw-r--r--src/components/MetaItems/ReadingTime/ReadingTime.tsx59
-rw-r--r--src/components/MetaItems/Thematics/Thematics.tsx43
-rw-r--r--src/components/MetaItems/Topics/Topics.tsx37
-rw-r--r--src/components/MetaItems/Website/Website.tsx21
-rw-r--r--src/components/MetaItems/index.tsx21
-rw-r--r--src/components/Notice/Notice.module.scss28
-rw-r--r--src/components/Notice/Notice.tsx21
-rw-r--r--src/components/Pagination/Pagination.module.scss92
-rw-r--r--src/components/Pagination/Pagination.tsx136
-rw-r--r--src/components/PaginationCursor/PaginationCursor.module.scss43
-rw-r--r--src/components/PaginationCursor/PaginationCursor.tsx43
-rw-r--r--src/components/PostFooter/PostFooter.module.scss18
-rw-r--r--src/components/PostFooter/PostFooter.tsx53
-rw-r--r--src/components/PostHeader/PostHeader.module.scss83
-rw-r--r--src/components/PostHeader/PostHeader.tsx57
-rw-r--r--src/components/PostMeta/PostMeta.module.scss31
-rw-r--r--src/components/PostMeta/PostMeta.tsx67
-rw-r--r--src/components/PostPreview/PostPreview.module.scss105
-rw-r--r--src/components/PostPreview/PostPreview.tsx120
-rw-r--r--src/components/PostsList/PostsList.module.scss51
-rw-r--r--src/components/PostsList/PostsList.tsx110
-rw-r--r--src/components/ProjectPreview/ProjectPreview.module.scss98
-rw-r--r--src/components/ProjectPreview/ProjectPreview.tsx73
-rw-r--r--src/components/ProjectSummary/ProjectSummary.module.scss73
-rw-r--r--src/components/ProjectSummary/ProjectSummary.tsx178
-rw-r--r--src/components/ProjectsList/ProjectsList.module.scss25
-rw-r--r--src/components/ProjectsList/ProjectsList.tsx21
-rw-r--r--src/components/SearchForm/SearchForm.module.scss6
-rw-r--r--src/components/SearchForm/SearchForm.tsx70
-rw-r--r--src/components/Settings/AckeeSelect/AckeeSelect.module.scss6
-rw-r--r--src/components/Settings/AckeeSelect/AckeeSelect.tsx96
-rw-r--r--src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx50
-rw-r--r--src/components/Settings/ReduceMotion/ReduceMotion.tsx48
-rw-r--r--src/components/Settings/Settings.module.scss17
-rw-r--r--src/components/Settings/Settings.tsx30
-rw-r--r--src/components/Settings/ThemeToggle/ThemeToggle.tsx41
-rw-r--r--src/components/Sidebar/Sidebar.module.scss43
-rw-r--r--src/components/Sidebar/Sidebar.tsx40
-rw-r--r--src/components/Spinner/Spinner.module.scss48
-rw-r--r--src/components/Spinner/Spinner.tsx24
-rw-r--r--src/components/Toolbar/Toolbar.module.scss114
-rw-r--r--src/components/Toolbar/Toolbar.tsx162
-rw-r--r--src/components/Tooltip/Tooltip.module.scss120
-rw-r--r--src/components/Tooltip/Tooltip.tsx59
-rw-r--r--src/components/WidgetParts/ExpandableWidget/ExpandableWidget.module.scss146
-rw-r--r--src/components/WidgetParts/ExpandableWidget/ExpandableWidget.tsx61
-rw-r--r--src/components/WidgetParts/List/List.module.scss49
-rw-r--r--src/components/WidgetParts/List/List.tsx7
-rw-r--r--src/components/WidgetParts/OrderedList/OrderedList.module.scss66
-rw-r--r--src/components/WidgetParts/OrderedList/OrderedList.tsx7
-rw-r--r--src/components/WidgetParts/index.tsx5
-rw-r--r--src/components/Widgets/CVPreview/CVPreview.module.scss6
-rw-r--r--src/components/Widgets/CVPreview/CVPreview.tsx45
-rw-r--r--src/components/Widgets/RecentPosts/RecentPosts.module.scss109
-rw-r--r--src/components/Widgets/RecentPosts/RecentPosts.tsx78
-rw-r--r--src/components/Widgets/RelatedThematics/RelatedThematics.tsx41
-rw-r--r--src/components/Widgets/RelatedTopics/RelatedTopics.tsx41
-rw-r--r--src/components/Widgets/Sharing/Sharing.module.scss193
-rw-r--r--src/components/Widgets/Sharing/Sharing.tsx238
-rw-r--r--src/components/Widgets/SocialMedia/SocialMedia.module.scss59
-rw-r--r--src/components/Widgets/SocialMedia/SocialMedia.tsx113
-rw-r--r--src/components/Widgets/ThematicsList/ThematicsList.tsx76
-rw-r--r--src/components/Widgets/ToC/ToC.tsx55
-rw-r--r--src/components/Widgets/TopicsList/TopicsList.tsx76
-rw-r--r--src/components/Widgets/index.tsx21
-rw-r--r--src/pages/404.tsx85
-rw-r--r--src/pages/_app.tsx9
-rw-r--r--src/pages/article/[slug].tsx291
-rw-r--r--src/pages/blog/index.tsx269
-rw-r--r--src/pages/blog/page/[id].tsx205
-rw-r--r--src/pages/contact.tsx151
-rw-r--r--src/pages/cv.tsx181
-rw-r--r--src/pages/index.tsx241
-rw-r--r--src/pages/mentions-legales.tsx150
-rw-r--r--src/pages/projet/[slug].tsx186
-rw-r--r--src/pages/projets.tsx128
-rw-r--r--src/pages/recherche/index.tsx213
-rw-r--r--src/pages/sujet/[slug].tsx224
-rw-r--r--src/pages/thematique/[slug].tsx214
-rw-r--r--src/services/graphql/api.ts25
-rw-r--r--src/services/graphql/mutations.ts82
-rw-r--r--src/services/graphql/queries.ts535
-rw-r--r--src/ts/types/app.ts160
-rw-r--r--src/ts/types/articles.ts102
-rw-r--r--src/ts/types/blog.ts41
-rw-r--r--src/ts/types/comments.ts61
-rw-r--r--src/ts/types/contact.ts19
-rw-r--r--src/ts/types/cover.ts9
-rw-r--r--src/ts/types/nav.ts5
-rw-r--r--src/ts/types/prism.ts51
-rw-r--r--src/ts/types/repos.ts7
-rw-r--r--src/ts/types/seo.ts6
-rw-r--r--src/ts/types/taxonomies.ts114
-rw-r--r--src/utils/helpers/format.ts332
-rw-r--r--src/utils/helpers/i18n.ts2
-rw-r--r--src/utils/helpers/prism.ts34
-rw-r--r--src/utils/helpers/projects.ts86
-rw-r--r--src/utils/helpers/sort.ts21
-rw-r--r--src/utils/hooks/useGithubApi.tsx23
-rw-r--r--src/utils/hooks/useHeadingsTree.tsx104
-rw-r--r--src/utils/providers/ackee.tsx3
-rw-r--r--src/utils/providers/prism-theme.tsx2
192 files changed, 9 insertions, 13156 deletions
diff --git a/__tests__/jest/components/Branding.test.tsx b/__tests__/jest/components/Branding.test.tsx
deleted file mode 100644
index 14266be..0000000
--- a/__tests__/jest/components/Branding.test.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import Branding from '@components/Branding/Branding';
-import { render, screen } from '@test-utils';
-
-describe('Branding', () => {
- it('renders the title wrapped with an h1 element on homepage', () => {
- render(<Branding isHome={true} />);
- expect(
- screen.getByRole('heading', { level: 1, name: 'Armand Philippot' })
- ).toBeInTheDocument();
- });
-
- it('renders the title wrapped without an h1 element on other pages', () => {
- render(<Branding isHome={false} />);
- expect(
- screen.queryByRole('heading', { level: 1, name: 'Armand Philippot' })
- ).not.toBeInTheDocument();
- });
-
- it('renders the baseline', () => {
- render(<Branding isHome={false} />);
- // Currently, only French translation is returned.
- expect(screen.getByText('Intégrateur web')).toBeInTheDocument();
- });
-});
diff --git a/__tests__/jest/components/Copyright.test.tsx b/__tests__/jest/components/Copyright.test.tsx
deleted file mode 100644
index 08beb50..0000000
--- a/__tests__/jest/components/Copyright.test.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import Copyright from '@components/Copyright/Copyright';
-import { render, screen } from '@test-utils';
-
-describe('Copyright', () => {
- it('renders the Copyright component', () => {
- render(<Copyright />);
- });
-
- it('displays author name', () => {
- render(<Copyright />);
- expect(screen.getByText('Armand Philippot')).toBeInTheDocument();
- });
-});
diff --git a/__tests__/jest/components/Header.test.tsx b/__tests__/jest/components/Header.test.tsx
deleted file mode 100644
index 44c6871..0000000
--- a/__tests__/jest/components/Header.test.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import Header from '@components/Header/Header';
-import { render } from '@test-utils';
-
-// Toolbar uses forwardRef. Without mocking an error occurred.
-jest.mock('@components/Toolbar/Toolbar', () => 'div');
-
-describe('Header', () => {
- it('renders the Header component', () => {
- const { container } = render(<Header isHome={false} />);
- expect(container).toBeTruthy();
- });
-});
diff --git a/src/components/Branding/Branding.module.scss b/src/components/Branding/Branding.module.scss
deleted file mode 100644
index 2cd3b15..0000000
--- a/src/components/Branding/Branding.module.scss
+++ /dev/null
@@ -1,169 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- --logo-size: clamp(#{fun.convert-px(68)}, 18vw, #{fun.convert-px(100)});
-
- display: grid;
- grid-template-columns:
- var(--logo-size)
- minmax(0, 1fr);
- grid-template-rows: repeat(2, max-content);
- align-items: center;
- column-gap: var(--spacing-sm);
- padding: var(--spacing-sm) 0;
- text-shadow: fun.convert-px(2) fun.convert-px(2) 0 var(--color-fg-inverted);
-}
-
-.logo {
- --branding-logo-animation: none;
-
- grid-column: 1;
- grid-row: 1 / -1;
- justify-self: center;
- display: flex;
- place-content: center;
- width: var(--logo-size);
- height: var(--logo-size);
- position: relative;
- border-radius: 50%;
- transition: all 0.6s linear 0s;
- transform-style: preserve-3d;
- animation: var(--branding-logo-animation);
-
- &__front,
- &__back {
- width: 100%;
- height: 100%;
- padding: fun.convert-px(2);
- position: absolute;
- top: 0;
- left: 0;
- backface-visibility: hidden;
- background: var(--color-bg);
- border: fun.convert-px(2) solid var(--color-primary-dark);
- border-radius: 50%;
- transition: all 0.6s linear 0s;
- }
-
- &__front {
- box-shadow: fun.convert-px(1) fun.convert-px(2) fun.convert-px(1) 0
- var(--color-shadow-light),
- fun.convert-px(2) fun.convert-px(3) fun.convert-px(3) 0
- var(--color-shadow-light);
- }
-
- &__back {
- transform: rotateY(180deg);
- }
-
- img,
- svg {
- border-radius: 50%;
- }
-
- &:hover {
- transform: rotateY(180deg);
- }
-
- &:hover & {
- &__front {
- box-shadow: none;
- }
-
- &__back {
- box-shadow: fun.convert-px(1) fun.convert-px(2) fun.convert-px(1) 0
- var(--color-shadow-light),
- fun.convert-px(2) fun.convert-px(3) fun.convert-px(3) 0
- var(--color-shadow-light);
- }
- }
-}
-
-.name {
- --branding-name-animation: none;
-
- grid-column: 2;
- grid-row: 1;
- margin: 0;
- font-family: var(--font-family-secondary);
- font-size: clamp(var(--font-size-xl), 6vw, var(--font-size-2xl));
- font-weight: 500;
- letter-spacing: 0.01ex;
- position: relative;
- overflow: hidden;
-
- &::after {
- content: "|";
- display: block;
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- right: 0;
- background: var(--color-bg);
- color: var(--color-primary-darker);
- font-weight: 400;
- visibility: hidden;
- transform: translateX(100%);
- transform-origin: right;
- animation: var(--branding-name-animation);
- }
-}
-
-.job {
- --branding-job-animation: none;
-
- grid-column: 2;
- grid-row: 2;
- width: max-content;
- margin: 0;
- color: var(--color-fg-light);
- font-family: var(--font-family-secondary);
- font-size: var(--font-size-lg);
- font-weight: 500;
- position: relative;
- overflow: hidden;
-
- &::after {
- content: "|";
- display: block;
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- right: 0;
- background: var(--color-bg);
- color: var(--color-primary-darker);
- font-weight: 400;
- visibility: hidden;
- transform: translateX(100%);
- transform-origin: right;
- animation: var(--branding-job-animation);
- }
-}
-
-.link {
- background: linear-gradient(
- to top,
- var(--color-primary-light) fun.convert-px(5),
- transparent fun.convert-px(5)
- )
- left / 0 100% no-repeat;
- text-decoration: none;
- transition: all 0.6s ease-out 0s;
-
- &:hover,
- &:focus {
- background-size: 100% 100%;
- }
-
- &:focus {
- color: var(--color-primary-light);
- }
-
- &:active {
- background-size: 0 100%;
- color: var(--color-primary-dark);
- }
-}
diff --git a/src/components/Branding/Branding.tsx b/src/components/Branding/Branding.tsx
deleted file mode 100644
index b19116d..0000000
--- a/src/components/Branding/Branding.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import photo from '@assets/images/armand-philippot.jpg';
-import { settings } from '@utils/config';
-import Image from 'next/image';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { ReactElement, useEffect, useRef } from 'react';
-import { useIntl } from 'react-intl';
-import { Person, WithContext } from 'schema-dts';
-import styles from './Branding.module.scss';
-import Logo from './Logo/Logo';
-
-type BrandingReturn = ({ isHome }: { isHome: boolean }) => ReactElement;
-
-const Branding: BrandingReturn = ({ isHome = false }) => {
- const intl = useIntl();
- const { locale } = useRouter();
- const TitleTag = isHome ? 'h1' : 'p';
- const logoRef = useRef<HTMLDivElement>(null);
- const titleRef = useRef<HTMLHeadingElement | HTMLParagraphElement>(null);
- const jobRef = useRef<HTMLParagraphElement>(null);
-
- useEffect(() => {
- if (logoRef.current) {
- logoRef.current.style.setProperty(
- '--branding-logo-animation',
- 'flip-logo 9s ease-in 0s 1'
- );
- }
- }, []);
-
- useEffect(() => {
- if (titleRef.current) {
- titleRef.current.style.setProperty(
- '--branding-name-animation',
- 'blink 0.8s ease-in-out 0s 2, typing 4.3s linear 0s 1'
- );
- }
- }, []);
-
- useEffect(() => {
- if (jobRef.current) {
- jobRef.current.style.setProperty(
- '--branding-job-animation',
- 'hide-text 4.25s linear 0s 1, blink 0.8s ease-in-out 4.25s 2, typing 3.8s linear 4.25s 1'
- );
- }
- }, []);
-
- const schemaJsonLd: WithContext<Person> = {
- '@context': 'https://schema.org',
- '@type': 'Person',
- '@id': `${settings.url}/#branding`,
- name: settings.name,
- url: settings.url,
- jobTitle: locale?.startsWith('en')
- ? settings.baseline.en
- : settings.baseline.fr,
- image: photo.src,
- subjectOf: { '@id': `${settings.url}` },
- };
-
- return (
- <>
- <Script
- id="schema-branding"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <div id="branding" className={styles.wrapper}>
- <div className={styles.logo} ref={logoRef}>
- <div className={styles.logo__front}>
- <Image
- src={photo}
- alt={intl.formatMessage(
- {
- defaultMessage: '{brandingName} picture',
- description: 'Branding: branding name picture.',
- id: 'ILRLTq',
- },
- {
- brandingName: settings.name,
- }
- )}
- layout="responsive"
- />
- </div>
- <div className={styles.logo__back}>
- <Logo />
- </div>
- </div>
- <TitleTag ref={titleRef} className={styles.name}>
- <Link href="/">
- <a className={styles.link}>{settings.name}</a>
- </Link>
- </TitleTag>
- <p ref={jobRef} className={styles.job}>
- {locale?.startsWith('en')
- ? settings.baseline.en
- : settings.baseline.fr}
- </p>
- </div>
- </>
- );
-};
-
-export default Branding;
diff --git a/src/components/Branding/Logo/Logo.module.scss b/src/components/Branding/Logo/Logo.module.scss
deleted file mode 100644
index 3d62bf9..0000000
--- a/src/components/Branding/Logo/Logo.module.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-.wrapper {
- position: relative;
-}
-
-.bg-left {
- fill: var(--color-primary-light);
-}
-
-.bg-right {
- fill: var(--color-primary-dark);
-}
-
-.letter {
- fill: var(--color-fg-inverted);
- stroke: var(--color-primary-darker);
- stroke-width: 5;
-}
-
-.letter-shadow {
- fill: var(--color-shadow-darker);
- stroke: var(--color-shadow-darker);
- stroke-width: 5;
-}
diff --git a/src/components/Branding/Logo/Logo.tsx b/src/components/Branding/Logo/Logo.tsx
deleted file mode 100644
index 0623042..0000000
--- a/src/components/Branding/Logo/Logo.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import styles from './Logo.module.scss';
-
-const Logo = () => {
- return (
- <svg
- viewBox="0 0 512 512"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.wrapper}
- >
- <path className={styles['bg-left']} d="M 0,0 H 506 L 0,506 Z" />
- <path className={styles['bg-right']} d="M 512,512 H 6 L 512,6 Z" />
- <path
- className={styles['letter-shadow']}
- d="m 66.049088,353.26557 h 57.233082 l 15.4763,-40.00476 h 56.64908 l 15.76831,40.00476 h 57.2331 L 196.28357,165.21398 h -58.10911 z m 80.009522,-79.42552 21.02441,-55.18904 21.02439,55.18904 z"
- />
- <path
- className={styles['letter']}
- d="m 59.569539,346.78602 h 57.233081 l 15.4763,-40.00476 H 188.928 l 15.76831,40.00476 h 57.2331 L 189.80402,158.73443 h -58.10911 z m 80.009521,-79.42552 21.02441,-55.18904 21.02439,55.18904 z"
- />
- <path
- className={styles['letter-shadow']}
- d="m 288.84935,353.26557 h 54.89704 v -50.51696 h 40.88078 c 42.04881,0 68.91332,-28.61654 68.91332,-68.32931 0,-38.5447 -21.60841,-69.20532 -67.74528,-69.20532 h -96.94586 z m 54.89704,-92.56578 v -53.437 h 29.78458 c 16.35231,0 23.94446,10.51221 23.94446,27.15651 0,15.47629 -8.46817,26.28049 -25.40449,26.28049 z"
- />
- <path
- className={styles['letter']}
- d="m 282.3698,346.78602 h 54.89704 v -50.51696 h 40.88078 c 42.04881,0 68.91332,-28.61654 68.91332,-68.3293 0,-38.54471 -21.60841,-69.20533 -67.74528,-69.20533 H 282.3698 Z m 54.89704,-92.56578 v -53.437 h 29.78458 c 16.35231,0 23.94446,10.51221 23.94446,27.15652 0,15.47628 -8.46817,26.28048 -25.40449,26.28048 z"
- />
- </svg>
- );
-};
-
-export default Logo;
diff --git a/src/components/Breadcrumb/Breadcrumb.module.scss b/src/components/Breadcrumb/Breadcrumb.module.scss
deleted file mode 100644
index b8fadf8..0000000
--- a/src/components/Breadcrumb/Breadcrumb.module.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-@use "@styles/abstracts/placeholders";
-
-.wrapper {
- composes: grid from "@styles/layout/_grid.scss";
- padding: var(--spacing-md) 0;
-}
-
-.list {
- @extend %reset-ordered-list;
-
- grid-column: 2;
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- gap: var(--spacing-2xs);
- margin: 0;
- font-size: var(--font-size-sm);
-}
-
-.item {
- &:not(:last-of-type) {
- &::after {
- content: ">";
- margin-left: var(--spacing-2xs);
- }
- }
-}
diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx
deleted file mode 100644
index a7b945a..0000000
--- a/src/components/Breadcrumb/Breadcrumb.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import { settings } from '@utils/config';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { useIntl } from 'react-intl';
-import { BreadcrumbList, WithContext } from 'schema-dts';
-import styles from './Breadcrumb.module.scss';
-
-const Breadcrumb = ({ pageTitle }: { pageTitle: string }) => {
- const intl = useIntl();
- const router = useRouter();
-
- const isHome = router.pathname === '/';
- const isArticle = router.pathname.includes('/article/');
- const isProject = router.pathname.includes('/projet/');
- const isSubject = router.pathname.includes('/sujet/');
- const isThematic = router.pathname.includes('/thematique/');
-
- const getItems = () => {
- return (
- <>
- <li className={styles.item}>
- <Link href="/">
- <a>
- {intl.formatMessage({
- defaultMessage: 'Home',
- description: 'Breadcrumb: Home item',
- id: 'Enij19',
- })}
- </a>
- </Link>
- </li>
- {(isArticle || isThematic || isSubject) && (
- <>
- <li className={styles.item}>
- <Link href="/blog">
- <a>
- {intl.formatMessage({
- defaultMessage: 'Blog',
- description: 'Breadcrumb: Blog item',
- id: 'z0ic9c',
- })}
- </a>
- </Link>
- </li>
- </>
- )}
- {isProject && (
- <>
- <li className={styles.item}>
- <Link href="/projets">
- <a>
- {intl.formatMessage({
- defaultMessage: 'Projects',
- description: 'Breadcrumb: Projects item',
- id: 'Igx3qp',
- })}
- </a>
- </Link>
- </li>
- </>
- )}
- <li className="screen-reader-text">{pageTitle}</li>
- </>
- );
- };
-
- const getElementsSchema = () => {
- const items = [];
- const homepage: BreadcrumbList['itemListElement'] = {
- '@type': 'ListItem',
- position: 1,
- name: intl.formatMessage({
- defaultMessage: 'Home',
- description: 'Breadcrumb: Home item',
- id: 'Enij19',
- }),
- item: settings.url,
- };
-
- items.push(homepage);
-
- if (isArticle || isThematic || isSubject) {
- const blog: BreadcrumbList['itemListElement'] = {
- '@type': 'ListItem',
- position: 2,
- name: intl.formatMessage({
- defaultMessage: 'Blog',
- description: 'Breadcrumb: Blog item',
- id: 'z0ic9c',
- }),
- item: `${settings.url}/blog`,
- };
-
- items.push(blog);
- }
-
- if (isProject) {
- const blog: BreadcrumbList['itemListElement'] = {
- '@type': 'ListItem',
- position: 2,
- name: intl.formatMessage({
- defaultMessage: 'Projects',
- description: 'Breadcrumb: Projects item',
- id: 'Igx3qp',
- }),
- item: `${settings.url}/projets`,
- };
-
- items.push(blog);
- }
-
- const currentPage: BreadcrumbList['itemListElement'] = {
- '@type': 'ListItem',
- position: items.length + 1,
- name: pageTitle,
- item: `${settings.url}${router.asPath}`,
- };
-
- items.push(currentPage);
-
- return items;
- };
-
- const schemaJsonLd: WithContext<BreadcrumbList> = {
- '@context': 'https://schema.org',
- '@type': 'BreadcrumbList',
- '@id': `${settings.url}/#breadcrumb`,
- itemListElement: getElementsSchema(),
- };
-
- return (
- <>
- <Script
- id="schema-breadcrumb"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- {!isHome && (
- <nav id="breadcrumb" className={styles.wrapper}>
- <span className="screen-reader-text">
- {intl.formatMessage({
- defaultMessage: 'You are here:',
- description: 'Breadcrumb: You are here prefix',
- id: '16zl9Z',
- })}
- </span>
- <ol className={styles.list}>{getItems()}</ol>
- </nav>
- )}
- </>
- );
-};
-
-export default Breadcrumb;
diff --git a/src/components/Buttons/Button/Button.tsx b/src/components/Buttons/Button/Button.tsx
deleted file mode 100644
index ada23fe..0000000
--- a/src/components/Buttons/Button/Button.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { ButtonKind, ButtonPosition } from '@ts/types/app';
-import { ReactNode } from 'react';
-import styles from '../Buttons.module.scss';
-
-const Button = ({
- children,
- clickHandler,
- kind = 'secondary',
- position = 'left',
- spacing = false,
- isDisabled = false,
-}: {
- children: ReactNode;
- clickHandler: any;
- kind?: ButtonKind;
- position?: ButtonPosition;
- spacing?: boolean;
- isDisabled?: boolean;
-}) => {
- const spacingClass = spacing ? styles.spacing : '';
- const classes = `${styles.btn} ${styles[position]} ${styles[kind]} ${spacingClass}`;
-
- return (
- <button
- className={classes}
- type="button"
- disabled={isDisabled}
- onClick={clickHandler}
- >
- {children}
- </button>
- );
-};
-
-export default Button;
diff --git a/src/components/Buttons/ButtonHelp/ButtonHelp.module.scss b/src/components/Buttons/ButtonHelp/ButtonHelp.module.scss
deleted file mode 100644
index b2a05d7..0000000
--- a/src/components/Buttons/ButtonHelp/ButtonHelp.module.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.icon {
- color: var(--color-primary-dark);
- font-weight: 600;
-}
-
-.active {
- .icon {
- color: var(--color-fg-inverted);
- }
-}
-
-.wrapper {
- width: fun.convert-px(44);
- height: fun.convert-px(44);
- background: var(--color-bg);
- border: fun.convert-px(3) solid var(--color-primary-dark);
- border-radius: 50%;
- box-shadow: fun.convert-px(1) fun.convert-px(1) 0 0 var(--color-shadow);
- transition: all 0.3s ease-in-out 0s;
-
- @include mix.pointer("fine") {
- width: fun.convert-px(30);
- height: fun.convert-px(30);
- line-height: 1;
- }
-
- &:hover,
- &:focus {
- border-color: var(--color-primary-light);
- color: var(--color-primary-light);
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow-light),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-2)
- var(--color-shadow-light),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(5) fun.convert-px(-4)
- var(--color-shadow-light),
- fun.convert-px(7) fun.convert-px(10) fun.convert-px(12) fun.convert-px(-3)
- var(--color-shadow-light);
- transform: scale(1.1);
-
- .icon {
- transform: scale(1.1);
- }
- }
-
- &.active {
- background: var(--color-primary);
- }
-}
diff --git a/src/components/Buttons/ButtonHelp/ButtonHelp.tsx b/src/components/Buttons/ButtonHelp/ButtonHelp.tsx
deleted file mode 100644
index 2616a8f..0000000
--- a/src/components/Buttons/ButtonHelp/ButtonHelp.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { SetStateAction } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './ButtonHelp.module.scss';
-
-const ButtonHelp = ({
- showHelp,
- setShowHelp,
- title,
-}: {
- showHelp: boolean;
- setShowHelp: (value: SetStateAction<boolean>) => void;
- title?: string;
-}) => {
- const intl = useIntl();
-
- const handleClick = () => {
- setShowHelp((prev) => !prev);
- };
-
- const activeModifier = showHelp ? styles.active : '';
-
- return (
- <button
- onClick={handleClick}
- title={title}
- className={`${styles.wrapper} ${activeModifier}`}
- >
- <span className={styles.icon} aria-hidden="true">
- ?
- </span>
- <span className="screen-reader-text">
- {intl.formatMessage({
- defaultMessage: 'Help',
- description: 'ButtonHelp: screen reader text',
- id: 'oPf+XA',
- })}
- </span>
- </button>
- );
-};
-
-export default ButtonHelp;
diff --git a/src/components/Buttons/ButtonLink/ButtonLink.tsx b/src/components/Buttons/ButtonLink/ButtonLink.tsx
deleted file mode 100644
index 179c686..0000000
--- a/src/components/Buttons/ButtonLink/ButtonLink.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { ButtonPosition } from '@ts/types/app';
-import Link from 'next/link';
-import { ReactNode } from 'react';
-import styles from '../Buttons.module.scss';
-
-const ButtonLink = ({
- children,
- target,
- position = 'left',
- isExternal = false,
-}: {
- children: ReactNode;
- target: string;
- position?: ButtonPosition;
- isExternal?: boolean;
-}) => {
- const classes = `${styles.btn} ${styles[position]} ${styles.tertiary}`;
-
- return isExternal ? (
- <a className={classes} href={target}>
- {children}
- </a>
- ) : (
- <Link href={target}>
- <a className={classes}>{children}</a>
- </Link>
- );
-};
-
-export default ButtonLink;
diff --git a/src/components/Buttons/ButtonSubmit/ButtonSubmit.tsx b/src/components/Buttons/ButtonSubmit/ButtonSubmit.tsx
deleted file mode 100644
index 4725cad..0000000
--- a/src/components/Buttons/ButtonSubmit/ButtonSubmit.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { ReactNode } from 'react';
-import styles from '../Buttons.module.scss';
-
-type Modifier = 'search' | 'submit';
-
-const ButtonSubmit = ({
- children,
- modifier = 'submit',
-}: {
- children: ReactNode;
- modifier?: Modifier;
-}) => {
- const withModifier = modifier === 'search' ? styles.search : styles.primary;
-
- return (
- <button type="submit" className={`${styles.btn} ${withModifier}`}>
- {children}
- </button>
- );
-};
-
-export default ButtonSubmit;
diff --git a/src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx b/src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx
deleted file mode 100644
index 7ceb70d..0000000
--- a/src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { CloseIcon, CogIcon, SearchIcon } from '@components/Icons';
-import { ForwardedRef, forwardRef, SetStateAction } from 'react';
-import { useIntl } from 'react-intl';
-import styles from '../Buttons.module.scss';
-
-type ButtonType = 'search' | 'settings';
-
-const ButtonToolbar = (
- {
- type,
- isActivated,
- setIsActivated,
- }: {
- type: ButtonType;
- isActivated: boolean;
- setIsActivated: (value: SetStateAction<boolean>) => void;
- },
- ref: ForwardedRef<HTMLButtonElement>
-) => {
- const intl = useIntl();
- const ButtonIcon = () => (type === 'search' ? <SearchIcon /> : <CogIcon />);
- const btnClasses = isActivated
- ? `${styles.toolbar} ${styles['toolbar--activated']}`
- : styles.toolbar;
-
- return (
- <button
- ref={ref}
- className={btnClasses}
- type="button"
- onClick={() => setIsActivated(!isActivated)}
- >
- <span className={styles.icon}>
- <span className={styles.front}>
- <ButtonIcon />
- </span>
- <span className={styles.back}>
- <CloseIcon />
- </span>
- </span>
- {isActivated ? (
- <span className="screen-reader-text">
- {intl.formatMessage(
- {
- defaultMessage: 'Close {type}',
- description: 'ButtonToolbar: Close button',
- id: 'SWq8a4',
- },
- {
- type,
- }
- )}
- </span>
- ) : (
- <span className="screen-reader-text">
- {intl.formatMessage(
- {
- defaultMessage: 'Open {type}',
- description: 'ButtonToolbar: Open button',
- id: 'Z1eSIz',
- },
- {
- type,
- }
- )}
- </span>
- )}
- </button>
- );
-};
-
-export default forwardRef(ButtonToolbar);
diff --git a/src/components/Buttons/Buttons.module.scss b/src/components/Buttons/Buttons.module.scss
deleted file mode 100644
index 0ea9289..0000000
--- a/src/components/Buttons/Buttons.module.scss
+++ /dev/null
@@ -1,289 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-@use "@styles/abstracts/placeholders";
-
-.btn {
- display: block;
- border: none;
- font-size: var(--font-size-md);
-}
-
-.left {
- margin-right: auto;
-}
-
-.right {
- margin-left: auto;
-}
-
-.center {
- margin-left: auto;
- margin-right: auto;
-}
-
-.primary {
- margin: auto;
- padding: var(--spacing-2xs) var(--spacing-md);
- background: var(--color-primary);
- border: fun.convert-px(2) solid var(--color-bg);
- border-radius: fun.convert-px(5);
- box-shadow: 0 0 0 fun.convert-px(2) var(--color-primary),
- 0 0 0 fun.convert-px(3) var(--color-primary-darker),
- fun.convert-px(2) fun.convert-px(2) 0 fun.convert-px(3)
- var(--color-primary-dark);
- color: var(--color-fg-inverted);
- font-weight: 600;
- text-shadow: fun.convert-px(2) fun.convert-px(2) 0 var(--color-shadow);
- transition: all 0.25s ease-in-out 0s;
-
- &:hover,
- &:focus {
- background: var(--color-primary-light);
- box-shadow: 0 0 0 fun.convert-px(2) var(--color-primary-light),
- 0 0 0 fun.convert-px(3) var(--color-primary-darker),
- fun.convert-px(7) fun.convert-px(7) 0 fun.convert-px(2)
- var(--color-primary-dark);
- transform: translateX(#{fun.convert-px(-4)})
- translateY(#{fun.convert-px(-4)});
- }
-
- &:focus {
- text-decoration: underline solid var(--color-fg-inverted) fun.convert-px(2);
- }
-
- &:active {
- background: var(--color-primary-dark);
- box-shadow: 0 0 0 fun.convert-px(2) var(--color-primary),
- 0 0 0 fun.convert-px(3) var(--color-primary-darker),
- 0 0 0 0 var(--color-primary-dark);
- text-decoration: none;
- transform: translateX(#{fun.convert-px(4)}) translateY(#{fun.convert-px(4)});
- }
-}
-
-.secondary {
- padding: var(--spacing-2xs) var(--spacing-md);
- background: var(--color-bg);
- border: fun.convert-px(3) solid var(--color-primary);
- border-radius: fun.convert-px(5);
- box-shadow: fun.convert-px(2) fun.convert-px(2) 0 0 var(--color-bg),
- fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-primary-dark),
- fun.convert-px(5) fun.convert-px(5) 0 0 var(--color-bg),
- fun.convert-px(6) fun.convert-px(6) 0 0 var(--color-primary-dark);
- color: var(--color-primary);
- font-weight: 600;
- transition: all 0.35s ease-out 0s;
-
- &:disabled {
- color: var(--color-fg-light);
- border-color: var(--color-border-dark);
- box-shadow: fun.convert-px(2) fun.convert-px(2) 0 0 var(--color-bg),
- fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-primary-darker),
- fun.convert-px(5) fun.convert-px(5) 0 0 var(--color-bg),
- fun.convert-px(6) fun.convert-px(6) 0 0 var(--color-primary-darker);
- cursor: wait;
- }
-
- &:not(:disabled) {
- &:hover,
- &:focus {
- transform: translateX(#{fun.convert-px(-3)})
- translateY(#{fun.convert-px(-5)});
- border-color: var(--color-primary-light);
- box-shadow: fun.convert-px(2) fun.convert-px(3) 0 0 var(--color-bg),
- fun.convert-px(4) fun.convert-px(5) 0 0 var(--color-primary),
- fun.convert-px(6) fun.convert-px(8) 0 0 var(--color-bg),
- fun.convert-px(8) fun.convert-px(10) 0 0 var(--color-primary),
- fun.convert-px(10) fun.convert-px(12) fun.convert-px(1) 0
- var(--color-shadow-light),
- fun.convert-px(10) fun.convert-px(12) fun.convert-px(5)
- fun.convert-px(1) var(--color-shadow-light);
- color: var(--color-primary-light);
- }
-
- &:focus {
- text-decoration: underline var(--color-primary) fun.convert-px(2);
- }
-
- &:active {
- text-decoration: none;
- transform: translateX(#{fun.convert-px(5)})
- translateY(#{fun.convert-px(6)});
- box-shadow: 0 0 0 0 var(--color-shadow);
- }
- }
-}
-
-.tertiary {
- display: flex;
- flex-flow: row wrap;
- place-items: center;
- gap: var(--spacing-2xs);
- width: max-content;
- padding: var(--spacing-2xs) var(--spacing-sm);
- position: relative;
- background: var(--color-bg);
- border: fun.convert-px(3) solid var(--color-primary);
- border-radius: fun.convert-px(5);
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-2)
- var(--color-shadow),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(5) fun.convert-px(-4)
- var(--color-shadow);
- color: var(--color-primary);
- font-weight: 600;
- text-decoration: underline transparent 0;
- transition: all 0.3s ease-in-out 0s, text-decoration 0.35s ease-in-out 0s;
-
- &:hover,
- &:focus {
- border-color: var(--color-primary-light);
- color: var(--color-primary-light);
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow-light),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-2)
- var(--color-shadow-light),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(5) fun.convert-px(-4)
- var(--color-shadow-light),
- fun.convert-px(7) fun.convert-px(10) fun.convert-px(12) fun.convert-px(-3)
- var(--color-shadow-light);
- transform: scale(1.1);
- }
-
- &:focus {
- text-decoration: underline var(--color-primary-light) fun.convert-px(3);
- }
-
- &:active {
- border-color: var(--color-primary-dark);
- color: var(--color-primary-dark);
- box-shadow: 0 0 0 0 var(--color-shadow);
- text-decoration: underline transparent 0;
- transform: scale(0.94);
- }
-}
-
-:global {
- [data-theme="dark"] {
- :local {
- .tertiary {
- img[src*="png"] {
- background: none;
- }
- }
- }
- }
-}
-
-.toolbar {
- --draw-border-thickness: #{fun.convert-px(4)};
- --icon-size: 100%;
-
- display: flex;
- flex-flow: row nowrap;
- place-items: center;
- width: var(--btn-size, 100%);
- height: var(--btn-size, 100%);
- padding: var(--spacing-2xs);
- background: none;
- border: none;
- font-size: var(--font-size-md);
-
- &:hover,
- &:focus {
- --draw-border-color1: var(--color-primary-light);
- --draw-border-color2: var(--color-primary-lighter);
-
- @extend %draw-borders;
- }
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- border-radius: 8%;
- }
- }
-}
-
-.icon {
- display: block;
- width: 100%;
- height: 100%;
- position: relative;
- transform-style: preserve-3d;
- transform: translate3d(0, 0, 0);
- transition: all 0.5s ease-in-out 0s;
-}
-
-.front,
-.back {
- display: flex;
- place-content: center;
- width: var(--icon-size);
- height: var(--icon-size);
- position: absolute;
- top: 0;
- right: 0;
- transition: all 0.6s ease-in 0s;
- backface-visibility: hidden;
-}
-
-.front {
- transform: scale(1);
- z-index: 20;
-}
-
-.back {
- transform: scale(0.2) rotateY(180deg);
- z-index: 10;
-}
-
-.toolbar--activated {
- .icon {
- transform: rotateY(180deg);
- }
-
- .front {
- transform: scale(0.2);
- }
-
- .back {
- transform: scale(1) rotateY(180deg);
- }
-}
-
-.search {
- background: transparent;
- margin-left: calc(var(--btn-size) * -1);
- z-index: 5;
- transition: all 0.3s ease-in-out 0s;
-
- svg {
- transform: scale(0.85);
- transition: all 0.3s ease-in-out 0s;
- }
-
- &:hover,
- &:focus {
- svg {
- transform: scale(0.85) rotate(20deg) translateY(#{fun.convert-px(3)});
- }
- }
-
- &:focus {
- outline: var(--color-primary-light) solid fun.convert-px(3);
- }
-
- &:active {
- outline: none;
-
- svg {
- transform: scale(0.7);
- }
- }
-}
-
-.spacing {
- margin-top: var(--spacing-md);
- margin-bottom: var(--spacing-md);
-}
diff --git a/src/components/Buttons/index.tsx b/src/components/Buttons/index.tsx
deleted file mode 100644
index 9b4b756..0000000
--- a/src/components/Buttons/index.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import Button from './Button/Button';
-import ButtonLink from './ButtonLink/ButtonLink';
-import ButtonToolbar from './ButtonToolbar/ButtonToolbar';
-import ButtonSubmit from './ButtonSubmit/ButtonSubmit';
-import ButtonHelp from './ButtonHelp/ButtonHelp';
-
-export { Button, ButtonHelp, ButtonLink, ButtonToolbar, ButtonSubmit };
diff --git a/src/components/Comment/Comment.module.scss b/src/components/Comment/Comment.module.scss
deleted file mode 100644
index dd52db2..0000000
--- a/src/components/Comment/Comment.module.scss
+++ /dev/null
@@ -1,99 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.item {
- margin: var(--spacing-sm) 0;
-}
-
-.wrapper {
- background: var(--color-bg);
- border: fun.convert-px(1) solid var(--color-border);
- box-shadow: fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow-light),
- fun.convert-px(4) fun.convert-px(4) fun.convert-px(3) fun.convert-px(-2)
- var(--color-shadow);
- padding: var(--spacing-md);
- position: relative;
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- display: grid;
- grid-template-columns: minmax(#{fun.convert-px(150)}, 1fr) minmax(0, 85ch);
- column-gap: var(--spacing-lg);
- }
- }
-}
-
-.header {
- display: flex;
- flex-flow: column wrap;
- align-items: center;
- row-gap: var(--spacing-sm);
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- grid-row: 1 / 4;
- }
- }
-}
-
-.avatar {
- width: fun.convert-px(85);
- height: fun.convert-px(85);
- margin: 0 auto;
- border-radius: fun.convert-px(3);
- box-shadow: 0 0 0 fun.convert-px(1) var(--color-shadow-light),
- fun.convert-px(2) fun.convert-px(2) 0 fun.convert-px(1) var(--color-shadow);
- position: relative;
-
- img {
- border-radius: fun.convert-px(3);
- }
-}
-
-.author {
- color: var(--color-primary-darker);
- font-weight: 600;
- text-align: center;
-}
-
-.date {
- color: var(--color-fg-secondary);
- font-size: var(--font-size-sm);
- display: flex;
- flex-flow: row wrap;
- column-gap: var(--spacing-2xs);
- justify-content: center;
- margin: var(--spacing-md) 0;
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- justify-content: left;
- margin: 0 0 var(--spacing-md);
- }
- }
-}
-
-.body {
- overflow-wrap: break-word;
-}
-
-.footer {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- padding: var(--spacing-md) 0 0;
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- padding: var(--spacing-sm) 0 0;
- }
- }
-
- button {
- margin: 0;
- }
-}
-
-.list {
- padding-left: var(--spacing-md);
-}
diff --git a/src/components/Comment/Comment.tsx b/src/components/Comment/Comment.tsx
deleted file mode 100644
index 355363b..0000000
--- a/src/components/Comment/Comment.tsx
+++ /dev/null
@@ -1,200 +0,0 @@
-import { Button } from '@components/Buttons';
-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 { 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,
- isNested = false,
-}: {
- articleId: number;
- comment: CommentData;
- isNested?: boolean;
-}) => {
- const intl = useIntl();
- const router = useRouter();
- const locale = router.locale ? router.locale : settings.locales.defaultLocale;
- const [shouldOpenForm, setShouldOpenForm] = useState<boolean>(false);
-
- const getCommentAuthor = () => {
- return comment.author.url ? (
- <Link href={comment.author.url}>
- <a className={styles.author}>{comment.author.name}</a>
- </Link>
- ) : (
- <span className={styles.author}>{comment.author.name}</span>
- );
- };
-
- const getLocaleDate = () => {
- const date = getFormattedDate(comment.date, locale);
- const time = new Date(comment.date)
- .toLocaleTimeString(locale, {
- hour: 'numeric',
- minute: 'numeric',
- })
- .replace(':', 'h');
- return intl.formatMessage(
- {
- defaultMessage: '{date} at {time}',
- description: 'Comment: publication date',
- id: 'CT3ydM',
- },
- {
- date,
- time,
- }
- );
- };
-
- const getApprovedComment = () => {
- return (
- <>
- <article
- className={styles.wrapper}
- id={`comment-${comment.databaseId}`}
- >
- <header className={styles.header}>
- {comment.author.gravatarUrl && (
- <div className={styles.avatar}>
- <Image
- src={comment.author.gravatarUrl}
- alt={comment.author.name}
- layout="fill"
- />
- </div>
- )}
- {getCommentAuthor()}
- </header>
- <dl className={styles.date}>
- <dt>
- {intl.formatMessage({
- defaultMessage: 'Published on:',
- description: 'Comment: publication date label',
- id: 'soj7do',
- })}
- </dt>
- <dd>
- <time dateTime={comment.date}>
- <Link href={`#comment-${comment.databaseId}`}>
- <a>{getLocaleDate()}</a>
- </Link>
- </time>
- </dd>
- </dl>
- <div
- className={styles.body}
- dangerouslySetInnerHTML={{ __html: comment.content }}
- ></div>
- {!isNested && (
- <footer className={styles.footer}>
- <Button clickHandler={() => setShouldOpenForm((prev) => !prev)}>
- {shouldOpenForm
- ? intl.formatMessage({
- defaultMessage: 'Cancel reply',
- description: 'Comment: reply button',
- id: 'e1Forh',
- })
- : intl.formatMessage({
- defaultMessage: 'Reply',
- description: 'Comment: reply button',
- id: 'hzHuCc',
- })}
- </Button>
- </footer>
- )}
- </article>
- {shouldOpenForm && (
- <DynamicCommentForm
- articleId={articleId}
- parentId={comment.databaseId}
- />
- )}
- {comment.replies.length > 0 && (
- <ol className={styles.list}>
- {comment.replies.map((reply) => {
- return (
- <Comment
- articleId={articleId}
- key={reply.databaseId}
- comment={reply}
- isNested={true}
- />
- );
- })}
- </ol>
- )}
- </>
- );
- };
-
- const getCommentStatus = () => {
- return (
- <p>
- {intl.formatMessage({
- defaultMessage: 'This comment is awaiting moderation.',
- description: 'Comment: awaiting moderation message',
- id: 'rXeTkM',
- })}
- </p>
- );
- };
-
- const schemaJsonLd: WithContext<CommentSchema> = {
- '@context': 'https://schema.org',
- '@id': `${settings.url}/#comment-${comment.databaseId}`,
- '@type': 'Comment',
- parentItem: isNested
- ? { '@id': `${settings.url}/#comment-${comment.parentDatabaseId}` }
- : undefined,
- about: { '@type': 'Article', '@id': `${settings.url}/#article` },
- author: {
- '@type': 'Person',
- name: comment.author.name,
- image: comment.author.gravatarUrl,
- url: comment.author.url,
- },
- creator: {
- '@type': 'Person',
- name: comment.author.name,
- image: comment.author.gravatarUrl,
- url: comment.author.url,
- },
- dateCreated: comment.date,
- datePublished: comment.date,
- text: comment.content,
- };
-
- return (
- <>
- <Script
- id="schema-comments"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <li className={styles.item}>
- {comment.approved ? getApprovedComment() : getCommentStatus()}
- </li>
- </>
- );
-};
-
-export default Comment;
diff --git a/src/components/CommentForm/CommentForm.module.scss b/src/components/CommentForm/CommentForm.module.scss
deleted file mode 100644
index d19a1ef..0000000
--- a/src/components/CommentForm/CommentForm.module.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.wrapper {
- width: min(calc(100vw - (var(--spacing-md) * 2)), fun.convert-px(500));
- margin: auto;
-
- &--reply {
- width: 100%;
- margin-top: var(--spacing-sm);
- padding: var(--spacing-md);
- position: relative;
- background: var(--color-bg);
- border: fun.convert-px(1) solid var(--color-border);
- box-shadow: fun.convert-px(3) fun.convert-px(3) 0 0
- var(--color-shadow-light),
- fun.convert-px(4) fun.convert-px(4) fun.convert-px(3) fun.convert-px(-2)
- var(--color-shadow);
- }
-}
-
-.title {
- width: max-content;
- margin-left: auto;
- margin-right: auto;
-}
diff --git a/src/components/CommentForm/CommentForm.tsx b/src/components/CommentForm/CommentForm.tsx
deleted file mode 100644
index 128dc58..0000000
--- a/src/components/CommentForm/CommentForm.tsx
+++ /dev/null
@@ -1,240 +0,0 @@
-import { ButtonSubmit } from '@components/Buttons';
-import { Field, Form, FormItem, Label } from '@components/FormElements';
-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 { 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;
-}) => {
- const intl = useIntl();
- const [name, setName] = useState<string>('');
- const [email, setEmail] = useState<string>('');
- const [website, setWebsite] = useState<string>('');
- const [comment, setComment] = useState<string>('');
- 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('');
- setEmail('');
- setWebsite('');
- setComment('');
- setIsSubmitting(false);
- };
-
- const isEmptyString = (value: string): boolean => value.trim() === '';
- const areRequiredFieldsSet = (): boolean =>
- !isEmptyString(name) && !isEmptyString(email) && !isEmptyString(comment);
-
- const sendComment = async () => {
- const data = {
- author: name,
- authorEmail: email,
- authorUrl: website,
- content: comment,
- parent: parentId,
- commentOn: articleId,
- mutationId: 'createComment',
- };
-
- const createdComment = await createComment(data);
-
- if (createdComment.success) {
- resetForm();
- setNoticeType('success');
- if (createdComment.comment?.approved) {
- setNotice(
- intl.formatMessage({
- defaultMessage: 'Thanks for your comment!',
- description: 'CommentForm: success notice',
- id: 'AVUUgG',
- })
- );
- } else {
- setNotice(
- intl.formatMessage({
- defaultMessage:
- 'Thanks for your comment! It is now awaiting moderation.',
- description: 'CommentForm: success notice but awaiting moderation',
- id: 'Ul2NIl',
- })
- );
- }
-
- setTimeout(() => {
- setNotice(undefined);
- }, 10000);
- } else {
- setNoticeType('error');
- setNotice(
- intl.formatMessage({
- defaultMessage:
- 'An unexpected error happened. Comment cannot be submitted.',
- description: 'CommentForm: error notice',
- id: 'cjK9Ad',
- })
- );
- }
- };
-
- const submitHandler = async (e: SubmitEvent) => {
- e.preventDefault();
- setNotice(undefined);
- setIsSubmitting(true);
-
- if (areRequiredFieldsSet()) {
- sendComment();
- } else {
- setIsSubmitting(false);
- setNoticeType('warning');
- setNotice(
- intl.formatMessage({
- defaultMessage:
- 'Some required fields are empty. Comment cannot be submitted.',
- description: 'CommentForm: missing required fields',
- id: 'Rle+UK',
- })
- );
- }
- };
-
- const isReply = parentId !== 0;
- const wrapperClasses = `${styles.wrapper} ${
- isReply ? styles['wrapper--reply'] : ''
- }`;
-
- const getLabel = (
- body: string,
- htmlFor: string,
- required: boolean = false
- ) => {
- return <Label body={body} htmlFor={htmlFor} required={required} />;
- };
-
- const nameLabelBody = intl.formatMessage({
- defaultMessage: 'Name',
- description: 'CommentForm: Name field label',
- id: 'F7QxJH',
- });
-
- const emailLabelBody = intl.formatMessage({
- defaultMessage: 'Email',
- description: 'CommentForm: Email field label',
- id: 'Oim3rQ',
- });
-
- const websiteLabelBody = intl.formatMessage({
- defaultMessage: 'Website',
- description: 'CommentForm: Website field label',
- id: 'jN+dY5',
- });
-
- const commentLabelBody = intl.formatMessage({
- defaultMessage: 'Comment',
- description: 'CommentForm: Comment field label',
- id: 'J4nhm4',
- });
-
- return (
- <div className={wrapperClasses}>
- <h2 className={styles.title}>
- {intl.formatMessage({
- defaultMessage: 'Leave a comment',
- description: 'CommentForm: Form title',
- id: '+aHn7j',
- })}
- </h2>
- <Form
- submitHandler={submitHandler}
- kind={isReply ? 'centered' : undefined}
- >
- <FormItem>
- <Field
- id="commenter-name"
- name="commenter-name"
- label={getLabel(nameLabelBody, 'commenter-name', true)}
- value={name}
- setValue={setName}
- required={true}
- ref={nameFieldRef}
- />
- </FormItem>
- <FormItem>
- <Field
- id="commenter-email"
- name="commenter-email"
- kind="email"
- label={getLabel(emailLabelBody, 'commenter-email', true)}
- value={email}
- setValue={setEmail}
- required={true}
- />
- </FormItem>
- <FormItem>
- <Field
- id="commenter-website"
- name="commenter-website"
- label={getLabel(websiteLabelBody, 'commenter-website')}
- value={website}
- setValue={setWebsite}
- />
- </FormItem>
- <FormItem>
- <Field
- id="commenter-comment"
- name="commenter-comment"
- kind="textarea"
- label={getLabel(commentLabelBody, 'commenter-comment', true)}
- value={comment}
- setValue={setComment}
- required={true}
- />
- </FormItem>
- <FormItem>
- <noscript>
- {intl.formatMessage({
- defaultMessage: 'Javascript is required to post a comment.',
- description: 'CommentForm: noscript tag',
- id: 'g1cFCa',
- })}
- </noscript>
- <ButtonSubmit>
- {intl.formatMessage({
- defaultMessage: 'Send',
- description: 'CommentForm: Send button',
- id: 'WGFOmA',
- })}
- </ButtonSubmit>
- </FormItem>
- </Form>
- {isSubmitting && (
- <Spinner
- message={intl.formatMessage({
- defaultMessage: 'Submitting...',
- description: 'CommentForm: submitting message',
- id: 'HEJ3Gv',
- })}
- />
- )}
- {notice && <Notice type={noticeType}>{notice}</Notice>}
- </div>
- );
-};
-
-export default CommentForm;
diff --git a/src/components/CommentsList/CommentsList.module.scss b/src/components/CommentsList/CommentsList.module.scss
deleted file mode 100644
index 4971b15..0000000
--- a/src/components/CommentsList/CommentsList.module.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-@use "@styles/abstracts/placeholders";
-
-.title,
-.no-comments {
- width: max-content;
- margin-left: auto;
- margin-right: auto;
-}
-
-.list {
- @extend %reset-ordered-list;
-
- margin-bottom: var(--spacing-lg);
-}
diff --git a/src/components/CommentsList/CommentsList.tsx b/src/components/CommentsList/CommentsList.tsx
deleted file mode 100644
index 0eaac17..0000000
--- a/src/components/CommentsList/CommentsList.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import Comment from '@components/Comment/Comment';
-import Spinner from '@components/Spinner/Spinner';
-import { getCommentsByPostId } from '@services/graphql/queries';
-import { Comment as CommentData } from '@ts/types/comments';
-import { useIntl } from 'react-intl';
-import useSWR from 'swr';
-import styles from './CommentsList.module.scss';
-
-const CommentsList = ({
- articleId,
- comments,
-}: {
- articleId: number;
- comments: CommentData[];
-}) => {
- const intl = useIntl();
- const { data, error } = useSWR<CommentData[]>(
- '/api/comments',
- () => getCommentsByPostId(articleId),
- { fallbackData: comments }
- );
-
- const getCommentsList = () => {
- if (error) {
- return intl.formatMessage({
- defaultMessage: 'Failed to load.',
- description: 'CommentsList: failed to load',
- id: 'Zlkww3',
- });
- }
-
- if (!data) return <Spinner />;
-
- return data.map((comment) => {
- return (
- <Comment
- key={comment.databaseId}
- articleId={articleId}
- comment={comment}
- />
- );
- });
- };
-
- return (
- <>
- <h2 className={styles.title}>
- {intl.formatMessage({
- defaultMessage: 'Comments',
- description: 'CommentsList: Comments section title',
- id: 'Ns8CFb',
- })}
- </h2>
- {data && data.length > 0 ? (
- <ol className={styles.list}>{getCommentsList()}</ol>
- ) : (
- <p className={styles['no-comments']}>
- {intl.formatMessage({
- defaultMessage: 'No comments yet.',
- description: 'CommentsList: No comment message',
- id: 'e9L59q',
- })}
- </p>
- )}
- </>
- );
-};
-
-export default CommentsList;
diff --git a/src/components/ContactForm/ContactForm.module.scss b/src/components/ContactForm/ContactForm.module.scss
deleted file mode 100644
index 3f0e861..0000000
--- a/src/components/ContactForm/ContactForm.module.scss
+++ /dev/null
@@ -1,21 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.status {
- max-width: max-content;
- margin: var(--spacing-md) 0;
- padding: var(--spacing-sm);
- border: fun.convert-px(3) solid var(--color-border-light);
- border-radius: fun.convert-px(5);
-
- &--error {
- border-color: var(--color-token-red);
- }
-
- &--success {
- border-color: var(--color-token-green);
- }
-
- &--warning {
- border-color: var(--color-token-orange);
- }
-}
diff --git a/src/components/ContactForm/ContactForm.tsx b/src/components/ContactForm/ContactForm.tsx
deleted file mode 100644
index 5af6982..0000000
--- a/src/components/ContactForm/ContactForm.tsx
+++ /dev/null
@@ -1,180 +0,0 @@
-import { ButtonSubmit } from '@components/Buttons';
-import { Field, Form, FormItem, Label } from '@components/FormElements';
-import { sendMail } from '@services/graphql/mutations';
-import { settings } from '@utils/config';
-import { FormEvent, useState } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './ContactForm.module.scss';
-
-type Status = 'success' | 'error' | 'warning';
-
-const ContactForm = () => {
- const intl = useIntl();
- const [name, setName] = useState('');
- const [email, setEmail] = useState('');
- const [subject, setSubject] = useState('');
- const [message, setMessage] = useState('');
- const [status, setStatus] = useState<Status>();
- const [statusMessage, setStatusMessage] = useState<string>('');
-
- const resetForm = () => {
- setName('');
- setEmail('');
- setSubject('');
- setMessage('');
- };
-
- const submitHandler = async (e: FormEvent) => {
- e.preventDefault();
-
- if (!name || !email || !message) {
- setStatus('warning');
- setStatusMessage(
- intl.formatMessage({
- defaultMessage:
- 'Warning: mail not sent. Some required fields are empty.',
- description: 'ContactForm: missing fields message.',
- id: 'WpycgB',
- })
- );
- return;
- }
-
- const messageHTML = message.replace(/\r?\n/g, '<br />');
- const body = `Message received from ${name} <${email}> on ${settings.url}.<br /><br />${messageHTML}`;
- const replyTo = `${name} <${email}>`;
- const data = {
- body,
- mutationId: 'contact',
- replyTo,
- subject,
- };
- const mail = await sendMail(data);
-
- if (mail.sent) {
- setStatus('success');
- setStatusMessage(
- intl.formatMessage({
- defaultMessage:
- 'Thanks. Your message was successfully sent. I will answer it as soon as possible.',
- description: 'ContactForm: success message',
- id: 'gQKeF+',
- })
- );
- resetForm();
- } else {
- const errorPrefix = intl.formatMessage({
- defaultMessage: 'An error occurred:',
- description: 'ContactForm: error message',
- id: 'pTxT7N',
- });
- const error = `${errorPrefix} ${mail.message}`;
- setStatus('error');
- setStatusMessage(error);
- }
- };
-
- const getStatus = () => {
- if (!status) return <></>;
-
- const statusModifier = `status--${status}`;
-
- return (
- <p className={`${styles.status} ${styles[statusModifier]}`}>
- {statusMessage}
- </p>
- );
- };
-
- const getLabel = (
- body: string,
- htmlFor: string,
- required: boolean = false
- ) => {
- return <Label body={body} htmlFor={htmlFor} required={required} />;
- };
-
- const nameLabelBody = intl.formatMessage({
- defaultMessage: 'Name',
- description: 'ContactForm: name field label',
- id: '6ibqFS',
- });
-
- const emailLabelBody = intl.formatMessage({
- defaultMessage: 'Email',
- description: 'ContactForm: email field label',
- id: 'Vuryko',
- });
-
- const subjectLabelBody = intl.formatMessage({
- defaultMessage: 'Subject',
- description: 'ContactForm: subject field label',
- id: 'uMURuJ',
- });
-
- const messageLabelBody = intl.formatMessage({
- defaultMessage: 'Message',
- description: 'ContactForm: message field label',
- id: '0zBQpa',
- });
-
- return (
- <>
- <Form submitHandler={submitHandler}>
- <FormItem>
- <Field
- id="contact-name"
- name="name"
- value={name}
- setValue={setName}
- required={true}
- label={getLabel(nameLabelBody, 'contact-name', true)}
- />
- </FormItem>
- <FormItem>
- <Field
- id="contact-email"
- kind="email"
- name="email"
- value={email}
- setValue={setEmail}
- required={true}
- label={getLabel(emailLabelBody, 'contact-email', true)}
- />
- </FormItem>
- <FormItem>
- <Field
- id="contact-subject"
- name="subject"
- value={subject}
- setValue={setSubject}
- label={getLabel(subjectLabelBody, 'contact-subject')}
- />
- </FormItem>
- <FormItem>
- <Field
- id="contact-message"
- kind="textarea"
- name="message"
- value={message}
- setValue={setMessage}
- required={true}
- label={getLabel(messageLabelBody, 'contact-message', true)}
- />
- </FormItem>
- <FormItem>
- <ButtonSubmit>
- {intl.formatMessage({
- defaultMessage: 'Send',
- description: 'ContactForm: send button text',
- id: 'X7n7N2',
- })}
- </ButtonSubmit>
- </FormItem>
- </Form>
- {getStatus()}
- </>
- );
-};
-
-export default ContactForm;
diff --git a/src/components/Copyright/Copyright.module.scss b/src/components/Copyright/Copyright.module.scss
deleted file mode 100644
index 35445b2..0000000
--- a/src/components/Copyright/Copyright.module.scss
+++ /dev/null
@@ -1,33 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- --icon-size: #{fun.convert-px(70)};
-
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- place-content: center;
- gap: var(--spacing-2xs);
- margin: 0;
- font-family: var(--font-family-secondary);
- font-size: var(--font-size-md);
- text-align: center;
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- place-content: start;
- text-align: left;
- }
- }
-}
-
-.name {
- flex: 1 0 100%;
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- flex: auto;
- }
- }
-}
diff --git a/src/components/Copyright/Copyright.tsx b/src/components/Copyright/Copyright.tsx
deleted file mode 100644
index d2de2e9..0000000
--- a/src/components/Copyright/Copyright.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { CopyrightIcon } from '@components/Icons';
-import { settings } from '@utils/config';
-import styles from './Copyright.module.scss';
-
-const Copyright = () => {
- return (
- <p className={styles.wrapper}>
- <span className={styles.name}>{settings.name}</span>
- <CopyrightIcon />
- <span>
- {settings.copyright.startYear} - {settings.copyright.endYear}
- </span>
- </p>
- );
-};
-
-export default Copyright;
diff --git a/src/components/Footer/Footer.module.scss b/src/components/Footer/Footer.module.scss
deleted file mode 100644
index 1d156f8..0000000
--- a/src/components/Footer/Footer.module.scss
+++ /dev/null
@@ -1,90 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- display: flex;
- flex-flow: column wrap;
- gap: var(--spacing-xs);
- place-items: center;
- place-content: center;
- padding: var(--spacing-md) 0 calc(var(--toolbar-size) + var(--spacing-md));
- border-top: fun.convert-px(3) solid var(--color-border-light);
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- flex-flow: row wrap;
- font-size: var(--font-size-sm);
- }
- }
-}
-
-.back-to-top {
- --button-size: #{fun.convert-px(55)};
- --icon-size: #{fun.convert-px(32)};
-
- position: fixed;
- bottom: calc(var(--toolbar-size) + var(--spacing-md));
- right: var(--spacing-md);
- transition: all 0.4s ease-in 0s;
-
- &--hidden {
- opacity: 0;
- transform: translateY(calc(var(--button-size) + var(--spacing-md)));
- }
-
- &--visible {
- opacity: 1;
- transform: translateY(0);
- }
-
- a {
- display: flex;
- place-content: center;
- padding: 0;
- width: var(--button-size);
- height: var(--button-size);
-
- svg {
- height: 85%;
- }
-
- :global {
- .arrow-head {
- transform: translateY(30%);
- transition: all 0.45s ease-in-out 0s;
- }
-
- .arrow-bar {
- opacity: 0;
- transform: translateY(30%) translateX(25%) scale(0.5);
- transition: transform 0.45s ease-in-out 0s, opacity 0.3s ease-in-out 0s;
- }
- }
-
- &:hover,
- &:focus {
- :global {
- .arrow-head {
- transform: translateY(0);
- }
-
- .arrow-bar {
- opacity: 1;
- transform: translateY(0) translateX(0) scale(1);
- }
- }
-
- svg {
- :global {
- animation: pulse 1.2s ease-in-out 0.6s infinite;
- }
- }
- }
-
- &:active {
- svg {
- animation-play-state: paused;
- }
- }
- }
-}
diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx
deleted file mode 100644
index 381b4a8..0000000
--- a/src/components/Footer/Footer.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { ButtonLink } from '@components/Buttons';
-import Copyright from '@components/Copyright/Copyright';
-import FooterNav from '@components/FooterNav/FooterNav';
-import { ArrowIcon } from '@components/Icons';
-import { useEffect, useState } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './Footer.module.scss';
-
-const Footer = () => {
- const intl = useIntl();
- const [backToTopClasses, setBackToTopClasses] = useState(
- `${styles['back-to-top']} ${styles['back-to-top--hidden']}`
- );
-
- const handleScroll = () => {
- const currentScrollY = window.scrollY;
-
- if (currentScrollY > 300) {
- setBackToTopClasses(
- `${styles['back-to-top']} ${styles['back-to-top--visible']}`
- );
- } else {
- setBackToTopClasses(
- `${styles['back-to-top']} ${styles['back-to-top--hidden']}`
- );
- }
- };
-
- useEffect(() => {
- window.addEventListener('scroll', handleScroll);
- return () => window.removeEventListener('scroll', handleScroll);
- }, []);
-
- return (
- <footer className={styles.wrapper}>
- <Copyright />
- <FooterNav />
- <div className={backToTopClasses}>
- <ButtonLink target="#top" position="center">
- <span className="screen-reader-text">
- {intl.formatMessage({
- defaultMessage: 'Back to top',
- description: 'Footer: Back to top button',
- id: 'dqrd6I',
- })}
- </span>
- <ArrowIcon direction="top" />
- </ButtonLink>
- </div>
- </footer>
- );
-};
-
-export default Footer;
diff --git a/src/components/FooterNav/FooterNav.module.scss b/src/components/FooterNav/FooterNav.module.scss
deleted file mode 100644
index 73ea568..0000000
--- a/src/components/FooterNav/FooterNav.module.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-@use "@styles/abstracts/mixins" as mix;
-@use "@styles/abstracts/placeholders";
-
-.list {
- @extend %flex-list;
-
- gap: var(--spacing-xs);
- place-content: center;
-}
-
-.item {
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- &::before {
- content: "\2022";
- margin-right: var(--spacing-xs);
- }
- }
- }
-}
diff --git a/src/components/FooterNav/FooterNav.tsx b/src/components/FooterNav/FooterNav.tsx
deleted file mode 100644
index 763e951..0000000
--- a/src/components/FooterNav/FooterNav.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import Link from 'next/link';
-import styles from './FooterNav.module.scss';
-import { NavItem } from '@ts/types/nav';
-import { useIntl } from 'react-intl';
-
-const FooterNav = () => {
- const intl = useIntl();
-
- const footerNavConfig: NavItem[] = [
- {
- id: 'legal-notice',
- name: intl.formatMessage({
- defaultMessage: 'Legal notice',
- description: 'FooterNav: legal notice link',
- id: 'yWjXRx',
- }),
- slug: '/mentions-legales',
- },
- ];
-
- const navItems = footerNavConfig.map((item) => {
- return (
- <li key={item.id} className={styles.item}>
- <Link href={item.slug}>
- <a className={styles.link}>{item.name}</a>
- </Link>
- </li>
- );
- });
-
- return (
- <nav
- className={styles.nav}
- aria-label={intl.formatMessage({
- defaultMessage: 'Footer',
- description: 'FooterNav: aria-label',
- id: 'HTdaZj',
- })}
- >
- <ul className={styles.list}>{navItems}</ul>
- </nav>
- );
-};
-
-export default FooterNav;
diff --git a/src/components/FormElements/Field/Field.module.scss b/src/components/FormElements/Field/Field.module.scss
deleted file mode 100644
index 9100495..0000000
--- a/src/components/FormElements/Field/Field.module.scss
+++ /dev/null
@@ -1,53 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.field {
- background: var(--color-bg-tertiary);
- border: fun.convert-px(2) solid var(--color-border);
- box-shadow: fun.convert-px(3) fun.convert-px(3) 0 0 var(--color-shadow);
- transition: all 0.25s linear 0s;
-
- &:not(.select) {
- width: 100%;
- padding: var(--spacing-2xs) var(--spacing-xs);
- }
-
- &:hover {
- box-shadow: fun.convert-px(5) fun.convert-px(5) 0 fun.convert-px(1)
- var(--color-shadow);
- transform: translate(#{fun.convert-px(-3)}, #{fun.convert-px(-3)});
- }
-
- &:focus {
- background: var(--color-bg);
- border-color: var(--color-primary);
- box-shadow: 0 0 0 0 var(--color-shadow);
- transform: translate(#{fun.convert-px(3)}, #{fun.convert-px(3)});
- outline: none;
- transition: all 0.2s ease-in-out 0s, transform 0.3s ease-out 0s;
- }
-}
-
-.select {
- padding: var(--spacing-2xs) var(--spacing-xs);
- cursor: pointer;
-
- @include mix.pointer("fine") {
- padding: fun.convert-px(3) var(--spacing-xs);
- }
-
- &:hover {
- box-shadow: fun.convert-px(4) fun.convert-px(4) 0 fun.convert-px(1)
- var(--color-shadow);
- transform: translate(#{fun.convert-px(-2)}, #{fun.convert-px(-2)});
- }
-
- &:focus {
- box-shadow: 0 0 0 0 var(--color-shadow);
- transform: translate(#{fun.convert-px(3)}, #{fun.convert-px(3)});
- }
-}
-
-.textarea {
- min-height: fun.convert-px(200);
-}
diff --git a/src/components/FormElements/Field/Field.tsx b/src/components/FormElements/Field/Field.tsx
deleted file mode 100644
index c8df0f9..0000000
--- a/src/components/FormElements/Field/Field.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import {
- ChangeEvent,
- ForwardedRef,
- forwardRef,
- ReactElement,
- SetStateAction,
-} from 'react';
-import styles from './Field.module.scss';
-
-type FieldType = 'email' | 'number' | 'search' | 'select' | 'text' | 'textarea';
-type SelectOptions = {
- id: string;
- name: string;
- value: string;
-};
-
-const Field = (
- {
- id,
- name,
- value,
- setValue,
- required = false,
- kind = 'text',
- label,
- options,
- }: {
- id: string;
- name: string;
- value: string;
- setValue: (value: SetStateAction<string>) => void;
- required?: boolean;
- kind?: FieldType;
- label?: ReactElement;
- options?: SelectOptions[];
- },
- ref: ForwardedRef<HTMLInputElement | HTMLTextAreaElement>
-) => {
- const updateValue = (
- e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
- ) => {
- setValue(e.target.value);
- };
-
- const getOptions = () => {
- return options
- ? options.map((option) => (
- <option key={option.id} value={option.value}>
- {option.name}
- </option>
- ))
- : '';
- };
-
- const getField = () => {
- switch (kind) {
- case 'select':
- return (
- <select
- name={name}
- id={id}
- value={value}
- onChange={updateValue}
- required={required}
- className={`${styles.field} ${styles.select}`}
- >
- {getOptions()}
- </select>
- );
- case 'textarea':
- return (
- <textarea
- ref={ref as ForwardedRef<HTMLTextAreaElement>}
- id={id}
- name={name}
- value={value}
- required={required}
- onChange={updateValue}
- className={`${styles.field} ${styles.textarea}`}
- />
- );
- default:
- return (
- <input
- ref={ref as ForwardedRef<HTMLInputElement>}
- type={kind}
- id={id}
- name={name}
- value={value}
- required={required}
- onChange={updateValue}
- className={styles.field}
- />
- );
- }
- };
-
- return (
- <>
- {label}
- {getField()}
- </>
- );
-};
-
-export default forwardRef(Field);
diff --git a/src/components/FormElements/Form/Form.module.scss b/src/components/FormElements/Form/Form.module.scss
deleted file mode 100644
index 0f7c437..0000000
--- a/src/components/FormElements/Form/Form.module.scss
+++ /dev/null
@@ -1,37 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.wrapper {
- width: 100%;
-}
-
-.centered {
- max-width: 45ch;
- margin-left: auto;
- margin-right: auto;
-}
-
-.search {
- display: flex;
- flex-flow: row nowrap;
- align-items: center;
-
- > input {
- padding-right: calc(var(--btn-size) + var(--spacing-2xs));
-
- &:hover ~ button {
- transform: translate(fun.convert-px(-3), fun.convert-px(-3));
- }
-
- &:focus ~ button {
- transform: translate(fun.convert-px(3), fun.convert-px(3));
- }
- }
-}
-
-.settings {
- display: flex;
- flex-flow: row nowrap;
- align-items: center;
- margin: var(--spacing-sm) 0;
- position: relative;
-}
diff --git a/src/components/FormElements/Form/Form.tsx b/src/components/FormElements/Form/Form.tsx
deleted file mode 100644
index 10fdcdf..0000000
--- a/src/components/FormElements/Form/Form.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { ReactNode } from 'react';
-import styles from './Form.module.scss';
-
-type FormKind = 'centered' | 'search' | 'settings';
-
-const Form = ({
- children,
- submitHandler,
- kind,
- id,
-}: {
- children: ReactNode;
- submitHandler: any;
- kind?: FormKind;
- id?: string;
-}) => {
- const kindStyles = kind ? styles[kind] : '';
- const classes = `${styles.wrapper} ${kindStyles}`;
-
- return (
- <form onSubmit={submitHandler} className={classes} id={id}>
- {children}
- </form>
- );
-};
-
-export default Form;
diff --git a/src/components/FormElements/FormItem/FormItem.module.scss b/src/components/FormElements/FormItem/FormItem.module.scss
deleted file mode 100644
index 07ef56f..0000000
--- a/src/components/FormElements/FormItem/FormItem.module.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-.wrapper {
- margin: var(--spacing-xs) 0;
- max-width: 45ch;
-}
diff --git a/src/components/FormElements/FormItem/FormItem.tsx b/src/components/FormElements/FormItem/FormItem.tsx
deleted file mode 100644
index 8d674f1..0000000
--- a/src/components/FormElements/FormItem/FormItem.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import styles from './FormItem.module.scss';
-
-const FormItem: React.FunctionComponent = ({ children }) => {
- return <div className={styles.wrapper}>{children}</div>;
-};
-
-export default FormItem;
diff --git a/src/components/FormElements/Label/Label.module.scss b/src/components/FormElements/Label/Label.module.scss
deleted file mode 100644
index c527b16..0000000
--- a/src/components/FormElements/Label/Label.module.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.regular {
- display: block;
- color: var(--color-primary-darker);
- font-size: var(--font-size-sm);
- font-variant: small-caps;
- font-weight: 600;
-}
-
-.settings {
- --icon-size: #{fun.convert-px(25)};
- --toggle-width: #{fun.convert-px(45)};
- --toggle-height: calc(var(--toggle-width) / 2);
-
- display: inline-flex;
- align-items: center;
-}
-
-.required {
- color: var(--color-secondary);
-}
diff --git a/src/components/FormElements/Label/Label.tsx b/src/components/FormElements/Label/Label.tsx
deleted file mode 100644
index baedff0..0000000
--- a/src/components/FormElements/Label/Label.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import styles from './Label.module.scss';
-
-type LabelKind = 'regular' | 'settings';
-
-const Label = ({
- body,
- htmlFor,
- required = false,
- kind = 'regular',
-}: {
- body: string;
- htmlFor: string;
- required?: boolean;
- kind?: LabelKind;
-}) => {
- return (
- <label htmlFor={htmlFor} className={styles[kind]}>
- {body}
- {required && <span className={styles.required}> *</span>}
- </label>
- );
-};
-
-export default Label;
diff --git a/src/components/FormElements/Toggle/Toggle.module.scss b/src/components/FormElements/Toggle/Toggle.module.scss
deleted file mode 100644
index 48c88f6..0000000
--- a/src/components/FormElements/Toggle/Toggle.module.scss
+++ /dev/null
@@ -1,75 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.label {
- --icon-size: #{fun.convert-px(25)};
- --toggle-width: #{fun.convert-px(45)};
- --toggle-height: calc(var(--toggle-width) / 2);
-
- display: inline-flex;
- align-items: center;
-}
-
-.title {
- margin-right: var(--spacing-xs);
-}
-
-.toggle {
- display: inline-flex;
- align-items: center;
- width: var(--toggle-width);
- height: var(--toggle-height);
- background: var(--color-shadow-light);
- border: fun.convert-px(1) solid var(--color-primary);
- border-radius: fun.convert-px(32);
- box-shadow: inset 0 0 fun.convert-px(3) 0 var(--color-shadow-dark);
- margin: 0 var(--spacing-2xs);
- position: relative;
-
- &::after {
- content: "";
- display: block;
- width: calc(var(--toggle-width) / 2);
- height: calc(var(--toggle-width) / 2);
- background: var(--color-primary-light);
- border: fun.convert-px(1) solid var(--color-primary);
- border-radius: 50%;
- box-shadow: inset 0 0 fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
- 0 0 fun.convert-px(2) fun.convert-px(1) var(--color-shadow-light);
- position: absolute;
- left: fun.convert-px(-2);
- transition: all 0.3s ease-in-out 0s;
- }
-}
-
-.checkbox {
- position: absolute;
- opacity: 0;
- cursor: pointer;
-
- &:checked ~ .label {
- .toggle::after {
- position: absolute;
- left: calc(100% - (var(--toggle-width) / 2) + #{fun.convert-px(2)});
- }
- }
-
- &:hover,
- &:focus {
- ~ .label {
- .toggle::after {
- background: var(--color-primary-lighter);
- }
- }
- }
-
- &:focus ~ .label {
- .title {
- text-decoration: underline solid var(--color-primary) fun.convert-px(2);
- }
-
- .toggle {
- outline: var(--color-border) solid fun.convert-px(5);
- }
- }
-}
diff --git a/src/components/FormElements/Toggle/Toggle.tsx b/src/components/FormElements/Toggle/Toggle.tsx
deleted file mode 100644
index 4db7d43..0000000
--- a/src/components/FormElements/Toggle/Toggle.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { FormEvent, ReactElement } from 'react';
-import { Form } from '..';
-import styles from './Toggle.module.scss';
-
-const Toggle = ({
- id,
- label,
- value,
- changeHandler,
- leftChoice,
- rightChoice,
- name,
-}: {
- id: string;
- label: string;
- value: boolean;
- changeHandler: (value: boolean) => void;
- leftChoice: ReactElement | string;
- rightChoice: ReactElement | string;
- name?: string;
-}) => {
- const onSubmit = (e: FormEvent) => {
- e.preventDefault();
- };
-
- return (
- <Form kind="settings" submitHandler={onSubmit}>
- <input
- className={styles.checkbox}
- type="checkbox"
- id={id}
- name={name ? name : id}
- checked={value}
- onChange={() => changeHandler(!value)}
- />
- <label htmlFor={id} className={styles.label}>
- <span className={styles.title}>{label}</span>
- {leftChoice}
- <span className={styles.toggle}></span>
- {rightChoice}
- </label>
- </Form>
- );
-};
-
-export default Toggle;
diff --git a/src/components/FormElements/index.tsx b/src/components/FormElements/index.tsx
deleted file mode 100644
index 8ca69b4..0000000
--- a/src/components/FormElements/index.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import Field from './Field/Field';
-import Form from './Form/Form';
-import FormItem from './FormItem/FormItem';
-import Label from './Label/Label';
-import Toggle from './Toggle/Toggle';
-
-export { Field, Form, FormItem, Label, Toggle };
diff --git a/src/components/Header/Header.module.scss b/src/components/Header/Header.module.scss
deleted file mode 100644
index aa0d8cf..0000000
--- a/src/components/Header/Header.module.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.wrapper {
- display: grid;
- grid-template-columns:
- minmax(0, 1fr) min(calc(100vw - calc(var(--spacing-md) * 2)), 100ch)
- minmax(0, 1fr);
- align-items: center;
- padding: var(--spacing-sm) 0 var(--spacing-md);
- position: relative;
- background: var(--color-bg);
- border-bottom: fun.convert-px(3) solid var(--color-border-light);
-}
-
-.body {
- grid-column: 2;
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- justify-content: space-between;
- gap: var(--spacing-md);
-}
diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx
deleted file mode 100644
index 0b773e9..0000000
--- a/src/components/Header/Header.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import Branding from '@components/Branding/Branding';
-import Toolbar from '@components/Toolbar/Toolbar';
-import styles from './Header.module.scss';
-
-const Header = ({ isHome }: { isHome: boolean }) => {
- return (
- <header id="top" className={styles.wrapper}>
- <div className={styles.body}>
- <Branding isHome={isHome} />
- <Toolbar />
- </div>
- </header>
- );
-};
-
-export default Header;
diff --git a/src/components/Icons/Arrow/Arrow.module.scss b/src/components/Icons/Arrow/Arrow.module.scss
deleted file mode 100644
index 49e9b02..0000000
--- a/src/components/Icons/Arrow/Arrow.module.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- fill: var(--color-primary);
- width: var(--icon-size, #{fun.convert-px(30)});
- transition: all 0.25s ease-in-out 0s;
-}
diff --git a/src/components/Icons/Arrow/Arrow.tsx b/src/components/Icons/Arrow/Arrow.tsx
deleted file mode 100644
index e9131d1..0000000
--- a/src/components/Icons/Arrow/Arrow.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import styles from './Arrow.module.scss';
-
-type ArrowDirection = 'top' | 'right' | 'bottom' | 'left';
-
-const ArrowIcon = ({ direction = 'right' }: { direction?: ArrowDirection }) => {
- if (direction === 'top') {
- return (
- <svg
- className={styles.icon}
- viewBox="0 0 23.476 64.644995"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- className="arrow-head"
- d="M 23.476001,24.637 11.715001,0 0,24.800001 Z"
- />
- <path
- className="arrow-bar"
- d="m 15.441001,64.644997 -0.018,-40.007999 H 8.035 l 0.142,40.007999 z"
- />
- </svg>
- );
- }
-
- if (direction === 'bottom') {
- return (
- <svg
- className={styles.icon}
- viewBox="0 0 23.476 64.644995"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- className="arrow-head"
- d="m 23.476001,40.007997 -11.761,24.637 L 0,39.844996 Z"
- />
- <path
- className="arrow-bar"
- d="m 15.441001,0 -0.018,40.007999 H 8.035 L 8.177,0 Z"
- />
- </svg>
- );
- }
-
- if (direction === 'left') {
- return (
- <svg
- className={styles.icon}
- viewBox="0 0 64.644997 23.476001"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- className="arrow-head"
- d="M 24.637,23.476 0,11.715 24.8,-8.3923343e-8 Z"
- />
- <path
- className="arrow-bar"
- d="m 64.644997,15.441 -40.008,-0.018 V 8.0349999 l 40.008,0.142 z"
- />
- </svg>
- );
- }
-
- return (
- <svg
- className={styles.icon}
- viewBox="0 0 64.644997 23.476001"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- className="arrow-head"
- d="M 40.007997,23.476 64.644997,11.715 39.844997,-8.3923343e-8 Z"
- />
- <path
- className="arrow-bar"
- d="M 0,15.441 40.008,15.423 V 8.0349999 L 0,8.1769999 Z"
- />
- </svg>
- );
-};
-
-export default ArrowIcon;
diff --git a/src/components/Icons/Blog/Blog.module.scss b/src/components/Icons/Blog/Blog.module.scss
deleted file mode 100644
index 5376c61..0000000
--- a/src/components/Icons/Blog/Blog.module.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- margin: auto;
- width: var(--icon-size, #{fun.convert-px(40)});
-}
-
-.lines {
- fill: var(--color-fg);
- stroke-width: 4;
-}
-
-.picture {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
-}
-
-.background {
- fill: var(--color-bg);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
diff --git a/src/components/Icons/Blog/Blog.tsx b/src/components/Icons/Blog/Blog.tsx
deleted file mode 100644
index bd32111..0000000
--- a/src/components/Icons/Blog/Blog.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import styles from './Blog.module.scss';
-
-const BlogIcon = () => {
- return (
- <svg
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.icon}
- >
- <path
- className={styles.background}
- d="M 28.992096,1.4822128 H 90.770752 V 82.312253 H 28.992096 Z"
- />
- <path
- className={styles.background}
- d="m 19.110672,8.992094 h 61.778656 v 80.83004 H 19.110672 Z"
- />
- <path
- className={styles.background}
- d="m 9.229248,17.687748 h 61.778656 v 80.83004 H 9.229248 Z"
- />
- <path
- className={styles.picture}
- d="M 18.149242,74.65544 H 33.375246 V 90.194215 H 18.149242 Z"
- />
- <path
- className={styles.picture}
- d="M 18.142653,24.858688 H 62.094499 V 35.908926 H 18.142653 Z"
- />
- <path
- className={styles.lines}
- d="m 17.618576,41.908926 h 45 v 2 h -45 z"
- />
- <path
- className={styles.lines}
- d="m 17.618576,49.908926 h 45 v 2 h -45 z"
- />
- <path
- className={styles.lines}
- d="m 17.618576,57.908926 h 45 v 2 h -45 z"
- />
- <path
- className={styles.lines}
- d="m 17.618576,65.908926 h 45 v 2 h -45 z"
- />
- <path
- className={styles.lines}
- d="m 41.833105,73.424828 h 20.785471 v 2 H 41.833105 Z"
- />
- <path
- className={styles.lines}
- d="m 41.833105,81.424828 h 20.785471 v 2 H 41.833105 Z"
- />
- <path
- className={styles.lines}
- d="m 41.833105,89.424828 h 20.785471 v 2 H 41.833105 Z"
- />
- </svg>
- );
-};
-
-export default BlogIcon;
diff --git a/src/components/Icons/CV/CV.module.scss b/src/components/Icons/CV/CV.module.scss
deleted file mode 100644
index aaa8a1a..0000000
--- a/src/components/Icons/CV/CV.module.scss
+++ /dev/null
@@ -1,54 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- margin: auto;
- width: var(--icon-size, #{fun.convert-px(40)});
-}
-
-.lock {
- fill: var(--color-bg);
- stroke: var(--color-primary-darker);
- stroke-width: 3;
-}
-
-.lines {
- fill: var(--color-fg);
- stroke-width: 4;
-}
-
-.seal-top {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 2;
-}
-
-.seal-bottom {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 2;
-}
-
-.diploma {
- fill: var(--color-bg);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.top {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.handle {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 3;
-}
-
-.bottom {
- fill: var(--color-primary);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
diff --git a/src/components/Icons/CV/CV.tsx b/src/components/Icons/CV/CV.tsx
deleted file mode 100644
index 876d1cb..0000000
--- a/src/components/Icons/CV/CV.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import styles from './CV.module.scss';
-
-const CVIcon = () => {
- return (
- <svg
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.icon}
- >
- <path
- className={styles.bottom}
- d="M 0.72670447,19.813041 H 77.467597 v 54.36591 H 0.72670447 Z"
- />
- <path
- className={styles.handle}
- d="m 22.263958,10.17849 c 12.6493,-1.81512 21.613185,-1.732794 33.666442,0 l 1.683339,10.99517 h -5.891624 v -5.474639 c -7.949741,-2.722434 -16.311959,-2.706359 -25.249837,0 v 5.474639 h -5.891625 z"
- />
- <path
- className={styles.top}
- d="M 0.72670447,19.813041 H 77.467597 V 51.17622 H 0.72670447 Z"
- />
- <path
- className={styles.diploma}
- d="M 44.217117,47.159906 H 98.921356 V 82.664122 H 44.217117 Z"
- />
- <path
- className={styles['seal-bottom']}
- d="m 84.933665,80.775336 h 6.957554 V 90.992144 L 88.412426,87.2244 84.933665,90.992144 Z"
- />
- <path
- className={styles['seal-top']}
- d="m 93.326919,76.83334 a 4.914472,4.9188584 0 0 1 -4.914493,4.918858 4.914472,4.9188584 0 0 1 -4.914461,-4.918858 4.914472,4.9188584 0 0 1 4.914461,-4.918858 4.914472,4.9188584 0 0 1 4.914493,4.918858 z"
- />
- <path
- className={styles.lines}
- d="m 54.53557,60.491974 h 34.067282 v 1.515453 H 54.53557 Z"
- />
- <path
- className={styles.lines}
- d="m 54.53557,67.437763 h 34.067282 v 1.515453 H 54.53557 Z"
- />
- <path
- className={styles.lines}
- d="m 54.53557,74.383628 h 17.563315 v 1.515454 H 54.53557 Z"
- />
- <path
- className={styles.lines}
- d="m 63.495911,53.546123 h 16.146628 v 1.515452 H 63.495911 Z"
- />
- <path
- className={styles.lock}
- d="M 34.048314,42.893007 H 44.145988 V 57.849688 H 34.048314 Z"
- />
- </svg>
- );
-};
-
-export default CVIcon;
diff --git a/src/components/Icons/Close/Close.module.scss b/src/components/Icons/Close/Close.module.scss
deleted file mode 100644
index 5a1f638..0000000
--- a/src/components/Icons/Close/Close.module.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- margin: auto;
- width: var(--icon-size, #{fun.convert-px(40)});
-}
-
-.line {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 3;
-}
diff --git a/src/components/Icons/Close/Close.tsx b/src/components/Icons/Close/Close.tsx
deleted file mode 100644
index 12214de..0000000
--- a/src/components/Icons/Close/Close.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import styles from './Close.module.scss';
-
-const CloseIcon = () => {
- return (
- <svg
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.icon}
- >
- <path
- className={styles.line}
- d="m 3.6465461,3.6465455 c 2.8785908,-2.87859092 7.5134339,-2.87859092 10.3920249,0 L 96.353457,85.96143 c 2.878587,2.878591 2.878587,7.513434 0,10.392025 -2.878597,2.878591 -7.513432,2.878591 -10.392029,0 L 3.6465451,14.038571 C 0.76795421,11.15998 0.76795421,6.5251364 3.6465461,3.6465455 Z"
- />
- <path
- className={styles.line}
- d="m 96.353453,3.646546 c 2.878592,2.8785909 2.878592,7.513435 0,10.392026 L 14.03857,96.353457 c -2.878589,2.878587 -7.5134337,2.878587 -10.3920246,0 -2.87859084,-2.878597 -2.87858985,-7.513442 -1e-6,-10.392029 L 85.961428,3.646546 c 2.878591,-2.87859097 7.513434,-2.87859097 10.392025,0 z"
- />
- </svg>
- );
-};
-
-export default CloseIcon;
diff --git a/src/components/Icons/Cog/Cog.module.scss b/src/components/Icons/Cog/Cog.module.scss
deleted file mode 100644
index a861f0c..0000000
--- a/src/components/Icons/Cog/Cog.module.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- width: var(--icon-size, #{fun.convert-px(40)});
- margin: auto;
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
diff --git a/src/components/Icons/Cog/Cog.tsx b/src/components/Icons/Cog/Cog.tsx
deleted file mode 100644
index 7a04d76..0000000
--- a/src/components/Icons/Cog/Cog.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import styles from './Cog.module.scss';
-
-const CogIcon = () => {
- return (
- <svg
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.icon}
- >
- <path d="m 71.782287,3.1230469 c -1.164356,0 -2.3107,0.076326 -3.435131,0.2227895 L 66.33766,9.1021499 C 64.651951,9.5517047 63.049493,10.204637 61.558109,11.033725 L 56.112383,8.2889128 c -1.970928,1.4609237 -3.730521,3.1910632 -5.22513,5.1351362 l 2.648234,5.494014 c -0.855644,1.477262 -1.537042,3.067161 -2.016082,4.743334 l -5.791433,1.911821 c -0.188001,1.269731 -0.286444,2.568579 -0.286444,3.890587 0,1.164355 0.07633,2.310701 0.222789,3.435131 l 5.756315,2.009497 c 0.449555,1.685708 1.102486,3.288168 1.931575,4.779551 l -2.744813,5.445725 c 1.460924,1.970927 3.191063,3.730521 5.135137,5.22513 l 5.494014,-2.648233 c 1.477261,0.85564 3.067161,1.537039 4.743334,2.016081 L 67.8917,55.51812 c 1.26973,0.188002 2.568578,0.286444 3.890587,0.286444 1.16565,0 2.313889,-0.07601 3.43952,-0.222789 l 2.008399,-5.756314 c 1.684332,-0.449523 3.285984,-1.103103 4.776259,-1.931575 l 5.445725,2.744812 c 1.970928,-1.460924 3.730521,-3.191061 5.22513,-5.135136 l -2.648233,-5.494015 c 0.85564,-1.477262 1.537039,-3.067161 2.016082,-4.743334 l 5.79253,-1.91182 c 0.187995,-1.269731 0.285346,-2.56858 0.285346,-3.890588 0,-1.16565 -0.07601,-2.313889 -0.222789,-3.439521 L 92.143942,24.015886 C 91.694419,22.331554 91.04084,20.729903 90.212367,19.239628 l 2.744812,-5.445726 C 91.496255,11.822973 89.766118,10.063381 87.822043,8.5687715 L 82.328028,11.217006 C 80.850766,10.361361 79.260867,9.6799641 77.584694,9.2009234 L 75.672874,3.4094907 C 74.403143,3.2214898 73.104295,3.1230469 71.782287,3.1230469 Z m 0,15.0520191 a 11.288679,11.288679 0 0 1 11.288739,11.288739 11.288679,11.288679 0 0 1 -11.288739,11.28874 11.288679,11.288679 0 0 1 -11.28874,-11.28874 11.288679,11.288679 0 0 1 11.28874,-11.288739 z" />
- <path d="m 38.326115,25.84777 c -1.583642,0 -3.142788,0.103807 -4.672127,0.303016 l -2.73312,7.829173 c -2.292736,0.611441 -4.472242,1.499494 -6.500676,2.627139 L 17.01345,32.873874 c -2.680664,1.987004 -5.073889,4.340169 -7.1067095,6.984309 l 3.6018685,7.472418 c -1.163764,2.009226 -2.090533,4.171652 -2.742078,6.451418 l -7.8769382,2.60027 C 2.6338924,58.109252 2.5,59.875819 2.5,61.673885 c 0,1.583642 0.1038125,3.142788 0.3030165,4.672128 l 7.8291725,2.73312 c 0.611441,2.292734 1.499494,4.472243 2.627139,6.500673 L 9.5261037,82.98655 c 1.9870063,2.680661 4.3401703,5.07389 6.9843093,7.106709 l 7.472419,-3.601867 c 2.009226,1.16376 4.171651,2.090533 6.451418,2.742079 l 2.60027,7.876932 C 34.761483,97.366114 36.528049,97.5 38.326115,97.5 c 1.585404,0 3.147126,-0.103373 4.678099,-0.303015 l 2.731628,-7.829178 c 2.290862,-0.611397 4.469272,-1.500329 6.496197,-2.627132 l 7.406741,3.733224 c 2.680664,-1.987007 5.07389,-4.340171 7.10671,-6.984313 l -3.601866,-7.472415 c 1.163756,-2.00923 2.090529,-4.171655 2.742076,-6.45142 l 7.878431,-2.60027 c 0.255691,-1.726964 0.3881,-3.49353 0.3881,-5.291596 0,-1.585404 -0.103373,-3.147127 -0.303016,-4.678099 L 66.020041,54.264159 C 65.408645,51.973296 64.51971,49.794888 63.392903,47.767962 l 3.733224,-7.406742 c -1.987006,-2.680664 -4.340168,-5.073889 -6.984309,-7.10671 l -7.472419,3.601867 c -2.009228,-1.163762 -4.171651,-2.090533 -6.451418,-2.742076 l -2.60027,-7.876939 C 41.890748,25.981661 40.124181,25.84777 38.326115,25.84777 Z m 0,20.472278 A 15.353754,15.353754 0 0 1 53.679952,61.673885 15.353754,15.353754 0 0 1 38.326115,77.027724 15.353754,15.353754 0 0 1 22.972279,61.673885 15.353754,15.353754 0 0 1 38.326115,46.320048 Z" />
- </svg>
- );
-};
-
-export default CogIcon;
diff --git a/src/components/Icons/Contact/Contact.module.scss b/src/components/Icons/Contact/Contact.module.scss
deleted file mode 100644
index 963c1dc..0000000
--- a/src/components/Icons/Contact/Contact.module.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- margin: auto;
- width: var(--icon-size, #{fun.convert-px(40)});
-}
-
-.envelop {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.lines {
- fill: var(--color-fg);
-}
-
-.background {
- fill: var(--color-shadow-dark);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.paper {
- fill: var(--color-bg);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
diff --git a/src/components/Icons/Contact/Contact.tsx b/src/components/Icons/Contact/Contact.tsx
deleted file mode 100644
index 19295d0..0000000
--- a/src/components/Icons/Contact/Contact.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import styles from './Contact.module.scss';
-
-const ContactIcon = () => {
- return (
- <svg
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.icon}
- >
- <path
- className={styles.background}
- d="M 1.5262527,42.535416 H 98.473747 V 98.371662 H 1.5262527 Z"
- />
- <path
- className={styles.envelop}
- d="m 49.999985,1.6283075 c 2.855148,0 48.473753,40.8563885 48.473753,40.8563885 H 1.5262359 c 0,0 45.6186001,-40.8563885 48.4737491,-40.8563885 z"
- />
- <path
- className={styles.paper}
- d="M 8.3434839,28.463842 H 91.656465 V 97.348661 H 8.3434839 Z"
- />
- <path
- className={styles.envelop}
- d="M 49.999985,63.571925 98.473738,98.371692 H 1.5262359 Z"
- />
- <path
- className={styles.lines}
- d="m 24.562439,37.640923 h 50.875053 v 1.5 H 24.562439 Z"
- />
- <path
- className={styles.lines}
- d="m 24.562439,45.140923 h 50.875053 v 1.5 H 24.562439 Z"
- />
- <path
- className={styles.lines}
- d="m 24.562443,52.640923 h 50.875053 v 1.5 H 24.562443 Z"
- />
- <path
- className={styles.lines}
- d="M 24.562447,60.140923 H 75.4375 v 1.5 H 24.562447 Z"
- />
- <path
- className={styles.envelop}
- d="M 39.93749,70.965004 1.5262559,43.55838 v 54.813242 z"
- />
- <path
- className={styles.envelop}
- d="M 60.0625,70.965004 98.473738,43.55838 v 54.813242 z"
- />
- </svg>
- );
-};
-
-export default ContactIcon;
diff --git a/src/components/Icons/Copyright/Copyright.module.scss b/src/components/Icons/Copyright/Copyright.module.scss
deleted file mode 100644
index 8ea801e..0000000
--- a/src/components/Icons/Copyright/Copyright.module.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- width: var(--icon-size, #{fun.convert-px(40)});
- fill: var(--color-fg);
-}
diff --git a/src/components/Icons/Copyright/Copyright.tsx b/src/components/Icons/Copyright/Copyright.tsx
deleted file mode 100644
index d27c042..0000000
--- a/src/components/Icons/Copyright/Copyright.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import styles from './Copyright.module.scss';
-
-const CopyrightIcon = () => {
- return (
- <svg
- className={styles.icon}
- viewBox="0 0 211.99811 63.999996"
- xmlns="http://www.w3.org/2000/svg"
- >
- <title>CC BY SA</title>
- <path d="m 175.53911,15.829498 c 0,-3.008 1.485,-4.514 4.458,-4.514 2.973,0 4.457,1.504 4.457,4.514 0,2.971 -1.486,4.457 -4.457,4.457 -2.971,0 -4.458,-1.486 -4.458,-4.457 z" />
- <path d="m 188.62611,24.057498 v 13.085 h -3.656 v 15.542 h -9.944 v -15.541 h -3.656 v -13.086 c 0,-0.572 0.2,-1.057 0.599,-1.457 0.401,-0.399 0.887,-0.6 1.457,-0.6 h 13.144 c 0.533,0 1.01,0.2 1.428,0.6 0.417,0.4 0.628,0.886 0.628,1.457 z" />
- <path d="m 179.94147,-1.9073486e-6 c -8.839,0 -16.34167,3.0848125073486 -22.51367,9.2578125073486 -6.285,6.4000004 -9.42969,13.9811874 -9.42969,22.7421874 0,8.762 3.14469,16.284312 9.42969,22.570312 6.361,6.286 13.86467,9.429688 22.51367,9.429688 8.799,0 16.43611,-3.181922 22.91211,-9.544922 6.096,-5.98 9.14453,-13.464078 9.14453,-22.455078 0,-8.952 -3.10646,-16.532188 -9.31446,-22.7421874 -6.172,-6.172 -13.75418,-9.2578125073486 -22.74218,-9.2578125073486 z M 180.05475,5.7714825 c 7.238,0 13.40967,2.55225 18.51367,7.6562495 5.103,5.106 7.65625,11.294313 7.65625,18.570313 0,7.391 -2.51397,13.50575 -7.54297,18.34375 -5.295,5.221 -11.50591,7.828125 -18.6289,7.828125 -7.162,0 -13.33268,-2.589484 -18.51368,-7.771484 -5.18,-5.178001 -7.76953,-11.310485 -7.76953,-18.396485 0,-7.047 2.60813,-13.238266 7.82813,-18.572265 5.029,-5.1040004 11.18103,-7.6582035 18.45703,-7.6582035 z" />
- <path d="m 91.998554,27.114498 c 0.609,-3.924 2.189,-6.962 4.742,-9.114 2.552,-2.152 5.655996,-3.228 9.313996,-3.228 5.027,0 9.029,1.62 12,4.856 2.971,3.238 4.457,7.391 4.457,12.457 0,4.915 -1.543,9 -4.627,12.256 -3.088,3.256 -7.086,4.886 -12.002,4.886 -3.619,0 -6.742996,-1.085 -9.370996,-3.257 -2.629,-2.172 -4.209,-5.257 -4.743,-9.257 h 8.059 c 0.189996,3.886 2.532996,5.829 7.028996,5.829 2.246,0 4.057,-0.972 5.428,-2.914 1.373,-1.942 2.059,-4.534 2.059,-7.771 0,-3.391 -0.629,-5.971 -1.885,-7.743 -1.258,-1.771 -3.066,-2.657 -5.43,-2.657 -4.268,0 -6.667,1.885 -7.199996,5.656 h 2.342996 l -6.341996,6.343 -6.343,-6.343 z" />
- <path d="m 105.94241,-1.8610229e-6 c -8.799996,0 -16.304676,3.1054062610229 -22.513666,9.3164061610229 -6.285,6.3999997 -9.42969,13.9625467 -9.42969,22.6855467 0,8.763 3.14469,16.28336 9.42969,22.568359 6.361,6.286001 13.86467,9.429688 22.513666,9.429688 8.836,0 16.47511,-3.162328 22.91211,-9.486328 6.096,-6.057 9.14453,-13.559672 9.14453,-22.513672 0,-8.952 -3.10646,-16.513547 -9.31446,-22.6855468 -6.211,-6.21 -13.79118,-9.3144530610229 -22.74218,-9.3144530610229 z M 106.05569,5.7714825 c 7.275,0 13.44667,2.5698437 18.51367,7.7148435 5.103,5.028 7.65625,11.200672 7.65625,18.513672 0,7.353 -2.51397,13.46775 -7.54297,18.34375 -5.295,5.219 -11.50591,7.828125 -18.6289,7.828125 -7.161996,0 -13.332676,-2.589484 -18.513676,-7.771484 -5.18,-5.143 -7.76953,-11.275391 -7.76953,-18.400391 0,-7.046 2.60813,-13.217672 7.82813,-18.513672 5.029,-5.1429998 11.18103,-7.7148435 18.457026,-7.7148435 z" />
- <path d="M 31.942383,5.9265138e-7 C 23.066111,5.9265138e-7 15.579851,3.1065496 9.484666,9.3147376 6.399571,12.400832 4.046856,15.896269 2.427808,19.801386 0.80876,23.706506 0,27.771846 0,32.000976 c 0,4.26713 0.800415,8.32413 2.400463,12.17225 1.600051,3.84811 3.933123,7.30532 7.000216,10.37141 3.067093,3.06609 6.534587,5.40951 10.400708,7.02756 3.867116,1.62105 7.914819,2.4278 12.142946,2.4278 4.22813,0 8.32441,-0.8171 12.28553,-2.45515 3.96313,-1.63805 7.50614,-4.00301 10.62923,-7.0881 3.0081,-2.93309 5.28529,-6.31477 6.82834,-10.14289 1.54104,-3.82712 2.31257,-7.93174 2.31257,-12.31288 0,-4.34313 -0.78277,-8.44771 -2.34382,-12.31483 C 60.094133,15.82003 57.808593,12.380471 54.800503,9.3713796 48.515313,3.1241896 40.893653,5.9265136e-7 31.942383,5.9265138e-7 Z M 32.057623,5.7716626 c 7.23822,0 13.42863,2.571923 18.57478,7.7150794 2.47408,2.478074 4.35948,5.297144 5.65252,8.459244 1.29504,3.16209 1.94342,6.51384 1.94342,10.05694 0,7.35423 -2.49445,13.46816 -7.4846,18.34432 -2.59208,2.51407 -5.49406,4.43661 -8.71316,5.77166 -3.2231,1.33404 -6.54486,1.9981 -9.97296,1.9981 -3.467107,0 -6.782568,-0.65672 -9.943661,-1.97076 -3.164098,-1.31604 -5.999858,-3.21894 -8.513933,-5.71502 -2.515077,-2.49507 -4.447918,-5.33279 -5.800959,-8.51588 -1.354042,-3.1791 -2.029358,-6.48331 -2.029358,-9.91242 0,-3.4671 0.675316,-6.79186 2.029358,-9.97295 1.352043,-3.1811 3.285882,-6.046798 5.800959,-8.599875 4.991151,-5.1041594 11.14337,-7.6584384 18.457594,-7.6584384 z" />
- <path d="m 50.114533,26.687816 -4.22913,2.22907 c -0.45702,-0.95103 -1.02003,-1.61905 -1.68605,-2.00006 -0.66802,-0.38001 -1.30704,-0.57102 -1.91406,-0.57102 -2.85709,0 -4.28713,1.88506 -4.28713,5.65717 0,1.71406 0.363,3.0841 1.08603,4.11313 0.72302,1.02903 1.78906,1.54405 3.2011,1.54405 1.86506,0 3.1801,-0.91503 3.94112,-2.74309 l 4.00012,2.00007 c -0.87502,1.56304 -2.05706,2.79108 -3.54111,3.68611 -1.48604,0.89602 -3.10509,1.34304 -4.85715,1.34304 -2.89608,0 -5.20915,-0.87503 -6.94121,-2.62908 -1.73605,-1.75205 -2.60207,-4.19013 -2.60207,-7.31323 0,-3.04809 0.88502,-5.46616 2.65808,-7.25722 1.77005,-1.79005 4.00812,-2.68608 6.7132,-2.68608 3.96212,-0.002 6.78321,1.54105 8.45826,4.62714 z" />
- <path d="m 31.656963,26.687816 -4.287128,2.22907 c -0.458013,-0.95103 -1.019029,-1.61905 -1.685048,-2.00006 -0.667024,-0.38001 -1.286042,-0.57102 -1.858057,-0.57102 -2.856087,0 -4.28613,1.88506 -4.28613,5.65717 0,1.71406 0.362014,3.0841 1.085029,4.11313 0.724025,1.02903 1.791056,1.54405 3.201101,1.54405 1.867057,0 3.181095,-0.91503 3.944118,-2.74309 l 3.942125,2.00007 c -0.83803,1.56304 -2.000065,2.79108 -3.486111,3.68611 -1.484043,0.89602 -3.123093,1.34304 -4.914149,1.34304 -2.857088,0 -5.163158,-0.87503 -6.915212,-2.62908 -1.752053,-1.75205 -2.62808,-4.19013 -2.62808,-7.31323 0,-3.04809 0.886028,-5.46616 2.657081,-7.25722 1.771054,-1.79005 4.009125,-2.68608 6.715205,-2.68608 3.963122,-0.002 6.800209,1.54105 8.515256,4.62714 z" />
- </svg>
- );
-};
-
-export default CopyrightIcon;
diff --git a/src/components/Icons/Hamburger/Hamburger.module.scss b/src/components/Icons/Hamburger/Hamburger.module.scss
deleted file mode 100644
index 9965c5e..0000000
--- a/src/components/Icons/Hamburger/Hamburger.module.scss
+++ /dev/null
@@ -1,56 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- position: relative;
- width: 100%;
-
- &,
- &::before,
- &::after {
- background: var(--color-primary-lighter);
- background-image: linear-gradient(
- to right,
- var(--color-primary-light) 0%,
- var(--color-primary-lighter) 100%
- );
- border: fun.convert-px(1) solid var(--color-primary-darker);
- border-radius: fun.convert-px(3);
- display: block;
- height: fun.convert-px(7);
- margin: auto;
- transition: all 0.25s ease-in-out 0s, transform 0.4s ease-in 0s;
- }
-
- &::before,
- &::after {
- content: "";
- position: absolute;
- left: fun.convert-px(-1);
- right: fun.convert-px(-1);
- }
-
- &::before {
- bottom: fun.convert-px(15);
- }
-
- &::after {
- top: fun.convert-px(15);
- }
-
- &--active {
- background: transparent;
- border: transparent;
-
- &::before {
- transform-origin: 50% 50%;
- transform: rotate(45deg);
- bottom: 0;
- }
-
- &::after {
- transform-origin: 50% 50%;
- transform: rotate(-45deg);
- top: 0;
- }
- }
-}
diff --git a/src/components/Icons/Hamburger/Hamburger.tsx b/src/components/Icons/Hamburger/Hamburger.tsx
deleted file mode 100644
index 9b39272..0000000
--- a/src/components/Icons/Hamburger/Hamburger.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import styles from './Hamburger.module.scss';
-
-const HamburgerIcon = ({ isActive }: { isActive: boolean }) => {
- const withModifier = isActive ? ` ${styles['icon--active']}` : '';
- const iconClasses = `${styles.icon} ${withModifier}`;
-
- return <span className={iconClasses}></span>;
-};
-
-export default HamburgerIcon;
diff --git a/src/components/Icons/Home/Home.module.scss b/src/components/Icons/Home/Home.module.scss
deleted file mode 100644
index f2e7f9e..0000000
--- a/src/components/Icons/Home/Home.module.scss
+++ /dev/null
@@ -1,42 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- margin: auto;
- width: var(--icon-size, #{fun.convert-px(40)});
-}
-
-.wall {
- fill: var(--color-bg);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.indoor {
- fill: var(--color-shadow-dark);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.door {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.roof {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.chimney {
- fill: var(--color-bg);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
-}
-
-.lines {
- fill: var(--color-primary-darker);
- stroke-width: 4;
-}
diff --git a/src/components/Icons/Home/Home.tsx b/src/components/Icons/Home/Home.tsx
deleted file mode 100644
index 11c0c8c..0000000
--- a/src/components/Icons/Home/Home.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import styles from './Home.module.scss';
-
-const HomeIcon = () => {
- return (
- <svg
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.icon}
- >
- <path
- className={styles.wall}
- d="M 9.2669392,15.413749 H 90.709833 V 97.751815 H 9.2669392 Z"
- />
- <path
- className={styles.indoor}
- d="m 39.190941,65.836418 h 21.594871 v 31.91539 H 39.190941 Z"
- />
- <path
- className={styles.door}
- d="m 39.190941,65.836418 h 21.594871 v 31.91539 H 39.190941 Z"
- />
- <path
- className={styles.roof}
- d="M 4.8219096,11.719266 H 94.720716 l 3.47304,33.365604 H 1.7830046 Z"
- />
- <path
- className={styles.chimney}
- d="M 70.41848,2.2481852 H 82.957212 V 22.636212 H 70.41848 Z"
- />
- <path
- className={styles.lines}
- d="M 3.9536645,19.342648 H 61.003053 v 3.293563 H 3.9536645 Z"
- />
- <path
- className={styles.lines}
- d="m 38.973709,32.057171 h 57.049389 v 3.293563 H 38.973709 Z"
- />
- </svg>
- );
-};
-
-export default HomeIcon;
diff --git a/src/components/Icons/Moon/Moon.module.scss b/src/components/Icons/Moon/Moon.module.scss
deleted file mode 100644
index 799a282..0000000
--- a/src/components/Icons/Moon/Moon.module.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.moon {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
- width: var(--icon-size, #{fun.convert-px(25)});
-}
diff --git a/src/components/Icons/Moon/Moon.tsx b/src/components/Icons/Moon/Moon.tsx
deleted file mode 100644
index 26f56a1..0000000
--- a/src/components/Icons/Moon/Moon.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { useIntl } from 'react-intl';
-import styles from './Moon.module.scss';
-
-const MoonIcon = () => {
- const intl = useIntl();
-
- return (
- <svg
- className={styles.moon}
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- >
- <title>
- {intl.formatMessage({
- defaultMessage: 'Dark theme',
- description: 'Icons: Moon icon (dark theme)',
- id: 'ode0YK',
- })}
- </title>
- <path d="M 51.077315,1.9893942 A 43.319985,43.319985 0 0 1 72.840039,39.563145 43.319985,43.319985 0 0 1 29.520053,82.88313 43.319985,43.319985 0 0 1 5.4309911,75.569042 48.132997,48.132997 0 0 0 46.126047,98 48.132997,48.132997 0 0 0 94.260004,49.867002 48.132997,48.132997 0 0 0 51.077315,1.9893942 Z" />
- </svg>
- );
-};
-
-export default MoonIcon;
diff --git a/src/components/Icons/Projects/Projects.module.scss b/src/components/Icons/Projects/Projects.module.scss
deleted file mode 100644
index 3cf939a..0000000
--- a/src/components/Icons/Projects/Projects.module.scss
+++ /dev/null
@@ -1,40 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- margin: auto;
- width: var(--icon-size, #{fun.convert-px(40)});
-}
-
-.root,
-.separator,
-.cursor,
-.line,
-.text {
- fill: var(--color-fg);
-}
-
-.stand {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-dark);
-
- &--top {
- stroke-width: 3;
- }
-
- &--bottom {
- stroke-width: 2;
- }
-}
-
-.screen {
- fill: var(--color-bg);
- stroke: var(--color-primary-dark);
- stroke-width: 3;
-}
-
-.contour {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-dark);
- stroke-width: 3;
-}
diff --git a/src/components/Icons/Projects/Projects.tsx b/src/components/Icons/Projects/Projects.tsx
deleted file mode 100644
index d4af247..0000000
--- a/src/components/Icons/Projects/Projects.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import styles from './Projects.module.scss';
-
-const ProjectsIcon = () => {
- return (
- <svg
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.icon}
- >
- <path
- d="M 1.0206528,11.991149 H 98.979347 V 78.466748 H 1.0206528 Z"
- className={styles.contour}
- />
- <path
- d="M 6.2503581,18.032451 H 93.563283 V 71.12731 H 6.2503581 Z"
- className={styles.screen}
- />
- <path
- d="m 40.038268,78.939276 c 4.614714,2.7794 4.333151,10.099225 0,17.60572 H 50 59.961731 c -4.333151,-7.506495 -4.614715,-14.82632 0,-17.60572 H 50 Z"
- className={`${styles.stand} ${styles['stand--top']}`}
- />
- <path
- d="m 31.084262,96.254656 h 37.831475 c 1.394769,0 2.517635,0.404907 2.517635,0.907864 v 1.179616 c 0,0.502956 -1.122866,0.907864 -2.517635,0.907864 H 31.084262 c -1.394769,0 -2.517635,-0.404908 -2.517635,-0.907864 V 97.16252 c 0,-0.502957 1.122866,-0.907864 2.517635,-0.907864 z"
- className={`${styles.stand} ${styles['stand--bottom']}`}
- />
- <path
- d="m 13.259277,26.737199 h 29.132596 v 2.567314 H 13.259277 Z"
- className={styles.line}
- />
- <path
- d="M 13.259277,36.439141 H 36.46805 v 2.567315 H 13.259277 Z"
- className={styles.line}
- />
- <path
- d="m 13.259277,46.141084 h 26.586812 v 2.567314 H 13.259277 Z"
- className={styles.line}
- />
- <path
- d="m 18.443194,65.930804 h 4.417548 v 1 h -4.417548 z"
- className={styles.cursor}
- />
- <path
- d="m 77.586096,42.217577 v -1.680914 l 6.160884,-2.39919 -6.160884,-2.406595 v -1.68832 l 7.604842,2.89532 v 2.38438 z"
- className={styles.text}
- />
- <path
- d="m 68.396606,43.291289 6.07943,-11.136982 h 1.688318 l -6.049809,11.136982 z"
- className={styles.text}
- />
- <path
- d="m 59.384832,39.322258 v -2.38438 l 7.604841,-2.89532 v 1.68832 l -6.168289,2.406595 6.168289,2.399191 v 1.680915 z"
- className={styles.text}
- />
- <path
- d="M 7.1079167,57.876372 H 92.892083 v 0.813634 H 7.1079167 Z"
- className={styles.separator}
- />
- <path
- d="m 17.042456,64.960616 q 0,0.632276 -0.426175,0.9816 -0.422681,0.345831 -1.254074,0.37727 v 0.611318 h -0.380763 v -0.600838 q -0.751047,-0.02795 -1.170236,-0.352818 -0.419189,-0.328364 -0.551931,-1.002559 l 0.89427,-0.164183 q 0.06637,0.394736 0.261992,0.579878 0.199115,0.181648 0.565905,0.216581 v -1.365857 q -0.01048,-0.007 -0.0524,-0.01398 -0.04192,-0.01048 -0.05589,-0.01048 -0.562412,-0.129244 -0.848857,-0.303907 -0.286445,-0.178155 -0.443642,-0.447135 -0.153701,-0.272472 -0.153701,-0.663715 0,-0.579878 0.394736,-0.894269 0.394736,-0.317886 1.159755,-0.349325 v -0.468093 h 0.380763 v 0.468095 q 0.681183,0.02445 1.047973,0.303911 0.36679,0.275967 0.527479,0.918723 l -0.92222,0.136236 q -0.104797,-0.600837 -0.653236,-0.674195 v 1.22962 l 0.03843,0.007 q 0.101305,0 0.614811,0.167676 0.517,0.167676 0.772007,0.496041 0.255006,0.324871 0.255006,0.817418 z m -2.061012,-2.731715 q -0.639264,0.04891 -0.639264,0.558918 0,0.157196 0.0524,0.2585 0.0524,0.09781 0.157197,0.167676 0.104797,0.06986 0.429668,0.174662 z m 1.152769,2.745688 q 0,-0.174662 -0.06288,-0.282954 -0.06288,-0.111783 -0.185141,-0.181648 -0.118771,-0.06986 -0.523987,-0.185142 v 1.28202 q 0.772006,-0.0524 0.772006,-0.632276 z"
- className={styles.root}
- />
- </svg>
- );
-};
-
-export default ProjectsIcon;
diff --git a/src/components/Icons/Search/Search.module.scss b/src/components/Icons/Search/Search.module.scss
deleted file mode 100644
index 4c42028..0000000
--- a/src/components/Icons/Search/Search.module.scss
+++ /dev/null
@@ -1,31 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.icon {
- display: block;
- margin: auto;
- width: var(--icon-size, #{fun.convert-px(40)});
-}
-
-.big-handle {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 3;
-}
-
-.glass {
- fill: var(--color-bg-opacity);
- stroke: var(--color-primary-darker);
- stroke-width: 2;
-}
-
-.upright {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 3;
-}
-
-.small-handle {
- fill: var(--color-primary);
- stroke: var(--color-primary-darker);
- stroke-width: 2;
-}
diff --git a/src/components/Icons/Search/Search.tsx b/src/components/Icons/Search/Search.tsx
deleted file mode 100644
index abb7b53..0000000
--- a/src/components/Icons/Search/Search.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import styles from './Search.module.scss';
-
-const SearchIcon = () => {
- return (
- <svg
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- className={styles.icon}
- >
- <path
- className={styles['small-handle']}
- d="m 45.39268,48.064692 5.611922,4.307881 -10.292886,13.414321 -5.611923,-4.307882 z"
- />
- <path
- className={styles.upright}
- d="M 90.904041,28.730105 A 27.725691,27.730085 0 0 1 63.17835,56.46019 27.725691,27.730085 0 0 1 35.45266,28.730105 27.725691,27.730085 0 0 1 63.17835,1.00002 27.725691,27.730085 0 0 1 90.904041,28.730105 Z"
- />
- <path
- className={styles.glass}
- d="M 82.438984,28.730105 A 19.260633,19.263685 0 0 1 63.17835,47.99379 19.260633,19.263685 0 0 1 43.917716,28.730105 19.260633,19.263685 0 0 1 63.17835,9.4664203 19.260633,19.263685 0 0 1 82.438984,28.730105 Z"
- />
- <path
- className={styles['big-handle']}
- d="m 35.826055,60.434903 5.75193,4.415356 c 0.998045,0.766128 1.184879,2.186554 0.418913,3.184809 L 18.914717,98.117182 c -0.765969,0.998256 -2.186094,1.185131 -3.18414,0.418997 L 9.9786472,94.120827 C 8.9806032,93.354698 8.7937692,91.934273 9.5597392,90.936014 L 32.641919,60.853903 c 0.765967,-0.998254 2.186091,-1.185129 3.184136,-0.419 z"
- />
- </svg>
- );
-};
-
-export default SearchIcon;
diff --git a/src/components/Icons/Sun/Sun.module.scss b/src/components/Icons/Sun/Sun.module.scss
deleted file mode 100644
index 5682aa3..0000000
--- a/src/components/Icons/Sun/Sun.module.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.sun {
- fill: var(--color-primary-lighter);
- stroke: var(--color-primary-darker);
- stroke-width: 4;
- width: var(--icon-size, #{fun.convert-px(25)});
-}
diff --git a/src/components/Icons/Sun/Sun.tsx b/src/components/Icons/Sun/Sun.tsx
deleted file mode 100644
index 12f47d3..0000000
--- a/src/components/Icons/Sun/Sun.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { useIntl } from 'react-intl';
-import styles from './Sun.module.scss';
-
-const SunIcon = () => {
- const intl = useIntl();
-
- return (
- <svg
- className={styles.sun}
- viewBox="0 0 100 100"
- xmlns="http://www.w3.org/2000/svg"
- >
- <title>
- {intl.formatMessage({
- defaultMessage: 'Light theme',
- description: 'Icons: Sun icon (light theme)',
- id: 'KeRtm/',
- })}
- </title>
- <path d="M 69.398043,50.000437 A 19.399259,19.399204 0 0 1 49.998784,69.399641 19.399259,19.399204 0 0 1 30.599525,50.000437 19.399259,19.399204 0 0 1 49.998784,30.601234 19.399259,19.399204 0 0 1 69.398043,50.000437 Z m 27.699233,1.125154 c 2.657696,0.0679 1.156196,12.061455 -1.435545,11.463959 L 80.113224,59.000697 c -2.589801,-0.597494 -1.625657,-8.345536 1.032041,-8.278609 z m -18.06653,37.251321 c 1.644087,2.091234 -9.030355,8.610337 -10.126414,6.188346 L 62.331863,80.024585 c -1.096058,-2.423931 5.197062,-6.285342 6.839209,-4.194107 z M 38.611418,97.594444 C 38.02653,100.18909 26.24148,95.916413 27.436475,93.54001 l 7.168026,-14.256474 c 1.194024,-2.376403 8.102101,0.151313 7.517214,2.744986 z M 6.1661563,71.834242 C 3.7916868,73.028262 -0.25499873,61.16274 2.3386824,60.577853 L 17.905618,57.067567 c 2.593681,-0.584886 4.894434,6.403678 2.518995,7.598668 z M 6.146757,30.055146 c -2.3764094,-1.194991 4.46571,-11.714209 6.479353,-9.97798 l 12.090589,10.414462 c 2.014613,1.736229 -1.937017,7.926514 -4.314396,6.731524 z M 38.56777,4.2639045 C 37.982883,1.6682911 50.480855,0.41801247 50.415868,3.0766733 L 50.020123,19.028638 c -0.06596,2.657691 -7.357169,3.394862 -7.943027,0.800218 z m 40.403808,9.1622435 c 1.635357,-2.098023 10.437771,6.872168 8.339742,8.506552 l -12.58818,9.805327 c -2.099,1.634383 -7.192276,-3.626682 -5.557888,-5.724706 z M 97.096306,50.69105 c 2.657696,-0.06596 1.164926,12.462047 -1.425846,11.863582 L 80.122924,58.96578 c -2.590771,-0.597496 -1.636327,-7.814 1.021371,-7.879957 z" />
- </svg>
- );
-};
-
-export default SunIcon;
diff --git a/src/components/Icons/index.tsx b/src/components/Icons/index.tsx
deleted file mode 100644
index 5fe2c19..0000000
--- a/src/components/Icons/index.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import ArrowIcon from './Arrow/Arrow';
-import BlogIcon from './Blog/Blog';
-import CloseIcon from './Close/Close';
-import CogIcon from './Cog/Cog';
-import ContactIcon from './Contact/Contact';
-import CopyrightIcon from './Copyright/Copyright';
-import CVIcon from './CV/CV';
-import HamburgerIcon from './Hamburger/Hamburger';
-import HomeIcon from './Home/Home';
-import MoonIcon from './Moon/Moon';
-import ProjectsIcon from './Projects/Projects';
-import SearchIcon from './Search/Search';
-import SunIcon from './Sun/Sun';
-
-export {
- ArrowIcon,
- BlogIcon,
- CloseIcon,
- CogIcon,
- ContactIcon,
- CopyrightIcon,
- CVIcon,
- HamburgerIcon,
- HomeIcon,
- MoonIcon,
- ProjectsIcon,
- SearchIcon,
- SunIcon,
-};
diff --git a/src/components/Layouts/Layout.module.scss b/src/components/Layouts/Layout.module.scss
deleted file mode 100644
index 33339d4..0000000
--- a/src/components/Layouts/Layout.module.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.noscript {
- width: 100%;
- padding: var(--spacing-xs) var(--spacing-sm);
- position: fixed;
- top: 0;
- z-index: 10;
- background: var(--color-bg);
- border-bottom: fun.convert-px(3) solid var(--color-border);
- color: var(--color-primary-darker);
- font-size: var(--font-size-sm);
- font-weight: 600;
- text-align: center;
-}
-
-.noscript-spacing {
- width: 100%;
- height: fun.convert-px(80);
-}
diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx
deleted file mode 100644
index ada32b3..0000000
--- a/src/components/Layouts/Layout.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import Footer from '@components/Footer/Footer';
-import Header from '@components/Header/Header';
-import Main from '@components/Main/Main';
-import Breadcrumb from '@components/Breadcrumb/Breadcrumb';
-import { settings } from '@utils/config';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import { ReactElement, ReactNode, useEffect, useRef } from 'react';
-import { useIntl } from 'react-intl';
-import { SearchAction, WebSite, WithContext } from 'schema-dts';
-import styles from './Layout.module.scss';
-import Script from 'next/script';
-
-const Layout = ({
- children,
- isHome = false,
-}: {
- children: ReactNode;
- isHome?: boolean;
-}) => {
- const intl = useIntl();
- const { asPath, locale } = useRouter();
- const ref = useRef<HTMLSpanElement>(null);
-
- useEffect(() => {
- ref.current?.focus();
- }, [asPath]);
-
- type QueryAction = SearchAction & {
- 'query-input': string;
- };
-
- const searchActionSchema: QueryAction = {
- '@type': 'SearchAction',
- target: {
- '@type': 'EntryPoint',
- urlTemplate: `${settings.url}/recherche?s={search_term_string}`,
- },
- query: 'required',
- 'query-input': 'required name=search_term_string',
- };
-
- const schemaJsonLd: WithContext<WebSite> = {
- '@context': 'https://schema.org',
- '@id': `${settings.url}`,
- '@type': 'WebSite',
- name: settings.name,
- description: locale?.startsWith('en')
- ? settings.baseline.en
- : settings.baseline.fr,
- url: settings.url,
- author: { '@id': `${settings.url}/#branding` },
- copyrightYear: Number(settings.copyright.startYear),
- creator: { '@id': `${settings.url}/#branding` },
- editor: { '@id': `${settings.url}/#branding` },
- inLanguage: settings.locales.defaultLocale,
- potentialAction: searchActionSchema,
- };
-
- return (
- <>
- <Head>
- <meta property="og:site_name" content={settings.name} />
- <meta
- property="og:locale"
- content={`${settings.locales.defaultLocale}_${settings.locales.defaultCountry}`}
- />
- <meta property="twitter:card" content="summary" />
- <meta property="twitter:site" content={settings.twitterId} />
- <meta property="twitter:creator" content={settings.twitterId} />
- <meta
- name="theme-color"
- content="#14578a"
- media="(prefers-color-scheme: light)"
- />
- <meta
- name="theme-color"
- content="#85bbd6"
- media="(prefers-color-scheme: dark)"
- />
- <link
- rel="alternate"
- href="/feed.xml"
- type="application/rss+xml"
- title={`${settings.name}'s RSS feed`}
- />
- <link
- rel="alternate"
- href="/atom.xml"
- type="application/atom+xml"
- title={`${settings.name}'s Atom feed`}
- />
- <link
- rel="alternate"
- href="/feed.json"
- type="application/feed+json"
- title={`${settings.name}'s Json feed`}
- />
- <link rel="icon" href="/favicon.ico" sizes="any" />
- <link rel="icon" href="/icon.svg" type="image/svg+xml" />
- <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
- <link rel="manifest" href="/manifest.webmanifest" />
- </Head>
- <Script
- id="schema-layout"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- ></Script>
- <Script
- strategy="afterInteractive"
- async
- src={`${settings.ackee.url}/${settings.ackee.filename}`}
- data-ackee-server={settings.ackee.url}
- data-ackee-domain-id={settings.ackee.siteId}
- />
- <noscript>
- <div className={styles['noscript-spacing']}></div>
- </noscript>
- <span ref={ref} tabIndex={-1} />
- <a href="#main" className="screen-reader-text">
- {intl.formatMessage({
- defaultMessage: 'Skip to content',
- description: 'Layout: Skip to content button',
- id: 'iqAbyn',
- })}
- </a>
- <Header isHome={isHome} />
- <Main>{children}</Main>
- <Footer />
- <noscript>
- <div className={styles.noscript}>
- {intl.formatMessage({
- defaultMessage:
- 'Without Javascript, some features may not work like loading more posts or use search. If you want to benefit from these features, please activate Javascript.',
- description: 'Layout: noscript banner',
- id: 'LR70nt',
- })}
- </div>
- </noscript>
- </>
- );
-};
-
-export const getLayout = (page: ReactElement) => {
- const pageTitle: string = page.props.breadcrumbTitle;
-
- return (
- <Layout>
- <Breadcrumb pageTitle={pageTitle} />
- {page}
- </Layout>
- );
-};
-
-export default Layout;
diff --git a/src/components/MDX/CodeBlock/CodeBlock.tsx b/src/components/MDX/CodeBlock/CodeBlock.tsx
deleted file mode 100644
index c330063..0000000
--- a/src/components/MDX/CodeBlock/CodeBlock.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import {
- PrismDefaultPlugins,
- PrismLanguages,
- PrismPlugins,
-} from '@ts/types/prism';
-import { usePrismTheme } from '@utils/providers/prism-theme';
-import { useRouter } from 'next/router';
-import Prism from 'prismjs';
-import { useCallback, useEffect, useMemo } from 'react';
-import { useIntl } from 'react-intl';
-
-const CodeBlock = ({
- code,
- language,
- plugins,
-}: {
- code: string;
- language: PrismLanguages;
- plugins: PrismPlugins[];
-}) => {
- const intl = useIntl();
- const router = useRouter();
- const { setCodeBlocks } = usePrismTheme();
-
- useEffect(() => {
- const allPre: NodeListOf<HTMLPreElement> = document.querySelectorAll(
- 'pre[data-prismjs-color-scheme-current]'
- );
- setCodeBlocks(allPre);
- }, [setCodeBlocks, router.asPath]);
-
- const defaultPlugins: PrismDefaultPlugins[] = useMemo(
- () => [
- 'autoloader',
- 'toolbar',
- 'show-language',
- 'copy-to-clipboard',
- 'color-scheme',
- 'match-braces',
- 'normalize-whitespace',
- ],
- []
- );
-
- const loadPrismPlugins = useCallback(
- async (prismPlugins: (PrismDefaultPlugins | PrismPlugins)[]) => {
- for (const plugin of prismPlugins) {
- try {
- if (plugin === 'color-scheme') {
- await import(`@utils/plugins/prism-${plugin}`);
- } else {
- await import(`prismjs/plugins/${plugin}/prism-${plugin}.min.js`);
-
- if (plugin === 'autoloader')
- Prism.plugins.autoloader.languages_path = '/prism/';
- }
- } catch (error) {
- console.error('CodeBlock: an error occurred with Prism.');
- console.error(error);
- }
- }
- },
- []
- );
-
- useEffect(() => {
- loadPrismPlugins([...defaultPlugins, ...plugins]).then(() => {
- Prism.highlightAll();
- });
- }, [loadPrismPlugins, defaultPlugins, plugins]);
-
- const copyText = intl.formatMessage({
- defaultMessage: 'Copy',
- description: 'Prism: copy button text (no clicked)',
- id: '/ly3AC',
- });
- const copiedText = intl.formatMessage({
- defaultMessage: 'Copied!',
- description: 'Prism: copy button text (clicked)',
- id: 'OV9r1K',
- });
- const errorText = intl.formatMessage({
- defaultMessage: 'Use Ctrl+c to copy',
- description: 'Prism: error text',
- id: 'z9qkcQ',
- });
- const darkTheme = intl.formatMessage({
- defaultMessage: 'Dark Theme 🌙',
- description: 'Prism: toggle dark theme button text',
- id: 'nFMdWI',
- });
- const lightTheme = intl.formatMessage({
- defaultMessage: 'Light Theme 🌞',
- description: 'Prism: toggle light theme button text',
- id: 'Ua2g2p',
- });
-
- const defaultPluginsClasses = 'match-braces';
- const pluginsClasses = plugins.join(' ');
-
- return (
- <pre
- className={`language-${language} ${defaultPluginsClasses} ${pluginsClasses}`}
- data-prismjs-copy={copyText}
- data-prismjs-copy-success={copiedText}
- data-prismjs-copy-error={errorText}
- data-prismjs-color-scheme-dark={darkTheme}
- data-prismjs-color-scheme-light={lightTheme}
- >
- <code className={`language-${language}`}>{code}</code>
- </pre>
- );
-};
-
-export default CodeBlock;
diff --git a/src/components/MDX/Gallery/Gallery.module.scss b/src/components/MDX/Gallery/Gallery.module.scss
deleted file mode 100644
index 2654b59..0000000
--- a/src/components/MDX/Gallery/Gallery.module.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-@use "@styles/abstracts/mixins" as mix;
-@use "@styles/abstracts/placeholders";
-
-.wrapper {
- @extend %reset-list;
-
- display: grid;
- grid-template-columns: minmax(0, 1fr);
- gap: var(--spacing-sm);
- max-width: 100%;
- margin: var(--spacing-sm) 0;
-
- @for $i from 0 to 6 {
- &--#{$i}-columns {
- @include mix.media("screen") {
- @include mix.dimensions("xs") {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
-
- @include mix.dimensions("sm") {
- grid-template-columns: repeat(#{$i}, minmax(0, 1fr));
- }
- }
- }
- }
-}
-
-.item {
- > figure {
- margin: 0;
- }
-}
diff --git a/src/components/MDX/Gallery/Gallery.tsx b/src/components/MDX/Gallery/Gallery.tsx
deleted file mode 100644
index 561ec53..0000000
--- a/src/components/MDX/Gallery/Gallery.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Children, ReactElement } from 'react';
-import styles from './Gallery.module.scss';
-
-const Gallery = ({
- children,
- columns = 2,
-}: {
- children: ReactElement;
- columns: number;
-}) => {
- const columnClass = styles[`wrapper--${columns}-columns`];
-
- return (
- <ul className={`${styles.wrapper} ${columnClass}`}>
- {Children.map(children, (child) => {
- return <li className={styles.item}>{child}</li>;
- })}
- </ul>
- );
-};
-
-export default Gallery;
diff --git a/src/components/MDX/Link/Link.tsx b/src/components/MDX/Link/Link.tsx
deleted file mode 100644
index 40e773b..0000000
--- a/src/components/MDX/Link/Link.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ReactChildren } from 'react';
-
-const Link = ({
- children,
- target,
- isExternal = false,
- lang,
-}: {
- children: ReactChildren;
- target: string;
- isExternal: boolean;
- lang?: string;
-}) => {
- const className = isExternal ? 'external' : '';
-
- return (
- <a href={target} className={className} hrefLang={lang}>
- {children}
- </a>
- );
-};
-
-export default Link;
diff --git a/src/components/MDX/ResponsiveImage/ResponsiveImage.module.scss b/src/components/MDX/ResponsiveImage/ResponsiveImage.module.scss
deleted file mode 100644
index cf2b77f..0000000
--- a/src/components/MDX/ResponsiveImage/ResponsiveImage.module.scss
+++ /dev/null
@@ -1,50 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.wrapper {
- width: 100%;
- max-width: 100%;
- margin: var(--spacing-sm) auto;
- position: relative;
- text-align: center;
-}
-
-.caption {
- margin: 0;
- padding: fun.convert-px(4) var(--spacing-2xs);
- background: var(--color-bg-secondary);
- border: fun.convert-px(1) solid var(--color-border);
- box-shadow: 0 fun.convert-px(-1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow-light);
- font-weight: 500;
-}
-
-.link {
- display: flex;
- flex-flow: column;
- background: none;
- text-decoration: none;
-
- .caption {
- color: var(--color-primary-darker);
- }
-
- &:hover,
- &:focus {
- transform: scale(1.1);
- }
-
- &:focus {
- .caption {
- text-decoration: underline solid var(--color-primary-darker)
- fun.convert-px(3);
- }
- }
-
- &:active {
- transform: scale(0.9);
-
- .caption {
- text-decoration: none;
- }
- }
-}
diff --git a/src/components/MDX/ResponsiveImage/ResponsiveImage.tsx b/src/components/MDX/ResponsiveImage/ResponsiveImage.tsx
deleted file mode 100644
index 6c39e7f..0000000
--- a/src/components/MDX/ResponsiveImage/ResponsiveImage.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { ResponsiveImageProps } from '@ts/types/app';
-import Image from 'next/image';
-import Link from 'next/link';
-import styles from './ResponsiveImage.module.scss';
-
-const ResponsiveImage = (props: ResponsiveImageProps) => {
- const { caption, linkTarget, ...attributes } = props;
-
- return (
- <figure className={styles.wrapper}>
- {linkTarget ? (
- <Link href={linkTarget}>
- <a className={styles.link}>
- <Image
- alt={attributes.alt}
- layout={attributes.layout || 'intrinsic'}
- {...attributes}
- />
- {caption && (
- <figcaption className={styles.caption}>{caption}</figcaption>
- )}
- </a>
- </Link>
- ) : (
- <>
- <Image
- alt={attributes.alt}
- layout={attributes.layout || 'intrinsic'}
- {...attributes}
- />
- {caption && (
- <figcaption className={styles.caption}>{caption}</figcaption>
- )}
- </>
- )}
- </figure>
- );
-};
-
-export default ResponsiveImage;
diff --git a/src/components/MDX/index.tsx b/src/components/MDX/index.tsx
deleted file mode 100644
index bc7aa35..0000000
--- a/src/components/MDX/index.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import CodeBlock from './CodeBlock/CodeBlock';
-import Gallery from './Gallery/Gallery';
-import Link from './Link/Link';
-import ResponsiveImage from './ResponsiveImage/ResponsiveImage';
-
-export { CodeBlock, Gallery, Link, ResponsiveImage };
diff --git a/src/components/Main/Main.module.scss b/src/components/Main/Main.module.scss
deleted file mode 100644
index 819474c..0000000
--- a/src/components/Main/Main.module.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.wrapper {
- flex: 1;
-
- :global {
- animation: fade-in 1.5s ease-in-out 0s 1;
- }
-}
diff --git a/src/components/Main/Main.tsx b/src/components/Main/Main.tsx
deleted file mode 100644
index b21ab1c..0000000
--- a/src/components/Main/Main.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { FunctionComponent } from 'react';
-import styles from './Main.module.scss';
-
-const Main: FunctionComponent = ({ children }) => {
- return (
- <main id="main" className={styles.wrapper}>
- {children}
- </main>
- );
-};
-
-export default Main;
diff --git a/src/components/MainNav/MainNav.module.scss b/src/components/MainNav/MainNav.module.scss
deleted file mode 100644
index f3e6c10..0000000
--- a/src/components/MainNav/MainNav.module.scss
+++ /dev/null
@@ -1,242 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-@use "@styles/abstracts/placeholders";
-
-.wrapper {
- --icon-size: #{fun.convert-px(30)};
-
- display: flex;
- flex-flow: column nowrap;
- align-items: center;
- height: var(--btn-size);
- width: calc(var(--btn-size) * 1.2);
- background: var(--color-bg);
- position: relative;
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- background: inherit;
- }
-
- @include mix.dimensions("md") {
- width: unset;
- height: unset;
- }
- }
-}
-
-.label {
- --draw-border-thickness: #{fun.convert-px(5)};
- --draw-border-color1: var(--color-primary-light);
- --draw-border-color2: var(--color-primary-lighter);
-
- flex: 1;
- display: flex;
- flex-flow: column nowrap;
- width: 100%;
- padding: var(--spacing-2xs);
-
- &:hover {
- @extend %draw-borders;
- }
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- display: none;
- }
- }
-}
-
-.checkbox {
- position: absolute;
-
- // centered checkbox = btn-size - approximated checkbox size / 2
- top: calc((var(--btn-size) - #{fun.convert-px(14)}) / 2);
- left: calc(((var(--btn-size) * 1.2) - #{fun.convert-px(14)}) / 2);
- opacity: 0;
- cursor: pointer;
-
- &:hover {
- ~ .label {
- @extend %draw-borders;
- }
- }
-
- &:focus {
- ~ .label {
- @extend %draw-borders;
- }
- }
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- display: none;
- }
- }
-}
-
-.nav {
- display: flex;
- flex-flow: column wrap;
- place-content: center;
- padding-bottom: var(--toolbar-size);
- position: fixed;
- bottom: 0;
- z-index: -1;
- background: var(--color-bg-opacity);
- box-shadow: 0 0 fun.convert-px(3) 0 var(--color-shadow-dark);
- text-align: center;
- opacity: 1;
- visibility: visible;
- transform: translateY(0);
- transition: all 0.8s ease-in-out 0s;
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- padding-bottom: 0;
- position: absolute;
- bottom: auto;
- left: auto;
- right: auto;
- top: calc(var(--btn-size) + var(--spacing-sm));
- z-index: unset;
- border-bottom-width: fun.convert-px(5);
- transform-origin: 50% -100%;
- }
-
- @include mix.dimensions("md") {
- background: transparent;
- border: none;
- box-shadow: none;
- position: relative;
- top: 0;
- }
- }
-}
-
-.list {
- @extend %reset-list;
-
- @include mix.media("screen") {
- @include mix.dimensions(null, "2xs", "height") {
- display: grid;
- grid-template-columns: min-content min-content;
- max-height: calc(100vh - var(--toolbar-size));
- }
-
- @include mix.dimensions("md") {
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- gap: var(--spacing-2xs);
- }
- }
-}
-
-.link {
- --draw-border-thickness: #{fun.convert-px(4)};
- --draw-border-color1: var(--color-primary-light);
- --draw-border-color2: var(--color-primary-lighter);
-
- display: block;
- min-width: fun.convert-px(85);
- padding: var(--spacing-xs) var(--spacing-xs) var(--spacing-2xs);
- background: var(--color-bg);
- background-repeat: no-repeat;
- font-size: var(--font-size-sm);
- font-variant: small-caps;
- font-weight: 600;
- text-decoration: none;
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- margin: 0;
- background-color: inherit;
- border-radius: 8%;
- }
- }
-
- &:hover,
- &:focus {
- @extend %draw-borders;
- }
-
- &:focus {
- color: var(--color-primary-light);
- }
-
- &:active {
- --draw-border-color1: var(--color-primary-dark);
- --draw-border-color2: var(--color-primary-light);
-
- @extend %draw-borders;
- }
-
- &.current {
- background-image: linear-gradient(to right, transparent, transparent),
- linear-gradient(to bottom, transparent, transparent),
- linear-gradient(
- to left,
- var(--color-primary-lighter),
- var(--color-primary-light)
- ),
- linear-gradient(to top, transparent, transparent);
- background-position: top left, top right, bottom center, bottom left;
- background-size: 0% var(--draw-border-thickness),
- var(--draw-border-thickness) 0%, 60% var(--draw-border-thickness),
- var(--draw-border-thickness) 0%;
-
- &:hover,
- &:focus {
- --draw-border-color1: var(--color-primary-light);
- --draw-border-color2: var(--color-primary-lighter);
-
- @extend %draw-borders;
- }
-
- &:active {
- --draw-border-color1: var(--color-primary-dark);
- --draw-border-color2: var(--color-primary-light);
-
- @extend %draw-borders;
- }
- }
-}
-
-.checkbox:not(:checked) {
- ~ .nav {
- opacity: 0;
- visibility: hidden;
- transform: translateY(100vw);
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- transform: perspective(20rem) translate3d(0, 100%, -20rem);
- }
-
- @include mix.dimensions("md") {
- opacity: 1;
- visibility: visible;
- transform: none;
- }
- }
- }
-}
-
-.checkbox:checked {
- ~ .label:hover {
- span {
- background: none;
- box-shadow: none;
- }
- }
-
- &:hover {
- ~ .label {
- span {
- background: none;
- box-shadow: none;
- }
- }
- }
-}
diff --git a/src/components/MainNav/MainNav.tsx b/src/components/MainNav/MainNav.tsx
deleted file mode 100644
index 9cb6b4c..0000000
--- a/src/components/MainNav/MainNav.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import {
- BlogIcon,
- ContactIcon,
- CVIcon,
- HamburgerIcon,
- HomeIcon,
- ProjectsIcon,
-} from '@components/Icons';
-import { NavItem } from '@ts/types/nav';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import { ForwardedRef, forwardRef, SetStateAction } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './MainNav.module.scss';
-
-const MainNav = (
- {
- isOpened,
- setIsOpened,
- }: {
- isOpened: boolean;
- setIsOpened: (value: SetStateAction<boolean>) => void;
- },
- ref: ForwardedRef<HTMLDivElement>
-) => {
- const intl = useIntl();
- const router = useRouter();
-
- const mainNavConfig: NavItem[] = [
- {
- id: 'home',
- name: intl.formatMessage({
- defaultMessage: 'Home',
- description: 'MainNav: home link',
- id: 'ZJMNRW',
- }),
- slug: '/',
- },
- {
- id: 'blog',
- name: intl.formatMessage({
- defaultMessage: 'Blog',
- description: 'MainNav: blog link',
- id: 'zPJifH',
- }),
- slug: '/blog',
- },
- {
- id: 'projects',
- name: intl.formatMessage({
- defaultMessage: 'Projects',
- description: 'MainNav: projects link',
- id: 'akSutM',
- }),
- slug: '/projets',
- },
- {
- id: 'cv',
- name: intl.formatMessage({
- defaultMessage: 'Resume',
- description: 'MainNav: resume link',
- id: 'jpv+Nz',
- }),
- slug: '/cv',
- },
- {
- id: 'contact',
- name: intl.formatMessage({
- defaultMessage: 'Contact',
- description: 'MainNav: contact link',
- id: 'c2NtPj',
- }),
- slug: '/contact',
- },
- ];
-
- const getIcon = (id: string) => {
- switch (id) {
- case 'home':
- return <HomeIcon />;
- case 'blog':
- return <BlogIcon />;
- case 'contact':
- return <ContactIcon />;
- case 'cv':
- return <CVIcon />;
- case 'projects':
- return <ProjectsIcon />;
- default:
- break;
- }
- };
-
- const navItems = mainNavConfig.map((item) => {
- const currentClass = router.asPath === item.slug ? styles.current : '';
-
- return (
- <li key={item.id}>
- <Link href={item.slug}>
- <a className={`${styles.link} ${currentClass}`}>
- {getIcon(item.id)}
- <span>{item.name}</span>
- </a>
- </Link>
- </li>
- );
- });
-
- return (
- <div id="main-nav" ref={ref} className={styles.wrapper}>
- <input
- type="checkbox"
- name="main-nav__checkbox"
- id="main-nav__checkbox"
- aria-labelledby="main-nav-toggle"
- className={styles.checkbox}
- checked={isOpened}
- onChange={() => setIsOpened(!isOpened)}
- autoComplete="off"
- />
- <label
- htmlFor="main-nav__checkbox"
- id="main-nav-toggle"
- className={styles.label}
- >
- <HamburgerIcon isActive={isOpened} />
- <span className="screen-reader-text">
- {isOpened
- ? intl.formatMessage({
- defaultMessage: 'Close menu',
- description: 'MainNav: close button',
- id: 'dE8xxV',
- })
- : intl.formatMessage({
- defaultMessage: 'Open menu',
- description: 'MainNav: open button',
- id: 'azc1GT',
- })}
- </span>
- </label>
- <nav
- className={styles.nav}
- aria-label={intl.formatMessage({
- defaultMessage: 'Primary',
- description: 'MainNav: aria-label',
- id: 'H7C5Bk',
- })}
- >
- <ul className={styles.list}>{navItems}</ul>
- </nav>
- </div>
- );
-};
-
-export default forwardRef(MainNav);
diff --git a/src/components/MetaItems/Author/Author.tsx b/src/components/MetaItems/Author/Author.tsx
deleted file mode 100644
index 4ff0086..0000000
--- a/src/components/MetaItems/Author/Author.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-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',
- id: 'jCyqZS',
- })}
- value={name}
- kind={kind}
- />
- );
-};
-
-export default Author;
diff --git a/src/components/MetaItems/CommentsCount/CommentsCount.tsx b/src/components/MetaItems/CommentsCount/CommentsCount.tsx
deleted file mode 100644
index 04cffa6..0000000
--- a/src/components/MetaItems/CommentsCount/CommentsCount.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-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',
- id: 'lKGNKx',
- },
- { total }
- );
- };
-
- return (
- <MetaItem
- title={intl.formatMessage({
- defaultMessage: 'Comments:',
- description: 'CommentsCount: comment count meta label',
- id: '6BRtAu',
- })}
- 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
deleted file mode 100644
index 4314ed9..0000000
--- a/src/components/MetaItems/Dates/Dates.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-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',
- id: '52Fev1',
- })}
- 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',
- id: 'C+r/LF',
- })}
- 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
deleted file mode 100644
index 0b159ca..0000000
--- a/src/components/MetaItems/MetaItem/MetaItem.module.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-.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
deleted file mode 100644
index 5c51283..0000000
--- a/src/components/MetaItems/MetaItem/MetaItem.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-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
deleted file mode 100644
index 679abcd..0000000
--- a/src/components/MetaItems/PostsCount/PostsCount.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-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',
- id: 'p1zZ/Z',
- })}
- value={intl.formatMessage(
- {
- defaultMessage:
- '{total, plural, =0 {No articles} one {# article} other {# articles}}',
- description: 'PostCount: total found articles',
- id: '4EMSLO',
- },
- { total }
- )}
- kind={kind}
- />
- );
-};
-
-export default PostsCount;
diff --git a/src/components/MetaItems/ReadingTime/ReadingTime.tsx b/src/components/MetaItems/ReadingTime/ReadingTime.tsx
deleted file mode 100644
index 79d6f3c..0000000
--- a/src/components/MetaItems/ReadingTime/ReadingTime.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-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',
- id: 'ySsWZl',
- });
- }
-
- return intl.formatMessage(
- {
- defaultMessage:
- '{time, plural, =0 {# minutes} one {# minute} other {# minutes}}',
- description: 'ReadingTime: reading time value',
- id: 'wdqOpf',
- },
- { time }
- );
- };
-
- return (
- <MetaItem
- title={intl.formatMessage({
- defaultMessage: 'Reading time:',
- description: 'ReadingTime: reading time meta label',
- id: 'n0Gbod',
- })}
- value={getEstimation()}
- info={intl.formatMessage(
- {
- defaultMessage: 'Approximately {number} words',
- description: 'ReadingTime: number of words',
- id: 'k7/SkN',
- },
- { 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
deleted file mode 100644
index e655c5d..0000000
--- a/src/components/MetaItems/Thematics/Thematics.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-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',
- id: '1r4ujR',
- },
- { 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
deleted file mode 100644
index d5d90f0..0000000
--- a/src/components/MetaItems/Topics/Topics.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-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',
- id: '0pp/IQ',
- },
- { 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
deleted file mode 100644
index 7d2dc06..0000000
--- a/src/components/MetaItems/Website/Website.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-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',
- id: 'JsOoAW',
- })}
- 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
deleted file mode 100644
index e90d5a6..0000000
--- a/src/components/MetaItems/index.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-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/Notice/Notice.module.scss b/src/components/Notice/Notice.module.scss
deleted file mode 100644
index aa7175c..0000000
--- a/src/components/Notice/Notice.module.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.message {
- border: fun.convert-px(2) solid;
- font-weight: bold;
- margin: var(--spacing-sm) auto;
- padding: var(--spacing-2xs) var(--spacing-xs);
-
- &--error {
- border-color: var(--color-token-red);
- color: var(--color-token-red);
- }
-
- &--info {
- border-color: var(--color-token-blue);
- color: var(--color-token-blue);
- }
-
- &--success {
- border-color: var(--color-token-green);
- color: var(--color-token-green);
- }
-
- &--warning {
- border-color: var(--color-token-orange);
- color: var(--color-token-orange);
- }
-}
diff --git a/src/components/Notice/Notice.tsx b/src/components/Notice/Notice.tsx
deleted file mode 100644
index 02b1f12..0000000
--- a/src/components/Notice/Notice.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { NoticeType } from '@ts/types/app';
-import { ReactNode } from 'react';
-import styles from './Notice.module.scss';
-
-const Notice = ({
- children,
- type,
-}: {
- children: ReactNode;
- type: NoticeType;
-}) => {
- const withModifier = `message--${type}`;
-
- return (
- <div className={`${styles.message} ${styles[withModifier]}`}>
- {children}
- </div>
- );
-};
-
-export default Notice;
diff --git a/src/components/Pagination/Pagination.module.scss b/src/components/Pagination/Pagination.module.scss
deleted file mode 100644
index 4d74d1b..0000000
--- a/src/components/Pagination/Pagination.module.scss
+++ /dev/null
@@ -1,92 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-@use "@styles/abstracts/placeholders";
-
-.list {
- @extend %flex-list;
- justify-content: center;
-
- row-gap: var(--spacing-sm);
-}
-
-.link {
- display: block;
- padding: var(--spacing-xs) var(--spacing-sm);
- background: var(--color-bg);
- border: fun.convert-px(2) solid var(--color-primary);
- box-shadow: fun.convert-px(2) fun.convert-px(2) 0 0
- var(--color-primary-darker);
- font-weight: 600;
- text-decoration: none;
-
- @include mix.pointer("fine") {
- padding: var(--spacing-2xs) var(--spacing-xs);
- }
-
- &--current {
- padding: calc(var(--spacing-xs) / 1.5) var(--spacing-sm);
- border-color: var(--color-primary-darker);
- box-shadow: none;
- color: var(--color-primary-darker);
- transform: translateY(#{fun.convert-px(10)});
-
- @include mix.pointer("fine") {
- padding: calc(var(--spacing-2xs) / 1.5) var(--spacing-xs);
- transform: translateY(#{fun.convert-px(7)});
- }
- }
-
- &:not(.link--current) {
- &:hover,
- &:focus {
- border-color: var(--color-primary-light);
- box-shadow: fun.convert-px(2) fun.convert-px(2) 0 0
- var(--color-primary-darker),
- 0 fun.convert-px(2) fun.convert-px(2) fun.convert-px(1)
- var(--color-shadow-dark),
- 0 fun.convert-px(7) fun.convert-px(7) fun.convert-px(2)
- var(--color-shadow-light);
- color: var(--color-primary-light);
- transform: translateY(#{fun.convert-px(-5)});
- }
-
- &:active {
- padding: calc(var(--spacing-xs) / 1.5) var(--spacing-sm);
- border-color: var(--color-primary-dark);
- box-shadow: none;
- color: var(--color-primary-dark);
- transform: translateY(#{fun.convert-px(10)});
-
- @include mix.pointer("fine") {
- padding: calc(var(--spacing-2xs) / 1.5) var(--spacing-xs);
- transform: translateY(#{fun.convert-px(7)});
- }
- }
- }
-}
-
-.item {
- position: relative;
-
- &:first-child {
- .link {
- border-top-left-radius: fun.convert-px(4);
- border-bottom-left-radius: fun.convert-px(4);
- }
- }
-
- &:last-child {
- .link {
- border-top-right-radius: fun.convert-px(4);
- border-bottom-right-radius: fun.convert-px(4);
- }
- }
-
- &:not(:first-child) {
- margin-left: fun.convert-px(-1);
- }
-
- &:not(:last-child) {
- margin-right: fun.convert-px(-1);
- }
-}
diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx
deleted file mode 100644
index 55c366a..0000000
--- a/src/components/Pagination/Pagination.tsx
+++ /dev/null
@@ -1,136 +0,0 @@
-import { settings } from '@utils/config';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import { useIntl } from 'react-intl';
-import styles from './Pagination.module.scss';
-
-const Pagination = ({ baseUrl, total }: { baseUrl: string; total: number }) => {
- const intl = useIntl();
- const { asPath } = useRouter();
- const totalPages = Math.floor(total / settings.postsPerPage);
- const currentPage = asPath.includes('/page/')
- ? Number(asPath.split(`${baseUrl}/page/`)[1])
- : 1;
- const hasPreviousPage = currentPage !== 1;
- const hasNextPage = currentPage !== totalPages;
-
- const getPreviousPageItem = () => {
- return (
- <li className={styles.item}>
- <Link href={`${baseUrl}/page/${currentPage - 1}`}>
- <a className={styles.link}>
- {intl.formatMessage(
- {
- defaultMessage: '{icon} Previous page',
- description: 'Pagination: previous page link',
- id: 'aMFqPH',
- },
- { icon: '←' }
- )}
- </a>
- </Link>
- </li>
- );
- };
-
- const getNextPageItem = () => {
- return (
- <li className={styles.item}>
- <Link href={`${baseUrl}/page/${currentPage + 1}`}>
- <a className={styles.link}>
- {intl.formatMessage(
- {
- defaultMessage: 'Next page {icon}',
- description: 'Pagination: Next page link',
- id: 'R4yaW6',
- },
- { icon: '→' }
- )}
- </a>
- </Link>
- </li>
- );
- };
-
- const getPages = () => {
- const pages = [];
- for (let i = 1; i <= totalPages; i++) {
- if (i === currentPage) {
- pages.push({
- id: `page-${i}`,
- link: (
- <span className={`${styles.link} ${styles['link--current']}`}>
- {intl.formatMessage(
- {
- defaultMessage: '<a11y>Page </a11y>{number}',
- description: 'Pagination: page number',
- id: 'TSXPzr',
- },
- {
- number: i,
- a11y: (chunks: string) => (
- <span className="screen-reader-text">{chunks}</span>
- ),
- }
- )}
- </span>
- ),
- });
- } else {
- pages.push({
- id: `page-${i}`,
- link: (
- <Link href={`${baseUrl}/page/${i}`}>
- <a className={styles.link}>
- {intl.formatMessage(
- {
- defaultMessage: '<a11y>Page </a11y>{number}',
- description: 'Pagination: page number',
- id: 'TSXPzr',
- },
- {
- number: i,
- a11y: (chunks: string) => (
- <span className="screen-reader-text">{chunks}</span>
- ),
- }
- )}
- </a>
- </Link>
- ),
- });
- }
- }
-
- return pages;
- };
-
- const getItems = () => {
- const pages = getPages();
-
- return pages.map((page) => (
- <li key={page.id} className={styles.item}>
- {page.link}
- </li>
- ));
- };
-
- return (
- <nav className={styles.wrapper} aria-labelledby="pagination-title">
- <h2 id="pagination-title" className="screen-reader-text">
- {intl.formatMessage({
- defaultMessage: 'Pagination',
- description: 'Pagination: pagination title',
- id: 'BAkq7J',
- })}
- </h2>
- <ul className={styles.list}>
- {hasPreviousPage && getPreviousPageItem()}
- {getItems()}
- {hasNextPage && getNextPageItem()}
- </ul>
- </nav>
- );
-};
-
-export default Pagination;
diff --git a/src/components/PaginationCursor/PaginationCursor.module.scss b/src/components/PaginationCursor/PaginationCursor.module.scss
deleted file mode 100644
index 542584c..0000000
--- a/src/components/PaginationCursor/PaginationCursor.module.scss
+++ /dev/null
@@ -1,43 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.wrapper {
- width: max-content;
- margin: var(--spacing-sm) auto var(--spacing-md);
- text-align: center;
-
- .bar[value] {
- display: block;
- width: clamp(25ch, 20vw, 30ch);
- max-width: 100%;
- height: fun.convert-px(13);
- appearance: none;
- background: var(--color-bg-tertiary);
- border: fun.convert-px(1) solid var(--color-primary-darker);
- border-radius: 1em;
- box-shadow: inset 0 0 fun.convert-px(4) fun.convert-px(1)
- var(--color-shadow-light);
-
- &::-webkit-progress-value {
- background-color: var(--color-primary-dark);
- border-radius: 1em;
- }
-
- &::-moz-progress-bar {
- background-color: var(--color-primary-dark);
- border-radius: 1em;
- }
-
- &::-webkit-progress-bar {
- background: var(--color-bg-tertiary);
- border: fun.convert-px(1) solid var(--color-primary-darker);
- border-radius: 1em;
- box-shadow: inset 0 0 fun.convert-px(4) fun.convert-px(1)
- var(--color-shadow-light);
- }
- }
-}
-
-.info {
- margin-bottom: var(--spacing-2xs);
- font-size: var(--font-size-sm);
-}
diff --git a/src/components/PaginationCursor/PaginationCursor.tsx b/src/components/PaginationCursor/PaginationCursor.tsx
deleted file mode 100644
index d64f961..0000000
--- a/src/components/PaginationCursor/PaginationCursor.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { useIntl } from 'react-intl';
-import styles from './PaginationCursor.module.scss';
-
-const PaginationCursor = ({
- current,
- total,
-}: {
- current: number;
- total: number;
-}) => {
- const intl = useIntl();
-
- return (
- <div className={styles.wrapper}>
- <div className={styles.info}>
- {intl.formatMessage(
- {
- defaultMessage:
- '{articlesCount, plural, =0 {# loaded articles} one {# loaded article} other {# loaded articles}} out of a total of {total}',
- description: 'PaginationCursor: loaded articles count message',
- id: 'du4MLN',
- },
- { articlesCount: current, total }
- )}
- </div>
- <progress
- className={styles.bar}
- max={total}
- value={current}
- aria-valuemin={0}
- aria-valuemax={total}
- aria-label={intl.formatMessage({
- defaultMessage:
- 'Number of articles loaded out of the total available.',
- description: 'PaginationCursor: loaded articles count aria-label',
- id: 'mC21ht',
- })}
- ></progress>
- </div>
- );
-};
-
-export default PaginationCursor;
diff --git a/src/components/PostFooter/PostFooter.module.scss b/src/components/PostFooter/PostFooter.module.scss
deleted file mode 100644
index 7c1f1ce..0000000
--- a/src/components/PostFooter/PostFooter.module.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/placeholders";
-
-.meta {
- flex-flow: column wrap;
-}
-
-.list {
- @extend %flex-list;
-
- gap: var(--spacing-xs);
-}
-
-.item {
- > a {
- padding: calc(var(--spacing-2xs) / 2) var(--spacing-xs);
- }
-}
diff --git a/src/components/PostFooter/PostFooter.tsx b/src/components/PostFooter/PostFooter.tsx
deleted file mode 100644
index 9bc4053..0000000
--- a/src/components/PostFooter/PostFooter.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { ButtonLink } from '@components/Buttons';
-import { TopicPreview } from '@ts/types/taxonomies';
-import Image from 'next/image';
-import { useIntl } from 'react-intl';
-import styles from './PostFooter.module.scss';
-
-const PostFooter = ({ topics }: { topics: TopicPreview[] }) => {
- const intl = useIntl();
-
- const getTopics = () => {
- return topics.map((topic) => {
- return (
- <li className={styles.item} key={topic.id}>
- <ButtonLink target={`/sujet/${topic.slug}`}>
- {topic.featuredImage && (
- <Image
- src={topic.featuredImage.sourceUrl}
- alt={topic.featuredImage.altText}
- layout="intrinsic"
- width="20"
- height="20"
- />
- )}
- {topic.title}
- </ButtonLink>
- </li>
- );
- });
- };
-
- return (
- <footer>
- {topics.length > 0 && (
- <>
- <dl className={styles.meta}>
- <dt>
- {intl.formatMessage({
- defaultMessage: 'Read more articles about:',
- description: 'PostFooter: read more posts about given subjects',
- id: 'YEudoh',
- })}
- </dt>
- <dd>
- <ul className={styles.list}>{getTopics()}</ul>
- </dd>
- </dl>
- </>
- )}
- </footer>
- );
-};
-
-export default PostFooter;
diff --git a/src/components/PostHeader/PostHeader.module.scss b/src/components/PostHeader/PostHeader.module.scss
deleted file mode 100644
index f813060..0000000
--- a/src/components/PostHeader/PostHeader.module.scss
+++ /dev/null
@@ -1,83 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- composes: grid from "@styles/layout/_grid.scss";
- max-width: 100%;
- position: relative;
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- margin-bottom: var(--spacing-md);
- }
-
- @include mix.dimensions("lg") {
- --grid-gap: var(--spacing-lg);
- }
- }
-
- &::before,
- &::after {
- content: "";
- width: 100%;
- height: 100%;
- background: var(--color-bg-secondary);
- border-top: fun.convert-px(3) solid var(--color-border-light);
- border-bottom: fun.convert-px(3) solid var(--color-border-light);
- }
-
- &::before {
- grid-column: 1;
- justify-self: start;
- border-right: fun.convert-px(3) solid var(--color-border-light);
- }
-
- &::after {
- grid-column: 3;
- justify-self: end;
- border-left: fun.convert-px(3) solid var(--color-border-light);
- }
-}
-
-.body {
- grid-column: 2;
- background: var(--color-bg);
-}
-
-.title {
- flex: 0 0 100%;
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- margin: 0;
- position: relative;
- text-shadow: fun.convert-px(1) fun.convert-px(1) 0 var(--color-shadow-light);
-
- &::before,
- &::after {
- content: "";
- width: 100%;
- height: fun.convert-px(4);
- background: radial-gradient(
- ellipse at center,
- var(--color-primary-light),
- var(--color-primary-dark)
- );
- }
-}
-
-.cover {
- display: block;
- width: fun.convert-px(50);
- height: fun.convert-px(50);
- position: relative;
- margin-right: var(--spacing-sm);
-}
-
-.intro {
- margin: var(--spacing-sm) 0 0;
-
- > *:last-child {
- margin-bottom: 0;
- }
-}
diff --git a/src/components/PostHeader/PostHeader.tsx b/src/components/PostHeader/PostHeader.tsx
deleted file mode 100644
index c0a6b68..0000000
--- a/src/components/PostHeader/PostHeader.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import PostMeta from '@components/PostMeta/PostMeta';
-import { ArticleMeta } from '@ts/types/articles';
-import { Cover } from '@ts/types/cover';
-import Image from 'next/image';
-import React, { ReactElement } from 'react';
-import styles from './PostHeader.module.scss';
-
-const PostHeader = ({
- cover,
- intro,
- title,
- meta,
-}: {
- cover?: Cover;
- intro?: string | ReactElement;
- meta?: ArticleMeta;
- title: string;
-}) => {
- const getIntro = () => {
- if (React.isValidElement(intro)) {
- const Intro = () => intro;
- return (
- <div className={styles.intro}>
- <Intro />
- </div>
- );
- }
-
- return (
- intro && (
- <div
- className={styles.intro}
- dangerouslySetInnerHTML={{ __html: intro }}
- ></div>
- )
- );
- };
-
- return (
- <header className={styles.wrapper}>
- <div className={styles.body}>
- <h1 className={styles.title}>
- {cover && (
- <span className={styles.cover}>
- <Image src={cover.sourceUrl} alt={cover.altText} layout="fill" />
- </span>
- )}
- {title}
- </h1>
- {meta && <PostMeta kind="article" meta={meta} />}
- {getIntro()}
- </div>
- </header>
- );
-};
-
-export default PostHeader;
diff --git a/src/components/PostMeta/PostMeta.module.scss b/src/components/PostMeta/PostMeta.module.scss
deleted file mode 100644
index d438635..0000000
--- a/src/components/PostMeta/PostMeta.module.scss
+++ /dev/null
@@ -1,31 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- &--list {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- margin-top: var(--spacing-md);
- font-size: var(--font-size-sm);
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- display: flex;
- flex-flow: column nowrap;
- margin: 0;
- composes: meta from "@components/PostPreview/PostPreview.module.scss";
- }
- }
- }
-
- &--article {
- flex-flow: column wrap;
- margin: var(--spacing-sm) 0 0;
-
- @include mix.media("screen") {
- @include mix.dimensions("xs") {
- font-size: var(--font-size-sm);
- }
- }
- }
-}
diff --git a/src/components/PostMeta/PostMeta.tsx b/src/components/PostMeta/PostMeta.tsx
deleted file mode 100644
index 7fba0be..0000000
--- a/src/components/PostMeta/PostMeta.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-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 { useRouter } from 'next/router';
-import styles from './PostMeta.module.scss';
-
-const PostMeta = ({
- meta,
- kind = 'list',
-}: {
- meta: ArticleMeta;
- kind?: MetaKind;
-}) => {
- const {
- author,
- commentCount,
- dates,
- readingTime,
- results,
- thematics,
- topics,
- website,
- wordsCount,
- } = meta;
- const { asPath } = useRouter();
- const isThematic = () => asPath.includes('/thematique/');
-
- const wrapperClass = styles[`wrapper--${kind}`];
-
- return (
- <dl className={wrapperClass}>
- {author && <Author name={author.name} kind={kind} />}
- {dates && (
- <Dates
- publication={dates.publication}
- update={dates.update}
- kind={kind}
- />
- )}
- {readingTime !== undefined && wordsCount !== undefined && (
- <ReadingTime time={readingTime} words={wordsCount} kind={kind} />
- )}
- {results !== undefined && <PostsCount total={results} kind={kind} />}
- {!isThematic() && thematics && thematics.length > 0 && (
- <Thematics list={thematics} kind={kind} />
- )}
- {isThematic() && topics && topics.length > 0 && (
- <Topics list={topics} kind={kind} />
- )}
- {website && <Website url={website} kind={kind} />}
- {commentCount !== undefined && (
- <CommentsCount total={commentCount} kind={kind} />
- )}
- </dl>
- );
-};
-
-export default PostMeta;
diff --git a/src/components/PostPreview/PostPreview.module.scss b/src/components/PostPreview/PostPreview.module.scss
deleted file mode 100644
index c30ab75..0000000
--- a/src/components/PostPreview/PostPreview.module.scss
+++ /dev/null
@@ -1,105 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- --icon-size: #{fun.convert-px(20)};
-
- padding: var(--spacing-2xs) 0 var(--spacing-lg);
- transition: all 0.3s ease-in-out 0s, border 0s;
-
- &:hover {
- --icon-size: #{fun.convert-px(25)};
-
- a {
- > svg {
- :global {
- animation: pulse 1.5s ease-in-out 0.5s infinite;
- }
- }
-
- &:hover {
- > svg {
- animation: none;
- }
- }
- }
- }
-
- &:active {
- --icon-size: 0;
- }
-}
-
-.cover {
- width: auto;
- height: fun.convert-px(100);
- margin: 0 auto var(--spacing-sm);
- position: relative;
- border: fun.convert-px(1) solid var(--color-border);
-}
-
-h2.title {
- background: none;
- text-shadow: none;
-}
-
-@include mix.media("screen") {
- @include mix.dimensions("xs") {
- .wrapper {
- margin: 0;
- padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-md);
- border: fun.convert-px(1) solid var(--color-primary-dark);
- border-radius: fun.convert-px(3);
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) 0
- var(--color-shadow),
- fun.convert-px(3) fun.convert-px(3) fun.convert-px(3) fun.convert-px(-1)
- var(--color-shadow-light),
- fun.convert-px(5) fun.convert-px(5) fun.convert-px(7) fun.convert-px(-1)
- var(--color-shadow-light);
- }
-
- .read-more {
- font-size: var(--font-size-sm);
- }
- }
-
- @include mix.dimensions("sm") {
- .wrapper {
- display: grid;
- grid-template-columns: minmax(0, 3fr) minmax(0, 1fr);
- grid-template-rows: repeat(3, max-content);
- column-gap: var(--spacing-md);
- }
-
- .cover {
- grid-column: 2;
- grid-row: 1;
- margin: 0 0 var(--spacing-sm);
- }
-
- .header {
- grid-column: 1;
- grid-row: 1;
- align-self: center;
- }
-
- .meta {
- grid-column: 2;
- grid-row: 2 / 4;
- }
-
- .body {
- grid-column: 1;
- grid-row: 2;
- }
-
- .footer {
- grid-column: 1;
- grid-row: 3;
- }
-
- .read-more {
- margin: 0;
- }
- }
-}
diff --git a/src/components/PostPreview/PostPreview.tsx b/src/components/PostPreview/PostPreview.tsx
deleted file mode 100644
index 0b9e332..0000000
--- a/src/components/PostPreview/PostPreview.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import { ButtonLink } from '@components/Buttons';
-import { ArrowIcon } from '@components/Icons';
-import PostMeta from '@components/PostMeta/PostMeta';
-import { TitleLevel } from '@ts/types/app';
-import { ArticleMeta, ArticlePreview } from '@ts/types/articles';
-import { settings } from '@utils/config';
-import Image from 'next/image';
-import Link from 'next/link';
-import { FormattedMessage } from 'react-intl';
-import { BlogPosting, WithContext } from 'schema-dts';
-import styles from './PostPreview.module.scss';
-import Script from 'next/script';
-
-const PostPreview = ({
- post,
- titleLevel,
-}: {
- post: ArticlePreview;
- titleLevel: TitleLevel;
-}) => {
- const TitleTag = `h${titleLevel}` as keyof JSX.IntrinsicElements;
- const {
- commentCount,
- dates,
- featuredImage,
- info,
- intro,
- slug,
- thematics,
- title,
- topics,
- } = post;
-
- const meta: ArticleMeta = {
- commentCount: commentCount ? commentCount : 0,
- dates: dates,
- readingTime: info.readingTime,
- thematics: thematics,
- topics: topics,
- wordsCount: info.wordsCount,
- };
-
- const publicationDate = new Date(dates.publication);
- const updateDate = new Date(dates.update);
-
- const schemaJsonLd: WithContext<BlogPosting> = {
- '@context': 'https://schema.org',
- '@type': 'BlogPosting',
- name: title,
- description: intro,
- articleBody: intro,
- author: { '@id': `${settings.url}/#branding` },
- commentCount: commentCount ? commentCount : 0,
- copyrightYear: publicationDate.getFullYear(),
- creator: { '@id': `${settings.url}/#branding` },
- dateCreated: publicationDate.toISOString(),
- dateModified: updateDate.toISOString(),
- datePublished: publicationDate.toISOString(),
- editor: { '@id': `${settings.url}/#branding` },
- headline: title,
- image: featuredImage?.sourceUrl,
- inLanguage: settings.locales.defaultLocale,
- isBasedOn: `${settings.url}/article/${slug}`,
- isPartOf: { '@id': `${settings.url}/blog` },
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- thumbnailUrl: featuredImage?.sourceUrl,
- };
-
- return (
- <>
- <Script
- id="schema-post-preview"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article className={styles.wrapper}>
- {featuredImage && Object.keys(featuredImage).length > 0 && (
- <div className={styles.cover}>
- <Image
- src={featuredImage.sourceUrl}
- alt={featuredImage.altText}
- layout="fill"
- objectFit="contain"
- />
- </div>
- )}
- <header className={styles.header}>
- <TitleTag className={styles.title}>
- <Link href={`/article/${slug}`}>
- <a>{title}</a>
- </Link>
- </TitleTag>
- </header>
- <div
- className={styles.body}
- dangerouslySetInnerHTML={{ __html: intro }}
- ></div>
- <footer className={styles.footer}>
- <ButtonLink target={`/article/${slug}`} position="left">
- <FormattedMessage
- defaultMessage="Read more<a11y> about {title}</a11y>"
- description="PostPreview: read more link"
- id="bkbrN7"
- values={{
- title,
- a11y: (chunks: string) => (
- <span className="screen-reader-text">{chunks}</span>
- ),
- }}
- />
- <ArrowIcon />
- </ButtonLink>
- </footer>
- <PostMeta meta={meta} />
- </article>
- </>
- );
-};
-
-export default PostPreview;
diff --git a/src/components/PostsList/PostsList.module.scss b/src/components/PostsList/PostsList.module.scss
deleted file mode 100644
index b4ffbd9..0000000
--- a/src/components/PostsList/PostsList.module.scss
+++ /dev/null
@@ -1,51 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-@use "@styles/abstracts/placeholders";
-
-.section {
- --column-3: 0;
- --grid-gap: 0;
-
- composes: grid from "@styles/layout/_grid.scss";
- align-items: first baseline;
-}
-
-.year {
- grid-column: 2;
- margin: var(--spacing-md) 0 0;
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- grid-column: 1;
- justify-self: end;
- position: sticky;
- top: var(--spacing-xs);
- margin-right: var(--spacing-lg);
- }
- }
-}
-
-.list {
- @extend %reset-ordered-list;
-
- grid-column: 2;
- margin: 0 auto var(--spacing-md);
-}
-
-li.item {
- border-bottom: fun.convert-px(1) solid var(--color-border);
-
- &:not(:last-of-type) {
- margin: 0 0 var(--spacing-md) 0;
- }
-
- &:first-of-type {
- margin-top: var(--spacing-sm);
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- margin-top: 0;
- }
- }
- }
-}
diff --git a/src/components/PostsList/PostsList.tsx b/src/components/PostsList/PostsList.tsx
deleted file mode 100644
index f998846..0000000
--- a/src/components/PostsList/PostsList.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-import PostPreview from '@components/PostPreview/PostPreview';
-import { PostsList as PostsListData } from '@ts/types/blog';
-import { sortPostsByYear } from '@utils/helpers/sort';
-import { ForwardedRef, forwardRef, Fragment } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './PostsList.module.scss';
-
-const PostsList = (
- {
- data,
- showYears,
- }: {
- data: PostsListData[];
- showYears: boolean;
- },
- ref: ForwardedRef<HTMLSpanElement>
-) => {
- const intl = useIntl();
- const titleLevel = showYears ? 3 : 2;
-
- const getPostsListByYear = () => {
- const posts = sortPostsByYear(data);
- const years = Object.keys(posts).reverse();
-
- const getLastPostId = () => {
- const oldestYear = Object.keys(posts)[0];
- const lastPost = posts[oldestYear][posts[oldestYear].length - 1];
- return lastPost.id;
- };
-
- return years.map((year) => {
- return (
- <section key={year} className={styles.section}>
- {showYears && (
- <h2 className={styles.year}>
- <span className="screen-reader-text">
- {intl.formatMessage({
- defaultMessage: 'Published on',
- description: 'PostsList: published on year label',
- id: 'EvODgw',
- })}{' '}
- </span>
- {year}
- </h2>
- )}
- <ol className={styles.list}>
- {posts[year].map((post) => {
- const isLastPost = post.id === getLastPostId();
- return (
- <Fragment key={post.id}>
- <li className={styles.item}>
- <PostPreview post={post} titleLevel={titleLevel} />
- </li>
- {isLastPost && (
- <li className={styles.item}>
- <span ref={ref} tabIndex={-1} />
- </li>
- )}
- </Fragment>
- );
- })}
- </ol>
- </section>
- );
- });
- };
-
- const getPostsList = () => {
- return data.map((page) => {
- const getLastPostId = () => {
- const lastPost = page.posts[page.posts.length - 1];
- return lastPost.id;
- };
-
- if (page.posts.length === 0) {
- return (
- <p key="no-result">
- {intl.formatMessage({
- defaultMessage: 'No results found.',
- description: 'PostsList: no results',
- id: 'vK7Sxv',
- })}
- </p>
- );
- } else {
- return (
- <Fragment key={page.pageInfo.endCursor}>
- <ol className={styles.list}>
- {page.posts.map((post) => {
- const isLastPost = post.id === getLastPostId();
- return (
- <Fragment key={post.id}>
- <li key={post.id} className={styles.item}>
- <PostPreview post={post} titleLevel={titleLevel} />
- </li>
- {isLastPost && <span ref={ref} tabIndex={-1} />}
- </Fragment>
- );
- })}
- </ol>
- </Fragment>
- );
- }
- });
- };
-
- return <div>{showYears ? getPostsListByYear() : getPostsList()}</div>;
-};
-
-export default forwardRef(PostsList);
diff --git a/src/components/ProjectPreview/ProjectPreview.module.scss b/src/components/ProjectPreview/ProjectPreview.module.scss
deleted file mode 100644
index 3bf56ec..0000000
--- a/src/components/ProjectPreview/ProjectPreview.module.scss
+++ /dev/null
@@ -1,98 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.article {
- display: flex;
- flex-flow: column nowrap;
- height: 100%;
- padding: var(--spacing-md);
- text-align: center;
-}
-
-.cover {
- height: fun.convert-px(150);
- position: relative;
-}
-
-.title {
- flex: 1;
- margin: var(--spacing-xs) 0;
- background: none;
- text-decoration: underline solid transparent 0;
- text-shadow: none;
- transition: all 0.3s linear 0s;
-}
-
-.body {
- margin: 0 0 var(--spacing-xs);
-}
-
-.footer {
- margin-top: auto;
-}
-
-.meta {
- display: block;
-
- &__item {
- display: flex;
- flex-flow: row wrap;
- place-content: center;
- gap: var(--spacing-2xs);
- }
-}
-
-.link {
- display: block;
- height: 100%;
- background: var(--color-bg);
- color: inherit;
- text-decoration: none;
- border: fun.convert-px(3) solid var(--color-primary);
- border-radius: fun.convert-px(5);
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-2)
- var(--color-shadow),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(5) fun.convert-px(-4)
- var(--color-shadow);
- transition: all 0.3s ease-in-out 0s;
-
- &:hover,
- &:focus,
- &:active {
- color: inherit;
- }
-
- &:hover,
- &:focus {
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow-light),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-2)
- var(--color-shadow-light),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(5) fun.convert-px(-4)
- var(--color-shadow-light),
- fun.convert-px(7) fun.convert-px(10) fun.convert-px(12) fun.convert-px(-3)
- var(--color-shadow-light);
- transform: scale(1.05);
- }
-
- &:focus {
- .title {
- text-decoration: underline solid var(--color-primary) 0.3ex;
- }
- }
-
- &:active {
- box-shadow: 0 0 0 0 var(--color-shadow);
- transform: scale(0.95);
-
- .title {
- text-decoration: none;
- }
- }
-}
-
-.techno {
- padding: 0 var(--spacing-2xs);
- border: fun.convert-px(1) solid var(--color-primary-darker);
-}
diff --git a/src/components/ProjectPreview/ProjectPreview.tsx b/src/components/ProjectPreview/ProjectPreview.tsx
deleted file mode 100644
index 1e1ced2..0000000
--- a/src/components/ProjectPreview/ProjectPreview.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Project } from '@ts/types/app';
-import { slugify } from '@utils/helpers/slugify';
-import Image from 'next/image';
-import Link from 'next/link';
-import { useIntl } from 'react-intl';
-import styles from './ProjectPreview.module.scss';
-
-const ProjectPreview = ({ project }: { project: Project }) => {
- const { id, meta, tagline, title } = project;
- const intl = useIntl();
-
- return (
- <Link href={`/projet/${project.slug}`}>
- <a className={styles.link}>
- <article className={styles.article}>
- <header>
- {meta.hasCover && (
- <div className={styles.cover}>
- <Image
- src={`/projects/${id}.jpg`}
- layout="fill"
- objectFit="contain"
- objectPosition="center"
- alt={intl.formatMessage(
- {
- defaultMessage: '{title} picture',
- description: 'ProjectPreview: cover alt text',
- id: '2pykor',
- },
- { title }
- )}
- />
- </div>
- )}
- <h2 className={styles.title}>{title}</h2>
- </header>
- {tagline && (
- <div
- className={styles.body}
- dangerouslySetInnerHTML={{ __html: tagline }}
- ></div>
- )}
- <footer className={styles.footer}>
- <dl className={styles.meta}>
- {meta.technologies && (
- <div className={styles.meta__item}>
- <dt className="screen-reader-text">
- {intl.formatMessage(
- {
- defaultMessage:
- '{count, plural, =0 {Technologies:} one {Technology:} other {Technologies:}}',
- description: 'ProjectPreview: technologies list label',
- id: 'okFrAO',
- },
- { count: meta.technologies.length }
- )}
- </dt>
- {meta.technologies.map((techno) => (
- <dd key={slugify(techno)} className={styles.techno}>
- {techno}
- </dd>
- ))}
- </div>
- )}
- </dl>
- </footer>
- </article>
- </a>
- </Link>
- );
-};
-
-export default ProjectPreview;
diff --git a/src/components/ProjectSummary/ProjectSummary.module.scss b/src/components/ProjectSummary/ProjectSummary.module.scss
deleted file mode 100644
index cf1e77f..0000000
--- a/src/components/ProjectSummary/ProjectSummary.module.scss
+++ /dev/null
@@ -1,73 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.wrapper {
- margin-bottom: var(--spacing-md);
- padding: var(--spacing-sm) var(--spacing-md) var(--spacing-md);
- border: fun.convert-px(1) solid var(--color-border);
-}
-
-.cover {
- height: fun.convert-px(150);
- position: relative;
-}
-
-.info {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(20ch, 1fr));
- align-items: start;
- justify-content: left;
- column-gap: var(--spacing-md);
- margin: var(--spacing-md) 0 0;
-}
-
-.inline-data {
- display: inline-block;
- margin-top: fun.convert-px(3);
-
- &:not(:last-of-type) {
- margin-right: var(--spacing-xs);
- }
-}
-
-.techno {
- padding: 0 var(--spacing-2xs);
- border: fun.convert-px(1) solid var(--color-primary-darker);
-}
-
-.repo {
- display: block;
- width: 3em;
- height: 3em;
- background: none;
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-1)
- var(--color-shadow),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(4) fun.convert-px(-3)
- var(--color-shadow),
- 0 0 0 0 var(--color-shadow);
- transition: all 0.3s linear 0s;
-
- &:hover,
- &:focus {
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
- fun.convert-px(1) fun.convert-px(1) fun.convert-px(2) fun.convert-px(-1)
- var(--color-shadow-light),
- fun.convert-px(3) fun.convert-px(3) fun.convert-px(4) fun.convert-px(-4)
- var(--color-shadow-light),
- fun.convert-px(6) fun.convert-px(6) fun.convert-px(10) fun.convert-px(-3)
- var(--color-shadow);
- transform: scale(1.15);
- }
-
- &:focus {
- outline: var(--color-primary) dashed fun.convert-px(2);
- }
-
- &:active {
- box-shadow: 0 0 0 0 var(--color-shadow);
- outline: none;
- transform: scale(0.9);
- }
-}
diff --git a/src/components/ProjectSummary/ProjectSummary.tsx b/src/components/ProjectSummary/ProjectSummary.tsx
deleted file mode 100644
index 79e783e..0000000
--- a/src/components/ProjectSummary/ProjectSummary.tsx
+++ /dev/null
@@ -1,178 +0,0 @@
-import GithubIcon from '@assets/images/social-media/github.svg';
-import GitlabIcon from '@assets/images/social-media/gitlab.svg';
-import { ProjectMeta } from '@ts/types/app';
-import { settings } from '@utils/config';
-import { getFormattedDate } from '@utils/helpers/format';
-import { slugify } from '@utils/helpers/slugify';
-import useGithubApi from '@utils/hooks/useGithubApi';
-import Image from 'next/image';
-import { useRouter } from 'next/router';
-import { useIntl } from 'react-intl';
-import styles from './ProjectSummary.module.scss';
-
-const ProjectSummary = ({
- id,
- meta,
-}: {
- id: string;
- title: string;
- meta: ProjectMeta;
-}) => {
- const { hasCover, license, repos, technologies } = meta;
- const intl = useIntl();
- const router = useRouter();
- const locale = router.locale ? router.locale : settings.locales.defaultLocale;
- const { data } = useGithubApi(repos?.github ? repos.github : '');
-
- return (
- <div className={styles.wrapper}>
- {hasCover && (
- <div className={styles.cover}>
- <Image
- src={`/projects/${id}.jpg`}
- alt={intl.formatMessage({
- defaultMessage: '{title} preview',
- description: 'ProjectSummary: cover alt text',
- id: 'mh7tGg',
- })}
- layout="fill"
- objectFit="contain"
- />
- </div>
- )}
- <dl className={styles.info}>
- {data && (
- <div className={styles.info__item}>
- <dt>
- {intl.formatMessage({
- defaultMessage: 'Created on:',
- description: 'ProjectSummary: creation date label',
- id: 'CWi0go',
- })}
- </dt>
- <dd>
- <time dateTime={data.created_at}>
- {getFormattedDate(data.created_at, locale)}
- </time>
- </dd>
- </div>
- )}
- {data && (
- <div className={styles.info__item}>
- <dt>
- {intl.formatMessage({
- defaultMessage: 'Last updated on:',
- description: 'ProjectSummary: update date label',
- id: 'vJ+QDV',
- })}
- </dt>
- <dd>
- <time dateTime={data.updated_at}>
- {getFormattedDate(data.updated_at, locale)}
- </time>
- </dd>
- </div>
- )}
- <div className={styles.info__item}>
- <dt>
- {intl.formatMessage({
- defaultMessage: 'License:',
- description: 'ProjectSummary: license label',
- id: 'hKagVG',
- })}
- </dt>
- <dd>{license}</dd>
- </div>
- {technologies && (
- <div className={styles.info__item}>
- <dt>
- {intl.formatMessage(
- {
- defaultMessage:
- '{count, plural, =0 {Technologies:} one {Technology:} other {Technologies:}}',
- description: 'ProjectSummary: technologies list label',
- id: 'enwhNm',
- },
- { count: technologies.length }
- )}
- </dt>
- {technologies.map((techno) => (
- <dd
- key={slugify(techno)}
- className={`${styles.techno} ${styles['inline-data']}`}
- >
- {techno}
- </dd>
- ))}
- </div>
- )}
- {repos && (
- <div className={styles.info__item}>
- <dt>
- {intl.formatMessage(
- {
- defaultMessage:
- '{count, plural, =0 {Repositories:} one {Repository:} other {Repositories:}}',
- description: 'ProjectSummary: repositories list label',
- id: 'OTTv+m',
- },
- { count: Object.keys(repos).length }
- )}
- </dt>
- {repos.github && (
- <dd className={styles['inline-data']}>
- <a
- href={`https://github.com/${repos.github}`}
- className={styles.repo}
- >
- <GithubIcon />
- <span className="screen-reader-text">Github</span>
- </a>
- </dd>
- )}
- {repos.gitlab && (
- <dd className={styles['inline-data']}>
- <a
- href={`https://gitlab.com/${repos.gitlab}`}
- className={styles.repo}
- >
- <GitlabIcon />
- <span className="screen-reader-text">Gitlab</span>
- </a>
- </dd>
- )}
- </div>
- )}
- {data && repos && (
- <div>
- <dt>
- {intl.formatMessage({
- defaultMessage: 'Popularity:',
- description: 'ProjectSummary: popularity label',
- id: 'vgMk0q',
- })}
- </dt>
- {repos.github && (
- <dd>
- ⭐&nbsp;
- <a href={`https://github.com/${repos.github}/stargazers`}>
- {intl.formatMessage(
- {
- defaultMessage:
- '{starsCount, plural, =0 {0 stars on Github} one {# star on Github} other {# stars on Github}}',
- description: 'ProjectSummary: technologies list label',
- id: 'aA3hOT',
- },
- { starsCount: data.stargazers_count }
- )}
- </a>
- </dd>
- )}
- </div>
- )}
- </dl>
- </div>
- );
-};
-
-export default ProjectSummary;
diff --git a/src/components/ProjectsList/ProjectsList.module.scss b/src/components/ProjectsList/ProjectsList.module.scss
deleted file mode 100644
index fbed08d..0000000
--- a/src/components/ProjectsList/ProjectsList.module.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/placeholders";
-
-.list {
- --items: 4;
- --items-size: 31ch;
-
- @extend %reset-list;
-
- display: grid;
- grid-template-columns: repeat(
- auto-fit,
- min(calc(100vw - (var(--spacing-md) * 2)), var(--items-size))
- );
- gap: var(--spacing-sm);
- place-content: center;
- width: min(
- calc(100vw - (var(--spacing-md) * 2)),
- calc(
- (var(--items-size) * var(--items)) +
- (var(--spacing-sm) * (var(--items) - 1))
- )
- );
- margin: var(--spacing-sm) auto 0;
-}
diff --git a/src/components/ProjectsList/ProjectsList.tsx b/src/components/ProjectsList/ProjectsList.tsx
deleted file mode 100644
index 07e6a71..0000000
--- a/src/components/ProjectsList/ProjectsList.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import ProjectPreview from '@components/ProjectPreview/ProjectPreview';
-import { Project } from '@ts/types/app';
-import styles from './ProjectsList.module.scss';
-
-const ProjectsList = ({ projects }: { projects: Project[] }) => {
- const getProjectItems = () => {
- return projects.map((project) => {
- return project.title ? (
- <li className={styles.item} key={project.id}>
- <ProjectPreview project={project} />
- </li>
- ) : (
- ''
- );
- });
- };
-
- return <ul className={styles.list}>{getProjectItems()}</ul>;
-};
-
-export default ProjectsList;
diff --git a/src/components/SearchForm/SearchForm.module.scss b/src/components/SearchForm/SearchForm.module.scss
deleted file mode 100644
index 4debfbb..0000000
--- a/src/components/SearchForm/SearchForm.module.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.title {
- margin-bottom: var(--spacing-sm);
- color: var(--color-primary-dark);
- font-size: var(--font-size-lg);
- font-weight: 600;
-}
diff --git a/src/components/SearchForm/SearchForm.tsx b/src/components/SearchForm/SearchForm.tsx
deleted file mode 100644
index f4735af..0000000
--- a/src/components/SearchForm/SearchForm.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import { ButtonSubmit } from '@components/Buttons';
-import { Field, Form } from '@components/FormElements';
-import { SearchIcon } from '@components/Icons';
-import { useRouter } from 'next/router';
-import { FormEvent, useEffect, useRef, useState } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './SearchForm.module.scss';
-
-const SearchForm = ({ isOpened }: { isOpened: boolean }) => {
- const intl = useIntl();
- const [query, setQuery] = useState('');
- const inputRef = useRef<HTMLInputElement>(null);
- const router = useRouter();
-
- useEffect(() => {
- setTimeout(() => {
- if (isOpened && inputRef.current) {
- inputRef.current.focus();
- }
- }, 400);
- }, [isOpened]);
-
- const launchSearch = (e: FormEvent) => {
- e.preventDefault();
- router.push({ pathname: '/recherche', query: { s: query } });
- setQuery('');
- };
-
- return (
- <>
- <div className={styles.title}>
- {intl.formatMessage({
- defaultMessage: 'Search',
- description: 'SearchForm : form title',
- id: 'eFMu2E',
- })}
- </div>
- <Form submitHandler={launchSearch} kind="search" id="search">
- <label htmlFor="search-query" className="screen-reader-text">
- {intl.formatMessage({
- defaultMessage: 'Keywords:',
- description: 'SearchForm: search field label',
- id: 'YvMPuD',
- })}
- </label>
- <Field
- ref={inputRef}
- id="search-query"
- name="search-query"
- kind="search"
- value={query}
- setValue={setQuery}
- required={true}
- />
- <ButtonSubmit modifier="search">
- <SearchIcon />
- <span className="screen-reader-text">
- {intl.formatMessage({
- defaultMessage: 'Search',
- description: 'SearchForm: search button text',
- id: 'AnaPbu',
- })}
- </span>
- </ButtonSubmit>
- </Form>
- </>
- );
-};
-
-export default SearchForm;
diff --git a/src/components/Settings/AckeeSelect/AckeeSelect.module.scss b/src/components/Settings/AckeeSelect/AckeeSelect.module.scss
deleted file mode 100644
index b145761..0000000
--- a/src/components/Settings/AckeeSelect/AckeeSelect.module.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.wrapper {
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- gap: var(--spacing-xs);
-}
diff --git a/src/components/Settings/AckeeSelect/AckeeSelect.tsx b/src/components/Settings/AckeeSelect/AckeeSelect.tsx
deleted file mode 100644
index f711fe2..0000000
--- a/src/components/Settings/AckeeSelect/AckeeSelect.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Field, Label } from '@components/FormElements';
-import Tooltip from '@components/Tooltip/Tooltip';
-import { LocalStorage } from '@services/local-storage';
-import { useAckeeTracker } from '@utils/providers/ackee';
-import { useEffect, useState } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './AckeeSelect.module.scss';
-
-const AckeeSelect = () => {
- const intl = useIntl();
- const options = [
- {
- id: 'partial',
- name: intl.formatMessage({
- defaultMessage: 'Partial',
- description: 'AckeeSelect: partial option name',
- id: 'e/8Kyj',
- }),
- value: 'partial',
- },
- {
- id: 'full',
- name: intl.formatMessage({
- defaultMessage: 'Full',
- description: 'AckeeSelect: full option name',
- id: 'PzRpPw',
- }),
- value: 'full',
- },
- ];
- const [value, setValue] = useState<string>('full');
- const { setDetailed } = useAckeeTracker();
-
- useEffect(() => {
- setDetailed(value === 'full');
- }, [setDetailed, value]);
-
- useEffect(() => {
- const initialState = LocalStorage.get('ackee-tracking');
- if (initialState) setValue(initialState);
- }, []);
-
- useEffect(() => {
- LocalStorage.set('ackee-tracking', `${value}`);
- }, [value]);
-
- const label = (
- <Label
- body={intl.formatMessage({
- defaultMessage: 'Tracking:',
- description: 'AckeeSelect: select label',
- id: '2pmylc',
- })}
- htmlFor="ackee-settings"
- kind="settings"
- />
- );
-
- const message = [
- intl.formatMessage({
- defaultMessage: 'Partial includes only page url, views and duration.',
- description: 'AckeeSelect: tooltip message',
- id: 'skb4W5',
- }),
- intl.formatMessage({
- defaultMessage:
- 'Full includes all information from partial as well as information about referrer, operating system, device, browser, screen size and language.',
- description: 'AckeeSelect: tooltip message',
- id: 'Ogccx6',
- }),
- ];
-
- return (
- <div className={styles.wrapper}>
- <Field
- id="ackee-settings"
- name="ackee-settings"
- kind="select"
- label={label}
- options={options}
- value={value}
- setValue={setValue}
- />
- <Tooltip
- message={message}
- title={intl.formatMessage({
- defaultMessage: 'Ackee tracking (analytics)',
- description: 'AckeeSelect: tooltip title',
- id: 'F1EQX3',
- })}
- />
- </div>
- );
-};
-
-export default AckeeSelect;
diff --git a/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx b/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx
deleted file mode 100644
index 20ad267..0000000
--- a/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Toggle } from '@components/FormElements';
-import { MoonIcon, SunIcon } from '@components/Icons';
-import Spinner from '@components/Spinner/Spinner';
-import { usePrismTheme } from '@utils/providers/prism-theme';
-import { useEffect, useState } from 'react';
-import { useIntl } from 'react-intl';
-
-const PrismThemeToggle = () => {
- const intl = useIntl();
- const [isMounted, setIsMounted] = useState<boolean>(false);
-
- useEffect(() => {
- setIsMounted(true);
- }, []);
-
- const { theme, setTheme, resolvedTheme } = usePrismTheme();
- const [isDarkTheme, setIsDarkTheme] = useState<boolean>(theme === 'dark');
-
- useEffect(() => {
- if (theme === 'system') {
- setIsDarkTheme(resolvedTheme === 'dark');
- } else {
- setIsDarkTheme(theme === 'dark');
- }
- }, [theme, resolvedTheme]);
-
- const updateTheme = () => {
- isDarkTheme ? setTheme('light') : setTheme('dark');
- setIsDarkTheme(!isDarkTheme);
- };
-
- if (!isMounted) return <Spinner />;
-
- return (
- <Toggle
- id="prism-theme"
- label={intl.formatMessage({
- defaultMessage: 'Code blocks:',
- description: 'PrismThemeToggle: toggle label',
- id: 'w0UfY0',
- })}
- leftChoice={<SunIcon />}
- rightChoice={<MoonIcon />}
- value={isDarkTheme}
- changeHandler={updateTheme}
- />
- );
-};
-
-export default PrismThemeToggle;
diff --git a/src/components/Settings/ReduceMotion/ReduceMotion.tsx b/src/components/Settings/ReduceMotion/ReduceMotion.tsx
deleted file mode 100644
index 00562cd..0000000
--- a/src/components/Settings/ReduceMotion/ReduceMotion.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Toggle } from '@components/FormElements';
-import { LocalStorage } from '@services/local-storage';
-import { useEffect, useState } from 'react';
-import { useIntl } from 'react-intl';
-
-const ReduceMotion = () => {
- const intl = useIntl();
- const [isDeactivated, setIsDeactivated] = useState<boolean>(false);
-
- useEffect(() => {
- const initialState = LocalStorage.get('reduced-motion');
- if (initialState) setIsDeactivated(initialState === 'true' ? true : false);
- }, []);
-
- useEffect(() => {
- document.documentElement.dataset.reducedMotion = `${isDeactivated}`;
- LocalStorage.set('reduced-motion', `${isDeactivated}`);
- }, [isDeactivated]);
-
- const updateState = () => {
- setIsDeactivated(!isDeactivated);
- };
-
- return (
- <Toggle
- id="reduced-motion"
- label={intl.formatMessage({
- defaultMessage: 'Animations:',
- description: 'ReduceMotion: toggle label',
- id: 'X3PDXO',
- })}
- leftChoice={intl.formatMessage({
- defaultMessage: 'On',
- description: 'ReduceMotion: toggle on label',
- id: 'qPU/Qn',
- })}
- rightChoice={intl.formatMessage({
- defaultMessage: 'Off',
- description: 'ReduceMotion: toggle off label',
- id: 'w1nIrj',
- })}
- value={isDeactivated}
- changeHandler={updateState}
- />
- );
-};
-
-export default ReduceMotion;
diff --git a/src/components/Settings/Settings.module.scss b/src/components/Settings/Settings.module.scss
deleted file mode 100644
index fe6b17b..0000000
--- a/src/components/Settings/Settings.module.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.title {
- --icon-size: #{fun.convert-px(30)};
-
- display: flex;
- flex-flow: row nowrap;
- gap: var(--spacing-2xs);
- margin-bottom: var(--spacing-md);
- color: var(--color-primary-dark);
- font-size: var(--font-size-lg);
- font-weight: 600;
-
- svg {
- margin: 0;
- }
-}
diff --git a/src/components/Settings/Settings.tsx b/src/components/Settings/Settings.tsx
deleted file mode 100644
index fec4c45..0000000
--- a/src/components/Settings/Settings.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { CogIcon } from '@components/Icons';
-import ThemeToggle from '@components/Settings/ThemeToggle/ThemeToggle';
-import { useIntl } from 'react-intl';
-import AckeeSelect from './AckeeSelect/AckeeSelect';
-import PrismThemeToggle from './PrismThemeToggle/PrismThemeToggle';
-import ReduceMotion from './ReduceMotion/ReduceMotion';
-import styles from './Settings.module.scss';
-
-const Settings = () => {
- const intl = useIntl();
-
- return (
- <>
- <div className={styles.title}>
- <CogIcon />{' '}
- {intl.formatMessage({
- defaultMessage: 'Settings',
- description: 'Settings: modal title',
- id: 'bHEmkY',
- })}
- </div>
- <ThemeToggle />
- <ReduceMotion />
- <PrismThemeToggle />
- <AckeeSelect />
- </>
- );
-};
-
-export default Settings;
diff --git a/src/components/Settings/ThemeToggle/ThemeToggle.tsx b/src/components/Settings/ThemeToggle/ThemeToggle.tsx
deleted file mode 100644
index ec2cee1..0000000
--- a/src/components/Settings/ThemeToggle/ThemeToggle.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Toggle } from '@components/FormElements';
-import { MoonIcon, SunIcon } from '@components/Icons';
-import Spinner from '@components/Spinner/Spinner';
-import { useTheme } from 'next-themes';
-import { useEffect, useState } from 'react';
-import { useIntl } from 'react-intl';
-
-const ThemeToggle = () => {
- const intl = useIntl();
- const [isMounted, setIsMounted] = useState<boolean>(false);
- const { resolvedTheme, setTheme } = useTheme();
-
- useEffect(() => {
- setIsMounted(true);
- }, []);
-
- if (!isMounted) return <Spinner />;
-
- const isDarkTheme = resolvedTheme === 'dark';
-
- const updateTheme = () => {
- setTheme(isDarkTheme ? 'light' : 'dark');
- };
-
- return (
- <Toggle
- id="dark-theme"
- label={intl.formatMessage({
- defaultMessage: 'Theme:',
- description: 'ThemeToggle: toggle label',
- id: 'O9XLDc',
- })}
- leftChoice={<SunIcon />}
- rightChoice={<MoonIcon />}
- value={isDarkTheme}
- changeHandler={updateTheme}
- />
- );
-};
-
-export default ThemeToggle;
diff --git a/src/components/Sidebar/Sidebar.module.scss b/src/components/Sidebar/Sidebar.module.scss
deleted file mode 100644
index fb6230d..0000000
--- a/src/components/Sidebar/Sidebar.module.scss
+++ /dev/null
@@ -1,43 +0,0 @@
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- grid-column: 2;
-
- &--left {
- margin: var(--spacing-md) 0;
- }
-
- &--right {
- margin: var(--spacing-md) 0 0;
- }
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- align-self: stretch;
- margin: 0 var(--spacing-xs) var(--spacing-md);
-
- &--right {
- grid-row: 2 / 4;
- grid-column: 3;
- }
- }
-
- @include mix.dimensions("lg") {
- &--left {
- grid-row: 2 / 4;
- grid-column: 1;
- }
- }
- }
-}
-
-.body {
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- align-self: flex-start;
- width: 100%;
- position: sticky;
- top: var(--spacing-xs);
- }
- }
-}
diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx
deleted file mode 100644
index 9e2079d..0000000
--- a/src/components/Sidebar/Sidebar.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Children, cloneElement, isValidElement, ReactNode } from 'react';
-import styles from './Sidebar.module.scss';
-
-type SidebarPosition = 'left' | 'right';
-
-const Sidebar = ({
- children,
- position,
- ariaLabel,
- title,
-}: {
- children: ReactNode;
- position: SidebarPosition;
- ariaLabel?: string;
- title?: string;
-}) => {
- const childrenWithProps = Children.map(children, (child) => {
- if (isValidElement(child)) {
- return cloneElement(child, { titleLevel: title ? 3 : 2 });
- }
- return child;
- });
-
- const positionClass = `wrapper--${position}`;
-
- return (
- <aside
- className={`${styles.wrapper} ${styles[positionClass]}`}
- aria-label={ariaLabel}
- aria-labelledby={title ? `${position}-sidebar-title` : undefined}
- >
- <div className={styles.body}>
- {title && <h2 id={`${position}-sidebar-title`}>{title}</h2>}
- {childrenWithProps}
- </div>
- </aside>
- );
-};
-
-export default Sidebar;
diff --git a/src/components/Spinner/Spinner.module.scss b/src/components/Spinner/Spinner.module.scss
deleted file mode 100644
index 8d818a2..0000000
--- a/src/components/Spinner/Spinner.module.scss
+++ /dev/null
@@ -1,48 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.wrapper {
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- justify-content: center;
- gap: var(--spacing-2xs);
- margin: var(--spacing-md) 0;
-}
-
-.ball {
- width: fun.convert-px(8);
- height: fun.convert-px(8);
- background: linear-gradient(
- to right,
- var(--color-primary-light) 0%,
- var(--color-primary-lighter) 100%
- );
- border-radius: 50%;
- animation: spinner 1.4s infinite ease-in-out both;
-
- &:first-child {
- animation-delay: -0.32s;
- }
-
- &:nth-child(2) {
- animation-delay: -0.16s;
- }
-}
-
-.text {
- margin-left: var(--spacing-xs);
- color: var(--color-primary-darker);
- text-align: center;
-}
-
-@keyframes spinner {
- 0%,
- 80%,
- 100% {
- transform: scale(0);
- }
-
- 40% {
- transform: scale(1);
- }
-}
diff --git a/src/components/Spinner/Spinner.tsx b/src/components/Spinner/Spinner.tsx
deleted file mode 100644
index 9117d90..0000000
--- a/src/components/Spinner/Spinner.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useIntl } from 'react-intl';
-import styles from './Spinner.module.scss';
-
-const Spinner = ({ message }: { message?: string }) => {
- const intl = useIntl();
-
- return (
- <div className={styles.wrapper}>
- <div className={styles.ball}></div>
- <div className={styles.ball}></div>
- <div className={styles.ball}></div>
- <div className={styles.text}>
- {message ||
- intl.formatMessage({
- defaultMessage: 'Loading...',
- description: 'Spinner: loading text',
- id: 'q9cJQe',
- })}
- </div>
- </div>
- );
-};
-
-export default Spinner;
diff --git a/src/components/Toolbar/Toolbar.module.scss b/src/components/Toolbar/Toolbar.module.scss
deleted file mode 100644
index debb3b7..0000000
--- a/src/components/Toolbar/Toolbar.module.scss
+++ /dev/null
@@ -1,114 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.wrapper {
- --btn-size: #{fun.convert-px(60)};
-
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- justify-content: space-around;
- width: 100%;
- height: var(--toolbar-size);
- position: fixed;
- bottom: 0;
- left: 0;
- z-index: 5;
- background: var(--color-bg);
- border-top: fun.convert-px(4) solid;
- border-image: radial-gradient(
- ellipse at top,
- var(--color-primary-lighter) 20%,
- var(--color-primary) 100%
- )
- 1;
- box-shadow: 0 fun.convert-px(-2) fun.convert-px(3) fun.convert-px(-1)
- var(--color-shadow-dark);
-
- :global {
- animation: slide-in-from-bottom 0.8s ease-in-out 0s 1;
- }
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- --toolbar-size: auto;
-
- justify-content: flex-end;
- gap: var(--spacing-sm);
- width: auto;
- background: inherit;
- border: none;
- box-shadow: none;
- position: relative;
- left: unset;
- margin-right: unset;
- transform: unset;
-
- :global {
- animation: slide-in-from-top 1s ease-in-out 0s 1;
- }
- }
- }
-}
-
-.menu {
- padding: var(--spacing-md);
- position: absolute;
- bottom: 100%;
- left: 0;
- right: 0;
- background: var(--color-bg-secondary);
- border-top: fun.convert-px(4) solid;
- border-bottom: fun.convert-px(4) solid;
- border-image: radial-gradient(
- ellipse at top,
- var(--color-primary-lighter) 20%,
- var(--color-primary) 100%
- )
- 1;
- box-shadow: fun.convert-px(2) fun.convert-px(-2) fun.convert-px(3)
- fun.convert-px(-1) var(--color-shadow-dark);
- transition: all 0.7s ease-in-out 0s;
-
- &--closed {
- transform: translateX(-100%);
- visibility: hidden;
- }
-
- &--opened {
- transform: translateX(0);
- visibility: visible;
- }
-
- @include mix.media("screen") {
- @include mix.dimensions("sm") {
- width: fun.convert-px(500);
- left: unset;
- right: unset;
- top: 120%;
- bottom: unset;
- border: fun.convert-px(4) solid;
- border-image: radial-gradient(
- ellipse at top,
- var(--color-primary-lighter) 20%,
- var(--color-primary) 100%
- )
- 1;
- box-shadow: fun.convert-px(2) fun.convert-px(2) fun.convert-px(3)
- fun.convert-px(1) var(--color-shadow-dark);
- transform-origin: 50% -200%;
- transition: all 0.8s ease-in-out 0s;
-
- &--closed {
- opacity: 0;
- transform: perspective(20rem) translate3d(0, 100%, -20rem);
- visibility: hidden;
- }
-
- &--opened {
- opacity: 1;
- transform: none;
- }
- }
- }
-}
diff --git a/src/components/Toolbar/Toolbar.tsx b/src/components/Toolbar/Toolbar.tsx
deleted file mode 100644
index 17f9ef9..0000000
--- a/src/components/Toolbar/Toolbar.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import { ButtonToolbar } from '@components/Buttons';
-import MainNav from '@components/MainNav/MainNav';
-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);
- const [isSettingsOpened, setIsSettingsOpened] = useState<boolean>(false);
- const mainNavRef = useRef<HTMLDivElement>(null);
- const searchBtnRef = useRef<HTMLButtonElement>(null);
- const searchModalRef = useRef<HTMLDivElement>(null);
- const settingsBtnRef = useRef<HTMLButtonElement>(null);
- const settingsModalRef = useRef<HTMLDivElement>(null);
-
- useEffect(() => {
- if (isNavOpened) {
- setIsSearchOpened(false);
- setIsSettingsOpened(false);
- }
- }, [isNavOpened]);
-
- useEffect(() => {
- if (isSearchOpened) {
- setIsNavOpened(false);
- setIsSettingsOpened(false);
- }
- }, [isSearchOpened]);
-
- useEffect(() => {
- if (isSettingsOpened) {
- setIsNavOpened(false);
- setIsSearchOpened(false);
- }
- }, [isSettingsOpened]);
-
- const isClickOutside = (
- ref: RefObject<HTMLDivElement>,
- target: EventTarget
- ) => {
- return ref.current && !ref.current.contains(target as Node);
- };
-
- const isToggleBtn = (ref: RefObject<HTMLDivElement>, target: EventTarget) => {
- return (
- ref.current &&
- ref.current.previousElementSibling &&
- ref.current.previousElementSibling.contains(target as Node)
- );
- };
-
- const isSearchBtn = useCallback((target: HTMLElement) => {
- return (
- target === searchBtnRef.current || searchBtnRef.current?.contains(target)
- );
- }, []);
-
- const isSettingsBtn = useCallback((target: HTMLElement) => {
- return (
- target === settingsBtnRef.current ||
- settingsBtnRef.current?.contains(target)
- );
- }, []);
-
- const handleVisibility = useCallback(
- (e: MouseEvent | FocusEvent) => {
- let ref: RefObject<HTMLDivElement> | null = null;
- if (isNavOpened) ref = mainNavRef;
- if (isSearchOpened) ref = searchModalRef;
- if (isSettingsOpened) ref = settingsModalRef;
-
- if (!ref || !ref.current || !ref.current.id) return;
- if (!isClickOutside(ref, e.target as Node)) return;
- if (isToggleBtn(ref, e.target as Node)) return;
-
- if (
- ref.current.id === 'main-nav' &&
- !isSettingsBtn(e.target as HTMLElement) &&
- !isSearchBtn(e.target as HTMLElement)
- ) {
- setIsNavOpened(false);
- }
-
- if (
- ref.current.id === 'search-modal' &&
- !isSettingsBtn(e.target as HTMLElement)
- )
- setIsSearchOpened(false);
- if (
- ref.current.id === 'settings-modal' &&
- !isSearchBtn(e.target as HTMLElement)
- )
- setIsSettingsOpened(false);
- },
- [isNavOpened, isSearchOpened, isSettingsOpened, isSearchBtn, isSettingsBtn]
- );
-
- useEffect(() => {
- document.addEventListener('mousedown', handleVisibility);
- document.addEventListener('focusin', handleVisibility);
-
- return () => {
- document.removeEventListener('mousedown', handleVisibility);
- document.removeEventListener('focusin', handleVisibility);
- };
- }, [handleVisibility]);
-
- const searchClasses = `${styles.menu} ${
- isSearchOpened ? styles['menu--opened'] : styles['menu--closed']
- }`;
-
- const settingsClasses = `${styles.menu} ${
- isSettingsOpened ? styles['menu--opened'] : styles['menu--closed']
- }`;
-
- return (
- <div className={styles.wrapper}>
- <MainNav
- ref={mainNavRef}
- isOpened={isNavOpened}
- setIsOpened={setIsNavOpened}
- />
- <ButtonToolbar
- ref={searchBtnRef}
- type="search"
- isActivated={isSearchOpened}
- setIsActivated={setIsSearchOpened}
- />
- <div id="search-modal" className={searchClasses} ref={searchModalRef}>
- <DynamicSearchForm isOpened={isSearchOpened} />
- </div>
- <ButtonToolbar
- ref={settingsBtnRef}
- type="settings"
- isActivated={isSettingsOpened}
- setIsActivated={setIsSettingsOpened}
- />
- <div
- id="settings-modal"
- className={settingsClasses}
- ref={settingsModalRef}
- >
- <DynamicSettings />
- </div>
- </div>
- );
-};
-
-export default Toolbar;
diff --git a/src/components/Tooltip/Tooltip.module.scss b/src/components/Tooltip/Tooltip.module.scss
deleted file mode 100644
index 34fa23d..0000000
--- a/src/components/Tooltip/Tooltip.module.scss
+++ /dev/null
@@ -1,120 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.title {
- padding: var(--spacing-2xs) var(--spacing-xs);
- position: absolute;
- top: calc(var(--spacing-sm) * -1);
- left: var(--spacing-lg);
- background: var(--color-bg);
- border: fun.convert-px(1) solid var(--color-primary-dark);
- box-shadow: fun.convert-px(1) fun.convert-px(1) 0 0 var(--color-shadow);
- color: var(--color-primary-darker);
- font-size: var(--font-size-sm);
- font-variant: small-caps;
- font-weight: 500;
-
- @include mix.media("screen") {
- @include mix.dimensions(null, "2xs", "height") {
- top: 0;
- }
-
- @include mix.dimensions("md") {
- left: var(--spacing-md);
- }
- }
-
- &::before {
- content: "?";
- padding: var(--spacing-2xs);
- position: absolute;
- top: fun.convert-px(-1);
- bottom: fun.convert-px(-1);
- right: 100%;
- background: var(--color-primary-dark);
- border: fun.convert-px(1) solid var(--color-primary-dark);
- box-shadow: fun.convert-px(1) fun.convert-px(1) 0 0 var(--color-shadow);
- color: var(--color-fg-inverted);
- font-weight: 600;
- }
-}
-
-.message {
- transition: all 0.5s ease-in-out 0;
-}
-
-.wrapper {
- padding: 9% 6% var(--spacing-sm) 6%;
- position: absolute;
- bottom: 30%;
- left: fun.convert-px(15);
- right: fun.convert-px(15);
- background: var(--color-bg);
- border: fun.convert-px(2) solid var(--color-primary-dark);
- border-radius: fun.convert-px(3);
- box-shadow: fun.convert-px(1) fun.convert-px(1) 0 0 var(--color-shadow),
- fun.convert-px(2) fun.convert-px(2) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow-light);
- transform-origin: bottom;
-
- @include mix.media("screen") {
- @include mix.dimensions(null, "2xs", "height") {
- overflow-y: auto;
- top: 18%;
- }
-
- @include mix.dimensions("sm") {
- bottom: unset;
- left: fun.convert-px(15);
- right: fun.convert-px(15);
- top: 100%;
- transform-origin: top;
- }
- }
-
- ul,
- p {
- margin: 0;
- padding: 0;
- }
-}
-
-.hidden {
- visibility: hidden;
- opacity: 0;
- transition: all 0.5s ease-in-out 0s, opacity 0.3s ease-in-out 0.2s;
- transform: scaleY(0);
-
- .message,
- .title {
- opacity: 0;
- }
-
- .message {
- transition: all 0.3s ease-in-out 0s;
- }
-
- .title {
- transition: all 0.2s ease-in-out 0.2s;
- }
-}
-
-.visible {
- visibility: visible;
- opacity: 1;
- transform: scaleY(1);
- transition: all 0.8s ease-in-out 0s, opacity 0.7s ease-in-out 0.2s;
-
- .message,
- .title {
- opacity: 1;
- }
-
- .message {
- transition: all 0.5s ease-in-out 0.2s;
- }
-
- .title {
- transition: all 0.4s ease-in-out 0s;
- }
-}
diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx
deleted file mode 100644
index 56a87ab..0000000
--- a/src/components/Tooltip/Tooltip.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { ButtonHelp } from '@components/Buttons';
-import { useState } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './Tooltip.module.scss';
-
-const Tooltip = ({
- message,
- title,
-}: {
- message: string | string[];
- title: string;
-}) => {
- const intl = useIntl();
- const [isOpen, setIsOpen] = useState<boolean>(false);
-
- const getMessageFromArray = (strings: string[]) => {
- let keyIndex = 0;
- return (
- <ul>
- {strings.map((string) => {
- keyIndex = keyIndex + 1;
- return <li key={`message-${keyIndex}`}>{string}</li>;
- })}
- </ul>
- );
- };
-
- const buttonTitle = isOpen
- ? intl.formatMessage({
- defaultMessage: 'Close help',
- description: 'Tooltip: button title',
- id: '9kx83j',
- })
- : intl.formatMessage({
- defaultMessage: 'Show help',
- description: 'Tooltip: button title',
- id: 'A5n+C9',
- });
-
- const wrapperModifier = isOpen ? styles.visible : styles.hidden;
-
- return (
- <div>
- <ButtonHelp
- showHelp={isOpen}
- setShowHelp={setIsOpen}
- title={buttonTitle}
- />
- <div className={`${styles.wrapper} ${wrapperModifier}`}>
- <div className={styles.title}>{title}</div>
- <div className={styles.message}>
- {Array.isArray(message) ? getMessageFromArray(message) : message}
- </div>
- </div>
- </div>
- );
-};
-
-export default Tooltip;
diff --git a/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.module.scss b/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.module.scss
deleted file mode 100644
index 6a7757d..0000000
--- a/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.module.scss
+++ /dev/null
@@ -1,146 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-
-.title {
- margin: 0;
- padding: 0;
- background: none;
- font-size: var(--font-size-xl);
- text-align: left;
-}
-
-.icon {
- display: flex;
- flex-flow: row nowrap;
- align-items: center;
- justify-content: center;
- width: fun.convert-px(30);
- height: fun.convert-px(30);
- background: var(--color-bg);
- border: fun.convert-px(1) solid var(--color-primary);
- border-radius: fun.convert-px(3);
- color: var(--color-primary);
- font-weight: 800;
- transition: all 0.25s ease-in-out 0s;
-
- &::before,
- &::after {
- content: "";
- background: var(--color-primary);
- transition: all 0.4s ease-out 0s;
- }
-
- &::before {
- width: 10%;
- height: 60%;
- position: relative;
- left: 30%;
- }
-
- &::after {
- width: 60%;
- height: 10%;
- position: relative;
- left: -5%;
- }
-}
-
-.body {
- width: 100%;
- max-height: 0;
- margin: 0 0 fun.convert-px(-6); // collapse borders
- overflow: hidden;
- visibility: hidden;
- transition: all 0.6s cubic-bezier(0, 1, 0, 1) 0s, margin 0.2s ease-in-out 0s,
- border 0.1s ease-in-out 0.3s, visibility 0.1s linear 0.6s;
-
- &--borders {
- border: 0 solid transparent;
- }
-
- > *:last-child {
- margin-bottom: 0;
- }
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- font-size: var(--font-size-sm);
- font-weight: 500;
- }
- }
-}
-
-.wrapper {
- --header-height: #{fun.convert-px(65)};
-
- display: flex;
- flex-flow: column;
-
- &--expanded {
- .icon::before {
- height: 0;
- }
-
- .body {
- max-height: 10000px; // needs a fixed value for transition.
- margin: var(--spacing-sm) 0;
- overflow: visible;
- visibility: visible;
- transition: visibility 0.1s linear 0s, max-height 0.6s linear 0s,
- margin 0.2s ease-in-out 0s;
-
- &--borders {
- border: fun.convert-px(2) solid var(--color-primary-dark);
- }
- }
- }
-}
-
-.wrapper--expanded.wrapper--toc {
- @include mix.media("screen") {
- @include mix.dimensions("lg") {
- max-height: 100vh;
-
- .body {
- overflow-y: auto;
- }
- }
- }
-}
-
-.header {
- display: flex;
- flex-flow: row nowrap;
- align-items: center;
- justify-content: space-between;
- gap: var(--spacing-md);
- width: 100%;
- min-height: var(--header-height);
- padding: 0;
- position: sticky;
- top: 0;
- z-index: 3;
- background: var(--color-bg);
- border: none;
- border-top: fun.convert-px(2) solid var(--color-primary-dark);
- border-bottom: fun.convert-px(2) solid var(--color-primary-dark);
- cursor: pointer;
-
- &:hover,
- &:focus {
- .icon {
- background: var(--color-primary-light);
- color: var(--color-fg-inverted);
- transform: scale(1.2);
-
- &::before,
- &::after {
- background: var(--color-bg);
- }
- }
- }
-
- > button {
- padding: 0 var(--spacing-xs);
- }
-}
diff --git a/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.tsx b/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.tsx
deleted file mode 100644
index 38e57ad..0000000
--- a/src/components/WidgetParts/ExpandableWidget/ExpandableWidget.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { TitleLevel } from '@ts/types/app';
-import { ReactNode, useState } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './ExpandableWidget.module.scss';
-
-const ExpandableWidget = ({
- children,
- title,
- titleLevel = 2,
- expand = false,
- withBorders = false,
- kind = 'regular',
-}: {
- children: ReactNode;
- title: string;
- titleLevel?: TitleLevel;
- expand?: boolean;
- withBorders?: boolean;
- kind?: 'regular' | 'toc';
-}) => {
- const intl = useIntl();
- const [isExpanded, setIsExpanded] = useState<boolean>(expand);
-
- const handleExpanse = () => setIsExpanded((prev) => !prev);
-
- const TitleTag = `h${titleLevel}` as keyof JSX.IntrinsicElements;
-
- const wrapperKindClass = styles[`wrapper--${kind}`];
- const wrapperClasses = `${styles.wrapper} ${
- isExpanded ? styles['wrapper--expanded'] : ''
- } ${wrapperKindClass}`;
-
- const bodyClasses = `${styles.body} ${
- withBorders ? styles['body--borders'] : ''
- }`;
-
- return (
- <div className={wrapperClasses}>
- <button type="button" className={styles.header} onClick={handleExpanse}>
- <span className="screen-reader-text">
- {isExpanded
- ? intl.formatMessage({
- defaultMessage: 'Collapse',
- description: 'ExpandableWidget: collapse text',
- id: 'WRkY1/',
- })
- : intl.formatMessage({
- defaultMessage: 'Expand',
- description: 'ExpandableWidget: expand text',
- id: 'hV0qHp',
- })}
- </span>
- <TitleTag className={styles.title}>{title}</TitleTag>
- <span className={styles.icon} aria-hidden={true}></span>
- </button>
- <div className={bodyClasses}>{children}</div>
- </div>
- );
-};
-
-export default ExpandableWidget;
diff --git a/src/components/WidgetParts/List/List.module.scss b/src/components/WidgetParts/List/List.module.scss
deleted file mode 100644
index 958f792..0000000
--- a/src/components/WidgetParts/List/List.module.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-
-.list {
- margin: 0;
- padding: 0;
- list-style-type: none;
-
- .list {
- border: none;
- border-top: fun.convert-px(1) solid var(--color-primary-dark);
- }
-
- li {
- margin: 0;
-
- &:not(:last-of-type) {
- border-bottom: fun.convert-px(1) solid var(--color-primary-dark);
- }
- }
-
- a {
- display: flex;
- flex-flow: row nowrap;
- width: 100%;
- padding: var(--spacing-2xs) var(--spacing-xs);
- background: none;
- text-decoration: underline solid transparent 0;
- transition: all 0.2s ease-in-out 0s, font-weight 0s,
- text-decoration-color 0s;
-
- &:hover,
- &:focus {
- background: var(--color-bg-secondary);
- font-weight: 600;
- }
-
- &:focus {
- color: var(--color-primary);
- text-decoration-color: var(--color-primary-light);
- text-decoration-thickness: 0.25ex;
- }
-
- &:active {
- background: var(--color-bg-tertiary);
- text-decoration-color: transparent;
- text-decoration-thickness: 0;
- }
- }
-}
diff --git a/src/components/WidgetParts/List/List.tsx b/src/components/WidgetParts/List/List.tsx
deleted file mode 100644
index 317c4d1..0000000
--- a/src/components/WidgetParts/List/List.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import styles from './List.module.scss';
-
-const List = ({ items }: { items: Array<any> }) => {
- return <ul className={styles.list}>{items}</ul>;
-};
-
-export default List;
diff --git a/src/components/WidgetParts/OrderedList/OrderedList.module.scss b/src/components/WidgetParts/OrderedList/OrderedList.module.scss
deleted file mode 100644
index a286932..0000000
--- a/src/components/WidgetParts/OrderedList/OrderedList.module.scss
+++ /dev/null
@@ -1,66 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/placeholders";
-
-.list {
- @extend %reset-ordered-list;
- counter-reset: link;
-
- .list {
- border-top: fun.convert-px(1) solid var(--color-primary-dark);
- }
-
- a {
- display: flex;
- flex-flow: row nowrap;
- width: 100%;
- padding: var(--spacing-2xs) var(--spacing-xs);
- background: none;
- text-decoration: underline solid transparent 0;
- transition: all 0.16s ease-in-out 0s, text-decoration-color 0s;
- counter-increment: link;
-
- &:hover,
- &:focus {
- background: var(--color-bg-secondary);
- }
-
- &:focus {
- color: var(--color-primary);
- text-decoration-color: var(--color-primary-light);
- text-decoration-thickness: 0.25ex;
- }
-
- &:active {
- background: var(--color-bg-tertiary);
- text-decoration-color: transparent;
- text-decoration-thickness: 0;
- }
-
- &::before {
- content: counters(link, ".") ". ";
- color: var(--color-secondary);
- padding-right: var(--spacing-2xs);
- }
- }
-
- li {
- width: 100%;
- margin: 0;
-
- &:not(:last-of-type) {
- border-bottom: fun.convert-px(1) solid var(--color-primary-dark);
- }
-
- &::before {
- display: none;
- }
- }
-
- li li a::before {
- padding-left: var(--spacing-sm);
- }
-
- li li li a::before {
- padding-left: var(--spacing-lg);
- }
-}
diff --git a/src/components/WidgetParts/OrderedList/OrderedList.tsx b/src/components/WidgetParts/OrderedList/OrderedList.tsx
deleted file mode 100644
index a12ec06..0000000
--- a/src/components/WidgetParts/OrderedList/OrderedList.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import styles from './OrderedList.module.scss';
-
-const OrderedList = ({ items }: { items: Array<any> }) => {
- return <ol className={styles.list}>{items}</ol>;
-};
-
-export default OrderedList;
diff --git a/src/components/WidgetParts/index.tsx b/src/components/WidgetParts/index.tsx
deleted file mode 100644
index 59df3bd..0000000
--- a/src/components/WidgetParts/index.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import ExpandableWidget from './ExpandableWidget/ExpandableWidget';
-import List from './List/List';
-import OrderedList from './OrderedList/OrderedList';
-
-export { ExpandableWidget, List, OrderedList };
diff --git a/src/components/Widgets/CVPreview/CVPreview.module.scss b/src/components/Widgets/CVPreview/CVPreview.module.scss
deleted file mode 100644
index 6ddd696..0000000
--- a/src/components/Widgets/CVPreview/CVPreview.module.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.preview {
- position: relative;
- width: 100%;
- height: 20rem;
- margin-bottom: var(--spacing-sm);
-}
diff --git a/src/components/Widgets/CVPreview/CVPreview.tsx b/src/components/Widgets/CVPreview/CVPreview.tsx
deleted file mode 100644
index cf6a8fa..0000000
--- a/src/components/Widgets/CVPreview/CVPreview.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { ExpandableWidget } from '@components/WidgetParts';
-import Image from 'next/image';
-import Link from 'next/link';
-import { FormattedMessage } from 'react-intl';
-import styles from './CVPreview.module.scss';
-
-const CVPreview = ({
- title,
- imgSrc,
- pdf,
-}: {
- title: string;
- imgSrc: string;
- pdf: string;
-}) => {
- return (
- <ExpandableWidget title={title} expand={true}>
- <div className={styles.preview}>
- <Image
- src={imgSrc}
- layout="fill"
- objectFit="contain"
- objectPosition="left"
- alt="CV Armand Philippot"
- />
- </div>
- <p>
- <FormattedMessage
- defaultMessage="Download <link>CV in PDF</link>"
- description="CVPreview: download as PDF link"
- id="xC3Khf"
- values={{
- link: (chunks: string) => (
- <Link href={pdf}>
- <a>{chunks}</a>
- </Link>
- ),
- }}
- />
- </p>
- </ExpandableWidget>
- );
-};
-
-export default CVPreview;
diff --git a/src/components/Widgets/RecentPosts/RecentPosts.module.scss b/src/components/Widgets/RecentPosts/RecentPosts.module.scss
deleted file mode 100644
index 1b85265..0000000
--- a/src/components/Widgets/RecentPosts/RecentPosts.module.scss
+++ /dev/null
@@ -1,109 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/placeholders";
-
-.list {
- --items: 3;
- --items-size: 25ch;
-
- @extend %reset-list;
-
- display: grid;
- grid-template-columns: repeat(
- auto-fit,
- min(calc(100vw - (var(--spacing-md) * 2)), var(--items-size))
- );
- justify-content: center;
- gap: var(--spacing-sm);
- width: min(
- calc(100vw - (var(--spacing-md) * 2)),
- calc(
- (var(--items-size) * var(--items)) +
- (var(--spacing-sm) * (var(--items) - 1))
- )
- );
- margin-bottom: var(--spacing-md);
-}
-
-.item {
- text-align: center;
-}
-
-.article {
- display: flex;
- flex-flow: column nowrap;
- height: 100%;
- padding: 0 0 var(--spacing-md);
-}
-
-.title {
- flex: 1;
- margin: var(--spacing-sm) 0;
- padding: 0 var(--spacing-md);
- text-decoration: underline solid transparent 0;
- transition: all 0.3s linear 0s;
-}
-
-.link {
- display: block;
- height: 100%;
- background: var(--color-bg);
- color: inherit;
- text-decoration: none;
- border: fun.convert-px(3) solid var(--color-primary);
- border-radius: fun.convert-px(5);
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-2)
- var(--color-shadow),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(5) fun.convert-px(-4)
- var(--color-shadow);
- transition: all 0.3s ease-in-out 0s;
-
- &:hover,
- &:focus,
- &:active {
- color: inherit;
- }
-
- &:hover,
- &:focus {
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow-light),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-2)
- var(--color-shadow-light),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(5) fun.convert-px(-4)
- var(--color-shadow-light),
- fun.convert-px(7) fun.convert-px(10) fun.convert-px(12) fun.convert-px(-3)
- var(--color-shadow-light);
- transform: scale(1.05);
- }
-
- &:focus {
- .title {
- text-decoration: underline solid var(--color-primary) 0.3ex;
- }
- }
-
- &:active {
- box-shadow: 0 0 0 0 var(--color-shadow);
- transform: scale(0.95);
-
- .title {
- text-decoration: none;
- }
- }
-}
-
-.cover {
- width: 100%;
- height: clamp(fun.convert-px(100), 20vw, fun.convert-px(150));
- position: relative;
- border: fun.convert-px(1) solid var(--color-border);
-}
-
-.meta {
- display: block;
- margin: 0;
- padding: 0 var(--spacing-md);
- font-size: var(--font-size-sm);
-}
diff --git a/src/components/Widgets/RecentPosts/RecentPosts.tsx b/src/components/Widgets/RecentPosts/RecentPosts.tsx
deleted file mode 100644
index 11d8558..0000000
--- a/src/components/Widgets/RecentPosts/RecentPosts.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import Spinner from '@components/Spinner/Spinner';
-import { getPublishedPosts } from '@services/graphql/queries';
-import { ArticlePreview } from '@ts/types/articles';
-import { PostsList } from '@ts/types/blog';
-import { settings } from '@utils/config';
-import { getFormattedDate } from '@utils/helpers/format';
-import Image from 'next/image';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import { useIntl } from 'react-intl';
-import useSWR from 'swr';
-import styles from './RecentPosts.module.scss';
-
-const RecentPosts = ({ posts }: { posts: PostsList }) => {
- const intl = useIntl();
- const { data, error } = useSWR<PostsList>(
- '/recent-posts',
- () => getPublishedPosts({ first: 3 }),
- { fallbackData: posts }
- );
- const router = useRouter();
- const locale = router.locale ? router.locale : settings.locales.defaultLocale;
-
- const getPost = (post: ArticlePreview) => {
- return (
- <li key={post.id} className={styles.item}>
- <Link href={`/article/${post.slug}`}>
- <a className={styles.link}>
- <article className={styles.article}>
- {post.featuredImage &&
- Object.keys(post.featuredImage).length > 0 && (
- <div className={styles.cover}>
- <Image
- src={post.featuredImage.sourceUrl}
- alt={post.featuredImage.altText}
- layout="fill"
- objectFit="contain"
- />
- </div>
- )}
- <h3 className={styles.title}>{post.title}</h3>
- <dl className={styles.meta}>
- <dt>
- {intl.formatMessage({
- defaultMessage: 'Published on:',
- description: 'RecentPosts: publication date label',
- id: '1h+N2z',
- })}
- </dt>
- <dd>
- <time dateTime={post.dates.publication}>
- {getFormattedDate(post.dates.publication, locale)}
- </time>
- </dd>
- </dl>
- </article>
- </a>
- </Link>
- </li>
- );
- };
-
- const getPostsItems = () => {
- if (error)
- return intl.formatMessage({
- defaultMessage: 'Failed to load.',
- description: 'RecentPosts: failed to load text',
- id: 'iyEh0R',
- });
- if (!data) return <Spinner />;
-
- return data.posts.map((post) => getPost(post));
- };
-
- return <ul className={styles.list}>{getPostsItems()}</ul>;
-};
-
-export default RecentPosts;
diff --git a/src/components/Widgets/RelatedThematics/RelatedThematics.tsx b/src/components/Widgets/RelatedThematics/RelatedThematics.tsx
deleted file mode 100644
index a66de82..0000000
--- a/src/components/Widgets/RelatedThematics/RelatedThematics.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { ExpandableWidget, List } from '@components/WidgetParts';
-import { ThematicPreview } from '@ts/types/taxonomies';
-import Link from 'next/link';
-import { useIntl } from 'react-intl';
-
-const RelatedThematics = ({ thematics }: { thematics: ThematicPreview[] }) => {
- const intl = useIntl();
- const sortedThematics = [...thematics].sort((a, b) =>
- a.title.localeCompare(b.title)
- );
-
- const thematicsList = sortedThematics.map((thematic) => {
- return (
- <li key={thematic.databaseId}>
- <Link href={`/thematique/${thematic.slug}`}>
- <a>{thematic.title}</a>
- </Link>
- </li>
- );
- });
-
- return (
- <ExpandableWidget
- expand={true}
- title={intl.formatMessage(
- {
- defaultMessage:
- '{thematicsCount, plural, =0 {Related thematics} one {Related thematic} other {Related thematics}}',
- description: 'RelatedThematics: widget title',
- id: 'qXQETZ',
- },
- { thematicsCount: thematics.length }
- )}
- withBorders={true}
- >
- <List items={thematicsList} />
- </ExpandableWidget>
- );
-};
-
-export default RelatedThematics;
diff --git a/src/components/Widgets/RelatedTopics/RelatedTopics.tsx b/src/components/Widgets/RelatedTopics/RelatedTopics.tsx
deleted file mode 100644
index 992173d..0000000
--- a/src/components/Widgets/RelatedTopics/RelatedTopics.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { ExpandableWidget, List } from '@components/WidgetParts';
-import { TopicPreview } from '@ts/types/taxonomies';
-import Link from 'next/link';
-import { useIntl } from 'react-intl';
-
-const RelatedTopics = ({ topics }: { topics: TopicPreview[] }) => {
- const intl = useIntl();
- const sortedTopics = [...topics].sort((a, b) =>
- a.title.localeCompare(b.title)
- );
-
- const topicsList = sortedTopics.map((topic) => {
- return (
- <li key={topic.databaseId}>
- <Link href={`/sujet/${topic.slug}`}>
- <a>{topic.title}</a>
- </Link>
- </li>
- );
- });
-
- return (
- <ExpandableWidget
- expand={true}
- title={intl.formatMessage(
- {
- defaultMessage:
- '{topicsCount, plural, =0 {Related topics} one {Related topic} other {Related topics}}',
- description: 'RelatedTopics: widget title',
- id: 'w/lPUh',
- },
- { topicsCount: topicsList.length }
- )}
- withBorders={true}
- >
- <List items={topicsList} />
- </ExpandableWidget>
- );
-};
-
-export default RelatedTopics;
diff --git a/src/components/Widgets/Sharing/Sharing.module.scss b/src/components/Widgets/Sharing/Sharing.module.scss
deleted file mode 100644
index ada3e2f..0000000
--- a/src/components/Widgets/Sharing/Sharing.module.scss
+++ /dev/null
@@ -1,193 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/mixins" as mix;
-@use "@styles/abstracts/placeholders";
-
-.list {
- @extend %flex-list;
-
- gap: var(--spacing-sm);
- padding: var(--spacing-2xs) 0 0 var(--spacing-2xs);
-
- @include mix.media("screen") {
- @include mix.dimensions("md") {
- gap: var(--spacing-xs);
- width: min-content;
- }
- }
-}
-
-.link {
- display: flex;
- flex-flow: row nowrap;
- align-items: center;
- padding: var(--spacing-2xs) var(--spacing-xs);
- border-radius: fun.convert-px(3);
- font-weight: 600;
- text-decoration: none;
- transition: all 0.3s ease-in-out 0s;
-
- &:hover,
- &:focus {
- color: hsl(0, 0%, 100%);
- transform: translateX(#{fun.convert-px(-3)})
- translateY(#{fun.convert-px(-3)});
- }
-
- &:active {
- color: hsl(0, 0%, 100%);
- transform: translateX(#{fun.convert-px(2)}) translateY(#{fun.convert-px(2)});
-
- @include mix.motion("reduce") {
- transform: none;
- }
- }
-
- &::before {
- display: block;
- background-repeat: no-repeat;
- content: "";
- filter: drop-shadow(
- #{fun.convert-px(1)} #{fun.convert-px(1)} #{fun.convert-px(1)} hsl(0, 0%, 0%)
- );
- width: fun.convert-px(30);
- height: fun.convert-px(30);
- }
-
- &--diaspora {
- background: hsl(0, 0%, 13%);
- box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(0, 0%, 3%);
-
- &:hover,
- &:focus {
- box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 hsl(0, 0%, 3%);
- }
-
- &:active {
- box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 hsl(0, 0%, 3%);
- }
-
- &::before {
- background-image: url(fun.encode-svg(
- '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path style="fill:#ffffff;" d="M15.257 21.928l-2.33-3.255c-.622-.87-1.128-1.549-1.155-1.55-.027 0-1.007 1.317-2.317 3.115-1.248 1.713-2.28 3.115-2.292 3.115-.035 0-4.5-3.145-4.51-3.178-.006-.016 1.003-1.497 2.242-3.292 1.239-1.794 2.252-3.29 2.252-3.325 0-.056-.401-.197-3.55-1.247a1604.93 1604.93 0 01-3.593-1.2c-.033-.013.153-.635.79-2.648.46-1.446.845-2.642.857-2.656.013-.015 1.71.528 3.772 1.207 2.062.678 3.766 1.233 3.787 1.233.021 0 .045-.032.053-.07.008-.039.026-1.794.04-3.902.013-2.107.036-3.848.05-3.87.02-.03.599-.038 2.725-.038 1.485 0 2.716.01 2.735.023.023.016.064 1.175.132 3.776.112 4.273.115 4.33.183 4.33.026 0 1.66-.547 3.631-1.216 1.97-.668 3.593-1.204 3.605-1.191.04.045 1.656 5.307 1.636 5.327-.011.01-1.656.574-3.655 1.252-2.75.932-3.638 1.244-3.645 1.284-.006.029.94 1.442 2.143 3.202 1.184 1.733 2.148 3.164 2.143 3.18-.012.036-4.442 3.299-4.48 3.299-.015 0-.577-.767-1.249-1.705z"/></svg>'
- ));
- }
- }
-
- &--email {
- background: hsl(0, 0%, 44%);
- box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(0, 0%, 34%);
-
- &:hover,
- &:focus {
- box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 hsl(0, 0%, 34%);
- }
-
- &:active {
- box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 hsl(0, 0%, 34%);
- }
-
- &::before {
- background-image: url(fun.encode-svg(
- '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M15.909 12.123L24 17.238V6.792zM0 6.792v10.446l8.091-5.115zM22.5 3.75h-21c-.748 0-1.343.558-1.455 1.276L12 12.904l11.955-7.877c-.112-.718-.706-1.276-1.455-1.276zm-7.965 9.279l-2.123 1.398a.75.75 0 01-.825 0l-2.122-1.4-9.417 5.957c.116.712.707 1.266 1.452 1.266h21c.746 0 1.337-.553 1.452-1.266z"/></svg>'
- ));
- }
- }
-
- &--facebook {
- background: hsl(214, 89%, 52%);
- box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(214, 89%, 42%);
-
- &:hover,
- &:focus {
- box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0
- hsl(214, 89%, 42%);
- }
-
- &:active {
- box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0
- hsl(214, 89%, 42%);
- }
-
- &::before {
- background-image: url(fun.encode-svg(
- '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'
- ));
- }
- }
-
- &--journal-du-hacker {
- background: hsl(210, 24%, 51%);
- box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(210, 24%, 41%);
-
- &:hover,
- &:focus {
- box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0
- hsl(210, 24%, 41%);
- }
-
- &:active {
- box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0
- hsl(210, 24%, 41%);
- }
-
- &::before {
- background-image: url(fun.encode-svg(
- '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M17.822 23.297a6.644 6.644 0 00-.654.032c-1.104.1-2.451-.378-3.244-1.15a3.223 3.223 0 01-.52-.739c-.209-.425-.22-.489-.211-1.178a8.174 8.174 0 01.19-1.585c.243-1.151.155-1.449-.514-1.737-.4-.172-.632-.135-1 .16-.268.215-.28.463-.07 1.532.298 1.526.286 2.238-.05 2.907-.28.56-.443.703-1.287 1.133-1.005.513-1.461.638-2.332.638-.73 0-1.014-.082-1.276-.366-.134-.145-.148-.2-.085-.32.099-.184.329-.3.959-.488.277-.082.604-.236.727-.341.123-.105.329-.265.457-.354.32-.222.562-.761.563-1.254 0-.331-.188-1.034-.45-1.676-.138-.338-.38.085-.38.666 0 .434-.673 1.569-.93 1.569-.048 0-.288.101-.532.225-.43.219-.47.225-1.31.225-.815 0-.889-.011-1.235-.194-.42-.22-.902-.694-1.094-1.073a2.752 2.752 0 00-.227-.377c-.083-.102-.08-.143.018-.293.206-.314.473-.317 1.186-.011.583.25 1.22.215 1.582-.086.168-.139.325-.697.342-1.217.02-.598-.049-.66-.596-.528-.86.206-1.762-.084-2.76-.887-.916-.739-1.362-.845-2.241-.538-.262.092-.51.153-.552.137-.042-.016-.134-.136-.204-.268-.118-.218-.12-.252-.02-.403.156-.24.714-.573 1.185-.708.297-.086.588-.11 1.076-.09.655.026.687.035 1.567.458.54.259.99.43 1.127.43.27 0 1.014-.37 1.159-.577.167-.238.124-.34-.322-.776-1.19-1.16-1.943-2.608-2.24-4.31-.124-.702-.14-1.888-.035-2.483.116-.656.677-2.273.915-2.64.385-.59 1.823-1.965 2.585-2.469C9.187.905 11.43.395 13.715.785c2.457.42 4.507 1.61 5.849 3.394 1.062 1.414 1.554 2.859 1.553 4.57 0 1.778-.497 3.238-1.599 4.693a6.207 6.207 0 00-.34.476c0 .013.205.12.456.238.737.345 1.169.844 1.726 1.994.256.527.531 1.031.613 1.12.225.247.614.42 1.099.49.588.085.804.178.9.388.109.24-.111.55-.402.563-.11.005-.394.033-.63.062-.887.107-1.851-.251-2.416-.898-.17-.193-.503-.616-.74-.939-.455-.616-.818-.922-1.054-.888-.117.017-.14.066-.127.28.008.142.068.34.133.438.09.137.127.412.161 1.196.05 1.153.147 1.458.55 1.726.306.204.552.198 1.11-.025.581-.233.923-.238 1.159-.018.243.227.2.637-.11 1.026-.33.419-1.338.899-2.001.954-1.194.1-2.371-.602-2.828-1.686-.062-.147-.197-.61-.301-1.03-.12-.486-.221-.762-.28-.762-.109 0-.263.401-.27.705-.003.12-.056.417-.118.657-.328 1.282.307 2.309 1.66 2.684.657.182.808.299.808.623 0 .319-.165.494-.454.481z"/></svg>'
- ));
- }
- }
-
- &--linkedin {
- background: hsl(210, 90%, 40%);
- box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(210, 90%, 30%);
-
- &:hover,
- &:focus {
- box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0
- hsl(210, 90%, 30%);
- }
-
- &:active {
- box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0
- hsl(210, 90%, 30%);
- }
-
- &::before {
- background-image: url(fun.encode-svg(
- '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path style="fill:#ffffff;" d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'
- ));
- }
- }
-
- &--twitter {
- background: hsl(203, 89%, 53%);
- box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(203, 89%, 43%);
-
- &:hover,
- &:focus {
- box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0
- hsl(203, 89%, 43%);
- }
-
- &:active {
- box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0
- hsl(203, 89%, 43%);
- }
-
- &::before {
- background-image: url(fun.encode-svg(
- '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg>'
- ));
- }
- }
-}
-
-:global {
- [data-theme="dark"] {
- :local {
- .link {
- filter: brightness(0.85) contrast(1.1);
- }
- }
- }
-}
diff --git a/src/components/Widgets/Sharing/Sharing.tsx b/src/components/Widgets/Sharing/Sharing.tsx
deleted file mode 100644
index 45fe3ce..0000000
--- a/src/components/Widgets/Sharing/Sharing.tsx
+++ /dev/null
@@ -1,238 +0,0 @@
-import { ExpandableWidget } from '@components/WidgetParts';
-import { useRouter } from 'next/router';
-import { useEffect, useState } from 'react';
-import { useIntl } from 'react-intl';
-import styles from './Sharing.module.scss';
-
-type Parameters = {
- content: string;
- image: string;
- title: string;
- url: string;
-};
-
-type Website = {
- id: string;
- name: string;
- parameters: Parameters;
- url: string;
-};
-
-const Sharing = ({ excerpt, title }: { excerpt: string; title: string }) => {
- const intl = useIntl();
- const [pageExcerpt, setPageExcerpt] = useState('');
- const [pageUrl, setPageUrl] = useState('');
- const [domainName, setDomainName] = useState('');
- const router = useRouter();
-
- useEffect(() => {
- const divEl = document.createElement('div');
- divEl.innerHTML = excerpt;
- const cleanExcerpt = divEl.textContent!;
- setPageExcerpt(cleanExcerpt);
- }, [excerpt]);
-
- useEffect(() => {
- const { protocol, hostname, port } = window.location;
- const currentPort = port ? `:${port}` : '';
- const fullUrl = `${protocol}//${hostname}${currentPort}${router.asPath}`;
-
- setDomainName(hostname);
- setPageUrl(fullUrl);
- }, [router.asPath]);
-
- const getSharingUrl = (website: Website): string => {
- const { id, parameters, url } = website;
- let sharingUrl = `${url}?`;
- let count = 0;
-
- for (const [key, value] of Object.entries(parameters)) {
- if (!value) continue;
-
- sharingUrl += count > 0 ? `&${value}=` : `${value}=`;
-
- switch (key) {
- case 'content':
- if (id === 'email') {
- const intro = intl.formatMessage({
- defaultMessage: 'Introduction:',
- description: 'Sharing: email content prefix',
- id: 'yfgMcl',
- });
- const readMore = intl.formatMessage({
- defaultMessage: 'Read more here:',
- description: 'Sharing: content link prefix',
- id: 'UsQske',
- });
- const body = `${intro}\n\n"${pageExcerpt}"\n\n${readMore} ${pageUrl}`;
- sharingUrl += encodeURI(body);
- } else {
- sharingUrl += encodeURI(pageExcerpt);
- }
- break;
- case 'title':
- const prefix =
- id === 'email'
- ? intl.formatMessage(
- {
- defaultMessage: 'Seen on {domainName}:',
- description: 'Sharing: seen on text',
- id: 'eUXMG4',
- },
- { domainName }
- )
- : '';
- sharingUrl += encodeURI(`${prefix} ${title}`);
- break;
- case 'url':
- sharingUrl += encodeURI(pageUrl);
- break;
- default:
- break;
- }
-
- count++;
- }
-
- return sharingUrl;
- };
-
- const websites = [
- {
- id: 'diaspora',
- name: intl.formatMessage({
- defaultMessage: 'Diaspora',
- description: 'Sharing: Diaspora',
- id: 'Dhow1m',
- }),
- parameters: {
- content: '',
- image: '',
- title: 'title',
- url: 'url',
- },
- url: 'https://share.diasporafoundation.org/',
- },
- {
- id: 'facebook',
- name: intl.formatMessage({
- defaultMessage: 'Facebook',
- description: 'Sharing: Facebook',
- id: '7iiaRx',
- }),
- parameters: {
- content: '',
- image: '',
- title: '',
- url: 'u',
- },
- url: 'https://www.facebook.com/sharer/sharer.php',
- },
- {
- id: 'linkedin',
- name: intl.formatMessage({
- defaultMessage: 'LinkedIn',
- description: 'Sharing: LinkedIn',
- id: 'csCQQk',
- }),
- parameters: {
- content: '',
- image: '',
- title: '',
- url: 'url',
- },
- url: 'https://www.linkedin.com/sharing/share-offsite/',
- },
- {
- id: 'twitter',
- name: intl.formatMessage({
- defaultMessage: 'Twitter',
- description: 'Sharing: Twitter',
- id: 'WjVBnY',
- }),
- parameters: {
- content: '',
- image: '',
- title: 'text',
- url: 'url',
- },
- url: 'https://twitter.com/intent/tweet',
- },
- {
- id: 'journal-du-hacker',
- name: intl.formatMessage({
- defaultMessage: 'Journal du hacker',
- description: 'Sharing: Journal du hacker',
- id: 'P0I+Xm',
- }),
- parameters: {
- content: '',
- image: '',
- title: 'title',
- url: 'url',
- },
- url: 'https://www.journalduhacker.net/stories/new',
- },
- {
- id: 'email',
- name: intl.formatMessage({
- defaultMessage: 'Email',
- description: 'Sharing: Email',
- id: 'lKZm9t',
- }),
- parameters: {
- content: 'body',
- image: '',
- title: 'subject',
- url: '',
- },
- url: 'mailto:',
- },
- ];
-
- const getItems = () => {
- return websites.map((website) => {
- const { id, name } = website;
- const sharingUrl = getSharingUrl(website);
- const linkModifier = `link--${id}`;
-
- return (
- <li key={id}>
- <a
- href={sharingUrl}
- title={name}
- className={`${styles.link} ${styles[linkModifier]}`}
- >
- <span className="screen-reader-text">
- {intl.formatMessage(
- {
- defaultMessage: 'Share on {name}',
- description: 'Sharing: share on social network text',
- id: 'ureXFw',
- },
- { name }
- )}
- </span>
- </a>
- </li>
- );
- });
- };
-
- return (
- <ExpandableWidget
- title={intl.formatMessage({
- defaultMessage: 'Share',
- description: 'Sharing: widget title',
- id: 'q3U6uI',
- })}
- expand={true}
- >
- <ul className={`${styles.list} ${styles['list--sharing']}`}>
- {getItems()}
- </ul>
- </ExpandableWidget>
- );
-};
-
-export default Sharing;
diff --git a/src/components/Widgets/SocialMedia/SocialMedia.module.scss b/src/components/Widgets/SocialMedia/SocialMedia.module.scss
deleted file mode 100644
index 2ef34bf..0000000
--- a/src/components/Widgets/SocialMedia/SocialMedia.module.scss
+++ /dev/null
@@ -1,59 +0,0 @@
-@use "@styles/abstracts/functions" as fun;
-@use "@styles/abstracts/placeholders";
-
-.list {
- @extend %flex-list;
-
- flex: 0 0 100%;
- gap: var(--spacing-xs);
- align-items: center;
- padding: var(--spacing-2xs) 0 0 var(--spacing-2xs);
-}
-
-.link {
- display: block;
- width: 3em;
- height: 3em;
- background: none;
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
- fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-1)
- var(--color-shadow),
- fun.convert-px(3) fun.convert-px(4) fun.convert-px(4) fun.convert-px(-3)
- var(--color-shadow),
- 0 0 0 0 var(--color-shadow);
- transition: all 0.3s linear 0s;
-
- &:hover,
- &:focus {
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
- fun.convert-px(1) fun.convert-px(1) fun.convert-px(2) fun.convert-px(-1)
- var(--color-shadow-light),
- fun.convert-px(3) fun.convert-px(3) fun.convert-px(4) fun.convert-px(-4)
- var(--color-shadow-light),
- fun.convert-px(6) fun.convert-px(6) fun.convert-px(10) fun.convert-px(-3)
- var(--color-shadow);
- transform: scale(1.15);
- }
-
- &:focus {
- outline: var(--color-primary) dashed fun.convert-px(2);
- }
-
- &:active {
- box-shadow: 0 0 0 0 var(--color-shadow);
- outline: none;
- transform: scale(0.9);
- }
-}
-
-:global {
- [data-theme="dark"] {
- :local {
- .icon {
- filter: brightness(0.85) contrast(1.1);
- }
- }
- }
-}
diff --git a/src/components/Widgets/SocialMedia/SocialMedia.tsx b/src/components/Widgets/SocialMedia/SocialMedia.tsx
deleted file mode 100644
index decf657..0000000
--- a/src/components/Widgets/SocialMedia/SocialMedia.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import GithubIcon from '@assets/images/social-media/github.svg';
-import GitlabIcon from '@assets/images/social-media/gitlab.svg';
-import LinkedInIcon from '@assets/images/social-media/linkedin.svg';
-import TwitterIcon from '@assets/images/social-media/twitter.svg';
-import styles from './SocialMedia.module.scss';
-import { ExpandableWidget } from '@components/WidgetParts';
-import { useIntl } from 'react-intl';
-
-const SocialMedia = ({
- title,
- github = false,
- gitlab = false,
- linkedin = false,
- twitter = false,
-}: {
- title: string;
- github?: boolean;
- gitlab?: boolean;
- linkedin?: boolean;
- twitter?: boolean;
-}) => {
- const intl = useIntl();
-
- const websites = [
- {
- id: 'github',
- name: intl.formatMessage({
- defaultMessage: 'Github',
- description: 'SocialMedia: Github',
- id: 'SWjj4l',
- }),
- url: 'https://github.com/ArmandPhilippot',
- },
- {
- id: 'gitlab',
- name: intl.formatMessage({
- defaultMessage: 'Gitlab',
- description: 'SocialMedia: Gitlab',
- id: 'obmlFh',
- }),
- url: 'https://gitlab.com/ArmandPhilippot',
- },
- {
- id: 'linkedin',
- name: intl.formatMessage({
- defaultMessage: 'LinkedIn',
- description: 'SocialMedia: LinkedIn',
- id: 'VbcHZ4',
- }),
- url: 'https://www.linkedin.com/in/armandphilippot',
- },
- {
- id: 'twitter',
- name: intl.formatMessage({
- defaultMessage: 'Twitter',
- description: 'SocialMedia: Twitter',
- id: 'IPs/Ck',
- }),
- url: 'https://twitter.com/ArmandPhilippot',
- },
- ];
-
- const getIcon = (id: string) => {
- switch (id) {
- case 'github':
- return <GithubIcon className={styles.icon} />;
- case 'gitlab':
- return <GitlabIcon className={styles.icon} />;
- case 'linkedin':
- return <LinkedInIcon className={styles.icon} />;
- case 'twitter':
- return <TwitterIcon className={styles.icon} />;
- default:
- break;
- }
- };
-
- const shouldDisplayLink = (id: string) => {
- switch (id) {
- case 'github':
- return github;
- case 'gitlab':
- return gitlab;
- case 'linkedin':
- return linkedin;
- case 'twitter':
- return twitter;
- default:
- break;
- }
- };
-
- const items = websites.map((website) => {
- return shouldDisplayLink(website.id) ? (
- <li key={website.id}>
- <a href={website.url} className={styles.link}>
- {getIcon(website.id)}
- <span className="screen-reader-text">{website.name}</span>
- </a>
- </li>
- ) : (
- ''
- );
- });
-
- return (
- <ExpandableWidget title={title} expand={true}>
- <ul className={styles.list}>{items}</ul>
- </ExpandableWidget>
- );
-};
-
-export default SocialMedia;
diff --git a/src/components/Widgets/ThematicsList/ThematicsList.tsx b/src/components/Widgets/ThematicsList/ThematicsList.tsx
deleted file mode 100644
index 51254ee..0000000
--- a/src/components/Widgets/ThematicsList/ThematicsList.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import Spinner from '@components/Spinner/Spinner';
-import { ExpandableWidget, List } from '@components/WidgetParts';
-import { getAllThematics } from '@services/graphql/queries';
-import { TitleLevel } from '@ts/types/app';
-import { ThematicPreview } from '@ts/types/taxonomies';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import { useIntl } from 'react-intl';
-import useSWR from 'swr';
-
-const ThematicsList = ({
- title,
- titleLevel,
- initialData,
-}: {
- title: string;
- titleLevel?: TitleLevel;
- initialData?: ThematicPreview[];
-}) => {
- const intl = useIntl();
- const router = useRouter();
- const isThematic = () => router.asPath.includes('/thematique/');
- const currentThematicSlug = isThematic()
- ? router.asPath.replace('/thematique/', '')
- : '';
-
- const { data, error } = useSWR('/api/thematics', getAllThematics, {
- fallbackData: initialData,
- });
-
- const getList = () => {
- if (error)
- return (
- <ul>
- {intl.formatMessage({
- defaultMessage: 'Failed to load.',
- description: 'ThematicsList: failed to load text',
- id: 'PxMDzL',
- })}
- </ul>
- );
- if (!data)
- return (
- <ul>
- <Spinner />
- </ul>
- );
-
- const thematics = data.map((thematic) => {
- return currentThematicSlug !== thematic.slug ? (
- <li key={thematic.databaseId}>
- <Link href={`/thematique/${thematic.slug}`}>
- <a>{thematic.title}</a>
- </Link>
- </li>
- ) : (
- ''
- );
- });
-
- return <List items={thematics} />;
- };
-
- return (
- <ExpandableWidget
- title={title}
- titleLevel={titleLevel}
- withBorders={true}
- expand={true}
- >
- {getList()}
- </ExpandableWidget>
- );
-};
-
-export default ThematicsList;
diff --git a/src/components/Widgets/ToC/ToC.tsx b/src/components/Widgets/ToC/ToC.tsx
deleted file mode 100644
index 3f759db..0000000
--- a/src/components/Widgets/ToC/ToC.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { ExpandableWidget, OrderedList } from '@components/WidgetParts';
-import { Heading } from '@ts/types/app';
-import useHeadingsTree from '@utils/hooks/useHeadingsTree';
-import { FormattedMessage, useIntl } from 'react-intl';
-
-const ToC = () => {
- const intl = useIntl();
- const headingsTree = useHeadingsTree('article');
- const title = intl.formatMessage({
- defaultMessage: 'Table of contents',
- description: 'ToC: widget title',
- id: 'Zg4L7U',
- });
-
- const getItems = (headings: Heading[]) => {
- return headings.map((heading) => {
- return (
- <li key={heading.id}>
- <a href={`#${heading.id}`}>
- <FormattedMessage
- defaultMessage="<a11y>Jump to </a11y>{title}"
- description="ToC: link"
- id="GgIWnN"
- values={{
- title: heading.title,
- a11y: (chunks: string) => (
- <span className="screen-reader-text">{chunks}</span>
- ),
- }}
- />
- </a>
- {heading.children.length > 0 && (
- <OrderedList items={getItems(heading.children)} />
- )}
- </li>
- );
- });
- };
-
- return (
- <ExpandableWidget title={title} kind="toc" expand={true} withBorders={true}>
- <noscript>
- {intl.formatMessage({
- defaultMessage:
- 'Javascript is required to use the table of contents.',
- description: 'ToC: noscript tag',
- id: 'RZzx/4',
- })}
- </noscript>
- <OrderedList items={getItems(headingsTree)} />
- </ExpandableWidget>
- );
-};
-
-export default ToC;
diff --git a/src/components/Widgets/TopicsList/TopicsList.tsx b/src/components/Widgets/TopicsList/TopicsList.tsx
deleted file mode 100644
index 7bc7d70..0000000
--- a/src/components/Widgets/TopicsList/TopicsList.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import Spinner from '@components/Spinner/Spinner';
-import { ExpandableWidget, List } from '@components/WidgetParts';
-import { getAllTopics } from '@services/graphql/queries';
-import { TitleLevel } from '@ts/types/app';
-import { TopicPreview } from '@ts/types/taxonomies';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import { useIntl } from 'react-intl';
-import useSWR from 'swr';
-
-const TopicsList = ({
- title,
- titleLevel,
- initialData,
-}: {
- title: string;
- titleLevel?: TitleLevel;
- initialData?: TopicPreview[];
-}) => {
- const intl = useIntl();
- const router = useRouter();
- const isTopic = () => router.asPath.includes('/sujet/');
- const currentTopicSlug = isTopic()
- ? router.asPath.replace('/sujet/', '')
- : '';
-
- const { data, error } = useSWR('/api/topics', getAllTopics, {
- fallbackData: initialData,
- });
-
- const getList = () => {
- if (error)
- return (
- <ul>
- {intl.formatMessage({
- defaultMessage: 'Failed to load.',
- description: 'TopicsList: failed to load text',
- id: '00Pf5p',
- })}
- </ul>
- );
- if (!data)
- return (
- <ul>
- <Spinner />
- </ul>
- );
-
- const topics = data.map((topic) => {
- return currentTopicSlug !== topic.slug ? (
- <li key={topic.databaseId}>
- <Link href={`/sujet/${topic.slug}`}>
- <a>{topic.title}</a>
- </Link>
- </li>
- ) : (
- ''
- );
- });
-
- return <List items={topics} />;
- };
-
- return (
- <ExpandableWidget
- title={title}
- titleLevel={titleLevel}
- withBorders={true}
- expand={true}
- >
- {getList()}
- </ExpandableWidget>
- );
-};
-
-export default TopicsList;
diff --git a/src/components/Widgets/index.tsx b/src/components/Widgets/index.tsx
deleted file mode 100644
index 8354449..0000000
--- a/src/components/Widgets/index.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import CVPreview from './CVPreview/CVPreview';
-import RecentPosts from './RecentPosts/RecentPosts';
-import RelatedThematics from './RelatedThematics/RelatedThematics';
-import RelatedTopics from './RelatedTopics/RelatedTopics';
-import Sharing from './Sharing/Sharing';
-import SocialMedia from './SocialMedia/SocialMedia';
-import ThematicsList from './ThematicsList/ThematicsList';
-import ToC from './ToC/ToC';
-import TopicsList from './TopicsList/TopicsList';
-
-export {
- CVPreview,
- RecentPosts,
- RelatedThematics,
- RelatedTopics,
- Sharing,
- SocialMedia,
- ThematicsList,
- ToC,
- TopicsList,
-};
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
deleted file mode 100644
index 24c6951..0000000
--- a/src/pages/404.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { getLayout } from '@components/Layouts/Layout';
-import PostHeader from '@components/PostHeader/PostHeader';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { settings } from '@utils/config';
-import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n';
-import { GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import Link from 'next/link';
-import { FormattedMessage, useIntl } from 'react-intl';
-
-const Error404: NextPageWithLayout = () => {
- const intl = useIntl();
-
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: 'Error 404: Page not found - {websiteName}',
- description: '404Page: SEO - Page title',
- id: '310o3F',
- },
- { websiteName: settings.name }
- );
- const pageDescription = intl.formatMessage({
- defaultMessage: 'Page not found.',
- description: '404Page: SEO - Meta description',
- id: '48Ww//',
- });
-
- return (
- <>
- <Head>
- <title>{pageTitle}</title>
- <meta name="description" content={pageDescription} />
- </Head>
- <div className={`${styles.article} ${styles['article--no-comments']}`}>
- <PostHeader
- title={intl.formatMessage({
- defaultMessage: 'Page not found',
- description: '404Page: page title',
- id: 'OccTWi',
- })}
- />
- <div className={styles.body}>
- <FormattedMessage
- defaultMessage="Sorry, it seems that the page your are looking for does not exist. If you think this path should work, feel free to <link>contact me</link> with the necessary information so that I can fix the problem."
- description="404Page: page body"
- id="ZWh78Y"
- values={{
- link: (chunks: string) => (
- <Link href="/contact/">
- <a>{chunks}</a>
- </Link>
- ),
- }}
- />
- </div>
- </div>
- </>
- );
-};
-
-Error404.getLayout = getLayout;
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const intl = await getIntlInstance();
- const breadcrumbTitle = intl.formatMessage({
- defaultMessage: 'Error 404',
- description: '404Page: breadcrumb item',
- id: 'ywkCsK',
- });
- const { locale } = context;
- const translation = await loadTranslation(locale);
-
- return {
- props: {
- breadcrumbTitle,
- locale,
- translation,
- },
- };
-};
-
-export default Error404;
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 84c2469..939b337 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -1,17 +1,16 @@
-import { AppPropsWithLayout } from '@ts/types/app';
import { settings } from '@utils/config';
import { AckeeProvider } from '@utils/providers/ackee';
import { PrismThemeProvider } from '@utils/providers/prism-theme';
import { ThemeProvider } from 'next-themes';
+import { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import { IntlProvider } from 'react-intl';
import '../styles/globals.scss';
-const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => {
+const App = ({ Component, pageProps }: AppProps) => {
const { locale, defaultLocale } = useRouter();
const appLocale: string = locale || settings.locales.defaultLocale;
- const getLayout = Component.getLayout ?? ((page) => page);
return (
<AckeeProvider domain={settings.ackee.url} siteId={settings.ackee.siteId}>
<IntlProvider
@@ -25,7 +24,7 @@ const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => {
enableSystem={true}
>
<PrismThemeProvider>
- {getLayout(<Component {...pageProps} />)}
+ <Component {...pageProps} />
</PrismThemeProvider>
</ThemeProvider>
</IntlProvider>
@@ -33,4 +32,4 @@ const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => {
);
};
-export default MyApp;
+export default App;
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx
deleted file mode 100644
index 27a6f7b..0000000
--- a/src/pages/article/[slug].tsx
+++ /dev/null
@@ -1,291 +0,0 @@
-import CommentForm from '@components/CommentForm/CommentForm';
-import CommentsList from '@components/CommentsList/CommentsList';
-import { getLayout } from '@components/Layouts/Layout';
-import PostFooter from '@components/PostFooter/PostFooter';
-import PostHeader from '@components/PostHeader/PostHeader';
-import Sidebar from '@components/Sidebar/Sidebar';
-import Spinner from '@components/Spinner/Spinner';
-import { Sharing, ToC } from '@components/Widgets';
-import {
- getAllPostsSlug,
- getCommentsByPostId,
- getPostBySlug,
-} from '@services/graphql/queries';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { ArticleMeta, ArticleProps } from '@ts/types/articles';
-import { PrismDefaultPlugins, PrismPlugins } from '@ts/types/prism';
-import { settings } from '@utils/config';
-import { getFormattedPaths } from '@utils/helpers/format';
-import { loadTranslation } from '@utils/helpers/i18n';
-import { addPrismClasses } from '@utils/helpers/prism';
-import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import Prism from 'prismjs';
-import { ParsedUrlQuery } from 'querystring';
-import { useCallback, useEffect, useMemo } from 'react';
-import { useIntl } from 'react-intl';
-import { Blog, BlogPosting, Graph, WebPage } from 'schema-dts';
-
-const SingleArticle: NextPageWithLayout<ArticleProps> = ({
- comments,
- post,
-}) => {
- const intl = useIntl();
- const router = useRouter();
-
- const loadPrismPlugins = useCallback(
- async (prismPlugins: (PrismDefaultPlugins | PrismPlugins)[]) => {
- for (const plugin of prismPlugins) {
- try {
- if (plugin === 'color-scheme') {
- await import(`@utils/plugins/prism-${plugin}`);
- } else {
- await import(`prismjs/plugins/${plugin}/prism-${plugin}.min.js`);
-
- if (plugin === 'autoloader')
- Prism.plugins.autoloader.languages_path = '/prism/';
- }
- } catch (error) {
- console.error('Article: an error occurred with Prism.');
- console.error(error);
- }
- }
- },
- []
- );
-
- const plugins: (PrismDefaultPlugins | PrismPlugins)[] = useMemo(
- () => [
- 'autoloader',
- 'toolbar',
- 'show-language',
- 'copy-to-clipboard',
- 'color-scheme',
- 'command-line',
- 'line-numbers',
- 'match-braces',
- 'normalize-whitespace',
- ],
- []
- );
-
- useEffect(() => {
- loadPrismPlugins(plugins).then(() => {
- addPrismClasses();
- Prism.highlightAll();
- });
- }, [plugins, loadPrismPlugins]);
-
- if (router.isFallback) return <Spinner />;
-
- const {
- author,
- commentCount,
- content,
- databaseId,
- dates,
- featuredImage,
- info,
- intro,
- seo,
- topics,
- thematics,
- title,
- } = post;
-
- const meta: ArticleMeta = {
- author,
- commentCount: commentCount || undefined,
- dates,
- readingTime: info.readingTime,
- thematics,
- wordsCount: info.wordsCount,
- };
-
- const articleUrl = `${settings.url}${router.asPath}`;
-
- const webpageSchema: WebPage = {
- '@id': `${articleUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- lastReviewed: dates.update,
- name: seo.title,
- description: seo.metaDesc,
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${articleUrl}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const blogSchema: Blog = {
- '@id': `${settings.url}/#blog`,
- '@type': 'Blog',
- blogPost: { '@id': `${settings.url}/#article` },
- isPartOf: {
- '@id': `${articleUrl}`,
- },
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- };
-
- const publicationDate = new Date(dates.publication);
- const updateDate = new Date(dates.update);
-
- const blogPostSchema: BlogPosting = {
- '@id': `${settings.url}/#article`,
- '@type': 'BlogPosting',
- name: title,
- description: intro,
- articleBody: content,
- author: { '@id': `${settings.url}/#branding` },
- commentCount: commentCount || undefined,
- copyrightYear: publicationDate.getFullYear(),
- creator: { '@id': `${settings.url}/#branding` },
- dateCreated: publicationDate.toISOString(),
- dateModified: updateDate.toISOString(),
- datePublished: publicationDate.toISOString(),
- discussionUrl: `${articleUrl}/#comments`,
- editor: { '@id': `${settings.url}/#branding` },
- headline: title,
- image: featuredImage?.sourceUrl,
- inLanguage: settings.locales.defaultLocale,
- isPartOf: {
- '@id': `${settings.url}/blog`,
- },
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${articleUrl}` },
- thumbnailUrl: featuredImage?.sourceUrl,
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, blogSchema, blogPostSchema],
- };
-
- const copyText = intl.formatMessage({
- defaultMessage: 'Copy',
- description: 'Prism: copy button text (no clicked)',
- id: '/ly3AC',
- });
- const copiedText = intl.formatMessage({
- defaultMessage: 'Copied!',
- description: 'Prism: copy button text (clicked)',
- id: 'OV9r1K',
- });
- const errorText = intl.formatMessage({
- defaultMessage: 'Use Ctrl+c to copy',
- description: 'Prism: error text',
- id: 'z9qkcQ',
- });
- const darkTheme = intl.formatMessage({
- defaultMessage: 'Dark Theme 🌙',
- description: 'Prism: toggle dark theme button text',
- id: 'nFMdWI',
- });
- const lightTheme = intl.formatMessage({
- defaultMessage: 'Light Theme 🌞',
- description: 'Prism: toggle light theme button text',
- id: 'Ua2g2p',
- });
-
- return (
- <>
- <Head>
- <title>{seo.title}</title>
- <meta name="description" content={seo.metaDesc} />
- <meta property="og:url" content={`${articleUrl}`} />
- <meta property="og:type" content="article" />
- <meta property="og:title" content={title} />
- <meta property="og:description" content={intro} />
- <meta property="og:image" content={featuredImage?.sourceUrl} />
- <meta property="og:image:alt" content={featuredImage?.altText} />
- </Head>
- <Script
- id="schema-article"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="article"
- className={styles.article}
- data-prismjs-copy={copyText}
- data-prismjs-copy-success={copiedText}
- data-prismjs-copy-error={errorText}
- data-prismjs-color-scheme-dark={darkTheme}
- data-prismjs-color-scheme-light={lightTheme}
- >
- <PostHeader intro={intro} meta={meta} title={title} />
- <Sidebar
- position="left"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Table of Contents',
- description: 'ArticlePage: ToC sidebar aria-label',
- id: '9nhYRA',
- })}
- >
- <ToC />
- </Sidebar>
- <div
- className={styles.body}
- dangerouslySetInnerHTML={{ __html: content }}
- ></div>
- <PostFooter topics={topics} />
- <Sidebar
- position="right"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Sidebar',
- description: 'ArticlePage: right sidebar aria-label',
- id: 'JeYOeA',
- })}
- >
- <Sharing title={title} excerpt={intro} />
- </Sidebar>
- <section id="comments" className={styles.comments}>
- <CommentsList articleId={databaseId} comments={comments} />
- <CommentForm articleId={databaseId} />
- </section>
- </article>
- </>
- );
-};
-
-SingleArticle.getLayout = getLayout;
-
-interface PostParams extends ParsedUrlQuery {
- slug: string;
-}
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const { locale } = context;
- const translation = await loadTranslation(locale);
- const { slug } = context.params as PostParams;
- const post = await getPostBySlug(slug);
- const comments = await getCommentsByPostId(post.databaseId);
- const breadcrumbTitle = post.title;
-
- return {
- props: {
- breadcrumbTitle,
- comments,
- post,
- translation,
- },
- };
-};
-
-export const getStaticPaths: GetStaticPaths = async () => {
- const allSlugs = await getAllPostsSlug();
- const paths = getFormattedPaths(allSlugs);
-
- return {
- paths,
- fallback: true,
- };
-};
-
-export default SingleArticle;
diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx
deleted file mode 100644
index b5ced07..0000000
--- a/src/pages/blog/index.tsx
+++ /dev/null
@@ -1,269 +0,0 @@
-import { Button } from '@components/Buttons';
-import { getLayout } from '@components/Layouts/Layout';
-import Pagination from '@components/Pagination/Pagination';
-import PaginationCursor from '@components/PaginationCursor/PaginationCursor';
-import PostHeader from '@components/PostHeader/PostHeader';
-import PostsList from '@components/PostsList/PostsList';
-import Sidebar from '@components/Sidebar/Sidebar';
-import Spinner from '@components/Spinner/Spinner';
-import { ThematicsList, TopicsList } from '@components/Widgets';
-import {
- getAllThematics,
- getAllTopics,
- getPostsTotal,
- getPublishedPosts,
-} from '@services/graphql/queries';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { BlogPageProps, PostsList as PostsListData } from '@ts/types/blog';
-import { settings } from '@utils/config';
-import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n';
-import { GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { useEffect, useRef, useState } from 'react';
-import { useIntl } from 'react-intl';
-import { Blog as BlogSchema, Graph, WebPage } from 'schema-dts';
-import useSWRInfinite from 'swr/infinite';
-
-const Blog: NextPageWithLayout<BlogPageProps> = ({
- allThematics,
- allTopics,
- posts,
- totalPosts,
-}) => {
- const intl = useIntl();
- const lastPostRef = useRef<HTMLSpanElement>(null);
- const router = useRouter();
- const [isMounted, setIsMounted] = useState<boolean>(false);
-
- useEffect(() => {
- if (typeof window !== undefined) setIsMounted(true);
- }, []);
-
- const getKey = (pageIndex: number, previousData: PostsListData) => {
- if (previousData && !previousData.posts) return null;
-
- return pageIndex === 0
- ? { first: settings.postsPerPage }
- : {
- first: settings.postsPerPage,
- after: previousData.pageInfo.endCursor,
- };
- };
-
- const { data, error, size, setSize } = useSWRInfinite(
- getKey,
- getPublishedPosts,
- { fallbackData: [posts] }
- );
- const [totalPostsCount, setTotalPostsCount] = useState<number>(totalPosts);
-
- useEffect(() => {
- if (data) setTotalPostsCount(data[0].pageInfo.total);
- }, [data]);
-
- const [loadedPostsCount, setLoadedPostsCount] = useState<number>(
- settings.postsPerPage
- );
-
- useEffect(() => {
- if (data && data.length > 0) {
- const newCount =
- settings.postsPerPage +
- data[0].pageInfo.total -
- data[data.length - 1].pageInfo.total;
- setLoadedPostsCount(newCount);
- }
- }, [data]);
-
- const isLoadingInitialData = !data && !error;
- const isLoadingMore: boolean =
- isLoadingInitialData ||
- (size > 0 && data !== undefined && typeof data[size - 1] === 'undefined');
-
- const hasNextPage = data && data[data.length - 1].pageInfo.hasNextPage;
-
- const loadMorePosts = () => {
- if (lastPostRef.current) {
- lastPostRef.current.focus();
- }
- setSize(size + 1);
- };
-
- const getPostsList = () => {
- if (error)
- return intl.formatMessage({
- defaultMessage: 'Failed to load.',
- description: 'BlogPage: failed to load text',
- id: 'C/XGkH',
- });
- if (!data) return <Spinner />;
-
- return <PostsList ref={lastPostRef} data={data} showYears={true} />;
- };
-
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: 'Blog: development, open source - {websiteName}',
- description: 'BlogPage: SEO - Page title',
- id: '+Y+tLK',
- },
- { websiteName: settings.name }
- );
- const pageDescription = intl.formatMessage(
- {
- defaultMessage:
- "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.",
- description: 'BlogPage: SEO - Meta description',
- id: '18h/t0',
- },
- { websiteName: settings.name }
- );
- const pageUrl = `${settings.url}${router.asPath}`;
-
- const webpageSchema: WebPage = {
- '@id': `${pageUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: pageTitle,
- description: pageDescription,
- inLanguage: settings.locales.defaultLocale,
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${settings.url}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const blogSchema: BlogSchema = {
- '@id': `${settings.url}/#blog`,
- '@type': 'Blog',
- author: { '@id': `${settings.url}/#branding` },
- creator: { '@id': `${settings.url}/#branding` },
- editor: { '@id': `${settings.url}/#branding` },
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${pageUrl}` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, blogSchema],
- };
-
- const title = intl.formatMessage({
- defaultMessage: 'Blog',
- description: 'BlogPage: page title',
- id: '7TbbIk',
- });
-
- return (
- <>
- <Head>
- <title>{pageTitle}</title>
- <meta name="description" content={pageDescription} />
- <meta property="og:url" content={`${pageUrl}`} />
- <meta property="og:type" content="website" />
- <meta property="og:title" content={title} />
- <meta property="og:description" content={pageDescription} />
- </Head>
- <Script
- id="schema-blog"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="blog"
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader title={title} meta={{ results: totalPostsCount }} />
- <div className={styles.body}>
- {getPostsList()}
- {hasNextPage &&
- (isMounted ? (
- <>
- <PaginationCursor
- current={loadedPostsCount}
- total={totalPostsCount}
- />
- <Button
- isDisabled={isLoadingMore}
- clickHandler={loadMorePosts}
- position="center"
- spacing={true}
- >
- {intl.formatMessage({
- defaultMessage: 'Load more?',
- description: 'BlogPage: load more text',
- id: 'Kqq2cm',
- })}
- </Button>
- </>
- ) : (
- <Pagination baseUrl="/blog" total={totalPostsCount} />
- ))}
- </div>
- <Sidebar
- position="right"
- title={intl.formatMessage({
- defaultMessage: 'Filter by:',
- description: 'BlogPage: sidebar title',
- id: 'KERk7L',
- })}
- >
- <ThematicsList
- initialData={allThematics}
- title={intl.formatMessage({
- defaultMessage: 'Thematics',
- description: 'BlogPage: thematics list widget title',
- id: 'HriY57',
- })}
- />
- <TopicsList
- initialData={allTopics}
- title={intl.formatMessage({
- defaultMessage: 'Topics',
- description: 'BlogPage: topics list widget title',
- id: '2D9tB5',
- })}
- />
- </Sidebar>
- </article>
- </>
- );
-};
-
-Blog.getLayout = getLayout;
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const intl = await getIntlInstance();
- const breadcrumbTitle = intl.formatMessage({
- defaultMessage: 'Blog',
- description: 'BlogPage: breadcrumb item',
- id: 'R0eDmw',
- });
- const firstPosts = await getPublishedPosts({ first: settings.postsPerPage });
- const totalPosts = await getPostsTotal();
- const allThematics = await getAllThematics();
- const allTopics = await getAllTopics();
- const { locale } = context;
- const translation = await loadTranslation(locale);
-
- return {
- props: {
- allThematics,
- allTopics,
- breadcrumbTitle,
- locale,
- posts: firstPosts,
- totalPosts,
- translation,
- },
- };
-};
-
-export default Blog;
diff --git a/src/pages/blog/page/[id].tsx b/src/pages/blog/page/[id].tsx
deleted file mode 100644
index 6c4d2f8..0000000
--- a/src/pages/blog/page/[id].tsx
+++ /dev/null
@@ -1,205 +0,0 @@
-import { getLayout } from '@components/Layouts/Layout';
-import Pagination from '@components/Pagination/Pagination';
-import PostHeader from '@components/PostHeader/PostHeader';
-import PostsList from '@components/PostsList/PostsList';
-import Sidebar from '@components/Sidebar/Sidebar';
-import { ThematicsList, TopicsList } from '@components/Widgets';
-import {
- getAllThematics,
- getAllTopics,
- getEndCursor,
- getPostsTotal,
- getPublishedPosts,
-} from '@services/graphql/queries';
-import { NextPageWithLayout } from '@ts/types/app';
-import { BlogPageProps } from '@ts/types/blog';
-import { settings } from '@utils/config';
-import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n';
-import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { useIntl } from 'react-intl';
-import { Blog, Graph, WebPage } from 'schema-dts';
-import styles from '@styles/pages/Page.module.scss';
-import { getFormattedPageNumbers } from '@utils/helpers/format';
-import { useEffect } from 'react';
-import Spinner from '@components/Spinner/Spinner';
-
-const BlogPage: NextPageWithLayout<BlogPageProps> = ({
- allThematics,
- allTopics,
- posts,
- totalPosts,
-}) => {
- const intl = useIntl();
- const router = useRouter();
- const pageNumber = Number(router.query.id);
-
- useEffect(() => {
- if (router.query.id === '1') router.push('/blog');
- }, [router]);
-
- if (router.isFallback) return <Spinner />;
-
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: 'Blog - Page {number} - {websiteName}',
- description: 'BlogPage: SEO - Page title',
- id: '8w+jnD',
- },
- { number: pageNumber, websiteName: settings.name }
- );
- const pageDescription = intl.formatMessage(
- {
- defaultMessage:
- "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.",
- description: 'BlogPage: SEO - Meta description',
- id: '18h/t0',
- },
- { websiteName: settings.name }
- );
- const pageUrl = `${settings.url}${router.asPath}`;
-
- const webpageSchema: WebPage = {
- '@id': `${pageUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: pageTitle,
- description: pageDescription,
- inLanguage: settings.locales.defaultLocale,
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${settings.url}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const blogSchema: Blog = {
- '@id': `${settings.url}/#blog`,
- '@type': 'Blog',
- author: { '@id': `${settings.url}/#branding` },
- creator: { '@id': `${settings.url}/#branding` },
- editor: { '@id': `${settings.url}/#branding` },
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${pageUrl}` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, blogSchema],
- };
-
- const title = intl.formatMessage({
- defaultMessage: 'Blog',
- description: 'BlogPage: page title',
- id: '7TbbIk',
- });
-
- return (
- <>
- <Head>
- <title>{pageTitle}</title>
- <meta name="description" content={pageDescription} />
- <meta property="og:url" content={`${pageUrl}`} />
- <meta property="og:type" content="website" />
- <meta property="og:title" content={title} />
- <meta property="og:description" content={pageDescription} />
- </Head>
- <Script
- id="schema-blog"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="blog"
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader title={title} meta={{ results: totalPosts }} />
- <div className={styles.body}>
- <PostsList data={[posts]} showYears={true} />
- <Pagination baseUrl="/blog" total={totalPosts} />
- </div>
- <Sidebar
- position="right"
- title={intl.formatMessage({
- defaultMessage: 'Filter by:',
- description: 'BlogPage: sidebar title',
- id: 'KERk7L',
- })}
- >
- <ThematicsList
- initialData={allThematics}
- title={intl.formatMessage({
- defaultMessage: 'Thematics',
- description: 'BlogPage: thematics list widget title',
- id: 'HriY57',
- })}
- />
- <TopicsList
- initialData={allTopics}
- title={intl.formatMessage({
- defaultMessage: 'Topics',
- description: 'BlogPage: topics list widget title',
- id: '2D9tB5',
- })}
- />
- </Sidebar>
- </article>
- </>
- );
-};
-
-BlogPage.getLayout = getLayout;
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const intl = await getIntlInstance();
- const breadcrumbTitle = intl.formatMessage({
- defaultMessage: 'Blog',
- description: 'BlogPage: breadcrumb item',
- id: 'R0eDmw',
- });
- const { locale, params } = context;
- const queriedPageNumber = params ? Number(params.id) : 1;
- const queriedPostsNumber = settings.postsPerPage * queriedPageNumber;
- const endCursor =
- queriedPostsNumber === 1
- ? undefined
- : await getEndCursor({ first: queriedPostsNumber });
- const posts = await getPublishedPosts({
- first: settings.postsPerPage,
- after: endCursor,
- });
- const totalPosts = await getPostsTotal();
- const allThematics = await getAllThematics();
- const allTopics = await getAllTopics();
- const translation = await loadTranslation(locale);
-
- return {
- props: {
- allThematics,
- allTopics,
- breadcrumbTitle,
- locale,
- posts,
- totalPosts,
- translation,
- },
- };
-};
-
-export default BlogPage;
-
-export const getStaticPaths: GetStaticPaths = async () => {
- const totalPosts = await getPostsTotal();
- const totalPages = Math.floor(totalPosts / settings.postsPerPage);
- const paths = getFormattedPageNumbers(totalPages);
-
- return {
- paths,
- fallback: true,
- };
-};
diff --git a/src/pages/contact.tsx b/src/pages/contact.tsx
deleted file mode 100644
index 5934dd9..0000000
--- a/src/pages/contact.tsx
+++ /dev/null
@@ -1,151 +0,0 @@
-import ContactForm from '@components/ContactForm/ContactForm';
-import { getLayout } from '@components/Layouts/Layout';
-import PostHeader from '@components/PostHeader/PostHeader';
-import Sidebar from '@components/Sidebar/Sidebar';
-import { SocialMedia } from '@components/Widgets';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { settings } from '@utils/config';
-import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n';
-import { GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { useIntl } from 'react-intl';
-import { ContactPage as ContactPageSchema, Graph, WebPage } from 'schema-dts';
-
-const ContactPage: NextPageWithLayout = () => {
- const intl = useIntl();
- const router = useRouter();
-
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: 'Contact form - {websiteName}',
- description: 'ContactPage: SEO - Page title',
- id: 'Y3qRib',
- },
- { websiteName: settings.name }
- );
- const pageDescription = intl.formatMessage(
- {
- defaultMessage:
- "Contact {websiteName} through its website. All you need to do it's to fill the contact form.",
- description: 'ContactPage: SEO - Meta description',
- id: 'OIffB4',
- },
- { websiteName: settings.name }
- );
- const pageUrl = `${settings.url}${router.asPath}`;
- const title = intl.formatMessage({
- defaultMessage: 'Contact',
- description: 'ContactPage: page title',
- id: 'AN9iy7',
- });
- const intro = intl.formatMessage({
- defaultMessage: 'Please fill the form to contact me.',
- description: 'ContactPage: page introduction',
- id: '8Ls2mD',
- });
-
- const webpageSchema: WebPage = {
- '@id': `${pageUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: pageTitle,
- description: pageDescription,
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${pageUrl}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const contactSchema: ContactPageSchema = {
- '@id': `${settings.url}/#contact`,
- '@type': 'ContactPage',
- name: title,
- description: intro,
- author: { '@id': `${settings.url}/#branding` },
- creator: { '@id': `${settings.url}/#branding` },
- editor: { '@id': `${settings.url}/#branding` },
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${pageUrl}` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, contactSchema],
- };
-
- return (
- <>
- <Head>
- <title>{pageTitle}</title>
- <meta name="description" content={pageDescription} />
- <meta property="og:url" content={`${pageUrl}`} />
- <meta property="og:type" content="article" />
- <meta property="og:title" content={title} />
- <meta property="og:description" content={intro} />
- </Head>
- <Script
- id="schema-contact"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="contact"
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader title={title} intro={intro} />
- <div className={styles.body}>
- <p>
- {intl.formatMessage({
- defaultMessage: 'All fields marked with * are required.',
- description: 'ContactPage: required fields text',
- id: 'txusHd',
- })}
- </p>
- <ContactForm />
- </div>
- <Sidebar position="right">
- <SocialMedia
- title={intl.formatMessage({
- defaultMessage: 'Find me elsewhere',
- description: 'ContactPage: social media widget title',
- id: 'Qh2CwH',
- })}
- github={true}
- gitlab={true}
- linkedin={true}
- />
- </Sidebar>
- </article>
- </>
- );
-};
-
-ContactPage.getLayout = getLayout;
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const intl = await getIntlInstance();
- const breadcrumbTitle = intl.formatMessage({
- defaultMessage: 'Contact',
- description: 'ContactPage: breadcrumb item',
- id: 'CzTbM4',
- });
- const { locale } = context;
- const translation = await loadTranslation(locale);
-
- return {
- props: {
- breadcrumbTitle,
- locale,
- translation,
- },
- };
-};
-
-export default ContactPage;
diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx
deleted file mode 100644
index 71eb449..0000000
--- a/src/pages/cv.tsx
+++ /dev/null
@@ -1,181 +0,0 @@
-import { getLayout } from '@components/Layouts/Layout';
-import PostHeader from '@components/PostHeader/PostHeader';
-import Sidebar from '@components/Sidebar/Sidebar';
-import { CVPreview, SocialMedia, ToC } from '@components/Widgets';
-import CVContent, { intro, meta, pdf, image } from '@content/pages/cv.mdx';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { ArticleMeta } from '@ts/types/articles';
-import { settings } from '@utils/config';
-import { loadTranslation } from '@utils/helpers/i18n';
-import { GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { useIntl } from 'react-intl';
-import { AboutPage, Graph, WebPage } from 'schema-dts';
-
-const CV: NextPageWithLayout = () => {
- const intl = useIntl();
- const router = useRouter();
- const dates = {
- publication: meta.publishedOn,
- update: meta.updatedOn,
- };
-
- const pageMeta: ArticleMeta = {
- dates,
- };
- const pageUrl = `${settings.url}${router.asPath}`;
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: 'CV Front-end developer - {websiteName}',
- description: 'CVPage: SEO - Page title',
- id: 'Y1ZdJ6',
- },
- { websiteName: settings.name }
- );
- const pageDescription = intl.formatMessage(
- {
- defaultMessage:
- 'Discover the curriculum of {websiteName}, front-end developer located in France: skills, experiences and training.',
- description: 'CVPage: SEO - Meta description',
- id: 'bBdMGm',
- },
- { websiteName: settings.name }
- );
-
- const webpageSchema: WebPage = {
- '@id': `${pageUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: pageTitle,
- description: pageDescription,
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${pageUrl}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const publicationDate = new Date(dates.publication);
- const updateDate = new Date(dates.update);
-
- const cvSchema: AboutPage = {
- '@id': `${settings.url}/#cv`,
- '@type': 'AboutPage',
- name: pageTitle,
- description: intro,
- author: { '@id': `${settings.url}/#branding` },
- creator: { '@id': `${settings.url}/#branding` },
- dateCreated: publicationDate.toISOString(),
- dateModified: updateDate.toISOString(),
- datePublished: publicationDate.toISOString(),
- editor: { '@id': `${settings.url}/#branding` },
- image,
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- thumbnailUrl: image,
- mainEntityOfPage: { '@id': `${pageUrl}` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, cvSchema],
- };
-
- const title = intl.formatMessage(
- {
- defaultMessage: "{name}'s CV",
- description: 'CVPage: page title',
- id: 'Mj2BQf',
- },
- { name: settings.name }
- );
-
- return (
- <>
- <Head>
- <title>{pageTitle}</title>
- <meta name="description" content={pageDescription} />
- <meta property="og:url" content={`${pageUrl}`} />
- <meta property="og:type" content="article" />
- <meta property="og:title" content={title} />
- <meta property="og:description" content={intro} />
- <meta property="og:image" content={image} />
- <meta property="og:image:alt" content={title} />
- </Head>
- <Script
- id="schema-cv"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="cv"
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader intro={intro} meta={pageMeta} title={meta.title} />
- <Sidebar
- position="left"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Table of Contents',
- description: 'CVPage: ToC sidebar aria-label',
- id: 'g4DckL',
- })}
- >
- <ToC />
- </Sidebar>
- <div className={styles.body}>
- <CVContent />
- </div>
- <Sidebar
- position="right"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Sidebar',
- description: 'CVPage: right sidebar aria-label',
- id: 'QHOm5t',
- })}
- >
- <CVPreview
- title={intl.formatMessage({
- defaultMessage: 'Others formats',
- description: 'CVPage: cv preview widget title',
- id: 'B9OCyV',
- })}
- imgSrc={image}
- pdf={pdf}
- />
- <SocialMedia
- title={intl.formatMessage({
- defaultMessage: 'Open-source projects',
- description: 'CVPage: social media widget title',
- id: '+Dre5J',
- })}
- github={true}
- gitlab={true}
- />
- </Sidebar>
- </article>
- </>
- );
-};
-
-CV.getLayout = getLayout;
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const breadcrumbTitle = meta.title;
- const { locale } = context;
- const translation = await loadTranslation(locale);
-
- return {
- props: {
- breadcrumbTitle,
- locale,
- translation,
- },
- };
-};
-
-export default CV;
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
deleted file mode 100644
index ca0a809..0000000
--- a/src/pages/index.tsx
+++ /dev/null
@@ -1,241 +0,0 @@
-import FeedIcon from '@assets/images/icon-feed.svg';
-import { ButtonLink } from '@components/Buttons';
-import { ContactIcon } from '@components/Icons';
-import Layout from '@components/Layouts/Layout';
-import { ResponsiveImage } from '@components/MDX';
-import { RecentPosts } from '@components/Widgets';
-import HomePageContent from '@content/pages/homepage.mdx';
-import { getPublishedPosts } from '@services/graphql/queries';
-import styles from '@styles/pages/Home.module.scss';
-import { NextPageWithLayout, ResponsiveImageProps } from '@ts/types/app';
-import { PostsList } from '@ts/types/blog';
-import { settings } from '@utils/config';
-import { loadTranslation } from '@utils/helpers/i18n';
-import { NestedMDXComponents } from 'mdx/types';
-import { GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import Script from 'next/script';
-import type { ReactElement } from 'react';
-import { useIntl } from 'react-intl';
-import { Graph, WebPage } from 'schema-dts';
-
-type HomePageProps = {
- recentPosts: PostsList;
-};
-
-const Home: NextPageWithLayout<HomePageProps> = ({
- recentPosts,
-}: {
- recentPosts: PostsList;
-}) => {
- const intl = useIntl();
-
- const CodingLinks = () => {
- return (
- <ul className={styles['links-list']}>
- <li>
- <ButtonLink target="/thematique/developpement-web">
- {intl.formatMessage({
- defaultMessage: 'Web development',
- description: 'HomePage: link to web development thematic',
- id: 'vkF/RP',
- })}
- </ButtonLink>
- </li>
- <li>
- <ButtonLink target="/projets">
- {intl.formatMessage({
- defaultMessage: 'Projects',
- description: 'HomePage: link to projects',
- id: 'N44SOc',
- })}
- </ButtonLink>
- </li>
- </ul>
- );
- };
-
- const ColdarkRepos = () => {
- return (
- <ul className={styles['links-list']}>
- <li>
- <ButtonLink
- target="https://github.com/ArmandPhilippot/coldark"
- isExternal={true}
- >
- Github
- </ButtonLink>
- </li>
- <li>
- <ButtonLink
- target="https://gitlab.com/ArmandPhilippot/coldark"
- isExternal={true}
- >
- Gitlab
- </ButtonLink>
- </li>
- </ul>
- );
- };
-
- const LibreLinks = () => {
- return (
- <ul className={styles['links-list']}>
- <li>
- <ButtonLink target="/thematique/libre">
- {intl.formatMessage({
- defaultMessage: 'Free',
- description: 'HomePage: link to free thematic',
- id: 'w8GrOf',
- })}
- </ButtonLink>
- </li>
- <li>
- <ButtonLink target="/thematique/linux">
- {intl.formatMessage({
- defaultMessage: 'Linux',
- description: 'HomePage: link to Linux thematic',
- id: 'jASD7k',
- })}
- </ButtonLink>
- </li>
- </ul>
- );
- };
-
- const ShaarliLink = () => {
- return (
- <ul className={styles['links-list']}>
- <li>
- <ButtonLink target="https://shaarli.armandphilippot.com/">
- {intl.formatMessage({
- defaultMessage: 'Shaarli',
- description: 'HomePage: link to Shaarli',
- id: 'i5L19t',
- })}
- </ButtonLink>
- </li>
- </ul>
- );
- };
-
- const MoreLinks = () => {
- return (
- <ul className={styles['links-list']}>
- <li>
- <ButtonLink target="/contact">
- <ContactIcon />
- {intl.formatMessage({
- defaultMessage: 'Contact me',
- description: 'HomePage: contact button text',
- id: 'sO/Iwj',
- })}
- </ButtonLink>
- </li>
- <li>
- <ButtonLink target="/feed">
- <FeedIcon className={styles['icon--feed']} />
- {intl.formatMessage({
- defaultMessage: 'Subscribe',
- description: 'HomePage: RSS feed subscription text',
- id: 'T4YA64',
- })}
- </ButtonLink>
- </li>
- </ul>
- );
- };
-
- const getRecentPosts = () => {
- return <RecentPosts posts={recentPosts} />;
- };
-
- const components: NestedMDXComponents = {
- CodingLinks: CodingLinks,
- ColdarkRepos: ColdarkRepos,
- Image: (props: ResponsiveImageProps) => ResponsiveImage({ ...props }),
- LibreLinks: LibreLinks,
- MoreLinks: MoreLinks,
- RecentPosts: getRecentPosts,
- ShaarliLink: ShaarliLink,
- };
-
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: '{websiteName} | Front-end developer: WordPress/React',
- description: 'HomePage: SEO - Page title',
- id: 'PXp2hv',
- },
- { websiteName: settings.name }
- );
- const pageDescription = intl.formatMessage(
- {
- defaultMessage:
- '{websiteName} is a front-end developer located in France. He codes and he writes mostly about web development and open-source.',
- description: 'HomePage: SEO - Meta description',
- id: 'tMuNTy',
- },
- { websiteName: settings.name }
- );
-
- const webpageSchema: WebPage = {
- '@id': `${settings.url}/#home`,
- '@type': 'WebPage',
- name: pageTitle,
- description: pageDescription,
- author: { '@id': `${settings.url}/#branding` },
- creator: { '@id': `${settings.url}/#branding` },
- editor: { '@id': `${settings.url}/#branding` },
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${settings.url}`,
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema],
- };
-
- return (
- <>
- <Head>
- <title>{pageTitle}</title>
- <meta name="description" content={pageDescription} />
- <meta property="og:type" content="website" />
- <meta property="og:url" content={`${settings.url}`} />
- <meta property="og:title" content={pageTitle} />
- <meta property="og:description" content={pageDescription} />
- </Head>
- <Script
- id="schema-homepage"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <div id="home">
- <HomePageContent components={components} />
- </div>
- </>
- );
-};
-
-Home.getLayout = function getLayout(page: ReactElement) {
- return <Layout isHome={true}>{page}</Layout>;
-};
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const { locale } = context;
- const translation = await loadTranslation(locale);
- const recentPosts = await getPublishedPosts({ first: 3 });
-
- return {
- props: {
- recentPosts,
- translation,
- },
- };
-};
-
-export default Home;
diff --git a/src/pages/mentions-legales.tsx b/src/pages/mentions-legales.tsx
deleted file mode 100644
index b103b5e..0000000
--- a/src/pages/mentions-legales.tsx
+++ /dev/null
@@ -1,150 +0,0 @@
-import { getLayout } from '@components/Layouts/Layout';
-import { Link } from '@components/MDX';
-import PostHeader from '@components/PostHeader/PostHeader';
-import Sidebar from '@components/Sidebar/Sidebar';
-import { ToC } from '@components/Widgets';
-import LegalNoticeContent, {
- intro,
- meta,
-} from '@content/pages/legal-notice.mdx';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { ArticleMeta } from '@ts/types/articles';
-import { settings } from '@utils/config';
-import { loadTranslation } from '@utils/helpers/i18n';
-import { NestedMDXComponents } from 'mdx/types';
-import { GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { useIntl } from 'react-intl';
-import { Article, Graph, WebPage } from 'schema-dts';
-
-const LegalNotice: NextPageWithLayout = () => {
- const intl = useIntl();
- const router = useRouter();
- const dates = {
- publication: meta.publishedOn,
- update: meta.updatedOn,
- };
-
- const pageMeta: ArticleMeta = {
- dates,
- };
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: 'Legal notice - {websiteName}',
- description: 'LegalNoticePage: SEO - Page title',
- id: '4zAUSu',
- },
- { websiteName: settings.name }
- );
- const pageDescription = intl.formatMessage(
- {
- defaultMessage: "Discover the legal notice of {websiteName}'s website.",
- description: 'LegalNoticePage: SEO - Meta description',
- id: 'uvB+32',
- },
- { websiteName: settings.name }
- );
- const pageUrl = `${settings.url}${router.asPath}`;
- const title = intl.formatMessage({
- defaultMessage: 'Legal notice',
- description: 'LegalNoticePage: page title',
- id: '/IirIt',
- });
- const publicationDate = new Date(dates.publication);
- const updateDate = new Date(dates.update);
-
- const webpageSchema: WebPage = {
- '@id': `${pageUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: pageTitle,
- description: pageDescription,
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${pageUrl}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const articleSchema: Article = {
- '@id': `${settings.url}/#legal-notice`,
- '@type': 'Article',
- name: title,
- description: intro,
- author: { '@id': `${settings.url}/#branding` },
- copyrightYear: publicationDate.getFullYear(),
- creator: { '@id': `${settings.url}/#branding` },
- dateCreated: publicationDate.toISOString(),
- dateModified: updateDate.toISOString(),
- datePublished: publicationDate.toISOString(),
- editor: { '@id': `${settings.url}/#branding` },
- headline: title,
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${pageUrl}` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, articleSchema],
- };
-
- const components: NestedMDXComponents = {
- Link: (props) => Link(props),
- };
-
- return (
- <>
- <Head>
- <title>{pageTitle}</title>
- <meta name="description" content={pageDescription} />
- <meta property="og:url" content={`${pageUrl}`} />
- <meta property="og:type" content="article" />
- <meta property="og:title" content={pageTitle} />
- <meta property="og:description" content={intro} />
- </Head>
- <Script
- id="schema-legal-notice"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="legal-notice"
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader intro={intro} meta={pageMeta} title={meta.title} />
- <Sidebar position="left">
- <ToC />
- </Sidebar>
- <div className={styles.body}>
- <LegalNoticeContent components={components} />
- </div>
- </article>
- </>
- );
-};
-
-LegalNotice.getLayout = getLayout;
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const breadcrumbTitle = meta.title;
- const { locale } = context;
- const translation = await loadTranslation(locale);
-
- return {
- props: {
- breadcrumbTitle,
- locale,
- translation,
- },
- };
-};
-
-export default LegalNotice;
diff --git a/src/pages/projet/[slug].tsx b/src/pages/projet/[slug].tsx
deleted file mode 100644
index 1f09fed..0000000
--- a/src/pages/projet/[slug].tsx
+++ /dev/null
@@ -1,186 +0,0 @@
-import { getLayout } from '@components/Layouts/Layout';
-import { CodeBlock, Gallery, Link, ResponsiveImage } from '@components/MDX';
-import PostHeader from '@components/PostHeader/PostHeader';
-import ProjectSummary from '@components/ProjectSummary/ProjectSummary';
-import Sidebar from '@components/Sidebar/Sidebar';
-import { Sharing, ToC } from '@components/Widgets';
-import styles from '@styles/pages/Page.module.scss';
-import {
- NextPageWithLayout,
- Project as ProjectData,
- ProjectProps,
-} from '@ts/types/app';
-import { settings } from '@utils/config';
-import { loadTranslation } from '@utils/helpers/i18n';
-import {
- getAllProjectsFilename,
- getProjectData,
-} from '@utils/helpers/projects';
-import { MDXComponents, NestedMDXComponents } from 'mdx/types';
-import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { ParsedUrlQuery } from 'querystring';
-import { ComponentType } from 'react';
-import { useIntl } from 'react-intl';
-import { Article, Graph, WebPage } from 'schema-dts';
-
-const Project: NextPageWithLayout<ProjectProps> = ({
- project,
-}: {
- project: ProjectData;
-}) => {
- const intl = useIntl();
- const router = useRouter();
- const projectUrl = `${settings.url}${router.asPath}`;
- const { id, intro, meta, title, seo } = project;
- const dates = {
- publication: meta.publishedOn,
- update: meta.updatedOn,
- };
-
- const components: NestedMDXComponents = {
- CodeBlock: (props) => CodeBlock(props),
- Gallery: (props) => Gallery(props),
- Image: (props) => ResponsiveImage({ caption: props.caption, ...props }),
- Link: (props) => Link(props),
- pre: ({ children }) => CodeBlock(children.props),
- };
-
- const ProjectContent: ComponentType<MDXComponents> =
- require(`../../content/projects/${id}.mdx`).default;
-
- const webpageSchema: WebPage = {
- '@id': `${projectUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: seo.title,
- description: seo.description,
- inLanguage: settings.locales.defaultLocale,
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${settings.url}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const publicationDate = new Date(dates.publication);
- const updateDate = new Date(dates.update);
-
- const articleSchema: Article = {
- '@id': `${settings.url}/project`,
- '@type': 'Article',
- name: title,
- description: intro,
- author: { '@id': `${settings.url}/#branding` },
- copyrightYear: publicationDate.getFullYear(),
- creator: { '@id': `${settings.url}/#branding` },
- dateCreated: publicationDate.toISOString(),
- dateModified: updateDate.toISOString(),
- datePublished: publicationDate.toISOString(),
- editor: { '@id': `${settings.url}/#branding` },
- headline: title,
- thumbnailUrl: meta.hasCover ? `/projects/${id}.jpg` : '',
- image: meta.hasCover ? `/projects/${id}.jpg` : '',
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${projectUrl}` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, articleSchema],
- };
-
- return (
- <>
- <Head>
- <title>{seo.title}</title>
- <meta name="description" content={seo.description} />
- <meta property="og:url" content={`${projectUrl}`} />
- <meta property="og:type" content="article" />
- <meta property="og:title" content={title} />
- <meta property="og:description" content={intro} />
- </Head>
- <Script
- id="schema-project"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="project"
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader title={title} intro={intro} meta={{ dates }} />
- <Sidebar
- position="left"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Table of Contents',
- description: 'ProjectPage: ToC sidebar aria-label',
- id: '6dXfvr',
- })}
- >
- <ToC />
- </Sidebar>
- <div className={styles.body}>
- <ProjectSummary id={id} title={title} meta={meta} />
- <ProjectContent components={components} />
- </div>
- <Sidebar
- position="right"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Sidebar',
- description: 'ProjectPage: right sidebar aria-label',
- id: 'hHrNd0',
- })}
- >
- <Sharing title={title} excerpt={intro} />
- </Sidebar>
- </article>
- </>
- );
-};
-
-Project.getLayout = getLayout;
-
-interface ProjectParams extends ParsedUrlQuery {
- slug: string;
-}
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const { locale } = context;
- const translation = await loadTranslation(locale);
- const { slug } = context.params as ProjectParams;
- const project = await getProjectData(slug);
- const breadcrumbTitle = project.title;
-
- return {
- props: {
- breadcrumbTitle,
- locale,
- project,
- translation,
- },
- };
-};
-
-export const getStaticPaths: GetStaticPaths = async () => {
- const filenames = getAllProjectsFilename();
- const paths = filenames.map((filename) => {
- return {
- params: {
- slug: filename,
- },
- };
- });
-
- return {
- paths,
- fallback: false,
- };
-};
-
-export default Project;
diff --git a/src/pages/projets.tsx b/src/pages/projets.tsx
deleted file mode 100644
index 8a81f39..0000000
--- a/src/pages/projets.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import { getLayout } from '@components/Layouts/Layout';
-import PostHeader from '@components/PostHeader/PostHeader';
-import ProjectsList from '@components/ProjectsList/ProjectsList';
-import PageContent, { meta } from '@content/pages/projects.mdx';
-import styles from '@styles/pages/Projects.module.scss';
-import { Project } from '@ts/types/app';
-import { settings } from '@utils/config';
-import { loadTranslation } from '@utils/helpers/i18n';
-import { getSortedProjects } from '@utils/helpers/projects';
-import { GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { useIntl } from 'react-intl';
-import { Article, Graph, WebPage } from 'schema-dts';
-
-const Projects = ({ projects }: { projects: Project[] }) => {
- const intl = useIntl();
- const dates = {
- publication: meta.publishedOn,
- update: meta.updatedOn,
- };
- const publicationDate = new Date(dates.publication);
- const updateDate = new Date(dates.update);
- const router = useRouter();
- const pageUrl = `${settings.url}${router.asPath}`;
- const pageTitle = intl.formatMessage(
- {
- defaultMessage: 'Projects: open-source makings - {websiteName}',
- description: 'ProjectsPage: SEO - Page title',
- id: 'SX1z3t',
- },
- { websiteName: settings.name }
- );
- const pageDescription = intl.formatMessage(
- {
- defaultMessage:
- 'Discover {websiteName} projects. Mostly related to web development and open source.',
- description: 'ProjectsPage: SEO - Meta description',
- id: 's6U1Xt',
- },
- { websiteName: settings.name }
- );
-
- const webpageSchema: WebPage = {
- '@id': `${pageUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: pageTitle,
- description: pageDescription,
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${pageUrl}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const articleSchema: Article = {
- '@id': `${settings.url}/#projects`,
- '@type': 'Article',
- name: meta.title,
- description: pageDescription,
- author: { '@id': `${settings.url}/#branding` },
- copyrightYear: publicationDate.getFullYear(),
- creator: { '@id': `${settings.url}/#branding` },
- dateCreated: publicationDate.toISOString(),
- dateModified: updateDate.toISOString(),
- datePublished: publicationDate.toISOString(),
- editor: { '@id': `${settings.url}/#branding` },
- headline: meta.title,
- inLanguage: settings.locales.defaultLocale,
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${pageUrl}` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, articleSchema],
- };
-
- return (
- <>
- <Head>
- <title>{pageTitle}</title>
- <meta name="description" content={pageDescription} />
- <meta property="og:url" content={`${pageUrl}`} />
- <meta property="og:type" content="article" />
- <meta property="og:title" content={meta.title} />
- <meta property="og:description" content={pageDescription} />
- </Head>
- <Script
- id="schema-projects"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article id="projects" className={styles.article}>
- <PostHeader title={meta.title} intro={<PageContent />} />
- <div className={styles.body}>
- {projects.length > 0 && <ProjectsList projects={projects} />}
- </div>
- </article>
- </>
- );
-};
-
-Projects.getLayout = getLayout;
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const breadcrumbTitle = meta.title;
- const { locale } = context;
- const projects: Project[] = await getSortedProjects();
- const translation = await loadTranslation(locale);
-
- return {
- props: {
- breadcrumbTitle,
- locale,
- projects,
- translation,
- },
- };
-};
-
-export default Projects;
diff --git a/src/pages/recherche/index.tsx b/src/pages/recherche/index.tsx
deleted file mode 100644
index b843f8d..0000000
--- a/src/pages/recherche/index.tsx
+++ /dev/null
@@ -1,213 +0,0 @@
-import { Button } from '@components/Buttons';
-import { getLayout } from '@components/Layouts/Layout';
-import PaginationCursor from '@components/PaginationCursor/PaginationCursor';
-import PostHeader from '@components/PostHeader/PostHeader';
-import PostsList from '@components/PostsList/PostsList';
-import Sidebar from '@components/Sidebar/Sidebar';
-import Spinner from '@components/Spinner/Spinner';
-import { ThematicsList, TopicsList } from '@components/Widgets';
-import { getPublishedPosts } from '@services/graphql/queries';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { PostsList as PostsListData } from '@ts/types/blog';
-import { settings } from '@utils/config';
-import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n';
-import { GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import { useEffect, useRef, useState } from 'react';
-import { useIntl } from 'react-intl';
-import useSWRInfinite from 'swr/infinite';
-
-const Search: NextPageWithLayout = () => {
- const intl = useIntl();
- const [query, setQuery] = useState('');
- const router = useRouter();
- const lastPostRef = useRef<HTMLSpanElement>(null);
-
- useEffect(() => {
- if (!router.isReady) return;
-
- if (router.query?.s && typeof router.query.s === 'string') {
- setQuery(router.query.s);
- }
- }, [router.isReady, router.query.s]);
-
- const getKey = (pageIndex: number, previousData: PostsListData) => {
- if (previousData && !previousData.posts) return null;
-
- return pageIndex === 0
- ? { first: settings.postsPerPage, searchQuery: query }
- : {
- first: settings.postsPerPage,
- after: previousData.pageInfo.endCursor,
- searchQuery: query,
- };
- };
-
- const { data, error, size, setSize } = useSWRInfinite(
- getKey,
- getPublishedPosts
- );
- const [totalPostsCount, setTotalPostsCount] = useState<number>(0);
-
- useEffect(() => {
- if (data) setTotalPostsCount(data[0].pageInfo.total);
- }, [data]);
-
- const [loadedPostsCount, setLoadedPostsCount] = useState<number>(
- settings.postsPerPage
- );
-
- useEffect(() => {
- if (data && data.length > 0) {
- const newCount =
- settings.postsPerPage +
- data[0].pageInfo.total -
- data[data.length - 1].pageInfo.total;
- setLoadedPostsCount(newCount);
- }
- }, [data]);
-
- const isLoadingInitialData = !data && !error;
- const isLoadingMore: boolean =
- isLoadingInitialData ||
- (size > 0 && data !== undefined && typeof data[size - 1] === 'undefined');
-
- const hasNextPage = data && data[data.length - 1].pageInfo.hasNextPage;
-
- const title = query
- ? intl.formatMessage(
- {
- defaultMessage: 'Search results for {query}',
- description: 'SearchPage: search results text',
- id: 'VSGuGE',
- },
- { query }
- )
- : intl.formatMessage({
- defaultMessage: 'Search',
- description: 'SearchPage: page title',
- id: 'U+35YD',
- });
-
- const description = query
- ? intl.formatMessage(
- {
- defaultMessage: 'Discover search results for {query}',
- description: 'SearchPage: meta description with query',
- id: 'A4LTGq',
- },
- { query }
- )
- : intl.formatMessage(
- {
- defaultMessage: 'Search for a post on {websiteName}',
- description: 'SearchPage: meta description without query',
- id: 'PrIz5o',
- },
- { websiteName: settings.name }
- );
-
- const head = {
- title: `${title} | ${settings.name}`,
- description,
- };
-
- const loadMorePosts = () => {
- if (lastPostRef.current) {
- lastPostRef.current.focus();
- }
- setSize(size + 1);
- };
-
- const getPostsList = () => {
- if (error)
- return intl.formatMessage({
- defaultMessage: 'Failed to load.',
- description: 'SearchPage: failed to load text',
- id: 'fOe8rH',
- });
- if (!data) return <Spinner />;
-
- return <PostsList ref={lastPostRef} data={data} showYears={false} />;
- };
-
- return (
- <>
- <Head>
- <title>{head.title}</title>
- <meta name="description" content={head.description} />
- </Head>
- <article
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader title={title} meta={{ results: totalPostsCount }} />
- <div className={styles.body}>
- {getPostsList()}
- {hasNextPage && (
- <>
- <PaginationCursor
- current={loadedPostsCount}
- total={totalPostsCount}
- />
- <Button
- isDisabled={isLoadingMore}
- clickHandler={loadMorePosts}
- position="center"
- spacing={true}
- >
- {intl.formatMessage({
- defaultMessage: 'Load more?',
- description: 'SearchPage: load more text',
- id: 'pEtJik',
- })}
- </Button>
- </>
- )}
- </div>
- <Sidebar position="right">
- <ThematicsList
- title={intl.formatMessage({
- defaultMessage: 'Thematics',
- description: 'SearchPage: thematics list widget title',
- id: 'Dq6+WH',
- })}
- />
- <TopicsList
- title={intl.formatMessage({
- defaultMessage: 'Topics',
- description: 'SearchPage: topics list widget title',
- id: 'N804XO',
- })}
- />
- </Sidebar>
- </article>
- </>
- );
-};
-
-Search.getLayout = getLayout;
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const intl = await getIntlInstance();
- const breadcrumbTitle = intl.formatMessage({
- defaultMessage: 'Search',
- description: 'SearchPage: breadcrumb item',
- id: 'TfU6Qm',
- });
- const { locale } = context;
- const translation = await loadTranslation(locale);
-
- return {
- props: {
- breadcrumbTitle,
- locale,
- translation,
- },
- };
-};
-
-export default Search;
diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx
deleted file mode 100644
index 30dd36c..0000000
--- a/src/pages/sujet/[slug].tsx
+++ /dev/null
@@ -1,224 +0,0 @@
-import { getLayout } from '@components/Layouts/Layout';
-import PostHeader from '@components/PostHeader/PostHeader';
-import PostPreview from '@components/PostPreview/PostPreview';
-import Sidebar from '@components/Sidebar/Sidebar';
-import Spinner from '@components/Spinner/Spinner';
-import { RelatedThematics, ToC, TopicsList } from '@components/Widgets';
-import {
- getAllTopics,
- getAllTopicsSlug,
- getTopicBySlug,
-} from '@services/graphql/queries';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { ArticleMeta } from '@ts/types/articles';
-import { TopicProps, ThematicPreview } from '@ts/types/taxonomies';
-import { settings } from '@utils/config';
-import { getFormattedPaths } from '@utils/helpers/format';
-import { loadTranslation } from '@utils/helpers/i18n';
-import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { ParsedUrlQuery } from 'querystring';
-import { useRef } from 'react';
-import { useIntl } from 'react-intl';
-import { Article as Article, Graph, WebPage } from 'schema-dts';
-
-const Topic: NextPageWithLayout<TopicProps> = ({ topic, allTopics }) => {
- const intl = useIntl();
- const relatedThematics = useRef<ThematicPreview[]>([]);
- const router = useRouter();
-
- if (router.isFallback) return <Spinner />;
-
- const updateRelatedThematics = (newThematics: ThematicPreview[]) => {
- newThematics.forEach((thematic) => {
- const thematicIndex = relatedThematics.current.findIndex(
- (relatedThematic) => relatedThematic.id === thematic.id
- );
- const hasThematic = thematicIndex === -1 ? false : true;
-
- if (!hasThematic) relatedThematics.current.push(thematic);
- });
- };
-
- const getPostsList = () => {
- return [...topic.posts].reverse().map((post) => {
- updateRelatedThematics(post.thematics);
-
- return (
- <li key={post.id} className={styles.item}>
- <PostPreview post={post} titleLevel={3} />
- </li>
- );
- });
- };
-
- const meta: ArticleMeta = {
- dates: topic.dates,
- results: topic.posts.length,
- website: topic.officialWebsite,
- };
- const topicUrl = `${settings.url}${router.asPath}`;
-
- const webpageSchema: WebPage = {
- '@id': `${topicUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: topic.seo.title,
- description: topic.seo.metaDesc,
- inLanguage: settings.locales.defaultLocale,
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${settings.url}`,
- isPartOf: {
- '@id': `${settings.url}`,
- },
- };
-
- const publicationDate = new Date(topic.dates.publication);
- const updateDate = new Date(topic.dates.update);
-
- const articleSchema: Article = {
- '@id': `${settings.url}/#topic`,
- '@type': 'Article',
- name: topic.title,
- description: topic.intro,
- author: { '@id': `${settings.url}/#branding` },
- copyrightYear: publicationDate.getFullYear(),
- creator: { '@id': `${settings.url}/#branding` },
- dateCreated: publicationDate.toISOString(),
- dateModified: updateDate.toISOString(),
- datePublished: publicationDate.toISOString(),
- editor: { '@id': `${settings.url}/#branding` },
- headline: topic.title,
- thumbnailUrl: topic.featuredImage?.sourceUrl,
- image: topic.featuredImage?.sourceUrl,
- inLanguage: settings.locales.defaultLocale,
- isPartOf: { '@id': `${settings.url}/blog` },
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${topicUrl}` },
- subjectOf: { '@id': `${settings.url}/blog` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, articleSchema],
- };
-
- return (
- <>
- <Head>
- <title>{topic.seo.title}</title>
- <meta name="description" content={topic.seo.metaDesc} />
- <meta property="og:url" content={`${topicUrl}`} />
- <meta property="og:type" content="article" />
- <meta property="og:title" content={topic.title} />
- <meta property="og:description" content={topic.intro} />
- <meta property="og:image" content={topic.featuredImage?.sourceUrl} />
- <meta property="og:image:alt" content={topic.featuredImage?.altText} />
- </Head>
- <Script
- id="schema-subject"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="topic"
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader
- cover={topic.featuredImage}
- intro={topic.intro}
- meta={meta}
- title={topic.title}
- />
- <Sidebar
- position="left"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Table of Contents',
- description: 'TopicPage: ToC sidebar aria-label',
- id: 'lsDB5G',
- })}
- >
- <ToC />
- </Sidebar>
- <div className={styles.body}>
- <div dangerouslySetInnerHTML={{ __html: topic.content }}></div>
- {topic.posts.length > 0 && (
- <section className={styles.section}>
- <h2>
- {intl.formatMessage(
- {
- defaultMessage: 'All posts in {name}',
- description: 'TopicPage: posts list title',
- id: 'FLkF2R',
- },
- { name: topic.title }
- )}
- </h2>
- <ol className={styles.list}>{getPostsList()}</ol>
- </section>
- )}
- </div>
- <Sidebar
- position="right"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Sidebar',
- description: 'TopicPage: right sidebar aria-label',
- id: 'eu3beS',
- })}
- >
- <RelatedThematics thematics={relatedThematics.current} />
- <TopicsList
- initialData={allTopics}
- title={intl.formatMessage({
- defaultMessage: 'Others topics',
- description: 'TopicPage: topics list widget title',
- id: '+4tiVb',
- })}
- />
- </Sidebar>
- </article>
- </>
- );
-};
-
-Topic.getLayout = getLayout;
-
-interface PostParams extends ParsedUrlQuery {
- slug: string;
-}
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const { locale } = context;
- const translation = await loadTranslation(locale);
- const { slug } = context.params as PostParams;
- const topic = await getTopicBySlug(slug);
- const allTopics = await getAllTopics();
- const breadcrumbTitle = topic.title;
-
- return {
- props: {
- allTopics,
- breadcrumbTitle,
- locale,
- topic,
- translation,
- },
- };
-};
-
-export const getStaticPaths: GetStaticPaths = async () => {
- const allTopics = await getAllTopicsSlug();
- const paths = getFormattedPaths(allTopics);
-
- return {
- paths,
- fallback: true,
- };
-};
-
-export default Topic;
diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx
deleted file mode 100644
index db22214..0000000
--- a/src/pages/thematique/[slug].tsx
+++ /dev/null
@@ -1,214 +0,0 @@
-import { getLayout } from '@components/Layouts/Layout';
-import PostHeader from '@components/PostHeader/PostHeader';
-import PostPreview from '@components/PostPreview/PostPreview';
-import Sidebar from '@components/Sidebar/Sidebar';
-import Spinner from '@components/Spinner/Spinner';
-import { RelatedTopics, ThematicsList, ToC } from '@components/Widgets';
-import {
- getAllThematics,
- getAllThematicsSlug,
- getThematicBySlug,
-} from '@services/graphql/queries';
-import styles from '@styles/pages/Page.module.scss';
-import { NextPageWithLayout } from '@ts/types/app';
-import { ArticleMeta } from '@ts/types/articles';
-import { TopicPreview, ThematicProps } from '@ts/types/taxonomies';
-import { settings } from '@utils/config';
-import { getFormattedPaths } from '@utils/helpers/format';
-import { loadTranslation } from '@utils/helpers/i18n';
-import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next';
-import Head from 'next/head';
-import { useRouter } from 'next/router';
-import Script from 'next/script';
-import { ParsedUrlQuery } from 'querystring';
-import { useRef } from 'react';
-import { useIntl } from 'react-intl';
-import { Article, Graph, WebPage } from 'schema-dts';
-
-const Thematic: NextPageWithLayout<ThematicProps> = ({
- thematic,
- allThematics,
-}) => {
- const intl = useIntl();
- const relatedTopics = useRef<TopicPreview[]>([]);
- const router = useRouter();
-
- if (router.isFallback) return <Spinner />;
-
- const updateRelatedTopics = (newTopics: TopicPreview[]) => {
- newTopics.forEach((topic) => {
- const topicIndex = relatedTopics.current.findIndex(
- (relatedTopic) => relatedTopic.id === topic.id
- );
- const hasTopic = topicIndex === -1 ? false : true;
-
- if (!hasTopic) relatedTopics.current.push(topic);
- });
- };
-
- const getPostsList = () => {
- return [...thematic.posts].reverse().map((post) => {
- updateRelatedTopics(post.topics);
-
- return (
- <li key={post.id} className={styles.item}>
- <PostPreview post={post} titleLevel={3} />
- </li>
- );
- });
- };
-
- const meta: ArticleMeta = {
- dates: thematic.dates,
- results: thematic.posts.length,
- };
- const thematicUrl = `${settings.url}${router.asPath}`;
-
- const webpageSchema: WebPage = {
- '@id': `${thematicUrl}`,
- '@type': 'WebPage',
- breadcrumb: { '@id': `${settings.url}/#breadcrumb` },
- name: thematic.seo.title,
- description: thematic.seo.metaDesc,
- inLanguage: settings.locales.defaultLocale,
- reviewedBy: { '@id': `${settings.url}/#branding` },
- url: `${settings.url}`,
- };
-
- const publicationDate = new Date(thematic.dates.publication);
- const updateDate = new Date(thematic.dates.update);
-
- const articleSchema: Article = {
- '@id': `${settings.url}/#thematic`,
- '@type': 'Article',
- name: thematic.title,
- description: thematic.intro,
- author: { '@id': `${settings.url}/#branding` },
- copyrightYear: publicationDate.getFullYear(),
- creator: { '@id': `${settings.url}/#branding` },
- dateCreated: publicationDate.toISOString(),
- dateModified: updateDate.toISOString(),
- datePublished: publicationDate.toISOString(),
- editor: { '@id': `${settings.url}/#branding` },
- headline: thematic.title,
- inLanguage: settings.locales.defaultLocale,
- isPartOf: { '@id': `${settings.url}/blog` },
- license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr',
- mainEntityOfPage: { '@id': `${thematicUrl}` },
- subjectOf: { '@id': `${settings.url}/blog` },
- };
-
- const schemaJsonLd: Graph = {
- '@context': 'https://schema.org',
- '@graph': [webpageSchema, articleSchema],
- };
-
- return (
- <>
- <Head>
- <title>{thematic.seo.title}</title>
- <meta name="description" content={thematic.seo.metaDesc} />
- <meta property="og:url" content={`${thematic}`} />
- <meta property="og:type" content="article" />
- <meta property="og:title" content={thematic.title} />
- <meta property="og:description" content={thematic.intro} />
- </Head>
- <Script
- id="schema-thematic"
- type="application/ld+json"
- dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
- />
- <article
- id="thematic"
- className={`${styles.article} ${styles['article--no-comments']}`}
- >
- <PostHeader intro={thematic.intro} meta={meta} title={thematic.title} />
- <Sidebar
- position="left"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Table of Contents',
- description: 'ThematicPage: ToC sidebar aria-label',
- id: 'YwvYfw',
- })}
- >
- <ToC />
- </Sidebar>
- <div className={styles.body}>
- <div dangerouslySetInnerHTML={{ __html: thematic.content }}></div>
- {thematic.posts.length > 0 && (
- <section className={styles.section}>
- <h2>
- {intl.formatMessage(
- {
- defaultMessage: 'All posts in {name}',
- description: 'ThematicPage: posts list title',
- id: 'P7fxX2',
- },
- { name: thematic.title }
- )}
- </h2>
- <ol className={styles.list}>{getPostsList()}</ol>
- </section>
- )}
- </div>
- <Sidebar
- position="right"
- ariaLabel={intl.formatMessage({
- defaultMessage: 'Sidebar',
- description: 'ThematicPage: right sidebar aria-label',
- id: 'syLgY9',
- })}
- >
- <RelatedTopics topics={relatedTopics.current} />
- <ThematicsList
- initialData={allThematics}
- title={intl.formatMessage({
- defaultMessage: 'Others thematics',
- description: 'ThematicPage: thematics list widget title',
- id: 'norrGp',
- })}
- />
- </Sidebar>
- </article>
- </>
- );
-};
-
-Thematic.getLayout = getLayout;
-
-interface PostParams extends ParsedUrlQuery {
- slug: string;
-}
-
-export const getStaticProps: GetStaticProps = async (
- context: GetStaticPropsContext
-) => {
- const { locale } = context;
- const translation = await loadTranslation(locale);
- const { slug } = context.params as PostParams;
- const thematic = await getThematicBySlug(slug);
- const allThematics = await getAllThematics();
- const breadcrumbTitle = thematic.title;
-
- return {
- props: {
- allThematics,
- breadcrumbTitle,
- locale,
- thematic,
- translation,
- },
- };
-};
-
-export const getStaticPaths: GetStaticPaths = async () => {
- const allSlugs = await getAllThematicsSlug();
- const paths = getFormattedPaths(allSlugs);
-
- return {
- paths,
- fallback: true,
- };
-};
-
-export default Thematic;
diff --git a/src/services/graphql/api.ts b/src/services/graphql/api.ts
deleted file mode 100644
index a5be026..0000000
--- a/src/services/graphql/api.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { RequestType, VariablesType } from '@ts/types/app';
-import { settings } from '@utils/config';
-import { GraphQLClient } from 'graphql-request';
-
-export const getGraphQLClient = (): GraphQLClient => {
- const apiUrl = settings.api.url;
-
- if (!apiUrl) throw new Error('API URL not defined.');
-
- return new GraphQLClient(apiUrl);
-};
-
-export const fetchApi = async <T extends RequestType>(
- query: string,
- variables: VariablesType<T>
-): Promise<T> => {
- const client = getGraphQLClient();
-
- try {
- return await client.request(query, variables);
- } catch (error) {
- console.error(error, undefined, 2);
- process.exit(1);
- }
-};
diff --git a/src/services/graphql/mutations.ts b/src/services/graphql/mutations.ts
deleted file mode 100644
index c697835..0000000
--- a/src/services/graphql/mutations.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { CommentData, CreateComment, CreatedComment } from '@ts/types/comments';
-import { ContactData, SendEmail } from '@ts/types/contact';
-import { gql } from 'graphql-request';
-import { fetchApi } from './api';
-
-//==============================================================================
-// Comment mutation
-//==============================================================================
-
-export const createComment = async (
- data: CommentData
-): Promise<CreatedComment> => {
- const mutation = gql`
- mutation CreateComment(
- $author: String!
- $authorEmail: String!
- $authorUrl: String!
- $content: String!
- $parent: ID!
- $commentOn: Int!
- $mutationId: String!
- ) {
- createComment(
- input: {
- author: $author
- authorEmail: $authorEmail
- authorUrl: $authorUrl
- content: $content
- parent: $parent
- commentOn: $commentOn
- clientMutationId: $mutationId
- }
- ) {
- clientMutationId
- success
- comment {
- approved
- }
- }
- }
- `;
-
- const variables = { ...data };
- const response = await fetchApi<CreateComment>(mutation, variables);
-
- return response.createComment;
-};
-
-//==============================================================================
-// Contact mutation
-//==============================================================================
-
-export const sendMail = async (data: ContactData) => {
- const mutation = gql`
- mutation SendEmail(
- $subject: String!
- $body: String!
- $replyTo: String!
- $mutationId: String!
- ) {
- sendEmail(
- input: {
- clientMutationId: $mutationId
- body: $body
- replyTo: $replyTo
- subject: $subject
- }
- ) {
- clientMutationId
- message
- sent
- origin
- replyTo
- to
- }
- }
- `;
-
- const variables = { ...data };
- const response = await fetchApi<SendEmail>(mutation, variables);
- return response.sendEmail;
-};
diff --git a/src/services/graphql/queries.ts b/src/services/graphql/queries.ts
deleted file mode 100644
index 9caf62b..0000000
--- a/src/services/graphql/queries.ts
+++ /dev/null
@@ -1,535 +0,0 @@
-import { Slug } from '@ts/types/app';
-import { Article, PostBy, TotalArticles } from '@ts/types/articles';
-import {
- AllPostsSlug,
- LastPostCursor,
- PostsList,
- RawPostsList,
-} from '@ts/types/blog';
-import { Comment, CommentsByPostId } from '@ts/types/comments';
-import {
- AllTopics,
- AllTopicsSlug,
- AllThematics,
- AllThematicsSlug,
- Topic,
- TopicBy,
- TopicPreview,
- Thematic,
- ThematicBy,
- ThematicPreview,
-} from '@ts/types/taxonomies';
-import {
- getFormattedPost,
- getFormattedPostPreview,
- getFormattedTopic,
- getFormattedThematic,
- getFormattedComments,
- buildCommentsTree,
-} from '@utils/helpers/format';
-import { gql } from 'graphql-request';
-import { fetchApi } from './api';
-
-//==============================================================================
-// Posts list queries
-//==============================================================================
-
-export const getPostsTotal = async (): Promise<number> => {
- const query = gql`
- query PostsTotal {
- posts {
- pageInfo {
- total
- }
- }
- }
- `;
-
- const response = await fetchApi<TotalArticles>(query, null);
- return response.posts.pageInfo.total;
-};
-
-export const getPublishedPosts = async ({
- first = 10,
- after = '',
- searchQuery = '',
-}: {
- first: number;
- after?: string;
- searchQuery?: string;
-}): Promise<PostsList> => {
- const query = gql`
- query AllPublishedPosts($first: Int, $after: String, $searchQuery: String) {
- posts(
- after: $after
- first: $first
- where: {
- status: PUBLISH
- orderby: { field: DATE, order: DESC }
- search: $searchQuery
- }
- ) {
- edges {
- cursor
- node {
- acfPosts {
- postsInTopic {
- ... on Topic {
- databaseId
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- id
- slug
- title
- }
- }
- postsInThematic {
- ... on Thematic {
- databaseId
- id
- slug
- title
- }
- }
- }
- commentCount
- contentParts {
- beforeMore
- }
- date
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- id
- info {
- readingTime
- wordsCount
- }
- databaseId
- modified
- slug
- title
- }
- }
- pageInfo {
- endCursor
- hasNextPage
- total
- }
- }
- }
- `;
-
- const variables = { first, after, searchQuery };
- const response = await fetchApi<RawPostsList>(query, variables);
- const formattedPosts = response.posts.edges.map((post) => {
- return getFormattedPostPreview(post.node);
- });
-
- return {
- posts: formattedPosts,
- pageInfo: response.posts.pageInfo,
- };
-};
-
-export const getAllPostsSlug = async (): Promise<Slug[]> => {
- // 10 000 is an arbitrary number that I use for small websites.
- const query = gql`
- query AllPostsSlug {
- posts(first: 10000) {
- nodes {
- slug
- }
- }
- }
- `;
-
- const response = await fetchApi<AllPostsSlug>(query, null);
- return response.posts.nodes;
-};
-
-//==============================================================================
-// Single Post query
-//==============================================================================
-
-export const getPostBySlug = async (slug: string): Promise<Article> => {
- const query = gql`
- query PostBySlug($slug: ID!) {
- post(id: $slug, idType: SLUG) {
- acfPosts {
- postsInTopic {
- ... on Topic {
- id
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- slug
- title
- }
- }
- postsInThematic {
- ... on Thematic {
- id
- slug
- title
- }
- }
- }
- author {
- node {
- firstName
- lastName
- name
- }
- }
- commentCount
- contentParts {
- afterMore
- beforeMore
- }
- databaseId
- date
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- id
- info {
- readingTime
- wordsCount
- }
- modified
- seo {
- metaDesc
- title
- }
- title
- }
- }
- `;
- const variables = { slug };
- const response = await fetchApi<PostBy>(query, variables);
-
- return getFormattedPost(response.post);
-};
-
-//==============================================================================
-// Comments query
-//==============================================================================
-
-export const getCommentsByPostId = async (id: number): Promise<Comment[]> => {
- const query = gql`
- query PostComments($id: ID!) {
- comments(where: { contentId: $id, order: ASC, orderby: COMMENT_DATE }) {
- nodes {
- approved
- author {
- node {
- databaseId
- gravatarUrl
- name
- url
- }
- }
- content
- databaseId
- date
- parentDatabaseId
- }
- }
- }
- `;
-
- const variables = { id };
- const response = await fetchApi<CommentsByPostId>(query, variables);
- const formattedComments = getFormattedComments(response.comments.nodes);
-
- return buildCommentsTree(formattedComments);
-};
-
-//==============================================================================
-// Topic query
-//==============================================================================
-
-export const getTopicBySlug = async (slug: string): Promise<Topic> => {
- const query = gql`
- query TopicBySlug($slug: ID!) {
- topic(id: $slug, idType: SLUG) {
- acfTopics {
- officialWebsite
- postsInTopic {
- ... on Post {
- acfPosts {
- postsInTopic {
- ... on Topic {
- databaseId
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- id
- slug
- title
- }
- }
- postsInThematic {
- ... on Thematic {
- databaseId
- id
- slug
- title
- }
- }
- }
- id
- info {
- readingTime
- wordsCount
- }
- commentCount
- contentParts {
- beforeMore
- }
- databaseId
- date
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- modified
- slug
- title
- }
- }
- }
- contentParts {
- afterMore
- beforeMore
- }
- databaseId
- date
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- id
- info {
- readingTime
- wordsCount
- }
- modified
- seo {
- metaDesc
- title
- }
- title
- }
- }
- `;
- const variables = { slug };
- const response = await fetchApi<TopicBy>(query, variables);
-
- return getFormattedTopic(response.topic);
-};
-
-export const getAllTopicsSlug = async (): Promise<Slug[]> => {
- // 10 000 is an arbitrary number that I use for small websites.
- const query = gql`
- query AllTopicsSlug {
- topics(first: 10000) {
- nodes {
- slug
- }
- }
- }
- `;
- const response = await fetchApi<AllTopicsSlug>(query, null);
- return response.topics.nodes;
-};
-
-export const getAllTopics = async (): Promise<TopicPreview[]> => {
- // 10 000 is an arbitrary number that I use for small websites.
- const query = gql`
- query AllTopics {
- topics(first: 10000, where: { orderby: { field: TITLE, order: ASC } }) {
- nodes {
- databaseId
- slug
- title
- }
- }
- }
- `;
-
- const response = await fetchApi<AllTopics>(query, null);
- return response.topics.nodes;
-};
-
-//==============================================================================
-// Thematic query
-//==============================================================================
-
-export const getThematicBySlug = async (slug: string): Promise<Thematic> => {
- const query = gql`
- query ThematicBySlug($slug: ID!) {
- thematic(id: $slug, idType: SLUG) {
- acfThematics {
- postsInThematic {
- ... on Post {
- acfPosts {
- postsInTopic {
- ... on Topic {
- databaseId
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- id
- slug
- title
- }
- }
- postsInThematic {
- ... on Thematic {
- databaseId
- id
- slug
- title
- }
- }
- }
- id
- info {
- readingTime
- wordsCount
- }
- commentCount
- contentParts {
- beforeMore
- }
- databaseId
- date
- featuredImage {
- node {
- altText
- sourceUrl
- title
- }
- }
- modified
- slug
- title
- }
- }
- }
- contentParts {
- afterMore
- beforeMore
- }
- databaseId
- date
- id
- info {
- readingTime
- wordsCount
- }
- modified
- seo {
- metaDesc
- title
- }
- title
- }
- }
- `;
- const variables = { slug };
- const response = await fetchApi<ThematicBy>(query, variables);
-
- return getFormattedThematic(response.thematic);
-};
-
-export const getAllThematicsSlug = async (): Promise<Slug[]> => {
- // 10 000 is an arbitrary number that I use for small websites.
- const query = gql`
- query AllThematicsSlug {
- thematics(first: 10000) {
- nodes {
- slug
- }
- }
- }
- `;
- const response = await fetchApi<AllThematicsSlug>(query, null);
- return response.thematics.nodes;
-};
-
-export const getAllThematics = async (): Promise<ThematicPreview[]> => {
- // 10 000 is an arbitrary number that I use for small websites.
- const query = gql`
- query AllThematics {
- thematics(
- first: 10000
- where: { orderby: { field: TITLE, order: ASC } }
- ) {
- nodes {
- databaseId
- slug
- title
- }
- }
- }
- `;
-
- const response = await fetchApi<AllThematics>(query, null);
- return response.thematics.nodes;
-};
-
-export const getEndCursor = async ({
- first = 10,
-}: {
- first: number;
-}): Promise<string> => {
- const query = gql`
- query EndCursorAfter($first: Int) {
- posts(first: $first) {
- pageInfo {
- hasNextPage
- endCursor
- }
- }
- }
- `;
-
- const variables = { first };
- const response = await fetchApi<LastPostCursor>(query, variables);
-
- return response.posts.pageInfo.endCursor;
-};
diff --git a/src/ts/types/app.ts b/src/ts/types/app.ts
deleted file mode 100644
index 4243762..0000000
--- a/src/ts/types/app.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-import { NextPage } from 'next';
-import { AppProps } from 'next/app';
-import { ImageProps } from 'next/image';
-import { ReactElement, ReactNode } from 'react';
-import { PostBy, TotalArticles } from './articles';
-import { AllPostsSlug, LastPostCursor, RawPostsList } from './blog';
-import { CommentData, CommentsByPostId, CreateComment } from './comments';
-import { ContactData, SendEmail } from './contact';
-import {
- AllTopics,
- AllTopicsSlug,
- AllThematics,
- AllThematicsSlug,
- TopicBy,
- ThematicBy,
-} from './taxonomies';
-
-//==============================================================================
-// Next
-//==============================================================================
-
-export type NextPageWithLayout<P = {}> = NextPage<P> & {
- getLayout?: (page: ReactElement) => ReactNode;
-};
-
-export type AppPropsWithLayout = AppProps & {
- Component: NextPageWithLayout;
-};
-
-//==============================================================================
-// API
-//==============================================================================
-
-export type VariablesType<T> = T extends PostBy | TopicBy | ThematicBy
- ? Slug
- : T extends RawPostsList
- ? CursorPagination
- : T extends CommentsByPostId
- ? { id: number }
- : T extends CreateComment
- ? CommentData
- : T extends LastPostCursor
- ? { first: number }
- : T extends SendEmail
- ? ContactData
- : null;
-
-export type RequestType =
- | AllPostsSlug
- | AllTopics
- | AllTopicsSlug
- | AllThematics
- | AllThematicsSlug
- | CommentsByPostId
- | CreateComment
- | LastPostCursor
- | PostBy
- | RawPostsList
- | SendEmail
- | ThematicBy
- | TopicBy
- | TotalArticles;
-
-//==============================================================================
-// Globals
-//==============================================================================
-
-export type ButtonKind = 'primary' | 'secondary' | 'tertiary';
-
-export type ButtonPosition = 'left' | 'right' | 'center';
-
-export type ContentInfo = {
- readingTime: number;
- wordsCount: number;
-};
-
-export type ContentParts = {
- afterMore: string;
- beforeMore: string;
-};
-
-export type CursorPagination = {
- first: number;
- after: string;
-};
-
-export type Dates = {
- publication: string;
- update: string;
-};
-
-export type Heading = {
- depth: number;
- id: string;
- children: Heading[];
- title: string;
-};
-
-export type Meta = {
- title: string;
- publishedOn: string;
- updatedOn: string;
-};
-
-export type MetaKind = 'article' | 'list';
-
-export type NoticeType = 'error' | 'info' | 'success' | 'warning';
-
-export type PageInfo = {
- endCursor: string;
- hasNextPage: boolean;
- total: number;
-};
-
-export type ParamsIds = {
- params: { id: string };
-};
-
-export type ParamsSlug = {
- params: { slug: string };
-};
-
-export type Project = {
- cover?: string;
- id: string;
- intro: string;
- meta: ProjectMeta;
- slug: string;
- tagline?: string;
- title: string;
- seo: {
- title: string;
- description: string;
- };
-};
-
-export type ProjectMeta = Omit<Meta, 'title'> & {
- hasCover: boolean;
- license: string;
- repos?: {
- github?: string;
- gitlab?: string;
- };
- technologies?: string[];
-};
-
-export type ProjectProps = {
- project: Project;
-};
-
-export type ResponsiveImageProps = ImageProps & {
- caption?: string;
- linkTarget?: string;
-};
-
-export type Slug = {
- slug: string;
-};
-
-export type TitleLevel = 2 | 3 | 4 | 5 | 6;
diff --git a/src/ts/types/articles.ts b/src/ts/types/articles.ts
deleted file mode 100644
index 64d2860..0000000
--- a/src/ts/types/articles.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { ContentInfo, ContentParts, Dates } from './app';
-import { Comment } from './comments';
-import { Cover, RawCover } from './cover';
-import { SEO } from './seo';
-import { RawTopicPreview, TopicPreview, ThematicPreview } from './taxonomies';
-
-export type ArticleAuthor = {
- firstName: string;
- lastName: string;
- name: string;
-};
-
-export type RawACFPosts = {
- postsInTopic: RawTopicPreview[] | null;
- postsInThematic: ThematicPreview[] | null;
-};
-
-export type ACFPosts = {
- postsInTopic: TopicPreview[] | null;
- postsInThematic: ThematicPreview[] | null;
-};
-
-export type ArticleMeta = {
- author?: ArticleAuthor;
- commentCount?: number;
- dates?: Dates;
- readingTime?: number;
- results?: number;
- topics?: TopicPreview[];
- thematics?: ThematicPreview[];
- website?: string;
- wordsCount?: number;
-};
-
-export type Article = {
- author: ArticleAuthor;
- commentCount: number | null;
- content: string;
- databaseId: number;
- dates: Dates;
- featuredImage: Cover;
- id: string;
- info: ContentInfo;
- intro: string;
- seo: SEO;
- topics: TopicPreview[] | [];
- thematics: ThematicPreview[] | [];
- title: string;
-};
-
-export type RawArticle = Pick<
- Article,
- 'commentCount' | 'databaseId' | 'id' | 'info' | 'seo' | 'title'
-> & {
- acfPosts: RawACFPosts;
- author: { node: ArticleAuthor };
- contentParts: ContentParts;
- date: string;
- featuredImage: RawCover;
- modified: string;
-};
-
-export type ArticlePreview = Pick<
- Article,
- | 'commentCount'
- | 'dates'
- | 'id'
- | 'info'
- | 'intro'
- | 'topics'
- | 'thematics'
- | 'title'
-> & { featuredImage: Cover; slug: string };
-
-export type RawArticlePreview = Pick<
- Article,
- 'commentCount' | 'id' | 'info' | 'title'
-> & {
- acfPosts: ACFPosts;
- contentParts: Pick<ContentParts, 'beforeMore'>;
- date: string;
- featuredImage: RawCover;
- modified: string;
- slug: string;
-};
-
-export type PostBy = {
- post: RawArticle;
-};
-
-export type ArticleProps = {
- comments: Comment[];
- post: Article;
-};
-
-export type TotalArticles = {
- posts: {
- pageInfo: {
- total: number;
- };
- };
-};
diff --git a/src/ts/types/blog.ts b/src/ts/types/blog.ts
deleted file mode 100644
index 05bdd1f..0000000
--- a/src/ts/types/blog.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { PageInfo, Slug } from './app';
-import { ArticlePreview, RawArticlePreview } from './articles';
-import { ThematicPreview, TopicPreview } from './taxonomies';
-
-export type PostsList = {
- posts: ArticlePreview[];
- pageInfo: PageInfo;
-};
-
-export type PostsListEdges = {
- cursor: string;
- node: RawArticlePreview;
-};
-
-export type RawPostsList = {
- posts: {
- edges: PostsListEdges[];
- pageInfo: PageInfo;
- };
-};
-
-export type LastPostCursor = {
- posts: {
- pageInfo: {
- endCursor: string;
- };
- };
-};
-
-export type AllPostsSlug = {
- posts: {
- nodes: Slug[];
- };
-};
-
-export type BlogPageProps = {
- allThematics: ThematicPreview[];
- allTopics: TopicPreview[];
- posts: PostsList;
- totalPosts: number;
-};
diff --git a/src/ts/types/comments.ts b/src/ts/types/comments.ts
deleted file mode 100644
index aa3fac3..0000000
--- a/src/ts/types/comments.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-//==============================================================================
-// Comments query
-//==============================================================================
-
-export type CommentAuthor = {
- name: string;
- gravatarUrl: string;
- url: string;
-};
-
-export type RawCommentAuthor = {
- node: CommentAuthor;
-};
-
-export type Comment = {
- approved: '';
- author: CommentAuthor;
- databaseId: number;
- content: string;
- date: string;
- parentDatabaseId: number;
- replies: Comment[];
-};
-
-export type RawComment = Omit<Comment, 'author' | 'replies'> & {
- author: RawCommentAuthor;
-};
-
-export type CommentsNode = {
- nodes: RawComment[];
-};
-
-export type CommentsByPostId = {
- comments: CommentsNode;
-};
-
-//==============================================================================
-// Comment mutations
-//==============================================================================
-
-export type CommentData = {
- author: string;
- authorEmail: string;
- authorUrl: string;
- content: string;
- parent: number;
- commentOn: number;
- mutationId: string;
-};
-
-export type CreatedComment = {
- clientMutationId: string;
- success: boolean;
- comment: null | {
- approved: boolean;
- };
-};
-
-export type CreateComment = {
- createComment: CreatedComment;
-};
diff --git a/src/ts/types/contact.ts b/src/ts/types/contact.ts
deleted file mode 100644
index ef6847a..0000000
--- a/src/ts/types/contact.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-export type ContactData = {
- body: string;
- mutationId: string;
- replyTo: string;
- subject: string;
-};
-
-export type SentEmail = {
- clientMutationId: string;
- message: string;
- origin: string;
- replyTo: string;
- sent: boolean;
- to: string;
-};
-
-export type SendEmail = {
- sendEmail: SentEmail;
-};
diff --git a/src/ts/types/cover.ts b/src/ts/types/cover.ts
deleted file mode 100644
index 4df898e..0000000
--- a/src/ts/types/cover.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export type Cover = {
- altText: string;
- sourceUrl: string;
- title: string;
-} | null;
-
-export type RawCover = {
- node: Cover;
-} | null;
diff --git a/src/ts/types/nav.ts b/src/ts/types/nav.ts
deleted file mode 100644
index 7cfc46b..0000000
--- a/src/ts/types/nav.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export type NavItem = {
- id: string;
- name: string;
- slug: string;
-};
diff --git a/src/ts/types/prism.ts b/src/ts/types/prism.ts
deleted file mode 100644
index 663bc08..0000000
--- a/src/ts/types/prism.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-export type PrismLanguages =
- | 'apacheconf'
- | 'bash'
- | 'css'
- | 'diff'
- | 'docker'
- | 'editorconfig'
- | 'ejs'
- | 'git'
- | 'graphql'
- | 'html'
- | 'ignore'
- | 'ini'
- | 'javascript'
- | 'jsdoc'
- | 'json'
- | 'jsx'
- | 'makefile'
- | 'markup'
- | 'php'
- | 'phpdoc'
- | 'regex'
- | 'scss'
- | 'shell-session'
- | 'smarty'
- | 'tcl'
- | 'toml'
- | 'tsx'
- | 'twig'
- | 'yaml';
-
-export type PrismDefaultPlugins =
- | 'autoloader'
- | 'color-scheme'
- | 'copy-to-clipboard'
- | 'match-braces'
- | 'normalize-whitespace'
- | 'show-language'
- | 'toolbar';
-
-export type PrismPlugins =
- | 'command-line'
- | 'diff-highlight'
- | 'inline-color'
- | 'line-highlight'
- | 'line-numbers';
-
-export type PrismProviderProps = {
- language: PrismLanguages;
- plugins: PrismPlugins[];
-};
diff --git a/src/ts/types/repos.ts b/src/ts/types/repos.ts
deleted file mode 100644
index 7dacacc..0000000
--- a/src/ts/types/repos.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export type RepoData = {
- created_at: string;
- updated_at: string;
- stargazers_count: number;
-};
-
-export type RepoAPI = 'github';
diff --git a/src/ts/types/seo.ts b/src/ts/types/seo.ts
deleted file mode 100644
index 18e3c95..0000000
--- a/src/ts/types/seo.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export type SEO = {
- title: string;
- metaDesc: string;
- metaRobotsNofollow: string;
- metaRobotsNoindex: string;
-};
diff --git a/src/ts/types/taxonomies.ts b/src/ts/types/taxonomies.ts
deleted file mode 100644
index 17fc022..0000000
--- a/src/ts/types/taxonomies.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import { ContentInfo, ContentParts, Dates, Slug } from './app';
-import { ArticlePreview, RawArticlePreview } from './articles';
-import { Cover, RawCover } from './cover';
-import { SEO } from './seo';
-
-//==============================================================================
-// Taxonomies base
-//==============================================================================
-
-type Taxonomy = {
- content: string;
- databaseId: number;
- dates: Dates;
- id: string;
- info: ContentInfo;
- intro: string;
- posts: ArticlePreview[];
- seo: SEO;
- title: string;
-};
-
-type TaxonomyPreview = Pick<
- Taxonomy,
- 'databaseId' | 'id' | 'info' | 'seo' | 'title'
-> & {
- slug: string;
-};
-
-//==============================================================================
-// Topics
-//==============================================================================
-
-export type Topic = Taxonomy & {
- featuredImage: Cover;
- officialWebsite: string;
-};
-
-export type RawTopicPreview = TaxonomyPreview & {
- featuredImage: RawCover;
-};
-
-export type TopicPreview = TaxonomyPreview & {
- featuredImage: Cover;
-};
-
-export type AllTopics = {
- topics: {
- nodes: TopicPreview[];
- };
-};
-
-export type RawTopic = TopicPreview & {
- acfTopics: {
- officialWebsite: string;
- postsInTopic: RawArticlePreview[];
- };
- contentParts: ContentParts;
- date: string;
- featuredImage: RawCover;
- modified: string;
-};
-
-export type TopicBy = {
- topic: RawTopic;
-};
-
-export type AllTopicsSlug = {
- topics: {
- nodes: Slug[];
- };
-};
-
-export type TopicProps = {
- allTopics: TopicPreview[];
- topic: Topic;
-};
-
-//==============================================================================
-// Thematics
-//==============================================================================
-
-export type Thematic = Taxonomy;
-
-export type ThematicPreview = TaxonomyPreview;
-
-export type AllThematics = {
- thematics: {
- nodes: ThematicPreview[];
- };
-};
-
-export type RawThematic = TaxonomyPreview & {
- acfThematics: {
- postsInThematic: RawArticlePreview[];
- };
- contentParts: ContentParts;
- date: string;
- modified: string;
-};
-
-export type ThematicBy = {
- thematic: RawThematic;
-};
-
-export type AllThematicsSlug = {
- thematics: {
- nodes: Slug[];
- };
-};
-
-export type ThematicProps = {
- allThematics: ThematicPreview[];
- thematic: Thematic;
-};
diff --git a/src/utils/helpers/format.ts b/src/utils/helpers/format.ts
deleted file mode 100644
index 47a7b57..0000000
--- a/src/utils/helpers/format.ts
+++ /dev/null
@@ -1,332 +0,0 @@
-import { ParamsIds, ParamsSlug, Slug } from '@ts/types/app';
-import {
- Article,
- ArticlePreview,
- RawArticle,
- RawArticlePreview,
-} from '@ts/types/articles';
-import { Comment, RawComment } from '@ts/types/comments';
-import {
- RawTopic,
- RawTopicPreview,
- RawThematic,
- Topic,
- TopicPreview,
- Thematic,
-} from '@ts/types/taxonomies';
-import { settings } from '@utils/config';
-
-/**
- * Format a post preview from RawArticlePreview to ArticlePreview type.
- * @param rawPost - A post preview coming from WP GraphQL.
- * @returns A formatted post preview.
- */
-export const getFormattedPostPreview = (rawPost: RawArticlePreview) => {
- const {
- acfPosts,
- commentCount,
- contentParts,
- date,
- featuredImage,
- id,
- info,
- modified,
- slug,
- title,
- } = rawPost;
-
- const dates = {
- publication: date,
- update: modified,
- };
-
- const topics = acfPosts.postsInTopic ? acfPosts.postsInTopic : [];
- const thematics = acfPosts.postsInThematic ? acfPosts.postsInThematic : [];
-
- const formattedPost: ArticlePreview = {
- commentCount,
- dates,
- featuredImage: featuredImage ? featuredImage.node : null,
- id,
- info,
- intro: contentParts.beforeMore,
- slug,
- topics,
- thematics,
- title,
- };
-
- return formattedPost;
-};
-
-/**
- * Format an array of posts list from RawArticlePreview to ArticlePreview type.
- * @param rawPosts - A posts list coming from WP GraphQL.
- * @returns A formatted posts list.
- */
-export const getFormattedPostsList = (
- rawPosts: RawArticlePreview[]
-): ArticlePreview[] => {
- return rawPosts
- .filter((post) => Object.getOwnPropertyNames(post).length > 0)
- .map((post) => {
- return getFormattedPostPreview(post);
- });
-};
-
-/**
- * Format a topic from RawTopic to Topic type.
- * @param rawTopic - A topic coming from WP GraphQL.
- * @returns A formatted topic.
- */
-export const getFormattedTopic = (rawTopic: RawTopic): Topic => {
- const {
- acfTopics,
- contentParts,
- databaseId,
- date,
- featuredImage,
- id,
- info,
- modified,
- seo,
- title,
- } = rawTopic;
-
- const dates = {
- publication: date,
- update: modified,
- };
-
- const posts = getFormattedPostsList(acfTopics.postsInTopic);
-
- const formattedTopic: Topic = {
- content: contentParts.afterMore,
- databaseId,
- dates,
- featuredImage: featuredImage ? featuredImage.node : null,
- id,
- info,
- intro: contentParts.beforeMore,
- officialWebsite: acfTopics.officialWebsite,
- posts,
- seo,
- title,
- };
-
- return formattedTopic;
-};
-
-/**
- * Format a thematic from RawThematic to Thematic type.
- * @param rawThematic - A thematic coming from wP GraphQL.
- * @returns A formatted thematic.
- */
-export const getFormattedThematic = (rawThematic: RawThematic): Thematic => {
- const {
- acfThematics,
- contentParts,
- databaseId,
- date,
- id,
- info,
- modified,
- seo,
- title,
- } = rawThematic;
-
- const dates = {
- publication: date,
- update: modified,
- };
-
- const posts = getFormattedPostsList(acfThematics.postsInThematic);
-
- const formattedThematic: Thematic = {
- content: contentParts.afterMore,
- databaseId,
- dates,
- id,
- info,
- intro: contentParts.beforeMore,
- posts,
- seo,
- title,
- };
-
- return formattedThematic;
-};
-
-/**
- * Format a comments list from RawComment to Comment type.
- * @param rawComments - A comments list coming from WP GraphQL.
- * @returns A formatted comments list.
- */
-export const getFormattedComments = (rawComments: RawComment[]): Comment[] => {
- const formattedComments: Comment[] = rawComments.map((comment) => {
- const formattedComment: Comment = {
- ...comment,
- author: comment.author.node,
- replies: [],
- };
-
- return formattedComment;
- });
-
- return formattedComments;
-};
-
-/**
- * Create a comments tree with replies.
- * @param comments - A flatten comments list.
- * @returns An array of comments with replies.
- */
-export const buildCommentsTree = (comments: Comment[]) => {
- type CommentsHashTable = {
- [key: string]: Comment;
- };
-
- const hashTable: CommentsHashTable = Object.create(null);
- const commentsTree: Comment[] = [];
-
- comments.forEach(
- (comment) => (hashTable[comment.databaseId] = { ...comment, replies: [] })
- );
-
- comments.forEach((comment) => {
- if (!comment.parentDatabaseId) {
- commentsTree.push(hashTable[comment.databaseId]);
- } else {
- hashTable[comment.parentDatabaseId].replies.push(
- hashTable[comment.databaseId]
- );
- }
- });
-
- return commentsTree;
-};
-
-export const getFormattedTopicsPreview = (
- topics: RawTopicPreview[]
-): TopicPreview[] => {
- const formattedTopics: TopicPreview[] = topics.map((topic) => {
- return {
- ...topic,
- featuredImage: topic.featuredImage ? topic.featuredImage.node : null,
- };
- });
-
- return formattedTopics;
-};
-
-/**
- * Format an article from RawArticle to Article type.
- * @param rawPost - An article coming from WP GraphQL.
- * @returns A formatted article.
- */
-export const getFormattedPost = (rawPost: RawArticle): Article => {
- const {
- acfPosts,
- author,
- commentCount,
- contentParts,
- databaseId,
- date,
- featuredImage,
- id,
- info,
- modified,
- seo,
- title,
- } = rawPost;
-
- const dates = {
- publication: date,
- update: modified,
- };
-
- const topics = acfPosts.postsInTopic
- ? getFormattedTopicsPreview(acfPosts.postsInTopic)
- : [];
-
- const formattedPost: Article = {
- author: author.node,
- commentCount,
- content: contentParts.afterMore,
- databaseId,
- dates,
- featuredImage: featuredImage ? featuredImage.node : null,
- id,
- info,
- intro: contentParts.beforeMore,
- seo,
- topics,
- thematics: acfPosts.postsInThematic ? acfPosts.postsInThematic : [],
- title,
- };
-
- return formattedPost;
-};
-
-/**
- * Converts a date to a string by using the specified locale.
- * @param {string} date - The date.
- * @param {string} [locale] - A locale.
- * @returns {string} The formatted date to locale date string.
- */
-export const getFormattedDate = (
- date: string,
- locale: string = settings.locales.defaultLocale
-): string => {
- const dateOptions: Intl.DateTimeFormatOptions = {
- day: 'numeric',
- month: 'long',
- year: 'numeric',
- };
-
- return new Date(date).toLocaleDateString(locale, dateOptions);
-};
-
-/**
- * Converts a date to a time string by using the specified locale.
- * @param {string} date - The date.
- * @param {string} [locale] - A locale.
- * @returns {string} The formatted time to locale date string.
- */
-export const getFormattedTime = (
- date: string,
- locale: string = settings.locales.defaultLocale
-): string => {
- const time = new Date(date).toLocaleTimeString(locale, {
- hour: 'numeric',
- minute: 'numeric',
- });
-
- return locale === 'fr' ? time.replace(':', 'h') : time;
-};
-
-/**
- * Convert an array of slugs to an array of params with slug.
- * @param {Slug} array - An array of object with slug.
- * @returns {ParamsSlug} An array of params with slug.
- */
-export const getFormattedPaths = (array: Slug[]): ParamsSlug[] => {
- return array.map((object) => {
- return { params: { slug: object.slug } };
- });
-};
-
-/**
- * Convert a number of pages to an array of params with ids.
- * @param {number} totalPages - The total pages.
- * @returns {ParamsIds} An array of params with ids.
- */
-export const getFormattedPageNumbers = (totalPages: number): ParamsIds[] => {
- const paths = [];
-
- for (let i = 1; i <= totalPages; i++) {
- paths.push({ params: { id: `${i}` } });
- }
-
- return paths;
-};
diff --git a/src/utils/helpers/i18n.ts b/src/utils/helpers/i18n.ts
index c4734ad..5d19c8c 100644
--- a/src/utils/helpers/i18n.ts
+++ b/src/utils/helpers/i18n.ts
@@ -3,7 +3,7 @@ import { settings } from '@utils/config';
import { readFile } from 'fs/promises';
import path from 'path';
-type Messages = { [key: string]: string };
+export type Messages = { [key: string]: string };
export const defaultLocale = settings.locales.defaultLocale;
diff --git a/src/utils/helpers/prism.ts b/src/utils/helpers/prism.ts
deleted file mode 100644
index a5f5787..0000000
--- a/src/utils/helpers/prism.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Check if the current block has a defined language.
- * @param classList - A list of class.
- * @returns {boolean} - True if a class starts with "language-".
- */
-const isLanguageBlock = (classList: DOMTokenList) => {
- const classes = Array.from(classList);
- return classes.some((className) => /language-.*/.test(className));
-};
-
-/**
- * Add automatically some classes and attributes for PrismJs.
- *
- * These classes and attributes are needed by Prism or to customize comments.
- */
-export const addPrismClasses = () => {
- const preTags = document.getElementsByTagName('pre');
-
- Array.from(preTags).forEach((preTag) => {
- if (!isLanguageBlock(preTag.classList)) return;
-
- preTag.classList.add('match-braces');
-
- if (preTag.classList.contains('filter-output')) {
- preTag.setAttribute('data-filter-output', '#output#');
- }
-
- if (preTag.classList.contains('language-bash')) {
- preTag.classList.add('command-line');
- } else if (!preTag.classList.contains('language-diff')) {
- preTag.classList.add('line-numbers');
- }
- });
-};
diff --git a/src/utils/helpers/projects.ts b/src/utils/helpers/projects.ts
deleted file mode 100644
index 1612dae..0000000
--- a/src/utils/helpers/projects.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { Project, ProjectMeta } from '@ts/types/app';
-import { readdirSync } from 'fs';
-import path from 'path';
-
-/**
- * Retrieve project's data by id.
- * @param {string} id - The filename without extension.
- * @returns {Promise<Project>} - The project data.
- */
-export const getProjectData = async (id: string): Promise<Project> => {
- try {
- const {
- intro,
- meta,
- seo,
- tagline,
- }: {
- intro: string;
- meta: ProjectMeta & { title: string };
- seo: { title: string; description: string };
- tagline?: string;
- } = await import(`../../content/projects/${id}.mdx`);
-
- const { title, ...onlyMeta } = meta;
-
- return {
- id,
- intro: intro || '',
- meta: onlyMeta || {},
- slug: id,
- title,
- seo: seo || {},
- tagline: tagline || '',
- };
- } catch (err) {
- console.error(err);
- throw err;
- }
-};
-
-/**
- * Retrieve the projects data from filenames.
- * @param {string[]} filenames - An array of filenames.
- * @returns {Promise<Project[]>} An array of projects with meta.
- */
-const getProjectsWithMeta = async (filenames: string[]): Promise<Project[]> => {
- return Promise.all(
- filenames.map(async (filename) => {
- return getProjectData(filename);
- })
- );
-};
-
-/**
- * Method to sort an array of projects by publication date.
- * @param {Project} a - A single project.
- * @param {Project} b - A single project.
- * @returns The result used by Array.sort() method: 1 || -1 || 0.
- */
-const sortProjectByPublicationDate = (a: Project, b: Project) => {
- if (a.meta.publishedOn < b.meta.publishedOn) return 1;
- if (a.meta.publishedOn > b.meta.publishedOn) return -1;
- return 0;
-};
-
-/**
- * Retrieve all the projects filename.
- * @returns {string[]} An array of filenames.
- */
-export const getAllProjectsFilename = (): string[] => {
- const projectsDirectory = path.join(process.cwd(), 'src/content/projects');
- const filenames = readdirSync(projectsDirectory);
-
- return filenames.map((filename) => filename.replace(/\.mdx$/, ''));
-};
-
-/**
- * Retrieve all projects in content folder sorted by publication date.
- * @returns {Promise<Project[]>} An array of projects.
- */
-export const getSortedProjects = async (): Promise<Project[]> => {
- const filenames = getAllProjectsFilename();
- const projects = await getProjectsWithMeta(filenames);
-
- return [...projects].sort(sortProjectByPublicationDate);
-};
diff --git a/src/utils/helpers/sort.ts b/src/utils/helpers/sort.ts
deleted file mode 100644
index c1ee35d..0000000
--- a/src/utils/helpers/sort.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { ArticlePreview } from '@ts/types/articles';
-import { PostsList } from '@ts/types/blog';
-
-type YearCollection = {
- [key: string]: ArticlePreview[];
-};
-
-export const sortPostsByYear = (data: PostsList[]) => {
- const yearCollection: YearCollection = {};
-
- data.forEach((page) => {
- page.posts.forEach((post) => {
- const postYear = new Date(post.dates.publication)
- .getFullYear()
- .toString();
- yearCollection[postYear] = [...(yearCollection[postYear] || []), post];
- });
- });
-
- return yearCollection;
-};
diff --git a/src/utils/hooks/useGithubApi.tsx b/src/utils/hooks/useGithubApi.tsx
deleted file mode 100644
index 4b0b3b2..0000000
--- a/src/utils/hooks/useGithubApi.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { RepoData } from '@ts/types/repos';
-import useSWR, { Fetcher } from 'swr';
-
-const fetcher: Fetcher<RepoData, string> = (...args) =>
- fetch(...args).then((res) => res.json());
-
-/**
- * Retrieve data from Github API.
- * @param repo The repo name. Format: "User/project-slug".
- * @returns {object} The data and two booleans to determine if is loading/error.
- */
-const useGithubApi = (repo: string) => {
- const apiUrl = repo ? `https://api.github.com/repos/${repo}` : null;
- const { data, error } = useSWR<RepoData>(apiUrl, fetcher);
-
- return {
- data,
- isLoading: !error && !data,
- isError: error,
- };
-};
-
-export default useGithubApi;
diff --git a/src/utils/hooks/useHeadingsTree.tsx b/src/utils/hooks/useHeadingsTree.tsx
deleted file mode 100644
index f2be406..0000000
--- a/src/utils/hooks/useHeadingsTree.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import { Heading } from '@ts/types/app';
-import { slugify } from '@utils/helpers/slugify';
-import { useRouter } from 'next/router';
-import { useCallback, useEffect, useMemo, useState } from 'react';
-
-const useHeadingsTree = (wrapper: string) => {
- const router = useRouter();
- const depths = useMemo(() => ['h2', 'h3', 'h4', 'h5', 'h6'], []);
- const [allHeadings, setAllHeadings] =
- useState<NodeListOf<HTMLHeadingElement>>();
-
- useEffect(() => {
- const query = depths
- .map((depth) => `${wrapper} > *:not(aside, #comments) ${depth}`)
- .join(', ');
- const result: NodeListOf<HTMLHeadingElement> =
- document.querySelectorAll(query);
- setAllHeadings(result);
- }, [depths, wrapper, router.asPath]);
-
- const [headingsTree, setHeadingsTree] = useState<Heading[]>([]);
-
- const getElementDepth = useCallback(
- (el: HTMLHeadingElement) => {
- return depths.findIndex((depth) => depth === el.localName);
- },
- [depths]
- );
-
- const formatHeadings = useCallback(
- (headings: NodeListOf<HTMLHeadingElement>): Heading[] => {
- const formattedHeadings: Heading[] = [];
-
- Array.from(headings).forEach((heading) => {
- const title: string = heading.textContent!;
- const id = slugify(title);
- const depth = getElementDepth(heading);
- const children: Heading[] = [];
-
- heading.id = id;
-
- formattedHeadings.push({
- depth,
- id,
- children,
- title,
- });
- });
-
- return formattedHeadings;
- },
- [getElementDepth]
- );
-
- const buildSubTree = useCallback(
- (parent: Heading, currentHeading: Heading): void => {
- if (parent.depth === currentHeading.depth - 1) {
- parent.children.push(currentHeading);
- } else {
- const lastItem = parent.children[parent.children.length - 1];
- buildSubTree(lastItem, currentHeading);
- }
- },
- []
- );
-
- const buildTree = useCallback(
- (headings: Heading[]): Heading[] => {
- const tree: Heading[] = [];
-
- headings.forEach((heading) => {
- if (heading.depth === 0) {
- tree.push(heading);
- } else {
- const lastItem = tree[tree.length - 1];
- buildSubTree(lastItem, heading);
- }
- });
-
- return tree;
- },
- [buildSubTree]
- );
-
- const getHeadingsList = useCallback(
- (headings: NodeListOf<HTMLHeadingElement>): Heading[] => {
- const formattedHeadings = formatHeadings(headings);
-
- return buildTree(formattedHeadings);
- },
- [formatHeadings, buildTree]
- );
-
- useEffect(() => {
- if (allHeadings) {
- const headingsList = getHeadingsList(allHeadings);
- setHeadingsTree(headingsList);
- }
- }, [allHeadings, getHeadingsList]);
-
- return headingsTree;
-};
-
-export default useHeadingsTree;
diff --git a/src/utils/providers/ackee.tsx b/src/utils/providers/ackee.tsx
index c103668..0cb0166 100644
--- a/src/utils/providers/ackee.tsx
+++ b/src/utils/providers/ackee.tsx
@@ -1,5 +1,5 @@
import { useRouter } from 'next/router';
-import { createContext, FC, useContext, useState } from 'react';
+import { createContext, FC, ReactNode, useContext, useState } from 'react';
import useAckee from 'use-ackee';
export type AckeeProps = {
@@ -10,6 +10,7 @@ export type AckeeProps = {
};
export type AckeeProviderProps = {
+ children: ReactNode;
domain: string;
siteId: string;
ignoreLocalhost?: boolean;
diff --git a/src/utils/providers/prism-theme.tsx b/src/utils/providers/prism-theme.tsx
index 2ed8454..62bcf56 100644
--- a/src/utils/providers/prism-theme.tsx
+++ b/src/utils/providers/prism-theme.tsx
@@ -2,6 +2,7 @@ import { LocalStorage } from '@services/local-storage';
import {
createContext,
FC,
+ ReactNode,
useCallback,
useContext,
useEffect,
@@ -22,6 +23,7 @@ export type UsePrismThemeProps = {
export type PrismThemeProviderProps = {
attribute?: string;
+ children: ReactNode;
storageKey?: string;
themes?: PrismTheme[];
};