From 3d2f7e27dea15bab9b6100789c4e75f76d0048ad Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 27 May 2020 12:19:43 -0400 Subject: [PATCH] wip: default theme --- src/client/app/router.ts | 16 ++- src/client/theme-default/Layout.vue | 2 +- src/client/theme-default/components/NavBar.ts | 33 +++++ .../theme-default/components/NavBar.vue | 36 +----- src/client/theme-default/components/Page.vue | 4 + .../theme-default/components/SideBar.ts | 115 ++++++++++++++++++ .../theme-default/components/SideBar.vue | 34 +++++- src/client/theme-default/config.ts | 11 +- src/client/theme-default/index.ts | 1 + src/client/theme-default/styles/code.css | 2 +- .../theme-default/styles/custom-blocks.css | 58 +++++++++ src/client/theme-default/styles/layout.css | 2 +- src/client/theme-default/styles/vars.css | 2 +- 13 files changed, 262 insertions(+), 54 deletions(-) create mode 100644 src/client/theme-default/components/NavBar.ts create mode 100644 src/client/theme-default/components/SideBar.ts create mode 100644 src/client/theme-default/styles/custom-blocks.css diff --git a/src/client/app/router.ts b/src/client/app/router.ts index cda60ece..2a3dd616 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -67,7 +67,7 @@ export function createRouter( targetLoc.hash ) as HTMLElement if (target) { - scrollTo(target, targetLoc.hash, false) + scrollTo(target, targetLoc.hash) return } } @@ -103,10 +103,11 @@ export function createRouter( ) { e.preventDefault() 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) { 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 { go(href) @@ -145,18 +146,15 @@ export function useRoute(): 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 target = el.classList.contains('.header-anchor') ? el : document.querySelector(hash) if (target) { const targetTop = (target as HTMLElement).offsetTop - pageOffset - 15 - const currentTop = window.scrollY - const distance = Math.abs(targetTop - currentTop) - // only smooth scroll if distance is smaller than 1.5 x - // screen height. - if (!smooth || distance > window.innerHeight * 1.5) { + // only smooth scroll if distance is smaller than screen height. + if (!smooth || Math.abs(targetTop - window.scrollY) > window.innerHeight) { window.scrollTo(0, targetTop) } else { window.scrollTo({ diff --git a/src/client/theme-default/Layout.vue b/src/client/theme-default/Layout.vue index 7a069510..66fac926 100644 --- a/src/client/theme-default/Layout.vue +++ b/src/client/theme-default/Layout.vue @@ -15,7 +15,7 @@ + diff --git a/src/client/theme-default/components/SideBar.ts b/src/client/theme-default/components/SideBar.ts new file mode 100644 index 00000000..fd30968e --- /dev/null +++ b/src/client/theme-default/components/SideBar.ts @@ -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 [] +} diff --git a/src/client/theme-default/components/SideBar.vue b/src/client/theme-default/components/SideBar.vue index b005e9e3..98ad1467 100644 --- a/src/client/theme-default/components/SideBar.vue +++ b/src/client/theme-default/components/SideBar.vue @@ -1,5 +1,33 @@ + + + + diff --git a/src/client/theme-default/config.ts b/src/client/theme-default/config.ts index 8c73973b..7fe5f028 100644 --- a/src/client/theme-default/config.ts +++ b/src/client/theme-default/config.ts @@ -37,11 +37,16 @@ export namespace DefaultTheme { [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 { - title: string - path?: string + text: string + link?: string /** * @default false */ diff --git a/src/client/theme-default/index.ts b/src/client/theme-default/index.ts index 4fb8746e..ea1da688 100644 --- a/src/client/theme-default/index.ts +++ b/src/client/theme-default/index.ts @@ -1,6 +1,7 @@ import './styles/vars.css' import './styles/layout.css' import './styles/code.css' +import './styles/custom-blocks.css' import Layout from './Layout.vue' import NotFound from './NotFound.vue' diff --git a/src/client/theme-default/styles/code.css b/src/client/theme-default/styles/code.css index 46208fd3..e420a5d7 100644 --- a/src/client/theme-default/styles/code.css +++ b/src/client/theme-default/styles/code.css @@ -1,6 +1,6 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, Courier New, monospace; - font-size: 0.9em; + font-size: 0.85em; color: var(--text-color-light); background-color: rgba(27, 31, 35, 0.05); padding: 0.25rem 0.5rem; diff --git a/src/client/theme-default/styles/custom-blocks.css b/src/client/theme-default/styles/custom-blocks.css new file mode 100644 index 00000000..ef363e4b --- /dev/null +++ b/src/client/theme-default/styles/custom-blocks.css @@ -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; +} diff --git a/src/client/theme-default/styles/layout.css b/src/client/theme-default/styles/layout.css index 8c1e7125..ccdffff8 100644 --- a/src/client/theme-default/styles/layout.css +++ b/src/client/theme-default/styles/layout.css @@ -35,7 +35,7 @@ aside { left: 0; height: 100%; 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); background-color: #fff; z-index: 3; diff --git a/src/client/theme-default/styles/vars.css b/src/client/theme-default/styles/vars.css index 2de084ec..e188d548 100644 --- a/src/client/theme-default/styles/vars.css +++ b/src/client/theme-default/styles/vars.css @@ -1,7 +1,7 @@ .theme { --border-color: rgb(226, 232, 240); --header-height: 4rem; - --sidebar-width: 18rem; + --sidebar-width: 20rem; --text-color: #2c3e50; --text-color-light: #476582; --code-bg-color: #282c34;