Skip to content

Commit

Permalink
[wip] configurable toc, both inline and sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
hikerpig committed Oct 15, 2021
1 parent a82e9b4 commit a592d9a
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 28 deletions.
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# http://editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 100
quote_type = single

[*.md]
trim_trailing_whitespace = false
3 changes: 2 additions & 1 deletion packages/gatsby-theme-kb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ module.exports = {
|:----------------------:|:-------------:|:----------------------------------------------------------------------------:|
| rootNote | `/readme` | Root note's name (without exts)
| contentPath | | Location of local content |
| extensions | ['.md', '.mdx'] | Valid content file exts |
| extensions | `['.md', '.mdx']` | Valid content file exts |
| ignore | `['.git']` | A list of file globs to ignore |
| wikiLinkLabelTemplate | | A template string for specifying wiki link label, see [ options.wikiLinkLabelTemplate](# options.wikiLinkLabelTemplate) |
| getPluginMdx | (defaultPluginMdx) => PluginMdx | Customise pre-configured `gatsby-plugin-mdx`, please do always return a valid gatsby plugin object |
| tocTypes | `['sidebar']` | Customise the toc location, type is `false \| Array<'inline' | 'sidebar'>` |


### options.wikiLinkLabelTemplate
Expand Down
6 changes: 6 additions & 0 deletions packages/gatsby-theme-kb/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ let rootNoteSlug
let extensions
let mediaTypes
let wikiLinkLabelTemplate
let tocTypes = ['sidebar'];

function padSlugLeading(str) {
if (typeof str !== 'string') return str
Expand All @@ -27,6 +28,10 @@ exports.onPreBootstrap = async ({ store }, themeOptions) => {
mediaTypes = themeOptions.mediaTypes || ['text/markdown', 'text/x-markdown']
wikiLinkLabelTemplate =
themeOptions.wikiLinkLabelTemplate || wikiLinkLabelTemplate

if ('tocTypes' in themeOptions) {
tocTypes = themeOptions.tocTypes
}
}

function getTitle(node, content) {
Expand Down Expand Up @@ -190,6 +195,7 @@ exports.createPages = async ({ graphql, actions }, options) => {
id: n.id,
wikiLinkLabelTemplate,
refWordMdxSlugDict,
tocTypes,
}
}

Expand Down
44 changes: 44 additions & 0 deletions packages/gatsby-theme-kb/src/components/InlineTOC/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react'
import { TableOfContents, TOCItem } from '../../type'
import { slugifyTitle } from '../../utils/toc'
import './inline-toc.css'

interface InlineTOCProps {
tableOfContents: TableOfContents
}

const InlineTOC = ({ tableOfContents }: InlineTOCProps) => {
return (
<div className="inline-toc">
<div className="inline-toc__header">Table Of Contents</div>
{tableOfContents.items && (
<TOCItemComponent item={tableOfContents} className="inline-toc__top-node" />
)}
</div>
)
}

type TOCItemProps = {
item: TOCItem
className?: string
}

function TOCItemComponent({ item, className }: TOCItemProps) {
const itemsElement = item.items ? (
<ol className={className}>
{item.items.map((childItem) => (
<TOCItemComponent key={childItem.url} item={childItem} />
))}
</ol>
) : null
return item.title ? (
<li className={className}>
<a href={`#${slugifyTitle(item.title)}`}>{item.title}</a>
{itemsElement}
</li>
) : (
itemsElement
)
}

export default InlineTOC
24 changes: 24 additions & 0 deletions packages/gatsby-theme-kb/src/components/InlineTOC/inline-toc.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.inline-toc {
margin-bottom: 1em;
}

.inline-toc ol {
margin-top: 0;
}

.inline-toc__header {
font-size: 1.2em;
font-weight: bold;
}

.inline-toc__top-node {
margin-left: 0;
}

.inline-toc li {
list-style: disc;
}

.inline-toc__top-node > li {
list-style: none;
}
7 changes: 5 additions & 2 deletions packages/gatsby-theme-kb/src/components/Topic/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ import LinkReference from '../LinkReference'
import * as HEADER_COMPONENTS from '../mdx-components/header-components'
import { MDXProvider } from '@mdx-js/react'
import slugify from 'slugify'
import InlineTOC from '../InlineTOC'
import './topic.css'

export type Props = {
file: TopicFlie
currentLocation: Location
refWordMdxSlugDict: {[key: string]: string}
wikiLinkLabelTemplateFn?: WikiLinkLabelTemplateFn | null
showInlineTOC?: boolean
}

const Topic: React.FC<Props> = ({ file, currentLocation, refWordMdxSlugDict, wikiLinkLabelTemplateFn }: Props) => {
const Topic: React.FC<Props> = ({ file, currentLocation, refWordMdxSlugDict, wikiLinkLabelTemplateFn, showInlineTOC }: Props) => {
let referenceBlock
const { frontmatter, inboundReferences, outboundReferences } = file.childMdx
const { frontmatter, inboundReferences, outboundReferences, tableOfContents } = file.childMdx
const { title, slug } = file.fields

// console.log(
Expand Down Expand Up @@ -66,6 +68,7 @@ const Topic: React.FC<Props> = ({ file, currentLocation, refWordMdxSlugDict, wik
return (
<div className="topic">
{shouldRenderTitle ? <h1 id={slugify(realTitle)}>{realTitle}</h1> : null}
{showInlineTOC && <InlineTOC tableOfContents={tableOfContents} />}
<MDXProvider components={{ a: ProvidedAnchorTag, ...HEADER_COMPONENTS }}>
<MDXRenderer scope="">{file.childMdx.body}</MDXRenderer>
</MDXProvider>
Expand Down
40 changes: 21 additions & 19 deletions packages/gatsby-theme-kb/src/components/TopicLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import React, {
useState,
useMemo,
useRef,
useLayoutEffect,
useCallback,
} from 'react'
import React, { useState, useMemo, useRef, useLayoutEffect, useCallback } from 'react'
import { useScrollRestoration } from 'gatsby-react-router-scroll'
import useBreakpoint from 'use-breakpoint'
import { useStaticQuery, graphql } from 'gatsby'
Expand All @@ -19,6 +13,7 @@ import { isServer } from '../../env'

export type Props = React.PropsWithChildren<{
pageContext: PageContext
showSidebarTOC?: boolean
}>

const BREAKPOINTS = {
Expand All @@ -44,7 +39,7 @@ function cmpBreakpoint(p1: BreakpointName, p2: BreakpointName) {
}

export default function TopicLayout(props: Props) {
const { children, pageContext } = props
const { children, pageContext, showSidebarTOC } = props
const tocRestoration = useScrollRestoration('toc')
const data = useStaticQuery(graphql`
query TopicLayoutQuery {
Expand Down Expand Up @@ -144,7 +139,6 @@ export default function TopicLayout(props: Props) {
}
})
const rightClass = classnames(rightClassObject)
// "topic-layout__right flex-shrink-0 p-5 hover:shadow-md"

const sideBar = useMemo(() => {
return (
Expand All @@ -163,12 +157,14 @@ export default function TopicLayout(props: Props) {
{expandIcon}
<div className="topic-layout__header-title">{title}</div>
</div>
<div className="flex items-center">
<div className="top-layout__header-item toc-layout__toc-icon" onClick={handleTocClick}>
T
{showSidebarTOC && (
<div className="flex items-center">
<div className="top-layout__header-item toc-layout__toc-icon" onClick={handleTocClick}>
T
</div>
<DarkModeToggle></DarkModeToggle>
</div>
<DarkModeToggle></DarkModeToggle>
</div>
)}
</div>
<div className="topic-layout__main md:m-auto flex min-h-screen">
<div
Expand All @@ -181,17 +177,23 @@ export default function TopicLayout(props: Props) {
{children}
</main>
<div className={rightClass} ref={rightEleRef}>
{isMobileMode ? null: (
{isMobileMode ? null : (
<>
<GraphButton currentFileId={pageContext.id} showHint></GraphButton>
<DarkModeToggle showHint />
</>
)}

<div {...tocRestoration}>
{isMobileMode && <header><b>Table Of Contents</b></header>}
<div id="toc" className="toc tocbot js-toc" />
</div>
{showSidebarTOC && (
<div {...tocRestoration}>
{isMobileMode && (
<header>
<b>Table Of Contents</b>
</header>
)}
<div id="toc" className="toc tocbot js-toc" />
</div>
)}
</div>
</div>
{isMobileMode && (menuOpened || rightMenuOpened) ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react'
import slugify from 'slugify'
import { slugifyTitle } from '../../utils/toc'

function makeHeaderComponent(tag) {
return (props) => {
const slugified = typeof props.children === 'string' ? slugify(props.children): props.children
const slugified = typeof props.children === 'string' ? slugifyTitle(props.children): props.children
const id = slugified ? slugified: props.children
// console.log(`${tag} props`, props, id)
return React.createElement(tag, {
Expand All @@ -18,4 +18,4 @@ export const h2 = makeHeaderComponent('h2')
export const h3 = makeHeaderComponent('h3')
export const h4 = makeHeaderComponent('h4')
export const h5 = makeHeaderComponent('h5')
export const h6 = makeHeaderComponent('h6')
export const h6 = makeHeaderComponent('h6')
11 changes: 9 additions & 2 deletions packages/gatsby-theme-kb/src/templates/Topic.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ export default (props) => {
console.debug('Topic props', props)
}

const { pageContext } = props
const file = props.data.file
let wikiLinkLabelTemplateFn = null
if (props.pageContext.wikiLinkLabelTemplate) {
if (pageContext.wikiLinkLabelTemplate) {
wikiLinkLabelTemplateFn = function (data) {
try {
return evaluateTemplate(props.pageContext.wikiLinkLabelTemplate, data)
Expand All @@ -34,14 +35,19 @@ export default (props) => {
}
}

const tocTypes = ('tocTypes' in pageContext ? pageContext.tocTypes: null) || []
const showInlineTOC = tocTypes && tocTypes.includes('sidebar')
const showSidebarTOC = tocTypes && tocTypes.includes('inline')

return (
<TopicLayout pageContext={props.pageContext}>
<TopicLayout pageContext={props.pageContext} showSidebarTOC={showSidebarTOC}>
<Seo title={file.fields.title}></Seo>
<Topic
file={file}
currentLocation={props.location}
wikiLinkLabelTemplateFn={wikiLinkLabelTemplateFn}
refWordMdxSlugDict={props.pageContext.refWordMdxSlugDict}
showInlineTOC={showInlineTOC}
></Topic>
</TopicLayout>
)
Expand All @@ -56,6 +62,7 @@ export const pageQuery = graphql`
title
private
}
tableOfContents
...GatsbyGardenReferences
}
fields {
Expand Down
14 changes: 13 additions & 1 deletion packages/gatsby-theme-kb/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,19 @@ export type TopicFlie = {
frontmatter: {
title: string
}
tableOfContents: TableOfContents
}
}

export type WikiLinkLabelTemplateFn = (data: { refWord: string, title: string }) => string
export type WikiLinkLabelTemplateFn = (data: { refWord: string; title: string }) => string

/**
* Table of content item generated by remark
*/
export type TOCItem = {
items?: TOCItem[]
title: string
url: string
}

export type TableOfContents = TOCItem
5 changes: 5 additions & 0 deletions packages/gatsby-theme-kb/src/utils/toc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import slugify from 'slugify'

export function slugifyTitle(str: string) {
return slugify(str)
}

0 comments on commit a592d9a

Please sign in to comment.