Skip to content

Commit

Permalink
docs: add quick links to docs pages for small screens and add section…
Browse files Browse the repository at this point in the history
… table of contents indexes (#4145)
  • Loading branch information
tmorehouse committed Sep 26, 2019
1 parent d6d00ca commit 22268aa
Show file tree
Hide file tree
Showing 19 changed files with 366 additions and 82 deletions.
49 changes: 49 additions & 0 deletions docs/components/doc-breadcrumbs.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<template>
<nav aria-label="Breadcrumbs">
<b-breadcrumb :items="items" class="d-inline-flex bg-transparent px-2 py-1 my-0">
</b-breadcrumb>
</nav>
</template>

<style scoped>
.breadcrumb /deep/ .breadcrumb-item {
font-size: 0.875rem;
line-height: 1.5;
margin-bottom: 0;
}
</style>

<script>
import { nav } from '~/content'
const navLookup = nav.reduce((obj, section) => {
obj[section.base.replace('/', '')] = section
return obj
}, {})
export default {
name: 'BDVBreadcrumbs',
computed: {
items() {
const items = [{ text: 'Home', to: '/' }, { text: 'Docs', to: '/docs' }]
const section = this.$route.name.split('-')[1] || ''
const slug = this.$route.params.slug || ''
if (section) {
const sectionMeta = navLookup[section] || {}
items.push({
text: sectionMeta.title || section,
to: ['/docs', section].join('/')
})
if (slug) {
const pagesMeta = sectionMeta.pages || {}
items.push({
text: (pagesMeta[slug] || {}).title || slug,
to: ['/docs', section, slug].join('/')
})
}
}
return items
}
}
}
</script>
8 changes: 8 additions & 0 deletions docs/components/feedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export default {
}
},
computed: {
show() {
const name = this.$route.name
const slug = this.$route.params.slug
return slug || name === 'docs'
},
reportIssueUrl() {
// Add appreciate query params for proper issue title
return `${this.baseUrl}/issues/new?title=Docs`
Expand Down Expand Up @@ -35,6 +40,9 @@ export default {
}
},
render(h) {
if (!this.show) {
return h()
}
const $reportIssueButton = h(
'b-button',
{
Expand Down
127 changes: 127 additions & 0 deletions docs/components/quick-links.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<template>
<nav
:class="['bd-quick-links', 'mb-3', { 'd-none': !quickLinksVisible || !hasContent }]"
:aria-hidden="hasContent ? null : 'true'"
>
<header v-if="hasContent">
<b-button
v-b-toggle.bd-quick-links-collapse
class="font-weight-bold"
variant="outline-secondary"
size="sm"
block
>
<span v-if="quickLinksExpanded">Hide</span>
<span v-else>Show</span>
page table of contents
</b-button>
</header>
<b-collapse v-if="hasContent" id="bd-quick-links-collapse" v-model="quickLinksExpanded" tag="ul">
<li v-for="h2 in toc.toc" :key="h2.href">
<b-link :href="h2.href" @click="scrollIntoView($event, h2.href)">
<span v-html="h2.label"></span>
</b-link>
<ul v-if="h2.toc && h2.toc.length > 0" :key="`sub-${h2.href}`">
<li v-for="h3 in h2.toc" :key="h3.href">
<b-link :href="h3.href" @click="scrollIntoView($event, h3.href)">
<span v-html="h3.label"></span>
</b-link>
</li>
</ul>
</li>
</b-collapse>
</nav>
</template>

<style scoped lang="scss">
#bd-quick-links-collapse {
list-style-type: square;
border-left: 5px solid #ccc;
padding-left: 2.5rem;
margin-top: 1rem;
ul {
list-style-type: circle;
padding-left: 1.25rem;
margin-bottom: 0.25rem;
}
}
</style>

<script>
import { offsetTop, scrollTo } from '~/utils'
export default {
name: 'BDVQuickToc',
data() {
return {
toc: {},
offset: 0,
quickLinksExpanded: false,
quickLinksVisible: false
}
},
computed: {
hasContent() {
return !!this.toc.toc
}
},
created() {
this.$root.$on('docs-set-toc', toc => {
// Reset visible/expanded states
this.quickLinksVisible = false
this.quickLinksExpanded = false
// Update the TOC content
this.toc = toc
// Re-position the quick links
this.positionQuickLinks()
})
},
mounted() {
// Set the correct offset based on the header height
const $header = document.body.querySelector('header.navbar')
if ($header) {
this.offset = $header.offsetHeight + 6
}
// Re-position the quick links
this.positionQuickLinks()
},
methods: {
scrollIntoView(evt, href) {
evt.preventDefault()
evt.stopPropagation()
// We use an attribute `querySelector()` rather than `getElementByID()`,
// as some auto-generated ID's are invalid or not unique
const id = (href || '').replace(/#/g, '')
const $el = document.body.querySelector(`[id="${id}"]`)
if ($el) {
// Get the document scrolling element
const scroller = document.scrollingElement || document.documentElement || document.body
// Scroll heading into view (minus offset to account for nav top height
scrollTo(scroller, offsetTop($el) - 70, 100, () => {
// Set a tab index so we can focus header for a11y support
$el.tabIndex = -1
// Focus the heading
$el.focus()
})
}
},
positionQuickLinks() {
if (typeof document === 'undefined') {
return
}
// Move the quick links to the correct position, if possible
const $body = document.body
const $referenceNode = $body.querySelector('.bd-lead') || $body.querySelector('h1')
if ($referenceNode) {
// IE 11 doesn't support the `node.after()` method, and appears
// that the polyfill doesn't polyfill this method
$referenceNode.insertAdjacentElement('afterend', this.$el)
}
// Make the quick links visible
// We hide them initially to make the position change not that distracting
this.quickLinksVisible = true
}
}
}
</script>
24 changes: 0 additions & 24 deletions docs/components/search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,6 @@
placeholder="Search..."
aria-label="Search docs"
></b-form-input>
<button
v-b-toggle.bd-docs-nav
type="button"
class="btn btn-link bd-search-docs-toggle d-md-none p-0 ml-3"
aria-label="Toggle docs navigation"
>
<svg
class=""
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 30 30"
width="30"
height="30"
focusable="false"
>
<title>Menu</title>
<path
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-miterlimit="10"
d="M4 7h22M4 15h22M4 23h22"
/>
</svg>
</button>
</form>
</template>

Expand Down
75 changes: 75 additions & 0 deletions docs/components/section-toc.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<template>
<Main>
<Section>
<h1 :id="id" tabindex="-1">
<span class="bd-content-title">
{{ groupTitle }} <span class="small text-muted">- table of contents</span>
</span>
</h1>
<p v-if="groupDescription" class="bd-lead">{{ groupDescription }}</p>
<b-list-group tag="nav" :aria-label="`${groupTitle} section navigation`" class="mb-5">
<b-list-group-item
v-for="page in pages"
:key="page.slug"
:to="`/docs/${slug}/${page.slug}`"
active-class=""
>
<strong class="text-primary">{{ page.title }}</strong> &mdash;
<span class="text-muted">{{ page.description }}</span>
</b-list-group-item>
</b-list-group>
</Section>
</Main>
</template>

<style scoped>
.list-group .list-group-item:hover strong {
text-decoration: underline;
}
</style>

<script>
import Main from '~/components/main'
import Section from '~/components/section'
import { nav } from '~/content'
// Normalize nav into a lookup object
const groups = nav.reduce((obj, g) => {
const groupSlug = g.base.replace(/\/$/, '')
obj[groupSlug] = g
return obj
}, {})
export default {
layout: 'docs',
components: {
Main,
Section
},
computed: {
slug() {
return this.$route.path.replace(/^\//, '').split('/')[1] || ''
},
id() {
return `bd-section-toc-${this.slug}`
},
group() {
return groups[this.slug] || {}
},
groupDescription() {
return this.group.description
},
groupTitle() {
const title = this.group.title || ''
return title === 'Components'
? 'Component groups'
: title === 'Misc'
? 'Miscellaneous'
: title || ''
},
pages() {
return this.group.pages || []
}
}
}
</script>
2 changes: 1 addition & 1 deletion docs/components/section.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const scrollIntoView = (evt, href) => {
evt.preventDefault()
evt.stopPropagation()
// We use an attribute `querySelector()` rather than `getElementByID()`,
// as some auto-generated ID's are invalid or may not be unique
// as some auto-generated ID's are invalid or not unique
const id = (href || '').replace(/#/g, '')
const $el = document.body.querySelector(`[id="${id}"]`)
if ($el) {
Expand Down
10 changes: 2 additions & 8 deletions docs/components/sidebar.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
<template>
<b-collapse
id="bd-docs-nav"
class="bd-links"
tag="nav"
is-nav
aria-label="Main navigation"
>
<nav id="bd-docs-nav" class="bd-links d-none d-md-block" aria-label="Main navigation">
<b-link to="/" exact router-tag="div" active-class="active">
<b-link to="/" exact class="bd-toc-link" active-class="">Home</b-link>
</b-link>
Expand Down Expand Up @@ -59,7 +53,7 @@
<b-link to="/play" exact router-tag="div" active-class="active">
<b-link to="/play" exact class="bd-toc-link" active-class="">Playground</b-link>
</b-link>
</b-collapse>
</nav>
</template>

<script>
Expand Down
17 changes: 6 additions & 11 deletions docs/components/toc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,31 +46,26 @@
</template>

<script>
import { makeTOC, offsetTop, scrollTo } from '~/utils'
import { offsetTop, scrollTo } from '~/utils'
export default {
name: 'BDVToc',
data() {
return {
readme: '',
meta: null,
toc: {},
offset: 0
}
},
computed: {
toc() {
return makeTOC(this.readme, this.meta)
}
created() {
this.$root.$on('docs-set-toc', toc => {
this.toc = toc
})
},
mounted() {
const $header = document.body.querySelector('header.navbar')
if ($header) {
this.offset = $header.offsetHeight + 6
}
this.$root.$on('setTOC', (readme, meta) => {
this.readme = readme
this.meta = meta || null
})
},
methods: {
isArray(value) {
Expand Down
Loading

0 comments on commit 22268aa

Please sign in to comment.