Skip to content

Commit

Permalink
feat: toggle sidebar children (#223)
Browse files Browse the repository at this point in the history
* Create SidebarItem component

* Add collapsable attribute

* Add arrow icon

* Update document

* Hide collapsable by default

* Automatically open collapsed item by path

* Handle open state in parent

* Update index.js

* Update SidebarItem.vue
  • Loading branch information
ittus authored and egoist committed May 11, 2019
1 parent fb36e7c commit 4ad0d8d
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 124 deletions.
162 changes: 38 additions & 124 deletions src/components/Sidebar.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<template>
<div class="Sidebar" :class="{isShown: $store.state.showSidebar}">
<InjectedComponents position="sidebar:start" />

<InjectedComponents position="mobile-sidebar:start" />

<HeaderNav
Expand All @@ -11,56 +10,13 @@
/>

<div class="SidebarItems">
<div
<sidebar-item
v-for="(item, index) in $store.getters.sidebar"
:class="['SidebarItem', item.title && 'hasTitle']"
:key="index"
>
<div class="ItemTitle" v-if="item.title">{{ item.title }}</div>
<template v-for="(link, index) of getChildren(item)">
<a
v-if="isExternalLink(link.link)"
:key="index"
:href="link.link"
class="ItemLink"
target="_blank"
>
{{ link.title }}
<external-link-icon />
</a>
<router-link
v-else
:key="index"
:to="link.link"
:prefetch-files="getPrefetchFiles(link.link)"
class="ItemLink"
:class="{active: $route.path === link.link}"
>
{{ link.title }}
</router-link>
<div
class="LinkToc"
v-if="
!$store.state.fetchingFile &&
link.toc !== false &&
link.link === $route.path &&
$store.state.page.headings &&
$store.state.page.headings.length > 0
"
:key="`toc-${index}`"
>
<router-link
class="TocHeading"
:to="{hash: heading.slug}"
:data-level="heading.level"
v-for="heading in $store.state.page.headings"
:key="heading.slug"
v-html="heading.text"
>
</router-link>
</div>
</template>
</div>
:item="item"
:open="currentOpenIndex === index"
@toggle="openSidebar(index)"
/>
</div>

<InjectedComponents position="sidebar:end" />
Expand All @@ -69,29 +25,48 @@
</template>

<script>
import {isExternalLink, getFileUrl, getFilenameByPath} from '../utils'
import HeaderNav from './HeaderNav.vue'
import SidebarItem from './SidebarItem.vue'
export default {
components: {
HeaderNav
HeaderNav,
SidebarItem
},
data() {
return {
currentOpenIndex: 0
}
},
watch: {
'$route.path': {
handler() {
this.currentOpenIndex = this.getCurrentIndex(
this.$route.path,
this.$store.getters.sidebar
)
},
immediate: true
}
},
methods: {
isExternalLink,
getPrefetchFiles(path) {
const {sourcePath, routes} = this.$store.getters.config
if (routes && routes[path]) {
const {file} = routes[path]
return file ? [file] : []
openSidebar(index) {
this.currentOpenIndex = this.currentOpenIndex === index ? -1 : index
},
getCurrentIndex(currentPath, sidebarItems) {
for (let idx = 0; idx < sidebarItems.length; idx++) {
if (
this.getChildren(sidebarItems[idx]).some(
child => child.link === currentPath
)
) {
return idx
}
}
const filename = getFilenameByPath(path)
const fileUrl = getFileUrl(sourcePath, filename)
return fileUrl ? [fileUrl] : []
return -1
},
getChildren(item) {
// backward compabillity
return item.children || item.links
return item.children || item.links || []
}
}
}
Expand Down Expand Up @@ -130,65 +105,4 @@ export default {
display: none;
}
}
.SidebarItem {
&:not(:last-child) {
padding-bottom: 1.2rem;
margin-bottom: 1.2rem;
}
&.hasTitle {
& .ItemLink {
font-size: 0.9rem;
}
}
&.hasTitle >>> .TocHeading {
font-size: 0.9rem;
}
}
.ItemTitle {
font-size: 1rem;
padding: 0 20px;
margin-bottom: 10px;
position: relative;
color: var(--sidebar-section-title-color);
text-transform: uppercase;
}
.ItemLink {
padding: 2px 20px;
display: flex;
font-size: 1.1rem;
position: relative;
&.active {
font-weight: bold;
}
}
.TocHeading {
display: flex;
line-height: 1.4;
margin: 5px 0;
position: relative;
&[data-level='2'] {
padding: 0 20px;
&:before {
content: '-';
margin-right: 5px;
color: #979797;
}
}
&[data-level='3'] {
padding: 0 20px 0 40px;
}
&.router-link-exact-active {
font-weight: bold;
}
}
</style>
195 changes: 195 additions & 0 deletions src/components/SidebarItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
<template>
<div :class="['SidebarItem', item.title && 'hasTitle']">
<div
class="ItemTitle"
v-if="item.title"
:collapsable="item.collapsable"
:class="{collapsable: item.collapsable}"
@click="$emit('toggle')"
>
{{ item.title }}
<span
v-if="item.collapsable"
class="arrow"
:class="open ? 'down' : 'right'"
></span>
</div>
<template v-if="!item.collapsable || open">
<template v-for="(link, index) of children">
<a
v-if="isExternalLink(link.link)"
:key="index"
:href="link.link"
class="ItemLink"
target="_blank"
>
{{ link.title }}
<external-link-icon />
</a>
<router-link
v-else
:key="index"
:to="link.link"
:prefetch-files="getPrefetchFiles(link.link)"
class="ItemLink"
:class="{active: $route.path === link.link}"
>
{{ link.title }}
</router-link>
<div
class="LinkToc"
v-if="
!$store.state.fetchingFile &&
link.toc !== false &&
link.link === $route.path &&
$store.state.page.headings &&
$store.state.page.headings.length > 0
"
:key="`toc-${index}`"
>
<router-link
class="TocHeading"
:to="{hash: heading.slug}"
:data-level="heading.level"
v-for="heading in $store.state.page.headings"
:key="heading.slug"
v-html="heading.text"
>
</router-link>
</div>
</template>
</template>
</div>
</template>

<script>
import {isExternalLink, getFileUrl, getFilenameByPath} from '../utils'
export default {
props: {
item: {
type: Object,
required: true,
default() {
return {}
}
},
open: {
type: Boolean,
required: false,
default() {
return true
}
}
},
computed: {
children() {
return this.item.children || this.item.links || []
}
},
methods: {
isExternalLink,
getPrefetchFiles(path) {
const {sourcePath, routes} = this.$store.getters.config
if (routes && routes[path]) {
const {file} = routes[path]
return file ? [file] : []
}
const filename = getFilenameByPath(path)
const fileUrl = getFileUrl(sourcePath, filename)
return fileUrl ? [fileUrl] : []
}
}
}
</script>

<style scoped>
.SidebarItem {
&:not(:last-child) {
margin-bottom: 1.2rem;
}
&.hasTitle {
& .ItemLink {
font-size: 0.9rem;
}
}
&.hasTitle >>> .TocHeading {
font-size: 0.9rem;
}
}
.ItemTitle {
font-size: 1rem;
padding: 0 20px;
margin-bottom: 10px;
position: relative;
color: var(--sidebar-section-title-color);
text-transform: uppercase;
&.collapsable {
&:hover {
cursor: pointer;
}
}
}
.ItemLink {
padding: 2px 20px;
display: flex;
font-size: 1.1rem;
position: relative;
&.active {
font-weight: bold;
}
}
.TocHeading {
display: flex;
line-height: 1.4;
margin: 5px 0;
position: relative;
&[data-level='2'] {
padding: 0 20px;
&:before {
content: '-';
margin-right: 5px;
color: #979797;
}
}
&[data-level='3'] {
padding: 0 20px 0 40px;
}
&.router-link-exact-active {
font-weight: bold;
}
}
a {
text-decoration: none;
color: var(--text-color);
}
.arrow {
display: inline-block;
position: relative;
top: -0.1em;
left: 0.5em;
&.right {
border-left: 6px solid #ccc;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
}
&.down {
border-top: 6px solid #ccc;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
}
}
</style>
Loading

0 comments on commit 4ad0d8d

Please sign in to comment.