diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-09-21 13:50:18 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-09-21 13:50:18 +0200 |
| commit | a5e6692f6dcab2157dc92b509f61418c06b2ebd7 (patch) | |
| tree | b5cebad901d8bc2f24c66b82ce7257c7da0539f1 | |
| parent | 08c7b3d0eb2ced622cdd3c4d14a3958ac8161cb8 (diff) | |
fix(projects): load content dynamically and refresh table of contents
The previous way of handling content import was causing issue. So I use
dynamic import instead. However, the table of contents was not
displayed because the wrapper is first empty. I added a mutation
observer to refresh the table of contents when the body is updated.
| -rw-r--r-- | src/pages/projets/[slug].tsx | 9 | ||||
| -rw-r--r-- | src/utils/hooks/use-headings-tree.tsx | 13 | ||||
| -rw-r--r-- | src/utils/hooks/use-mutation-observer.tsx | 28 |
3 files changed, 47 insertions, 3 deletions
diff --git a/src/pages/projets/[slug].tsx b/src/pages/projets/[slug].tsx index b396fc3..7539b55 100644 --- a/src/pages/projets/[slug].tsx +++ b/src/pages/projets/[slug].tsx @@ -33,6 +33,7 @@ import useGithubApi, { type RepoData } from '@utils/hooks/use-github-api'; import useSettings from '@utils/hooks/use-settings'; import { MDXComponents, NestedMDXComponents } from 'mdx/types'; import { GetStaticPaths, GetStaticProps } from 'next'; +import dynamic from 'next/dynamic'; import Head from 'next/head'; import { useRouter } from 'next/router'; import Script from 'next/script'; @@ -56,8 +57,12 @@ const ProjectPage: NextPageWithLayout<ProjectPageProps> = ({ project }) => { url: `/projets/${id}`, }); - const ProjectContent: ComponentType<MDXComponents> = - require(`../../content/projects/${id}.mdx`).default; + const ProjectContent: ComponentType<MDXComponents> = dynamic( + () => import(`../../content/projects/${id}.mdx`), + { + loading: () => <Spinner />, + } + ); const components: NestedMDXComponents = { Code: (props) => <Code {...props} />, diff --git a/src/utils/hooks/use-headings-tree.tsx b/src/utils/hooks/use-headings-tree.tsx index 4646b4a..f7ab452 100644 --- a/src/utils/hooks/use-headings-tree.tsx +++ b/src/utils/hooks/use-headings-tree.tsx @@ -1,5 +1,6 @@ import { slugify } from '@utils/helpers/strings'; import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useMutationObserver } from './use-mutation-observer'; export type Heading = { /** @@ -32,13 +33,23 @@ const useHeadingsTree = (wrapper: HTMLElement): Heading[] => { useState<NodeListOf<HTMLHeadingElement>>(); const [headingsTree, setHeadingsTree] = useState<Heading[]>([]); - useEffect(() => { + const getHeadingsInWrapper = useCallback(() => { const query = depths.join(', '); const result: NodeListOf<HTMLHeadingElement> = wrapper.querySelectorAll(query); setAllHeadings(result); }, [depths, wrapper]); + useEffect(() => { + getHeadingsInWrapper(); + }, [getHeadingsInWrapper]); + + useMutationObserver({ + callback: getHeadingsInWrapper, + options: { childList: true }, + nodeOrSelector: wrapper, + }); + const getDepth = useCallback( /** * Retrieve the heading element depth. diff --git a/src/utils/hooks/use-mutation-observer.tsx b/src/utils/hooks/use-mutation-observer.tsx new file mode 100644 index 0000000..c56f7aa --- /dev/null +++ b/src/utils/hooks/use-mutation-observer.tsx @@ -0,0 +1,28 @@ +import { useEffect } from 'react'; + +type UseMutationObserverProps = { + callback: () => void; + options: MutationObserverInit; + nodeOrSelector: string | HTMLElement; +}; + +export const useMutationObserver = ({ + callback, + options, + nodeOrSelector, +}: UseMutationObserverProps) => { + useEffect(() => { + const targetNode = + typeof nodeOrSelector === 'string' + ? document.querySelector(nodeOrSelector)! + : nodeOrSelector; + + const observer = new MutationObserver(callback); + + observer.observe(targetNode, options); + + return () => { + observer.disconnect(); + }; + }, [nodeOrSelector, options, callback]); +}; |
