[component/toc][f]: toc styles adjustment
This commit is contained in:
parent
ac93858d82
commit
73adb71e10
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
|
||||||
export const Heading = ({ level, observer }) => (props) => {
|
export const Heading = ({ level, observer }) => (props) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (observer) {
|
if (observer) {
|
||||||
|
|
@ -7,5 +8,9 @@ export const Heading = ({ level, observer }) => (props) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return React.createElement(`h${level}`, { ...props })
|
|
||||||
|
return React.createElement(`h${level}`, {
|
||||||
|
...props,
|
||||||
|
className: "scroll-mt-16"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export default function Layout({ children }) {
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
|
||||||
</Head>
|
</Head>
|
||||||
<Nav />
|
<Nav />
|
||||||
<main className="border-2 border-blue-500 max-w-7xl relative mx-auto px-2 sm:px-6 md:px-8">
|
<main className="max-w-7xl relative mx-auto px-2 sm:px-6 md:px-8">
|
||||||
{/* <div className="hidden md:block w-[16rem] fixed top-16 left-[max(0px,calc(50%-40rem))] bottom-0 right-auto px-8 overflow-y-auto border-2 border-red-500"> */}
|
{/* <div className="hidden md:block w-[16rem] fixed top-16 left-[max(0px,calc(50%-40rem))] bottom-0 right-auto px-8 overflow-y-auto border-2 border-red-500"> */}
|
||||||
{/* <Sidebar /> */}
|
{/* <Sidebar /> */}
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ import { YOUTUBE_REGEX } from "../lib/constants";
|
||||||
import siteConfig from "../config/siteConfig";
|
import siteConfig from "../config/siteConfig";
|
||||||
import getMDXComponents from "./_getMDXComponents";
|
import getMDXComponents from "./_getMDXComponents";
|
||||||
import getObserver from "./_getIntersectionObserver"
|
import getObserver from "./_getIntersectionObserver"
|
||||||
import { Paragraph } from "./Paragraph";
|
import MdxContent from "./MdxContent"
|
||||||
import { Anchor } from "./Anchor";
|
|
||||||
|
// import { Paragraph } from "./Paragraph";
|
||||||
|
// import { Anchor } from "./Anchor";
|
||||||
|
|
||||||
// const Anchor = dynamic(() => import('./Anchor').then(module => module.Anchor), {
|
// const Anchor = dynamic(() => import('./Anchor').then(module => module.Anchor), {
|
||||||
// ssr: false
|
// ssr: false
|
||||||
|
|
@ -15,46 +17,48 @@ import { Anchor } from "./Anchor";
|
||||||
|
|
||||||
// const Paragraph = dynamic(() => import("./Paragraph").then(mod => mod.Paragraph))
|
// const Paragraph = dynamic(() => import("./Paragraph").then(mod => mod.Paragraph))
|
||||||
|
|
||||||
|
export default function MdxPage({ body, frontMatter, editUrl }) {
|
||||||
export default function MdxPage({ children }) {
|
|
||||||
const [activeHeading, setActiveHeading] = useState("");
|
const [activeHeading, setActiveHeading] = useState("");
|
||||||
|
const [observer, setObserver] = useState(null);
|
||||||
|
|
||||||
const observer = getObserver((entry) => {
|
// run only after first render, in order to preserve the observer
|
||||||
if (entry.isIntersecting) {
|
useEffect((() => {
|
||||||
setActiveHeading(entry.target.id);
|
const observer = getObserver((entry) => {
|
||||||
}
|
if (entry.isIntersecting) {
|
||||||
})
|
setActiveHeading(entry.target.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setObserver(observer);
|
||||||
|
|
||||||
const components = getMDXComponents({
|
return observer.disconnect();
|
||||||
h: {
|
}), [])
|
||||||
observer
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeHeading) {
|
if (!activeHeading) {
|
||||||
const tocLink = document.querySelector(`.toc-link[href="#${activeHeading}"]`)
|
try {
|
||||||
tocLink.classList.add("active");
|
const path = window.location.hash;
|
||||||
|
if (path) {
|
||||||
return () => {
|
setActiveHeading(path.slice(1))
|
||||||
tocLink.classList.remove("active")
|
} else {
|
||||||
|
const firstTocHeading = document.querySelector(".toc-link");
|
||||||
|
const href = firstTocHeading.href;
|
||||||
|
setActiveHeading(href.match(/.+#(.+)/)[1]);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}, [activeHeading]);
|
const tocLink = document.querySelector(`.toc-link[href="#${activeHeading}"]`)
|
||||||
|
tocLink.classList.add("active");
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
tocLink.classList.remove("active")
|
||||||
|
}
|
||||||
|
}, [activeHeading])
|
||||||
|
|
||||||
const {
|
const {
|
||||||
Component,
|
title, description, date, authors, youtube, podcast, image, _raw
|
||||||
frontmatter: {
|
} = frontMatter
|
||||||
title,
|
|
||||||
description,
|
|
||||||
date,
|
|
||||||
keywords,
|
|
||||||
youtube,
|
|
||||||
podcast,
|
|
||||||
image,
|
|
||||||
_raw,
|
|
||||||
},
|
|
||||||
} = children;
|
|
||||||
|
|
||||||
let youtubeThumnbnail;
|
let youtubeThumnbnail;
|
||||||
|
|
||||||
|
|
@ -120,7 +124,7 @@ export default function MdxPage({ children }) {
|
||||||
{ name: "keywords", content: keywords ? keywords : "" },
|
{ name: "keywords", content: keywords ? keywords : "" },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<article className="border-2 border-green-500 prose dark:prose-invert prose-a:break-all mx-auto lg:mr-[20rem] p-6">
|
<article className="prose dark:prose-invert prose-a:break-all mx-auto lg:mr-[20rem] p-6">
|
||||||
<header>
|
<header>
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
{title && <h1 className="mb-0">{title}</h1>}
|
{title && <h1 className="mb-0">{title}</h1>}
|
||||||
|
|
@ -152,8 +156,8 @@ export default function MdxPage({ children }) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main className="my-12">
|
<main className="my-6">
|
||||||
<Component components={components} />
|
<MdxContent body={body} observer={observer}/>
|
||||||
{editUrl && (
|
{editUrl && (
|
||||||
<div className="mt-12 mb-6">
|
<div className="mt-12 mb-6">
|
||||||
<a
|
<a
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||||
|
import { Paragraph } from './Paragraph'
|
||||||
|
import { Anchor } from './Anchor'
|
||||||
|
import { Heading } from './Heading'
|
||||||
|
|
||||||
|
|
||||||
|
const MdxContent = ({ body, observer }) => {
|
||||||
|
const customComponents = {
|
||||||
|
Head,
|
||||||
|
p: Paragraph,
|
||||||
|
a: Anchor,
|
||||||
|
h1: Heading({ level: 1, observer }),
|
||||||
|
h2: Heading({ level: 2, observer }),
|
||||||
|
h3: Heading({ level: 3, observer }),
|
||||||
|
h4: Heading({ level: 4, observer }),
|
||||||
|
h5: Heading({ level: 5, observer }),
|
||||||
|
h6: Heading({ level: 6, observer }),
|
||||||
|
}
|
||||||
|
const Component = useMDXComponent(body.code);
|
||||||
|
|
||||||
|
return <Component components={ customComponents }/>
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(MdxContent);
|
||||||
|
|
@ -1,22 +1,16 @@
|
||||||
const getIntersectionObserver = (callback) => {
|
const getIntersectionObserver = (callback) => {
|
||||||
if (typeof window !== 'undefined') {
|
return new IntersectionObserver(
|
||||||
return new IntersectionObserver(
|
(entries) => {
|
||||||
(entries) => {
|
entries.forEach((entry) => {
|
||||||
const intersectingEntries = entries.filter((e) => e.isIntersecting);
|
callback(entry);
|
||||||
const firstEntry = intersectingEntries[0];
|
});
|
||||||
if (firstEntry) {
|
},
|
||||||
callback(firstEntry);
|
{
|
||||||
}
|
root: null,
|
||||||
// entries.forEach((entry) => {
|
// 65px is a navbar height
|
||||||
// callback(entry);
|
rootMargin: `-65px 0% -90% 0%`
|
||||||
// });
|
}
|
||||||
},
|
);
|
||||||
{ root: null,
|
|
||||||
threshold: 0.9,
|
|
||||||
rootMargin: `0% 0% -90% 0%`
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default getIntersectionObserver;
|
export default getIntersectionObserver;
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import Head from 'next/head'
|
|
||||||
import { Paragraph } from './Paragraph'
|
|
||||||
import { Anchor } from './Anchor'
|
|
||||||
import { Heading } from './Heading'
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {object} [props] Props passed to each component
|
|
||||||
* @param {object} [props.h] Props passed to Heading component
|
|
||||||
*/
|
|
||||||
const getMDXComponents = (props) => ({
|
|
||||||
Head,
|
|
||||||
p: Paragraph,
|
|
||||||
a: Anchor,
|
|
||||||
h1: Heading({ level: 1, ...props?.h }),
|
|
||||||
h2: Heading({ level: 2, ...props?.h }),
|
|
||||||
h3: Heading({ level: 3, ...props?.h }),
|
|
||||||
h4: Heading({ level: 4, ...props?.h }),
|
|
||||||
h5: Heading({ level: 5, ...props?.h }),
|
|
||||||
h6: Heading({ level: 6, ...props?.h }),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getMDXComponents;
|
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
import MdxPage from "../components/MDX";
|
import { allOtherPages } from 'contentlayer/generated';
|
||||||
import { allOtherPages } from "contentlayer/generated";
|
|
||||||
import { useMDXComponent } from "next-contentlayer/hooks";
|
|
||||||
|
|
||||||
import siteConfig from "../config/siteConfig";
|
import MdxPage from '../components/MDX';
|
||||||
|
import siteConfig from "../config/siteConfig"
|
||||||
|
|
||||||
export default function Page({ body, ...rest }) {
|
export default function Page({ body, ...meta }) {
|
||||||
const Component = useMDXComponent(body.code);
|
const frontMatter = {
|
||||||
const children = {
|
...meta,
|
||||||
Component,
|
date: meta.date === "Invalid Date" ? null : meta.date,
|
||||||
frontmatter: {
|
created: meta.created === "Invalid Date" ? null : meta.created
|
||||||
...rest,
|
}
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return <MdxPage children={children} />;
|
// enable editing content only for claims, concepts, and guide for now
|
||||||
|
const editUrl = ['claims', 'concepts', 'guide'].includes(meta._raw.sourceFileDir)
|
||||||
|
? siteConfig.repoRoot + siteConfig.repoEditPath + meta._raw.sourceFilePath
|
||||||
|
: null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MdxPage body={body} frontMatter={frontMatter} editUrl={editUrl} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStaticProps = async ({ params }) => {
|
export const getStaticProps = async ({ params }) => {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
/* OTHERS */
|
/* OTHERS */
|
||||||
|
|
||||||
html {
|
html {
|
||||||
/* scroll-behavior: smooth; */
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* bg-neutral-800
|
/* bg-neutral-800
|
||||||
|
|
@ -40,36 +40,32 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* rehype-toc classes */
|
/* rehype-toc classes */
|
||||||
|
/* nav element */
|
||||||
.toc {
|
.toc {
|
||||||
@apply
|
@apply
|
||||||
hidden
|
hidden
|
||||||
lg:block
|
lg:block
|
||||||
w-[20rem]
|
w-[20rem]
|
||||||
|
my-12
|
||||||
|
pt-12
|
||||||
px-8
|
px-8
|
||||||
py-24
|
|
||||||
fixed
|
fixed
|
||||||
top-16
|
top-16
|
||||||
/* bottom-0 */
|
bottom-0
|
||||||
right-[max(2rem,calc(50%-40rem+2rem))]
|
right-[max(2rem,calc(50%-40rem+2rem))]
|
||||||
overflow-y-auto
|
overflow-y-auto
|
||||||
border-2
|
border-l
|
||||||
border-red-500
|
border-slate-800
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* toc title */
|
||||||
.toc::before {
|
.toc::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: "On this page";
|
content: "On this page";
|
||||||
@apply text-white text-xl font-semibold top-12
|
@apply text-white text-xl font-semibold top-1
|
||||||
}
|
|
||||||
|
|
||||||
.toc-item {
|
|
||||||
@apply leading-3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-item-h1 {
|
|
||||||
@apply p-0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* list (ol) element */
|
||||||
.toc-level {
|
.toc-level {
|
||||||
@apply list-none p-0;
|
@apply list-none p-0;
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +74,20 @@ body {
|
||||||
@apply pl-2
|
@apply pl-2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* list item (li) element */
|
||||||
|
.toc-item {
|
||||||
|
@apply leading-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-item-h1 {
|
||||||
|
@apply p-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link {
|
||||||
|
@apply transition-colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* link (a) element */
|
||||||
.toc-item .toc-link {
|
.toc-item .toc-link {
|
||||||
@apply text-sm text-slate-400 no-underline break-normal
|
@apply text-sm text-slate-400 no-underline break-normal
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue