mirror of https://github.com/vuejs/vitepress
parent
796a1a6730
commit
255a2c4853
@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="theme">
|
||||
<h1>404</h1>
|
||||
<blockquote>{{ getMsg() }}</blockquote>
|
||||
<a :href="$site.base" aria-label="go to home">
|
||||
Take me home.
|
||||
</a>
|
||||
</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 {
|
||||
setup: () => ({
|
||||
getMsg() {
|
||||
return msgs[Math.floor(Math.random() * msgs.length)]
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
@ -1,19 +1,94 @@
|
||||
<template>
|
||||
<a
|
||||
class="site-title"
|
||||
class="title"
|
||||
:aria-label="$site.title + ', back to home'"
|
||||
:href="$site.base"
|
||||
>{{ $site.title }}</a
|
||||
>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/hello/index">hello</a></li>
|
||||
</ul>
|
||||
<img
|
||||
class="logo"
|
||||
v-if="$theme.logo"
|
||||
:src="withBase($theme.logo)"
|
||||
alt="logo"
|
||||
/>
|
||||
<span>{{ $site.title }}</span>
|
||||
</a>
|
||||
<nav class="nav-links" v-if="navData">
|
||||
<a
|
||||
class="nav-link"
|
||||
v-for="{ text, link, target, rel, ariaLabel } of navData"
|
||||
:class="{ active: isActiveLink(link) }"
|
||||
:href="withBase(link)"
|
||||
:target="target"
|
||||
:rel="rel"
|
||||
:aria-label="ariaLabel"
|
||||
>{{ text }}</a
|
||||
>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed } from 'vue'
|
||||
import { useSiteData, useRoute } from 'vitepress'
|
||||
import { withBase } from '../utils'
|
||||
|
||||
const normalizePath = (path) => {
|
||||
path = path
|
||||
.replace(/#.*$/, '')
|
||||
.replace(/\?.*$/, '')
|
||||
.replace(/\.html$/, '')
|
||||
if (path.endsWith('/')) {
|
||||
path += 'index'
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const isActiveLink = (link) => {
|
||||
return normalizePath(withBase(link)) === normalizePath(route.path)
|
||||
}
|
||||
|
||||
return {
|
||||
withBase,
|
||||
isActiveLink,
|
||||
// use computed in dev for hot reload
|
||||
navData: __DEV__
|
||||
? computed(() => useSiteData().value.themeConfig.nav)
|
||||
: useSiteData().value.themeConfig.nav
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.site-title {
|
||||
.title {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-right: 0.75rem;
|
||||
height: 1.3rem;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-color);
|
||||
margin-left: 1.5rem;
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
height: 1.75rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link.active {
|
||||
border-bottom: 2px solid var(--accent-color);
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,77 @@
|
||||
export namespace DefaultTheme {
|
||||
export interface Config {
|
||||
logo?: string
|
||||
nav?: NavItem[] | false
|
||||
sidebar?: SideBarConfig | MultiSideBarConfig
|
||||
search?: SearchConfig | false
|
||||
editLink?: EditLinkConfig | false
|
||||
lastUpdated?: string | boolean
|
||||
prevLink?: boolean
|
||||
nextLink?: boolean
|
||||
}
|
||||
|
||||
// navbar --------------------------------------------------------------------
|
||||
|
||||
export type NavItem = NavItemWithLink | NavItemWithChildren
|
||||
|
||||
export interface NavItemWithLink extends NavItemBase {
|
||||
link: string
|
||||
}
|
||||
|
||||
export interface NavItemWithChildren extends NavItemBase {
|
||||
items: NavItem[]
|
||||
}
|
||||
|
||||
export interface NavItemBase {
|
||||
text: string
|
||||
target?: string
|
||||
rel?: string
|
||||
ariaLabel?: string
|
||||
}
|
||||
|
||||
// sidebar -------------------------------------------------------------------
|
||||
|
||||
export type SideBarConfig = SideBarItem[] | 'auto' | false
|
||||
|
||||
export interface MultiSideBarConfig {
|
||||
[path: string]: SideBarConfig
|
||||
}
|
||||
|
||||
export type SideBarItem = string | [string, string] | SideBarGroup
|
||||
|
||||
export interface SideBarGroup {
|
||||
title: string
|
||||
path?: string
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
collapsable?: boolean
|
||||
children: SideBarItem[]
|
||||
}
|
||||
|
||||
// search --------------------------------------------------------------------
|
||||
|
||||
export interface SearchConfig {
|
||||
/**
|
||||
* @default 5
|
||||
*/
|
||||
maxSuggestions?: number
|
||||
/**
|
||||
* @default ''
|
||||
*/
|
||||
placeholder?: string
|
||||
algolia?: {
|
||||
apiKey: string
|
||||
indexName: string
|
||||
}
|
||||
}
|
||||
|
||||
// edit link -----------------------------------------------------------------
|
||||
|
||||
export interface EditLinkConfig {
|
||||
repo: string
|
||||
dir?: string
|
||||
branch?: string
|
||||
text?: string
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
import './layout.css'
|
||||
import Layout from './Layout.vue'
|
||||
import NotFound from './NotFound.vue'
|
||||
import { Theme } from '../app/theme'
|
||||
|
||||
const theme: Theme = {
|
||||
Layout
|
||||
Layout,
|
||||
NotFound
|
||||
}
|
||||
|
||||
export default theme
|
||||
|
@ -0,0 +1,59 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.theme {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
|
||||
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
--border-color: rgb(226, 232, 240);
|
||||
--header-height: 4rem;
|
||||
--sidebar-width: 18rem;
|
||||
--text-color: #2c3e50;
|
||||
--accent-color: #3eaf7c;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: var(--header-height);
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 1.75rem 0 1.5rem;
|
||||
}
|
||||
|
||||
aside {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: var(--sidebar-width);
|
||||
padding-top: calc(var(--header-height) + 1.5rem);
|
||||
border-right: 1px solid var(--border-color);
|
||||
background-color: #fff;
|
||||
z-index: 3;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: var(--header-height);
|
||||
margin-left: var(--sidebar-width);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, strong, b {
|
||||
font-weight: 600;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import { useSiteData } from 'vitepress'
|
||||
|
||||
export function withBase(path: string) {
|
||||
return (useSiteData().value.base + path).replace(/\/+/g, '/')
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
export * from './shared'
|
||||
export * from '../dist/client/app/exports'
|
||||
export * from '../dist/node/index'
|
||||
export * from '../dist/client/app/exports'
|
||||
export * from '../dist/client/theme-default/config'
|
||||
|
Loading…
Reference in new issue