aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2021-12-21 15:20:17 +0100
committerArmand Philippot <git@armandphilippot.com>2021-12-21 15:20:17 +0100
commit926e1df1e9a7de29134293fe2306c9d9ecb594a6 (patch)
treed83550ce445525a3993d2f345cb301be5e7403b3
parentae17973618c5ad5500ae69738da222187a09b019 (diff)
chore: add a table of contents
-rw-r--r--src/components/ToC/ToC.tsx31
-rw-r--r--src/pages/article/[slug].tsx2
-rw-r--r--src/pages/cv.tsx12
-rw-r--r--src/pages/mentions-legales.tsx12
-rw-r--r--src/ts/types/app.ts7
-rw-r--r--src/utils/hooks/useHeadingsTree.tsx8
6 files changed, 61 insertions, 11 deletions
diff --git a/src/components/ToC/ToC.tsx b/src/components/ToC/ToC.tsx
new file mode 100644
index 0000000..e7aafa5
--- /dev/null
+++ b/src/components/ToC/ToC.tsx
@@ -0,0 +1,31 @@
+import { t } from '@lingui/macro';
+import { Heading } from '@ts/types/app';
+import { slugify } from '@utils/helpers/slugify';
+import useHeadingsTree from '@utils/hooks/useHeadingsTree';
+
+const ToC = () => {
+ const headingsTree = useHeadingsTree('article');
+ const title = t`Table of contents`;
+
+ const getItems = (headings: Heading[]) => {
+ return headings.map((heading) => {
+ if (heading.id === slugify(title)) return;
+
+ return (
+ <li key={heading.id}>
+ <a href={`#${heading.id}`}>{heading.title}</a>
+ {heading.children.length > 0 && <ol>{getItems(heading.children)}</ol>}
+ </li>
+ );
+ });
+ };
+
+ return (
+ <>
+ <h2>{title}</h2>
+ <ol>{getItems(headingsTree)}</ol>
+ </>
+ );
+};
+
+export default ToC;
diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx
index 621b2e2..1fa65fa 100644
--- a/src/pages/article/[slug].tsx
+++ b/src/pages/article/[slug].tsx
@@ -3,6 +3,7 @@ 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 ToC from '@components/ToC/ToC';
import { t } from '@lingui/macro';
import { getAllPostsSlug, getPostBySlug } from '@services/graphql/queries';
import { NextPageWithLayout } from '@ts/types/app';
@@ -39,6 +40,7 @@ const SingleArticle: NextPageWithLayout<ArticleProps> = ({ post }) => {
title={title}
thematics={thematics}
/>
+ <ToC />
<div dangerouslySetInnerHTML={{ __html: content }}></div>
<PostFooter subjects={subjects} />
<section>
diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx
index de63ec6..44d943c 100644
--- a/src/pages/cv.tsx
+++ b/src/pages/cv.tsx
@@ -1,4 +1,5 @@
import { getLayout } from '@components/Layouts/Layout';
+import ToC from '@components/ToC/ToC';
import { seo } from '@config/seo';
import { getPageByUri } from '@services/graphql/queries';
import { NextPageWithLayout } from '@ts/types/app';
@@ -17,9 +18,16 @@ const CV: NextPageWithLayout<PageProps> = ({ page }) => {
<article>
<header>
<h1>{page.title}</h1>
- <div dangerouslySetInnerHTML={{ __html: page.intro }}></div>
+ {page.content && (
+ <div dangerouslySetInnerHTML={{ __html: page.intro }}></div>
+ )}
</header>
- <div dangerouslySetInnerHTML={{ __html: page.content }}></div>
+ <ToC />
+ <div
+ dangerouslySetInnerHTML={{
+ __html: page.content ? page.content : page.intro,
+ }}
+ ></div>
</article>
</>
);
diff --git a/src/pages/mentions-legales.tsx b/src/pages/mentions-legales.tsx
index 6155132..41e9a0c 100644
--- a/src/pages/mentions-legales.tsx
+++ b/src/pages/mentions-legales.tsx
@@ -1,4 +1,5 @@
import { getLayout } from '@components/Layouts/Layout';
+import ToC from '@components/ToC/ToC';
import { seo } from '@config/seo';
import { getPageByUri } from '@services/graphql/queries';
import { NextPageWithLayout } from '@ts/types/app';
@@ -17,9 +18,16 @@ const LegalNotice: NextPageWithLayout<PageProps> = ({ page }) => {
<article>
<header>
<h1>{page.title}</h1>
- <div dangerouslySetInnerHTML={{ __html: page.intro }}></div>
+ {page.content && (
+ <div dangerouslySetInnerHTML={{ __html: page.intro }}></div>
+ )}
</header>
- <div dangerouslySetInnerHTML={{ __html: page.content }}></div>
+ <ToC />
+ <div
+ dangerouslySetInnerHTML={{
+ __html: page.content ? page.content : page.intro,
+ }}
+ ></div>
</article>
</>
);
diff --git a/src/ts/types/app.ts b/src/ts/types/app.ts
index ba28416..e1d8917 100644
--- a/src/ts/types/app.ts
+++ b/src/ts/types/app.ts
@@ -76,6 +76,13 @@ export type Dates = {
update: string;
};
+export type Heading = {
+ depth: number;
+ id: string;
+ children: Heading[];
+ title: string;
+};
+
export type PageInfo = {
endCursor: string;
hasNextPage: boolean;
diff --git a/src/utils/hooks/useHeadingsTree.tsx b/src/utils/hooks/useHeadingsTree.tsx
index 8bb70c8..94d3b4b 100644
--- a/src/utils/hooks/useHeadingsTree.tsx
+++ b/src/utils/hooks/useHeadingsTree.tsx
@@ -1,13 +1,7 @@
+import { Heading } from '@ts/types/app';
import { slugify } from '@utils/helpers/slugify';
import { useCallback, useEffect, useMemo, useState } from 'react';
-type Heading = {
- depth: number;
- id: string;
- children: Heading[];
- title: string;
-};
-
const useHeadingsTree = (wrapper: string) => {
const [headingsTree, setHeadingsTree] = useState<Heading[]>([]);
const depths = useMemo(() => ['h2', 'h3', 'h4', 'h5', 'h6'], []);