refactor: refactor sidebar styles

pull/142/head
Kia King Ishii 4 years ago
parent 621ca3e26f
commit 1836934f7c

@ -8,16 +8,14 @@
</NavBar>
<ToggleSideBarButton @toggle="toggleSidebar" />
</header>
<aside :class="{ open: openSideBar }">
<SideBar>
<template #top>
<slot name="sidebar-top" />
</template>
<template #bottom>
<slot name="sidebar-bottom" />
</template>
</SideBar>
</aside>
<SideBar :open="openSideBar">
<template #sidebar-top>
<slot name="sidebar-top" />
</template>
<template #sidebar-bottom>
<slot name="sidebar-bottom" />
</template>
</SideBar>
<!-- TODO: make this button accessible -->
<div class="sidebar-mask" @click="toggleSidebar(false)" />
<main class="home" aria-labelledby="main-title" v-if="enableHome">

@ -42,7 +42,7 @@
@media screen and (max-width: 719px) {
.hide-mobile {
display: none;
display: none !important;
}
}
</style>

@ -1,70 +1,69 @@
<template>
<div class="nav-item">
<div class="navbar-link">
<a
class="nav-link"
class="item"
:class="classes"
:href="href"
:target="target"
:rel="rel"
:aria-label="item.ariaLabel"
>
{{ item.text }}
<OutboundLink v-if="isExternalLink" />
{{ item.text }} <OutboundLink v-if="isExternalLink" />
</a>
</div>
</template>
<script src="./NavBarLink"></script>
<style>
.nav-item {
<style scoped>
.navbar-link {
position: relative;
display: inline-block;
margin-left: 1.5rem;
line-height: 2rem;
padding: 0 1.5rem;
}
@media screen and (max-width: 719px) {
.nav-item {
display: block;
margin-left: 0;
padding: 0.3rem 1.5rem;
@media (min-width: 720px) {
.navbar-link {
padding: 0;
}
.navbar-link + .navbar-link,
.dropdown-wrapper + .navbar-link {
padding-left: 1.5rem;
}
}
.nav-link {
.item {
display: block;
margin-bottom: -2px;
border-bottom: 2px solid transparent;
font-size: 0.9rem;
font-weight: 500;
line-height: 1.4rem;
line-height: 40px;
font-size: 1rem;
font-weight: 600;
color: var(--c-text);
white-space: nowrap;
}
.nav-link:hover,
.nav-link.active {
border-bottom-color: var(--c-brand);
.item:hover,
.item.active {
text-decoration: none;
color: var(--c-brand);
}
.nav-link.external:hover {
border-bottom-color: transparent;
}
@media (min-width: 720px) {
.item {
border-bottom: 2px solid transparent;
line-height: 1.5rem;
font-size: .9rem;
font-weight: 500;
}
@media screen and (max-width: 719px) {
.nav-link {
line-height: 1.7;
font-size: 1em;
font-weight: 600;
border-bottom: none;
margin-bottom: 0;
.item:hover,
.item.active {
color: var(--c-text);
border-bottom-color: var(--c-brand);
}
.nav-link:hover,
.nav-link.active {
color: var(--c-brand);
.item.external:hover {
border-bottom-color: transparent;
}
}
</style>

@ -1,5 +1,5 @@
<template>
<nav class="nav-links" v-if="navData || repoInfo">
<nav v-if="navData || repoInfo" class="navbar-links">
<template v-if="navData">
<template v-for="item of navData">
<NavDropdownLink v-if="item.items" :item="item" />
@ -13,21 +13,17 @@
<script src="./NavBarLinks"></script>
<style>
.nav-links {
display: flex;
align-items: center;
height: 35px;
list-style-type: none;
transform: translateY(1px);
<style scoped>
.navbar-links {
padding: 1rem 0;
border-bottom: 1px solid var(--c-divider);
}
@media screen and (max-width: 719px) {
.nav-links {
display: block;
height: auto;
padding: 0.5rem 0 1rem;
border-bottom: 1px solid var(--c-divider);
@media (min-width: 720px) {
.navbar-links {
display: flex;
align-items: center;
border-bottom: 0;
}
}
</style>

@ -30,7 +30,7 @@ export default defineComponent({
.page {
margin: 0 auto;
padding: 0 1.5rem 4rem;
max-width: 50rem;
max-width: 48rem;
}
.content {

@ -1,123 +0,0 @@
import { useRoute, useSiteDataByRoute } from 'vitepress'
import { computed } from 'vue'
import { Header } from '../../../../types/shared'
import { getPathDirName } from '../utils'
import { DefaultTheme } from '../config'
import { useActiveSidebarLinks } from '../composables/activeSidebarLink'
import NavBarLinks from './NavBarLinks.vue'
import { SideBarItem } from './SideBarItem'
export interface ResolvedSidebarItem {
text: string
link?: string
isGroup?: boolean
children?: ResolvedSidebarItem[]
}
type ResolvedSidebar = ResolvedSidebarItem[]
export default {
components: {
NavBarLinks,
SideBarItem
},
setup() {
const route = useRoute()
const siteData = useSiteDataByRoute()
useActiveSidebarLinks()
return {
items: computed(() => {
const {
headers,
frontmatter: { sidebar, sidebarDepth = 2 }
} = route.data
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,
headers,
sidebarDepth
)
}
}
})
}
}
}
function resolveAutoSidebar(headers: Header[], depth: number): ResolvedSidebar {
const ret: ResolvedSidebar = []
if (headers === undefined) {
return []
}
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 config
}
function resolveMultiSidebar(
config: DefaultTheme.MultiSideBarConfig,
path: string,
headers: Header[],
depth: number
): ResolvedSidebar {
const paths = [path, Object.keys(config)[0]]
const item = paths.map((x) => config[getPathDirName(x)]).find(Boolean)
if (Array.isArray(item)) {
return resolveArraySidebar(item, depth)
}
if (item === 'auto') {
return resolveAutoSidebar(headers, depth)
}
return []
}

@ -1,96 +1,72 @@
<template>
<NavBarLinks class="show-mobile" />
<aside class="sidebar" :class="{ open }">
<div class="nav">
<NavBarLinks class="show-mobile" />
</div>
<slot name="top" />
<slot name="sidebar-top" />
<ul class="sidebar">
<SideBarItem v-for="item of items" :item="item" />
</ul>
<SideBarLinks />
<slot name="bottom" />
<slot name="sidebar-bottom" />
</aside>
</template>
<script src="./SideBar"></script>
<script lang="ts">
import { defineComponent } from 'vue'
import NavBarLinks from './NavBarLinks.vue'
import SideBarLinks from './SideBarLinks.vue'
<style>
.show-mobile {
display: none;
}
export default defineComponent({
components: {
NavBarLinks,
SideBarLinks
},
@media screen and (max-width: 719px) {
.show-mobile {
display: block;
props: {
open: { type: Boolean, required: true }
}
}
.sidebar,
.sidebar-items {
list-style-type: none;
line-height: 2;
padding: 0;
margin: 0;
}
})
</script>
<style scoped>
.sidebar {
padding: 1.5rem 0;
position: fixed;
top: var(--header-height);
bottom: 0;
left: 0;
z-index: var(--z-index-sidebar);
border-right: 1px solid var(--c-divider);
width: 16.4rem;
background-color: var(--c-bg);
overflow-y: auto;
transform: translateX(-100%);
transition: transform .25s ease;
}
.sidebar-data {
padding: 1.5rem 0;
}
@media screen and (max-width: 719px) {
.sidebar-data {
padding: 1rem;
@media (min-width: 720px) {
.sidebar {
transform: translateX(0);
}
}
.sidebar-items .sidebar-items {
padding-left: 1rem;
}
.sidebar-items .sidebar-items .sidebar-link {
border-left: 0;
}
.sidebar-items .sidebar-items .sidebar-link.active {
font-weight: 500;
}
.sidebar-items .sidebar-link {
padding: 0.35rem 1rem 0.35rem 2rem;
line-height: 1.4;
font-size: 0.95em;
font-weight: 400;
}
.sidebar-item + .sidebar-item {
padding-top: 0.75rem;
@media (min-width: 960px) {
.sidebar {
width: 20rem;
}
}
.sidebar-items > .sidebar-item + .sidebar-item {
padding-top: 0;
.sidebar.open {
transform: translateX(0);
}
.sidebar-link {
.nav {
display: block;
margin: 0;
border-left: 0.25rem solid transparent;
padding: 0.35rem 1.5rem 0.35rem 1.25rem;
line-height: 1.7;
font-size: 1.05em;
font-weight: 700;
color: var(--c-text);
}
a.sidebar-link:hover {
text-decoration: none;
color: var(--c-brand);
}
a.sidebar-link.active {
border-left-color: var(--c-brand);
font-weight: 600;
color: var(--c-brand);
@media (min-width: 720px) {
.nav {
display: none;
}
}
</style>

@ -1,33 +1,31 @@
import { useRoute, useSiteData } from 'vitepress'
import { FunctionalComponent, h, VNode } from 'vue'
import { Header } from '../../../../types/shared'
import { useRoute, useSiteData } from 'vitepress'
import { Header } from '/@types/shared'
import { DefaultTheme } from '../config'
import { joinUrl, isActive } from '../utils'
import { ResolvedSidebarItem } from './SideBar'
interface HeaderWithChildren extends Header {
children?: Header[]
}
export const SideBarItem: FunctionalComponent<{
item: ResolvedSidebarItem
export const SideBarLink: FunctionalComponent<{
item: DefaultTheme.SideBarItem
}> = (props) => {
const {
item: { link: relLink, text, children }
} = props
const route = useRoute()
const siteData = useSiteData()
const site = useSiteData()
const link = resolveLink(siteData.value.base, relLink || '')
const active = isActive(route, link)
const headers = route.data.headers
const text = props.item.text
const link = resolveLink(site.value.base, props.item.link)
const children = (props.item as DefaultTheme.SideBarGroup).children
const active = isActive(route, link)
const childItems = createChildren(active, children, headers)
return h('li', { class: 'sidebar-item' }, [
return h('li', { class: 'sidebar-link' }, [
h(
link ? 'a' : 'p',
{
class: { 'sidebar-link': true, active },
class: { 'sidebar-link-item': true, active },
href: link
},
text
@ -36,26 +34,30 @@ export const SideBarItem: FunctionalComponent<{
])
}
function resolveLink(base: string, path: string): string | undefined {
return path
? // keep relative hash to the same page
path.startsWith('#')
? path
: joinUrl(base, path)
: undefined
function resolveLink(base: string, path?: string): string | undefined {
if (path === undefined) {
return path
}
// keep relative hash to the same page
if (path.startsWith('#')) {
return path
}
return joinUrl(base, path)
}
function createChildren(
active: boolean,
children?: ResolvedSidebarItem[],
children?: DefaultTheme.SideBarItem[],
headers?: Header[]
): VNode | null {
if (children && children.length > 0) {
return h(
'ul',
{ class: 'sidebar-items' },
{ class: 'sidebar-links' },
children.map((c) => {
return h(SideBarItem, { item: c })
return h(SideBarLink, { item: c })
})
)
}
@ -65,7 +67,7 @@ function createChildren(
: null
}
function resolveHeaders(headers: Header[]): ResolvedSidebarItem[] {
function resolveHeaders(headers: Header[]): DefaultTheme.SideBarItem[] {
return mapHeaders(groupHeaders(headers))
}
@ -82,7 +84,7 @@ function groupHeaders(headers: Header[]): HeaderWithChildren[] {
return headers.filter((h) => h.level === 2)
}
function mapHeaders(headers: HeaderWithChildren[]): ResolvedSidebarItem[] {
function mapHeaders(headers: HeaderWithChildren[]): DefaultTheme.SideBarItem[] {
return headers.map((header) => ({
text: header.title,
link: `#${header.slug}`,

@ -0,0 +1,29 @@
<template>
<ul v-if="items.length > 0" class="sidebar-links">
<SideBarLink
v-for="item of items"
:key="item.text"
:item="item"
/>
</ul>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useSideBar } from '../composables/sideBar'
import { SideBarLink } from './SideBarLink'
export default defineComponent({
components: {
SideBarLink
},
setup() {
const items = useSideBar()
return {
items
}
}
})
</script>

@ -0,0 +1,77 @@
import { computed } from 'vue'
import { useRoute, useSiteDataByRoute } from 'vitepress'
import { Header } from '/@types/shared'
import { useActiveSidebarLinks } from '../composables/activeSidebarLink'
import { getSideBarConfig } from '../support/sideBar'
import { DefaultTheme } from '../config'
export function useSideBar() {
const route = useRoute()
const site = useSiteDataByRoute()
useActiveSidebarLinks()
return computed(() => {
// at first, we'll check if we can find the sidebar setting in frontmatter.
const headers = route.data.headers
const frontSidebar = route.data.frontmatter.sidebar
const sidebarDepth = route.data.frontmatter.sidebarDepth
// if it's `false`, we'll just return an empty array here.
if (frontSidebar === false) {
return []
}
// if it's `atuo`, render headers of the current page
if (frontSidebar === 'auto') {
return resolveAutoSidebar(headers, sidebarDepth)
}
// now, there's no sidebar setting at frontmatter; let's see the configs
const themeSidebar = getSideBarConfig(
site.value.themeConfig.sidebar,
route.path
)
if (themeSidebar === false) {
return []
}
if (themeSidebar === 'auto') {
return resolveAutoSidebar(headers, sidebarDepth)
}
return themeSidebar
})
}
function resolveAutoSidebar(
headers: Header[],
depth: number
): DefaultTheme.SideBarItem[] {
const ret: DefaultTheme.SideBarItem[] = []
if (headers === undefined) {
return []
}
let lastH2: DefaultTheme.SideBarItem | undefined = undefined
headers.forEach(({ level, title, slug }) => {
if (level - 1 > depth) {
return
}
const item: DefaultTheme.SideBarItem = {
text: title,
link: `#${slug}`
}
if (level === 2) {
lastH2 = item
ret.push(item)
} else if (lastH2) {
;((lastH2 as any).children || ((lastH2 as any).children = [])).push(item)
}
})
return ret
}

@ -2,6 +2,7 @@ import './styles/vars.css'
import './styles/layout.css'
import './styles/code.css'
import './styles/custom-blocks.css'
import './styles/sidebar-links.css'
import { Theme } from 'vitepress'
import Layout from './Layout.vue'

@ -197,7 +197,7 @@ blockquote > p {
height: var(--header-height);
background-color: #fff;
border-bottom: 1px solid var(--c-divider);
z-index: 4;
z-index: var(--z-index-navbar);
display: flex;
align-items: center;
justify-content: space-between;
@ -210,18 +210,6 @@ blockquote > p {
}
}
.theme aside {
position: fixed;
top: var(--header-height);
bottom: 0;
left: 0;
width: var(--sidebar-width);
border-right: 1px solid var(--c-divider);
background-color: #fff;
z-index: 3;
overflow-y: auto;
}
.theme.sidebar-open .sidebar-mask {
display: block;
}
@ -246,17 +234,6 @@ blockquote > p {
}
}
@media screen and (max-width: 719px) {
.theme aside {
transition: transform 0.2s ease;
transform: translateX(-100%);
}
.theme aside.open {
transform: translateX(0);
}
}
.sidebar-mask {
z-index: 2;
position: fixed;
@ -271,7 +248,7 @@ blockquote > p {
@media screen and (min-width: 960px) {
.theme main {
padding-left: var(--sidebar-width);
padding-left: 20rem;
}
}
@ -286,7 +263,7 @@ blockquote > p {
@media screen and (max-width: 959px) {
.theme main {
margin-left: var(--sidebar-width);
margin-left: 16.4rem;
}
}

@ -0,0 +1,85 @@
.sidebar-links {
margin: 0;
padding: 0;
list-style: none;
}
.sidebar-link-item {
display: block;
margin: 0;
border-left: .25rem solid transparent;
color: var(--c-text);
}
a.sidebar-link-item:hover {
text-decoration: none;
color: var(--c-brand);
}
a.sidebar-link-item.active {
color: var(--c-brand);
}
.sidebar > .sidebar-links {
padding: 1.5rem 0;
}
.sidebar > .sidebar-links > .sidebar-link + .sidebar-link {
padding-top: 1rem;
}
.sidebar > .sidebar-links > .sidebar-link > .sidebar-link-item {
padding: .35rem 1.5rem .35rem 1.25rem;
font-size: 1.1rem;
font-weight: 700;
}
.sidebar > .sidebar-links > .sidebar-link > a.sidebar-link-item.active {
border-left-color: var(--c-brand);
font-weight: 600;
}
.sidebar > .sidebar-links > .sidebar-link > .sidebar-links > .sidebar-link > .sidebar-link-item {
display: block;
padding: .35rem 1.5rem .35rem 2rem;
line-height: 1.4;
font-size: 1rem;
font-weight: 400;
}
.sidebar > .sidebar-links > .sidebar-link > .sidebar-links > .sidebar-link > a.sidebar-link-item.active {
border-left-color: var(--c-brand);
font-weight: 600;
}
.sidebar >
.sidebar-links >
.sidebar-link >
.sidebar-links >
.sidebar-link >
.sidebar-links >
.sidebar-link >
.sidebar-link-item {
display: block;
padding: .3rem 1.5rem .3rem 3rem;
line-height: 1.4;
font-size: .9rem;
font-weight: 400;
}
.sidebar >
.sidebar-links >
.sidebar-link >
.sidebar-links >
.sidebar-link >
.sidebar-links >
.sidebar-link >
.sidebar-links >
.sidebar-link >
.sidebar-link-item {
display: block;
padding: .3rem 1.5rem .3rem 4rem;
line-height: 1.4;
font-size: .9rem;
font-weight: 400;
}

@ -22,12 +22,18 @@
--font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
/**
* Z Indexes
* --------------------------------------------------------------------- */
--z-index-navbar: 1100;
--z-index-sidebar: 1000;
/**
* Sizes
* --------------------------------------------------------------------- */
--header-height: 3.6rem;
--sidebar-width: 16.4rem;
}
/** Fallback Styles */

@ -34,6 +34,7 @@ export function getSideBarConfig(
// get the very first segment of the path to compare with nulti sidebar keys
// and make sure it's surrounded by slash
path = removeExtention(path)
path = ensureStartingSlash(path).split('/')[1] || '/'
path = ensureSlash(path)

Loading…
Cancel
Save