Eject vuepress theme to customize 404 page to include sample app deeplinking
This commit is contained in:
parent
f0808e6997
commit
3f9ac3d5f4
156
docs/.vuepress/theme/AlgoliaSearchBox.vue
Normal file
156
docs/.vuepress/theme/AlgoliaSearchBox.vue
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<form
|
||||||
|
id="search-form"
|
||||||
|
class="algolia-search-wrapper search-box"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="algolia-search-input"
|
||||||
|
class="search-query"
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['options'],
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
this.initialize(this.options, this.$lang)
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
initialize (userOptions, lang) {
|
||||||
|
Promise.all([
|
||||||
|
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
|
||||||
|
import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
|
||||||
|
]).then(([docsearch]) => {
|
||||||
|
docsearch = docsearch.default
|
||||||
|
const { algoliaOptions = {}} = userOptions
|
||||||
|
docsearch(Object.assign(
|
||||||
|
{},
|
||||||
|
userOptions,
|
||||||
|
{
|
||||||
|
inputSelector: '#algolia-search-input',
|
||||||
|
// #697 Make docsearch work well at i18n mode.
|
||||||
|
algoliaOptions: Object.assign({
|
||||||
|
'facetFilters': [`lang:${lang}`].concat(algoliaOptions.facetFilters || [])
|
||||||
|
}, algoliaOptions)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
update (options, lang) {
|
||||||
|
this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
|
||||||
|
this.initialize(options, lang)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
$lang (newValue) {
|
||||||
|
this.update(this.options, newValue)
|
||||||
|
},
|
||||||
|
|
||||||
|
options (newValue) {
|
||||||
|
this.update(newValue, this.$lang)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.algolia-search-wrapper
|
||||||
|
& > span
|
||||||
|
vertical-align middle
|
||||||
|
.algolia-autocomplete
|
||||||
|
line-height normal
|
||||||
|
.ds-dropdown-menu
|
||||||
|
background-color #fff
|
||||||
|
border 1px solid #999
|
||||||
|
border-radius 4px
|
||||||
|
font-size 16px
|
||||||
|
margin 6px 0 0
|
||||||
|
padding 4px
|
||||||
|
text-align left
|
||||||
|
&:before
|
||||||
|
border-color #999
|
||||||
|
[class*=ds-dataset-]
|
||||||
|
border none
|
||||||
|
padding 0
|
||||||
|
.ds-suggestions
|
||||||
|
margin-top 0
|
||||||
|
.ds-suggestion
|
||||||
|
border-bottom 1px solid $borderColor
|
||||||
|
.algolia-docsearch-suggestion--highlight
|
||||||
|
color #2c815b
|
||||||
|
.algolia-docsearch-suggestion
|
||||||
|
border-color $borderColor
|
||||||
|
padding 0
|
||||||
|
.algolia-docsearch-suggestion--category-header
|
||||||
|
padding 5px 10px
|
||||||
|
margin-top 0
|
||||||
|
background $accentColor
|
||||||
|
color #fff
|
||||||
|
font-weight 600
|
||||||
|
.algolia-docsearch-suggestion--highlight
|
||||||
|
background rgba(255, 255, 255, 0.6)
|
||||||
|
.algolia-docsearch-suggestion--wrapper
|
||||||
|
padding 0
|
||||||
|
.algolia-docsearch-suggestion--title
|
||||||
|
font-weight 600
|
||||||
|
margin-bottom 0
|
||||||
|
color $textColor
|
||||||
|
.algolia-docsearch-suggestion--subcategory-column
|
||||||
|
vertical-align top
|
||||||
|
padding 5px 7px 5px 5px
|
||||||
|
border-color $borderColor
|
||||||
|
background #f1f3f5
|
||||||
|
&:after
|
||||||
|
display none
|
||||||
|
.algolia-docsearch-suggestion--subcategory-column-text
|
||||||
|
color #555
|
||||||
|
.algolia-docsearch-footer
|
||||||
|
border-color $borderColor
|
||||||
|
.ds-cursor .algolia-docsearch-suggestion--content
|
||||||
|
background-color #e7edf3 !important
|
||||||
|
color $textColor
|
||||||
|
|
||||||
|
@media (min-width: $MQMobile)
|
||||||
|
.algolia-search-wrapper
|
||||||
|
.algolia-autocomplete
|
||||||
|
.algolia-docsearch-suggestion
|
||||||
|
.algolia-docsearch-suggestion--subcategory-column
|
||||||
|
float none
|
||||||
|
width 150px
|
||||||
|
min-width 150px
|
||||||
|
display table-cell
|
||||||
|
.algolia-docsearch-suggestion--content
|
||||||
|
float none
|
||||||
|
display table-cell
|
||||||
|
width 100%
|
||||||
|
vertical-align top
|
||||||
|
.ds-dropdown-menu
|
||||||
|
min-width 515px !important
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.algolia-search-wrapper
|
||||||
|
.ds-dropdown-menu
|
||||||
|
min-width calc(100vw - 4rem) !important
|
||||||
|
max-width calc(100vw - 4rem) !important
|
||||||
|
.algolia-docsearch-suggestion--wrapper
|
||||||
|
padding 5px 7px 5px 5px !important
|
||||||
|
.algolia-docsearch-suggestion--subcategory-column
|
||||||
|
padding 0 !important
|
||||||
|
background white !important
|
||||||
|
.algolia-docsearch-suggestion--subcategory-column-text:after
|
||||||
|
content " > "
|
||||||
|
font-size 10px
|
||||||
|
line-height 14.4px
|
||||||
|
display inline-block
|
||||||
|
width 5px
|
||||||
|
margin -3px 3px 0
|
||||||
|
vertical-align middle
|
||||||
|
|
||||||
|
</style>
|
181
docs/.vuepress/theme/DropdownLink.vue
Normal file
181
docs/.vuepress/theme/DropdownLink.vue
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="dropdown-wrapper"
|
||||||
|
:class="{ open }"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="dropdown-title"
|
||||||
|
@click="toggle"
|
||||||
|
>
|
||||||
|
<span class="title">{{ item.text }}</span>
|
||||||
|
<span
|
||||||
|
class="arrow"
|
||||||
|
:class="open ? 'down' : 'right'"
|
||||||
|
></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<DropdownTransition>
|
||||||
|
<ul
|
||||||
|
class="nav-dropdown"
|
||||||
|
v-show="open"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
class="dropdown-item"
|
||||||
|
:key="subItem.link || index"
|
||||||
|
v-for="(subItem, index) in item.items"
|
||||||
|
>
|
||||||
|
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
class="dropdown-subitem-wrapper"
|
||||||
|
v-if="subItem.type === 'links'"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
class="dropdown-subitem"
|
||||||
|
:key="childSubItem.link"
|
||||||
|
v-for="childSubItem in subItem.items"
|
||||||
|
>
|
||||||
|
<NavLink :item="childSubItem"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<NavLink
|
||||||
|
v-else
|
||||||
|
:item="subItem"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</DropdownTransition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import NavLink from './NavLink.vue'
|
||||||
|
import DropdownTransition from './DropdownTransition.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { NavLink, DropdownTransition },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
open: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggle () {
|
||||||
|
this.open = !this.open
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.dropdown-wrapper
|
||||||
|
cursor pointer
|
||||||
|
.dropdown-title
|
||||||
|
display block
|
||||||
|
&:hover
|
||||||
|
border-color transparent
|
||||||
|
.arrow
|
||||||
|
vertical-align middle
|
||||||
|
margin-top -1px
|
||||||
|
margin-left 0.4rem
|
||||||
|
.nav-dropdown
|
||||||
|
.dropdown-item
|
||||||
|
color inherit
|
||||||
|
line-height 1.7rem
|
||||||
|
h4
|
||||||
|
margin 0.45rem 0 0
|
||||||
|
border-top 1px solid #eee
|
||||||
|
padding 0.45rem 1.5rem 0 1.25rem
|
||||||
|
.dropdown-subitem-wrapper
|
||||||
|
padding 0
|
||||||
|
list-style none
|
||||||
|
.dropdown-subitem
|
||||||
|
font-size 0.9em
|
||||||
|
a
|
||||||
|
display block
|
||||||
|
line-height 1.7rem
|
||||||
|
position relative
|
||||||
|
border-bottom none
|
||||||
|
font-weight 400
|
||||||
|
margin-bottom 0
|
||||||
|
padding 0 1.5rem 0 1.25rem
|
||||||
|
&:hover
|
||||||
|
color $accentColor
|
||||||
|
&.router-link-active
|
||||||
|
color $accentColor
|
||||||
|
&::after
|
||||||
|
content ""
|
||||||
|
width 0
|
||||||
|
height 0
|
||||||
|
border-left 5px solid $accentColor
|
||||||
|
border-top 3px solid transparent
|
||||||
|
border-bottom 3px solid transparent
|
||||||
|
position absolute
|
||||||
|
top calc(50% - 2px)
|
||||||
|
left 9px
|
||||||
|
&:first-child h4
|
||||||
|
margin-top 0
|
||||||
|
padding-top 0
|
||||||
|
border-top 0
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.dropdown-wrapper
|
||||||
|
&.open .dropdown-title
|
||||||
|
margin-bottom 0.5rem
|
||||||
|
.nav-dropdown
|
||||||
|
transition height .1s ease-out
|
||||||
|
overflow hidden
|
||||||
|
.dropdown-item
|
||||||
|
h4
|
||||||
|
border-top 0
|
||||||
|
margin-top 0
|
||||||
|
padding-top 0
|
||||||
|
h4, & > a
|
||||||
|
font-size 15px
|
||||||
|
line-height 2rem
|
||||||
|
.dropdown-subitem
|
||||||
|
font-size 14px
|
||||||
|
padding-left 1rem
|
||||||
|
|
||||||
|
@media (min-width: $MQMobile)
|
||||||
|
.dropdown-wrapper
|
||||||
|
height 1.8rem
|
||||||
|
&:hover .nav-dropdown
|
||||||
|
// override the inline style.
|
||||||
|
display block !important
|
||||||
|
.dropdown-title .arrow
|
||||||
|
// make the arrow always down at desktop
|
||||||
|
border-left 4px solid transparent
|
||||||
|
border-right 4px solid transparent
|
||||||
|
border-top 6px solid $arrowBgColor
|
||||||
|
border-bottom 0
|
||||||
|
.nav-dropdown
|
||||||
|
display none
|
||||||
|
// Avoid height shaked by clicking
|
||||||
|
height auto !important
|
||||||
|
box-sizing border-box;
|
||||||
|
max-height calc(100vh - 2.7rem)
|
||||||
|
overflow-y auto
|
||||||
|
position absolute
|
||||||
|
top 100%
|
||||||
|
right 0
|
||||||
|
background-color #fff
|
||||||
|
padding 0.6rem 0
|
||||||
|
border 1px solid #ddd
|
||||||
|
border-bottom-color #ccc
|
||||||
|
text-align left
|
||||||
|
border-radius 0.25rem
|
||||||
|
white-space nowrap
|
||||||
|
margin 0
|
||||||
|
</style>
|
33
docs/.vuepress/theme/DropdownTransition.vue
Normal file
33
docs/.vuepress/theme/DropdownTransition.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<transition
|
||||||
|
name="dropdown"
|
||||||
|
@enter="setHeight"
|
||||||
|
@after-enter="unsetHeight"
|
||||||
|
@before-leave="setHeight"
|
||||||
|
>
|
||||||
|
<slot/>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'DropdownTransition',
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setHeight (items) {
|
||||||
|
// explicitly set height so that it can be transitioned
|
||||||
|
items.style.height = items.scrollHeight + 'px'
|
||||||
|
},
|
||||||
|
|
||||||
|
unsetHeight (items) {
|
||||||
|
items.style.height = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
.dropdown-enter, .dropdown-leave-to
|
||||||
|
height 0 !important
|
||||||
|
|
||||||
|
</style>
|
162
docs/.vuepress/theme/Home.vue
Normal file
162
docs/.vuepress/theme/Home.vue
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<template>
|
||||||
|
<div class="home">
|
||||||
|
<div class="hero">
|
||||||
|
<img
|
||||||
|
v-if="data.heroImage"
|
||||||
|
:src="$withBase(data.heroImage)"
|
||||||
|
alt="hero"
|
||||||
|
>
|
||||||
|
|
||||||
|
<h1>{{ data.heroText || $title || 'Hello' }}</h1>
|
||||||
|
|
||||||
|
<p class="description">
|
||||||
|
{{ data.tagline || $description || 'Welcome to your VuePress site' }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p
|
||||||
|
class="action"
|
||||||
|
v-if="data.actionText && data.actionLink"
|
||||||
|
>
|
||||||
|
<NavLink
|
||||||
|
class="action-button"
|
||||||
|
:item="actionLink"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="features"
|
||||||
|
v-if="data.features && data.features.length"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="feature"
|
||||||
|
v-for="(feature, index) in data.features"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<h2>{{ feature.title }}</h2>
|
||||||
|
<p>{{ feature.details }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Content custom/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="footer"
|
||||||
|
v-if="data.footer"
|
||||||
|
>
|
||||||
|
{{ data.footer }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import NavLink from './NavLink.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { NavLink },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
data () {
|
||||||
|
return this.$page.frontmatter
|
||||||
|
},
|
||||||
|
|
||||||
|
actionLink () {
|
||||||
|
return {
|
||||||
|
link: this.data.actionLink,
|
||||||
|
text: this.data.actionText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.home
|
||||||
|
padding $navbarHeight 2rem 0
|
||||||
|
max-width 960px
|
||||||
|
margin 0px auto
|
||||||
|
.hero
|
||||||
|
text-align center
|
||||||
|
img
|
||||||
|
max-height 280px
|
||||||
|
display block
|
||||||
|
margin 3rem auto 1.5rem
|
||||||
|
h1
|
||||||
|
font-size 3rem
|
||||||
|
h1, .description, .action
|
||||||
|
margin 1.8rem auto
|
||||||
|
.description
|
||||||
|
max-width 35rem
|
||||||
|
font-size 1.6rem
|
||||||
|
line-height 1.3
|
||||||
|
color lighten($textColor, 40%)
|
||||||
|
.action-button
|
||||||
|
display inline-block
|
||||||
|
font-size 1.2rem
|
||||||
|
color #fff
|
||||||
|
background-color $accentColor
|
||||||
|
padding 0.8rem 1.6rem
|
||||||
|
border-radius 4px
|
||||||
|
transition background-color .1s ease
|
||||||
|
box-sizing border-box
|
||||||
|
border-bottom 1px solid darken($accentColor, 10%)
|
||||||
|
&:hover
|
||||||
|
background-color lighten($accentColor, 10%)
|
||||||
|
.features
|
||||||
|
border-top 1px solid $borderColor
|
||||||
|
padding 1.2rem 0
|
||||||
|
margin-top 2.5rem
|
||||||
|
display flex
|
||||||
|
flex-wrap wrap
|
||||||
|
align-items flex-start
|
||||||
|
align-content stretch
|
||||||
|
justify-content space-between
|
||||||
|
.feature
|
||||||
|
flex-grow 1
|
||||||
|
flex-basis 30%
|
||||||
|
max-width 30%
|
||||||
|
h2
|
||||||
|
font-size 1.4rem
|
||||||
|
font-weight 500
|
||||||
|
border-bottom none
|
||||||
|
padding-bottom 0
|
||||||
|
color lighten($textColor, 10%)
|
||||||
|
p
|
||||||
|
color lighten($textColor, 25%)
|
||||||
|
.footer
|
||||||
|
padding 2.5rem
|
||||||
|
border-top 1px solid $borderColor
|
||||||
|
text-align center
|
||||||
|
color lighten($textColor, 25%)
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.home
|
||||||
|
.features
|
||||||
|
flex-direction column
|
||||||
|
.feature
|
||||||
|
max-width 100%
|
||||||
|
padding 0 2.5rem
|
||||||
|
|
||||||
|
@media (max-width: $MQMobileNarrow)
|
||||||
|
.home
|
||||||
|
padding-left 1.5rem
|
||||||
|
padding-right 1.5rem
|
||||||
|
.hero
|
||||||
|
img
|
||||||
|
max-height 210px
|
||||||
|
margin 2rem auto 1.2rem
|
||||||
|
h1
|
||||||
|
font-size 2rem
|
||||||
|
h1, .description, .action
|
||||||
|
margin 1.2rem auto
|
||||||
|
.description
|
||||||
|
font-size 1.2rem
|
||||||
|
.action-button
|
||||||
|
font-size 1rem
|
||||||
|
padding 0.6rem 1.2rem
|
||||||
|
.feature
|
||||||
|
h2
|
||||||
|
font-size 1.25rem
|
||||||
|
</style>
|
183
docs/.vuepress/theme/Layout.vue
Normal file
183
docs/.vuepress/theme/Layout.vue
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="theme-container"
|
||||||
|
:class="pageClasses"
|
||||||
|
@touchstart="onTouchStart"
|
||||||
|
@touchend="onTouchEnd"
|
||||||
|
>
|
||||||
|
<Navbar
|
||||||
|
v-if="shouldShowNavbar"
|
||||||
|
@toggle-sidebar="toggleSidebar"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="sidebar-mask"
|
||||||
|
@click="toggleSidebar(false)"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<Sidebar
|
||||||
|
:items="sidebarItems"
|
||||||
|
@toggle-sidebar="toggleSidebar"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
name="sidebar-top"
|
||||||
|
slot="top"
|
||||||
|
/>
|
||||||
|
<slot
|
||||||
|
name="sidebar-bottom"
|
||||||
|
slot="bottom"
|
||||||
|
/>
|
||||||
|
</Sidebar>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="custom-layout"
|
||||||
|
v-if="$page.frontmatter.layout"
|
||||||
|
>
|
||||||
|
<component :is="$page.frontmatter.layout"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Home v-else-if="$page.frontmatter.home"/>
|
||||||
|
|
||||||
|
<Page
|
||||||
|
v-else
|
||||||
|
:sidebar-items="sidebarItems"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
name="page-top"
|
||||||
|
slot="top"
|
||||||
|
/>
|
||||||
|
<slot
|
||||||
|
name="page-bottom"
|
||||||
|
slot="bottom"
|
||||||
|
/>
|
||||||
|
</Page>
|
||||||
|
|
||||||
|
<SWUpdatePopup :updateEvent="swUpdateEvent"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import nprogress from 'nprogress'
|
||||||
|
import Home from './Home.vue'
|
||||||
|
import Navbar from './Navbar.vue'
|
||||||
|
import Page from './Page.vue'
|
||||||
|
import Sidebar from './Sidebar.vue'
|
||||||
|
import SWUpdatePopup from './SWUpdatePopup.vue'
|
||||||
|
import { resolveSidebarItems } from './util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { Home, Page, Sidebar, Navbar, SWUpdatePopup },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isSidebarOpen: false,
|
||||||
|
swUpdateEvent: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
shouldShowNavbar () {
|
||||||
|
const { themeConfig } = this.$site
|
||||||
|
const { frontmatter } = this.$page
|
||||||
|
if (
|
||||||
|
frontmatter.navbar === false ||
|
||||||
|
themeConfig.navbar === false) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
this.$title ||
|
||||||
|
themeConfig.logo ||
|
||||||
|
themeConfig.repo ||
|
||||||
|
themeConfig.nav ||
|
||||||
|
this.$themeLocaleConfig.nav
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldShowSidebar () {
|
||||||
|
const { frontmatter } = this.$page
|
||||||
|
return (
|
||||||
|
!frontmatter.layout &&
|
||||||
|
!frontmatter.home &&
|
||||||
|
frontmatter.sidebar !== false &&
|
||||||
|
this.sidebarItems.length
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
sidebarItems () {
|
||||||
|
return resolveSidebarItems(
|
||||||
|
this.$page,
|
||||||
|
this.$route,
|
||||||
|
this.$site,
|
||||||
|
this.$localePath
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
pageClasses () {
|
||||||
|
const userPageClass = this.$page.frontmatter.pageClass
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'no-navbar': !this.shouldShowNavbar,
|
||||||
|
'sidebar-open': this.isSidebarOpen,
|
||||||
|
'no-sidebar': !this.shouldShowSidebar
|
||||||
|
},
|
||||||
|
userPageClass
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
window.addEventListener('scroll', this.onScroll)
|
||||||
|
|
||||||
|
// configure progress bar
|
||||||
|
nprogress.configure({ showSpinner: false })
|
||||||
|
|
||||||
|
this.$router.beforeEach((to, from, next) => {
|
||||||
|
if (to.path !== from.path && !Vue.component(to.name)) {
|
||||||
|
nprogress.start()
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$router.afterEach(() => {
|
||||||
|
nprogress.done()
|
||||||
|
this.isSidebarOpen = false
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$on('sw-updated', this.onSWUpdated)
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleSidebar (to) {
|
||||||
|
this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
|
||||||
|
},
|
||||||
|
|
||||||
|
// side swipe
|
||||||
|
onTouchStart (e) {
|
||||||
|
this.touchStart = {
|
||||||
|
x: e.changedTouches[0].clientX,
|
||||||
|
y: e.changedTouches[0].clientY
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onTouchEnd (e) {
|
||||||
|
const dx = e.changedTouches[0].clientX - this.touchStart.x
|
||||||
|
const dy = e.changedTouches[0].clientY - this.touchStart.y
|
||||||
|
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
|
||||||
|
if (dx > 0 && this.touchStart.x <= 80) {
|
||||||
|
this.toggleSidebar(true)
|
||||||
|
} else {
|
||||||
|
this.toggleSidebar(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onSWUpdated (e) {
|
||||||
|
this.swUpdateEvent = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style src="prismjs/themes/prism-tomorrow.css"></style>
|
||||||
|
<style src="./styles/theme.styl" lang="stylus"></style>
|
49
docs/.vuepress/theme/NavLink.vue
Normal file
49
docs/.vuepress/theme/NavLink.vue
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<router-link
|
||||||
|
class="nav-link"
|
||||||
|
:to="link"
|
||||||
|
v-if="!isExternal(link)"
|
||||||
|
:exact="exact"
|
||||||
|
>{{ item.text }}</router-link>
|
||||||
|
<a
|
||||||
|
v-else
|
||||||
|
:href="link"
|
||||||
|
class="nav-link external"
|
||||||
|
:target="isMailto(link) || isTel(link) ? null : '_blank'"
|
||||||
|
:rel="isMailto(link) || isTel(link) ? null : 'noopener noreferrer'"
|
||||||
|
>
|
||||||
|
{{ item.text }}
|
||||||
|
<OutboundLink/>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { isExternal, isMailto, isTel, ensureExt } from './util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
link () {
|
||||||
|
return ensureExt(this.item.link)
|
||||||
|
},
|
||||||
|
|
||||||
|
exact () {
|
||||||
|
if (this.$site.locales) {
|
||||||
|
return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
|
||||||
|
}
|
||||||
|
return this.link === '/'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
isExternal,
|
||||||
|
isMailto,
|
||||||
|
isTel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
151
docs/.vuepress/theme/NavLinks.vue
Normal file
151
docs/.vuepress/theme/NavLinks.vue
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<nav
|
||||||
|
class="nav-links"
|
||||||
|
v-if="userLinks.length || repoLink"
|
||||||
|
>
|
||||||
|
<!-- user links -->
|
||||||
|
<div
|
||||||
|
class="nav-item"
|
||||||
|
v-for="item in userLinks"
|
||||||
|
:key="item.link"
|
||||||
|
>
|
||||||
|
<DropdownLink
|
||||||
|
v-if="item.type === 'links'"
|
||||||
|
:item="item"
|
||||||
|
/>
|
||||||
|
<NavLink
|
||||||
|
v-else
|
||||||
|
:item="item"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- repo link -->
|
||||||
|
<a
|
||||||
|
v-if="repoLink"
|
||||||
|
:href="repoLink"
|
||||||
|
class="repo-link"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{{ repoLabel }}
|
||||||
|
<OutboundLink/>
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DropdownLink from './DropdownLink.vue'
|
||||||
|
import { resolveNavLinkItem } from './util'
|
||||||
|
import NavLink from './NavLink.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { NavLink, DropdownLink },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
userNav () {
|
||||||
|
return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || []
|
||||||
|
},
|
||||||
|
|
||||||
|
nav () {
|
||||||
|
const { locales } = this.$site
|
||||||
|
if (locales && Object.keys(locales).length > 1) {
|
||||||
|
const currentLink = this.$page.path
|
||||||
|
const routes = this.$router.options.routes
|
||||||
|
const themeLocales = this.$site.themeConfig.locales || {}
|
||||||
|
const languageDropdown = {
|
||||||
|
text: this.$themeLocaleConfig.selectText || 'Languages',
|
||||||
|
items: Object.keys(locales).map(path => {
|
||||||
|
const locale = locales[path]
|
||||||
|
const text = themeLocales[path] && themeLocales[path].label || locale.lang
|
||||||
|
let link
|
||||||
|
// Stay on the current page
|
||||||
|
if (locale.lang === this.$lang) {
|
||||||
|
link = currentLink
|
||||||
|
} else {
|
||||||
|
// Try to stay on the same page
|
||||||
|
link = currentLink.replace(this.$localeConfig.path, path)
|
||||||
|
// fallback to homepage
|
||||||
|
if (!routes.some(route => route.path === link)) {
|
||||||
|
link = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { text, link }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return [...this.userNav, languageDropdown]
|
||||||
|
}
|
||||||
|
return this.userNav
|
||||||
|
},
|
||||||
|
|
||||||
|
userLinks () {
|
||||||
|
return (this.nav || []).map(link => {
|
||||||
|
return Object.assign(resolveNavLinkItem(link), {
|
||||||
|
items: (link.items || []).map(resolveNavLinkItem)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
repoLink () {
|
||||||
|
const { repo } = this.$site.themeConfig
|
||||||
|
if (repo) {
|
||||||
|
return /^https?:/.test(repo)
|
||||||
|
? repo
|
||||||
|
: `https://github.com/${repo}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
repoLabel () {
|
||||||
|
if (!this.repoLink) return
|
||||||
|
if (this.$site.themeConfig.repoLabel) {
|
||||||
|
return this.$site.themeConfig.repoLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0]
|
||||||
|
const platforms = ['GitHub', 'GitLab', 'Bitbucket']
|
||||||
|
for (let i = 0; i < platforms.length; i++) {
|
||||||
|
const platform = platforms[i]
|
||||||
|
if (new RegExp(platform, 'i').test(repoHost)) {
|
||||||
|
return platform
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Source'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.nav-links
|
||||||
|
display inline-block
|
||||||
|
a
|
||||||
|
line-height 1.4rem
|
||||||
|
color inherit
|
||||||
|
&:hover, &.router-link-active
|
||||||
|
color $accentColor
|
||||||
|
.nav-item
|
||||||
|
position relative
|
||||||
|
display inline-block
|
||||||
|
margin-left 1.5rem
|
||||||
|
line-height 2rem
|
||||||
|
&:first-child
|
||||||
|
margin-left 0
|
||||||
|
.repo-link
|
||||||
|
margin-left 1.5rem
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.nav-links
|
||||||
|
.nav-item, .repo-link
|
||||||
|
margin-left 0
|
||||||
|
|
||||||
|
@media (min-width: $MQMobile)
|
||||||
|
.nav-links a
|
||||||
|
&:hover, &.router-link-active
|
||||||
|
color $textColor
|
||||||
|
.nav-item > a:not(.external)
|
||||||
|
&:hover, &.router-link-active
|
||||||
|
margin-bottom -2px
|
||||||
|
border-bottom 2px solid lighten($accentColor, 8%)
|
||||||
|
</style>
|
133
docs/.vuepress/theme/Navbar.vue
Normal file
133
docs/.vuepress/theme/Navbar.vue
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<template>
|
||||||
|
<header class="navbar">
|
||||||
|
<SidebarButton @toggle-sidebar="$emit('toggle-sidebar')"/>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
:to="$localePath"
|
||||||
|
class="home-link"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="logo"
|
||||||
|
v-if="$site.themeConfig.logo"
|
||||||
|
:src="$withBase($site.themeConfig.logo)"
|
||||||
|
:alt="$siteTitle"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
ref="siteName"
|
||||||
|
class="site-name"
|
||||||
|
v-if="$siteTitle"
|
||||||
|
:class="{ 'can-hide': $site.themeConfig.logo }"
|
||||||
|
>{{ $siteTitle }}</span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="links"
|
||||||
|
:style="{
|
||||||
|
'max-width': linksWrapMaxWidth + 'px'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<AlgoliaSearchBox
|
||||||
|
v-if="isAlgoliaSearch"
|
||||||
|
:options="algolia"
|
||||||
|
/>
|
||||||
|
<SearchBox v-else-if="$site.themeConfig.search !== false"/>
|
||||||
|
<NavLinks class="can-hide"/>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SidebarButton from './SidebarButton.vue'
|
||||||
|
import AlgoliaSearchBox from '@AlgoliaSearchBox'
|
||||||
|
import SearchBox from './SearchBox.vue'
|
||||||
|
import NavLinks from './NavLinks.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
linksWrapMaxWidth: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
const MOBILE_DESKTOP_BREAKPOINT = 719 // refer to config.styl
|
||||||
|
const NAVBAR_VERTICAL_PADDING = parseInt(css(this.$el, 'paddingLeft')) + parseInt(css(this.$el, 'paddingRight'))
|
||||||
|
const handleLinksWrapWidth = () => {
|
||||||
|
if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) {
|
||||||
|
this.linksWrapMaxWidth = null
|
||||||
|
} else {
|
||||||
|
this.linksWrapMaxWidth = this.$el.offsetWidth - NAVBAR_VERTICAL_PADDING -
|
||||||
|
(this.$refs.siteName && this.$refs.siteName.offsetWidth || 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleLinksWrapWidth()
|
||||||
|
window.addEventListener('resize', handleLinksWrapWidth, false)
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
algolia () {
|
||||||
|
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
|
||||||
|
},
|
||||||
|
|
||||||
|
isAlgoliaSearch () {
|
||||||
|
return this.algolia && this.algolia.apiKey && this.algolia.indexName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function css (el, property) {
|
||||||
|
// NOTE: Known bug, will return 'auto' if style value is 'auto'
|
||||||
|
const win = el.ownerDocument.defaultView
|
||||||
|
// null means not to return pseudo styles
|
||||||
|
return win.getComputedStyle(el, null)[property]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
$navbar-vertical-padding = 0.7rem
|
||||||
|
$navbar-horizontal-padding = 1.5rem
|
||||||
|
|
||||||
|
.navbar
|
||||||
|
padding $navbar-vertical-padding $navbar-horizontal-padding
|
||||||
|
line-height $navbarHeight - 1.4rem
|
||||||
|
position relative
|
||||||
|
a, span, img
|
||||||
|
display inline-block
|
||||||
|
.logo
|
||||||
|
height $navbarHeight - 1.4rem
|
||||||
|
min-width $navbarHeight - 1.4rem
|
||||||
|
margin-right 0.8rem
|
||||||
|
vertical-align top
|
||||||
|
.site-name
|
||||||
|
font-size 1.3rem
|
||||||
|
font-weight 600
|
||||||
|
color $textColor
|
||||||
|
position relative
|
||||||
|
.links
|
||||||
|
padding-left 1.5rem
|
||||||
|
box-sizing border-box
|
||||||
|
background-color white
|
||||||
|
white-space nowrap
|
||||||
|
font-size 0.9rem
|
||||||
|
position absolute
|
||||||
|
right $navbar-horizontal-padding
|
||||||
|
top $navbar-vertical-padding
|
||||||
|
display flex
|
||||||
|
.search-box
|
||||||
|
flex: 0 0 auto
|
||||||
|
vertical-align top
|
||||||
|
.nav-links
|
||||||
|
flex 1
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.navbar
|
||||||
|
padding-left 4rem
|
||||||
|
.can-hide
|
||||||
|
display none
|
||||||
|
.links
|
||||||
|
padding-left 1.5rem
|
||||||
|
</style>
|
43
docs/.vuepress/theme/NotFound.vue
Normal file
43
docs/.vuepress/theme/NotFound.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<div class="theme-container">
|
||||||
|
<div class="content">
|
||||||
|
<template v-if="isSampleLink()">
|
||||||
|
<h1>Markwon Sample</h1>
|
||||||
|
<span>
|
||||||
|
Download
|
||||||
|
<a href="https://github.com/noties/Markwon/raw/sample-store/markwon-debug.apk">sample app</a> and open
|
||||||
|
<a :href="currentWindowLocation()">this link</a> again
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<h1>404</h1>
|
||||||
|
<blockquote>{{ getMsg() }}</blockquote>
|
||||||
|
<router-link to="/">Take me home.</router-link>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const msgs = [
|
||||||
|
`There's nothing here.`,
|
||||||
|
`How did we get here?`,
|
||||||
|
`That's a Four-Oh-Four.`,
|
||||||
|
`Looks like we've got some broken links.`
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
getMsg() {
|
||||||
|
return msgs[Math.floor(Math.random() * msgs.length)];
|
||||||
|
},
|
||||||
|
isSampleLink() {
|
||||||
|
const path = this.$router.currentRoute.path;
|
||||||
|
return /^\/app\/(sample|search).*$/.test(path);
|
||||||
|
},
|
||||||
|
currentWindowLocation() {
|
||||||
|
return window.location.href
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
246
docs/.vuepress/theme/Page.vue
Normal file
246
docs/.vuepress/theme/Page.vue
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page">
|
||||||
|
<slot name="top"/>
|
||||||
|
|
||||||
|
<Content :custom="false"/>
|
||||||
|
|
||||||
|
<div class="page-edit">
|
||||||
|
<div
|
||||||
|
class="edit-link"
|
||||||
|
v-if="editLink"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
:href="editLink"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>{{ editLinkText }}</a>
|
||||||
|
<OutboundLink/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="last-updated"
|
||||||
|
v-if="lastUpdated"
|
||||||
|
>
|
||||||
|
<span class="prefix">{{ lastUpdatedText }}: </span>
|
||||||
|
<span class="time">{{ lastUpdated }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page-nav" v-if="prev || next">
|
||||||
|
<p class="inner">
|
||||||
|
<span
|
||||||
|
v-if="prev"
|
||||||
|
class="prev"
|
||||||
|
>
|
||||||
|
←
|
||||||
|
<router-link
|
||||||
|
v-if="prev"
|
||||||
|
class="prev"
|
||||||
|
:to="prev.path"
|
||||||
|
>
|
||||||
|
{{ prev.title || prev.path }}
|
||||||
|
</router-link>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="next"
|
||||||
|
class="next"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
v-if="next"
|
||||||
|
:to="next.path"
|
||||||
|
>
|
||||||
|
{{ next.title || next.path }}
|
||||||
|
</router-link>
|
||||||
|
→
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<slot name="bottom"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { resolvePage, normalize, outboundRE, endingSlashRE } from './util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['sidebarItems'],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
lastUpdated () {
|
||||||
|
if (this.$page.lastUpdated) {
|
||||||
|
return new Date(this.$page.lastUpdated).toLocaleString(this.$lang)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
lastUpdatedText () {
|
||||||
|
if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
|
||||||
|
return this.$themeLocaleConfig.lastUpdated
|
||||||
|
}
|
||||||
|
if (typeof this.$site.themeConfig.lastUpdated === 'string') {
|
||||||
|
return this.$site.themeConfig.lastUpdated
|
||||||
|
}
|
||||||
|
return 'Last Updated'
|
||||||
|
},
|
||||||
|
|
||||||
|
prev () {
|
||||||
|
const prev = this.$page.frontmatter.prev
|
||||||
|
if (prev === false) {
|
||||||
|
return
|
||||||
|
} else if (prev) {
|
||||||
|
return resolvePage(this.$site.pages, prev, this.$route.path)
|
||||||
|
} else {
|
||||||
|
return resolvePrev(this.$page, this.sidebarItems)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
next () {
|
||||||
|
const next = this.$page.frontmatter.next
|
||||||
|
if (next === false) {
|
||||||
|
return
|
||||||
|
} else if (next) {
|
||||||
|
return resolvePage(this.$site.pages, next, this.$route.path)
|
||||||
|
} else {
|
||||||
|
return resolveNext(this.$page, this.sidebarItems)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
editLink () {
|
||||||
|
if (this.$page.frontmatter.editLink === false) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
repo,
|
||||||
|
editLinks,
|
||||||
|
docsDir = '',
|
||||||
|
docsBranch = 'master',
|
||||||
|
docsRepo = repo
|
||||||
|
} = this.$site.themeConfig
|
||||||
|
|
||||||
|
let path = normalize(this.$page.path)
|
||||||
|
if (endingSlashRE.test(path)) {
|
||||||
|
path += 'README.md'
|
||||||
|
} else {
|
||||||
|
path += '.md'
|
||||||
|
}
|
||||||
|
if (docsRepo && editLinks) {
|
||||||
|
return this.createEditLink(repo, docsRepo, docsDir, docsBranch, path)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
editLinkText () {
|
||||||
|
return (
|
||||||
|
this.$themeLocaleConfig.editLinkText ||
|
||||||
|
this.$site.themeConfig.editLinkText ||
|
||||||
|
`Edit this page`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
createEditLink (repo, docsRepo, docsDir, docsBranch, path) {
|
||||||
|
const bitbucket = /bitbucket.org/
|
||||||
|
if (bitbucket.test(repo)) {
|
||||||
|
const base = outboundRE.test(docsRepo)
|
||||||
|
? docsRepo
|
||||||
|
: repo
|
||||||
|
return (
|
||||||
|
base.replace(endingSlashRE, '') +
|
||||||
|
`/${docsBranch}` +
|
||||||
|
(docsDir ? '/' + docsDir.replace(endingSlashRE, '') : '') +
|
||||||
|
path +
|
||||||
|
`?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const base = outboundRE.test(docsRepo)
|
||||||
|
? docsRepo
|
||||||
|
: `https://github.com/${docsRepo}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
base.replace(endingSlashRE, '') +
|
||||||
|
`/edit/${docsBranch}` +
|
||||||
|
(docsDir ? '/' + docsDir.replace(endingSlashRE, '') : '') +
|
||||||
|
path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePrev (page, items) {
|
||||||
|
return find(page, items, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveNext (page, items) {
|
||||||
|
return find(page, items, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function find (page, items, offset) {
|
||||||
|
const res = []
|
||||||
|
items.forEach(item => {
|
||||||
|
if (item.type === 'group') {
|
||||||
|
res.push(...item.children || [])
|
||||||
|
} else {
|
||||||
|
res.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
for (let i = 0; i < res.length; i++) {
|
||||||
|
const cur = res[i]
|
||||||
|
if (cur.type === 'page' && cur.path === page.path) {
|
||||||
|
return res[i + offset]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
@require './styles/wrapper.styl'
|
||||||
|
|
||||||
|
.page
|
||||||
|
padding-bottom 2rem
|
||||||
|
|
||||||
|
.page-edit
|
||||||
|
@extend $wrapper
|
||||||
|
padding-top 1rem
|
||||||
|
padding-bottom 1rem
|
||||||
|
overflow auto
|
||||||
|
.edit-link
|
||||||
|
display inline-block
|
||||||
|
a
|
||||||
|
color lighten($textColor, 25%)
|
||||||
|
margin-right 0.25rem
|
||||||
|
.last-updated
|
||||||
|
float right
|
||||||
|
font-size 0.9em
|
||||||
|
.prefix
|
||||||
|
font-weight 500
|
||||||
|
color lighten($textColor, 25%)
|
||||||
|
.time
|
||||||
|
font-weight 400
|
||||||
|
color #aaa
|
||||||
|
|
||||||
|
.page-nav
|
||||||
|
@extend $wrapper
|
||||||
|
padding-top 1rem
|
||||||
|
padding-bottom 0
|
||||||
|
.inner
|
||||||
|
min-height 2rem
|
||||||
|
margin-top 0
|
||||||
|
border-top 1px solid $borderColor
|
||||||
|
padding-top 1rem
|
||||||
|
overflow auto // clear float
|
||||||
|
.next
|
||||||
|
float right
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.page-edit
|
||||||
|
.edit-link
|
||||||
|
margin-bottom .5rem
|
||||||
|
.last-updated
|
||||||
|
font-size .8em
|
||||||
|
float none
|
||||||
|
text-align left
|
||||||
|
|
||||||
|
</style>
|
85
docs/.vuepress/theme/SWUpdatePopup.vue
Normal file
85
docs/.vuepress/theme/SWUpdatePopup.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<transition name="sw-update-popup">
|
||||||
|
<div
|
||||||
|
v-if="enabled"
|
||||||
|
class="sw-update-popup"
|
||||||
|
>
|
||||||
|
{{message}}<br>
|
||||||
|
<button @click="reload">{{buttonText}}</button>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
updateEvent: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
popupConfig () {
|
||||||
|
for (const config of [this.$themeLocaleConfig, this.$site.themeConfig]) {
|
||||||
|
const sw = config.serviceWorker
|
||||||
|
if (sw && sw.updatePopup) {
|
||||||
|
return typeof sw.updatePopup === 'object' ? sw.updatePopup : {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
|
enabled () {
|
||||||
|
return Boolean(this.popupConfig && this.updateEvent)
|
||||||
|
},
|
||||||
|
|
||||||
|
message () {
|
||||||
|
const c = this.popupConfig
|
||||||
|
return (c && c.message) || 'New content is available.'
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonText () {
|
||||||
|
const c = this.popupConfig
|
||||||
|
return (c && c.buttonText) || 'Refresh'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
reload () {
|
||||||
|
if (this.updateEvent) {
|
||||||
|
this.updateEvent.skipWaiting().then(() => {
|
||||||
|
location.reload(true)
|
||||||
|
})
|
||||||
|
this.updateEvent = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.sw-update-popup
|
||||||
|
position fixed
|
||||||
|
right 1em
|
||||||
|
bottom 1em
|
||||||
|
padding 1em
|
||||||
|
border 1px solid $accentColor
|
||||||
|
border-radius 3px
|
||||||
|
background #fff
|
||||||
|
box-shadow 0 4px 16px rgba(0, 0, 0, 0.5)
|
||||||
|
text-align center
|
||||||
|
|
||||||
|
button
|
||||||
|
margin-top 0.5em
|
||||||
|
padding 0.25em 2em
|
||||||
|
|
||||||
|
.sw-update-popup-enter-active, .sw-update-popup-leave-active
|
||||||
|
transition opacity 0.3s, transform 0.3s
|
||||||
|
|
||||||
|
.sw-update-popup-enter, .sw-update-popup-leave-to
|
||||||
|
opacity 0
|
||||||
|
transform translate(0, 50%) scale(0.5)
|
||||||
|
</style>
|
238
docs/.vuepress/theme/SearchBox.vue
Normal file
238
docs/.vuepress/theme/SearchBox.vue
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
<template>
|
||||||
|
<div class="search-box">
|
||||||
|
<input
|
||||||
|
@input="query = $event.target.value"
|
||||||
|
aria-label="Search"
|
||||||
|
:value="query"
|
||||||
|
:class="{ 'focused': focused }"
|
||||||
|
autocomplete="off"
|
||||||
|
spellcheck="false"
|
||||||
|
@focus="focused = true"
|
||||||
|
@blur="focused = false"
|
||||||
|
@keyup.enter="go(focusIndex)"
|
||||||
|
@keyup.up="onUp"
|
||||||
|
@keyup.down="onDown"
|
||||||
|
>
|
||||||
|
<ul
|
||||||
|
class="suggestions"
|
||||||
|
v-if="showSuggestions"
|
||||||
|
:class="{ 'align-right': alignRight }"
|
||||||
|
@mouseleave="unfocus"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
class="suggestion"
|
||||||
|
v-for="(s, i) in suggestions"
|
||||||
|
:class="{ focused: i === focusIndex }"
|
||||||
|
@mousedown="go(i)"
|
||||||
|
@mouseenter="focus(i)"
|
||||||
|
>
|
||||||
|
<a :href="s.path" @click.prevent>
|
||||||
|
<span class="page-title">{{ s.title || s.path }}</span>
|
||||||
|
<span v-if="s.header" class="header">> {{ s.header.title }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
query: '',
|
||||||
|
focused: false,
|
||||||
|
focusIndex: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
showSuggestions () {
|
||||||
|
return (
|
||||||
|
this.focused &&
|
||||||
|
this.suggestions &&
|
||||||
|
this.suggestions.length
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
suggestions () {
|
||||||
|
const query = this.query.trim().toLowerCase()
|
||||||
|
if (!query) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { pages, themeConfig } = this.$site
|
||||||
|
const max = themeConfig.searchMaxSuggestions || 5
|
||||||
|
const localePath = this.$localePath
|
||||||
|
const matches = item => (
|
||||||
|
item.title &&
|
||||||
|
item.title.toLowerCase().indexOf(query) > -1
|
||||||
|
)
|
||||||
|
const res = []
|
||||||
|
for (let i = 0; i < pages.length; i++) {
|
||||||
|
if (res.length >= max) break
|
||||||
|
const p = pages[i]
|
||||||
|
// filter out results that do not match current locale
|
||||||
|
if (this.getPageLocalePath(p) !== localePath) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (matches(p)) {
|
||||||
|
res.push(p)
|
||||||
|
} else if (p.headers) {
|
||||||
|
for (let j = 0; j < p.headers.length; j++) {
|
||||||
|
if (res.length >= max) break
|
||||||
|
const h = p.headers[j]
|
||||||
|
if (matches(h)) {
|
||||||
|
res.push(Object.assign({}, p, {
|
||||||
|
path: p.path + '#' + h.slug,
|
||||||
|
header: h
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
|
||||||
|
// make suggestions align right when there are not enough items
|
||||||
|
alignRight () {
|
||||||
|
const navCount = (this.$site.themeConfig.nav || []).length
|
||||||
|
const repo = this.$site.repo ? 1 : 0
|
||||||
|
return navCount + repo <= 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getPageLocalePath (page) {
|
||||||
|
for (const localePath in this.$site.locales || {}) {
|
||||||
|
if (localePath !== '/' && page.path.indexOf(localePath) === 0) {
|
||||||
|
return localePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '/'
|
||||||
|
},
|
||||||
|
|
||||||
|
onUp () {
|
||||||
|
if (this.showSuggestions) {
|
||||||
|
if (this.focusIndex > 0) {
|
||||||
|
this.focusIndex--
|
||||||
|
} else {
|
||||||
|
this.focusIndex = this.suggestions.length - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onDown () {
|
||||||
|
if (this.showSuggestions) {
|
||||||
|
if (this.focusIndex < this.suggestions.length - 1) {
|
||||||
|
this.focusIndex++
|
||||||
|
} else {
|
||||||
|
this.focusIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
go (i) {
|
||||||
|
if (!this.showSuggestions) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$router.push(this.suggestions[i].path)
|
||||||
|
this.query = ''
|
||||||
|
this.focusIndex = 0
|
||||||
|
},
|
||||||
|
|
||||||
|
focus (i) {
|
||||||
|
this.focusIndex = i
|
||||||
|
},
|
||||||
|
|
||||||
|
unfocus () {
|
||||||
|
this.focusIndex = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.search-box
|
||||||
|
display inline-block
|
||||||
|
position relative
|
||||||
|
margin-right 1rem
|
||||||
|
input
|
||||||
|
cursor text
|
||||||
|
width 10rem
|
||||||
|
color lighten($textColor, 25%)
|
||||||
|
display inline-block
|
||||||
|
border 1px solid darken($borderColor, 10%)
|
||||||
|
border-radius 2rem
|
||||||
|
font-size 0.9rem
|
||||||
|
line-height 2rem
|
||||||
|
padding 0 0.5rem 0 2rem
|
||||||
|
outline none
|
||||||
|
transition all .2s ease
|
||||||
|
background #fff url(./search.svg) 0.6rem 0.5rem no-repeat
|
||||||
|
background-size 1rem
|
||||||
|
&:focus
|
||||||
|
cursor auto
|
||||||
|
border-color $accentColor
|
||||||
|
.suggestions
|
||||||
|
background #fff
|
||||||
|
width 20rem
|
||||||
|
position absolute
|
||||||
|
top 1.5rem
|
||||||
|
border 1px solid darken($borderColor, 10%)
|
||||||
|
border-radius 6px
|
||||||
|
padding 0.4rem
|
||||||
|
list-style-type none
|
||||||
|
&.align-right
|
||||||
|
right 0
|
||||||
|
.suggestion
|
||||||
|
line-height 1.4
|
||||||
|
padding 0.4rem 0.6rem
|
||||||
|
border-radius 4px
|
||||||
|
cursor pointer
|
||||||
|
a
|
||||||
|
white-space normal
|
||||||
|
color lighten($textColor, 35%)
|
||||||
|
.page-title
|
||||||
|
font-weight 600
|
||||||
|
.header
|
||||||
|
font-size 0.9em
|
||||||
|
margin-left 0.25em
|
||||||
|
&.focused
|
||||||
|
background-color #f3f4f5
|
||||||
|
a
|
||||||
|
color $accentColor
|
||||||
|
|
||||||
|
@media (max-width: $MQNarrow)
|
||||||
|
.search-box
|
||||||
|
input
|
||||||
|
cursor pointer
|
||||||
|
width 0
|
||||||
|
border-color transparent
|
||||||
|
position relative
|
||||||
|
&:focus
|
||||||
|
cursor text
|
||||||
|
left 0
|
||||||
|
width 10rem
|
||||||
|
|
||||||
|
@media (max-width: $MQNarrow) and (min-width: $MQMobile)
|
||||||
|
.search-box
|
||||||
|
.suggestions
|
||||||
|
left 0
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.search-box
|
||||||
|
margin-right 0
|
||||||
|
input
|
||||||
|
left 1rem
|
||||||
|
.suggestions
|
||||||
|
right 0
|
||||||
|
|
||||||
|
@media (max-width: $MQMobileNarrow)
|
||||||
|
.search-box
|
||||||
|
.suggestions
|
||||||
|
width calc(100vw - 4rem)
|
||||||
|
input:focus
|
||||||
|
width 8rem
|
||||||
|
</style>
|
113
docs/.vuepress/theme/Sidebar.vue
Normal file
113
docs/.vuepress/theme/Sidebar.vue
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sidebar">
|
||||||
|
<NavLinks/>
|
||||||
|
<slot name="top"/>
|
||||||
|
<ul class="sidebar-links" v-if="items.length">
|
||||||
|
<li v-for="(item, i) in items" :key="i">
|
||||||
|
<SidebarGroup
|
||||||
|
v-if="item.type === 'group'"
|
||||||
|
:item="item"
|
||||||
|
:first="i === 0"
|
||||||
|
:open="i === openGroupIndex"
|
||||||
|
:collapsable="item.collapsable || item.collapsible"
|
||||||
|
@toggle="toggleGroup(i)"
|
||||||
|
/>
|
||||||
|
<SidebarLink v-else :item="item"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<slot name="bottom"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SidebarGroup from './SidebarGroup.vue'
|
||||||
|
import SidebarLink from './SidebarLink.vue'
|
||||||
|
import NavLinks from './NavLinks.vue'
|
||||||
|
import { isActive } from './util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { SidebarGroup, SidebarLink, NavLinks },
|
||||||
|
|
||||||
|
props: ['items'],
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
openGroupIndex: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created () {
|
||||||
|
this.refreshIndex()
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'$route' () {
|
||||||
|
this.refreshIndex()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
refreshIndex () {
|
||||||
|
const index = resolveOpenGroupIndex(
|
||||||
|
this.$route,
|
||||||
|
this.items
|
||||||
|
)
|
||||||
|
if (index > -1) {
|
||||||
|
this.openGroupIndex = index
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleGroup (index) {
|
||||||
|
this.openGroupIndex = index === this.openGroupIndex ? -1 : index
|
||||||
|
},
|
||||||
|
|
||||||
|
isActive (page) {
|
||||||
|
return isActive(this.$route, page.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveOpenGroupIndex (route, items) {
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i]
|
||||||
|
if (item.type === 'group' && item.children.some(c => isActive(route, c.path))) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.sidebar
|
||||||
|
ul
|
||||||
|
padding 0
|
||||||
|
margin 0
|
||||||
|
list-style-type none
|
||||||
|
a
|
||||||
|
display inline-block
|
||||||
|
.nav-links
|
||||||
|
display none
|
||||||
|
border-bottom 1px solid $borderColor
|
||||||
|
padding 0.5rem 0 0.75rem 0
|
||||||
|
a
|
||||||
|
font-weight 600
|
||||||
|
.nav-item, .repo-link
|
||||||
|
display block
|
||||||
|
line-height 1.25rem
|
||||||
|
font-size 1.1em
|
||||||
|
padding 0.5rem 0 0.5rem 1.5rem
|
||||||
|
.sidebar-links
|
||||||
|
padding 1.5rem 0
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.sidebar
|
||||||
|
.nav-links
|
||||||
|
display block
|
||||||
|
.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
|
||||||
|
top calc(1rem - 2px)
|
||||||
|
.sidebar-links
|
||||||
|
padding 1rem 0
|
||||||
|
</style>
|
28
docs/.vuepress/theme/SidebarButton.vue
Normal file
28
docs/.vuepress/theme/SidebarButton.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sidebar-button" @click="$emit('toggle-sidebar')">
|
||||||
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512">
|
||||||
|
<path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class=""></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.sidebar-button
|
||||||
|
display none
|
||||||
|
width 1.25rem
|
||||||
|
height 1.25rem
|
||||||
|
position absolute
|
||||||
|
padding 0.6rem
|
||||||
|
top 0.6rem
|
||||||
|
left 1rem
|
||||||
|
.icon
|
||||||
|
display block
|
||||||
|
width 1.25rem
|
||||||
|
height 1.25rem
|
||||||
|
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.sidebar-button
|
||||||
|
display block
|
||||||
|
</style>
|
77
docs/.vuepress/theme/SidebarGroup.vue
Normal file
77
docs/.vuepress/theme/SidebarGroup.vue
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="sidebar-group"
|
||||||
|
:class="{ first, collapsable }"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="sidebar-heading"
|
||||||
|
:class="{ open }"
|
||||||
|
@click="$emit('toggle')"
|
||||||
|
>
|
||||||
|
<span>{{ item.title }}</span>
|
||||||
|
<span
|
||||||
|
class="arrow"
|
||||||
|
v-if="collapsable"
|
||||||
|
:class="open ? 'down' : 'right'">
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<DropdownTransition>
|
||||||
|
<ul
|
||||||
|
ref="items"
|
||||||
|
class="sidebar-group-items"
|
||||||
|
v-if="open || !collapsable"
|
||||||
|
>
|
||||||
|
<li v-for="child in item.children">
|
||||||
|
<SidebarLink :item="child"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</DropdownTransition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SidebarLink from './SidebarLink.vue'
|
||||||
|
import DropdownTransition from './DropdownTransition.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SidebarGroup',
|
||||||
|
props: ['item', 'first', 'open', 'collapsable'],
|
||||||
|
components: { SidebarLink, DropdownTransition }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
.sidebar-group
|
||||||
|
&:not(.first)
|
||||||
|
margin-top 1em
|
||||||
|
.sidebar-group
|
||||||
|
padding-left 0.5em
|
||||||
|
&:not(.collapsable)
|
||||||
|
.sidebar-heading
|
||||||
|
cursor auto
|
||||||
|
color inherit
|
||||||
|
|
||||||
|
.sidebar-heading
|
||||||
|
color #999
|
||||||
|
transition color .15s ease
|
||||||
|
cursor pointer
|
||||||
|
font-size 1.1em
|
||||||
|
font-weight bold
|
||||||
|
// text-transform uppercase
|
||||||
|
padding 0 1.5rem
|
||||||
|
margin-top 0
|
||||||
|
margin-bottom 0.5rem
|
||||||
|
&.open, &:hover
|
||||||
|
color inherit
|
||||||
|
.arrow
|
||||||
|
position relative
|
||||||
|
top -0.12em
|
||||||
|
left 0.5em
|
||||||
|
&:.open .arrow
|
||||||
|
top -0.18em
|
||||||
|
|
||||||
|
.sidebar-group-items
|
||||||
|
transition height .1s ease-out
|
||||||
|
overflow hidden
|
||||||
|
</style>
|
91
docs/.vuepress/theme/SidebarLink.vue
Normal file
91
docs/.vuepress/theme/SidebarLink.vue
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<script>
|
||||||
|
import { isActive, hashRE, groupHeaders } from './util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
functional: true,
|
||||||
|
|
||||||
|
props: ['item'],
|
||||||
|
|
||||||
|
render (h, { parent: { $page, $site, $route }, props: { item }}) {
|
||||||
|
// use custom active class matching logic
|
||||||
|
// due to edge case of paths ending with / + hash
|
||||||
|
const selfActive = isActive($route, item.path)
|
||||||
|
// for sidebar: auto pages, a hash link should be active if one of its child
|
||||||
|
// matches
|
||||||
|
const active = item.type === 'auto'
|
||||||
|
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
|
||||||
|
: selfActive
|
||||||
|
const link = renderLink(h, item.path, item.title || item.path, active)
|
||||||
|
const configDepth = $page.frontmatter.sidebarDepth != null
|
||||||
|
? $page.frontmatter.sidebarDepth
|
||||||
|
: $site.themeConfig.sidebarDepth
|
||||||
|
const maxDepth = configDepth == null ? 1 : configDepth
|
||||||
|
const displayAllHeaders = !!$site.themeConfig.displayAllHeaders
|
||||||
|
if (item.type === 'auto') {
|
||||||
|
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
|
||||||
|
} else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
|
||||||
|
const children = groupHeaders(item.headers)
|
||||||
|
return [link, renderChildren(h, children, item.path, $route, maxDepth)]
|
||||||
|
} else {
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLink (h, to, text, active) {
|
||||||
|
return h('router-link', {
|
||||||
|
props: {
|
||||||
|
to,
|
||||||
|
activeClass: '',
|
||||||
|
exactActiveClass: ''
|
||||||
|
},
|
||||||
|
class: {
|
||||||
|
active,
|
||||||
|
'sidebar-link': true
|
||||||
|
}
|
||||||
|
}, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderChildren (h, children, path, route, maxDepth, depth = 1) {
|
||||||
|
if (!children || depth > maxDepth) return null
|
||||||
|
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
|
||||||
|
const active = isActive(route, path + '#' + c.slug)
|
||||||
|
return h('li', { class: 'sidebar-sub-header' }, [
|
||||||
|
renderLink(h, path + '#' + c.slug, c.title, active),
|
||||||
|
renderChildren(h, c.children, path, route, maxDepth, depth + 1)
|
||||||
|
])
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import './styles/config.styl'
|
||||||
|
|
||||||
|
.sidebar .sidebar-sub-headers
|
||||||
|
padding-left 1rem
|
||||||
|
font-size 0.95em
|
||||||
|
|
||||||
|
a.sidebar-link
|
||||||
|
font-weight 400
|
||||||
|
display inline-block
|
||||||
|
color $textColor
|
||||||
|
border-left 0.25rem solid transparent
|
||||||
|
padding 0.35rem 1rem 0.35rem 1.25rem
|
||||||
|
line-height 1.4
|
||||||
|
width: 100%
|
||||||
|
box-sizing: border-box
|
||||||
|
&:hover
|
||||||
|
color $accentColor
|
||||||
|
&.active
|
||||||
|
font-weight 600
|
||||||
|
color $accentColor
|
||||||
|
border-left-color $accentColor
|
||||||
|
.sidebar-group &
|
||||||
|
padding-left 2rem
|
||||||
|
.sidebar-sub-headers &
|
||||||
|
padding-top 0.25rem
|
||||||
|
padding-bottom 0.25rem
|
||||||
|
border-left none
|
||||||
|
&.active
|
||||||
|
font-weight 500
|
||||||
|
</style>
|
1
docs/.vuepress/theme/search.svg
Normal file
1
docs/.vuepress/theme/search.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13"><g stroke-width="2" stroke="#aaa" fill="none"><path d="M11.29 11.71l-4-4"/><circle cx="5" cy="5" r="4"/></g></svg>
|
After Width: | Height: | Size: 216 B |
22
docs/.vuepress/theme/styles/arrow.styl
Normal file
22
docs/.vuepress/theme/styles/arrow.styl
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@require './config'
|
||||||
|
|
||||||
|
.arrow
|
||||||
|
display inline-block
|
||||||
|
width 0
|
||||||
|
height 0
|
||||||
|
&.up
|
||||||
|
border-left 4px solid transparent
|
||||||
|
border-right 4px solid transparent
|
||||||
|
border-bottom 6px solid $arrowBgColor
|
||||||
|
&.down
|
||||||
|
border-left 4px solid transparent
|
||||||
|
border-right 4px solid transparent
|
||||||
|
border-top 6px solid $arrowBgColor
|
||||||
|
&.right
|
||||||
|
border-top 4px solid transparent
|
||||||
|
border-bottom 4px solid transparent
|
||||||
|
border-left 6px solid $arrowBgColor
|
||||||
|
&.left
|
||||||
|
border-top 4px solid transparent
|
||||||
|
border-bottom 4px solid transparent
|
||||||
|
border-right 6px solid $arrowBgColor
|
129
docs/.vuepress/theme/styles/code.styl
Normal file
129
docs/.vuepress/theme/styles/code.styl
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
@require './config'
|
||||||
|
|
||||||
|
.content
|
||||||
|
code
|
||||||
|
color lighten($textColor, 20%)
|
||||||
|
padding 0.25rem 0.5rem
|
||||||
|
margin 0
|
||||||
|
font-size 0.85em
|
||||||
|
background-color rgba(27,31,35,0.05)
|
||||||
|
border-radius 3px
|
||||||
|
|
||||||
|
.content
|
||||||
|
pre, pre[class*="language-"]
|
||||||
|
line-height 1.4
|
||||||
|
padding 1.25rem 1.5rem
|
||||||
|
margin 0.85rem 0
|
||||||
|
background-color $codeBgColor
|
||||||
|
border-radius 6px
|
||||||
|
overflow auto
|
||||||
|
code
|
||||||
|
color #fff
|
||||||
|
padding 0
|
||||||
|
background-color transparent
|
||||||
|
border-radius 0
|
||||||
|
|
||||||
|
div[class*="language-"]
|
||||||
|
position relative
|
||||||
|
background-color $codeBgColor
|
||||||
|
border-radius 6px
|
||||||
|
.highlight-lines
|
||||||
|
user-select none
|
||||||
|
padding-top 1.3rem
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
line-height 1.4
|
||||||
|
.highlighted
|
||||||
|
background-color rgba(0, 0, 0, 66%)
|
||||||
|
pre, pre[class*="language-"]
|
||||||
|
background transparent
|
||||||
|
position relative
|
||||||
|
z-index 1
|
||||||
|
&::before
|
||||||
|
position absolute
|
||||||
|
z-index 3
|
||||||
|
top 0.8em
|
||||||
|
right 1em
|
||||||
|
font-size 0.75rem
|
||||||
|
color rgba(255, 255, 255, 0.4)
|
||||||
|
&:not(.line-numbers-mode)
|
||||||
|
.line-numbers-wrapper
|
||||||
|
display none
|
||||||
|
&.line-numbers-mode
|
||||||
|
.highlight-lines .highlighted
|
||||||
|
position relative
|
||||||
|
&:before
|
||||||
|
content ' '
|
||||||
|
position absolute
|
||||||
|
z-index 3
|
||||||
|
left 0
|
||||||
|
top 0
|
||||||
|
display block
|
||||||
|
width $lineNumbersWrapperWidth
|
||||||
|
height 100%
|
||||||
|
background-color rgba(0, 0, 0, 66%)
|
||||||
|
pre
|
||||||
|
padding-left $lineNumbersWrapperWidth + 1 rem
|
||||||
|
vertical-align middle
|
||||||
|
.line-numbers-wrapper
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
width $lineNumbersWrapperWidth
|
||||||
|
text-align center
|
||||||
|
color rgba(255, 255, 255, 0.3)
|
||||||
|
padding 1.25rem 0
|
||||||
|
line-height 1.4
|
||||||
|
br
|
||||||
|
user-select none
|
||||||
|
.line-number
|
||||||
|
position relative
|
||||||
|
z-index 4
|
||||||
|
user-select none
|
||||||
|
font-size 0.85em
|
||||||
|
&::after
|
||||||
|
content ''
|
||||||
|
position absolute
|
||||||
|
z-index 2
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width $lineNumbersWrapperWidth
|
||||||
|
height 100%
|
||||||
|
border-radius 6px 0 0 6px
|
||||||
|
border-right 1px solid rgba(0, 0, 0, 66%)
|
||||||
|
background-color $codeBgColor
|
||||||
|
|
||||||
|
|
||||||
|
for lang in $codeLang
|
||||||
|
div{'[class~="language-' + lang + '"]'}
|
||||||
|
&:before
|
||||||
|
content ('' + lang)
|
||||||
|
|
||||||
|
div[class~="language-javascript"]
|
||||||
|
&:before
|
||||||
|
content "js"
|
||||||
|
|
||||||
|
div[class~="language-typescript"]
|
||||||
|
&:before
|
||||||
|
content "ts"
|
||||||
|
|
||||||
|
div[class~="language-markup"]
|
||||||
|
&:before
|
||||||
|
content "html"
|
||||||
|
|
||||||
|
div[class~="language-markdown"]
|
||||||
|
&:before
|
||||||
|
content "md"
|
||||||
|
|
||||||
|
div[class~="language-json"]:before
|
||||||
|
content "json"
|
||||||
|
|
||||||
|
div[class~="language-ruby"]:before
|
||||||
|
content "rb"
|
||||||
|
|
||||||
|
div[class~="language-python"]:before
|
||||||
|
content "py"
|
||||||
|
|
||||||
|
div[class~="language-bash"]:before
|
||||||
|
content "sh"
|
20
docs/.vuepress/theme/styles/config.styl
Normal file
20
docs/.vuepress/theme/styles/config.styl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// colors
|
||||||
|
$accentColor = #3eaf7c
|
||||||
|
$textColor = #2c3e50
|
||||||
|
$borderColor = #eaecef
|
||||||
|
$codeBgColor = #282c34
|
||||||
|
$arrowBgColor = #ccc
|
||||||
|
|
||||||
|
// layout
|
||||||
|
$navbarHeight = 3.6rem
|
||||||
|
$sidebarWidth = 20rem
|
||||||
|
$contentWidth = 740px
|
||||||
|
|
||||||
|
// responsive breakpoints
|
||||||
|
$MQNarrow = 959px
|
||||||
|
$MQMobile = 719px
|
||||||
|
$MQMobileNarrow = 419px
|
||||||
|
|
||||||
|
// code
|
||||||
|
$lineNumbersWrapperWidth = 3.5rem
|
||||||
|
$codeLang = js ts html md vue css sass scss less stylus go java c sh yaml py
|
28
docs/.vuepress/theme/styles/custom-blocks.styl
Normal file
28
docs/.vuepress/theme/styles/custom-blocks.styl
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.custom-block
|
||||||
|
.custom-block-title
|
||||||
|
font-weight 600
|
||||||
|
margin-bottom -0.4rem
|
||||||
|
&.tip, &.warning, &.danger
|
||||||
|
padding .1rem 1.5rem
|
||||||
|
border-left-width .5rem
|
||||||
|
border-left-style solid
|
||||||
|
margin 1rem 0
|
||||||
|
&.tip
|
||||||
|
background-color #f3f5f7
|
||||||
|
border-color #42b983
|
||||||
|
&.warning
|
||||||
|
background-color rgba(255,229,100,.3)
|
||||||
|
border-color darken(#ffe564, 35%)
|
||||||
|
color darken(#ffe564, 70%)
|
||||||
|
.custom-block-title
|
||||||
|
color darken(#ffe564, 50%)
|
||||||
|
a
|
||||||
|
color $textColor
|
||||||
|
&.danger
|
||||||
|
background-color #ffe6e6
|
||||||
|
border-color darken(red, 20%)
|
||||||
|
color darken(red, 70%)
|
||||||
|
.custom-block-title
|
||||||
|
color darken(red, 40%)
|
||||||
|
a
|
||||||
|
color $textColor
|
37
docs/.vuepress/theme/styles/mobile.styl
Normal file
37
docs/.vuepress/theme/styles/mobile.styl
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
@require './config'
|
||||||
|
|
||||||
|
$mobileSidebarWidth = $sidebarWidth * 0.82
|
||||||
|
|
||||||
|
// narrow desktop / iPad
|
||||||
|
@media (max-width: $MQNarrow)
|
||||||
|
.sidebar
|
||||||
|
font-size 15px
|
||||||
|
width $mobileSidebarWidth
|
||||||
|
.page
|
||||||
|
padding-left $mobileSidebarWidth
|
||||||
|
|
||||||
|
// wide mobile
|
||||||
|
@media (max-width: $MQMobile)
|
||||||
|
.sidebar
|
||||||
|
top 0
|
||||||
|
padding-top $navbarHeight
|
||||||
|
transform translateX(-100%)
|
||||||
|
transition transform .2s ease
|
||||||
|
.page
|
||||||
|
padding-left 0
|
||||||
|
.theme-container
|
||||||
|
&.sidebar-open
|
||||||
|
.sidebar
|
||||||
|
transform translateX(0)
|
||||||
|
&.no-navbar
|
||||||
|
.sidebar
|
||||||
|
padding-top: 0
|
||||||
|
|
||||||
|
// narrow mobile
|
||||||
|
@media (max-width: $MQMobileNarrow)
|
||||||
|
h1
|
||||||
|
font-size 1.9rem
|
||||||
|
.content
|
||||||
|
div[class*="language-"]
|
||||||
|
margin 0.85rem -1.5rem
|
||||||
|
border-radius 0
|
48
docs/.vuepress/theme/styles/nprogress.styl
Normal file
48
docs/.vuepress/theme/styles/nprogress.styl
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#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)
|
190
docs/.vuepress/theme/styles/theme.styl
Normal file
190
docs/.vuepress/theme/styles/theme.styl
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
@require './config'
|
||||||
|
@require './nprogress'
|
||||||
|
@require './code'
|
||||||
|
@require './custom-blocks'
|
||||||
|
@require './arrow'
|
||||||
|
@require './wrapper'
|
||||||
|
@require './toc'
|
||||||
|
|
||||||
|
html, body
|
||||||
|
padding 0
|
||||||
|
margin 0
|
||||||
|
|
||||||
|
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 $textColor
|
||||||
|
|
||||||
|
.page
|
||||||
|
padding-left $sidebarWidth
|
||||||
|
|
||||||
|
.navbar
|
||||||
|
position fixed
|
||||||
|
z-index 20
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
right 0
|
||||||
|
height $navbarHeight
|
||||||
|
background-color #fff
|
||||||
|
box-sizing border-box
|
||||||
|
border-bottom 1px solid $borderColor
|
||||||
|
|
||||||
|
.sidebar-mask
|
||||||
|
position fixed
|
||||||
|
z-index 9
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100vw
|
||||||
|
height 100vh
|
||||||
|
display none
|
||||||
|
|
||||||
|
.sidebar
|
||||||
|
font-size 15px
|
||||||
|
background-color #fff
|
||||||
|
width $sidebarWidth
|
||||||
|
position fixed
|
||||||
|
z-index 10
|
||||||
|
margin 0
|
||||||
|
top $navbarHeight
|
||||||
|
left 0
|
||||||
|
bottom 0
|
||||||
|
box-sizing border-box
|
||||||
|
border-right 1px solid $borderColor
|
||||||
|
overflow-y auto
|
||||||
|
|
||||||
|
.content:not(.custom)
|
||||||
|
@extend $wrapper
|
||||||
|
> *:first-child
|
||||||
|
margin-top $navbarHeight
|
||||||
|
a:hover
|
||||||
|
text-decoration underline
|
||||||
|
p.demo
|
||||||
|
padding 1rem 1.5rem
|
||||||
|
border 1px solid #ddd
|
||||||
|
border-radius 4px
|
||||||
|
img
|
||||||
|
max-width 100%
|
||||||
|
|
||||||
|
.content.custom
|
||||||
|
padding 0
|
||||||
|
margin 0
|
||||||
|
img
|
||||||
|
max-width 100%
|
||||||
|
|
||||||
|
a
|
||||||
|
font-weight 500
|
||||||
|
color $accentColor
|
||||||
|
text-decoration none
|
||||||
|
|
||||||
|
p a code
|
||||||
|
font-weight 400
|
||||||
|
color $accentColor
|
||||||
|
|
||||||
|
kbd
|
||||||
|
background #eee
|
||||||
|
border solid 0.15rem #ddd
|
||||||
|
border-bottom solid 0.25rem #ddd
|
||||||
|
border-radius 0.15rem
|
||||||
|
padding 0 0.15em
|
||||||
|
|
||||||
|
blockquote
|
||||||
|
font-size 1.2rem
|
||||||
|
color #999
|
||||||
|
border-left .25rem solid #dfe2e5
|
||||||
|
margin-left 0
|
||||||
|
padding-left 1rem
|
||||||
|
|
||||||
|
ul, ol
|
||||||
|
padding-left 1.2em
|
||||||
|
|
||||||
|
strong
|
||||||
|
font-weight 600
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6
|
||||||
|
font-weight 600
|
||||||
|
line-height 1.25
|
||||||
|
.content:not(.custom) > &
|
||||||
|
margin-top (0.5rem - $navbarHeight)
|
||||||
|
padding-top ($navbarHeight + 1rem)
|
||||||
|
margin-bottom 0
|
||||||
|
&:first-child
|
||||||
|
margin-top -1.5rem
|
||||||
|
margin-bottom 1rem
|
||||||
|
+ p, + pre, + .custom-block
|
||||||
|
margin-top 2rem
|
||||||
|
&:hover .header-anchor
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
|
h1
|
||||||
|
font-size 2.2rem
|
||||||
|
|
||||||
|
h2
|
||||||
|
font-size 1.65rem
|
||||||
|
padding-bottom .3rem
|
||||||
|
border-bottom 1px solid $borderColor
|
||||||
|
|
||||||
|
h3
|
||||||
|
font-size 1.35rem
|
||||||
|
|
||||||
|
a.header-anchor
|
||||||
|
font-size 0.85em
|
||||||
|
float left
|
||||||
|
margin-left -0.87em
|
||||||
|
padding-right 0.23em
|
||||||
|
margin-top 0.125em
|
||||||
|
opacity 0
|
||||||
|
&:hover
|
||||||
|
text-decoration none
|
||||||
|
|
||||||
|
code, kbd, .line-number
|
||||||
|
font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace
|
||||||
|
|
||||||
|
p, ul, ol
|
||||||
|
line-height 1.7
|
||||||
|
|
||||||
|
hr
|
||||||
|
border 0
|
||||||
|
border-top 1px solid $borderColor
|
||||||
|
|
||||||
|
table
|
||||||
|
border-collapse collapse
|
||||||
|
margin 1rem 0
|
||||||
|
display: block
|
||||||
|
overflow-x: auto
|
||||||
|
|
||||||
|
tr
|
||||||
|
border-top 1px solid #dfe2e5
|
||||||
|
&:nth-child(2n)
|
||||||
|
background-color #f6f8fa
|
||||||
|
|
||||||
|
th, td
|
||||||
|
border 1px solid #dfe2e5
|
||||||
|
padding .6em 1em
|
||||||
|
|
||||||
|
.custom-layout
|
||||||
|
padding-top $navbarHeight
|
||||||
|
|
||||||
|
.theme-container
|
||||||
|
&.sidebar-open
|
||||||
|
.sidebar-mask
|
||||||
|
display: block
|
||||||
|
&.no-navbar
|
||||||
|
.content:not(.custom) > h1, h2, h3, h4, h5, h6
|
||||||
|
margin-top 1.5rem
|
||||||
|
padding-top 0
|
||||||
|
.sidebar
|
||||||
|
top 0
|
||||||
|
.custom-layout
|
||||||
|
padding-top 0
|
||||||
|
|
||||||
|
|
||||||
|
@media (min-width: ($MQMobile + 1px))
|
||||||
|
.theme-container.no-sidebar
|
||||||
|
.sidebar
|
||||||
|
display none
|
||||||
|
.page
|
||||||
|
padding-left 0
|
||||||
|
|
||||||
|
@require './mobile.styl'
|
3
docs/.vuepress/theme/styles/toc.styl
Normal file
3
docs/.vuepress/theme/styles/toc.styl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.table-of-contents
|
||||||
|
.badge
|
||||||
|
vertical-align middle
|
9
docs/.vuepress/theme/styles/wrapper.styl
Normal file
9
docs/.vuepress/theme/styles/wrapper.styl
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
$wrapper
|
||||||
|
max-width $contentWidth
|
||||||
|
margin 0 auto
|
||||||
|
padding 2rem 2.5rem
|
||||||
|
@media (max-width: $MQNarrow)
|
||||||
|
padding 2rem
|
||||||
|
@media (max-width: $MQMobileNarrow)
|
||||||
|
padding 1.5rem
|
||||||
|
|
216
docs/.vuepress/theme/util.js
Normal file
216
docs/.vuepress/theme/util.js
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
export const hashRE = /#.*$/
|
||||||
|
export const extRE = /\.(md|html)$/
|
||||||
|
export const endingSlashRE = /\/$/
|
||||||
|
export const outboundRE = /^(https?:|mailto:|tel:)/
|
||||||
|
|
||||||
|
export function normalize (path) {
|
||||||
|
return decodeURI(path)
|
||||||
|
.replace(hashRE, '')
|
||||||
|
.replace(extRE, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHash (path) {
|
||||||
|
const match = path.match(hashRE)
|
||||||
|
if (match) {
|
||||||
|
return match[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isExternal (path) {
|
||||||
|
return outboundRE.test(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMailto (path) {
|
||||||
|
return /^mailto:/.test(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTel (path) {
|
||||||
|
return /^tel:/.test(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ensureExt (path) {
|
||||||
|
if (isExternal(path)) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
const hashMatch = path.match(hashRE)
|
||||||
|
const hash = hashMatch ? hashMatch[0] : ''
|
||||||
|
const normalized = normalize(path)
|
||||||
|
|
||||||
|
if (endingSlashRE.test(normalized)) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return normalized + '.html' + hash
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isActive (route, path) {
|
||||||
|
const routeHash = route.hash
|
||||||
|
const linkHash = getHash(path)
|
||||||
|
if (linkHash && routeHash !== linkHash) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const routePath = normalize(route.path)
|
||||||
|
const pagePath = normalize(path)
|
||||||
|
return routePath === pagePath
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolvePage (pages, rawPath, base) {
|
||||||
|
if (base) {
|
||||||
|
rawPath = resolvePath(rawPath, base)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePath (relative, base, append) {
|
||||||
|
const firstChar = relative.charAt(0)
|
||||||
|
if (firstChar === '/') {
|
||||||
|
return relative
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstChar === '?' || firstChar === '#') {
|
||||||
|
return base + relative
|
||||||
|
}
|
||||||
|
|
||||||
|
const stack = base.split('/')
|
||||||
|
|
||||||
|
// remove trailing segment if:
|
||||||
|
// - not appending
|
||||||
|
// - appending to trailing slash (last segment is empty)
|
||||||
|
if (!append || !stack[stack.length - 1]) {
|
||||||
|
stack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve relative path
|
||||||
|
const segments = relative.replace(/^\//, '').split('/')
|
||||||
|
for (let i = 0; i < segments.length; i++) {
|
||||||
|
const segment = segments[i]
|
||||||
|
if (segment === '..') {
|
||||||
|
stack.pop()
|
||||||
|
} else if (segment !== '.') {
|
||||||
|
stack.push(segment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure leading slash
|
||||||
|
if (stack[0] !== '') {
|
||||||
|
stack.unshift('')
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack.join('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveSidebarItems (page, route, site, localePath) {
|
||||||
|
const { pages, themeConfig } = site
|
||||||
|
|
||||||
|
const localeConfig = localePath && themeConfig.locales
|
||||||
|
? themeConfig.locales[localePath] || themeConfig
|
||||||
|
: themeConfig
|
||||||
|
|
||||||
|
const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar
|
||||||
|
if (pageSidebarConfig === 'auto') {
|
||||||
|
return resolveHeaders(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar
|
||||||
|
if (!sidebarConfig) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
const { base, config } = resolveMatchingConfig(route, sidebarConfig)
|
||||||
|
return config
|
||||||
|
? config.map(item => resolveItem(item, pages, base))
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveHeaders (page) {
|
||||||
|
const headers = groupHeaders(page.headers || [])
|
||||||
|
return [{
|
||||||
|
type: 'group',
|
||||||
|
collapsable: false,
|
||||||
|
title: page.title,
|
||||||
|
children: headers.map(h => ({
|
||||||
|
type: 'auto',
|
||||||
|
title: h.title,
|
||||||
|
basePath: page.path,
|
||||||
|
path: page.path + '#' + h.slug,
|
||||||
|
children: h.children || []
|
||||||
|
}))
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function groupHeaders (headers) {
|
||||||
|
// group h3s under h2
|
||||||
|
headers = headers.map(h => Object.assign({}, h))
|
||||||
|
let lastH2
|
||||||
|
headers.forEach(h => {
|
||||||
|
if (h.level === 2) {
|
||||||
|
lastH2 = h
|
||||||
|
} else if (lastH2) {
|
||||||
|
(lastH2.children || (lastH2.children = [])).push(h)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return headers.filter(h => h.level === 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveNavLinkItem (linkItem) {
|
||||||
|
return Object.assign(linkItem, {
|
||||||
|
type: linkItem.items && linkItem.items.length ? 'links' : 'link'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveMatchingConfig (route, config) {
|
||||||
|
if (Array.isArray(config)) {
|
||||||
|
return {
|
||||||
|
base: '/',
|
||||||
|
config: config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const base in config) {
|
||||||
|
if (ensureEndingSlash(route.path).indexOf(base) === 0) {
|
||||||
|
return {
|
||||||
|
base,
|
||||||
|
config: config[base]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureEndingSlash (path) {
|
||||||
|
return /(\.html|\/)$/.test(path)
|
||||||
|
? path
|
||||||
|
: path + '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveItem (item, pages, base, isNested) {
|
||||||
|
if (typeof item === 'string') {
|
||||||
|
return resolvePage(pages, item, base)
|
||||||
|
} else if (Array.isArray(item)) {
|
||||||
|
return Object.assign(resolvePage(pages, item[0], base), {
|
||||||
|
title: item[1]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (isNested) {
|
||||||
|
console.error(
|
||||||
|
'[vuepress] Nested sidebar groups are not supported. ' +
|
||||||
|
'Consider using navbar + categories instead.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const children = item.children || []
|
||||||
|
return {
|
||||||
|
type: 'group',
|
||||||
|
title: item.title,
|
||||||
|
children: children.map(child => resolveItem(child, pages, base, true)),
|
||||||
|
collapsable: item.collapsable !== false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user