fix(vitepress): downgrade vitepress theme to 1.0.0-alpha8 to fix sidebar
This commit is contained in:
parent
7d4544f7b5
commit
e3bb4609c3
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { provide, watch } from 'vue'
|
||||
import { useData, useRoute } from 'vitepress'
|
||||
import { useRoute } from 'vitepress'
|
||||
import { useSidebar, useCloseSidebarOnEscape } from './composables/sidebar.js'
|
||||
import VPSkipLink from './components/VPSkipLink.vue'
|
||||
import VPBackdrop from './components/VPBackdrop.vue'
|
||||
|
@ -22,12 +22,10 @@ watch(() => route.path, closeSidebar)
|
|||
useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)
|
||||
|
||||
provide('close-sidebar', closeSidebar)
|
||||
|
||||
const { frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="frontmatter.layout !== false" class="Layout">
|
||||
<div class="Layout">
|
||||
<slot name="layout-top" />
|
||||
<VPSkipLink />
|
||||
<VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" />
|
||||
|
@ -63,7 +61,6 @@ const { frontmatter } = useData()
|
|||
<VPFooter />
|
||||
<slot name="layout-bottom" />
|
||||
</div>
|
||||
<Content v-else />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import docsearch from '@docsearch/js'
|
||||
import { default as docsearch } from '@docsearch/js'
|
||||
import { onMounted } from 'vue'
|
||||
import { useRouter, useRoute, useData } from 'vitepress'
|
||||
|
||||
|
@ -29,8 +29,7 @@ function poll() {
|
|||
}, 16)
|
||||
}
|
||||
|
||||
const docsearch$ = docsearch.default ?? docsearch
|
||||
type DocSearchProps = Parameters<typeof docsearch$>[0]
|
||||
type DocSearchProps = Parameters<typeof docsearch>[0]
|
||||
|
||||
function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
|
||||
// note: multi-lang search support is removed since the theme
|
||||
|
@ -75,7 +74,7 @@ function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
|
|||
}
|
||||
})
|
||||
|
||||
docsearch$(options)
|
||||
docsearch(options)
|
||||
}
|
||||
|
||||
function getRelativePath(absoluteUrl: string) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import { useAside } from '../composables/aside.ts'
|
||||
import { useAside } from '../composables/aside.js'
|
||||
|
||||
const { theme } = useData()
|
||||
const carbonOptions = theme.value.carbonAds
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import { useRoute, useData } from 'vitepress'
|
||||
import { useSidebar } from '../composables/sidebar.ts'
|
||||
import { useCopyCode } from '../composables/copy-code.js'
|
||||
import { useSidebar } from '../composables/sidebar.js'
|
||||
import Theme from '@theme/index'
|
||||
import VPPage from './VPPage.vue'
|
||||
import VPHome from './VPHome.vue'
|
||||
import VPDoc from './VPDoc.vue'
|
||||
import { inject } from 'vue'
|
||||
|
||||
const route = useRoute()
|
||||
const { frontmatter } = useData()
|
||||
const { hasSidebar } = useSidebar()
|
||||
|
||||
const NotFound = inject('NotFound')
|
||||
const NotFound = Theme.NotFound || (() => '404 Not Found')
|
||||
|
||||
useCopyCode()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vitepress'
|
||||
import { computed, provide, ref } from 'vue'
|
||||
import { useSidebar } from '../composables/sidebar.ts'
|
||||
import { useSidebar } from '../composables/sidebar.js'
|
||||
import VPDocAside from './VPDocAside.vue'
|
||||
import VPDocFooter from './VPDocFooter.vue'
|
||||
|
||||
|
@ -11,9 +11,6 @@ const { hasSidebar, hasAside } = useSidebar()
|
|||
const pageName = computed(() =>
|
||||
route.path.replace(/[./]+/g, '_').replace(/_html$/, '')
|
||||
)
|
||||
|
||||
const onContentUpdated = ref()
|
||||
provide('onContentUpdated', onContentUpdated)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -42,7 +39,7 @@ provide('onContentUpdated', onContentUpdated)
|
|||
<div class="content-container">
|
||||
<slot name="doc-before" />
|
||||
<main class="main">
|
||||
<Content class="vp-doc" :class="pageName" :onContentUpdated="onContentUpdated" />
|
||||
<Content class="vp-doc" :class="pageName" />
|
||||
</main>
|
||||
<slot name="doc-footer-before" />
|
||||
<VPDocFooter />
|
||||
|
@ -76,6 +73,10 @@ provide('onContentUpdated', onContentUpdated)
|
|||
max-width: 992px;
|
||||
}
|
||||
|
||||
.VPDoc:not(.has-sidebar) .aside {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.VPDoc:not(.has-sidebar) .content {
|
||||
max-width: 752px;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useData } from 'vitepress'
|
|||
import VPDocAsideOutline from './VPDocAsideOutline.vue'
|
||||
import VPDocAsideCarbonAds from './VPDocAsideCarbonAds.vue'
|
||||
|
||||
const { theme } = useData()
|
||||
const { page, theme } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -11,7 +11,7 @@ const { theme } = useData()
|
|||
<slot name="aside-top" />
|
||||
|
||||
<slot name="aside-outline-before" />
|
||||
<VPDocAsideOutline />
|
||||
<VPDocAsideOutline v-if="page.headers.length" />
|
||||
<slot name="aside-outline-after" />
|
||||
|
||||
<div class="spacer" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const VPCarbonAds = __CARBON__
|
||||
const VPCarbonAds = __ALGOLIA__
|
||||
? defineAsyncComponent(() => import('./VPCarbonAds.vue'))
|
||||
: () => null
|
||||
</script>
|
||||
|
|
|
@ -1,33 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import { computed, inject, ref, type Ref } from 'vue'
|
||||
import {
|
||||
getHeaders,
|
||||
useActiveAnchor,
|
||||
type MenuItem
|
||||
} from '../composables/outline.ts'
|
||||
import VPDocAsideOutlineItem from './VPDocAsideOutlineItem.vue'
|
||||
resolveHeaders,
|
||||
useOutline,
|
||||
useActiveAnchor
|
||||
} from '../composables/outline.js'
|
||||
|
||||
const { frontmatter, theme } = useData()
|
||||
const { page, frontmatter, theme } = useData()
|
||||
|
||||
const pageOutline = computed<DefaultTheme.Config['outline']>(
|
||||
() => frontmatter.value.outline ?? theme.value.outline
|
||||
)
|
||||
|
||||
const onContentUpdated = inject('onContentUpdated') as Ref<() => void>
|
||||
onContentUpdated.value = () => {
|
||||
headers.value = getHeaders(pageOutline.value)
|
||||
}
|
||||
|
||||
const headers = ref<MenuItem[]>([])
|
||||
const hasOutline = computed(() => headers.value.length > 0)
|
||||
const { hasOutline } = useOutline()
|
||||
|
||||
const container = ref()
|
||||
const marker = ref()
|
||||
|
||||
useActiveAnchor(container, marker)
|
||||
|
||||
const resolvedHeaders = computed(() => {
|
||||
return resolveHeaders(page.value.headers)
|
||||
})
|
||||
|
||||
function handleClick({ target: el }: Event) {
|
||||
const id = '#' + (el as HTMLAnchorElement).href!.split('#')[1]
|
||||
const heading = document.querySelector<HTMLAnchorElement>(
|
||||
|
@ -50,7 +42,24 @@ function handleClick({ target: el }: Event) {
|
|||
<span class="visually-hidden" id="doc-outline-aria-label">
|
||||
Table of Contents for current page
|
||||
</span>
|
||||
<VPDocAsideOutlineItem :headers="headers" :root="true" :onClick="handleClick" />
|
||||
|
||||
<ul class="root">
|
||||
<li
|
||||
v-for="{ text, link, children, hidden } in resolvedHeaders"
|
||||
v-show="!hidden"
|
||||
>
|
||||
<a class="outline-link" :href="link" @click="handleClick">
|
||||
{{ text }}
|
||||
</a>
|
||||
<ul v-if="children && frontmatter.outline === 'deep'">
|
||||
<li v-for="{ text, link, hidden } in children" v-show="!hidden">
|
||||
<a class="outline-link nested" :href="link" @click="handleClick">
|
||||
{{ text }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -91,4 +100,29 @@ function handleClick({ target: el }: Event) {
|
|||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.outline-link {
|
||||
display: block;
|
||||
line-height: 28px;
|
||||
color: var(--vp-c-text-2);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
|
||||
.outline-link:hover,
|
||||
.outline-link.active {
|
||||
color: var(--vp-c-text-1);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.outline-link.nested {
|
||||
padding-left: 13px;
|
||||
}
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import type { MenuItem } from '../composables/outline.ts'
|
||||
|
||||
defineProps<{
|
||||
headers: MenuItem[]
|
||||
onClick: (e: MouseEvent) => void
|
||||
root?: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul :class="root ? 'root' : 'nested'">
|
||||
<li v-for="{ children, link, title } in headers">
|
||||
<a class="outline-link" :href="link" @click="onClick">{{ title }}</a>
|
||||
<template v-if="children?.length">
|
||||
<VPDocAsideOutlineItem :headers="children" :onClick="onClick" />
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.root {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.nested {
|
||||
padding-left: 13px;
|
||||
}
|
||||
|
||||
.outline-link {
|
||||
display: block;
|
||||
line-height: 28px;
|
||||
color: var(--vp-c-text-2);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: color 0.5s;
|
||||
}
|
||||
|
||||
.outline-link:hover,
|
||||
.outline-link.active {
|
||||
color: var(--vp-c-text-1);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.outline-link.nested {
|
||||
padding-left: 13px;
|
||||
}
|
||||
</style>
|
|
@ -9,8 +9,8 @@ const { hasSidebar } = useSidebar()
|
|||
<template>
|
||||
<footer v-if="theme.footer" class="VPFooter" :class="{ 'has-sidebar': hasSidebar }">
|
||||
<div class="container">
|
||||
<p v-if="theme.footer.message" class="message" v-html="theme.footer.message"></p>
|
||||
<p v-if="theme.footer.copyright" class="copyright" v-html="theme.footer.copyright"></p>
|
||||
<p class="message" v-html="theme.footer.message"></p>
|
||||
<p class="copyright" v-html="theme.footer.copyright"></p>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
|
|
@ -20,7 +20,6 @@ export default {
|
|||
class="VPImage"
|
||||
v-bind="typeof image === 'string' ? $attrs : { ...image, ...$attrs }"
|
||||
:src="withBase(typeof image === 'string' ? image : image.src)"
|
||||
:alt="typeof image === 'string' ? '' : (image.alt || '')"
|
||||
/>
|
||||
<template v-else>
|
||||
<VPImage class="dark" :image="image.dark" v-bind="$attrs" />
|
||||
|
|
|
@ -102,7 +102,7 @@ const { hasSidebar } = useSidebar()
|
|||
}
|
||||
|
||||
.container :deep(*) {
|
||||
pointer-events: auto;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
|
|
@ -4,15 +4,12 @@ import VPFlyout from './VPFlyout.vue'
|
|||
import VPMenuLink from './VPMenuLink.vue'
|
||||
import VPSwitchAppearance from './VPSwitchAppearance.vue'
|
||||
import VPSocialLinks from './VPSocialLinks.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const { site, theme } = useData()
|
||||
|
||||
const hasExtraContent = computed(() => theme.value.localeLinks || site.value.appearance || theme.value.socialLinks)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPFlyout v-if="hasExtraContent" class="VPNavBarExtra" label="extra navigation">
|
||||
<VPFlyout class="VPNavBarExtra" label="extra navigation">
|
||||
<div v-if="theme.localeLinks" class="group">
|
||||
<p class="trans-title">{{ theme.localeLinks.text }}</p>
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@ function load() {
|
|||
|
||||
.DocSearch-Button .DocSearch-Button-Key:first-child {
|
||||
font-size: 1px;
|
||||
letter-spacing: -12px;
|
||||
letter-spacing: -1px;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ function unlockBodyScroll() {
|
|||
background-color: var(--vp-c-bg);
|
||||
overflow-y: auto;
|
||||
transition: background-color 0.5s;
|
||||
pointer-events: auto;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.VPNavScreen.fade-enter-active,
|
||||
|
|
|
@ -88,10 +88,6 @@ function toggle() {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.VPSidebarGroup.collapsible .title {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.title:hover .action {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
|
|
@ -5,35 +5,29 @@ import { useData } from 'vitepress'
|
|||
import { isActive } from '../support/utils.js'
|
||||
import VPLink from './VPLink.vue'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{ item: DefaultTheme.SidebarItem; depth?: number }>(),
|
||||
{ depth: 1 }
|
||||
)
|
||||
withDefaults(defineProps<{ item: DefaultTheme.SidebarItem; depth?: number }>(), { depth: 1 })
|
||||
|
||||
const { page, frontmatter } = useData()
|
||||
const maxDepth = computed<number>(
|
||||
() => frontmatter.value.sidebarDepth || Infinity
|
||||
)
|
||||
const maxDepth = computed<number>(() => frontmatter.value.sidebarDepth || Infinity)
|
||||
const closeSideBar = inject('close-sidebar') as () => void
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPLink
|
||||
class="link"
|
||||
:class="{ active: isActive(page.relativePath, item.link) }"
|
||||
:style="{ paddingLeft: 16 * (depth - 1) + 'px' }"
|
||||
:class="{ active: isActive(page.relativePath, item.link), offset: depth > 1 }"
|
||||
:href="item.link"
|
||||
@click="closeSideBar"
|
||||
>
|
||||
<span class="link-text" :class="{ light: depth > 1 }">{{ item.text }}</span>
|
||||
<template
|
||||
v-if="'items' in item && depth < maxDepth"
|
||||
v-for="child in item.items"
|
||||
:key="child.link"
|
||||
>
|
||||
<VPSidebarLink :item="child" :depth="depth + 1" />
|
||||
</template>
|
||||
</VPLink>
|
||||
<template
|
||||
v-if="'items' in item && depth < maxDepth"
|
||||
v-for="child in item.items"
|
||||
:key="child.link"
|
||||
>
|
||||
<VPSidebarLink :item="child" :depth="depth + 1" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
@ -44,6 +38,10 @@ const closeSideBar = inject('close-sidebar') as () => void
|
|||
transition: color 0.5s;
|
||||
}
|
||||
|
||||
.link.offset {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { APPEARANCE_KEY } from '../shared/shared.ts'
|
||||
import VPSwitch from './VPSwitch.vue'
|
||||
import VPIconSun from './icons/VPIconSun.vue'
|
||||
import VPIconMoon from './icons/VPIconMoon.vue'
|
||||
|
||||
const checked = ref(false)
|
||||
const toggle = typeof localStorage !== 'undefined' ? useAppearance() : () => {}
|
||||
|
||||
onMounted(() => {
|
||||
checked.value = document.documentElement.classList.contains('dark')
|
||||
})
|
||||
|
||||
function useAppearance() {
|
||||
const query = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const classList = document.documentElement.classList
|
||||
|
@ -39,7 +33,6 @@ function useAppearance() {
|
|||
}
|
||||
|
||||
function setClass(dark: boolean): void {
|
||||
checked.value = dark
|
||||
classList[dark ? 'add' : 'remove']('dark')
|
||||
}
|
||||
|
||||
|
@ -51,7 +44,6 @@ function useAppearance() {
|
|||
<VPSwitch
|
||||
class="VPSwitchAppearance"
|
||||
aria-label="toggle dark mode"
|
||||
:aria-checked="checked"
|
||||
@click="toggle"
|
||||
>
|
||||
<VPIconSun class="sun" />
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import { nextTick, watch } from 'vue'
|
||||
import { inBrowser, useData } from 'vitepress'
|
||||
|
||||
export function useCopyCode() {
|
||||
const { page } = useData()
|
||||
|
||||
if (inBrowser)
|
||||
watch(
|
||||
() => page.value.relativePath,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
document
|
||||
.querySelectorAll<HTMLSpanElement>(
|
||||
'.vp-doc div[class*="language-"] > button.copy'
|
||||
)
|
||||
.forEach(handleElement)
|
||||
})
|
||||
},
|
||||
{ immediate: true, flush: 'post' }
|
||||
)
|
||||
}
|
||||
|
||||
async function copyToClipboard(text: string) {
|
||||
try {
|
||||
return navigator.clipboard.writeText(text)
|
||||
} catch {
|
||||
const element = document.createElement('textarea')
|
||||
const previouslyFocusedElement = document.activeElement
|
||||
|
||||
element.value = text
|
||||
|
||||
// Prevent keyboard from showing on mobile
|
||||
element.setAttribute('readonly', '')
|
||||
|
||||
element.style.contain = 'strict'
|
||||
element.style.position = 'absolute'
|
||||
element.style.left = '-9999px'
|
||||
element.style.fontSize = '12pt' // Prevent zooming on iOS
|
||||
|
||||
const selection = document.getSelection()
|
||||
const originalRange = selection
|
||||
? selection.rangeCount > 0 && selection.getRangeAt(0)
|
||||
: null
|
||||
|
||||
document.body.appendChild(element)
|
||||
element.select()
|
||||
|
||||
// Explicit selection workaround for iOS
|
||||
element.selectionStart = 0
|
||||
element.selectionEnd = text.length
|
||||
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(element)
|
||||
|
||||
if (originalRange) {
|
||||
selection!.removeAllRanges() // originalRange can't be truthy when selection is falsy
|
||||
selection!.addRange(originalRange)
|
||||
}
|
||||
|
||||
// Get the focus back on the previously focused element, if any
|
||||
if (previouslyFocusedElement) {
|
||||
;(previouslyFocusedElement as HTMLElement).focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleElement(el: HTMLElement) {
|
||||
el.onclick = () => {
|
||||
const parent = el.parentElement
|
||||
const sibling = el.nextElementSibling
|
||||
?.nextElementSibling as HTMLPreElement | null
|
||||
if (!parent || !sibling) {
|
||||
return
|
||||
}
|
||||
|
||||
const isShell = /language-(shellscript|shell|bash|sh|zsh)/.test(
|
||||
parent.classList.toString()
|
||||
)
|
||||
|
||||
let { innerText: text = '' } = sibling
|
||||
|
||||
if (isShell) {
|
||||
text = text.replace(/^ *(\$|>) /gm, '')
|
||||
}
|
||||
|
||||
copyToClipboard(text).then(() => {
|
||||
el.classList.add('copied')
|
||||
setTimeout(() => {
|
||||
el.classList.remove('copied')
|
||||
el.blur()
|
||||
}, 2000)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,87 +1,64 @@
|
|||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import { onMounted, onUnmounted, onUpdated, type Ref } from 'vue'
|
||||
import type { Header } from '../../shared.js'
|
||||
import { Ref, computed, onMounted, onUpdated, onUnmounted } from 'vue'
|
||||
import { Header, useData } from 'vitepress'
|
||||
import { useAside } from '../composables/aside.js'
|
||||
import { throttleAndDebounce } from '../support/utils.js'
|
||||
|
||||
interface HeaderWithChildren extends Header {
|
||||
children?: Header[]
|
||||
hidden?: boolean
|
||||
}
|
||||
|
||||
interface MenuItemWithLinkAndChildren {
|
||||
text: string
|
||||
link: string
|
||||
children?: MenuItemWithLinkAndChildren[]
|
||||
hidden?: boolean
|
||||
}
|
||||
|
||||
// magic number to avoid repeated retrieval
|
||||
const PAGE_OFFSET = 71
|
||||
const PAGE_OFFSET = 56
|
||||
|
||||
export type MenuItem = Omit<Header, 'slug' | 'children'> & {
|
||||
children?: MenuItem[]
|
||||
}
|
||||
export function useOutline() {
|
||||
const { page } = useData()
|
||||
|
||||
export function getHeaders(pageOutline: DefaultTheme.Config['outline']) {
|
||||
if (pageOutline === false) return []
|
||||
let updatedHeaders: MenuItem[] = []
|
||||
document
|
||||
.querySelectorAll<HTMLHeadingElement>('h2, h3, h4, h5, h6')
|
||||
.forEach((el) => {
|
||||
if (el.textContent && el.id) {
|
||||
updatedHeaders.push({
|
||||
level: Number(el.tagName[1]),
|
||||
title: el.innerText.replace(/\s+#\s*$/, ''),
|
||||
link: `#${el.id}`
|
||||
})
|
||||
}
|
||||
})
|
||||
return resolveHeaders(updatedHeaders, pageOutline)
|
||||
}
|
||||
|
||||
export function resolveHeaders(
|
||||
headers: MenuItem[],
|
||||
levelsRange: Exclude<DefaultTheme.Config['outline'], false> = 2
|
||||
) {
|
||||
const levels: [number, number] =
|
||||
typeof levelsRange === 'number'
|
||||
? [levelsRange, levelsRange]
|
||||
: levelsRange === 'deep'
|
||||
? [2, 6]
|
||||
: levelsRange
|
||||
|
||||
return groupHeaders(headers, levels)
|
||||
}
|
||||
|
||||
function groupHeaders(headers: MenuItem[], levelsRange: [number, number]) {
|
||||
const result: MenuItem[] = []
|
||||
|
||||
headers = headers.map((h) => ({ ...h }))
|
||||
headers.forEach((h, index) => {
|
||||
if (h.level >= levelsRange[0] && h.level <= levelsRange[1]) {
|
||||
if (addToParent(index, headers, levelsRange)) {
|
||||
result.push(h)
|
||||
}
|
||||
}
|
||||
const hasOutline = computed(() => {
|
||||
return page.value.headers.length > 0
|
||||
})
|
||||
|
||||
return result
|
||||
return {
|
||||
hasOutline
|
||||
}
|
||||
}
|
||||
|
||||
function addToParent(
|
||||
currIndex: number,
|
||||
headers: MenuItem[],
|
||||
levelsRange: [number, number]
|
||||
) {
|
||||
if (currIndex === 0) {
|
||||
return true
|
||||
}
|
||||
export function resolveHeaders(headers: Header[]) {
|
||||
return mapHeaders(groupHeaders(headers))
|
||||
}
|
||||
|
||||
const currentHeader = headers[currIndex]
|
||||
for (let index = currIndex - 1; index >= 0; index--) {
|
||||
const header = headers[index]
|
||||
function groupHeaders(headers: Header[]): HeaderWithChildren[] {
|
||||
headers = headers.map((h) => Object.assign({}, h))
|
||||
|
||||
if (
|
||||
header.level < currentHeader.level &&
|
||||
header.level >= levelsRange[0] &&
|
||||
header.level <= levelsRange[1]
|
||||
) {
|
||||
if (header.children == null) header.children = []
|
||||
header.children.push(currentHeader)
|
||||
return false
|
||||
let lastH2: HeaderWithChildren | undefined
|
||||
|
||||
for (const h of headers) {
|
||||
if (h.level === 2) {
|
||||
lastH2 = h
|
||||
} else if (lastH2 && h.level <= 3) {
|
||||
;(lastH2.children || (lastH2.children = [])).push(h)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return headers.filter((h) => h.level === 2)
|
||||
}
|
||||
|
||||
function mapHeaders(
|
||||
headers: HeaderWithChildren[]
|
||||
): MenuItemWithLinkAndChildren[] {
|
||||
return headers.map((header) => ({
|
||||
text: header.title,
|
||||
link: `#${header.slug}`,
|
||||
children: header.children ? mapHeaders(header.children) : undefined,
|
||||
hidden: header.hidden
|
||||
}))
|
||||
}
|
||||
|
||||
export function useActiveAnchor(
|
||||
|
@ -174,7 +151,7 @@ export function useActiveAnchor(
|
|||
}
|
||||
|
||||
function getAnchorTop(anchor: HTMLAnchorElement): number {
|
||||
return anchor.parentElement!.offsetTop - PAGE_OFFSET
|
||||
return anchor.parentElement!.offsetTop - PAGE_OFFSET - 15
|
||||
}
|
||||
|
||||
function isAnchorActive(
|
||||
|
|
|
@ -23,9 +23,13 @@ export function useSidebar() {
|
|||
})
|
||||
|
||||
const hasAside = computed(() => {
|
||||
return (
|
||||
frontmatter.value.layout !== 'home' && frontmatter.value.aside !== false
|
||||
if (
|
||||
frontmatter.value.layout !== 'home' &&
|
||||
frontmatter.value.aside === false
|
||||
)
|
||||
return false
|
||||
|
||||
return hasSidebar.value
|
||||
})
|
||||
|
||||
function open() {
|
||||
|
|
|
@ -7,15 +7,15 @@ export interface GridSetting {
|
|||
|
||||
export type GridSize = 'xmini' | 'mini' | 'small' | 'medium' | 'big'
|
||||
|
||||
export interface UseSponsorsGridOptions {
|
||||
export interface UseSponsorsGridOprions {
|
||||
el: Ref<HTMLElement | null>
|
||||
size?: GridSize
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines grid configuration for each sponsor size in tuple.
|
||||
* Defines grid configuration for each sponsor size in touple.
|
||||
*
|
||||
* [Screen width, Column size]
|
||||
* [Screen widh, Column size]
|
||||
*
|
||||
* It sets grid size on matching screen size. For example, `[768, 5]` will
|
||||
* set 5 columns when screen size is bigger or equal to 768px.
|
||||
|
@ -49,7 +49,7 @@ const GridSettings: GridSetting = {
|
|||
export function useSponsorsGrid({
|
||||
el,
|
||||
size = 'medium'
|
||||
}: UseSponsorsGridOptions) {
|
||||
}: UseSponsorsGridOprions) {
|
||||
const onResize = throttleAndDebounce(manage, 100)
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
@ -147,8 +147,8 @@ button {
|
|||
background-image: none;
|
||||
}
|
||||
|
||||
button:enabled,
|
||||
[role='button']:enabled {
|
||||
button,
|
||||
[role='button'] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@ -213,13 +213,3 @@ fieldset {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
|
|
@ -145,12 +145,10 @@
|
|||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-font-family-base: 'Inter var experimental', 'Inter var', ui-sans-serif,
|
||||
system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Helvetica, Arial, 'Noto Sans', sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--vp-font-family-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco,
|
||||
Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||||
--vp-font-family-base: 'Inter var experimental', 'Inter var', -apple-system,
|
||||
BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
--vp-font-family-mono: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import { ensureStartingSlash } from './utils'
|
||||
import { ensureStartingSlash } from './utils.js'
|
||||
|
||||
/**
|
||||
* Get the `Sidebar` from sidebar option. This method will ensure to get correct
|
||||
|
|
|
@ -13,12 +13,12 @@ export function isExternal(path: string): boolean {
|
|||
}
|
||||
|
||||
export function throttleAndDebounce(fn: () => void, delay: number): () => void {
|
||||
let timeoutId: NodeJS.Timeout
|
||||
let timeout: any
|
||||
let called = false
|
||||
|
||||
return () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
|
||||
if (!called) {
|
||||
|
@ -28,7 +28,7 @@ export function throttleAndDebounce(fn: () => void, delay: number): () => void {
|
|||
called = false
|
||||
}, delay)
|
||||
} else {
|
||||
timeoutId = setTimeout(fn, delay)
|
||||
timeout = setTimeout(fn, delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue