diff --git a/src/components/ui/Markdown/overrides/headings/headings.tsx b/src/components/ui/Markdown/overrides/headings/headings.tsx index 30b45e2..c6c5669 100644 --- a/src/components/ui/Markdown/overrides/headings/headings.tsx +++ b/src/components/ui/Markdown/overrides/headings/headings.tsx @@ -1,5 +1,6 @@ import React from "react"; +import { removeSpecialCharacters } from "@utils/getToc"; import { getChildrenText } from "@utils/getChildrenText"; interface IProps { @@ -9,7 +10,7 @@ interface IProps { const Heading = ({ children, tagname }: IProps) => { const text = getChildrenText(children); - const id = `${tagname}_${text.replace(/\s/g, "_").trim()}`; + const id = removeSpecialCharacters(text); return React.createElement(tagname, { id }, children); }; diff --git a/src/components/ui/client/post/PostContent/Toc/item.tsx b/src/components/ui/client/post/PostContent/Toc/item.tsx index be9418d..fb7a090 100644 --- a/src/components/ui/client/post/PostContent/Toc/item.tsx +++ b/src/components/ui/client/post/PostContent/Toc/item.tsx @@ -1,5 +1,7 @@ import React, { memo } from "react"; +import NextLink from "next/link"; + import { IToc } from "@/types/base"; interface IProps { @@ -18,14 +20,15 @@ const TocItem = ({ item, isActive, onClick }: IProps) => { style={{ paddingLeft: `${level * 0.8}rem` }} className={`${className} w-full [&.active>a]:text-effect1`} > - {text} - + ); }; diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts index 98b3ce0..b4b6b47 100644 --- a/src/hooks/useIntersectionObserver.ts +++ b/src/hooks/useIntersectionObserver.ts @@ -1,5 +1,7 @@ import { useEffect, useRef } from "react"; +import { MARKDOWN_HEADING_SELECTOR } from "@utils/constant/toc"; + interface IHeadingElement { [key: string]: IntersectionObserverEntry; } @@ -44,7 +46,7 @@ export const useIntersectionObserver = ( const observer = new IntersectionObserver(callback, { rootMargin: "-72px 0px -90% 0px" }); - const headingElements = Array.from(document.querySelectorAll("h1, h2, h3, h4, h5, h6")); + const headingElements = Array.from(document.querySelectorAll(MARKDOWN_HEADING_SELECTOR)); headingElements.forEach((element) => observer.observe(element)); diff --git a/src/hooks/useTocEvent.ts b/src/hooks/useTocEvent.ts index 7f293b2..211093f 100644 --- a/src/hooks/useTocEvent.ts +++ b/src/hooks/useTocEvent.ts @@ -1,20 +1,21 @@ import { useEffect } from "react"; +import { MARKDOWN_HEADING_SELECTOR } from "@utils/constant/toc"; import { IToc } from "@/types/base"; const useTocEvent = (toc: IToc[]) => { const scroll = (id: string, behavior: ScrollBehavior = "smooth") => { - const targetHeading = document.getElementById(id); + const headingElements = Array.from(document.querySelectorAll(MARKDOWN_HEADING_SELECTOR)); + + if (!headingElements.length) return; + + const targetHeading = headingElements.find((heading) => heading.id === id); if (!targetHeading) return; const scrollY = window.scrollY + targetHeading.getBoundingClientRect().top - 80; - window.scrollTo({ - top: scrollY, - behavior, - left: 0, - }); + window.scrollTo({ top: scrollY, behavior, left: 0 }); }; const scrollToTargetItem: React.MouseEventHandler = (e) => { @@ -34,7 +35,7 @@ const useTocEvent = (toc: IToc[]) => { if (!decodedHash) return; - const item = toc.find((item) => item.text === decodedHash); + const item = toc.find((item) => item.id === decodedHash); if (!item) return; diff --git a/src/styles/globals.css b/src/styles/globals.css index e44dc07..54f4902 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -151,7 +151,7 @@ } & td { - @apply border-[1px] border-solid border-gray-500; + @apply border border-solid border-gray-300 !transition-[border-color,color] dark:border-gray-700; } & pre { diff --git a/src/utils/constant/toc.ts b/src/utils/constant/toc.ts new file mode 100644 index 0000000..93a022d --- /dev/null +++ b/src/utils/constant/toc.ts @@ -0,0 +1,3 @@ +const MARKDOWN_HEADING_SELECTOR = ".markdown > h1,.markdown > h2,.markdown > h3"; + +export { MARKDOWN_HEADING_SELECTOR }; diff --git a/src/utils/getToc.ts b/src/utils/getToc.ts index 92384f9..4d22ecb 100644 --- a/src/utils/getToc.ts +++ b/src/utils/getToc.ts @@ -3,6 +3,15 @@ import { ReactNode, JSX } from "react"; import { getChildrenText } from "@utils/getChildrenText"; import { IToc } from "@/types/base"; +const removeSpecialCharacters = (text: string) => { + return text + .replace(/[^a-zA-Z0-9가-힣\s]/g, "") + .replace(/\s+/g, " ") + .replace(/\s/g, "-") + .toLowerCase() + .trim(); +}; + const getToc = (markdown: ReactNode) => { const toc: IToc[] = []; @@ -16,13 +25,15 @@ const getToc = (markdown: ReactNode) => { const children = el?.props?.children; const text = getChildrenText(children); - const id = `${tagname}_${text.replace(/\s/g, "_").trim()}`; + const id = removeSpecialCharacters(text); const level = Number(tagname.replace("h", "")) - 1; + if (level > 2) return; + toc.push({ text, id, level }); }); return toc; }; -export { getToc }; +export { getToc, removeSpecialCharacters };