mirror of
https://github.com/Stone-Red-Code/website.git
synced 2026-09-04 23:44:11 +02:00
feat: implement table of contents
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import React, { FC, Fragment } from "react"
|
||||
|
||||
import { ITocItem } from "../../types"
|
||||
import { humanize } from "../../utils"
|
||||
import * as SC from "./styles"
|
||||
import { Container } from "../Container"
|
||||
import { Toc } from "../Toc"
|
||||
import * as SC from "./styles"
|
||||
|
||||
interface IPageContentProps {
|
||||
content: JSX.Element
|
||||
toc?: ITocItem[]
|
||||
recommendedReading?: string[]
|
||||
externalResources?: string[]
|
||||
}
|
||||
@@ -42,6 +45,7 @@ function ExtraLink({
|
||||
|
||||
export const PageContent: FC<IPageContentProps> = ({
|
||||
content,
|
||||
toc = [],
|
||||
recommendedReading,
|
||||
externalResources,
|
||||
}) => {
|
||||
@@ -52,6 +56,13 @@ export const PageContent: FC<IPageContentProps> = ({
|
||||
</SC.Content>
|
||||
|
||||
<SC.Sidebar>
|
||||
{toc.length > 0 && (
|
||||
<Toc
|
||||
header={<SC.SidebarHeader>Table of Contents</SC.SidebarHeader>}
|
||||
items={toc}
|
||||
/>
|
||||
)}
|
||||
|
||||
{recommendedReading && (
|
||||
<SC.RecommendedReading>
|
||||
<SC.SidebarHeader>Recommended reading</SC.SidebarHeader>
|
||||
|
||||
@@ -21,6 +21,8 @@ export const Sidebar = styled.div`
|
||||
color: ${props => (props.theme.name === "dark" ? "#f9f9f9" : "#172129")};
|
||||
margin: 64px 0;
|
||||
padding: 0 32px;
|
||||
position: sticky;
|
||||
top: 32px;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
@@ -50,7 +52,11 @@ export const SidebarHeader = styled.div`
|
||||
`
|
||||
|
||||
const ExtraLinks = styled.div`
|
||||
margin-top: 16px;
|
||||
margin-top: 32px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
`
|
||||
|
||||
export const RecommendedReading = styled(ExtraLinks)``
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, { FC } from "react"
|
||||
import { ITocItem } from "../../types"
|
||||
import * as SC from "./styles"
|
||||
|
||||
interface ITocProps {
|
||||
header: React.ReactNode
|
||||
items: ITocItem[]
|
||||
}
|
||||
|
||||
interface ITitle {
|
||||
prefix?: string
|
||||
title: string
|
||||
}
|
||||
|
||||
function extractTitle(title: string): ITitle {
|
||||
const maybePrefix = title.match(/^(\w+\.)/)
|
||||
const rest = maybePrefix ? title.replace(maybePrefix[0], "") : title
|
||||
|
||||
return {
|
||||
prefix: maybePrefix ? maybePrefix[0] : undefined,
|
||||
title: rest,
|
||||
}
|
||||
}
|
||||
|
||||
export const Toc: FC<ITocProps> = ({ header, items }) => {
|
||||
const { pathname } = window.location
|
||||
return (
|
||||
<SC.TocWrapper>
|
||||
{header}
|
||||
{items.map(item => {
|
||||
const { prefix, title } = extractTitle(item.title)
|
||||
|
||||
return (
|
||||
<SC.TocItem key={item.link} className={`depth-${item.depth}`}>
|
||||
{prefix}
|
||||
<SC.TocLink to={`${pathname}${item.link}`}>{title}</SC.TocLink>
|
||||
</SC.TocItem>
|
||||
)
|
||||
})}
|
||||
</SC.TocWrapper>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { AnchorLink } from "gatsby-plugin-anchor-links"
|
||||
import styled from "styled-components"
|
||||
|
||||
export const TocWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`
|
||||
|
||||
export const TocItem = styled.div`
|
||||
display: inline-block;
|
||||
|
||||
&.depth-3 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&.depth-2 + &.depth-2,
|
||||
&.depth-3 + &.depth-2 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
`
|
||||
|
||||
export const TocLink = styled(AnchorLink)`
|
||||
color: ${props => props.theme.main.foreground};
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`
|
||||
Reference in New Issue
Block a user