datahub/site/components/Layout.tsx
João Demenech a954575397
Website v0.4 (#860)
* [#858,site][xl]: add Examples to the Navbar, rename gallery to showcases, remove examples from showcases, move github stars to the navbar, add view on github button to the hero section, reduce padding on buttons, add RHS image to the hero

* [#858,site][xl]: make sidebar consistent on all pages

* [site][xs]: fix ts error on GitHub button component

* [site][xs]: fix external links on navbar needing two clicks to open

* [site, hero][xs]: align RHS image to the top
2023-05-09 14:39:23 -03:00

177 lines
6.1 KiB
TypeScript

import { siteConfig } from '@/config/siteConfig';
import { NextSeo } from 'next-seo';
import { useTheme } from 'next-themes';
import Link from 'next/link';
import { useCallback, useEffect, useState } from 'react';
import Nav from './Nav';
import { Hero } from './Hero';
import { Navigation } from './Navigation';
function useTableOfContents(tableOfContents) {
const [currentSection, setCurrentSection] = useState(tableOfContents[0]?.id);
const getHeadings = useCallback((toc) => {
return toc
.flatMap((node) => [node.id, ...node.children.map((child) => child.id)])
.map((id) => {
const el = document.getElementById(id);
if (!el) return null;
const style = window.getComputedStyle(el);
const scrollMt = parseFloat(style.scrollMarginTop);
const top = window.scrollY + el.getBoundingClientRect().top - scrollMt;
return { id, top };
})
.filter((el) => !!el);
}, []);
useEffect(() => {
if (tableOfContents.length === 0) return;
const headings = getHeadings(tableOfContents);
function onScroll() {
const top = window.scrollY + 4.5;
let current = headings[0].id;
headings.forEach((heading) => {
if (top >= heading.top) {
current = heading.id;
}
return current;
});
setCurrentSection(current);
}
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
return () => {
window.removeEventListener('scroll', onScroll);
};
}, [getHeadings, tableOfContents]);
return currentSection;
}
export default function Layout({
children,
title,
tableOfContents = [],
isHomePage = false,
sidebarTree = [],
}: {
children;
title?: string;
tableOfContents?;
urlPath?: string;
sidebarTree?: [];
isHomePage?: boolean;
}) {
// const { toc } = children.props;
const { theme, setTheme } = useTheme();
const currentSection = useTableOfContents(tableOfContents);
function isActive(section) {
if (section.id === currentSection) {
return true;
}
if (!section.children) {
return false;
}
return section.children.findIndex(isActive) > -1;
}
return (
<>
{title && <NextSeo title={title} />}
<Nav />
<div className="mx-auto p-6 bg-background dark:bg-background-dark">
{isHomePage && <Hero />}
<div className="relative mx-auto flex max-w-8xl justify-center sm:px-2 lg:px-8 xl:px-12">
{!!sidebarTree.length && (
<div className="hidden lg:relative lg:block lg:flex-none">
<div className="absolute inset-y-0 right-0 w-[50vw] bg-slate-50 dark:hidden" />
<div className="absolute bottom-0 right-0 top-16 hidden h-12 w-px bg-gradient-to-t from-slate-800 dark:block" />
<div className="absolute bottom-0 right-0 top-28 hidden w-px bg-slate-800 dark:block" />
<div className="sticky top-[4.5rem] -ml-0.5 h-[calc(100vh-4.5rem)] overflow-y-auto overflow-x-hidden py-16 pl-0.5">
<Navigation
navigation={sidebarTree}
className="w-64 pr-8 xl:w-72 xl:pr-16"
/>
</div>
</div>
)}
<div className="min-w-0 max-w-2xl flex-auto px-4 py-16 lg:max-w-none lg:pl-8 lg:pr-0 ">
{children}
</div>
{/** TABLE OF CONTENTS */}
<div className="hidden xl:sticky xl:right-0 xl:top-[4.5rem] xl:block xl:h-[calc(100vh-4.5rem)] xl:flex-none xl:overflow-y-auto xl:py-16 xl:mb-16">
{tableOfContents.length > 0 && siteConfig.tableOfContents && (
<nav aria-labelledby="on-this-page-title" className="w-56">
<h2 className="font-display text-sm font-medium text-primary dark:text-primary-dark">
On this page
</h2>
<ol className="mt-4 space-y-4 text-sm">
{tableOfContents.map((section) => (
<li key={section.id}>
<h3>
<Link
href={`#${section.id}`}
className={
isActive(section)
? 'text-secondary font-semibold'
: 'font-normal text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-300'
}
>
{section.title}
</Link>
</h3>
{section.children && section.children.length > 0 && (
<ol className="mt-4 space-y-4 pl-5 text-slate-500 dark:text-slate-400">
{section.children.map((subSection) => (
<li key={subSection.id}>
<Link
href={`#${subSection.id}`}
className={
isActive(subSection)
? 'text-secondary font-semibold'
: 'hover:text-slate-600 dark:hover:text-slate-300'
}
>
{subSection.title}
</Link>
</li>
))}
</ol>
)}
</li>
))}
</ol>
</nav>
)}
</div>
</div>
</div>
<footer className="flex items-center justify-center w-full h-24 border-t dark:border-slate-900 bg-background dark:bg-background-dark">
<a
className="flex items-center justify-center"
href="https://datopian.com/"
target="_blank"
rel="noopener noreferrer"
>
Built by{' '}
<img
src={
theme === 'dark'
? '/images/datopian-light-logotype.svg'
: '/images/datopian-dark-logotype.svg'
}
alt="Datopian Logo"
className="h-6 ml-2"
/>
</a>
</footer>
</>
);
}