feat(freesewing.dev): ToC and layout changes
This commit is contained in:
parent
fbbad22dcc
commit
6c41b1d941
17 changed files with 478 additions and 94 deletions
|
@ -55,7 +55,7 @@ const Header = ({ app, setSearch }) => {
|
||||||
text-neutral-content bg-transparent
|
text-neutral-content bg-transparent
|
||||||
border border-transparent
|
border border-transparent
|
||||||
hover:bg-transparent hover:border-base-100
|
hover:bg-transparent hover:border-base-100
|
||||||
sm:hidden
|
md:hidden
|
||||||
h-12
|
h-12
|
||||||
`}
|
`}
|
||||||
onClick={app.togglePrimaryMenu}>
|
onClick={app.togglePrimaryMenu}>
|
||||||
|
@ -79,14 +79,14 @@ const Header = ({ app, setSearch }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
<div className="flex flex-row gap-2 sm:hidden">
|
<div className="flex flex-row gap-2 md:hidden">
|
||||||
<button className="btn btn-sm btn h-12 px-12" onClick={() => setSearch(true)}>
|
<button className="btn btn-sm btn h-12 px-12" onClick={() => setSearch(true)}>
|
||||||
<SearchIcon />
|
<SearchIcon />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button className={`
|
<button className={`
|
||||||
btn btn-sm h-12
|
btn btn-sm h-12
|
||||||
hidden sm:flex
|
hidden md:flex
|
||||||
flex-row gap-1 mr-4 w-64 px-2
|
flex-row gap-1 mr-4 w-64 px-2
|
||||||
bg-base-100 text-base-content
|
bg-base-100 text-base-content
|
||||||
hover:bg-base-100 hover:text-base-content
|
hover:bg-base-100 hover:text-base-content
|
||||||
|
@ -110,7 +110,7 @@ const Header = ({ app, setSearch }) => {
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden sm:flex flex-row items-center">
|
<div className="hidden md:flex flex-row items-center">
|
||||||
<ThemePicker app={app} />
|
<ThemePicker app={app} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
150
packages/freesewing.dev/components/layouts/docs.js
Normal file
150
packages/freesewing.dev/components/layouts/docs.js
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import Link from 'next/link'
|
||||||
|
// Shared components
|
||||||
|
import Logo from 'shared/components/logos/freesewing.js'
|
||||||
|
import PrimaryNavigation from 'shared/components/navigation/primary'
|
||||||
|
import get from 'lodash.get'
|
||||||
|
import Right from 'shared/components/icons/right.js'
|
||||||
|
import Left from 'shared/components/icons/left.js'
|
||||||
|
// Site components
|
||||||
|
import Header from 'site/components/header'
|
||||||
|
import Footer from 'site/components/footer'
|
||||||
|
import Search from 'site/components/search'
|
||||||
|
|
||||||
|
const PageTitle = ({ app, slug, title }) => {
|
||||||
|
if (title) return <h1>{title}</h1>
|
||||||
|
if (slug) return <h1>{get(app.navigation, slug.split('/')).__title}</h1>
|
||||||
|
|
||||||
|
return <h1>FIXME: This page has no title</h1>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Breadcrumbs = ({ app, slug=false, title }) => {
|
||||||
|
if (!slug) return null
|
||||||
|
const crumbs = []
|
||||||
|
const chunks = slug.split('/')
|
||||||
|
for (const i in chunks) {
|
||||||
|
const j = parseInt(i)+parseInt(1)
|
||||||
|
const page = get(app.navigation, chunks.slice(0,j))
|
||||||
|
if (page) crumbs.push([page.__linktitle, '/'+chunks.slice(0,j).join('/'), (j < chunks.length)])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="flex flex-row flex-wrap gap-2 font-bold">
|
||||||
|
<li>
|
||||||
|
<Link href="/">
|
||||||
|
<a title="To the homepage" className="text-base-content">
|
||||||
|
<Logo size={24} fill="currentColor" stroke={false}/>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
{crumbs.map(crumb => (
|
||||||
|
<React.Fragment key={crumb[1]}>
|
||||||
|
<li className="text-base-content">»</li>
|
||||||
|
<li>
|
||||||
|
{crumb[2]
|
||||||
|
? (
|
||||||
|
<Link href={crumb[1]}>
|
||||||
|
<a title={crumb[0]} className="text-secondary hover:text-secondary-focus">
|
||||||
|
{crumb[0]}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
: <span className="text-base-content">{crumb[0]}</span>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultLayout = ({
|
||||||
|
app,
|
||||||
|
title=false,
|
||||||
|
children=[],
|
||||||
|
search,
|
||||||
|
setSearch,
|
||||||
|
noSearch=false,
|
||||||
|
workbench=false,
|
||||||
|
AltMenu=null,
|
||||||
|
}) => {
|
||||||
|
const startNavigation = () => {
|
||||||
|
app.startLoading()
|
||||||
|
// Force close of menu on mobile if it is open
|
||||||
|
if (app.primaryNavigation) app.setPrimaryNavigation(false)
|
||||||
|
// Force close of search modal if it is open
|
||||||
|
if (search) setSearch(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
router.events?.on('routeChangeStart', startNavigation)
|
||||||
|
router.events?.on('routeChangeComplete', () => app.stopLoading())
|
||||||
|
const slug = router.asPath.slice(1)
|
||||||
|
const [collapsePrimaryNav, setCollapsePrimaryNav] = useState(workbench || false)
|
||||||
|
const [collapseAltMenu, setCollapseAltMenu] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`
|
||||||
|
flex flex-col justify-between
|
||||||
|
min-h-screen
|
||||||
|
bg-base-100
|
||||||
|
`}>
|
||||||
|
<Header app={app} setSearch={setSearch} />
|
||||||
|
<main className="grow">
|
||||||
|
<div className="m-auto flex flex-row justify-center">
|
||||||
|
|
||||||
|
<aside className={`
|
||||||
|
fixed top-0 right-0 h-screen w-screen
|
||||||
|
overflow-y-auto z-20
|
||||||
|
bg-base-100 text-base-content md:bg-base-50
|
||||||
|
transition-all
|
||||||
|
${app.primaryMenu ? '' : 'translate-x-[-100%]'} transition-transform
|
||||||
|
md:flex md:sticky md:flex-row-reverse
|
||||||
|
md:relative md:transform-none
|
||||||
|
px-8 pt-24
|
||||||
|
|
||||||
|
md:w-40
|
||||||
|
lg:w-1/2 lg:min-w-80 lg:pr-2 lg:border-r-2
|
||||||
|
xl:w-1/3 xl:min-w-80 xl:pr-2 xl:border-0
|
||||||
|
2xl:min-w-96 2xl:pr-8
|
||||||
|
`}>
|
||||||
|
<PrimaryNavigation app={app} active={slug}/>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<section className="md:py-36 max-w-2/3">
|
||||||
|
<div>
|
||||||
|
{title && (
|
||||||
|
<div className="px-0 xl:pl-8 2xl:pl-16">
|
||||||
|
<Breadcrumbs app={app} slug={slug} title={title} />
|
||||||
|
<PageTitle app={app} slug={slug} title={title} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
{!noSearch && search && (
|
||||||
|
<>
|
||||||
|
<div className={`
|
||||||
|
fixed w-full max-h-screen bg-base-100 top-0 z-30 pt-0 pb-16 px-8
|
||||||
|
md:rounded-lg md:top-24
|
||||||
|
md:max-w-xl md:m-auto md:inset-x-12
|
||||||
|
md:max-w-2xl
|
||||||
|
lg:max-w-4xl
|
||||||
|
`}>
|
||||||
|
<Search app={app} search={search} setSearch={setSearch}/>
|
||||||
|
</div>
|
||||||
|
<div className="fixed top-0 left-0 w-full min-h-screen bg-neutral z-20 bg-opacity-70"></div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Footer app={app} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DefaultLayout
|
65
packages/freesewing.dev/components/wrappers/page.js
Normal file
65
packages/freesewing.dev/components/wrappers/page.js
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import { useSwipeable } from 'react-swipeable'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
|
// Layouts components
|
||||||
|
import Docs from 'site/components/layouts/docs'
|
||||||
|
|
||||||
|
const layouts = {
|
||||||
|
docs: Docs,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This component should wrap all page content */
|
||||||
|
const PageWrapper= ({
|
||||||
|
title="FIXME: No title set",
|
||||||
|
noSearch=false,
|
||||||
|
app=false,
|
||||||
|
layout=false,
|
||||||
|
children=[]
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const swipeHandlers = useSwipeable({
|
||||||
|
onSwipedLeft: evt => (app.primaryMenu) ? app.setPrimaryMenu(false) : null,
|
||||||
|
onSwipedRight: evt => (app.primaryMenu) ? null : app.setPrimaryMenu(true),
|
||||||
|
trackMouse: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const slug = router.asPath.slice(1)
|
||||||
|
|
||||||
|
useEffect(() => app.setSlug(slug), [slug])
|
||||||
|
|
||||||
|
// Trigger search with Ctrl+k
|
||||||
|
useHotkeys('ctrl+k', (evt) => {
|
||||||
|
evt.preventDefault()
|
||||||
|
setSearch(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
const [search, setSearch] = useState(false)
|
||||||
|
|
||||||
|
const childProps = {
|
||||||
|
app: app,
|
||||||
|
title: title,
|
||||||
|
search, setSearch, toggleSearch: () => setSearch(!search),
|
||||||
|
noSearch: noSearch,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Layout = layouts[layout]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={swipeHandlers.ref}
|
||||||
|
onMouseDown={swipeHandlers.onMouseDown}
|
||||||
|
data-theme={app.theme}
|
||||||
|
key={app.theme} // Thiis forces the data-theme update
|
||||||
|
>
|
||||||
|
{layout
|
||||||
|
? <Layout {...childProps}>{children}</Layout>
|
||||||
|
: children
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PageWrapper
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import Page from 'shared/components/wrappers/page.js'
|
import Page from 'site/components/wrappers/page.js'
|
||||||
import useApp from 'site/hooks/useApp.js'
|
import useApp from 'site/hooks/useApp.js'
|
||||||
import mdxMeta from 'site/prebuild/mdx.en.js'
|
import mdxMeta from 'site/prebuild/mdx.en.js'
|
||||||
import mdxLoader from 'shared/mdx/loader'
|
import mdxLoader from 'shared/mdx/loader'
|
||||||
import MdxWrapper from 'shared/components/wrappers/mdx'
|
import MdxWrapper from 'shared/components/wrappers/mdx'
|
||||||
|
import TocWrapper from 'shared/components/wrappers/toc'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import HelpUs from 'site/components/help-us.js'
|
import HelpUs from 'site/components/help-us.js'
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
|
@ -22,7 +23,7 @@ const MdxPage = props => {
|
||||||
* active state
|
* active state
|
||||||
*/
|
*/
|
||||||
return (
|
return (
|
||||||
<Page app={app} {...props.page}>
|
<Page app={app} {...props.page} layout="docs">
|
||||||
<Head>
|
<Head>
|
||||||
<meta property="og:title" content={props.page.title} key="title" />
|
<meta property="og:title" content={props.page.title} key="title" />
|
||||||
<meta property="og:type" content="article" key='type' />
|
<meta property="og:type" content="article" key='type' />
|
||||||
|
@ -36,8 +37,17 @@ const MdxPage = props => {
|
||||||
<meta property="og:locale" content="en_US" key='locale' />
|
<meta property="og:locale" content="en_US" key='locale' />
|
||||||
<meta property="og:site_name" content="freesewing.dev" key='site' />
|
<meta property="og:site_name" content="freesewing.dev" key='site' />
|
||||||
</Head>
|
</Head>
|
||||||
|
<div className="flex flex-row-reverse flex-wrap xl:flex-nowrap justify-end px-8 xl:px-0">
|
||||||
|
{props.toc && (
|
||||||
|
<div className="mb-8 px-0 w-full xl:w-80 2xl:w-96 xl:pl-8 2xl:pl-16">
|
||||||
|
<TocWrapper toc={props.toc} app={app}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="px-0 xl:pl-8 2xl:pl-16">
|
||||||
<MdxWrapper mdx={props.mdx} app={app}/>
|
<MdxWrapper mdx={props.mdx} app={app}/>
|
||||||
<HelpUs mdx slug={`/${props.page.slug}`} />
|
<HelpUs mdx slug={`/${props.page.slug}`} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -61,11 +71,12 @@ export default MdxPage
|
||||||
*/
|
*/
|
||||||
export async function getStaticProps({ params, locale }) {
|
export async function getStaticProps({ params, locale }) {
|
||||||
|
|
||||||
const { mdx, intro } = await mdxLoader('en', 'dev', params.mdxslug.join('/'))
|
const { mdx, intro, toc } = await mdxLoader('en', 'dev', params.mdxslug.join('/'))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
mdx,
|
mdx,
|
||||||
|
toc,
|
||||||
intro: intro.join(' '),
|
intro: intro.join(' '),
|
||||||
page: {
|
page: {
|
||||||
slug: params.mdxslug.join('/'),
|
slug: params.mdxslug.join('/'),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* Sourced from heroicons.com - Thanks guys! */
|
/* Sourced from heroicons.com - Thanks guys! */
|
||||||
const Docs = () => (
|
const Docs = ({ className="h-6 w-6" }) => (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const Guides = () => (
|
const Guides = ({ className="h-6 w-6" }) => (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 3L3 10.53v.98l6.84 2.65L12.48 21h.98L21 3z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 3L3 10.53v.98l6.84 2.65L12.48 21h.98L21 3z" />
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* Sourced from heroicons.com - Thanks guys! */
|
/* Sourced from heroicons.com - Thanks guys! */
|
||||||
const Help = () => (
|
const Help = ({ className="w-6 h-6" }) => (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* Sourced from heroicons.com - Thanks guys! */
|
/* Sourced from heroicons.com - Thanks guys! */
|
||||||
const Rss = () => (
|
const Rss = ({ className='h-6 w-6' }) => (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 5c7.18 0 13 5.82 13 13M6 11a7 7 0 017 7m-6 0a1 1 0 11-2 0 1 1 0 012 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 5c7.18 0 13 5.82 13 13M6 11a7 7 0 017 7m-6 0a1 1 0 11-2 0 1 1 0 012 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const Tutorial = () => (
|
const Tutorial = ({ className="w-6 h-6" }) => (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path d="M12 14l9-5-9-5-9 5 9 5z" />
|
<path d="M12 14l9-5-9-5-9 5 9 5z" />
|
||||||
<path d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z" />
|
<path d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z" />
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222" />
|
||||||
|
|
|
@ -74,8 +74,9 @@ const asideClasses = `
|
||||||
overflow-y-scroll
|
overflow-y-scroll
|
||||||
z-20
|
z-20
|
||||||
bg-base-100 text-base-content
|
bg-base-100 text-base-content
|
||||||
sm:bg-neutral sm:bg-opacity-95 sm:text-neutral-content
|
transition-all
|
||||||
transition-all `
|
xl:w-1/4
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
const DefaultLayout = ({
|
const DefaultLayout = ({
|
||||||
|
@ -110,19 +111,18 @@ const DefaultLayout = ({
|
||||||
bg-base-100
|
bg-base-100
|
||||||
`}>
|
`}>
|
||||||
<Header app={app} setSearch={setSearch} />
|
<Header app={app} setSearch={setSearch} />
|
||||||
<main className={`
|
<main className="grow bg-base-100">
|
||||||
grow flex flex-row
|
<div className="m-auto flex flex-row justify-center">
|
||||||
gap-2
|
|
||||||
${!workbench ? 'lg:gap-8 xl:gap-16' : ''}
|
|
||||||
`}>
|
|
||||||
<aside className={`
|
<aside className={`
|
||||||
${asideClasses}
|
${asideClasses}
|
||||||
${app.primaryMenu ? '' : 'translate-x-[-100%]'} transition-transform
|
${app.primaryMenu ? '' : 'translate-x-[-100%]'} transition-transform
|
||||||
sm:flex-row-reverse
|
sm:flex-row-reverse
|
||||||
${workbench && collapsePrimaryNav
|
${workbench && collapsePrimaryNav
|
||||||
? 'sm:px-0 sm:w-16'
|
? 'sm:px-0 sm:w-16'
|
||||||
: 'sm:px-1 md:px-4 lg:px-8 sm:w-[38.2%]'
|
: 'sm:px-1 md:px-4 lg:px-8'
|
||||||
}
|
}
|
||||||
|
w-96
|
||||||
`}>
|
`}>
|
||||||
{workbench && (
|
{workbench && (
|
||||||
<div className={`hidden sm:flex`}>
|
<div className={`hidden sm:flex`}>
|
||||||
|
@ -139,15 +139,17 @@ const DefaultLayout = ({
|
||||||
)}
|
)}
|
||||||
<PrimaryNavigation app={app} active={slug}/>
|
<PrimaryNavigation app={app} active={slug}/>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<div className="p-0 m-0 bg-base-100">
|
||||||
<section className={`
|
<section className={`
|
||||||
p-4 pt-24 sm:pt-28 w-full
|
p-4 pt-24 sm:pt-28
|
||||||
sm:px-1 md:px-4 lg:px-8
|
sm:px-1 md:px-4 lg:px-8
|
||||||
${workbench && collapsePrimaryNav
|
${workbench && collapsePrimaryNav
|
||||||
? ''
|
? ''
|
||||||
: 'max-w-61.8%'
|
: 'max-w-7xl'
|
||||||
}
|
}
|
||||||
`}>
|
`}>
|
||||||
<div className={workbench ? '' : "max-w-5xl"}>
|
<div>
|
||||||
{title && (
|
{title && (
|
||||||
<>
|
<>
|
||||||
<Breadcrumbs app={app} slug={slug} title={title} />
|
<Breadcrumbs app={app} slug={slug} title={title} />
|
||||||
|
@ -157,6 +159,10 @@ const DefaultLayout = ({
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
{workbench && AltMenu && (
|
{workbench && AltMenu && (
|
||||||
<aside className={`
|
<aside className={`
|
||||||
${asideClasses}
|
${asideClasses}
|
||||||
|
|
|
@ -19,11 +19,11 @@ const force = [
|
||||||
|
|
||||||
// List of icons matched to top-level slug
|
// List of icons matched to top-level slug
|
||||||
const icons = {
|
const icons = {
|
||||||
blog: <RssIcon />,
|
blog: (className='') => <RssIcon className={className}/>,
|
||||||
tutorials: <TutorialIcon />,
|
tutorials: (className='') => <TutorialIcon className={className}/>,
|
||||||
guides: <GuideIcon />,
|
guides: (className='') => <GuideIcon className={className}/>,
|
||||||
howtos: <HelpIcon />,
|
howtos: (className='') => <HelpIcon className={className}/>,
|
||||||
reference: <DocsIcon />
|
reference: (className='') => <DocsIcon className={className}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
/* helper method to order nav entries */
|
/* helper method to order nav entries */
|
||||||
|
@ -33,7 +33,7 @@ const order = obj => orderBy(obj, ['__order', '__title'], ['asc', 'asc'])
|
||||||
// Exported for re-use
|
// Exported for re-use
|
||||||
export const Chevron = ({w=8, m=2}) => <svg className={`
|
export const Chevron = ({w=8, m=2}) => <svg className={`
|
||||||
fill-current opacity-75 w-${w} h-${w} mr-${m}
|
fill-current opacity-75 w-${w} h-${w} mr-${m}
|
||||||
details-toggle hover:text-secondary sm:hover:text-secondary-focus
|
details-toggle hover:text-secondary sm:hover:text-secondary
|
||||||
`}
|
`}
|
||||||
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||||
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"/>
|
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"/>
|
||||||
|
@ -47,9 +47,9 @@ const currentChildren = current => Object.values(order(current))
|
||||||
// Exported for re-use
|
// Exported for re-use
|
||||||
export const linkClasses = `text-lg lg:text-xl
|
export const linkClasses = `text-lg lg:text-xl
|
||||||
py-1
|
py-1
|
||||||
text-base-content sm:text-neutral-content
|
text-base-content sm:text-base-content
|
||||||
hover:text-secondary
|
hover:text-secondary
|
||||||
sm:hover:text-secondary-focus
|
sm:hover:text-secondary
|
||||||
`
|
`
|
||||||
|
|
||||||
// Figure out whether a page is on the path to the active page
|
// Figure out whether a page is on the path to the active page
|
||||||
|
@ -76,7 +76,7 @@ const SubLevel = ({ nodes={}, active }) => (
|
||||||
flex flex-row
|
flex flex-row
|
||||||
px-2
|
px-2
|
||||||
text-base-content
|
text-base-content
|
||||||
sm:text-neutral-content
|
sm:text-base-content
|
||||||
hover:cursor-row-resize
|
hover:cursor-row-resize
|
||||||
items-center
|
items-center
|
||||||
`}>
|
`}>
|
||||||
|
@ -86,16 +86,16 @@ const SubLevel = ({ nodes={}, active }) => (
|
||||||
${linkClasses}
|
${linkClasses}
|
||||||
hover:cursor-pointer
|
hover:cursor-pointer
|
||||||
hover:border-secondary
|
hover:border-secondary
|
||||||
sm:hover:border-secondary-focus
|
sm:hover:border-secondary
|
||||||
${child.__slug === active
|
${child.__slug === active
|
||||||
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
|
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
|
||||||
: 'text-base-content sm:text-neutral-content'
|
: 'text-base-content sm:text-base-content'
|
||||||
}
|
}
|
||||||
`}>
|
`}>
|
||||||
<span className={`
|
<span className={`
|
||||||
text-3xl mr-2 inline-block p-0 leading-3
|
text-3xl mr-2 inline-block p-0 leading-3
|
||||||
${child.__slug === active
|
${child.__slug === active
|
||||||
? 'text-secondary sm:text-secondary-focus translate-y-1'
|
? 'text-secondary sm:text-secondary translate-y-1'
|
||||||
: 'translate-y-3'
|
: 'translate-y-3'
|
||||||
}
|
}
|
||||||
`}>
|
`}>
|
||||||
|
@ -120,15 +120,15 @@ const SubLevel = ({ nodes={}, active }) => (
|
||||||
${linkClasses}
|
${linkClasses}
|
||||||
hover:cursor-pointer
|
hover:cursor-pointer
|
||||||
hover:border-secondary
|
hover:border-secondary
|
||||||
sm:hover:border-secondary-focus
|
sm:hover:border-secondary
|
||||||
${child.__slug === active
|
${child.__slug === active
|
||||||
? 'text-secondary border-secondary sm:text-secondary-focus sm:border-secondary-focus'
|
? 'text-secondary border-secondary sm:text-secondary sm:border-secondary'
|
||||||
: 'text-base-content sm:text-neutral-content'
|
: 'text-base-content sm:text-base-content'
|
||||||
}`}>
|
}`}>
|
||||||
<span className={`
|
<span className={`
|
||||||
text-3xl mr-2 inline-block p-0 leading-3
|
text-3xl mr-2 inline-block p-0 leading-3
|
||||||
${child.__slug === active
|
${child.__slug === active
|
||||||
? 'text-secondary sm:text-secondary-focus translate-y-1'
|
? 'text-secondary sm:text-secondary translate-y-1'
|
||||||
: 'translate-y-3'
|
: 'translate-y-3'
|
||||||
}
|
}
|
||||||
`}>
|
`}>
|
||||||
|
@ -154,15 +154,15 @@ const TopLevel = ({ icon, title, nav, current, slug, hasChildren=false, active }
|
||||||
hover:cursor-row-resize
|
hover:cursor-row-resize
|
||||||
p-2
|
p-2
|
||||||
text-base-content
|
text-base-content
|
||||||
sm:text-neutral-content
|
sm:text-base-content
|
||||||
items-center
|
items-center
|
||||||
`}>
|
`}>
|
||||||
<span className="text-secondary-focus">{icon}</span>
|
<span className="text-secondary">{icon}</span>
|
||||||
<Link href={`/${slug}`}>
|
<Link href={`/${slug}`}>
|
||||||
<a className={`
|
<a className={`
|
||||||
grow ${linkClasses} hover:cursor-pointer
|
grow ${linkClasses} hover:cursor-pointer
|
||||||
${slug === active
|
${slug === active
|
||||||
? 'text-secondary sm:text-secondary-focus'
|
? 'text-secondary sm:text-secondary'
|
||||||
: ''
|
: ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
@ -175,12 +175,15 @@ const TopLevel = ({ icon, title, nav, current, slug, hasChildren=false, active }
|
||||||
</details>
|
</details>
|
||||||
)
|
)
|
||||||
|
|
||||||
const Navigation = ({ app, active }) => {
|
const Navigation = ({ app, active, className='' }) => {
|
||||||
if (!app.navigation) return null
|
if (!app.navigation) return null
|
||||||
const output = []
|
const output = []
|
||||||
for (const page of order(app.navigation)) output.push(<TopLevel
|
for (const page of order(app.navigation)) output.push(<TopLevel
|
||||||
key={page.__slug}
|
key={page.__slug}
|
||||||
icon={icons[page.__slug] || <span className="text-3xl mr-2 translate-y-3 inline-block p-0 leading-3">°</span>}
|
icon={icons[page.__slug]
|
||||||
|
? icons[page.__slug]('w-6 h-6')
|
||||||
|
: <span className="text-3xl mr-2 translate-y-3 inline-block p-0 leading-3">°</span>
|
||||||
|
}
|
||||||
title={page.__title}
|
title={page.__title}
|
||||||
slug={page.__slug}
|
slug={page.__slug}
|
||||||
hasChildren={keepClosed.indexOf(page.__slug) === -1}
|
hasChildren={keepClosed.indexOf(page.__slug) === -1}
|
||||||
|
@ -189,13 +192,39 @@ const Navigation = ({ app, active }) => {
|
||||||
active={active}
|
active={active}
|
||||||
/>)
|
/>)
|
||||||
|
|
||||||
return <div className='pb-20'>{output}</div>
|
return <div className={`pb-20 ${className}`}>{output}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Icons = ({ app, active, className='' }) => {
|
||||||
|
if (!app.navigation) return null
|
||||||
|
const output = []
|
||||||
|
for (const page of order(app.navigation)) {
|
||||||
|
output.push(
|
||||||
|
<li key={page.__slug} className="py-4">
|
||||||
|
<Link href={`/${page.__slug}`}>
|
||||||
|
<a
|
||||||
|
className={`grow ${linkClasses} hover:cursor-pointer flex flex-col items-center`}
|
||||||
|
title={page.__title}
|
||||||
|
>
|
||||||
|
{icons[page.__slug]
|
||||||
|
? icons[page.__slug]('w-14 h-14')
|
||||||
|
: <HelpIcon />
|
||||||
|
}
|
||||||
|
<span className='font-bold'>{page.__title}</span>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ul className={`flex flex-col items-center ${className}`}>{output}</ul>
|
||||||
}
|
}
|
||||||
|
|
||||||
const PrimaryMenu = ({ app, active }) => (
|
const PrimaryMenu = ({ app, active }) => (
|
||||||
<nav className="sm:max-w-lg grow mb-12">
|
<nav className="mb-12">
|
||||||
<ThemePicker app={app} className="w-full sm:hidden"/>
|
<ThemePicker app={app} className="w-full md:hidden"/>
|
||||||
<Navigation app={app} active={active} />
|
<Icons app={app} className="hidden md:block lg:hidden"/>
|
||||||
|
<Navigation app={app} active={active} className="md:hidden lg:block"/>
|
||||||
</nav>
|
</nav>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
41
packages/freesewing.shared/components/wrappers/toc.js
Normal file
41
packages/freesewing.shared/components/wrappers/toc.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* This is used to wrap a Table of Contents (toc) as returned
|
||||||
|
* from the mdxLoader method (see shared/mdx/loader.js)
|
||||||
|
* It is NOT for wrapping plain markdown/mdx
|
||||||
|
*/
|
||||||
|
import { useState, useEffect, Fragment } from 'react'
|
||||||
|
|
||||||
|
// See: https://mdxjs.com/guides/mdx-on-demand/
|
||||||
|
import { run } from '@mdx-js/mdx'
|
||||||
|
import * as runtime from 'react/jsx-runtime.js'
|
||||||
|
|
||||||
|
// Components that are available in all MDX
|
||||||
|
import customComponents from 'shared/components/mdx'
|
||||||
|
|
||||||
|
const TocWrapper = ({toc, app}) => {
|
||||||
|
|
||||||
|
const [mdxModule, setMdxModule] = useState()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
;(async () => {
|
||||||
|
setMdxModule(await run(toc, runtime))
|
||||||
|
})()
|
||||||
|
}, [toc])
|
||||||
|
|
||||||
|
// React component for MDX content
|
||||||
|
const MdxContent = mdxModule ? mdxModule.default : Fragment
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`
|
||||||
|
mdx mdx-toc text-base-content text-lg lg:text-xl
|
||||||
|
sticky top-8 max-h-screen overflow-y-auto
|
||||||
|
md:border-l-4 md:pl-4 md:mb-8 md:border-base-200
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{mdxModule && <MdxContent components={customComponents(app)}/>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TocWrapper
|
||||||
|
|
|
@ -10,6 +10,7 @@ import remarkFrontmatter from 'remark-frontmatter'
|
||||||
import remarkGfm from 'remark-gfm'
|
import remarkGfm from 'remark-gfm'
|
||||||
import remarkCopyLinkedFiles from 'remark-copy-linked-files'
|
import remarkCopyLinkedFiles from 'remark-copy-linked-files'
|
||||||
import { remarkIntroPlugin } from './remark-intro-plugin.mjs'
|
import { remarkIntroPlugin } from './remark-intro-plugin.mjs'
|
||||||
|
import mdxPluginToc from './mdx-plugin-toc.mjs'
|
||||||
import smartypants from 'remark-smartypants'
|
import smartypants from 'remark-smartypants'
|
||||||
// Rehype plugins we want to use
|
// Rehype plugins we want to use
|
||||||
import rehypeHighlight from 'rehype-highlight'
|
import rehypeHighlight from 'rehype-highlight'
|
||||||
|
@ -61,7 +62,27 @@ const mdxLoader = async (language, site, slug) => {
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
return {mdx, intro}
|
// This is not ideal as we're adding a second pass but for now it will do
|
||||||
|
// See: https://github.com/remarkjs/remark-toc/issues/37
|
||||||
|
const toc = String(
|
||||||
|
await compile(md, {
|
||||||
|
outputFormat: 'function-body',
|
||||||
|
remarkPlugins: [
|
||||||
|
remarkFrontmatter,
|
||||||
|
remarkGfm,
|
||||||
|
smartypants,
|
||||||
|
[
|
||||||
|
mdxPluginToc,
|
||||||
|
{ language: 'en' }
|
||||||
|
]
|
||||||
|
],
|
||||||
|
rehypePlugins: [
|
||||||
|
rehypeSlug,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return {mdx, intro, toc}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mdxLoader
|
export default mdxLoader
|
||||||
|
|
30
packages/freesewing.shared/mdx/mdx-plugin-toc.mjs
Normal file
30
packages/freesewing.shared/mdx/mdx-plugin-toc.mjs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import {toc} from 'mdast-util-toc'
|
||||||
|
|
||||||
|
const headings = {
|
||||||
|
en: 'Table of contents',
|
||||||
|
fr: 'Table des matières',
|
||||||
|
nl: 'Inhoudsopgave',
|
||||||
|
es: 'Tabla de contenido',
|
||||||
|
de: 'Inhaltsverzeichnis'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function mdxToc(options = {}) {
|
||||||
|
return (node) => {
|
||||||
|
const result = toc(node, { heading: false })
|
||||||
|
|
||||||
|
if (result.map) node.children = [
|
||||||
|
{
|
||||||
|
type: 'heading',
|
||||||
|
depth: 4,
|
||||||
|
children: [{
|
||||||
|
type: 'text',
|
||||||
|
value: headings[options.language || 'en']
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
result.map
|
||||||
|
]
|
||||||
|
else node.children = []
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@
|
||||||
"highlight.js": "^11.4.0",
|
"highlight.js": "^11.4.0",
|
||||||
"lodash.orderby": "^4.6.0",
|
"lodash.orderby": "^4.6.0",
|
||||||
"lodash.unset": "^4.5.2",
|
"lodash.unset": "^4.5.2",
|
||||||
|
"mdast-util-toc": "^6.1.0",
|
||||||
"react-markdown": "^8.0.0",
|
"react-markdown": "^8.0.0",
|
||||||
"react-sizeme": "^3.0.2",
|
"react-sizeme": "^3.0.2",
|
||||||
"react-timeago": "^6.2.1",
|
"react-timeago": "^6.2.1",
|
||||||
|
|
|
@ -35,6 +35,12 @@
|
||||||
.mdx a[aria-hidden="true"] {
|
.mdx a[aria-hidden="true"] {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
/* Watch out of P tags in the toc list */
|
||||||
|
.mdx-toc ul li p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
/* FreeSewing SVG output styles */
|
/* FreeSewing SVG output styles */
|
||||||
.fs-stroke-fabric { stroke: var(--pattern-fabric); }
|
.fs-stroke-fabric { stroke: var(--pattern-fabric); }
|
||||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -4428,6 +4428,11 @@
|
||||||
"@types/qs" "*"
|
"@types/qs" "*"
|
||||||
"@types/serve-static" "*"
|
"@types/serve-static" "*"
|
||||||
|
|
||||||
|
"@types/extend@^3.0.0":
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/extend/-/extend-3.0.1.tgz#923dc2d707d944382433e01d6cc0c69030ab2c75"
|
||||||
|
integrity sha512-R1g/VyKFFI2HLC1QGAeTtCBWCo6n75l41OnsVYNbmKG+kempOESaodf6BeJyUM3Q0rKa/NQcTHbB2+66lNnxLw==
|
||||||
|
|
||||||
"@types/formidable@^1.0.31":
|
"@types/formidable@^1.0.31":
|
||||||
version "1.2.5"
|
version "1.2.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-1.2.5.tgz#561d026e5f09179e5c8ef7b31e8f4652e11abe4c"
|
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-1.2.5.tgz#561d026e5f09179e5c8ef7b31e8f4652e11abe4c"
|
||||||
|
@ -4435,6 +4440,11 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/github-slugger@^1.0.0":
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/github-slugger/-/github-slugger-1.3.0.tgz#16ab393b30d8ae2a111ac748a015ac05a1fc5524"
|
||||||
|
integrity sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==
|
||||||
|
|
||||||
"@types/glob@^7.1.1":
|
"@types/glob@^7.1.1":
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
||||||
|
@ -12725,7 +12735,7 @@ github-from-package@0.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
|
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
|
||||||
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
|
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=
|
||||||
|
|
||||||
github-slugger@^1.1.1:
|
github-slugger@^1.0.0, github-slugger@^1.1.1:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e"
|
resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e"
|
||||||
integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==
|
integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==
|
||||||
|
@ -17418,6 +17428,20 @@ mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9"
|
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9"
|
||||||
integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==
|
integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==
|
||||||
|
|
||||||
|
mdast-util-toc@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mdast-util-toc/-/mdast-util-toc-6.1.0.tgz#1f38419f5ce774449c8daa87b39a4d940b24be7c"
|
||||||
|
integrity sha512-0PuqZELXZl4ms1sF7Lqigrqik4Ll3UhbI+jdTrfw7pZ9QPawgl7LD4GQ8MkU7bT/EwiVqChNTbifa2jLLKo76A==
|
||||||
|
dependencies:
|
||||||
|
"@types/extend" "^3.0.0"
|
||||||
|
"@types/github-slugger" "^1.0.0"
|
||||||
|
"@types/mdast" "^3.0.0"
|
||||||
|
extend "^3.0.0"
|
||||||
|
github-slugger "^1.0.0"
|
||||||
|
mdast-util-to-string "^3.1.0"
|
||||||
|
unist-util-is "^5.0.0"
|
||||||
|
unist-util-visit "^3.0.0"
|
||||||
|
|
||||||
mdn-data@2.0.14:
|
mdn-data@2.0.14:
|
||||||
version "2.0.14"
|
version "2.0.14"
|
||||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue