mirror of https://github.com/YunYouJun/valaxy
docs: add how to add default frontmatter
This commit is contained in:
parent
92645ba3d2
commit
e717b89ee7
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: Time Warning Test
|
||||
date: 2020-10-01
|
||||
categories:
|
||||
- test
|
||||
---
|
||||
|
||||
This is a test page for time warning.
|
|
@ -92,3 +92,7 @@ features:
|
|||
|
||||
tooltip:
|
||||
edit_this_page: Edit this page on GitHub
|
||||
|
||||
toc:
|
||||
base-config: Base Config
|
||||
extend-config: Extend Config
|
||||
|
|
|
@ -92,3 +92,7 @@ features:
|
|||
|
||||
tooltip:
|
||||
edit_this_page: 在 GitHub 上编辑此页
|
||||
|
||||
toc:
|
||||
base-config: 基础配置
|
||||
extend-config: 扩展配置
|
||||
|
|
|
@ -16,9 +16,9 @@ pnpm add [valaxy-addon-package1] [valaxy-addon-package2]
|
|||
|
||||
```ts
|
||||
// valaxy.config.ts
|
||||
import { defineConfig } from 'valaxy'
|
||||
import { defineValaxyConfig } from 'valaxy'
|
||||
|
||||
export default defineConfig({
|
||||
export default defineValaxyConfig({
|
||||
addons: [
|
||||
'valaxy-addon-package1',
|
||||
// pass addon options
|
||||
|
@ -26,3 +26,25 @@ export default defineConfig({
|
|||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Addon With Options
|
||||
|
||||
譬如开启 Waline 评论:
|
||||
|
||||
```ts
|
||||
import { defineValaxyConfig } from 'valaxy'
|
||||
import { addonWaline } from 'valaxy-addon-waline'
|
||||
|
||||
export default defineValaxyConfig({
|
||||
// 启用评论
|
||||
comment: {
|
||||
enable: true
|
||||
},
|
||||
// 设置 valaxy-addon-waline 配置项
|
||||
addons: [
|
||||
addonWaline({
|
||||
serverURL: 'https://your-waline-url',
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
---
|
||||
title: Extend Config
|
||||
title_zh-CN: 扩展配置
|
||||
categories:
|
||||
- config
|
||||
---
|
||||
|
||||
> [packages/valaxy/node/type.ts](https://github.com/YunYouJun/valaxy/blob/main/packages/valaxy/node/types.ts)
|
||||
|
||||
### Unocss Presets
|
||||
|
||||
```ts
|
||||
// types
|
||||
export interface ValaxyExtendConfig {
|
||||
/**
|
||||
* Markdown Feature
|
||||
*/
|
||||
features: {
|
||||
/**
|
||||
* enable katex for global
|
||||
*/
|
||||
katex: boolean
|
||||
}
|
||||
|
||||
vite?: ViteUserConfig
|
||||
vue?: Parameters<typeof Vue>[0]
|
||||
components?: Parameters<typeof Components>[0]
|
||||
unocss?: UnoCSSConfig
|
||||
/**
|
||||
* unocss presets
|
||||
*/
|
||||
unocssPresets?: {
|
||||
uno?: Parameters<typeof presetUno>[0]
|
||||
attributify?: Parameters<typeof presetAttributify>[0]
|
||||
icons?: Parameters<typeof presetIcons>[0]
|
||||
typography?: Parameters<typeof presetTypography>[0]
|
||||
}
|
||||
pages?: Parameters<typeof Pages>[0]
|
||||
/**
|
||||
* for markdown
|
||||
*/
|
||||
markdown?: MarkdownOptions
|
||||
extendMd?: (ctx: {
|
||||
route: {
|
||||
meta: { frontmatter: Record<string, any>; layout?: string } & object
|
||||
path: string
|
||||
component: string
|
||||
}
|
||||
data: Readonly<Record<string, any>>
|
||||
content: string
|
||||
excerpt?: string
|
||||
path: string
|
||||
}) => void
|
||||
addons?: ValaxyAddons
|
||||
}
|
||||
```
|
||||
|
||||
::: zh-CN
|
||||
所以,你可以像这样使用:
|
||||
:::
|
||||
|
||||
::: en
|
||||
So you can use it like this:
|
||||
:::
|
||||
|
||||
```ts
|
||||
// valaxy.config.ts
|
||||
import { defineValaxyConfig } from 'valaxy'
|
||||
import type { ThemeConfig } from 'valaxy-theme-yun'
|
||||
import { addonComponents } from 'valaxy-addon-components'
|
||||
import Inspect from 'vite-plugin-inspect'
|
||||
|
||||
const safelist = [
|
||||
'i-ri-home-line',
|
||||
]
|
||||
|
||||
export default defineValaxyConfig<ThemeConfig>({
|
||||
// site config see site.config.ts or write in siteConfig
|
||||
siteConfig: {},
|
||||
|
||||
theme: 'yun',
|
||||
themeConfig: {
|
||||
banner: {
|
||||
enable: true,
|
||||
title: '云游君的小站',
|
||||
},
|
||||
},
|
||||
|
||||
vite: {
|
||||
// https://github.com/antfu/vite-plugin-inspect
|
||||
// Visit http://localhost:3333/__inspect/ to see the inspector
|
||||
plugins: [Inspect()],
|
||||
},
|
||||
|
||||
unocss: {
|
||||
safelist,
|
||||
},
|
||||
|
||||
addons: [
|
||||
addonComponents()
|
||||
],
|
||||
})
|
||||
```
|
|
@ -4,7 +4,6 @@ title_zh-CN: 基础配置
|
|||
categories:
|
||||
- config
|
||||
end: false
|
||||
top: 10
|
||||
---
|
||||
|
||||
## 配置说明 {lang="zh-CN"}
|
||||
|
@ -14,12 +13,12 @@ top: 10
|
|||
::: zh-CN
|
||||
为了便于配置,Valaxy 将配置分为了三种。
|
||||
|
||||
`valaxy.config.ts` 是配置的主入口。
|
||||
`valaxy.config.ts` 是配置的主入口,它包含了一下配置。
|
||||
|
||||
- `siteConfig`: 站点**信息**配置,这部分内容面向站点展示且在任何主题也是通用的格式
|
||||
- `themeConfig`: 主题配置,这部分内容仅在特定主题生效
|
||||
- `runtimeConfig`: 运行时的配置(由 Valaxy 自动生成),用户无需配置
|
||||
- 其他 Valaxy 通用配置内容(如需要在 Node 端处理的配置)
|
||||
- 其他 Valaxy 通用配置内容(如需要在 Node 端处理的配置 `unocss`/`addons`)
|
||||
|
||||
譬如:
|
||||
:::
|
||||
|
@ -188,6 +187,28 @@ export default defineSiteConfig({
|
|||
|
||||
:::
|
||||
|
||||
### Default Frontmatter
|
||||
|
||||
为所有文章设置默认的 Frontmatter。
|
||||
|
||||
譬如:
|
||||
|
||||
> 设置 `time_warning: false`,则所有文章都不会显示阅读时间警告。
|
||||
|
||||
```ts {8-10}
|
||||
// site.config.ts
|
||||
import { defineSiteConfig } from 'valaxy'
|
||||
|
||||
export default defineSiteConfig({
|
||||
/**
|
||||
* 默认 Frontmatter
|
||||
*/
|
||||
frontmatter: {
|
||||
time_warning: false,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 社交图标 {lang="zh-CN"}
|
||||
|
||||
### Social Icons {lang="en"}
|
||||
|
@ -400,104 +421,3 @@ export default defineSiteConfig({
|
|||
::: en
|
||||
Please refer to [Using Themes](/themes/use) and the theme you are using to configure it.
|
||||
:::
|
||||
|
||||
## Valaxy 扩展配置 {lang="zh-CN"}
|
||||
|
||||
## Valaxy Extended Configuration {lang="zh-CN"}
|
||||
|
||||
> [packages/valaxy/node/type.ts](https://github.com/YunYouJun/valaxy/blob/main/packages/valaxy/node/types.ts)
|
||||
|
||||
### Unocss Presets
|
||||
|
||||
```ts
|
||||
// types
|
||||
export interface ValaxyExtendConfig {
|
||||
/**
|
||||
* Markdown Feature
|
||||
*/
|
||||
features: {
|
||||
/**
|
||||
* enable katex for global
|
||||
*/
|
||||
katex: boolean
|
||||
}
|
||||
|
||||
vite?: ViteUserConfig
|
||||
vue?: Parameters<typeof Vue>[0]
|
||||
components?: Parameters<typeof Components>[0]
|
||||
unocss?: UnoCSSConfig
|
||||
/**
|
||||
* unocss presets
|
||||
*/
|
||||
unocssPresets?: {
|
||||
uno?: Parameters<typeof presetUno>[0]
|
||||
attributify?: Parameters<typeof presetAttributify>[0]
|
||||
icons?: Parameters<typeof presetIcons>[0]
|
||||
typography?: Parameters<typeof presetTypography>[0]
|
||||
}
|
||||
pages?: Parameters<typeof Pages>[0]
|
||||
/**
|
||||
* for markdown
|
||||
*/
|
||||
markdown?: MarkdownOptions
|
||||
extendMd?: (ctx: {
|
||||
route: {
|
||||
meta: { frontmatter: Record<string, any>; layout?: string } & object
|
||||
path: string
|
||||
component: string
|
||||
}
|
||||
data: Readonly<Record<string, any>>
|
||||
content: string
|
||||
excerpt?: string
|
||||
path: string
|
||||
}) => void
|
||||
addons?: ValaxyAddons
|
||||
}
|
||||
```
|
||||
|
||||
::: zh-CN
|
||||
所以,你可以像这样使用:
|
||||
:::
|
||||
|
||||
::: en
|
||||
So you can use it like this:
|
||||
:::
|
||||
|
||||
```ts
|
||||
// valaxy.config.ts
|
||||
import { defineValaxyConfig } from 'valaxy'
|
||||
import type { ThemeConfig } from 'valaxy-theme-yun'
|
||||
import { addonComponents } from 'valaxy-addon-components'
|
||||
import Inspect from 'vite-plugin-inspect'
|
||||
|
||||
const safelist = [
|
||||
'i-ri-home-line',
|
||||
]
|
||||
|
||||
export default defineValaxyConfig<ThemeConfig>({
|
||||
// site config see site.config.ts or write in siteConfig
|
||||
siteConfig: {},
|
||||
|
||||
theme: 'yun',
|
||||
themeConfig: {
|
||||
banner: {
|
||||
enable: true,
|
||||
title: '云游君的小站',
|
||||
},
|
||||
},
|
||||
|
||||
vite: {
|
||||
// https://github.com/antfu/vite-plugin-inspect
|
||||
// Visit http://localhost:3333/__inspect/ to see the inspector
|
||||
plugins: [Inspect()],
|
||||
},
|
||||
|
||||
unocss: {
|
||||
safelist,
|
||||
},
|
||||
|
||||
addons: [
|
||||
addonComponents()
|
||||
],
|
||||
})
|
||||
```
|
|
@ -216,14 +216,14 @@ npm run dev
|
|||
::: zh-CN
|
||||
博客创建完毕,查看本地 `http://localhost:4859/`,玩的开心!
|
||||
|
||||
- Valaxy 博客通用的配置可参见 [配置](/guide/config) 与 [自定义扩展](/guide/custom/extend)。
|
||||
- Valaxy 博客通用的配置可参见 [配置](/guide/config/) 与 [自定义扩展](/guide/custom/extend)。
|
||||
- Valaxy 主题独有配置请参见对应主题文档。(Valaxy Theme Yun 主题文档编写中……)
|
||||
:::
|
||||
|
||||
::: en
|
||||
See `http://localhost:4859/`, have fun!
|
||||
|
||||
- See [Config](/guide/config) and [Custom Extensions](/guide/custom/extend) for the general configuration for Valaxy blogs.
|
||||
- See [Config](/guide/config/) and [Custom Extensions](/guide/custom/extend) for the general configuration for Valaxy blogs.
|
||||
- For configuring Valaxy themes, please see the documentation for the corresponding themes. (Docs for Valaxy Theme Yun is still work in progress)
|
||||
:::
|
||||
|
||||
|
|
|
@ -44,14 +44,14 @@ The relationship between Hexo blog directory and Valaxy blog directory is shown
|
|||
### Migrate Configurations {lang="en"}
|
||||
|
||||
::: zh-CN
|
||||
参考 [Valaxy 配置文档](/guide/config) 将 Hexo `_config.yml` 配置文件中的内容,迁移至 `valaxy.config.ts` 文件中。
|
||||
参考 [Valaxy 配置文档](/guide/config/) 将 Hexo `_config.yml` 配置文件中的内容,迁移至 `valaxy.config.ts` 文件中。
|
||||
|
||||
> 配置示例:[demo/yun/valaxy.config.ts](https://github.com/YunYouJun/valaxy/blob/main/demo/yun/valaxy.config.ts)、[yunyoujun.github.io/valaxy.config.ts](https://github.com/YunYouJun/yunyoujun.github.io/blob/valaxy/valaxy.config.ts)
|
||||
> `valaxy.config.ts` 提供了完备的类型提示,这意味着你在 VSCode 中可以直接鼠标悬浮查看各参数注释。
|
||||
:::
|
||||
|
||||
::: en
|
||||
Refer to [Valaxy Config](/guide/config) to migrate configurations from Hexo's `_config.yml` to Valaxy's `valaxy.config.ts`.
|
||||
Refer to [Valaxy Config](/guide/config/) to migrate configurations from Hexo's `_config.yml` to Valaxy's `valaxy.config.ts`.
|
||||
|
||||
> Examples of configuration: [demo/yun/valaxy.config.ts](https://github.com/YunYouJun/valaxy/blob/main/demo/yun/valaxy.config.ts), [yunyoujun.github.io/valaxy.config.ts](https://github.com/YunYouJun/yunyoujun.github.io/blob/valaxy/valaxy.config.ts).
|
||||
> `valaxy.config.ts` provides a complete type prompt, which means that you can hover on configuration fields directly to view the comments in VSCode.
|
||||
|
|
|
@ -15,7 +15,7 @@ top: 10
|
|||
- 将你的文章(Markdown 文件)复制至 Valaxy `pages/posts` 目录下。
|
||||
- 将你的自定义页面(非文章的 Markdown/HTML 文件)复制至 Valaxy `pages` 目录下。
|
||||
- 将你的静态资源(图片等)复制至 Valaxy `public` 目录下。
|
||||
- 参考 [配置](/guide/config) 配置你的配置文件 `valaxy.config.ts`/`site.config.ts`。
|
||||
- 参考 [配置](/guide/config/) 配置你的配置文件 `valaxy.config.ts`/`site.config.ts`。
|
||||
|
||||
## 常见问题 {lang="zh-CN"}
|
||||
|
||||
|
|
|
@ -37,7 +37,32 @@ export default defineValaxyConfig<PressTheme.Config>({
|
|||
theme: 'press',
|
||||
themeConfig: {
|
||||
logo: '/favicon.svg',
|
||||
sidebar: ['getting-started', 'guide', 'config', 'migration', 'built-ins', 'third', 'custom', 'examples', 'theme', 'addon', 'dev'],
|
||||
sidebar: [
|
||||
'getting-started',
|
||||
'guide',
|
||||
{
|
||||
text: 'category.config',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{
|
||||
text: 'toc.base-config',
|
||||
link: '/guide/config/',
|
||||
},
|
||||
{
|
||||
text: 'toc.extend-config',
|
||||
link: '/guide/config/extend',
|
||||
},
|
||||
],
|
||||
},
|
||||
'migration',
|
||||
'built-ins',
|
||||
'third',
|
||||
'custom',
|
||||
'examples',
|
||||
'theme',
|
||||
'addon',
|
||||
'dev',
|
||||
],
|
||||
socialLinks: [
|
||||
{ icon: 'i-ri-github-line', link: 'https://github.com/YunYouJun/valaxy' },
|
||||
],
|
||||
|
|
|
@ -32,7 +32,8 @@ function getTitle(post: Post | any) {
|
|||
v-if="category.total"
|
||||
p="t-2"
|
||||
w="full" border="t t-$pr-c-divider-light"
|
||||
class="category-list-item inline-flex items-center justify-between"
|
||||
class="press-sidebar-item category-list-item inline-flex items-center justify-between"
|
||||
text-14px
|
||||
tabindex="0"
|
||||
>
|
||||
<span class="category-name" font="bold" m="l-1" @click="displayCategory ? displayCategory(category.name) : null">
|
||||
|
@ -41,7 +42,9 @@ function getTitle(post: Post | any) {
|
|||
</span>
|
||||
<button
|
||||
tabindex="0" role="button" aria-label="toggle section"
|
||||
class="folder-action inline-flex cursor-pointer" opacity="50" @click="collapsable = !collapsable"
|
||||
class="caret folder-action inline-flex cursor-pointer"
|
||||
text-base
|
||||
@click="collapsable = !collapsable"
|
||||
>
|
||||
<div v-if="collapsable" i-ri-folder-add-line />
|
||||
<div v-else i-ri-folder-reduce-line />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { removeItemFromCategory, useCategory, usePageList, useSidebar } from 'valaxy'
|
||||
import { removeItemFromCategory, useCategories, usePageList, useSidebar } from 'valaxy'
|
||||
import { useThemeConfig } from '../composables'
|
||||
|
||||
defineProps<{
|
||||
|
@ -10,8 +10,9 @@ defineProps<{
|
|||
const pages = usePageList()
|
||||
const themeConfig = useThemeConfig()
|
||||
|
||||
const sidebar = computed(() => themeConfig.value.sidebar)
|
||||
const categories = computed(() => {
|
||||
const cs = useCategory('', pages.value)
|
||||
const cs = useCategories('', pages.value)
|
||||
const cList = cs.value
|
||||
removeItemFromCategory(cList, 'Uncategorized')
|
||||
|
||||
|
@ -25,6 +26,10 @@ const categories = computed(() => {
|
|||
return cList
|
||||
})
|
||||
|
||||
function getCategoryByName(name: string) {
|
||||
return categories.value.children.find(c => c.name === name)
|
||||
}
|
||||
|
||||
const { hasSidebar } = useSidebar()
|
||||
</script>
|
||||
|
||||
|
@ -35,7 +40,22 @@ const { hasSidebar } = useSidebar()
|
|||
@click.stop
|
||||
>
|
||||
<div text="left" m="2">
|
||||
<PressCategories :categories="categories.children" :collapsable="false" />
|
||||
<ul v-for="item in sidebar" :key="item" class="category-list">
|
||||
<template v-if="typeof item === 'string'">
|
||||
<PressCategory
|
||||
v-if="getCategoryByName(item)"
|
||||
:category="getCategoryByName(item)"
|
||||
:collapsable="false"
|
||||
/>
|
||||
</template>
|
||||
<PressSidebarItem
|
||||
v-else
|
||||
p="t-2"
|
||||
border="t t-$pr-c-divider-light"
|
||||
:item="item"
|
||||
:depth="0"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
@ -85,4 +105,54 @@ const { hasSidebar } = useSidebar()
|
|||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.category-list {
|
||||
&:first-child {
|
||||
.category-list-item {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.post-list-item {
|
||||
a {
|
||||
color: var(--va-c-text-light);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: var(--va-c-primary);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--va-c-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.press-sidebar-item {
|
||||
.caret {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: -7px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: var(--vp-c-text-3);
|
||||
cursor: pointer;
|
||||
transition: color 0.25s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&:hover .caret {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
&:hover .caret:hover {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
}
|
||||
|
||||
.category-list+.category-list {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useSidebarControl } from '../composables/sidebar'
|
||||
|
||||
const props = defineProps<{
|
||||
item: DefaultTheme.SidebarItem
|
||||
depth: number
|
||||
}>()
|
||||
|
||||
const {
|
||||
collapsed,
|
||||
collapsible,
|
||||
isLink,
|
||||
isActiveLink,
|
||||
hasActiveLink,
|
||||
hasChildren,
|
||||
toggle,
|
||||
} = useSidebarControl(computed(() => props.item))
|
||||
|
||||
const sectionTag = computed(() => (hasChildren.value ? 'section' : `div`))
|
||||
|
||||
const linkTag = computed(() => (isLink.value ? 'a' : 'div'))
|
||||
|
||||
const textTag = computed(() => {
|
||||
return !hasChildren.value
|
||||
? 'p'
|
||||
: props.depth + 2 === 7
|
||||
? 'p'
|
||||
: `h${props.depth + 2}`
|
||||
})
|
||||
|
||||
const itemRole = computed(() => (isLink.value ? undefined : 'button'))
|
||||
|
||||
const classes = computed(() => [
|
||||
[`level-${props.depth}`],
|
||||
{ collapsible: collapsible.value },
|
||||
{ collapsed: collapsed.value },
|
||||
{ 'is-link': isLink.value },
|
||||
{ 'is-active': isActiveLink.value },
|
||||
{ 'has-active': hasActiveLink.value },
|
||||
])
|
||||
|
||||
function onItemInteraction(e: MouseEvent | Event) {
|
||||
if ('key' in e && e.key !== 'Enter')
|
||||
return
|
||||
|
||||
!props.item.link && toggle()
|
||||
}
|
||||
|
||||
function onCaretClick() {
|
||||
props.item.link && toggle()
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const htmlText = computed(() => t(props.item.text || ''))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="sectionTag"
|
||||
class="VPSidebarItem" :class="classes"
|
||||
>
|
||||
<div
|
||||
v-if="item.text"
|
||||
class="press-sidebar-item item"
|
||||
:role="itemRole"
|
||||
:tabindex="item.items && 0"
|
||||
v-on="
|
||||
item.items
|
||||
? { click: onItemInteraction, keydown: onItemInteraction }
|
||||
: {}
|
||||
"
|
||||
>
|
||||
<div class="indicator" />
|
||||
|
||||
<AppLink
|
||||
v-if="item.link"
|
||||
:tag="linkTag"
|
||||
class="link"
|
||||
:href="item.link"
|
||||
:rel="item.rel"
|
||||
:target="item.target"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
|
||||
<component :is="textTag" class="text ml-1" v-html="htmlText" />
|
||||
</AppLink>
|
||||
<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
|
||||
<component :is="textTag" v-else class="text ml-1" v-html="htmlText" />
|
||||
|
||||
<button
|
||||
v-if="item.collapsed != null"
|
||||
tabindex="0" role="button" aria-label="toggle section"
|
||||
class="caret folder-action inline-flex cursor-pointer"
|
||||
@click="onCaretClick" @keydown.enter="onCaretClick"
|
||||
>
|
||||
<div v-if="collapsed" i-ri-folder-add-line />
|
||||
<div v-else i-ri-folder-reduce-line />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="item.items && item.items.length" class="items">
|
||||
<template v-if="depth < 5">
|
||||
<PressSidebarItem
|
||||
v-for="i in item.items"
|
||||
:key="i.text"
|
||||
:item="i"
|
||||
:depth="depth + 1"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.VPSidebarItem.collapsible > .item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
left: -17px;
|
||||
width: 2px;
|
||||
border-radius: 2px;
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-2.is-active > .item > .indicator,
|
||||
.VPSidebarItem.level-3.is-active > .item > .indicator,
|
||||
.VPSidebarItem.level-4.is-active > .item > .indicator,
|
||||
.VPSidebarItem.level-5.is-active > .item > .indicator {
|
||||
background-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
padding: 4px 0;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-0 .text {
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-1 .text,
|
||||
.VPSidebarItem.level-2 .text,
|
||||
.VPSidebarItem.level-3 .text,
|
||||
.VPSidebarItem.level-4 .text,
|
||||
.VPSidebarItem.level-5 .text {
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-0.is-link > .item > .link:hover .text,
|
||||
.VPSidebarItem.level-1.is-link > .item > .link:hover .text,
|
||||
.VPSidebarItem.level-2.is-link > .item > .link:hover .text,
|
||||
.VPSidebarItem.level-3.is-link > .item > .link:hover .text,
|
||||
.VPSidebarItem.level-4.is-link > .item > .link:hover .text,
|
||||
.VPSidebarItem.level-5.is-link > .item > .link:hover .text {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-0.has-active > .item > .text,
|
||||
.VPSidebarItem.level-1.has-active > .item > .text,
|
||||
.VPSidebarItem.level-2.has-active > .item > .text,
|
||||
.VPSidebarItem.level-3.has-active > .item > .text,
|
||||
.VPSidebarItem.level-4.has-active > .item > .text,
|
||||
.VPSidebarItem.level-5.has-active > .item > .text,
|
||||
.VPSidebarItem.level-0.has-active > .item > .link > .text,
|
||||
.VPSidebarItem.level-1.has-active > .item > .link > .text,
|
||||
.VPSidebarItem.level-2.has-active > .item > .link > .text,
|
||||
.VPSidebarItem.level-3.has-active > .item > .link > .text,
|
||||
.VPSidebarItem.level-4.has-active > .item > .link > .text,
|
||||
.VPSidebarItem.level-5.has-active > .item > .link > .text {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-0.is-active > .item .link > .text,
|
||||
.VPSidebarItem.level-1.is-active > .item .link > .text,
|
||||
.VPSidebarItem.level-2.is-active > .item .link > .text,
|
||||
.VPSidebarItem.level-3.is-active > .item .link > .text,
|
||||
.VPSidebarItem.level-4.is-active > .item .link > .text,
|
||||
.VPSidebarItem.level-5.is-active > .item .link > .text {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.VPSidebarItem.level-1 .items,
|
||||
.VPSidebarItem.level-2 .items,
|
||||
.VPSidebarItem.level-3 .items,
|
||||
.VPSidebarItem.level-4 .items,
|
||||
.VPSidebarItem.level-5 .items {
|
||||
border-left: 1px solid var(--vp-c-divider);
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.VPSidebarItem.collapsed .items {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,172 @@
|
|||
import { isClient } from '@vueuse/core'
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import {
|
||||
type ComputedRef,
|
||||
type Ref,
|
||||
computed,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
ref,
|
||||
watch,
|
||||
watchEffect,
|
||||
watchPostEffect,
|
||||
} from 'vue'
|
||||
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import type { PressTheme } from 'valaxy-theme-press'
|
||||
|
||||
export interface SidebarControl {
|
||||
collapsed: Ref<boolean>
|
||||
collapsible: ComputedRef<boolean>
|
||||
isLink: ComputedRef<boolean>
|
||||
isActiveLink: Ref<boolean>
|
||||
hasActiveLink: ComputedRef<boolean>
|
||||
hasChildren: ComputedRef<boolean>
|
||||
toggle(): void
|
||||
}
|
||||
|
||||
export const HASH_RE = /#.*$/
|
||||
export const EXT_RE = /(index)?\.(md|html)$/
|
||||
export function normalize(path: string): string {
|
||||
return decodeURI(path).replace(HASH_RE, '').replace(EXT_RE, '')
|
||||
}
|
||||
|
||||
export function isActive(
|
||||
currentPath: string,
|
||||
matchPath?: string,
|
||||
asRegex: boolean = false,
|
||||
): boolean {
|
||||
if (matchPath === undefined)
|
||||
return false
|
||||
|
||||
currentPath = normalize(`/${currentPath}`)
|
||||
|
||||
if (asRegex)
|
||||
return new RegExp(matchPath).test(currentPath)
|
||||
|
||||
if (normalize(matchPath) !== currentPath)
|
||||
return false
|
||||
|
||||
const hashMatch = matchPath.match(HASH_RE)
|
||||
|
||||
if (hashMatch)
|
||||
return (isClient ? location.hash : '') === hashMatch[0]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* a11y: cache the element that opened the Sidebar (the menu button) then
|
||||
* focus that button again when Menu is closed with Escape key.
|
||||
*/
|
||||
export function useCloseSidebarOnEscape(
|
||||
isOpen: Ref<boolean>,
|
||||
close: () => void,
|
||||
) {
|
||||
let triggerElement: HTMLButtonElement | undefined
|
||||
|
||||
watchEffect(() => {
|
||||
triggerElement = isOpen.value
|
||||
? (document.activeElement as HTMLButtonElement)
|
||||
: undefined
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keyup', onEscape)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keyup', onEscape)
|
||||
})
|
||||
|
||||
function onEscape(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape' && isOpen.value) {
|
||||
close()
|
||||
triggerElement?.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hashRef = ref(isClient ? location.hash : '')
|
||||
if (isClient) {
|
||||
window.addEventListener('hashchange', () => {
|
||||
hashRef.value = location.hash
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given sidebar item contains any active link.
|
||||
*/
|
||||
export function containsActiveLink(
|
||||
path: string,
|
||||
items: PressTheme.SidebarItem | PressTheme.SidebarItem[],
|
||||
): boolean {
|
||||
if (Array.isArray(items))
|
||||
return items.some(item => containsActiveLink(path, item))
|
||||
|
||||
return isActive(path, items.link)
|
||||
? true
|
||||
: items.items
|
||||
? containsActiveLink(path, items.items)
|
||||
: false
|
||||
}
|
||||
|
||||
export function useSidebarControl(
|
||||
item: ComputedRef<DefaultTheme.SidebarItem>,
|
||||
): SidebarControl {
|
||||
const collapsed = ref(false)
|
||||
|
||||
const collapsible = computed(() => {
|
||||
return item.value.collapsed != null
|
||||
})
|
||||
|
||||
const isLink = computed(() => {
|
||||
return !!item.value.link
|
||||
})
|
||||
|
||||
const isActiveLink = ref(false)
|
||||
const route = useRoute()
|
||||
const updateIsActiveLink = () => {
|
||||
isActiveLink.value = route.path === item.value.link
|
||||
}
|
||||
|
||||
watch([route, item, hashRef], updateIsActiveLink)
|
||||
onMounted(updateIsActiveLink)
|
||||
|
||||
const hasActiveLink = computed(() => {
|
||||
if (isActiveLink.value)
|
||||
return true
|
||||
|
||||
return item.value.items
|
||||
? containsActiveLink(route.path, item.value.items)
|
||||
: false
|
||||
})
|
||||
|
||||
const hasChildren = computed(() => {
|
||||
return !!(item.value.items && item.value.items.length)
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
collapsed.value = !!(collapsible.value && item.value.collapsed)
|
||||
})
|
||||
|
||||
watchPostEffect(() => {
|
||||
;(isActiveLink.value || hasActiveLink.value) && (collapsed.value = false)
|
||||
})
|
||||
|
||||
function toggle() {
|
||||
if (collapsible.value)
|
||||
collapsed.value = !collapsed.value
|
||||
}
|
||||
|
||||
return {
|
||||
collapsed,
|
||||
collapsible,
|
||||
isLink,
|
||||
isActiveLink,
|
||||
hasActiveLink,
|
||||
hasChildren,
|
||||
toggle,
|
||||
}
|
||||
}
|
|
@ -30,6 +30,49 @@ export namespace PressTheme {
|
|||
text?: string
|
||||
}
|
||||
|
||||
export type Sidebar = SidebarItem[] | SidebarMulti
|
||||
export interface SidebarMulti {
|
||||
[path: string]: SidebarItem[] | { items: SidebarItem[]; base: string }
|
||||
}
|
||||
export interface SidebarItem {
|
||||
/**
|
||||
* The text label of the item.
|
||||
*/
|
||||
text?: string
|
||||
|
||||
/**
|
||||
* The link of the item.
|
||||
*/
|
||||
link?: string
|
||||
|
||||
/**
|
||||
* The children of the item.
|
||||
*/
|
||||
items?: SidebarItem[]
|
||||
|
||||
/**
|
||||
* If not specified, group is not collapsible.
|
||||
*
|
||||
* If `true`, group is collapsible and collapsed by default
|
||||
*
|
||||
* If `false`, group is collapsible but expanded by default
|
||||
*/
|
||||
collapsed?: boolean
|
||||
|
||||
/**
|
||||
* Base path for the children items.
|
||||
*/
|
||||
base?: string
|
||||
|
||||
/**
|
||||
* Customize text that appears on the footer of previous/next page.
|
||||
*/
|
||||
docFooterText?: string
|
||||
|
||||
rel?: string
|
||||
target?: string
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
logo: string
|
||||
|
||||
|
@ -41,8 +84,8 @@ export namespace PressTheme {
|
|||
primary: string
|
||||
}
|
||||
|
||||
nav: Array<NavItem>
|
||||
sidebar: string[]
|
||||
nav: NavItem[]
|
||||
sidebar?: Sidebar
|
||||
|
||||
editLink: EditLink
|
||||
|
||||
|
|
|
@ -16,19 +16,20 @@ dayjs.extend(relativeTime)
|
|||
const { t } = useI18n()
|
||||
|
||||
/**
|
||||
* when the post is updated more than 30 days ago, show a warning
|
||||
* default 30 days, you can set `time_warning` in frontmatter to change it
|
||||
* when the post is updated more than 180 days ago, show a warning
|
||||
* default 180 days, you can set `time_warning` in frontmatter to change it
|
||||
*/
|
||||
const time_warning = computed(() => {
|
||||
const diff = dayjs().valueOf() - dayjs(props.frontmatter.updated).valueOf()
|
||||
const diff = dayjs().valueOf() - dayjs(props.frontmatter.updated || props.frontmatter.date).valueOf()
|
||||
if (typeof props.frontmatter.time_warning === 'number')
|
||||
return diff > props.frontmatter.time_warning
|
||||
else
|
||||
return props.frontmatter.time_warning || diff > 30 * 24 * 60 * 60 * 1000
|
||||
return props.frontmatter.time_warning
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
{{ frontmatter }}
|
||||
<blockquote v-if="time_warning" op="80">
|
||||
{{ t('post.time_warning', { ago: dayjs(frontmatter.updated).fromNow() }) }}
|
||||
</blockquote>
|
||||
|
|
|
@ -9,5 +9,5 @@ const addon = addonWaline.useAddonWaline()
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<WalineClient w="full" :options="addon.options" />
|
||||
<WalineClient w="full" :options="addon.options || {}" />
|
||||
</template>
|
||||
|
|
|
@ -10,4 +10,4 @@
|
|||
|
||||
如「作者头像」、「站点名称」等属于通用信息,请在 `site.config.ts` 中配置。
|
||||
|
||||
可参见 [Site Config | Valaxy Docs](https://valaxy.site/guide/config#site-config)。
|
||||
可参见 [Site Config | Valaxy Docs](https://valaxy.site/guide/config/#site-config)。
|
||||
|
|
|
@ -49,7 +49,7 @@ post:
|
|||
view_link: 查看链接
|
||||
read_more: 阅读更多
|
||||
cover: 封面
|
||||
time_warning: 本文最后更新于 {ago},文中所描述的信息可能已发生改变。
|
||||
time_warning: 本文最后更新于{ago},文中所描述的信息可能已发生改变。
|
||||
copyright:
|
||||
author: 本文作者
|
||||
link: 本文链接
|
||||
|
|
|
@ -77,6 +77,10 @@ export const defaultSiteConfig: SiteConfig = {
|
|||
enable: false,
|
||||
},
|
||||
|
||||
frontmatter: {
|
||||
time_warning: 180 * 24 * 60 * 60 * 1000,
|
||||
},
|
||||
|
||||
cdn: {
|
||||
prefix: 'https://unpkg.com/',
|
||||
},
|
||||
|
|
|
@ -52,7 +52,7 @@ export function createPagesPlugin(options: ResolvedValaxyOptions) {
|
|||
) {
|
||||
let path: string = route.component
|
||||
|
||||
const defaultFrontmatter = {}
|
||||
const defaultFrontmatter = valaxyConfig.siteConfig.frontmatter || {}
|
||||
if (!route.meta) {
|
||||
route.meta = {
|
||||
frontmatter: defaultFrontmatter,
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"require": "./dist/node/index.cjs"
|
||||
},
|
||||
"./types": "./dist/types/index.d.ts",
|
||||
"./default-theme": "./types/default-theme.d.ts",
|
||||
"./*": "./*"
|
||||
},
|
||||
"main": "dist/node/index.cjs",
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { FuseOptions } from '@vueuse/integrations/useFuse'
|
|||
import type { ILazyLoadOptions } from 'vanilla-lazyload'
|
||||
import type * as DefaultTheme from 'valaxy/default-theme'
|
||||
import type { ValaxyAddon } from '../types'
|
||||
import type { PostFrontMatter } from './posts'
|
||||
import type { FuseListItem } from './node'
|
||||
|
||||
export interface SocialLink {
|
||||
|
@ -157,6 +158,11 @@ export interface SiteConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set post default frontmatter
|
||||
*/
|
||||
frontmatter: Partial<PostFrontMatter>
|
||||
|
||||
/**
|
||||
* comment: waline/...
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue