Skip to content
This repository was archived by the owner on Aug 9, 2020. It is now read-only.
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
7 changes: 3 additions & 4 deletions src/core/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@ const { getContent } = require('./filesystem')
async function generateDatabase (manifest) {
const db = []

const _recursive = async ({ items, ...item }, bc = []) => {
const _recursive = async ({ items, ...item }) => {
if (item.input) {
db.push({
url: item.url,
breadcrumb: bc,
title: item.title,
breadcrumb: item.breadcrumb,
content: await getContent(item.input),
})
}

if (items) {
const breadcrumb = bc.concat(item.title)
await Promise.all(
items.map(i => _recursive(i, breadcrumb))
items.map(i => _recursive(i))
)
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/core/hydrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,22 @@ async function hydrateTree (tree, config, onRegenerate) {
throw new Error(`Duplicated URL was found: ${duplicated.join('\n\t- ')}`)
}

// continue the breadcrumb from parent
if (config.breadcrumbs && metaData.breadcrumbs !== false) {
const breadcrumbs = []
const breadcrumbsParent = itemParent.breadcrumbs || []

breadcrumbsParent
.concat({ title: hydratedItem.title, url: hydratedItem.url })
// only add unique urls to the breadcrumb
.forEach(crumb =>
breadcrumbs.findIndex(i => i.url === crumb.url) === -1 &&
breadcrumbs.push(crumb)
)

hydratedItem.breadcrumbs = breadcrumbs
}

// pull in source items if one exists
if (metaData.source) {
const source = await walkSource(config.temp, hoistedItem.path, metaData)
Expand Down
1 change: 1 addition & 0 deletions src/core/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = async (entrypoints, props) => {

await fs.outputFile(outputHtml, template)
await fs.outputJson(outputJson, {
title: item.title,
content: item.content,
})
}
Expand Down
1 change: 1 addition & 0 deletions src/utils/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const DEFAULT_CONFIG = {
languages: ['bash', 'json'],
header_links: [],
theme: 'default',
breadcrumbs: true,
prefix_titles: false,
table_of_contents: {
page: true,
Expand Down
14 changes: 14 additions & 0 deletions tests/manifest.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,21 @@ describe('integration: manifest', () => {
expect(res.toc.folder[0].title).to.equal('The Foo')
expect(res.toc.folder[0].description).to.equal('This is a test')
expect(res.toc.folder[0].url).to.equal('/foo/')
expect(res.breadcrumbs).to.have.length(1)
expect(res.breadcrumbs[0].title).to.equal('Mock')
expect(res.breadcrumbs[0].url).to.equal('/')
expect(res.items).to.have.length(7)
expect(res.items[0].path).to.equal('foo')
expect(res.items[0].title).to.equal('The Foo')
expect(res.items[0].description).to.equal('This is a test')
expect(res.items[0].url).to.equal('/foo/')
expect(res.items[0].input).to.equal(syspath.resolve(__dirname, 'mock/foo/index.md'))
expect(res.items[0].outputDir).to.equal('.gitdocs_build/foo/')
expect(res.items[0].breadcrumbs).to.have.length(2)
expect(res.items[0].breadcrumbs[0].title).to.equal('Mock')
expect(res.items[0].breadcrumbs[0].url).to.equal('/')
expect(res.items[0].breadcrumbs[1].title).to.equal('The Foo')
expect(res.items[0].breadcrumbs[1].url).to.equal('/foo/')
expect(res.items[0].items).to.have.length(3)
expect(res.items[1].title).to.equal('Garply')
expect(res.items[1].items[1].draft).to.be.true()
Expand All @@ -38,11 +46,17 @@ describe('integration: manifest', () => {
expect(res.items[4].url).to.equal('/gitdocs/')
expect(res.items[4].input).to.match(/\/@repos\/gitdocs/)
expect(res.items[4].outputDir).to.equal('.gitdocs_build/gitdocs/')
expect(res.items[4].breadcrumbs).to.have.length(2)
expect(res.items[4].breadcrumbs[0].title).to.equal('Mock')
expect(res.items[4].breadcrumbs[0].url).to.equal('/')
expect(res.items[4].breadcrumbs[1].title).to.equal('GitDocs')
expect(res.items[4].breadcrumbs[1].url).to.equal('/gitdocs/')
expect(res.items[4].items).to.have.length(1)
expect(res.items[4].items[0].path).to.equal('externals.md')
expect(res.items[4].items[0].title).to.equal('Externals')
expect(res.items[4].items[0].url).to.equal('/gitdocs/externals/')
expect(res.items[5].component).to.equal('Divider')
expect(res.items[6].title).to.equal('The Quux')
expect(res.items[6].items[1].breadcrumbs).to.be.undefined()
})
})
3 changes: 3 additions & 0 deletions tests/mock/qux/corge.md
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
---
breadcrumbs: false
---
# The Corge
36 changes: 36 additions & 0 deletions themes/default/breadcrumbs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react'
import PropTypes from 'prop-types'
import {
Wrapper,
CrumbWrapper,
Crumb,
Seperator,
} from './styles'

const Breadcrumbs = (props) => {
// don't show breadcrumbs if there is only one item
if (props.items.length < 2) {
return <div />
}

return (
<Wrapper>
{props.items.map((item, i) => item.url && (
<CrumbWrapper key={`breadcrumbs-${item.title}`}>
{i > 0 && <Seperator size={14} />}
<Crumb to={item.url}>{item.title}</Crumb>
</CrumbWrapper>
))}
</Wrapper>
)
}

Breadcrumbs.propTypes = {
items: PropTypes.array,
}

Breadcrumbs.defaultProps = {
items: [],
}

export default Breadcrumbs
31 changes: 31 additions & 0 deletions themes/default/breadcrumbs/styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import styled from 'react-emotion'
import { Link } from 'react-router-dom'
import { ChevronRight } from 'react-feather'

export const Wrapper = styled('nav')`
margin-bottom: 20px;
`

export const CrumbWrapper = styled('div')`
display: inline-block;
`

export const Crumb = styled(Link)`
color: #848B8E;
font-weight: 600;
font-size: 1rem;
text-decoration: none;
opacity: .5;
transition: opacity .1s;
&:hover {
opacity: 1;
}
`

export const Seperator = styled(ChevronRight)`
display: inline-block;
opacity: .2;
padding: 0 5px;
position: relative;
top: 2px;
`
4 changes: 4 additions & 0 deletions themes/default/page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Component } from 'react'
import Helmet from 'react-helmet'
import axios from 'axios'
import Markdown from '../markdown'
import Breadcrumbs from '../breadcrumbs'
import Loading from '../loading'
import TocPage from '../toc/page'
import TocFolder from '../toc/folder'
Expand Down Expand Up @@ -90,6 +91,9 @@ export default class Page extends Component {
<title>{route.title}</title>
</Helmet>

{route.breadcrumbs &&
<Breadcrumbs items={route.breadcrumbs} />}

{loading
? <Loading />
: (
Expand Down
34 changes: 15 additions & 19 deletions themes/default/search/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,25 +151,21 @@ class Search extends Component {
}

renderBreadCrumb (result) {
return result
.breadcrumb
return result.breadcrumb
.slice(1, result.breadcrumb.length)
.concat(result.title)
.map((b, i) => (
<span key={`${b}-${i}`}>
{
i !== 0 &&
<ChevronRight
size={14}
style={{
display: 'inline-block',
padding: '0 .25rem',
position: 'relative',
top: 2,
}}
/>
}
{b}
.map(({ title }, i) => (
<span key={`${title}-${i}`}>
{i !== 0 && <ChevronRight
size={14}
style={{
display: 'inline-block',
padding: '0 .25rem',
position: 'relative',
top: 2,
}}
/>}

{title}
</span>
))
}
Expand All @@ -181,7 +177,7 @@ class Search extends Component {
// Map over search results and create links
const items = results.map((r, i) =>
<Result
key={r.input}
key={r.url}
selected={i === selectedIndex}
innerRef={ref => i === selectedIndex ? this.activeItem = ref : null}
onClick={this.clearSearch}
Expand Down