mirror of
https://github.com/pterodactyl/documentation.git
synced 2025-12-10 00:09:39 -06:00
Format files, fix 404 page
This commit is contained in:
parent
d174526e67
commit
9d79ca61ac
@ -1,155 +1,51 @@
|
||||
<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"
|
||||
v-for="(subItem, index) in item.items"
|
||||
:key="subItem.link || index">
|
||||
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
|
||||
<ul class="dropdown-subitem-wrapper" v-if="subItem.type === 'links'">
|
||||
<li
|
||||
class="dropdown-subitem"
|
||||
v-for="childSubItem in subItem.items"
|
||||
:key="childSubItem.link">
|
||||
<NavLink :item="childSubItem"/>
|
||||
</li>
|
||||
</ul>
|
||||
<NavLink v-else :item="subItem"/>
|
||||
</li>
|
||||
</ul>
|
||||
</DropdownTransition>
|
||||
</div>
|
||||
<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"
|
||||
v-for="(subItem, index) in item.items"
|
||||
:key="subItem.link || index">
|
||||
<h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
|
||||
<ul class="dropdown-subitem-wrapper" v-if="subItem.type === 'links'">
|
||||
<li
|
||||
class="dropdown-subitem"
|
||||
v-for="childSubItem in subItem.items"
|
||||
:key="childSubItem.link">
|
||||
<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'
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
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>-->
|
||||
|
||||
@ -1,30 +1,29 @@
|
||||
<template>
|
||||
<transition name="dropdown"
|
||||
@enter="setHeight"
|
||||
@after-enter="unsetHeight"
|
||||
@before-leave="setHeight">
|
||||
<slot></slot>
|
||||
</transition>
|
||||
<transition name="dropdown"
|
||||
@enter="setHeight"
|
||||
@after-enter="unsetHeight"
|
||||
@before-leave="setHeight">
|
||||
<slot></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 = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
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>
|
||||
.dropdown-enter, .dropdown-leave-to {
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
.dropdown-enter, .dropdown-leave-to {
|
||||
height: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,134 +1,43 @@
|
||||
<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 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 in data.features">
|
||||
<h2>{{ feature.title }}</h2>
|
||||
<p>{{ feature.details }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Content custom/>
|
||||
<div class="footer" v-if="data.footer">
|
||||
{{ data.footer }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="features" v-if="data.features && data.features.length">
|
||||
<div class="feature" v-for="feature in data.features">
|
||||
<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'
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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-gtfo
|
||||
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>-->
|
||||
|
||||
@ -1,133 +1,133 @@
|
||||
<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 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>
|
||||
</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>
|
||||
</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 { resolveSidebarItems } from './util'
|
||||
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 { resolveSidebarItems } from './util';
|
||||
|
||||
export default {
|
||||
components: { Home, Page, Sidebar, Navbar },
|
||||
data () {
|
||||
return {
|
||||
isSidebarOpen: false
|
||||
}
|
||||
},
|
||||
|
||||
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
|
||||
export default {
|
||||
components: { Home, Page, Sidebar, Navbar },
|
||||
data() {
|
||||
return {
|
||||
isSidebarOpen: false
|
||||
};
|
||||
},
|
||||
userPageClass
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
window.addEventListener('scroll', this.onScroll)
|
||||
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
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
// configure progress bar
|
||||
nprogress.configure({ showSpinner: false })
|
||||
mounted() {
|
||||
window.addEventListener('scroll', this.onScroll);
|
||||
|
||||
this.$router.beforeEach((to, from, next) => {
|
||||
if (to.path !== from.path && !Vue.component(to.name)) {
|
||||
nprogress.start()
|
||||
}
|
||||
next()
|
||||
})
|
||||
// configure progress bar
|
||||
nprogress.configure({ showSpinner: false });
|
||||
|
||||
this.$router.afterEach(() => {
|
||||
nprogress.done()
|
||||
this.isSidebarOpen = false
|
||||
})
|
||||
},
|
||||
this.$router.beforeEach((to, from, next) => {
|
||||
if (to.path !== from.path && ! Vue.component(to.name)) {
|
||||
nprogress.start();
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
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)
|
||||
this.$router.afterEach(() => {
|
||||
nprogress.done();
|
||||
this.isSidebarOpen = false;
|
||||
});
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style src="prismjs/themes/prism-tomorrow.css"></style>
|
||||
<style src="./styles/main.css" lang="postcss"></style>
|
||||
<style src="./styles/main.css" lang="postcss"></style>
|
||||
|
||||
@ -1,46 +1,47 @@
|
||||
<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>
|
||||
<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'
|
||||
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
|
||||
}
|
||||
}
|
||||
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>
|
||||
|
||||
@ -1,134 +1,100 @@
|
||||
<template>
|
||||
<nav class="nav-links flex-no-shrink" 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 -->
|
||||
<div v-if="repoLink"
|
||||
class="nav-item">
|
||||
<a :href="repoLink"
|
||||
class="nav-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{{ repoLabel }}
|
||||
<OutboundLink/>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="nav-links flex-no-shrink" 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 -->
|
||||
<div v-if="repoLink"
|
||||
class="nav-item">
|
||||
<a :href="repoLink"
|
||||
class="nav-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{{ repoLabel }}
|
||||
<OutboundLink/>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DropdownLink from './DropdownLink.vue'
|
||||
import { resolveNavLinkItem } from './util'
|
||||
import NavLink from './NavLink.vue'
|
||||
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
|
||||
}
|
||||
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';
|
||||
}
|
||||
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-away
|
||||
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
|
||||
.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>
|
||||
-->
|
||||
@ -1,74 +1,42 @@
|
||||
<template>
|
||||
<header class="nav">
|
||||
<SidebarButton class="block md:hidden flex-no-shrink" @toggle-sidebar="$emit('toggle-sidebar')"/>
|
||||
<div class="logo-container">
|
||||
<router-link :to="$localePath" class="home-link">
|
||||
<img class="logo"
|
||||
v-if="$site.themeConfig.logo"
|
||||
:src="$withBase($site.themeConfig.logo)">
|
||||
<span class="site-name hidden md:inline"
|
||||
v-if="$siteTitle"
|
||||
:class="{ 'can-hide': $site.themeConfig.logo }">
|
||||
<header class="nav">
|
||||
<SidebarButton class="block md:hidden flex-no-shrink" @toggle-sidebar="$emit('toggle-sidebar')"/>
|
||||
<div class="logo-container">
|
||||
<router-link :to="$localePath" class="home-link">
|
||||
<img class="logo"
|
||||
v-if="$site.themeConfig.logo"
|
||||
:src="$withBase($site.themeConfig.logo)">
|
||||
<span class="site-name hidden md:inline"
|
||||
v-if="$siteTitle"
|
||||
:class="{ 'can-hide': $site.themeConfig.logo }">
|
||||
{{ $siteTitle }}
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<div class="flex">
|
||||
<SearchBox/>
|
||||
<NavLinks class="hidden md:flex"/>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<div class="flex">
|
||||
<SearchBox/>
|
||||
<NavLinks class="hidden md:flex"/>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarButton from './SidebarButton.vue'
|
||||
import AlgoliaSearchBox from '@AlgoliaSearchBox'
|
||||
import SearchBox from './SearchBox.vue'
|
||||
import NavLinks from './NavLinks.vue'
|
||||
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 },
|
||||
computed: {
|
||||
algolia () {
|
||||
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
|
||||
},
|
||||
isAlgoliaSearch () {
|
||||
return this.algolia && this.algolia.apiKey && this.algolia.indexName
|
||||
}
|
||||
}
|
||||
}
|
||||
export default {
|
||||
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
|
||||
computed: {
|
||||
algolia() {
|
||||
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {};
|
||||
},
|
||||
isAlgoliaSearch() {
|
||||
return this.algolia && this.algolia.apiKey && this.algolia.indexName;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<!--<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.navbar
|
||||
padding 0.7rem 1.5rem
|
||||
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
|
||||
font-size 0.9rem
|
||||
position absolute
|
||||
right 1.5rem
|
||||
top 0.7rem
|
||||
|
||||
@media (max-width: $MQMobile)
|
||||
.navbar
|
||||
padding-left 4rem
|
||||
.can-hide
|
||||
display none
|
||||
</style>-->
|
||||
|
||||
@ -1,26 +1,30 @@
|
||||
<template>
|
||||
<div class="theme-container">
|
||||
<div class="content">
|
||||
<h1>404</h1>
|
||||
<blockquote>{{ getMsg() }}</blockquote>
|
||||
<router-link to="/">Take me home.</router-link>
|
||||
<div class="theme-container">
|
||||
<div class="content error-page">
|
||||
<h1>404</h1>
|
||||
<blockquote>{{ getMsg() }}</blockquote>
|
||||
<router-link to="/">Take me home.</router-link>
|
||||
</div>
|
||||
</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.`
|
||||
]
|
||||
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.`,
|
||||
`Who let Dogmeat mess with the documentation again?`,
|
||||
`Someone unplugged the ethernet cable... again.`,
|
||||
`Couldn't locate that cap'n, should we start over?`,
|
||||
`I don't like this game of hide'n'seek anymore.`
|
||||
];
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
getMsg () {
|
||||
return msgs[Math.floor(Math.random() * msgs.length)]
|
||||
}
|
||||
}
|
||||
}
|
||||
export default {
|
||||
methods: {
|
||||
getMsg() {
|
||||
return msgs[Math.floor(Math.random() * msgs.length)];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,221 +1,137 @@
|
||||
<template>
|
||||
<div class="search-box">
|
||||
<input
|
||||
@input="query = $event.target.value"
|
||||
aria-label="Search"
|
||||
:value="query"
|
||||
placeholder="Search"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
@keyup.enter="go(focusIndex)"
|
||||
@keyup.up="onUp"
|
||||
@keyup.down="onDown">
|
||||
<div class="suggestion-container" v-if="showSuggestions" @mouseleave="unfocus">
|
||||
<div class="suggestion-padding"></div>
|
||||
<ul class="suggestions" :class="{ 'align-right': alignRight }">
|
||||
<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 class="search-box">
|
||||
<input
|
||||
@input="query = $event.target.value"
|
||||
aria-label="Search"
|
||||
:value="query"
|
||||
placeholder="Search"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
@keyup.enter="go(focusIndex)"
|
||||
@keyup.up="onUp"
|
||||
@keyup.down="onDown">
|
||||
<div class="suggestion-container" v-if="showSuggestions" @mouseleave="unfocus">
|
||||
<div class="suggestion-padding"></div>
|
||||
<ul class="suggestions" :class="{ 'align-right': alignRight }">
|
||||
<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>
|
||||
</div>
|
||||
</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
|
||||
}
|
||||
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
|
||||
}))
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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-no
|
||||
display inline-block
|
||||
position relative
|
||||
margin-right 0.5rem
|
||||
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
|
||||
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
|
||||
left 1rem
|
||||
&: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
|
||||
.suggestions
|
||||
right 0
|
||||
|
||||
@media (max-width: $MQMobileNarrow)
|
||||
.search-box
|
||||
.suggestions
|
||||
width calc(100vw - 4rem)
|
||||
input:focus
|
||||
width 8rem
|
||||
</style>-->
|
||||
|
||||
@ -1,105 +1,70 @@
|
||||
<template>
|
||||
<div class="sidebar">
|
||||
<NavLinks class="block md:hidden" />
|
||||
<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"
|
||||
@toggle="toggleGroup(i)"/>
|
||||
<SidebarLink v-else :item="item"/>
|
||||
</li>
|
||||
</ul>
|
||||
<slot name="bottom"/>
|
||||
</div>
|
||||
<div class="sidebar">
|
||||
<NavLinks class="block md:hidden"/>
|
||||
<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"
|
||||
@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'
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
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;
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--<style lang="stylus">
|
||||
@import './styles/config.styl'
|
||||
|
||||
.sidebar-no
|
||||
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>
|
||||
-->
|
||||
@ -1,7 +1,9 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
@ -1,64 +1,28 @@
|
||||
<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 class="sidebar-group-items" ref="items" v-if="open || !collapsable">
|
||||
<li v-for="child in item.children">
|
||||
<SidebarLink :item="child"/>
|
||||
</li>
|
||||
</ul>
|
||||
</DropdownTransition>
|
||||
</div>
|
||||
<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 class="sidebar-group-items" ref="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'
|
||||
import SidebarLink from './SidebarLink.vue';
|
||||
import DropdownTransition from './DropdownTransition.vue';
|
||||
|
||||
export default {
|
||||
name: 'SidebarGroup',
|
||||
props: ['item', 'first', 'open', 'collapsable'],
|
||||
components: { SidebarLink, DropdownTransition }
|
||||
}
|
||||
export default {
|
||||
name: 'SidebarGroup',
|
||||
props: ['item', 'first', 'open', 'collapsable'],
|
||||
components: { SidebarLink, DropdownTransition }
|
||||
};
|
||||
</script>
|
||||
|
||||
<!--<style lang="stylus">
|
||||
.sidebar-group-away
|
||||
&:not(.first)
|
||||
margin-top 1em
|
||||
.sidebar-group
|
||||
padding-left 0.5em
|
||||
&:not(.collapsable)
|
||||
.sidebar-heading
|
||||
cursor auto
|
||||
color inherit
|
||||
|
||||
.sidebar-heading-no
|
||||
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>
|
||||
-->
|
||||
@ -1,89 +1,57 @@
|
||||
<script>
|
||||
import { isActive, hashRE, groupHeaders } from './util'
|
||||
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
|
||||
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 renderLink (h, to, text, active) {
|
||||
return h('router-link', {
|
||||
props: {
|
||||
to,
|
||||
activeClass: '',
|
||||
exactActiveClass: ''
|
||||
},
|
||||
class: {
|
||||
active,
|
||||
'sidebar-link': true
|
||||
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)
|
||||
]);
|
||||
}));
|
||||
}
|
||||
}, 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 +1,7 @@
|
||||
<?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>
|
||||
<?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>
|
||||
|
||||
|
Before Width: | Height: | Size: 216 B After Width: | Height: | Size: 246 B |
@ -13,6 +13,12 @@
|
||||
@screen md {
|
||||
@apply .p-8;
|
||||
}
|
||||
|
||||
&.error-page {
|
||||
h1 {
|
||||
@apply .pt-12 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-of-contents {
|
||||
@ -43,7 +49,7 @@
|
||||
.next, .prev {
|
||||
@apply .inline-block;
|
||||
}
|
||||
|
||||
|
||||
.next {
|
||||
@apply .float-right;
|
||||
}
|
||||
|
||||
@ -46,6 +46,10 @@ a.header-anchor {
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
@apply .border-l-4 .border-grey-light .text-grey .p-1 .pl-4 .font-light .my-4 .text-xl;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@apply .mb-4 .font-semibold;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user