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

Right Hand Side Table of Content #113

Closed
wants to merge 16 commits into from
Closed
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
10 changes: 10 additions & 0 deletions node_modules/.yarn-integrity

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

22 changes: 22 additions & 0 deletions site/components/Container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export default function Container({
children,
fluid = false,
flex = false,
wide = false,
flexReverse = false,
cssBreakingPoint = "md",
className = "",
}) {
const commonClassNames = `${flex ? `${cssBreakingPoint}:flex` : "block"} ${
flexReverse ? `${cssBreakingPoint}:flex-row-reverse ` : ""
} ${className} px-4 sm:px-6`;
const wideClassNames = `max-w-screen-xl ${commonClassNames}`;
const regularClassNames = `max-w-4xl ${commonClassNames}`;
const normalClassNames = `${
wide ? wideClassNames : regularClassNames
} mx-auto ${commonClassNames}`;
const fluidClassNames = `${commonClassNames}`;
return (
<div className={fluid ? fluidClassNames : normalClassNames}>{children}</div>
);
}
21 changes: 21 additions & 0 deletions site/components/ContentLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import Container from './Container'
import TOC from './Toc';

export default function ContentLayout({ post, children }) {
return (
<Container
cssBreakingPoint="lg"
flex
flexReverse
className="flex-row-reverse"
>
<TOC
toc={post.toc}
cssBreakingPoint="lg"
className="bg-slate-800 mt-4 p-4 sticky top-0 overflow-y-auto max-h-screen lg:bg-transparent lg:mt-0 lg:pt-0 lg:pb-8 lg:top-4 lg:max-h-(screen-16) lg:border-l lg:border-gray-800 lg:min-w-40 lg:w-5/12 w-full lg:-mr-20 xl:min-w-72 xl:-mr-36"
/>
{children}
</Container>
);
}
89 changes: 66 additions & 23 deletions site/components/MDX.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,73 @@
import Head from 'next/head'
import { Paragraph } from './Link'
import { useRouter } from "next/router";
import { useState, useEffect } from "react";
import Head from "next/head";
import ContentLayout from "./ContentLayout";
import { Paragraph } from "./Link";
import { getPostBySlug } from "../lib/api";

const CustomHeading = ({ as, ...rest }) => {
const [value, setValue] = useState("");
const { asPath } = useRouter();
const post = getPostBySlug(asPath.split("#")[0]);
const toc = post.toc;
useEffect(() => {
toc.map((t) => {
if (t.content === rest.children) {
setValue(t.slug);
}
});
}, []);
if (as === "h2") {
return <h2 id={value} {...rest} />;
} else if (as === "h3") {
return <h2 id={value} {...rest} />;
} else if (as === "h4") {
return <h4 id={value} {...rest} />;
} else if (as === "h5") {
return <h5 id={value} {...rest} />;
} else if (as === "h6") {
return <h6 id={value} {...rest} />;
}
return <h1 id={value} {...rest} />;
};

const components = {
Head,
p: Paragraph
}
p: Paragraph,
h1: (props) => <CustomHeading as="h1" {...props} />,
h2: (props) => <CustomHeading as="h2" {...props} />,
h3: (props) => <CustomHeading as="h3" {...props} />,
h4: (props) => <CustomHeading as="h4" {...props} />,
h5: (props) => <CustomHeading as="h5" {...props} />,
h6: (props) => <CustomHeading as="h6" {...props} />,
};

export default function MdxPage({ children }) {
const { Component, frontmatter } = children

const { asPath } = useRouter();
const { Component, frontmatter } = children;
const post = getPostBySlug(asPath.split("#")[0]);
return (
<article className="prose dark:prose-invert mx-auto p-6">
<header>
<div className="mb-6">
<h1>{frontmatter.title}</h1>
{frontmatter.authors && (
<div className="-mt-6"><p className="opacity-60 pl-1">{frontmatter.authors}</p></div>
)}
{frontmatter.description && (
<p className="description">{frontmatter.description}</p>
)}
</div>
</header>
<main>
<Component components={components} />
</main>
</article>
)
<div className="flex justify-center">
<article className="prose dark:prose-invert p-6 min-w-full ">
<header>
<div className="mb-6">
<h1>{frontmatter.title}</h1>
{frontmatter.authors && (
<div className="-mt-6">
<p className="opacity-60 pl-1">{frontmatter.authors}</p>
</div>
)}
{frontmatter.description && (
<p className="description">{frontmatter.description}</p>
)}
</div>
</header>
<ContentLayout post={post}>
<main className="lg:w-full p-4">
<Component components={components} />
</main>
</ContentLayout>
</article>
</div>
);
}
74 changes: 74 additions & 0 deletions site/components/Toc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useState } from "react";
import Scrollspy from "react-scrollspy";
import ArrowRight from "./icons/ArrowRight";

export default function TOC({
className,
cssBreakingPoint = "xl",
toc,
contentSelector,
depth = 2,
}) {
if (!toc || !toc.length) return null;
const minLevel = toc.reduce((mLevel, item) => (!mLevel || item.lvl < mLevel) ? item.lvl : mLevel, 0)
const tocItems = toc.filter(item => item.lvl <= minLevel + depth).map(item => ({
...item,
content: item.content.replace(/[\s]?\{\#[\w\d\-_]+\}$/, '').replace(/(<([^>]+)>)/gi, ''),
//For TOC rendering in specification files in the spec repo we have "a" tags added manually to the spec markdown document
//MDX takes these "a" tags and uses them to render the "id" for headers like a-namedefinitionsapplicationaapplication
//slugWithATag contains transformed heading name that is later used for scroll spy identification
slugWithATag: item.content.replace(/<|>|"|\\|\/|=/gi, '').replace(/\s/gi, '-').toLowerCase()
}))
const [open, setOpen] = useState(true);
return (
<div
className={`${className} ${
tocItems.length ? "" : "hidden"
} ${cssBreakingPoint}:block z-20 w-full`}
>
<div
className={`flex cursor-pointer ${
tocItems.length ? "" : "hidden"
} ${cssBreakingPoint}:cursor-auto`}
onClick={() => setOpen(!open)}
>
<h5
className={`${
open && "mb-4"
} flex-1 text-primary-500 font-medium uppercase tracking-wide text-sm font-sans antialiased ${cssBreakingPoint}:mb-4 ${cssBreakingPoint}:text-xs ${cssBreakingPoint}:text-gray-500 ${cssBreakingPoint}:font-thin`}
>
On this page
</h5>
<div
className={`text-underline text-center p4 ${cssBreakingPoint}:hidden`}
>
<ArrowRight
className={`${
open ? "-rotate-90" : "rotate-90"
} transform transition duration-200 ease-in-out h-6 -mt-0.5 text-primary-500`}
/>
</div>
</div>
<div className={`${!open && "hidden"} ${cssBreakingPoint}:block`}>
<Scrollspy
items={tocItems.map((item) => item.slugWithATag)}
currentClassName="text-base font-extrabold text-yellow-500"
componentTag="div"
rootEl={contentSelector}
>
{tocItems.map((item, index) => (
<a
key={index}
className={`pl-${
(item.lvl - minLevel) * 2
} block mb-1 transition duration-100 ease-in-out no-underline font-normal text-sm font-sans antialiased hover:underline`}
href={`#${item.slug}`}
>
{item.content}
</a>
))}
</Scrollspy>
</div>
</div>
);
}
7 changes: 7 additions & 0 deletions site/components/icons/ArrowRight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function IconArrowRight ({ className }) {
return (
<svg className={className || 'inline-block'} fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
</svg>
)
}
Loading