* [packages][m]: mv @flowershow/core package here * [packages/core][xs]: rename to @portaljs/core * [package.json][xs]: setup npm workspaces * [packages/core][xs]:replace deprecated rollup executor * [core/package.json][s]: fix mermaid versions * [core/tsconfig][xs]: rm extends * [core/jest.config][xs]: rm coverageDirectory * [core/package.json][xs]: install core-js * [packages.json][s]:use same version for all nrwl packages * [core/.eslintrc][xs]: adjust ignorePatterns * [core/project.json][xs]: rm publish targets * [packages][m]: mv @flowershow/remark-wiki-link here * [packages][m]: mv @flowershow/remark-wiki-link here * [packages][m]: mv @flowershow/remark-embed here * [remark-callouts/project.json][xs]: adjst test pattern * [package.json][s]: install missing deps * [remark-callouts][xs]: adjst fields in package.json * [remark-callouts][s]: rm pubish targets and adjst build executor * [remark-embed/jest.config][xs]: rm unknown option coverageDirectory * [remark-embed][xs]: rm publish targets * [remark-embed][s]: rename to @portaljs/remark-embed * [remark-wiki-link/eslintrc][xs]:adjst ignorePatterns * [package.json][xs]: install missing deps * [remark-wiki-link/test][xs]:specify format - also temporarily force any type on htmlExtension * [remark-wiki-link/README][xs]: replace @flowershow with @portaljs * [remark-wiki-link][xs]:rm old changelog * [remark-wiki-link][xs]: adjst package.json * [remark-wiki-link/project.json][xs]: rm publish targets * [core][s]: rm old changelog * [core/README][xs]:correct scope name * [remark-callouts/README][xs]: add @portaljs to pckg name * [remark-embed/README][xs]: add @portaljs to pckg name * [package-lock.json][xs]: refresh after rebasing on main
148 lines
4.8 KiB
TypeScript
148 lines
4.8 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import Head from "next/head.js";
|
|
import { NextRouter, useRouter } from "next/router.js";
|
|
import clsx from "clsx";
|
|
|
|
import { useTableOfContents } from "./useTableOfContents";
|
|
import { collectHeadings } from "../../utils";
|
|
|
|
import { Nav } from "../Nav";
|
|
import { SiteToc, NavItem, NavGroup } from "../SiteToc";
|
|
import { Comments, CommentsConfig } from "../Comments";
|
|
import { Footer } from "./Footer";
|
|
import { EditThisPage } from "./EditThisPage";
|
|
import { TableOfContents, TocSection } from "./TableOfContents";
|
|
import { NavConfig, ThemeConfig } from "../Nav";
|
|
import { AuthorConfig } from "../types";
|
|
|
|
interface Props extends React.PropsWithChildren {
|
|
showComments: boolean;
|
|
showEditLink: boolean;
|
|
showSidebar: boolean;
|
|
showToc: boolean;
|
|
nav: NavConfig;
|
|
author: AuthorConfig;
|
|
theme: ThemeConfig;
|
|
urlPath: string;
|
|
commentsConfig: CommentsConfig;
|
|
siteMap: Array<NavItem | NavGroup>;
|
|
editUrl?: string;
|
|
}
|
|
|
|
export const Layout: React.FC<Props> = ({
|
|
children,
|
|
nav,
|
|
author,
|
|
theme,
|
|
showEditLink,
|
|
showToc,
|
|
showSidebar,
|
|
urlPath,
|
|
showComments,
|
|
commentsConfig,
|
|
editUrl,
|
|
siteMap,
|
|
}) => {
|
|
const [isScrolled, setIsScrolled] = useState(false);
|
|
const [tableOfContents, setTableOfContents] = useState<TocSection[]>([]);
|
|
const currentSection = useTableOfContents(tableOfContents);
|
|
const router: NextRouter = useRouter();
|
|
|
|
useEffect(() => {
|
|
if (!showToc) return;
|
|
const headingNodes: NodeListOf<HTMLHeadingElement> =
|
|
document.querySelectorAll("h1,h2,h3");
|
|
const toc = collectHeadings(headingNodes);
|
|
setTableOfContents(toc ?? []);
|
|
}, [router.asPath, showToc]); // update table of contents on route change with next/link
|
|
|
|
useEffect(() => {
|
|
function onScroll() {
|
|
setIsScrolled(window.scrollY > 0);
|
|
}
|
|
onScroll();
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
return () => {
|
|
window.removeEventListener("scroll", onScroll);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<Head>
|
|
<link
|
|
rel="icon"
|
|
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>💐</text></svg>"
|
|
/>
|
|
<meta charSet="utf-8" />
|
|
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
|
|
</Head>
|
|
<div className="min-h-screen bg-background dark:bg-background-dark">
|
|
{/* NAVBAR */}
|
|
<div
|
|
className={clsx(
|
|
"sticky top-0 z-50 w-full",
|
|
isScrolled
|
|
? "dark:bg-background-dark/95 bg-background/95 backdrop-blur [@supports(backdrop-filter:blur(0))]:dark:bg-background-dark/75"
|
|
: "dark:bg-background-dark bg-background"
|
|
)}
|
|
>
|
|
<div className="max-w-8xl mx-auto p-4 md:px-8">
|
|
<Nav
|
|
title={nav.title}
|
|
logo={nav.logo}
|
|
links={nav.links}
|
|
search={nav.search}
|
|
social={nav.social}
|
|
defaultTheme={theme.defaultTheme}
|
|
themeToggleIcon={theme.themeToggleIcon}
|
|
>
|
|
{showSidebar && <SiteToc currentPath={urlPath} nav={siteMap} />}
|
|
</Nav>
|
|
</div>
|
|
</div>
|
|
{/* wrapper for sidebar, main content and ToC */}
|
|
<div
|
|
className={clsx(
|
|
"max-w-8xl mx-auto px-4 md:px-8",
|
|
showSidebar && "lg:ml-[18rem]",
|
|
showToc && "xl:mr-[18rem]"
|
|
)}
|
|
>
|
|
{/* SIDEBAR */}
|
|
{showSidebar && (
|
|
<div className="hidden lg:block fixed z-20 w-[18rem] top-[4.6rem] right-auto bottom-0 left-[max(0px,calc(50%-44rem))] pt-8 pl-8 overflow-y-auto">
|
|
<SiteToc currentPath={urlPath} nav={siteMap} />
|
|
</div>
|
|
)}
|
|
{/* MAIN CONTENT & FOOTER */}
|
|
<main className="mx-auto pt-8">
|
|
{children}
|
|
{/* EDIT THIS PAGE LINK */}
|
|
{showEditLink && editUrl && <EditThisPage url={editUrl} />}
|
|
{/* PAGE COMMENTS */}
|
|
{showComments && (
|
|
<div
|
|
className="prose mx-auto pt-6 pb-6 text-center text-gray-700 dark:text-gray-300"
|
|
id="comment"
|
|
>
|
|
{<Comments commentsConfig={commentsConfig} slug={urlPath} />}
|
|
</div>
|
|
)}
|
|
</main>
|
|
<Footer links={nav.links} author={author} />
|
|
{/** TABLE OF CONTENTS */}
|
|
{showToc && tableOfContents.length > 0 && (
|
|
<div className="hidden xl:block fixed z-20 w-[18rem] top-[4.6rem] bottom-0 right-[max(0px,calc(50%-44rem))] left-auto pt-8 pr-8 overflow-y-auto">
|
|
<TableOfContents
|
|
tableOfContents={tableOfContents}
|
|
currentSection={currentSection}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|