提交 19647099 编写于 作者: E Evan You

feat: support nesting in sidebar

上级 11c65282
<template>
<div class="theme-container">
<div class="sidebar">
<ul>
<li v-for="item in sidebarItems">
<router-link v-if="item.type === 'page'" :to="item.path">
{{ item.title || item.path }}
</router-link>
<div class="sidebar-group" v-else-if="item.type === 'heading'">
<p class="sidebar-heading">{{ item.title }}</p>
<ul>
<li v-for="child in item.children">
<router-link v-if="child.type === 'page'" :to="child.path">
{{ child.title || child.path }}
</router-link>
</li>
</ul>
</div>
</li>
</ul>
</div>
<SideBar/>
<Index v-if="$page.path === '/index'" />
<Page v-else />
</div>
......@@ -28,18 +10,10 @@
import nprogress from 'nprogress'
import Index from './Index.vue'
import Page from './Page.vue'
import resolveSidebar from './resolveSidebar'
import SideBar from './SideBar.vue'
export default {
components: { Index, Page },
computed: {
sidebarItems () {
return resolveSidebar(
this.$route,
this.$site
)
}
},
components: { Index, Page, SideBar },
mounted () {
nprogress.configure({ showSpinner: false })
......@@ -57,6 +31,5 @@ export default {
}
</script>
<style src="./nprogress.css"></style>
<style src="prismjs/themes/prism-tomorrow.css"></style>
<style src="./theme.stylus" lang="stylus"></style>
<style src="./styles/theme.stylus" lang="stylus"></style>
<template>
<div class="sidebar">
<ul>
<li v-for="(item, i) in sidebarItems">
<router-link v-if="item.type === 'page'" :to="item.path">
{{ item.title || item.path }}
</router-link>
<div v-else-if="item.type === 'heading'"
class="sidebar-group"
:class="{ first: i === 0 }">
<p class="sidebar-heading">{{ item.title }}</p>
<ul>
<li v-for="child in item.children">
<router-link v-if="child.type === 'page'" :to="child.path">
{{ child.title || child.path }}
</router-link>
</li>
</ul>
</div>
</li>
</ul>
</div>
</template>
<script>
function resolveSidebar (route, site) {
const { pages, themeConfig } = site
const sidebarConfig = themeConfig.sidebar
if (!sidebarConfig) {
return pages
} else {
const matchingConfig = Array.isArray(sidebarConfig)
? sidebarConfig
: resolveMatchingSidebar(route, sidebarConfig)
return matchingConfig.map(item => resolveItem(item, site.pages))
}
}
function resolveMatchingSidebar (route, sidebarConfig) {
for (const base in sidebarConfig) {
if (ensureEndingSlash(route.path).indexOf(base) === 0) {
return sidebarConfig[base]
}
}
}
function ensureEndingSlash (path) {
return /(\.html|\/)$/.test(path)
? path
: path + '/'
}
function resolveItem (item, pages) {
if (typeof item === 'string') {
return resolvePage(pages, item)
} else if (Array.isArray(item)) {
return Object.assign(resolvePage(pages, item[0]), {
title: item[1]
})
} else {
const children = item.children || []
return {
type: 'heading',
title: item.title,
children: children.map(child => resolveItem(child, pages)),
collapsable: children.length && item.collapsable
}
}
}
function resolvePage (pages, rawPath) {
const path = normalize(rawPath)
for (let i = 0; i < pages.length; i++) {
if (normalize(pages[i].path) === path) {
return Object.assign({}, pages[i], {
type: 'page',
path: ensureExt(rawPath)
})
}
}
}
const hashRE = /#.*$/
const extRE = /\.(md|html)$/
const slashRE = /\/$/
function normalize (path) {
return path
.replace(hashRE, '')
.replace(extRE, '')
}
function ensureExt (path) {
if (slashRE.test(path)) {
return path
}
const hashMatch = path.match(hashRE)
const hash = hashMatch ? hashMatch[0] : ''
return normalize(path) + '.html' + hash
}
export default {
computed: {
sidebarItems () {
return resolveSidebar(
this.$route,
this.$site
)
}
}
}
</script>
<style lang="stylus">
@import './styles/config.stylus'
.sidebar
ul
padding 0
margin 0
list-style-type none
a
display inline-block
color $textColor
border-left 0.25rem solid transparent
padding 0.25rem
padding-left 1.25rem
&:hover
color $accentColor
&.router-link-active
font-weight 600
color $accentColor
border-left-color $accentColor
.sidebar-group:not(.first)
margin-top 1.5rem
.sidebar-heading
font-size 1.1em
font-weight bold
text-transform uppercase
padding-left 1.5rem
margin-top 0
margin-bottom 0.5rem
</style>
/*
Copied from nprogress since it doens't
allow programmatic configuration of color
*/
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: #41b883;
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
/* Fancy blur effect */
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px #41b883, 0 0 5px #41b883;
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}
/* Remove these to get rid of the spinner */
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: #41b883;
border-left-color: #41b883;
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
export default function resolveSidebar (route, site) {
const { pages, themeConfig } = site
const sidebarConfig = themeConfig.sidebar
if (!sidebarConfig) {
return pages
} else {
const matchingConfig = Array.isArray(sidebarConfig)
? sidebarConfig
: resolveMatchingSidebar(route, sidebarConfig)
return matchingConfig.map(item => resolveItem(item, site.pages))
}
}
function resolveMatchingSidebar (route, sidebarConfig) {
for (const base in sidebarConfig) {
if (ensureEndingSlash(route.path).indexOf(base) === 0) {
return sidebarConfig[base]
}
}
}
function ensureEndingSlash (path) {
return /(\.html|\/)$/.test(path)
? path
: path + '/'
}
function resolveItem (item, pages) {
if (typeof item === 'string') {
return Object.assign({ type: 'page' }, findPage(pages, item))
} else if (Array.isArray(item)) {
return {
type: 'heading',
title: item[0],
children: (item[1] || []).map(child => resolveItem(child, pages))
}
} else {
throw new Error(`Invalid sidebar item config: ${item}`)
}
}
function findPage (pages, path) {
path = normalize(path)
for (let i = 0; i < pages.length; i++) {
if (normalize(pages[i].path) === path) {
return pages[i]
}
}
}
function normalize (path) {
return path.replace(/\.(md|html)$/, '')
}
$textColor = #2c3e50
$accentColor = darken(#41b883, 5%)
$borderColor = #eaecef
$codeBgColor = #282c34
$sidebarWidth = 20rem
$contentWidth = 760px
#nprogress
pointer-events none
.bar
background $accentColor
position fixed
z-index 1031
top 0
left 0
width 100%
height 2px
.peg
display block
position absolute
right 0px
width 100px
height 100%
box-shadow 0 0 10px $accentColor, 0 0 5px $accentColor
opacity 1.0
transform rotate(3deg) translate(0px, -4px)
.spinner
display block
position fixed
z-index 1031
top 15px
right 15px
.spinner-icon
width 18px
height 18px
box-sizing border-box
border solid 2px transparent
border-top-color $accentColor
border-left-color $accentColor
border-radius 50%
animation nprogress-spinner 400ms linear infinite
.nprogress-custom-parent
overflow hidden
position relative
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar
position absolute
@keyframes nprogress-spinner
0% transform rotate(0deg)
100% transform rotate(360deg)
@import './config.stylus'
@import './nprogress.stylus'
body
font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
font-size 16px
color #2c3e50
color $textColor
.page
padding-left 300px
padding-left $sidebarWidth
.sidebar
width 300px
font-size 15px
width $sidebarWidth
position fixed
margin 0
top 0
left 0
bottom 0
padding 2em 0
padding 2rem 0
box-sizing border-box
border-right 1px solid #eaecef
border-right 1px solid $borderColor
overflow-y scroll
ul
padding 0
margin 0
list-style-type none
a
display inline-block
color #2c3e50
border-left 4px solid transparent
padding 0.25em
padding-left 1.25em
&:hover
color darken(#41b883, 5%)
&.router-link-active
font-weight 600
color darken(#41b883, 5%)
border-left-color darken(#41b883, 5%)
.markdown
max-width 760px
margin 2em auto
padding 0 2em
margin 2rem auto
padding 0 2rem
blockquote
font-size 1.25em
font-size 1.25rem
color #999
border-left .25em solid #dfe2e5
border-left .25rem solid #dfe2e5
margin-left 0
padding-left 1em
padding-left 1rem
ul, ol
padding-left 1.5em
padding-left 1.5rem
a, p a code
color darken(#41b883, 5%)
color $accentColor
text-decoration none
strong
......@@ -62,20 +50,20 @@ h1, h2, h3, h4, h5, h6
font-weight 600
line-height 1.25
&:not(:first-child)
margin-top 1.5em
margin-top 1.5rem
&:hover .header-anchor
opacity: 1
h1
font-size 2.3em
font-size 2.3rem
h2
font-size 1.8em
padding-bottom .3em
border-bottom 1px solid #eaecef
font-size 1.8rem
padding-bottom .3rem
border-bottom 1px solid $borderColor
h3
font-size 1.4em
font-size 1.4rem
a.header-anchor
font-size 0.85em
......@@ -93,42 +81,43 @@ p, ul, ol
p, h1, h2, h3, h4, h5, h6
code
padding 0.2em 0.5em
padding 0.2rem 0.5rem
margin 0
font-size 0.85em
font-size 0.85rem
background-color rgba(27,31,35,0.05)
border-radius 3px
p code
color lighten(#2c3e50, 20%)
color lighten($textColor, 20%)
pre, pre[class*="language-"]
background-color #2d2d2d
color white
background-color $codeBgColor
color #fff
line-height 1.4
border-radius 6px
padding 1.4em 1.6em
padding 1.25rem 1.5rem
white-space pre-wrap
word-break break-word
overflow auto
code
font-size 0.85em
font-size 0.85rem
.highlighted-line
background-color #14161a
background-color rgba(0, 0, 0, 66%)
display block
margin 0 -1.6em
padding 0.2em 1.6em 0
margin 0.1rem -1.8rem 0
padding 0.1rem 1.8rem
.custom-block
.custom-block-title
font-weight 600
text-transform uppercase
margin-bottom -0.4rem
&.tip, &.warning, &.danger
padding .1em 1.4em
border-left-width .5em
padding .1rem 1.5rem
border-left-width .5rem
border-left-style solid
margin 1em 0
margin 1rem 0
&.tip
background-color #f3f5f7
border-color #42b983
......@@ -147,30 +136,41 @@ pre, pre[class*="language-"]
p
&.demo
padding 1rem
padding 1rem 1.5rem
border 1px solid #ddd
border-radius 4px
$mobileSidebarWidth = $sidebarWidth * 0.82
// narrow desktop / iPad
@media (max-width: 959px)
body
font-size 15px
.sidebar
font-size 14px
width 250px
width $mobileSidebarWidth
.page
padding-left 250px
padding-left $mobileSidebarWidth
.markdown
margin 1.5em 0
padding 0 1.5em
margin 1.5rem 0
padding 0 1.5rem
// wide mobile
@media (max-width: 719px)
body
font-size 15px
.sidebar
width 275px
font-size 14px
width $mobileSidebarWidth
display none
.page
padding-left 0
.markdown
margin 1em 0
padding 0 1em
margin 1rem 0
padding 0 1rem
// narrow mobile
@media (max-width: 419px)
pre, pre[class*="language-"]
margin 0 -1.5rem
border-radius 0
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册