Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add TOC + site changes #655

Merged
merged 1 commit into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@typescript-eslint/eslint-plugin": "7.16.1",
"@typescript-eslint/parser": "7.16.1",
"autoprefixer": "^10.4.19",
"clsx": "^2.1.1",
"eslint": "8.57.0",
"eslint-plugin-qwik": "^2.0.0-alpha.4",
"postcss": "^8.4.39",
Expand All @@ -44,6 +45,7 @@
"rehype-pretty-code": "^0.14.0",
"sass": "^1.83.0",
"shiki": "^1.24.3",
"tailwind-merge": "^2.5.5",
"tailwindcss": "^3.4.6",
"typescript": "5.4.5",
"unist-util-visit": "^5.0.0",
Expand Down
17 changes: 17 additions & 0 deletions docs/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/src/components/Aside/Aside.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const Aside = component$(() => {

return (
<div
class={`fixed top-20 mt-8 flex h-[calc(100%-12rem)] flex-col overflow-hidden overflow-y-auto px-6 text-xl text-black dark:text-white`}
class={`fixed top-20 mt-8 flex h-full flex-col overflow-hidden overflow-y-auto px-6 text-black dark:text-white`}
>
{(menu?.items || []).map(({ text, items }, idx) => {
const [title, href] = text.split("|");
Expand All @@ -15,7 +15,7 @@ export const Aside = component$(() => {
<li>
{href ? (
<a
class="mb-2 block rounded bg-blue-700 px-4 py-1 text-base font-bold uppercase text-white no-underline"
class="mb-2 block rounded bg-blue-700 dark:bg-blue-600 px-4 py-1 text-base font-bold uppercase text-white no-underline"
href={href}
>
{title}
Expand Down
2 changes: 1 addition & 1 deletion docs/src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { GitHubIcon } from "../Icons/GitHubIcon";

export const Footer = component$(() => {
return (
<footer class="flex border-t-[2px] border-slate-200 bg-white px-6 py-4 dark:border-slate-800 dark:bg-black">
<footer class="flex border-t-[2px] border-slate-200 px-6 py-4 dark:border-slate-800 bg-[#F8F8FF] dark:bg-[#0D0F12] mt-6">
<div class="grid w-full grid-cols-12">
<div class="col-span-4" />
<div class="col-span-4" />
Expand Down
4 changes: 2 additions & 2 deletions docs/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export const Header = component$<Props>(({ links = [], showMenu = true }) => {
const showAsideSig = useSignal(false);
return (
<header
class={`fixed top-0 z-10 h-20 w-full border-b-[2px] border-slate-200 bg-white dark:border-slate-800 dark:bg-black`}
class={`fixed top-0 z-10 h-20 w-full border-b-[2px] border-zinc-100 bg-[#F8F8FF] dark:border-zinc-900 dark:bg-[#0D0F12]`}
>
<div class="grid h-full max-w-[1200px] grid-cols-12 px-6">
<div class="grid h-full max-w-[1376px] grid-cols-12 px-6">
<div class="col-span-3 flex items-center sm:col-span-4">
{showMenu && (
<button
Expand Down
2 changes: 1 addition & 1 deletion docs/src/components/MdxComponents/MdxComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const components: Record<string, any> = {
() => {
return (
<div class="relative">
<pre class="mb-4 mt-6 max-h-[650px] overflow-x-auto rounded-lg border bg-zinc-950 p-6 text-white dark:bg-zinc-900">
<pre class="mb-4 mt-6 max-h-[650px] overflow-x-auto rounded-lg bg-zinc-950 p-6 text-white dark:bg-zinc-950">
<Slot />
</pre>
</div>
Expand Down
217 changes: 189 additions & 28 deletions docs/src/components/Toc/Toc.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,192 @@
import { component$, useSignal } from "@qwik.dev/core";
import { useContent } from "@qwik.dev/router";
import { cn } from "~/utils/cn";
import { component$, useSignal, $, useOnWindow } from '@qwik.dev/core';
import { ContentHeading } from '@qwik.dev/router';

export const Toc = component$(() => {
const { headings } = useContent();
const lastScrollIdSig = useSignal("");
return (
<div class="fixed flex w-full flex-col px-6 pt-28 text-xl text-black dark:text-white">
<span class="mb-2 text-lg font-bold uppercase">On this page</span>
{(headings || []).map(({ text, id }, idx) => (
<ul
key={idx}
class="border-l-[4px] py-1 pl-4 hover:border-blue-700"
onMouseOver$={() => {
if (lastScrollIdSig.value !== id) {
const el = document.querySelector(`#${id}`);
if (el) {
el.scrollIntoView({ behavior: "smooth" });
lastScrollIdSig.value = id;
}
}
}}
>
<li class="text-base hover:text-blue-500">
<a href={`#${id}`}>{text}</a>
export const TOC = component$(
({ headings }: { headings: ContentHeading[] }) => {
if (headings.length === 0) {
return null;
}
return (
<div class="space-y-2 sticky top-24 max-h-[calc(80vh)] p-1 dark:text-white text-black hidden xl:block">
<div class="font-medium">On This Page</div>
<TableOfContents headings={headings} />
</div>
);
},
);

type TableOfContentsProps = { headings: ContentHeading[] };

interface Node extends ContentHeading {
children: Node[];
activeItem: string;
}
type Tree = Array<Node>;

const TableOfContents = component$<TableOfContentsProps>(({ headings }) => {
const sanitizedHeadings = headings.map(({ text, id, level }) => ({ text, id, level }));
const itemIds = headings.map(({ id }) => id);
const activeHeading = useActiveItem(itemIds);
const tree = buildTree(sanitizedHeadings);
const fixStartingBug: Node = { ...tree, children: [tree] };
return <RecursiveList tree={fixStartingBug} activeItem={activeHeading.value ?? ''} />;
});

function deltaToStrg(
currNode: Node,
nextNode: Node,
): 'same level' | 'down one level' | 'up one level' | 'upwards discontinuous' {
const delta = currNode.level - nextNode.level;
if (delta > 1) {
return 'upwards discontinuous';
}
if (delta === 1) {
return 'up one level';
}
if (delta === 0) {
return 'same level';
}
if (delta === -1) {
return 'down one level';
}

throw new Error(
`bad headings: are downwards discontinous from: #${currNode.id} to #${nextNode.id} bc from ${currNode.level} to ${nextNode.level}`,
);
}

function buildTree(nodes: ContentHeading[]) {
let currNode = nodes[0] as Node;
currNode.children = [];
const tree = [currNode];
const childrenMap = new Map<number, Tree>();
childrenMap.set(currNode.level, currNode.children);
for (let index = 1; index < nodes.length; index++) {
const nextNode = nodes[index] as Node;
nextNode.children = [];
childrenMap.set(nextNode.level, nextNode.children);
const deltaStrg = deltaToStrg(currNode, nextNode);
switch (deltaStrg) {
case 'upwards discontinuous': {
const delta = currNode.level - nextNode.level;
if (childrenMap.has(delta - 1)) {
const nthParent = childrenMap.get(delta - 1);
nthParent?.push(nextNode);
}
break;
}
case 'up one level': {
const grandParent = childrenMap.get(currNode.level - 2);
grandParent?.push(nextNode);
break;
}
case 'same level': {
const parent = childrenMap.get(currNode.level - 1);
parent?.push(nextNode);
break;
}
case 'down one level': {
currNode.children.push(nextNode);
break;
}
default:
break;
}
currNode = nextNode;
}
return tree[0];
}

type RecursiveListProps = {
tree: Node;
activeItem: string;
limit?: number;
};

const RecursiveList = component$<RecursiveListProps>(
({ tree, activeItem, limit = 3 }) => {
return tree?.children?.length && tree.level < limit ? (
<ul class={cn('m-0 list-none', { 'pl-4': tree.level !== 1 })}>
{tree.children.map((childNode) => (
<li key={childNode.id} class="mt-0 list-none pt-2">
<Anchor node={childNode} activeItem={activeItem} />
{childNode.children.length > 0 && (
<RecursiveList tree={childNode} activeItem={activeItem} />
)}
</li>
</ul>
))}
</div>
))}
</ul>
) : null;
},
);

const useActiveItem = (itemIds: string[]) => {
const activeId = useSignal<string>();

useOnWindow(
'scroll',
$(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
activeId.value = entry.target.id;
}
});
},
{ rootMargin: '0% 0% -85% 0%' },
);

itemIds.forEach((id) => {
const element = document.getElementById(id);
if (element) {
observer.observe(element);
}
});

return () => {
itemIds.forEach((id) => {
const element = document.getElementById(id);
if (element) {
observer.unobserve(element);
}
});
};
}),
);
});

return activeId;
};

type AnchorProps = {
node: Node;
activeItem: string;
};

const Anchor = component$<AnchorProps>(({ node, activeItem }) => {
const isActive = node.id === activeItem;
return (
<a
href={`#${node.id}`}
onClick$={[
$(() => {
const element = document.getElementById(node.id);
if (element) {
const navbarHeight = 90;
const position =
element.getBoundingClientRect().top + window.scrollY - navbarHeight;
window.scrollTo({ top: position, behavior: 'auto' });
}
}),
]}
class={cn(
node.level > 2 && 'ml-2',
'inline-block no-underline transition-colors',
isActive ? 'text-blue-500 dark:text-blue-300' : 'text-muted-foreground',
)}
>
{node.text}
</a>
);
});
9 changes: 5 additions & 4 deletions docs/src/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@
*/

html {
height: 100%;
&.dark {
@apply bg-black;
}
@apply bg-[#F8F8FF];
}

.dark {
@apply bg-[#0D0F12]
}

body {
Expand Down
2 changes: 1 addition & 1 deletion docs/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default component$(() => {
)}
<RouterHead />
</head>
<body lang="en" class="m-auto max-w-[1200px]">
<body lang="en" class="m-auto max-w-[1376px]">
<RouterOutlet />
{!isDev && <ServiceWorkerRegister />}
</body>
Expand Down
Loading
Loading