wip: default theme

pull/20/head
Evan You 5 years ago
parent 1be0a0b0a1
commit 3d2f7e27de

@ -67,7 +67,7 @@ export function createRouter(
targetLoc.hash targetLoc.hash
) as HTMLElement ) as HTMLElement
if (target) { if (target) {
scrollTo(target, targetLoc.hash, false) scrollTo(target, targetLoc.hash)
return return
} }
} }
@ -103,10 +103,11 @@ export function createRouter(
) { ) {
e.preventDefault() e.preventDefault()
if (pathname === currentUrl.pathname) { if (pathname === currentUrl.pathname) {
// smooth scroll bewteen hash anchors in the same page // scroll bewteen hash anchors in the same page
if (hash && hash !== currentUrl.hash) { if (hash && hash !== currentUrl.hash) {
history.pushState(null, '', hash) history.pushState(null, '', hash)
scrollTo(link, hash) // use smooth scroll when clicking on header anchor links
scrollTo(link, hash, link.classList.contains('header-anchor'))
} }
} else { } else {
go(href) go(href)
@ -145,18 +146,15 @@ export function useRoute(): Route {
return useRouter().route return useRouter().route
} }
function scrollTo(el: HTMLElement, hash: string, smooth = true) { function scrollTo(el: HTMLElement, hash: string, smooth = false) {
const pageOffset = document.getElementById('app')!.offsetTop const pageOffset = document.getElementById('app')!.offsetTop
const target = el.classList.contains('.header-anchor') const target = el.classList.contains('.header-anchor')
? el ? el
: document.querySelector(hash) : document.querySelector(hash)
if (target) { if (target) {
const targetTop = (target as HTMLElement).offsetTop - pageOffset - 15 const targetTop = (target as HTMLElement).offsetTop - pageOffset - 15
const currentTop = window.scrollY // only smooth scroll if distance is smaller than screen height.
const distance = Math.abs(targetTop - currentTop) if (!smooth || Math.abs(targetTop - window.scrollY) > window.innerHeight) {
// only smooth scroll if distance is smaller than 1.5 x
// screen height.
if (!smooth || distance > window.innerHeight * 1.5) {
window.scrollTo(0, targetTop) window.scrollTo(0, targetTop)
} else { } else {
window.scrollTo({ window.scrollTo({

@ -15,7 +15,7 @@
<script> <script>
import NavBar from './components/NavBar.vue' import NavBar from './components/NavBar.vue'
import SideBar from './components/Sidebar.vue' import SideBar from './components/SideBar.vue'
import Page from './components/Page.vue' import Page from './components/Page.vue'
export default { export default {

@ -0,0 +1,33 @@
// TODO dropdowns
import { computed } from 'vue'
import { useSiteData, useRoute } from 'vitepress'
import { withBase } from '../utils'
const normalizePath = (path: string): string => {
path = path
.replace(/#.*$/, '')
.replace(/\?.*$/, '')
.replace(/\.html$/, '')
if (path.endsWith('/')) {
path += 'index'
}
return path
}
export default {
setup() {
const route = useRoute()
const isActiveLink = (link: string): boolean => {
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
}
}
}

@ -26,41 +26,7 @@
</nav> </nav>
</template> </template>
<script> <script src="./NavBar"></script>
// TODO dropdowns
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> <style>
.title { .title {

@ -22,4 +22,8 @@
.content img { .content img {
max-width: 100%; max-width: 100%;
} }
/*
.content div > h1:first-child, .content div > h2:first-child {
margin-top: 0;
} */
</style> </style>

@ -0,0 +1,115 @@
import { useSiteData, usePageData, useRoute } from 'vitepress'
import { computed, h, FunctionalComponent } from 'vue'
import { Header } from '../../../../types/shared'
import { DefaultTheme } from '../config'
const SideBarItem: FunctionalComponent<{
item: ResolvedSidebarItem
}> = (props) => {
const {
item: { link, text, children }
} = props
return h('li', [
h('a', { href: link }, text),
children
? h(
'ul',
children.map((c) => h(SideBarItem, { item: c }))
)
: null
])
}
export default {
components: {
SideBarItem
},
setup() {
const pageData = usePageData()
const siteData = useSiteData()
const route = useRoute()
const resolveSidebar = () => {
const {
headers,
frontmatter: { sidebar, sidebarDepth = 2 }
} = pageData.value
if (sidebar === 'auto') {
// auto, render headers of current page
return resolveAutoSidebar(headers, sidebarDepth)
} else if (Array.isArray(sidebar)) {
// in-page array config
return resolveArraySidebar(sidebar, sidebarDepth)
} else if (sidebar === false) {
return []
} else {
// no explicit page sidebar config
// check global theme config
const { sidebar: themeSidebar } = siteData.value.themeConfig
if (themeSidebar === 'auto') {
return resolveAutoSidebar(headers, sidebarDepth)
} else if (Array.isArray(themeSidebar)) {
return resolveArraySidebar(themeSidebar, sidebarDepth)
} else if (themeSidebar === false) {
return []
} else if (typeof themeSidebar === 'object') {
return resolveMultiSidebar(themeSidebar, route.path, sidebarDepth)
}
}
}
return {
items: __DEV__ ? computed(resolveSidebar) : resolveSidebar()
}
}
}
type ResolvedSidebar = ResolvedSidebarItem[]
interface ResolvedSidebarItem {
text: string
link?: string
isGroup?: boolean
children?: ResolvedSidebarItem[]
}
function resolveAutoSidebar(headers: Header[], depth: number): ResolvedSidebar {
const ret: ResolvedSidebar = []
let lastH2: ResolvedSidebarItem | undefined = undefined
headers.forEach(({ level, title, slug }) => {
if (level - 1 > depth) {
return
}
const item: ResolvedSidebarItem = {
text: title,
link: `#${slug}`
}
if (level === 2) {
lastH2 = item
ret.push(item)
} else if (lastH2) {
;(lastH2.children || (lastH2.children = [])).push(item)
}
})
return ret
}
function resolveArraySidebar(
config: DefaultTheme.SideBarItem[],
depth: number
): ResolvedSidebar {
return []
}
function resolveMultiSidebar(
config: DefaultTheme.MultiSideBarConfig,
path: string,
depth: number
): ResolvedSidebar {
return []
}

@ -1,5 +1,33 @@
<template> <template>
<ul class="sidebar"> <div class="sidebar">
<li>sidebar</li> <ul>
<SideBarItem v-for="item of items" :item="item"></SideBarItem>
</ul> </ul>
</div>
</template> </template>
<script src="./SideBar"></script>
<style>
.sidebar ul {
list-style-type: none;
line-height: 2;
padding-left: 1.5rem;
margin: 0;
font-weight: 500;
}
.sidebar a {
color: var(--text-color);
}
.sidebar a:hover {
color: var(--accent-color);
}
.sidebar ul ul {
font-weight: 400;
font-size: 0.9em;
padding-left: 1rem;
}
</style>

@ -37,11 +37,16 @@ export namespace DefaultTheme {
[path: string]: SideBarConfig [path: string]: SideBarConfig
} }
export type SideBarItem = string | [string, string] | SideBarGroup export type SideBarItem = SideBarLink | SideBarGroup
export interface SideBarLink {
text: string
link: string
}
export interface SideBarGroup { export interface SideBarGroup {
title: string text: string
path?: string link?: string
/** /**
* @default false * @default false
*/ */

@ -1,6 +1,7 @@
import './styles/vars.css' import './styles/vars.css'
import './styles/layout.css' import './styles/layout.css'
import './styles/code.css' import './styles/code.css'
import './styles/custom-blocks.css'
import Layout from './Layout.vue' import Layout from './Layout.vue'
import NotFound from './NotFound.vue' import NotFound from './NotFound.vue'

@ -1,6 +1,6 @@
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, Courier New, monospace; font-family: source-code-pro, Menlo, Monaco, Consolas, Courier New, monospace;
font-size: 0.9em; font-size: 0.85em;
color: var(--text-color-light); color: var(--text-color-light);
background-color: rgba(27, 31, 35, 0.05); background-color: rgba(27, 31, 35, 0.05);
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;

@ -0,0 +1,58 @@
.custom-block .custom-block-title {
font-weight: 600;
margin-bottom: -0.4rem;
}
.custom-block.tip,
.custom-block.warning,
.custom-block.danger {
padding: 0.1rem 1.5rem;
border-left-width: 0.5rem;
border-left-style: solid;
margin: 1rem 0;
}
.custom-block.tip {
background-color: #f3f5f7;
border-color: #42b983;
}
.custom-block.warning {
background-color: rgba(255, 229, 100, 0.3);
border-color: #e7c000;
color: #6b5900;
}
.custom-block.warning .custom-block-title {
color: #b29400;
}
.custom-block.warning a {
color: var(--text-color);
}
.custom-block.danger {
background-color: #ffe6e6;
border-color: #c00;
color: #4d0000;
}
.custom-block.danger .custom-block-title {
color: #900;
}
.custom-block.danger a {
color: var(--text-color);
}
.custom-block.details {
display: block;
position: relative;
border-radius: 2px;
margin: 1.6em 0;
padding: 1.6em;
background-color: #eee;
}
.custom-block.details h4 {
margin-top: 0;
}
.custom-block.details figure:last-child,
.custom-block.details p:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
.custom-block.details summary {
outline: none;
cursor: pointer;
}

@ -35,7 +35,7 @@ aside {
left: 0; left: 0;
height: 100%; height: 100%;
width: var(--sidebar-width); width: var(--sidebar-width);
padding-top: calc(var(--header-height) + 1.5rem); padding: calc(var(--header-height) + 1.5rem) 1.5rem 1.5rem 0;
border-right: 1px solid var(--border-color); border-right: 1px solid var(--border-color);
background-color: #fff; background-color: #fff;
z-index: 3; z-index: 3;

@ -1,7 +1,7 @@
.theme { .theme {
--border-color: rgb(226, 232, 240); --border-color: rgb(226, 232, 240);
--header-height: 4rem; --header-height: 4rem;
--sidebar-width: 18rem; --sidebar-width: 20rem;
--text-color: #2c3e50; --text-color: #2c3e50;
--text-color-light: #476582; --text-color-light: #476582;
--code-bg-color: #282c34; --code-bg-color: #282c34;

Loading…
Cancel
Save