mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
feat(theme): plume-theme next devloping !
add plugin-auto-frontmatter | add plugin-blog-data | devloping new theme
This commit is contained in:
parent
44a05f1ffe
commit
5de60d4d6e
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,3 +12,5 @@ dist/
|
|||||||
*.log
|
*.log
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
.mind
|
.mind
|
||||||
|
|
||||||
|
packages/theme-back
|
||||||
|
|||||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -24,6 +24,7 @@
|
|||||||
"composables",
|
"composables",
|
||||||
"Docsearch",
|
"Docsearch",
|
||||||
"esbuild",
|
"esbuild",
|
||||||
|
"frontmatter",
|
||||||
"gsap",
|
"gsap",
|
||||||
"iarna",
|
"iarna",
|
||||||
"leancloud",
|
"leancloud",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { themePlume } from '@vuepress-plume/vuepress-theme-plume'
|
import themePlume from '@vuepress-plume/vuepress-theme-plume'
|
||||||
import { viteBundler } from '@vuepress/bundler-vite'
|
import { viteBundler } from '@vuepress/bundler-vite'
|
||||||
import { webpackBundler } from '@vuepress/bundler-webpack'
|
import { webpackBundler } from '@vuepress/bundler-webpack'
|
||||||
import { defineUserConfig } from '@vuepress/cli'
|
import { defineUserConfig } from '@vuepress/cli'
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: BFC 块级格式化上下文
|
title: BFC 块级格式化上下文
|
||||||
createTime: 2018/05/17 12:28:33
|
createTime: 2022-03-26T11:46:50.024Z
|
||||||
permalink: /article/o5g7ggvf
|
permalink: /article/o5g7ggvf
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
tags:
|
tags:
|
||||||
- html
|
- html
|
||||||
type: null
|
type: null
|
||||||
---
|
---
|
||||||
@ -35,4 +35,4 @@ BFC, Block Formating Context。是 W3C CSS2.1规范中的一个概念。 是页
|
|||||||
|
|
||||||
1. 同一个BFC的外边距会发生折叠(合并), 通过将其放在不同的BFC中规避折叠。
|
1. 同一个BFC的外边距会发生折叠(合并), 通过将其放在不同的BFC中规避折叠。
|
||||||
2. BFC可以包含浮动元素,即清除浮动。
|
2. BFC可以包含浮动元素,即清除浮动。
|
||||||
3. BFC可以阻止元素被浮动元素覆盖。
|
3. BFC可以阻止元素被浮动元素覆盖。
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: CSS At-Rules
|
title: CSS At-Rules
|
||||||
createTime: 2018/10/06 08:16:38
|
createTime: 2022-03-26T11:46:50.024Z
|
||||||
permalink: /article/btkqop1a
|
permalink: /article/btkqop1a
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- css
|
- css
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
@ -160,4 +160,4 @@ type: null
|
|||||||
|
|
||||||
## @media
|
## @media
|
||||||
|
|
||||||
媒体查询,详见 [CSS @media 媒体查询](/post/fe5ruia1/)
|
媒体查询,详见 [CSS @media 媒体查询](/post/fe5ruia1/)
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: CSS 媒体查询
|
title: CSS 媒体查询
|
||||||
createTime: 2018/08/18 08:43:02
|
createTime: 2022-03-26T11:46:50.024Z
|
||||||
permalink: /article/fe5ruia1
|
permalink: /article/fe5ruia1
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- css
|
- css
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: CSS选择器
|
title: CSS选择器
|
||||||
createTime: 2018/09/20 03:29:20
|
createTime: 2022-03-26T11:46:50.024Z
|
||||||
permalink: /article/8vev8ixl
|
permalink: /article/8vev8ixl
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- css
|
- css
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
@ -644,4 +644,4 @@ type: null
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: <!DOCTYPE> 文档类型声明
|
title: <!DOCTYPE> 文档类型声明
|
||||||
createTime: 2018/03/14 01:06:52
|
createTime: 2022-03-26T11:46:50.025Z
|
||||||
permalink: /article/s8udp6vp
|
permalink: /article/s8udp6vp
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- html
|
- html
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
@ -105,4 +105,4 @@ http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|||||||
|
|
||||||
``` html
|
``` html
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: HTML5新特性
|
title: HTML5新特性
|
||||||
createTime: 2018/02/17 12:49:58
|
createTime: 2022-03-26T11:46:50.025Z
|
||||||
permalink: /article/8rv45yuy
|
permalink: /article/8rv45yuy
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- html
|
- html
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
@ -272,4 +272,4 @@ history.replaceState({}, 'bar', 'bar.html')
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
[History API - Web API 接口参考 | MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/History_API)
|
[History API - Web API 接口参考 | MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/History_API)
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: WebComponent——template
|
title: WebComponent——template
|
||||||
lang: zh-CN
|
lang: zh-CN
|
||||||
tags:
|
tags:
|
||||||
- html
|
- html
|
||||||
- javascript
|
- javascript
|
||||||
createTime: 2018/8/2 11:15:27
|
createTime: 2022-03-26T11:46:50.025Z
|
||||||
permalink: /article/5fmy4kla
|
permalink: /article/5fmy4kla
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: WebComponent——custom elements
|
title: WebComponent——custom elements
|
||||||
tags:
|
tags:
|
||||||
- html
|
- html
|
||||||
- javascript
|
- javascript
|
||||||
createTime: 2018/08/01 11:15:27
|
createTime: 2022-03-26T11:46:50.025Z
|
||||||
permalink: /article/m63fd7lf
|
permalink: /article/m63fd7lf
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: meta 标签说明
|
title: meta 标签说明
|
||||||
createTime: 2018/03/15 01:21:48
|
createTime: 2022-03-26T11:46:50.025Z
|
||||||
permalink: /article/bp1nxjs6
|
permalink: /article/bp1nxjs6
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- html
|
- html
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
@ -216,4 +216,4 @@ content="app-id=APP_ID,affiliate-data=AFFILIATE_ID,app-argument=SOME_TEXT">
|
|||||||
<meta name="theme-color" content="#E64545">
|
<meta name="theme-color" content="#E64545">
|
||||||
<!-- 添加到主屏 -->
|
<!-- 添加到主屏 -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: 继承与原型链
|
title: 继承与原型链
|
||||||
createTime: 2018/07/06 09:40:54
|
createTime: 2022-03-26T13:40:54.727Z
|
||||||
permalink: /article/extends-prototype
|
permalink: /article/extends-prototype
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- javascript
|
- javascript
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: 正则表达式
|
title: 正则表达式
|
||||||
lang: zh-CN
|
lang: zh-CN
|
||||||
createTime: 2018/11/26 11:15:27
|
createTime: 2022-03-26T11:46:50.025Z
|
||||||
permalink: /article/e8qbp0dh
|
permalink: /article/e8qbp0dh
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- javascript
|
- javascript
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Event Loop 浏览器端的事件循环
|
title: Event Loop 浏览器端的事件循环
|
||||||
createTime: 2021/06/03 01:53:17
|
createTime: 2022-03-26T11:46:50.000Z
|
||||||
permalink: /article/browser-event-loop
|
permalink: /article/browser-event-loop
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 详解 Promise
|
title: 详解 Promise
|
||||||
createTime: 2020/11/22 12:58:28
|
createTime: 2022-03-26T11:46:50.026Z
|
||||||
permalink: /article/q40nq4hv
|
permalink: /article/q40nq4hv
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
sticky: true
|
sticky: true
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: 1px解决方案
|
title: 1px解决方案
|
||||||
createTime: 2019/05/15 10:41:32
|
createTime: 2022-03-26T11:46:50.026Z
|
||||||
permalink: /article/tz7ncicn
|
permalink: /article/tz7ncicn
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- html
|
- html
|
||||||
- css
|
- css
|
||||||
- develop
|
- develop
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: lerna使用
|
title: lerna使用
|
||||||
createTime: 2021/11/26 06:28:37
|
createTime: 2022-03-26T11:46:50.027Z
|
||||||
permalink: /article/i1wc1uld
|
permalink: /article/i1wc1uld
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
@ -118,4 +118,4 @@ lerna run build # 相当于在 package1、package2 中执行 npm run build
|
|||||||
```
|
```
|
||||||
|
|
||||||
### lerna clean
|
### lerna clean
|
||||||
删除所有包的node_modules
|
删除所有包的node_modules
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: 移动端适配方案
|
title: 移动端适配方案
|
||||||
createTime: 2020/08/14 01:54:29
|
createTime: 2022-03-26T11:46:50.027Z
|
||||||
permalink: /article/vhpmovsm
|
permalink: /article/vhpmovsm
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- develop
|
- develop
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
@ -108,4 +108,4 @@ css像素是一个抽象单位,主要用在浏览器上,用来精确的度
|
|||||||
1. 容器适配
|
1. 容器适配
|
||||||
2. 文本适配
|
2. 文本适配
|
||||||
3. 大于1px的边框、圆角、阴影
|
3. 大于1px的边框、圆角、阴影
|
||||||
4. 内边距和外边距
|
4. 内边距和外边距
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: Jenkins 使用
|
title: Jenkins 使用
|
||||||
lang: zh-CN
|
lang: zh-CN
|
||||||
createTime: 2018/09/16 11:15:27
|
createTime: 2022-03-26T11:46:50.027Z
|
||||||
permalink: /article/bmtl5ah4
|
permalink: /article/bmtl5ah4
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- 工具
|
- 工具
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: caniuse
|
title: caniuse
|
||||||
createTime: 2021/02/07 06:41:12
|
createTime: 2022-03-26T11:46:50.027Z
|
||||||
permalink: /article/h4z91gyz
|
permalink: /article/h4z91gyz
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
@ -10,4 +10,4 @@ type: null
|
|||||||
### 工具
|
### 工具
|
||||||
|
|
||||||
将caniuse 的feature 结果,以图片或者iframe的形式,嵌入到站点。
|
将caniuse 的feature 结果,以图片或者iframe的形式,嵌入到站点。
|
||||||
[https://caniuse.bitsofco.de/](https://caniuse.bitsofco.de/)
|
[https://caniuse.bitsofco.de/](https://caniuse.bitsofco.de/)
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: VSCode 常用插件推荐
|
title: VSCode 常用插件推荐
|
||||||
lang: zh-CN
|
lang: zh-CN
|
||||||
createTime: 2018/12/29 11:15:27
|
createTime: 2022-03-26T11:46:50.027Z
|
||||||
permalink: /article/ofp08jd8
|
permalink: /article/ofp08jd8
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
tags:
|
tags:
|
||||||
- VSCode
|
- VSCode
|
||||||
top: false
|
top: false
|
||||||
type: null
|
type: null
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Vue组件间通信
|
title: Vue组件间通信
|
||||||
lang: zh-CN
|
lang: zh-CN
|
||||||
tags:
|
tags:
|
||||||
- vue
|
- vue
|
||||||
createTime: 2018/07/20 11:15:27
|
createTime: 2022-03-26T11:46:50.028Z
|
||||||
permalink: /article/iezlvhvg
|
permalink: /article/iezlvhvg
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 面试2
|
title: 面试2
|
||||||
createTime: 2022/04/04 01:48:00
|
createTime: 2022-04-03T17:48:00.400Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /article/exavsmm1
|
permalink: /article/exavsmm1
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: 面试题以及个人答案 JS篇
|
title: 面试题以及个人答案 JS篇
|
||||||
tags:
|
tags:
|
||||||
- 面试
|
- 面试
|
||||||
createTime: 2018/08/23 11:15:27
|
createTime: 2022-03-26T11:46:50.028Z
|
||||||
permalink: /article/4ml7z17g
|
permalink: /article/4ml7z17g
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: 面试题以及个人答案 CSS篇
|
title: 面试题以及个人答案 CSS篇
|
||||||
tags:
|
tags:
|
||||||
- 面试
|
- 面试
|
||||||
createTime: 2018/08/22 11:15:27
|
createTime: 2022-03-26T11:46:50.028Z
|
||||||
permalink: /article/565o1wn0
|
permalink: /article/565o1wn0
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
top: false
|
top: false
|
||||||
|
|||||||
@ -2,4 +2,7 @@
|
|||||||
home: true
|
home: true
|
||||||
banner: /images/big-banner.jpg
|
banner: /images/big-banner.jpg
|
||||||
motto: 世间的美好总是不期而遇,恬静而自然。
|
motto: 世间的美好总是不期而遇,恬静而自然。
|
||||||
|
author: pengzhanbo
|
||||||
|
createTime: '2022/03/26 07:46:50'
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: README
|
title: README
|
||||||
createTime: 2022/04/04 11:13:30
|
createTime: '2022/04/04 11:13:30'
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/
|
permalink: /note/
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: plugin-caniuse
|
title: plugin-caniuse
|
||||||
createTime: 2022/05/13 01:02:51
|
createTime: '2022/05/13 01:02:51'
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-plugin/caniuse/
|
permalink: /note/vuepress-plugin/caniuse/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: API
|
title: API
|
||||||
createTime: 2022/05/13 05:49:14
|
createTime: 2022-05-14T10:43:53.200Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-plugin/netlify-functions/api/
|
permalink: /note/vuepress-plugin/netlify-functions/api/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 指南
|
title: 指南
|
||||||
createTime: 2022/05/13 01:28:38
|
createTime: '2022/05/13 01:28:38'
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-plugin/netlify-functions/
|
permalink: /note/vuepress-plugin/netlify-functions/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: functions开发指南
|
title: functions开发指南
|
||||||
createTime: 2022/05/13 05:45:24
|
createTime: 2022-05-14T10:43:53.201Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-plugin/netlify-functions/develop-functions/
|
permalink: /note/vuepress-plugin/netlify-functions/develop-functions/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 介绍
|
title: 介绍
|
||||||
createTime: 2022/05/13 05:47:06
|
createTime: 2022-05-14T10:43:53.202Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-plugin/netlify-functions/intro/
|
permalink: /note/vuepress-plugin/netlify-functions/intro/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 使用
|
title: 使用
|
||||||
createTime: 2022/05/13 05:45:01
|
createTime: 2022-05-14T10:43:53.203Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-plugin/netlify-functions/usage/
|
permalink: /note/vuepress-plugin/netlify-functions/usage/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 功能
|
title: 功能
|
||||||
createTime: 2022/05/13 05:45:11
|
createTime: 2022-05-14T10:43:53.204Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-plugin/netlify-functions/feature/
|
permalink: /note/vuepress-plugin/netlify-functions/feature/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: vuepress-theme-plume
|
title: vuepress-theme-plume
|
||||||
createTime: 2022/04/08 08:52:12
|
createTime: '2022/04/08 08:52:12'
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/
|
permalink: /note/vuepress-theme-plume/
|
||||||
article: true
|
article: true
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: markdown增强
|
title: markdown增强
|
||||||
createTime: 2022/04/09 06:43:32
|
createTime: 2022-05-14T10:43:53.216Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/markdown-enhance/
|
permalink: /note/vuepress-theme-plume/markdown-enhance/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: notes配置
|
title: notes配置
|
||||||
createTime: 2022/04/09 02:48:41
|
createTime: 2022-05-14T10:43:53.218Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/notes-config/
|
permalink: /note/vuepress-theme-plume/notes-config/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 主题插件配置
|
title: 主题插件配置
|
||||||
createTime: 2022/04/09 02:48:30
|
createTime: 2022-05-14T10:43:53.219Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/plugins-config/
|
permalink: /note/vuepress-theme-plume/plugins-config/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 主题配置
|
title: 主题配置
|
||||||
createTime: 2022/04/09 12:18:12
|
createTime: 2022-05-14T10:43:53.219Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/theme-config/
|
permalink: /note/vuepress-theme-plume/theme-config/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 基础功能
|
title: 基础功能
|
||||||
createTime: 2022/04/09 06:43:20
|
createTime: 2022-05-14T10:43:53.220Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/basis-power/
|
permalink: /note/vuepress-theme-plume/basis-power/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 快速开始
|
title: 快速开始
|
||||||
createTime: 2022/04/08 09:43:20
|
createTime: 2022-05-14T10:43:53.221Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/quick-start/
|
permalink: /note/vuepress-theme-plume/quick-start/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 编写文章
|
title: 编写文章
|
||||||
createTime: 2022/04/09 12:13:56
|
createTime: 2022-05-14T10:43:53.221Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/write-article/
|
permalink: /note/vuepress-theme-plume/write-article/
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 页面配置
|
title: 页面配置
|
||||||
createTime: 2022/04/09 01:24:17
|
createTime: 2022-05-14T10:43:53.222Z
|
||||||
author: pengzhanbo
|
author: pengzhanbo
|
||||||
permalink: /note/vuepress-theme-plume/page-config/
|
permalink: /note/vuepress-theme-plume/page-config/
|
||||||
---
|
---
|
||||||
|
|||||||
115
packages/plugin-auto-frontmatter/README.md
Normal file
115
packages/plugin-auto-frontmatter/README.md
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# `@vuepress-plume/vuepress-plugin-auto-frontmatter`
|
||||||
|
|
||||||
|
自动生成 `*.md` 文件的 `frontmatter` 配置。
|
||||||
|
|
||||||
|
## Install
|
||||||
|
```
|
||||||
|
yarn add @vuepress-plume/vuepress-plugin-auto-frontmatter
|
||||||
|
```
|
||||||
|
## Usage
|
||||||
|
``` js
|
||||||
|
// .vuepress/config.js
|
||||||
|
import { autoFrontmatterPlugin } from '@vuepress-plume/vuepress-plugin-auto-frontmatter'
|
||||||
|
export default {
|
||||||
|
//...
|
||||||
|
plugins: [
|
||||||
|
autoFrontmatterPlugin({
|
||||||
|
formatter: {
|
||||||
|
createTime(formatTime, matter, file) {
|
||||||
|
if (formatTime) return formatTime
|
||||||
|
return file.createTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `autoFrontmatterPlugin([options])`
|
||||||
|
|
||||||
|
### options
|
||||||
|
|
||||||
|
`{ glob?: string | string[]; formatter: Formatter }`
|
||||||
|
|
||||||
|
- `glob`
|
||||||
|
glob 匹配字符串或数组,匹配需要自动生成 `frontmatter` 的 md文件。
|
||||||
|
默认预设为 `['**/*.md', '!.vuepress/', '!node_modules/']`。
|
||||||
|
自定义匹配将被合并到预设配置中
|
||||||
|
example: `['blog/**']`
|
||||||
|
|
||||||
|
- `formatter`
|
||||||
|
配置`frontmatter`每个字段的生成规则。
|
||||||
|
```ts
|
||||||
|
interface MarkdownFile {
|
||||||
|
filepath: string
|
||||||
|
relativePath: string
|
||||||
|
content: string
|
||||||
|
createTime: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormatterFn<T = any, K = object> {
|
||||||
|
(value: T, data: K, file: MarkdownFile): T
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormatterObject<K = object, T = any> = Record<
|
||||||
|
string,
|
||||||
|
FormatterFn<T, K>
|
||||||
|
>
|
||||||
|
|
||||||
|
type FormatterArray = {
|
||||||
|
glob: string
|
||||||
|
formatter: FormatterObject
|
||||||
|
}[]
|
||||||
|
|
||||||
|
type Formatter = FormatterObject | FormatterArray
|
||||||
|
|
||||||
|
/**
|
||||||
|
* formatterObj 对象中的 key 即为 frontmatter 配置中的key
|
||||||
|
* 其方法返回的值将作为 frontmatter[key] 的值
|
||||||
|
* *.md
|
||||||
|
* ---
|
||||||
|
* createTime: 2022-03-26T11:46:50.000Z
|
||||||
|
* ---
|
||||||
|
*/
|
||||||
|
const formatterObj: Formatter = {
|
||||||
|
createTime(formatTime, matter, file) {
|
||||||
|
if (formatTime) return formatTime
|
||||||
|
return file.createTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatterArr: Formatter = [
|
||||||
|
{
|
||||||
|
// 更精细化的匹配某个 md文件,支持glob 匹配字符串
|
||||||
|
glob: '**/{README,index}.md',
|
||||||
|
// formatter 仅对 glob命中的文件有效
|
||||||
|
formatter: {
|
||||||
|
home(value, matter, file) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 通配,如果文件没有被其他精细glob命中,
|
||||||
|
// 则使用 通配 formatter
|
||||||
|
// 如果是数组,必须有且用一个 glob为 * 的 项
|
||||||
|
glob: '*',
|
||||||
|
formatter: {
|
||||||
|
title(title) {
|
||||||
|
return title || '默认标题'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why ?
|
||||||
|
|
||||||
|
- **为什么需要这个插件?**
|
||||||
|
|
||||||
|
有时候在开发一些主题时,期望使用户更专注于内容的编写,尽可能减少配置性的工作,可以将一些重复性的必要的配置
|
||||||
|
直接通过本插件自动生成。
|
||||||
|
|
||||||
|
以及,我确实想在写新文章的时候,更少的做配置工作,于是便有了这个插件
|
||||||
48
packages/plugin-auto-frontmatter/package.json
Normal file
48
packages/plugin-auto-frontmatter/package.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "@vuepress-plume/vuepress-plugin-auto-frontmatter",
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./lib/node/index.js",
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"main": "lib/node/index.js",
|
||||||
|
"types": "./lib/node/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm run clean && pnpm run copy && pnpm run ts",
|
||||||
|
"clean": "rimraf lib *.tsbuildinfo",
|
||||||
|
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
|
||||||
|
"ts": "tsc -b tsconfig.build.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vuepress/core": "2.0.0-beta.51",
|
||||||
|
"@vuepress/shared": "2.0.0-beta.51",
|
||||||
|
"@vuepress/utils": "2.0.0-beta.51",
|
||||||
|
"chokidar": "^3.5.3",
|
||||||
|
"glob-to-regexp": "^0.4.1",
|
||||||
|
"gray-matter": "^4.0.3"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"keyword": [
|
||||||
|
"VuePress",
|
||||||
|
"vuepress plugin",
|
||||||
|
"autoFrontmatter",
|
||||||
|
"vuepress-plugin-plugin-auto-frontmatter"
|
||||||
|
]
|
||||||
|
}
|
||||||
16
packages/plugin-auto-frontmatter/src/node/env.d.ts
vendored
Normal file
16
packages/plugin-auto-frontmatter/src/node/env.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
declare module 'glob-to-regexp' {
|
||||||
|
interface GlobToRegexp {
|
||||||
|
(
|
||||||
|
glob: string,
|
||||||
|
options?: {
|
||||||
|
globstar?: boolean
|
||||||
|
extended?: boolean
|
||||||
|
flags?: string
|
||||||
|
}
|
||||||
|
): RegExp
|
||||||
|
}
|
||||||
|
|
||||||
|
const globToRegexp: GlobToRegexp
|
||||||
|
|
||||||
|
export default globToRegexp
|
||||||
|
}
|
||||||
8
packages/plugin-auto-frontmatter/src/node/index.ts
Normal file
8
packages/plugin-auto-frontmatter/src/node/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import type { AutoFrontmatterOptions } from '../shared/index.js'
|
||||||
|
import { autoFrontmatterPlugin } from './plugin.js'
|
||||||
|
|
||||||
|
export * from './plugin.js'
|
||||||
|
|
||||||
|
export { AutoFrontmatterOptions }
|
||||||
|
|
||||||
|
export default autoFrontmatterPlugin
|
||||||
83
packages/plugin-auto-frontmatter/src/node/plugin.ts
Normal file
83
packages/plugin-auto-frontmatter/src/node/plugin.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import type { Plugin } from '@vuepress/core'
|
||||||
|
import { fs } from '@vuepress/utils'
|
||||||
|
import chokidar from 'chokidar'
|
||||||
|
import globToRegexp from 'glob-to-regexp'
|
||||||
|
import grayMatter from 'gray-matter'
|
||||||
|
import type {
|
||||||
|
AutoFrontmatterOptions,
|
||||||
|
FormatterArray,
|
||||||
|
FormatterObject,
|
||||||
|
MarkdownFile,
|
||||||
|
} from '../shared/index.js'
|
||||||
|
import { readMarkdown, readMarkdownList } from './readFiles.js'
|
||||||
|
|
||||||
|
export const autoFrontmatterPlugin = ({
|
||||||
|
glob = '',
|
||||||
|
formatter = {},
|
||||||
|
}: AutoFrontmatterOptions = {}): Plugin => {
|
||||||
|
glob = glob ? (Array.isArray(glob) ? glob : [glob]) : []
|
||||||
|
glob = ['**/*.{md,MD}', '!.vuepress/', '!node_modules/', ...glob]
|
||||||
|
|
||||||
|
const matterFormatter: FormatterArray = Array.isArray(formatter)
|
||||||
|
? formatter
|
||||||
|
: [{ glob: '*', formatter }]
|
||||||
|
|
||||||
|
const globFormatter: FormatterObject =
|
||||||
|
matterFormatter.find(({ glob }) => glob === '*')?.formatter || {}
|
||||||
|
|
||||||
|
const otherFormatters = matterFormatter
|
||||||
|
.filter(({ glob }) => glob !== '*')
|
||||||
|
.map(({ glob, formatter }) => {
|
||||||
|
return {
|
||||||
|
glob,
|
||||||
|
regexp: globToRegexp(glob, {
|
||||||
|
globstar: true,
|
||||||
|
extended: true,
|
||||||
|
}),
|
||||||
|
formatter,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function formatMarkdown(file: MarkdownFile): void {
|
||||||
|
const { filepath, relativePath } = file
|
||||||
|
|
||||||
|
const formatter =
|
||||||
|
otherFormatters.find(({ regexp }) => regexp.test(relativePath))
|
||||||
|
?.formatter || globFormatter
|
||||||
|
const { data, content } = grayMatter(file.content)
|
||||||
|
|
||||||
|
Object.keys(formatter).forEach((key) => {
|
||||||
|
const value = formatter[key](data[key], data, file)
|
||||||
|
data[key] = value ?? data[key]
|
||||||
|
})
|
||||||
|
const newContent = grayMatter.stringify({ content }, data)
|
||||||
|
|
||||||
|
fs.writeFileSync(filepath, newContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: '@vuepress-plume/vuepress-plugin-auto-frontmatter',
|
||||||
|
onInitialized: async (app) => {
|
||||||
|
const markdownList = await readMarkdownList(
|
||||||
|
app.dir.source(),
|
||||||
|
glob as string[]
|
||||||
|
)
|
||||||
|
markdownList.forEach((file) => formatMarkdown(file))
|
||||||
|
},
|
||||||
|
onWatched: async (app, watchers) => {
|
||||||
|
const watcher = chokidar.watch('**/*.md', {
|
||||||
|
cwd: app.dir.source(),
|
||||||
|
ignoreInitial: true,
|
||||||
|
ignored: /(node_modules|\.vuepress)\//,
|
||||||
|
})
|
||||||
|
|
||||||
|
watcher.on('add', (relativePath) => {
|
||||||
|
if ((glob as string[]).some((_) => !globToRegexp(_).test(relativePath)))
|
||||||
|
return
|
||||||
|
formatMarkdown(readMarkdown(app.dir.source(), relativePath))
|
||||||
|
})
|
||||||
|
|
||||||
|
watchers.push(watcher)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
33
packages/plugin-auto-frontmatter/src/node/readFiles.ts
Normal file
33
packages/plugin-auto-frontmatter/src/node/readFiles.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { fs, globby, path } from '@vuepress/utils'
|
||||||
|
import type { MarkdownFile } from '../shared/index.js'
|
||||||
|
|
||||||
|
type MarkdownFileList = MarkdownFile[]
|
||||||
|
|
||||||
|
export const readMarkdownList = async (
|
||||||
|
sourceDir: string,
|
||||||
|
glob: string[]
|
||||||
|
): Promise<MarkdownFileList> => {
|
||||||
|
const files: string[] = await globby(glob, {
|
||||||
|
cwd: sourceDir,
|
||||||
|
gitignore: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return files.map((file) => readMarkdown(sourceDir, file))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const readMarkdown = (
|
||||||
|
sourceDir: string,
|
||||||
|
relativePath: string
|
||||||
|
): MarkdownFile => {
|
||||||
|
const filepath = path.join(sourceDir, relativePath)
|
||||||
|
return {
|
||||||
|
filepath,
|
||||||
|
relativePath,
|
||||||
|
content: fs.readFileSync(filepath, 'utf-8'),
|
||||||
|
createTime: getFileCreateTime(fs.statSync(filepath)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getFileCreateTime = (stat: fs.Stats): Date => {
|
||||||
|
return stat.birthtime.getFullYear() !== 1970 ? stat.birthtime : stat.atime
|
||||||
|
}
|
||||||
36
packages/plugin-auto-frontmatter/src/shared/index.ts
Normal file
36
packages/plugin-auto-frontmatter/src/shared/index.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export interface MarkdownFile {
|
||||||
|
filepath: string
|
||||||
|
relativePath: string
|
||||||
|
content: string
|
||||||
|
createTime: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormatterFn<T = any, K = object> {
|
||||||
|
(value: T, data: K, file: MarkdownFile): T
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormatterObject<K = object, T = any> = Record<
|
||||||
|
string,
|
||||||
|
FormatterFn<T, K>
|
||||||
|
>
|
||||||
|
|
||||||
|
export type FormatterArray = {
|
||||||
|
glob: string
|
||||||
|
formatter: FormatterObject
|
||||||
|
}[]
|
||||||
|
|
||||||
|
export interface AutoFrontmatterOptions {
|
||||||
|
/**
|
||||||
|
* glob string
|
||||||
|
*/
|
||||||
|
glob?: string | string[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {
|
||||||
|
* key(value, data, file) {
|
||||||
|
* return value
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
formatter?: FormatterObject | FormatterArray
|
||||||
|
}
|
||||||
9
packages/plugin-auto-frontmatter/tsconfig.build.json
Normal file
9
packages/plugin-auto-frontmatter/tsconfig.build.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.build.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib"
|
||||||
|
},
|
||||||
|
"include": ["./src"],
|
||||||
|
"files": []
|
||||||
|
}
|
||||||
21
packages/plugin-blog-data/LICENSE
Normal file
21
packages/plugin-blog-data/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (C) 2021 - PRESENT by pengzhanbo
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
51
packages/plugin-blog-data/package.json
Normal file
51
packages/plugin-blog-data/package.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "@vuepress-plume/vuepress-plugin-blog-data",
|
||||||
|
"version": "1.0.0-beta.45",
|
||||||
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./lib/node/index.js",
|
||||||
|
"./client": "./lib/client/index.js",
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"main": "lib/node/index.js",
|
||||||
|
"types": "./lib/node/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm run clean && pnpm run copy && pnpm run ts",
|
||||||
|
"clean": "rimraf lib *.tsbuildinfo",
|
||||||
|
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
|
||||||
|
"ts": "tsc -b tsconfig.build.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.4.5",
|
||||||
|
"@vuepress/client": "2.0.0-beta.51",
|
||||||
|
"@vuepress/core": "2.0.0-beta.51",
|
||||||
|
"@vuepress/shared": "2.0.0-beta.51",
|
||||||
|
"@vuepress/utils": "2.0.0-beta.51",
|
||||||
|
"chokidar": "^3.5.3",
|
||||||
|
"glob-to-regexp": "^0.4.1",
|
||||||
|
"vue": "^3.2.41"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"keyword": [
|
||||||
|
"VuePress",
|
||||||
|
"vuepress plugin",
|
||||||
|
"blogData",
|
||||||
|
"vuepress-plugin-plugin-blog-data"
|
||||||
|
]
|
||||||
|
}
|
||||||
7
packages/plugin-blog-data/src/client/blogPostData.d.ts
vendored
Normal file
7
packages/plugin-blog-data/src/client/blogPostData.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { BlogPostData } from '../shared/index.js'
|
||||||
|
|
||||||
|
declare module '@internal/blogData' {
|
||||||
|
const blogPostData: BlogPostData
|
||||||
|
|
||||||
|
export { blogPostData }
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from './useBlogPostData.js'
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { blogPostData as blogPostDataRaw } from '@internal/blogData'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import type { BlogPostData } from '../../shared/index.js'
|
||||||
|
|
||||||
|
declare const __VUE_HMR_RUNTIME__: Record<string, any>
|
||||||
|
|
||||||
|
export type ThemeDataRef<T extends BlogPostData = BlogPostData> = Ref<T>
|
||||||
|
|
||||||
|
export const blogPostData: ThemeDataRef = ref(blogPostDataRaw)
|
||||||
|
|
||||||
|
export const useBlogPostData = <
|
||||||
|
T extends BlogPostData = BlogPostData
|
||||||
|
>(): ThemeDataRef<T> => blogPostData as ThemeDataRef<T>
|
||||||
|
|
||||||
|
if (import.meta.webpackHot || import.meta.hot) {
|
||||||
|
__VUE_HMR_RUNTIME__.updateBlogData = (data: BlogPostData) => {
|
||||||
|
blogPostData.value = data
|
||||||
|
}
|
||||||
|
}
|
||||||
36
packages/plugin-blog-data/src/client/config.ts
Normal file
36
packages/plugin-blog-data/src/client/config.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { setupDevtoolsPlugin } from '@vue/devtools-api'
|
||||||
|
import { defineClientConfig } from '@vuepress/client'
|
||||||
|
import { useBlogPostData } from './composables/index.js'
|
||||||
|
|
||||||
|
export default defineClientConfig({
|
||||||
|
enhance({ app }) {
|
||||||
|
// provide theme data & theme locale data
|
||||||
|
const blogPostData = useBlogPostData()
|
||||||
|
|
||||||
|
// setup devtools in dev mode
|
||||||
|
if (__VUEPRESS_DEV__ || __VUE_PROD_DEVTOOLS__) {
|
||||||
|
setupDevtoolsPlugin(
|
||||||
|
{
|
||||||
|
// fix recursive reference
|
||||||
|
app: app as any,
|
||||||
|
id: 'org.vuepress-plume.plugin-blog-data',
|
||||||
|
label: 'VuePress Blog Data Plugin',
|
||||||
|
packageName: '@vuepress/plugin-blog-data',
|
||||||
|
homepage: 'https://pengzhanbo.cn',
|
||||||
|
logo: 'https://v2.vuepress.vuejs.org/images/hero.png',
|
||||||
|
componentStateTypes: ['VuePress'],
|
||||||
|
},
|
||||||
|
(api) => {
|
||||||
|
api.on.inspectComponent((payload) => {
|
||||||
|
payload.instanceData.state.push({
|
||||||
|
type: 'VuePress',
|
||||||
|
key: 'blogPostData',
|
||||||
|
editable: false,
|
||||||
|
value: blogPostData.value,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
4
packages/plugin-blog-data/src/client/index.ts
Normal file
4
packages/plugin-blog-data/src/client/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import type { BlogPostData, BlogPostDataItem } from '../shared/index.js'
|
||||||
|
export * from './composables/index.js'
|
||||||
|
|
||||||
|
export { BlogPostData, BlogPostDataItem }
|
||||||
16
packages/plugin-blog-data/src/node/env.d.ts
vendored
Normal file
16
packages/plugin-blog-data/src/node/env.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
declare module 'glob-to-regexp' {
|
||||||
|
interface GlobToRegexp {
|
||||||
|
(
|
||||||
|
glob: string,
|
||||||
|
options?: {
|
||||||
|
globstar?: boolean
|
||||||
|
extended?: boolean
|
||||||
|
flags?: string
|
||||||
|
}
|
||||||
|
): RegExp
|
||||||
|
}
|
||||||
|
|
||||||
|
const globToRegexp: GlobToRegexp
|
||||||
|
|
||||||
|
export default globToRegexp
|
||||||
|
}
|
||||||
6
packages/plugin-blog-data/src/node/index.ts
Normal file
6
packages/plugin-blog-data/src/node/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { blogDataPlugin } from './plugin.js'
|
||||||
|
|
||||||
|
export * from '../shared/index.js'
|
||||||
|
export * from './plugin.js'
|
||||||
|
|
||||||
|
export default blogDataPlugin
|
||||||
63
packages/plugin-blog-data/src/node/plugin.ts
Normal file
63
packages/plugin-blog-data/src/node/plugin.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import type { Plugin } from '@vuepress/core'
|
||||||
|
import { getDirname, path } from '@vuepress/utils'
|
||||||
|
import chokidar from 'chokidar'
|
||||||
|
import globToRegexp from 'glob-to-regexp'
|
||||||
|
import { preparedBlogData } from './prepareBlogData.js'
|
||||||
|
import type { BlogDataPluginOptions } from './index.js'
|
||||||
|
|
||||||
|
const __dirname = getDirname(import.meta.url)
|
||||||
|
|
||||||
|
export interface PluginOption
|
||||||
|
extends Omit<BlogDataPluginOptions, 'include' | 'exclude'> {
|
||||||
|
include: {
|
||||||
|
(filepath: string): boolean
|
||||||
|
}[]
|
||||||
|
exclude: {
|
||||||
|
(filepath: string): boolean
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const globOptions = {
|
||||||
|
globstar: true,
|
||||||
|
extended: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const blogDataPlugin = ({
|
||||||
|
include,
|
||||||
|
exclude,
|
||||||
|
...pluginOptions
|
||||||
|
}: BlogDataPluginOptions = {}): Plugin => {
|
||||||
|
const options: PluginOption = {
|
||||||
|
include: toArray(include)
|
||||||
|
.map((str) => globToRegexp(str, globOptions))
|
||||||
|
.map((regexp) => (filepath: string) => regexp.test(filepath)),
|
||||||
|
|
||||||
|
exclude: toArray(exclude)
|
||||||
|
.map((str) => globToRegexp(str, globOptions))
|
||||||
|
.map((regexp) => (filepath: string) => !regexp.test(filepath)),
|
||||||
|
...pluginOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: '@vuepress-plume/vuepress-plugin-blog-data',
|
||||||
|
clientConfigFile: path.resolve(__dirname, '../client/config.js'),
|
||||||
|
onPrepared: async (app) => await preparedBlogData(app, options),
|
||||||
|
onWatched(app, watchers) {
|
||||||
|
const watcher = chokidar.watch('pages/**/*', {
|
||||||
|
cwd: app.dir.temp(),
|
||||||
|
ignoreInitial: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
watcher.on('add', async () => await preparedBlogData(app, options))
|
||||||
|
watcher.on('change', async () => await preparedBlogData(app, options))
|
||||||
|
watcher.on('unlink', async () => await preparedBlogData(app, options))
|
||||||
|
|
||||||
|
watchers.push(watcher)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toArray(likeArr: string | string[] | undefined): string[] {
|
||||||
|
if (Array.isArray(likeArr)) return likeArr
|
||||||
|
return likeArr ? [likeArr] : []
|
||||||
|
}
|
||||||
79
packages/plugin-blog-data/src/node/prepareBlogData.ts
Normal file
79
packages/plugin-blog-data/src/node/prepareBlogData.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import type { App } from '@vuepress/core'
|
||||||
|
import type { BlogPostData, BlogPostDataItem } from '../shared/index.js'
|
||||||
|
import type { PluginOption } from './plugin.js'
|
||||||
|
|
||||||
|
const HMR_CODE = `
|
||||||
|
if (import.meta.webpackHot) {
|
||||||
|
import.meta.webpackHot.accept()
|
||||||
|
if (__VUE_HMR_RUNTIME__.updateBlogData) {
|
||||||
|
__VUE_HMR_RUNTIME__.updateBlogData(blogPostData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.accept(({ blogPostData }) => {
|
||||||
|
__VUE_HMR_RUNTIME__.updateBlogData(blogPostData)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const getTimestamp = (time: Date): number => {
|
||||||
|
return new Date(time).getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const preparedBlogData = async (
|
||||||
|
app: App,
|
||||||
|
options: PluginOption
|
||||||
|
): Promise<void> => {
|
||||||
|
let pages = app.pages.filter((page) => {
|
||||||
|
return (
|
||||||
|
page.filePathRelative &&
|
||||||
|
options.exclude.every((filter) => filter(page.filePathRelative!)) &&
|
||||||
|
options.include.some((filter) => filter(page.filePathRelative!))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
if (options.sortBy) {
|
||||||
|
pages = pages.sort((prev, next) => {
|
||||||
|
if (options.sortBy === 'createTime') {
|
||||||
|
return getTimestamp(prev.frontmatter.createTime as Date) <
|
||||||
|
getTimestamp(next.frontmatter.createTime as Date)
|
||||||
|
? 1
|
||||||
|
: -1
|
||||||
|
} else {
|
||||||
|
return typeof options.sortBy === 'function' &&
|
||||||
|
options.sortBy(prev, next)
|
||||||
|
? 1
|
||||||
|
: -1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const blogData: BlogPostData = pages.map((page) => {
|
||||||
|
let extended: Partial<BlogPostDataItem> = {}
|
||||||
|
if (typeof options.extendBlogData === 'function') {
|
||||||
|
extended = options.extendBlogData(page)
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
path: page.path,
|
||||||
|
title: page.title,
|
||||||
|
...extended,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.excerpt) data.excerpt = page.excerpt
|
||||||
|
|
||||||
|
return data as BlogPostDataItem
|
||||||
|
})
|
||||||
|
|
||||||
|
let content = `\
|
||||||
|
export const blogPostData = JSON.parse(${JSON.stringify(
|
||||||
|
JSON.stringify(blogData)
|
||||||
|
)})
|
||||||
|
`
|
||||||
|
|
||||||
|
// inject HMR code
|
||||||
|
if (app.env.isDev) {
|
||||||
|
content += HMR_CODE
|
||||||
|
}
|
||||||
|
|
||||||
|
await app.writeTemp('internal/blogData.js', content)
|
||||||
|
}
|
||||||
16
packages/plugin-blog-data/src/shared/index.ts
Normal file
16
packages/plugin-blog-data/src/shared/index.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export interface BlogDataPluginOptions {
|
||||||
|
include?: string | string[]
|
||||||
|
exclude?: string | string[]
|
||||||
|
sortBy?: 'createTime' | false | (<T>(prev: T, next: T) => boolean)
|
||||||
|
excerpt?: boolean
|
||||||
|
extendBlogData?: <T>(page: T) => Partial<BlogPostDataItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BlogPostData<T extends object = object> = BlogPostDataItem<T>[]
|
||||||
|
|
||||||
|
export type BlogPostDataItem<T extends object = object> = {
|
||||||
|
path: string
|
||||||
|
title: string
|
||||||
|
excerpt: string
|
||||||
|
[x: string]: any
|
||||||
|
} & T
|
||||||
13
packages/plugin-blog-data/tsconfig.build.json
Normal file
13
packages/plugin-blog-data/tsconfig.build.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.build.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./lib",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@internal/blogData": ["./src/client/blogPostData.d.ts"]
|
||||||
|
},
|
||||||
|
"types": ["@vuepress/client/types", "vite/client", "webpack-env"]
|
||||||
|
},
|
||||||
|
"include": ["./src"]
|
||||||
|
}
|
||||||
@ -1,229 +0,0 @@
|
|||||||
# Change Log
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
||||||
|
|
||||||
# [1.0.0-beta.26](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2022-04-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* support sitemap and seo ([a57af59](https://github.com/pengzhanbo/vuepress-theme-plume/commit/a57af599e649d1f7ce357f704a222c7babc77b06))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.25](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2022-04-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* fix define config ([00bca40](https://github.com/pengzhanbo/vuepress-theme-plume/commit/00bca40d895499017308359bfce682cec055f42b))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.24](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2022-04-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* 主题配置函数 ([844feea](https://github.com/pengzhanbo/vuepress-theme-plume/commit/844feeae7406f5aee8edef35bce4a08e808f692e))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.23](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.22...v1.0.0-beta.23) (2022-04-23)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.22](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.21...v1.0.0-beta.22) (2022-04-18)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.21](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2022-04-18)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.20](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2022-04-18)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.19](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2022-04-18)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.18](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.17...v1.0.0-beta.18) (2022-04-14)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.17](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.16...v1.0.0-beta.17) (2022-04-12)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.16](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2022-04-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* sidebar ([a2794d7](https://github.com/pengzhanbo/vuepress-theme-plume/commit/a2794d72ea7a276d8fd876475f5e9c77c33f5e90))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.15](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.14...v1.0.0-beta.15) (2022-04-12)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.14](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.13...v1.0.0-beta.14) (2022-04-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* 修复sidebar问题 ([420ec9f](https://github.com/pengzhanbo/vuepress-theme-plume/commit/420ec9fb663793fe2d4fd7e9e61f12ca0d05217e))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.13](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.12...v1.0.0-beta.13) (2022-04-12)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* 修复note生成sidebar时目录嵌套时未正确识别路径 ([7b3a6e2](https://github.com/pengzhanbo/vuepress-theme-plume/commit/7b3a6e2252582a19bdbf42c1ddf85dfab199d57b))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.12](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.11...v1.0.0-beta.12) (2022-04-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* 修复page页toc问题 ([2d44799](https://github.com/pengzhanbo/vuepress-theme-plume/commit/2d4479909f4c84a8d71c8a97c93f21bde3b8208c))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.11](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.10...v1.0.0-beta.11) (2022-04-08)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.10](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.9...v1.0.0-beta.10) (2022-04-08)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.9](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.8...v1.0.0-beta.9) (2022-04-06)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.8](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.7...v1.0.0-beta.8) (2022-04-06)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.7](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.6...v1.0.0-beta.7) (2022-04-05)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.6](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.5...v1.0.0-beta.6) (2022-04-05)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.5](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.4...v1.0.0-beta.5) (2022-04-05)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.4](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.3...v1.0.0-beta.4) (2022-04-05)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.3](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2022-04-05)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.0.0-beta.2](https://github.com/pengzhanbo/vuepress-theme-plume/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2022-04-05)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 1.0.0-beta.1 (2022-04-05)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @vuepress-plume/vuepress-theme-plume
|
|
||||||
@ -1,15 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/vuepress-theme-plume",
|
"name": "@vuepress-plume/vuepress-theme-plume",
|
||||||
"version": "1.0.0-beta.45",
|
"version": "0.0.0",
|
||||||
"description": "A Blog Theme for VuePress 2.0",
|
|
||||||
"keywords": [
|
|
||||||
"VuePress",
|
|
||||||
"Theme",
|
|
||||||
"plume",
|
|
||||||
"vuepress-theme",
|
|
||||||
"vuepress-theme-plume",
|
|
||||||
"theme-plume"
|
|
||||||
],
|
|
||||||
"homepage": "https://pengzhanbo.cn/note/vuepress-theme-plume",
|
"homepage": "https://pengzhanbo.cn/note/vuepress-theme-plume",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
@ -19,7 +10,7 @@
|
|||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
@ -43,7 +34,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/lodash.merge": "^4.6.7",
|
"@types/lodash.merge": "^4.6.7",
|
||||||
|
"@vuepress-plume/vuepress-plugin-auto-frontmatter": "workspace:*",
|
||||||
"@vuepress-plume/vuepress-plugin-baidu-tongji": "workspace:*",
|
"@vuepress-plume/vuepress-plugin-baidu-tongji": "workspace:*",
|
||||||
|
"@vuepress-plume/vuepress-plugin-blog-data": "workspace:*",
|
||||||
"@vuepress-plume/vuepress-plugin-caniuse": "workspace:*",
|
"@vuepress-plume/vuepress-plugin-caniuse": "workspace:*",
|
||||||
"@vuepress-plume/vuepress-plugin-copy-code": "workspace:*",
|
"@vuepress-plume/vuepress-plugin-copy-code": "workspace:*",
|
||||||
"@vuepress/client": "2.0.0-beta.51",
|
"@vuepress/client": "2.0.0-beta.51",
|
||||||
@ -62,15 +55,8 @@
|
|||||||
"@vuepress/plugin-toc": "2.0.0-beta.51",
|
"@vuepress/plugin-toc": "2.0.0-beta.51",
|
||||||
"@vuepress/shared": "2.0.0-beta.51",
|
"@vuepress/shared": "2.0.0-beta.51",
|
||||||
"@vuepress/utils": "2.0.0-beta.51",
|
"@vuepress/utils": "2.0.0-beta.51",
|
||||||
"@vueuse/core": "^9.3.0",
|
|
||||||
"chokidar": "^3.5.3",
|
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
"gray-matter": "^4.0.3",
|
|
||||||
"json2yaml": "^1.1.0",
|
|
||||||
"lodash.merge": "^4.6.2",
|
|
||||||
"nanoid": "^4.0.0",
|
"nanoid": "^4.0.0",
|
||||||
"sass": "^1.55.0",
|
|
||||||
"sass-loader": "^13.1.0",
|
|
||||||
"ts-debounce": "^4.0.0",
|
"ts-debounce": "^4.0.0",
|
||||||
"vue": "^3.2.41",
|
"vue": "^3.2.41",
|
||||||
"vue-router": "4.1.5",
|
"vue-router": "4.1.5",
|
||||||
@ -78,16 +64,5 @@
|
|||||||
"vuepress-plugin-md-enhance": "2.0.0-beta.110",
|
"vuepress-plugin-md-enhance": "2.0.0-beta.110",
|
||||||
"vuepress-plugin-seo2": "2.0.0-beta.110",
|
"vuepress-plugin-seo2": "2.0.0-beta.110",
|
||||||
"vuepress-plugin-sitemap2": "2.0.0-beta.110"
|
"vuepress-plugin-sitemap2": "2.0.0-beta.110"
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"sass-loader": "^13.0.2"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"sass-loader": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,171 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import BlogInfo from '@theme-plume/BlogInfo.vue'
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import type { PageHeader } from '@vuepress/client'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useArchive } from '../composables/index.js'
|
|
||||||
import Toc from './Toc.js'
|
|
||||||
|
|
||||||
const archiveList = useArchive()
|
|
||||||
|
|
||||||
const headers = computed(() => {
|
|
||||||
return archiveList.value.map(({ year }) => {
|
|
||||||
return {
|
|
||||||
level: 2,
|
|
||||||
slug: year,
|
|
||||||
title: year,
|
|
||||||
link: '',
|
|
||||||
children: [],
|
|
||||||
} as PageHeader
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="archive-wrapper">
|
|
||||||
<div class="archive-container">
|
|
||||||
<DropdownTransition>
|
|
||||||
<div class="archive-content">
|
|
||||||
<div class="archive-box">
|
|
||||||
<div
|
|
||||||
v-for="archive in archiveList"
|
|
||||||
:key="archive.year"
|
|
||||||
class="archive-items"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
<a
|
|
||||||
class="header-anchor"
|
|
||||||
:href="'#' + archive.year"
|
|
||||||
aria-hidden="true"
|
|
||||||
>#</a
|
|
||||||
>
|
|
||||||
{{ archive.year }}
|
|
||||||
</h2>
|
|
||||||
<ul class="archive-list">
|
|
||||||
<li v-for="child in archive.children" :key="child.link">
|
|
||||||
<span>{{ child.date }}</span>
|
|
||||||
<RouterLink :to="child.link">{{ child.text }}</RouterLink>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="archive-toc">
|
|
||||||
<Toc :headers="headers" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DropdownTransition>
|
|
||||||
<BlogInfo></BlogInfo>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_mixins';
|
|
||||||
|
|
||||||
.archive-wrapper {
|
|
||||||
@include wrapper;
|
|
||||||
|
|
||||||
.archive-container {
|
|
||||||
@include container_wrapper;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 1.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.archive-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
.archive-box {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.archive-items {
|
|
||||||
h2 {
|
|
||||||
position: relative;
|
|
||||||
margin: 0 0 0 4rem;
|
|
||||||
border-left: solid 4px var(--c-border);
|
|
||||||
border-bottom: none;
|
|
||||||
padding: 1.25rem;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
width: 0.75rem;
|
|
||||||
height: 0.75rem;
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
border-radius: 6px;
|
|
||||||
border: solid 2px var(--c-border-dark);
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: -2px;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.archive-list {
|
|
||||||
list-style: none;
|
|
||||||
padding-left: 0;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
li {
|
|
||||||
position: relative;
|
|
||||||
margin-left: 4rem;
|
|
||||||
border-left: solid 4px var(--c-border);
|
|
||||||
padding: 0.75rem 1.25rem;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
width: 0.625rem;
|
|
||||||
height: 0.625rem;
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
border-radius: 5px;
|
|
||||||
border: solid 2px var(--c-border-dark);
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: -2px;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
transition: border-color var(--t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
> span {
|
|
||||||
position: absolute;
|
|
||||||
left: -1.25rem;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-100%, -50%);
|
|
||||||
color: var(--c-text-light);
|
|
||||||
font-size: 14px;
|
|
||||||
transition: color var(--t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
> a {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.5rem 1.25rem;
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
border-radius: var(--p-around);
|
|
||||||
// box-shadow: var(--shadow-sm);
|
|
||||||
color: var(--c-text);
|
|
||||||
transition: color var(--t-color), box-shadow var(--t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
> span,
|
|
||||||
> a {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// > a {
|
|
||||||
// box-shadow: var(--shadow);
|
|
||||||
// }
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
border-color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import Sidebar from '@theme-plume/Sidebar.vue'
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
||||||
import type { SidebarOptions } from '../../shared/index.js'
|
|
||||||
import { useAsideNavbar, useNavbarConfig } from '../composables/index.js'
|
|
||||||
|
|
||||||
const navbarConfig = useNavbarConfig()
|
|
||||||
const { asideNavbarShow, triggerAsideNavbar } = useAsideNavbar()
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<Transition name="fade">
|
|
||||||
<div
|
|
||||||
v-show="asideNavbarShow"
|
|
||||||
class="aside-navbar-wrapper"
|
|
||||||
@click.self="triggerAsideNavbar(false)"
|
|
||||||
>
|
|
||||||
<Sidebar :aside="(navbarConfig as SidebarOptions)" />
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/variables';
|
|
||||||
.aside-navbar-wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.75);
|
|
||||||
z-index: 30;
|
|
||||||
|
|
||||||
.plume-theme-sidebar-wrapper {
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 70%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 1.25rem 0 1.25rem 2.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (min-width: $MQMobile) {
|
|
||||||
.aside-navbar-wrapper {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
/* eslint-disable import/first, import/no-duplicates, import/order */
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
inheritAttrs: false,
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, toRefs } from 'vue'
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { useSiteData } from '@vuepress/client'
|
|
||||||
import type { NavLink } from '../../shared/index.js'
|
|
||||||
import { isLinkHttp, isLinkMailto, isLinkTel } from '@vuepress/shared'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
item: {
|
|
||||||
type: Object as PropType<NavLink>,
|
|
||||||
require: true,
|
|
||||||
default: () => ({ text: '' }),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const site = useSiteData()
|
|
||||||
const { item } = toRefs(props)
|
|
||||||
|
|
||||||
const hasHttpProtocol = computed(() => isLinkHttp(item.value.link))
|
|
||||||
const hasNonHttpProtocol = computed(
|
|
||||||
() => isLinkMailto(item.value.link) || isLinkTel(item.value.link)
|
|
||||||
)
|
|
||||||
|
|
||||||
const linkTarget = computed(() => {
|
|
||||||
if (hasNonHttpProtocol.value) return undefined
|
|
||||||
if (item.value.target) return item.value.target
|
|
||||||
if (hasHttpProtocol.value) return '_blank'
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
const isBlankTarget = computed(() => linkTarget.value === '_blank')
|
|
||||||
const isRouterLink = computed(
|
|
||||||
() =>
|
|
||||||
!hasHttpProtocol.value && !hasNonHttpProtocol.value && !isBlankTarget.value
|
|
||||||
)
|
|
||||||
|
|
||||||
const linkRel = computed(() => {
|
|
||||||
if (hasNonHttpProtocol.value) return undefined
|
|
||||||
if (item.value.rel) return item.value.rel
|
|
||||||
if (isBlankTarget.value) return 'noopener noreferrer'
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
const linkAriaLabel = computed(() => item.value.ariaLabel || item.value.text)
|
|
||||||
|
|
||||||
const shouldBeActiveInSubpath = computed(() => {
|
|
||||||
const localeKeys = Object.keys(site.value.locales)
|
|
||||||
if (localeKeys.length) {
|
|
||||||
return !localeKeys.some((key) => key === item.value.link)
|
|
||||||
}
|
|
||||||
return item.value.link !== '/'
|
|
||||||
})
|
|
||||||
|
|
||||||
const isActiveInSubpath = computed(() => {
|
|
||||||
if (!shouldBeActiveInSubpath.value) return false
|
|
||||||
return route.path.startsWith(item.value.link)
|
|
||||||
})
|
|
||||||
|
|
||||||
const isActive = computed(() => {
|
|
||||||
if (isRouterLink.value) return false
|
|
||||||
if (item.value.activeMatch) {
|
|
||||||
return new RegExp(item.value.activeMatch).test(route.path)
|
|
||||||
}
|
|
||||||
return isActiveInSubpath.value
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<RouterLink
|
|
||||||
v-if="isRouterLink"
|
|
||||||
:class="{ 'router-link-active': isActive }"
|
|
||||||
:to="item.link"
|
|
||||||
:aria-label="linkAriaLabel"
|
|
||||||
v-bind="$attrs"
|
|
||||||
>
|
|
||||||
<slot name="before" />
|
|
||||||
{{ item.text }}
|
|
||||||
<slot name="after" />
|
|
||||||
</RouterLink>
|
|
||||||
<a
|
|
||||||
v-else
|
|
||||||
class="external-link"
|
|
||||||
:href="item.link"
|
|
||||||
:rel="linkRel"
|
|
||||||
:target="linkTarget"
|
|
||||||
:aria-label="linkAriaLabel"
|
|
||||||
v-bind="$attrs"
|
|
||||||
>
|
|
||||||
<slot name="before" />
|
|
||||||
{{ item.text }}
|
|
||||||
<ExternalLinkIcon v-if="isBlankTarget" />
|
|
||||||
<slot name="after" />
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { debounce } from 'ts-debounce'
|
|
||||||
import { onMounted, ref } from 'vue'
|
|
||||||
import { getScrollTop, scrollTo } from '../utils/index.js'
|
|
||||||
import { BackTopIcon } from './icons/index.js'
|
|
||||||
|
|
||||||
const opacity = ref<number>(0)
|
|
||||||
const MAX_TOP = 300
|
|
||||||
|
|
||||||
const canShow = debounce((): void => {
|
|
||||||
opacity.value = getScrollTop(document) >= MAX_TOP ? 1 : 0
|
|
||||||
})
|
|
||||||
|
|
||||||
const scrollToTop = (): void => {
|
|
||||||
scrollTo(document, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (__VUEPRESS_SSR__) return
|
|
||||||
canShow()
|
|
||||||
window.addEventListener('scroll', () => canShow(), false)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="btn-back-top" :style="{ opacity: opacity }" @click="scrollToTop">
|
|
||||||
<BackTopIcon />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/variables';
|
|
||||||
.btn-back-top {
|
|
||||||
position: fixed;
|
|
||||||
right: 3rem;
|
|
||||||
bottom: 2.1rem;
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0.75rem 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
box-shadow: var(--shadow-md);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: opacity var(--t-color);
|
|
||||||
z-index: 99;
|
|
||||||
|
|
||||||
.back-top-icon {
|
|
||||||
width: 1.75rem;
|
|
||||||
height: 1.75rem;
|
|
||||||
color: var(--c-brand);
|
|
||||||
transition: color var(--t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.back-top-icon {
|
|
||||||
color: var(--c-brand-light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.btn-back-top {
|
|
||||||
right: 1.25rem;
|
|
||||||
bottom: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useThemeLocaleData } from '../composables/index.js'
|
|
||||||
import BloggerInfo from './BloggerInfo.vue'
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
|
|
||||||
const showInfo = computed(() => {
|
|
||||||
const avatar = themeLocale.value.avatar || {}
|
|
||||||
return avatar.name || avatar.url
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<aside v-if="showInfo" class="blog-info-wrapper">
|
|
||||||
<BloggerInfo />
|
|
||||||
</aside>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/variables';
|
|
||||||
.blog-info-wrapper {
|
|
||||||
width: 18.75rem;
|
|
||||||
margin-left: 1.25rem;
|
|
||||||
position: sticky;
|
|
||||||
top: calc(var(--navbar-height) + 1.25rem);
|
|
||||||
border-left: solid 1px var(--c-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.blog-info-wrapper {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,195 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import { isLinkHttp, isLinkMailto } from '@vuepress/shared'
|
|
||||||
import type { FunctionalComponent, Ref } from 'vue'
|
|
||||||
import { computed, ref } from 'vue'
|
|
||||||
import { usePostStat, useThemeLocaleData } from '../composables/index.js'
|
|
||||||
import {
|
|
||||||
EmailIcon,
|
|
||||||
FacebookIcon,
|
|
||||||
FolderIcon,
|
|
||||||
GithubIcon,
|
|
||||||
LinkedinIcon,
|
|
||||||
PostIcon,
|
|
||||||
QQIcon,
|
|
||||||
TagIcon,
|
|
||||||
TwitterIcon,
|
|
||||||
WeiBoIcon,
|
|
||||||
ZhiHuIcon,
|
|
||||||
} from './icons/index.js'
|
|
||||||
|
|
||||||
interface SocialItem {
|
|
||||||
url: string
|
|
||||||
icon: FunctionalComponent
|
|
||||||
}
|
|
||||||
type SocialData = SocialItem[]
|
|
||||||
type SocialRef = Ref<SocialData>
|
|
||||||
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
const avatar = computed(() => themeLocale.value.avatar || {})
|
|
||||||
|
|
||||||
const useSocialList = (): SocialRef => {
|
|
||||||
const list: SocialRef = ref([])
|
|
||||||
const social = themeLocale.value.social || {}
|
|
||||||
if (social.QQ) {
|
|
||||||
const url = isLinkHttp(social.QQ)
|
|
||||||
? social.QQ
|
|
||||||
: `https://wpa.qq.com/msgrd?v=3&uin=${social.QQ}&site=qq&menu=yes`
|
|
||||||
list.value.push({ url, icon: QQIcon })
|
|
||||||
}
|
|
||||||
if (social.email) {
|
|
||||||
const url = isLinkMailto(social.email)
|
|
||||||
? social.email
|
|
||||||
: `mailto:${social.email}`
|
|
||||||
list.value.push({ url, icon: EmailIcon })
|
|
||||||
}
|
|
||||||
if (social.github) {
|
|
||||||
const url = isLinkHttp(social.github)
|
|
||||||
? social.github
|
|
||||||
: `https://github.com/${social.github}`
|
|
||||||
list.value.push({ url, icon: GithubIcon })
|
|
||||||
}
|
|
||||||
if (social.linkedin) {
|
|
||||||
list.value.push({ url: social.linkedin, icon: LinkedinIcon })
|
|
||||||
}
|
|
||||||
if (social.weiBo) {
|
|
||||||
list.value.push({ url: social.weiBo, icon: WeiBoIcon })
|
|
||||||
}
|
|
||||||
if (social.zhiHu) {
|
|
||||||
list.value.push({ url: social.zhiHu, icon: ZhiHuIcon })
|
|
||||||
}
|
|
||||||
if (social.facebook) {
|
|
||||||
list.value.push({ url: social.facebook, icon: FacebookIcon })
|
|
||||||
}
|
|
||||||
if (social.twitter) {
|
|
||||||
list.value.push({ url: social.twitter, icon: TwitterIcon })
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
const socialList = useSocialList()
|
|
||||||
|
|
||||||
const postStat = usePostStat()
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<DropdownTransition>
|
|
||||||
<section class="blogger-info">
|
|
||||||
<div class="blogger-profile">
|
|
||||||
<p v-if="avatar.url" class="avatar-img">
|
|
||||||
<img :src="avatar.url" :alt="avatar.name" />
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
<h3>{{ avatar.name }}</h3>
|
|
||||||
<p>{{ avatar.description }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="blogger-social">
|
|
||||||
<a
|
|
||||||
v-for="item in socialList"
|
|
||||||
:key="item.url"
|
|
||||||
target="_blank"
|
|
||||||
:href="item.url"
|
|
||||||
>
|
|
||||||
<Component :is="item.icon" />
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<div class="post-stat">
|
|
||||||
<div class="post-stat-item">
|
|
||||||
<PostIcon />
|
|
||||||
<span>{{ postStat.postTotal }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="post-stat-item">
|
|
||||||
<FolderIcon />
|
|
||||||
<span>{{ postStat.categoryTotal }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="post-stat-item">
|
|
||||||
<TagIcon />
|
|
||||||
<span>{{ postStat.tagTotal }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</DropdownTransition>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
.blogger-info {
|
|
||||||
padding: 1.25rem;
|
|
||||||
// border-radius: var(--p-around);
|
|
||||||
// background-color: var(--c-bg-container);
|
|
||||||
// box-shadow: var(--shadow);
|
|
||||||
|
|
||||||
.blogger-profile {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-img {
|
|
||||||
width: 30%;
|
|
||||||
padding-right: 0.8rem;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
h3 {
|
|
||||||
text-align: left;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blogger-social {
|
|
||||||
vertical-align: middle;
|
|
||||||
text-align: center;
|
|
||||||
a {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0.5rem 0.15rem 0;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.email-icon,
|
|
||||||
.github-icon,
|
|
||||||
.weiBo-icon {
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-stat {
|
|
||||||
// display: flex;
|
|
||||||
display: none;
|
|
||||||
justify-content: space-around;
|
|
||||||
align-items: center;
|
|
||||||
border-top: 1px solid var(--c-border);
|
|
||||||
margin-top: 1.75rem;
|
|
||||||
padding-top: 1rem;
|
|
||||||
|
|
||||||
.post-stat-item {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--c-text-quote);
|
|
||||||
.icon {
|
|
||||||
width: 2rem;
|
|
||||||
height: 2rem;
|
|
||||||
color: var(--c-text-lightest);
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import BlogInfo from '@theme-plume/BlogInfo.vue'
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import { useCategoryList } from '../composables/index.js'
|
|
||||||
import CategoryGroup from './CategoryGroup.vue'
|
|
||||||
|
|
||||||
const categoryList = useCategoryList()
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="category-wrapper">
|
|
||||||
<div class="category-container">
|
|
||||||
<DropdownTransition>
|
|
||||||
<div class="category-content">
|
|
||||||
<CategoryGroup
|
|
||||||
v-for="(category, index) in categoryList"
|
|
||||||
:key="category.type + '_' + index"
|
|
||||||
:category="category"
|
|
||||||
:index="index"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</DropdownTransition>
|
|
||||||
<BlogInfo></BlogInfo>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_mixins';
|
|
||||||
|
|
||||||
.category-wrapper {
|
|
||||||
@include wrapper;
|
|
||||||
|
|
||||||
.category-container {
|
|
||||||
@include container_wrapper;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 1.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import type { FunctionalComponent, PropType } from 'vue'
|
|
||||||
import { h } from 'vue'
|
|
||||||
import type { CategoryItem } from '../composables/index.js'
|
|
||||||
const props = defineProps({
|
|
||||||
category: {
|
|
||||||
type: Object as PropType<CategoryItem>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
head: {
|
|
||||||
type: Number,
|
|
||||||
default: 2,
|
|
||||||
},
|
|
||||||
index: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const Heading: FunctionalComponent = () => {
|
|
||||||
const head = props.head > 4 ? 4 : props.head
|
|
||||||
return h(
|
|
||||||
`h${head}`,
|
|
||||||
{ id: props.category.label.trim().replace(/\s+/g, '-') },
|
|
||||||
[props.category.label]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<DropdownTransition :delay="index * 0.04">
|
|
||||||
<section class="category-group-wrapper">
|
|
||||||
<Heading />
|
|
||||||
<ul class="category-list">
|
|
||||||
<li
|
|
||||||
v-for="post in category.postList"
|
|
||||||
:key="post.path"
|
|
||||||
class="category-item"
|
|
||||||
>
|
|
||||||
<span>[{{ post.createTime }}]</span>
|
|
||||||
<RouterLink :to="post.path">{{ post.title }}</RouterLink>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<CategoryGroup
|
|
||||||
v-for="(cate, i) in category.children"
|
|
||||||
:key="cate.type + '__' + i"
|
|
||||||
:category="cate"
|
|
||||||
:head="head + 1"
|
|
||||||
:index="i"
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
</DropdownTransition>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
.category-group-wrapper {
|
|
||||||
padding: 1.25rem 1.5rem;
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
border-radius: var(--p-around);
|
|
||||||
margin-bottom: 1.25rem;
|
|
||||||
// box-shadow: var(--shadow);
|
|
||||||
transition: box-shadow var(--t-color);
|
|
||||||
|
|
||||||
.category-group-wrapper {
|
|
||||||
box-shadow: none;
|
|
||||||
border-radius: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin-left: 1.25rem;
|
|
||||||
border-bottom: solid 1px var(--c-border);
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-group-wrapper {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-list {
|
|
||||||
list-style: none;
|
|
||||||
padding-left: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-item {
|
|
||||||
span {
|
|
||||||
color: var(--c-text-lighter);
|
|
||||||
margin-right: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--c-text);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useDarkMode, useThemeLocaleData } from '../composables/index.js'
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
const isDarkMode = useDarkMode()
|
|
||||||
const toggleDarkMode = (): void => {
|
|
||||||
isDarkMode.value = !isDarkMode.value
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<button
|
|
||||||
class="toggle-dark-button"
|
|
||||||
:title="themeLocale.toggleDarkMode"
|
|
||||||
@click="toggleDarkMode"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
v-show="!isDarkMode"
|
|
||||||
class="icon"
|
|
||||||
focusable="false"
|
|
||||||
viewBox="0 0 32 32"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M16 12.005a4 4 0 1 1-4 4a4.005 4.005 0 0 1 4-4m0-2a6 6 0 1 0 6 6a6 6 0 0 0-6-6z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5.394 6.813l1.414-1.415l3.506 3.506L8.9 10.318z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path d="M2 15.005h5v2H2z" fill="currentColor" />
|
|
||||||
<path
|
|
||||||
d="M5.394 25.197L8.9 21.691l1.414 1.415l-3.506 3.505z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path d="M15 25.005h2v5h-2z" fill="currentColor" />
|
|
||||||
<path
|
|
||||||
d="M21.687 23.106l1.414-1.415l3.506 3.506l-1.414 1.414z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path d="M25 15.005h5v2h-5z" fill="currentColor" />
|
|
||||||
<path
|
|
||||||
d="M21.687 8.904l3.506-3.506l1.414 1.415l-3.506 3.505z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path d="M15 2.005h2v5h-2z" fill="currentColor" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<svg v-show="isDarkMode" class="icon" focusable="false" viewBox="0 0 32 32">
|
|
||||||
<path
|
|
||||||
d="M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.toggle-dark-button {
|
|
||||||
display: flex;
|
|
||||||
margin-left: 1rem;
|
|
||||||
border: 0;
|
|
||||||
background: none;
|
|
||||||
color: var(--c-text);
|
|
||||||
opacity: 0.8;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { defineComponent, Transition, TransitionGroup } from 'vue'
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'DropdownTransition',
|
|
||||||
components: {
|
|
||||||
Transition,
|
|
||||||
TransitionGroup,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
type: { type: String as PropType<'single' | 'group'>, default: 'single' },
|
|
||||||
delay: { type: Number, default: 0 },
|
|
||||||
duration: { type: Number, default: 0.25 },
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const setStyle = (item: Element): void => {
|
|
||||||
;(
|
|
||||||
item as HTMLElement
|
|
||||||
).style.transition = `transform ${props.duration}s ease-in-out ${props.delay}s, opacity ${props.duration}s ease-in-out ${props.delay}s`
|
|
||||||
;(item as HTMLElement).style.transform = 'translateY(-20px)'
|
|
||||||
;(item as HTMLElement).style.opacity = '0'
|
|
||||||
}
|
|
||||||
const unsetStyle = (item: Element): void => {
|
|
||||||
;(item as HTMLElement).style.transform = 'translateY(0)'
|
|
||||||
;(item as HTMLElement).style.opacity = '1'
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
setStyle,
|
|
||||||
unsetStyle,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<Transition
|
|
||||||
v-if="type === 'single'"
|
|
||||||
name="drop"
|
|
||||||
appear
|
|
||||||
@appear="setStyle"
|
|
||||||
@after-appear="unsetStyle"
|
|
||||||
@enter="setStyle"
|
|
||||||
@after-enter="unsetStyle"
|
|
||||||
@before-leave="setStyle"
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</Transition>
|
|
||||||
<TransitionGroup
|
|
||||||
v-if="type === 'group'"
|
|
||||||
name="drop"
|
|
||||||
appear
|
|
||||||
@appear="setStyle"
|
|
||||||
@after-appear="unsetStyle"
|
|
||||||
@enter="setStyle"
|
|
||||||
@after-enter="unsetStyle"
|
|
||||||
@before-leave="setStyle"
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</TransitionGroup>
|
|
||||||
</template>
|
|
||||||
@ -1,28 +1,3 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import BlogInfo from '@theme-plume/BlogInfo.vue'
|
|
||||||
import HomeBigBanner from '@theme-plume/HomeBigBanner.vue'
|
|
||||||
import PostList from '@theme-plume/PostList.vue'
|
|
||||||
</script>
|
|
||||||
<template>
|
<template>
|
||||||
<div class="plume-theme-home">
|
<div>home</div>
|
||||||
<HomeBigBanner></HomeBigBanner>
|
|
||||||
<div class="plume-theme-container">
|
|
||||||
<PostList></PostList>
|
|
||||||
<BlogInfo></BlogInfo>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_mixins';
|
|
||||||
.plume-theme-home {
|
|
||||||
@include wrapper;
|
|
||||||
|
|
||||||
.plume-theme-container {
|
|
||||||
@include container_wrapper;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 1.25rem 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -1,166 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { usePageFrontmatter, withBase } from '@vuepress/client'
|
|
||||||
import { isLinkHttp } from '@vuepress/shared'
|
|
||||||
import { computed, onMounted, ref } from 'vue'
|
|
||||||
import type { PlumeThemeHomeFrontmatter } from '../../shared/index.js'
|
|
||||||
import { useThemeLocaleData } from '../composables/index.js'
|
|
||||||
import { scrollTo } from '../utils/index.js'
|
|
||||||
import { ArrowBottomIcon } from './icons/index.js'
|
|
||||||
|
|
||||||
const frontmatter = usePageFrontmatter<PlumeThemeHomeFrontmatter>()
|
|
||||||
const MOBILE_WIDTH = 716
|
|
||||||
|
|
||||||
const bannerImg = ref(frontmatter.value.banner || '')
|
|
||||||
const hasBanner = computed(() => !!bannerImg.value)
|
|
||||||
const bannerStyle = computed(() => {
|
|
||||||
if (!hasBanner.value) return ''
|
|
||||||
const url = isLinkHttp(bannerImg.value)
|
|
||||||
? bannerImg.value
|
|
||||||
: withBase(bannerImg.value)
|
|
||||||
return {
|
|
||||||
'background-image': `url(${url})`,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleResize(): void {
|
|
||||||
if (__VUEPRESS_SSR__) return
|
|
||||||
const width = document.documentElement.offsetWidth
|
|
||||||
if (!hasBanner.value) return
|
|
||||||
if (width < MOBILE_WIDTH) {
|
|
||||||
bannerImg.value =
|
|
||||||
frontmatter.value.mobileBanner || frontmatter.value.banner || ''
|
|
||||||
} else {
|
|
||||||
bannerImg.value = frontmatter.value.banner || ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onMounted(() => {
|
|
||||||
handleResize()
|
|
||||||
window.addEventListener('resize', handleResize, false)
|
|
||||||
window.addEventListener('orientationchange', handleResize, false)
|
|
||||||
})
|
|
||||||
let screenHeight = 0
|
|
||||||
const arrowHandle = (): void => {
|
|
||||||
if (!screenHeight) {
|
|
||||||
screenHeight =
|
|
||||||
document.documentElement.clientHeight || document.body.clientHeight
|
|
||||||
screenHeight -=
|
|
||||||
document.querySelector<HTMLElement>('.navbar-wrapper')?.offsetHeight || 0
|
|
||||||
}
|
|
||||||
scrollTo(document, screenHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
const avatar = themeLocale.value.avatar || {}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div v-if="hasBanner" class="home-big-banner-wrapper" :style="bannerStyle">
|
|
||||||
<ArrowBottomIcon @click="arrowHandle" />
|
|
||||||
<div class="home-blogger-info">
|
|
||||||
<div class="blogger-img">
|
|
||||||
<img :src="avatar.url" :alt="avatar.name" />
|
|
||||||
</div>
|
|
||||||
<h3>{{ avatar.name }}</h3>
|
|
||||||
<p v-if="frontmatter.motto" class="blogger-motto">
|
|
||||||
{{ frontmatter.motto }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_variables';
|
|
||||||
|
|
||||||
.home-big-banner-wrapper {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100vh - var(--navbar-height));
|
|
||||||
background-color: transparent;
|
|
||||||
background-position: 0 0;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-attachment: fixed;
|
|
||||||
|
|
||||||
.arrow-bottom-icon {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1.25rem;
|
|
||||||
left: 50%;
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
color: var(--c-home-arrow-bottom);
|
|
||||||
animation: home-banner-arrow 1.5s ease 0.3s infinite;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-blogger-info {
|
|
||||||
margin: auto;
|
|
||||||
text-align: center;
|
|
||||||
.blogger-img {
|
|
||||||
width: 15rem;
|
|
||||||
height: 15rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 1.25rem;
|
|
||||||
background-color: rgba(0, 0, 0, 0.25);
|
|
||||||
margin: auto;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 4rem;
|
|
||||||
max-width: var(--content-width);
|
|
||||||
color: rgba(255, 255, 255, 0.85);
|
|
||||||
padding: 0 1.25rem;
|
|
||||||
margin: 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blogger-motto {
|
|
||||||
max-width: var(--content-width);
|
|
||||||
font-size: 2rem;
|
|
||||||
color: rgba(255, 255, 255, 0.75);
|
|
||||||
padding: 0 1.25rem;
|
|
||||||
border-radius: var(--p-around);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.home-big-banner-wrapper .home-blogger-info {
|
|
||||||
.blogger-img {
|
|
||||||
width: 50vw;
|
|
||||||
height: 50vw;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 3rem;
|
|
||||||
}
|
|
||||||
.blogger-motto {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes home-banner-arrow {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(-50%) translateY(-10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
10% {
|
|
||||||
opacity: 0.45;
|
|
||||||
}
|
|
||||||
|
|
||||||
95% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(-50%) translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
opacity: 0.25;
|
|
||||||
transform: translateX(-50%) translateY(-7px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import DarkModeButton from '@theme-plume/DarkModeButton.vue'
|
|
||||||
import NavbarBrand from '@theme-plume/NavbarBrand.vue'
|
|
||||||
import NavbarItems from '@theme-plume/NavbarItems.vue'
|
|
||||||
import ToggleSidebarButton from '@theme-plume/ToggleSidebarButton.vue'
|
|
||||||
import { computed, onMounted, ref } from 'vue'
|
|
||||||
import { useAsideNavbar, useThemeLocaleData } from '../composables/index.js'
|
|
||||||
import { getCssValue } from '../utils/index.js'
|
|
||||||
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
|
|
||||||
const { triggerAsideNavbar } = useAsideNavbar()
|
|
||||||
|
|
||||||
const navbar = ref<HTMLElement | null>(null)
|
|
||||||
const navbarBrand = ref<HTMLElement | null>(null)
|
|
||||||
|
|
||||||
const linksWrapperMaxWith = ref(0)
|
|
||||||
const linksWrapperStyle = computed(() => {
|
|
||||||
if (!linksWrapperMaxWith.value) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
maxWidth: linksWrapperMaxWith.value + 'px',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const enableDarkMode = computed(() => themeLocale.value.darkMode)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const MOBILE_DESKTOP_BREAKPOINT = 719
|
|
||||||
const navbarHorizontalPadding =
|
|
||||||
getCssValue(navbar.value, 'paddingLeft') +
|
|
||||||
getCssValue(navbar.value, 'paddingRight')
|
|
||||||
const handleLinkWrapWidth = (): void => {
|
|
||||||
if (window.innerWidth <= MOBILE_DESKTOP_BREAKPOINT) {
|
|
||||||
linksWrapperMaxWith.value = 0
|
|
||||||
} else {
|
|
||||||
linksWrapperMaxWith.value =
|
|
||||||
navbar.value!.offsetWidth -
|
|
||||||
navbarHorizontalPadding -
|
|
||||||
(navbarBrand.value?.offsetWidth || 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handleLinkWrapWidth()
|
|
||||||
window.addEventListener('resize', handleLinkWrapWidth, false)
|
|
||||||
window.addEventListener('orientationchange', handleLinkWrapWidth, false)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<header ref="navbar" class="navbar-wrapper">
|
|
||||||
<ToggleSidebarButton @toggle="triggerAsideNavbar(true)" />
|
|
||||||
<span ref="navbarBrand" class="navbar-brand-wrapper">
|
|
||||||
<NavbarBrand />
|
|
||||||
</span>
|
|
||||||
<div class="navbar-items-wrapper" :style="linksWrapperStyle">
|
|
||||||
<slot name="before" />
|
|
||||||
<div class="navbar-item-search">
|
|
||||||
<NavbarSearch />
|
|
||||||
</div>
|
|
||||||
<NavbarItems class="can-hide" is-header />
|
|
||||||
<slot name="after" />
|
|
||||||
<DarkModeButton v-if="enableDarkMode" />
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/variables';
|
|
||||||
.navbar-wrapper {
|
|
||||||
--navbar-line-height: calc(
|
|
||||||
var(--navbar-height) - 2 * var(--navbar-padding-v)
|
|
||||||
);
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 10;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: var(--navbar-height);
|
|
||||||
padding: 0 0 0 var(--navbar-padding-h);
|
|
||||||
background-color: var(--c-bg-navbar);
|
|
||||||
backdrop-filter: saturate(50%) blur(8px);
|
|
||||||
// box-shadow: var(--shadow);
|
|
||||||
line-height: var(--navbar-line-height);
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
|
|
||||||
.navbar-brand-wrapper {
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.logo {
|
|
||||||
height: var(--navbar-line-height);
|
|
||||||
margin-right: var(--navbar-padding-v);
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-name {
|
|
||||||
font-size: 1.3rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--c-text);
|
|
||||||
position: relative;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-items-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
height: 100%;
|
|
||||||
padding-right: var(--navbar-padding-h);
|
|
||||||
|
|
||||||
.navbar-item-search {
|
|
||||||
flex-grow: 1;
|
|
||||||
padding-left: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-items .navbar-item {
|
|
||||||
& > .router-link-active {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.DocSearch {
|
|
||||||
transition: background-color var(--t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.navbar-wrapper {
|
|
||||||
padding-left: 4rem;
|
|
||||||
|
|
||||||
.can-hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-name {
|
|
||||||
width: calc(100vw - 9.4rem);
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-items-wrapper {
|
|
||||||
justify-content: flex-end;
|
|
||||||
.navbar-item-search {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import {
|
|
||||||
ClientOnly,
|
|
||||||
useRouteLocale,
|
|
||||||
useSiteLocaleData,
|
|
||||||
withBase,
|
|
||||||
} from '@vuepress/client'
|
|
||||||
import { computed, h } from 'vue'
|
|
||||||
import type { FunctionalComponent } from 'vue'
|
|
||||||
import type { NavLink } from '../../shared/index.js'
|
|
||||||
import {
|
|
||||||
useDarkMode,
|
|
||||||
useSidebarIndex,
|
|
||||||
useThemeLocaleData,
|
|
||||||
} from '../composables/index.js'
|
|
||||||
|
|
||||||
const routeLocale = useRouteLocale()
|
|
||||||
const siteLocale = useSiteLocaleData()
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
const isDarkMode = useDarkMode()
|
|
||||||
const { hasSidebar } = useSidebarIndex()
|
|
||||||
|
|
||||||
const navbarBrandLink = computed(
|
|
||||||
() => (themeLocale.value.home as NavLink)?.link || routeLocale.value
|
|
||||||
)
|
|
||||||
const navbarBrandTitle = computed(() => siteLocale.value.title)
|
|
||||||
const navbarBrandLogo = computed(() => {
|
|
||||||
if (isDarkMode.value && themeLocale.value.logoDark !== undefined) {
|
|
||||||
return themeLocale.value.logoDark
|
|
||||||
}
|
|
||||||
return themeLocale.value.logo
|
|
||||||
})
|
|
||||||
const NavbarBrandLogo: FunctionalComponent = () => {
|
|
||||||
if (!navbarBrandLogo.value) return null
|
|
||||||
const img = h('img', {
|
|
||||||
class: 'logo',
|
|
||||||
src: withBase(navbarBrandLogo.value),
|
|
||||||
alt: navbarBrandTitle.value,
|
|
||||||
})
|
|
||||||
if (themeLocale.value.logoDark === undefined) {
|
|
||||||
return img
|
|
||||||
}
|
|
||||||
return h(ClientOnly, img)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<RouterLink
|
|
||||||
:to="navbarBrandLink"
|
|
||||||
:class="{
|
|
||||||
'navbar-brand': true,
|
|
||||||
'has-sidebar': hasSidebar,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<NavbarBrandLogo />
|
|
||||||
<span
|
|
||||||
v-if="navbarBrandTitle"
|
|
||||||
class="site-name"
|
|
||||||
:class="{ 'can-hide': navbarBrandLogo }"
|
|
||||||
>
|
|
||||||
{{ navbarBrandTitle }}
|
|
||||||
</span>
|
|
||||||
</RouterLink>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/variables';
|
|
||||||
.navbar-brand {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&.has-sidebar {
|
|
||||||
width: calc(18rem - var(--navbar-padding-h));
|
|
||||||
border-bottom: solid 1px var(--c-border);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.navbar-brand.has-sidebar {
|
|
||||||
width: auto;
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,334 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import AutoLink from '@theme-plume/AutoLink.vue'
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import { computed, ref, toRefs, watch } from 'vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
||||||
import type {
|
|
||||||
NavbarItem,
|
|
||||||
NavGroup,
|
|
||||||
ResolveNavbarItem,
|
|
||||||
} from '../../shared/index.js'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
item: {
|
|
||||||
type: Object as PropType<Exclude<ResolveNavbarItem, NavbarItem>>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isHeader: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const { item } = toRefs(props)
|
|
||||||
|
|
||||||
const dropdownAriaLabel = computed(
|
|
||||||
() => item.value.ariaLabel || item.value.text
|
|
||||||
)
|
|
||||||
const open = ref(false)
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => route.path,
|
|
||||||
() => {
|
|
||||||
open.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleDropdown = (e: MouseEvent): void => {
|
|
||||||
const isTriggerByTab = e.detail === 0
|
|
||||||
if (isTriggerByTab || props.isHeader) {
|
|
||||||
open.value = !open.value
|
|
||||||
} else {
|
|
||||||
open.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const isLastItemOfArray = (item: unknown, arr: unknown[]): boolean =>
|
|
||||||
arr[arr.length - 1] === item
|
|
||||||
|
|
||||||
const onSubTitleFocusout = (child: any): void => {
|
|
||||||
if (
|
|
||||||
isLastItemOfArray(child, item.value.children) &&
|
|
||||||
child.children &&
|
|
||||||
child.children.length === 0
|
|
||||||
) {
|
|
||||||
open.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onGrandChildFocusout = (grandchild: unknown, child: any): void => {
|
|
||||||
if (
|
|
||||||
isLastItemOfArray(grandchild, child.children) &&
|
|
||||||
isLastItemOfArray(child, item.value.children)
|
|
||||||
) {
|
|
||||||
open.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="navbar-dropdown-wrapper"
|
|
||||||
:class="{ open }"
|
|
||||||
@mouseleave="open = false"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
v-if="isHeader"
|
|
||||||
class="navbar-dropdown-title"
|
|
||||||
type="button"
|
|
||||||
:aria-label="dropdownAriaLabel"
|
|
||||||
@click="handleDropdown"
|
|
||||||
@mouseenter="open = true"
|
|
||||||
>
|
|
||||||
<span class="title">{{ item.text }}</span>
|
|
||||||
<span class="arrow down"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
v-else
|
|
||||||
class="navbar-dropdown-title-mobile"
|
|
||||||
type="button"
|
|
||||||
:aria-label="dropdownAriaLabel"
|
|
||||||
@click="open = !open"
|
|
||||||
>
|
|
||||||
<span class="title">{{ item.text }}</span>
|
|
||||||
<span class="arrow" :class="open ? 'down' : 'right'"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<DropdownTransition>
|
|
||||||
<ul v-show="open" class="navbar-dropdown">
|
|
||||||
<li
|
|
||||||
v-for="child in item.children"
|
|
||||||
:key="child.text"
|
|
||||||
class="navbar-dropdown-item"
|
|
||||||
>
|
|
||||||
<template v-if="(child as NavGroup<NavbarItem>).children">
|
|
||||||
<h4 class="navbar-dropdown-subtitle">
|
|
||||||
<AutoLink
|
|
||||||
v-if="(child as NavbarItem).link"
|
|
||||||
:item="(child as NavbarItem)"
|
|
||||||
@focusout="onSubTitleFocusout(child)"
|
|
||||||
/>
|
|
||||||
<span v-else>{{ child.text }}</span>
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<ul class="navbar-dropdown-subitem-wrapper">
|
|
||||||
<li
|
|
||||||
v-for="grandchild in (child as unknown as NavGroup<NavbarItem>).children"
|
|
||||||
:key="grandchild.link"
|
|
||||||
class="navbar-dropdown-subitem"
|
|
||||||
>
|
|
||||||
<AutoLink
|
|
||||||
:item="grandchild"
|
|
||||||
@focusout="onGrandChildFocusout(grandchild, child)"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<AutoLink
|
|
||||||
:item="(child as NavbarItem)"
|
|
||||||
@focusout="
|
|
||||||
isLastItemOfArray(child, item.children) && (open = false)
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</DropdownTransition>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_variables';
|
|
||||||
@import '../styles/_mixins';
|
|
||||||
|
|
||||||
.navbar-dropdown-wrapper {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.navbar-dropdown-title {
|
|
||||||
display: block;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-family: inherit;
|
|
||||||
cursor: inherit;
|
|
||||||
padding: inherit;
|
|
||||||
line-height: 1.4rem;
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--c-text);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow {
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-top: -1px;
|
|
||||||
margin-left: 0.4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown-title-mobile {
|
|
||||||
@extend .navbar-dropdown-title;
|
|
||||||
display: none;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: inherit;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown {
|
|
||||||
list-style: none;
|
|
||||||
.navbar-dropdown-item {
|
|
||||||
color: inherit;
|
|
||||||
line-height: 1.7rem;
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
.navbar-dropdown-subtitle {
|
|
||||||
margin: 0.45rem 0 0;
|
|
||||||
border-top: 1px solid var(--c-border);
|
|
||||||
padding: 1rem 0 0.45rem 0;
|
|
||||||
font-size: 1rem;
|
|
||||||
color: var(--c-text-light);
|
|
||||||
|
|
||||||
& > span {
|
|
||||||
padding: 0 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > a {
|
|
||||||
font-weight: inherit;
|
|
||||||
|
|
||||||
&.router-link-active {
|
|
||||||
&::after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown-subitem-wrapper {
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
.navbar-dropdown-subitem {
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
line-height: 1.7rem;
|
|
||||||
position: relative;
|
|
||||||
border-bottom: none;
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding: 0 1.5rem 0 1.25rem;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.router-link-active {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-left: 5px solid var(--c-text-accent);
|
|
||||||
border-top: 3px solid transparent;
|
|
||||||
border-bottom: 3px solid transparent;
|
|
||||||
position: absolute;
|
|
||||||
top: calc(50% - 2px);
|
|
||||||
left: 9px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child .navbar-dropdown-subtitle {
|
|
||||||
margin-top: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
border-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.navbar-dropdown-wrapper {
|
|
||||||
&.open .navbar-dropdown-title {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown-title {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown-title-mobile {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown {
|
|
||||||
@include dropdown_wrapper;
|
|
||||||
|
|
||||||
.navbar-dropdown-item {
|
|
||||||
.navbar-dropdown-subtitle {
|
|
||||||
padding-top: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
border-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown-subtitle,
|
|
||||||
& > a {
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown-subitem {
|
|
||||||
font-size: 1rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: ($MQMobile + 1)) {
|
|
||||||
.navbar-dropdown-wrapper {
|
|
||||||
height: 1.8rem;
|
|
||||||
|
|
||||||
&.open .navbar-dropdown {
|
|
||||||
opacity: 1;
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-dropdown {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-0.5rem);
|
|
||||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
||||||
height: auto !important;
|
|
||||||
max-height: calc(100vh - 2.7rem);
|
|
||||||
overflow-y: auto;
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
right: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
padding: 1.5rem 0.75rem;
|
|
||||||
border: 1px solid var(--c-border);
|
|
||||||
border-bottom-color: var(--c-border-dark);
|
|
||||||
text-align: left;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0;
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import AutoLink from '@theme-plume/AutoLink.vue'
|
|
||||||
import NavbarDropdown from '@theme-plume/NavbarDropdown.vue'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
||||||
import type {
|
|
||||||
NavGroup,
|
|
||||||
NavLink,
|
|
||||||
ResolveNavbarItem,
|
|
||||||
} from '../../shared/index.js'
|
|
||||||
import {
|
|
||||||
useNavbarConfig,
|
|
||||||
useNavbarRepo,
|
|
||||||
useNavbarSelectLanguage,
|
|
||||||
} from '../composables/index.js'
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
isHeader: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const navbarConfig = useNavbarConfig()
|
|
||||||
const navbarSelectLanguage = useNavbarSelectLanguage()
|
|
||||||
const navbarRepo = useNavbarRepo()
|
|
||||||
const navbarLinks = computed(() => [
|
|
||||||
...navbarConfig.value,
|
|
||||||
...navbarSelectLanguage.value,
|
|
||||||
...navbarRepo.value,
|
|
||||||
])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<nav v-if="navbarLinks.length" class="navbar-items">
|
|
||||||
<div v-for="item in navbarLinks" :key="item.text" class="navbar-item">
|
|
||||||
<NavbarDropdown
|
|
||||||
v-if="(item as NavGroup<ResolveNavbarItem>).children"
|
|
||||||
:item="(item as NavGroup<ResolveNavbarItem>)"
|
|
||||||
:is-header="isHeader"
|
|
||||||
/>
|
|
||||||
<AutoLink v-else :item="(item as NavLink)" />
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.navbar-wrapper {
|
|
||||||
.navbar-items {
|
|
||||||
--navbar-line-height: calc(
|
|
||||||
var(--navbar-height) - 2 * var(--navbar-padding-v)
|
|
||||||
);
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: inline-block;
|
|
||||||
line-height: 1.4rem;
|
|
||||||
color: inherit;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&.router-lint-active {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-item {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 1.5rem;
|
|
||||||
line-height: var(--navbar-line-height);
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import PostMeta from '@theme-plume/PostMeta.vue'
|
|
||||||
import Sidebar from '@theme-plume/Sidebar.vue'
|
|
||||||
import { usePageData } from '@vuepress/client'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import type { PlumeThemePageData } from '../../shared/index.js'
|
|
||||||
import { useDarkMode } from '../composables/index.js'
|
|
||||||
import Toc from './Toc.js'
|
|
||||||
|
|
||||||
const page = usePageData<PlumeThemePageData>()
|
|
||||||
const isDarkMode = useDarkMode()
|
|
||||||
|
|
||||||
const isNote = computed(() => {
|
|
||||||
return page.value.isNote || false
|
|
||||||
})
|
|
||||||
|
|
||||||
const enabledSidebar = computed(() => {
|
|
||||||
return isNote.value
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<DropdownTransition>
|
|
||||||
<main class="page-wrapper">
|
|
||||||
<slot name="top"></slot>
|
|
||||||
<div class="page-container" :class="{ 'has-sidebar': enabledSidebar }">
|
|
||||||
<main class="plume-theme-content">
|
|
||||||
<Sidebar v-if="enabledSidebar" />
|
|
||||||
<div class="page-content" :class="{ 'note-content': isNote }">
|
|
||||||
<h1>{{ page.title }}</h1>
|
|
||||||
<PostMeta :post="page" type="post" border />
|
|
||||||
<Content class="post-content" />
|
|
||||||
<div class="comment-container">
|
|
||||||
<Comment :darkmode="isDarkMode" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="page.headers?.length > 0" class="plume-theme-page-toc">
|
|
||||||
<Toc />
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
<slot name="bottom"></slot>
|
|
||||||
</main>
|
|
||||||
</DropdownTransition>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_mixins';
|
|
||||||
@import '../styles/variables';
|
|
||||||
.page-wrapper {
|
|
||||||
@include wrapper;
|
|
||||||
|
|
||||||
.page-container {
|
|
||||||
display: flex;
|
|
||||||
// padding-top: 1.25rem;
|
|
||||||
padding-bottom: 1.25rem;
|
|
||||||
|
|
||||||
.plume-theme-content {
|
|
||||||
@include container_wrapper;
|
|
||||||
@include content;
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-content {
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
max-width: var(--content-width);
|
|
||||||
padding: 0 2rem 1rem;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
&.note-content {
|
|
||||||
max-width: var(--content-note-width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content {
|
|
||||||
padding-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.has-sidebar {
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
|
|
||||||
.plume-theme-content {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.comment-container {
|
|
||||||
margin-top: 8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.page-wrapper .page-container .page-content {
|
|
||||||
padding: 0 0.75rem 1rem;
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.plume-theme-page-toc {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useThemeLocaleData } from '../composables/index.js'
|
|
||||||
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
|
|
||||||
const footer = computed(() => {
|
|
||||||
return themeLocale.value.footer
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<footer v-if="footer" class="theme-plume-footer">
|
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
|
||||||
<div
|
|
||||||
v-if="footer.content"
|
|
||||||
class="theme-plume-footer-content"
|
|
||||||
v-html="footer.content"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
v-if="footer.copyright"
|
|
||||||
class="theme-plume-footer-copyright"
|
|
||||||
v-html="footer.copyright"
|
|
||||||
></div>
|
|
||||||
</footer>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
.theme-plume-footer {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: flex-start;
|
|
||||||
width: 100%;
|
|
||||||
padding: 1.25rem;
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
// box-shadow: var(--shadow-footer);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.theme-plume-footer-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.theme-plume-footer-copyright {
|
|
||||||
margin: auto;
|
|
||||||
padding: 0 1.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,203 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { useOffsetPagination } from '@vueuse/core'
|
|
||||||
import { computed, ref, toRefs } from 'vue'
|
|
||||||
import { ArrowDoubleLeftIcon, ArrowDoubleRightIcon } from './icons/index.js'
|
|
||||||
|
|
||||||
const emit = defineEmits(['togglePage'])
|
|
||||||
const props = defineProps({
|
|
||||||
page: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
total: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const { page, total } = toRefs(props)
|
|
||||||
|
|
||||||
function handlePage({ currentPage }: { currentPage: number }): void {
|
|
||||||
emit('togglePage', currentPage)
|
|
||||||
}
|
|
||||||
const { currentPage, pageCount, isFirstPage, isLastPage, prev, next } =
|
|
||||||
useOffsetPagination({
|
|
||||||
total: total.value,
|
|
||||||
page: page.value,
|
|
||||||
pageSize: 10,
|
|
||||||
onPageChange: handlePage,
|
|
||||||
onPageCountChange: handlePage,
|
|
||||||
})
|
|
||||||
|
|
||||||
const pageList = computed(() => {
|
|
||||||
const list: (number | '')[] = []
|
|
||||||
const count = pageCount.value
|
|
||||||
const current = currentPage.value
|
|
||||||
if (count <= 3 || current <= 2) {
|
|
||||||
new Array(Math.min(3, count)).fill(0).forEach((_, i) => list.push(i + 1))
|
|
||||||
if (count > 3) {
|
|
||||||
list.push('')
|
|
||||||
list.push(count)
|
|
||||||
}
|
|
||||||
} else if (current > count - 2) {
|
|
||||||
list.push(1)
|
|
||||||
list.push('')
|
|
||||||
new Array(3).fill(count - 2).forEach((_, i) => list.push(_ + i))
|
|
||||||
} else {
|
|
||||||
list.push(1)
|
|
||||||
current > 3 && list.push('')
|
|
||||||
;[current - 1, current, current + 1].forEach((page: number) =>
|
|
||||||
list.push(page)
|
|
||||||
)
|
|
||||||
current < count - 2 && list.push('')
|
|
||||||
list.push(count)
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
})
|
|
||||||
|
|
||||||
const inputPage = ref(1)
|
|
||||||
function handleJump(): void {
|
|
||||||
if (
|
|
||||||
inputPage.value &&
|
|
||||||
inputPage.value >= 1 &&
|
|
||||||
inputPage.value <= pageCount.value &&
|
|
||||||
currentPage.value !== Number(inputPage.value)
|
|
||||||
) {
|
|
||||||
currentPage.value = Number(inputPage.value)
|
|
||||||
emit('togglePage', inputPage.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div v-if="pageCount > 1" class="pagination-wrapper">
|
|
||||||
<div class="pagination-container">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn-prev"
|
|
||||||
:disabled="isFirstPage"
|
|
||||||
@click="prev"
|
|
||||||
>
|
|
||||||
<ArrowDoubleLeftIcon />
|
|
||||||
</button>
|
|
||||||
<template v-for="count in pageList" :key="count">
|
|
||||||
<button
|
|
||||||
v-if="count"
|
|
||||||
type="button"
|
|
||||||
:disabled="count === currentPage"
|
|
||||||
@click="currentPage = count"
|
|
||||||
>
|
|
||||||
{{ count }}
|
|
||||||
</button>
|
|
||||||
<button v-else type="button" disabled>..</button>
|
|
||||||
</template>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn-next"
|
|
||||||
:disabled="isLastPage"
|
|
||||||
@click="next"
|
|
||||||
>
|
|
||||||
<ArrowDoubleRightIcon />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="pagination-form can-hide">
|
|
||||||
<span>跳转到:</span>
|
|
||||||
<input v-model="inputPage" type="number" :min="1" :max="pageCount" />
|
|
||||||
<span>/{{ pageCount }}</span>
|
|
||||||
<button type="button" class="btn-jump" @click="handleJump">确认</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/variables';
|
|
||||||
.pagination-wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
|
|
||||||
.pagination-container {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
button {
|
|
||||||
// border-right: solid 1px var(--c-border);
|
|
||||||
|
|
||||||
&:last-of-type {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: inherit;
|
|
||||||
padding: 0 0.8rem;
|
|
||||||
height: 2.125rem;
|
|
||||||
line-height: 2.125rem;
|
|
||||||
border: solid 1px transparent;
|
|
||||||
color: var(--c-text);
|
|
||||||
// box-shadow: var(--shadow-sm);
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
cursor: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.btn-prev,
|
|
||||||
&.btn-next,
|
|
||||||
&.btn-jump {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
color: var(--c-text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.btn-prev,
|
|
||||||
&.btn-next {
|
|
||||||
.icon {
|
|
||||||
width: 0.875rem;
|
|
||||||
height: 0.875rem;
|
|
||||||
vertical-align: text-top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-form {
|
|
||||||
input {
|
|
||||||
font-size: inherit;
|
|
||||||
padding: 0.5rem;
|
|
||||||
width: 3.25rem;
|
|
||||||
height: 2.125rem;
|
|
||||||
line-height: 2.125rem;
|
|
||||||
border: solid 1px transparent;
|
|
||||||
color: var(--c-text);
|
|
||||||
// box-shadow: var(--shadow-sm);
|
|
||||||
outline: 0;
|
|
||||||
background-color: var(--c-bg-light);
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border-color: var(--c-brand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.pagination-wrapper {
|
|
||||||
.can-hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
// .pagination-container {
|
|
||||||
// display: flex;
|
|
||||||
// justify-content: space-between;
|
|
||||||
// padding: 0 2rem;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import type { PostItem } from '../../shared/index.js'
|
|
||||||
import AutoLink from './AutoLink.vue'
|
|
||||||
import { TopIcon } from './icons/index.js'
|
|
||||||
import PostMeta from './PostMeta.vue'
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
post: {
|
|
||||||
type: Object as PropType<PostItem>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
index: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const handlePost = (path: string): void => {
|
|
||||||
router.push({ path })
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<DropdownTransition :delay="index * 0.04">
|
|
||||||
<section :key="post.path" class="post-list-item">
|
|
||||||
<div :class="{ sticky: post.sticky }">
|
|
||||||
<TopIcon v-if="post.sticky" />
|
|
||||||
<!-- <div
|
|
||||||
v-if="post.banner"
|
|
||||||
class="post-banner"
|
|
||||||
@click="handlePost(post.path)"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
:style="{
|
|
||||||
'background-image': `url(${post.banner})`,
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
</div> -->
|
|
||||||
<h3>
|
|
||||||
<AutoLink :item="{ text: post.title, link: post.path }" />
|
|
||||||
</h3>
|
|
||||||
<PostMeta :post="post" :show-author="false" />
|
|
||||||
<!--eslint-disable vue/no-v-html-->
|
|
||||||
<div
|
|
||||||
v-if="post.excerpt"
|
|
||||||
class="post-excerpt"
|
|
||||||
v-html="post.excerpt"
|
|
||||||
></div>
|
|
||||||
<div v-if="post.excerpt" class="post-more">
|
|
||||||
<AutoLink :item="{ text: '阅读全文', link: post.path }" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</DropdownTransition>
|
|
||||||
</template>
|
|
||||||
@ -1,195 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import PostItem from '@theme-plume/PostItem.vue'
|
|
||||||
import { usePageFrontmatter } from '@vuepress/client'
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import { nextTick, onMounted, toRefs, watch } from 'vue'
|
|
||||||
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
|
|
||||||
import type { PlumeThemeHomeFrontmatter } from '../../shared/index.js'
|
|
||||||
import type { PostListData } from '../composables/index.js'
|
|
||||||
import { usePostList } from '../composables/index.js'
|
|
||||||
import { scrollTo } from '../utils/index.js'
|
|
||||||
import Pagination from './Pagination.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
postList: {
|
|
||||||
type: Array as PropType<PostListData | undefined>,
|
|
||||||
required: false,
|
|
||||||
default: () => undefined,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const router = useRouter()
|
|
||||||
const frontmatter = usePageFrontmatter<PlumeThemeHomeFrontmatter>()
|
|
||||||
|
|
||||||
const propsRef = toRefs(props)
|
|
||||||
const { postList, total, page, setPostListPage, resetPostIndex } = usePostList()
|
|
||||||
|
|
||||||
watch(
|
|
||||||
[propsRef.postList],
|
|
||||||
([newPostList]) => {
|
|
||||||
newPostList && resetPostIndex(newPostList as unknown as PostListData)
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
let offsetHeight = 0
|
|
||||||
onBeforeRouteUpdate((to, from) => {
|
|
||||||
if (__VUEPRESS_SSR__) return
|
|
||||||
if (to.fullPath === from.fullPath) return
|
|
||||||
setPostListPage((to.query.p as unknown as number) || 1)
|
|
||||||
const { home, banner, mobileBanner } = frontmatter.value
|
|
||||||
let top = 0
|
|
||||||
if (home && (banner || mobileBanner)) {
|
|
||||||
offsetHeight =
|
|
||||||
offsetHeight ||
|
|
||||||
document.querySelector<HTMLElement>('.navbar-wrapper')?.offsetHeight ||
|
|
||||||
0
|
|
||||||
top = document.documentElement.clientHeight - offsetHeight
|
|
||||||
}
|
|
||||||
setTimeout(() => scrollTo(document, top), 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
if (__VUEPRESS_SSR__) return
|
|
||||||
await nextTick()
|
|
||||||
setPostListPage((route.query.p as unknown as number) || 1)
|
|
||||||
})
|
|
||||||
const togglePage = (currentPage: number): void => {
|
|
||||||
router.push({
|
|
||||||
path: route.path,
|
|
||||||
query: {
|
|
||||||
...route.query,
|
|
||||||
p: currentPage,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="post-list-wrapper">
|
|
||||||
<DropdownTransition>
|
|
||||||
<div>
|
|
||||||
<PostItem
|
|
||||||
v-for="(post, index) in postList"
|
|
||||||
:key="post.path"
|
|
||||||
:post="post"
|
|
||||||
:index="index"
|
|
||||||
></PostItem>
|
|
||||||
</div>
|
|
||||||
</DropdownTransition>
|
|
||||||
<Pagination :page="page" :total="total" @toggle-page="togglePage" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_variables';
|
|
||||||
.post-list-wrapper {
|
|
||||||
flex: 1;
|
|
||||||
padding-top: 2rem;
|
|
||||||
|
|
||||||
.post-list-item {
|
|
||||||
> div {
|
|
||||||
position: relative;
|
|
||||||
padding: 1.25rem 1.5rem;
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
border-radius: var(--p-around);
|
|
||||||
margin-bottom: 2.25rem;
|
|
||||||
// box-shadow: var(--shadow);
|
|
||||||
transition: box-shadow var(--t-color);
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&.sticky {
|
|
||||||
background-color: var(--c-bg-sticky);
|
|
||||||
}
|
|
||||||
|
|
||||||
// &:hover {
|
|
||||||
// box-shadow: var(--shadow-lg);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 2.65rem;
|
|
||||||
height: 2.65rem;
|
|
||||||
color: var(--c-brand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-banner {
|
|
||||||
position: relative;
|
|
||||||
height: 18.75rem;
|
|
||||||
margin: -1.25rem -1.5rem 1.25rem -1.5rem;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
transform: scale(100%);
|
|
||||||
transition: transform var(--t-transform);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(120%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 1.5rem;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border: solid 1.25rem;
|
|
||||||
border-color: transparent transparent var(--c-bg-container) transparent;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--c-text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-excerpt {
|
|
||||||
padding-top: 1.25rem;
|
|
||||||
|
|
||||||
:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
:last-child {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-more {
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
border-radius: var(--p-around);
|
|
||||||
background-color: var(--c-bg);
|
|
||||||
color: var(--c-brand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.post-list-wrapper {
|
|
||||||
.post-list-item {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,126 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
|
||||||
import type { PostItem } from '../../shared/index.js'
|
|
||||||
import { useThemeLocaleData } from '../composables/index.js'
|
|
||||||
import { getColor, normalizePath } from '../utils/index.js'
|
|
||||||
import { ClockIcon, FolderIcon, TagIcon, UserIcon } from './icons/index.js'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
post: {
|
|
||||||
type: Object as PropType<PostItem>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
border: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
showAuthor: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
|
|
||||||
const tags = computed(() => {
|
|
||||||
return (props.post.tags || []).filter((_, i) => i < 4)
|
|
||||||
})
|
|
||||||
|
|
||||||
const category = computed(() => {
|
|
||||||
return (props.post.category || []).filter((cate) =>
|
|
||||||
Boolean(cate.name?.trim())
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleTag = (tag: string): void => {
|
|
||||||
const tagConfig = themeLocale.value.tag
|
|
||||||
if (!tagConfig) return
|
|
||||||
const link = tagConfig.link.replace(/^\/|\/$/g, '')
|
|
||||||
router.replace({
|
|
||||||
path: `/${link}/`,
|
|
||||||
query: { tag: normalizePath(tag) },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="post-meta" :class="{ border: post.excerpt || border }">
|
|
||||||
<div v-if="post.author && showAuthor" class="post-meta-author">
|
|
||||||
<UserIcon />
|
|
||||||
<span>{{ post.author }}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="category.length > 0" class="post-meta-category">
|
|
||||||
<FolderIcon />
|
|
||||||
<template v-for="(cate, i) in category" :key="cate.type">
|
|
||||||
<span>{{ cate.name }}</span>
|
|
||||||
<span v-if="i < category.length - 1"> / </span>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div v-if="tags.length > 0">
|
|
||||||
<TagIcon />
|
|
||||||
<template v-for="tag in tags" :key="tag">
|
|
||||||
<span
|
|
||||||
class="post-meta-tag"
|
|
||||||
:style="{ 'background-color': getColor() }"
|
|
||||||
@click="handleTag(tag)"
|
|
||||||
>
|
|
||||||
{{ tag }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div class="post-meta-create-time">
|
|
||||||
<ClockIcon />
|
|
||||||
<span>{{ post.createTime }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
.post-meta {
|
|
||||||
color: var(--c-text-light);
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 14px;
|
|
||||||
|
|
||||||
&.border {
|
|
||||||
border-bottom: solid 1px var(--c-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
float: left;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 1.25rem;
|
|
||||||
height: 2rem;
|
|
||||||
line-height: 1.5rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 0.875rem;
|
|
||||||
height: 0.875rem;
|
|
||||||
margin-right: 0.2rem;
|
|
||||||
color: var(--c-text-lighter);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta-tag {
|
|
||||||
display: inline-block;
|
|
||||||
height: 1.25rem;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
padding: 0 0.4rem;
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
margin: 0 0.15rem;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import SidebarItems from '@theme-plume/SidebarItems.vue'
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import { onMounted, ref, watchEffect } from 'vue'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import type { SidebarOptions } from '../../shared/index.js'
|
|
||||||
import { useAsideNavbar, useSidebarIndex } from '../composables/index.js'
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
aside: {
|
|
||||||
type: Array as PropType<SidebarOptions>,
|
|
||||||
required: false,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const route = useRoute()
|
|
||||||
const { sidebarList } = useSidebarIndex()
|
|
||||||
const { triggerAsideNavbar } = useAsideNavbar()
|
|
||||||
watchEffect(() => {
|
|
||||||
triggerAsideNavbar(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
const el = ref<HTMLElement | null>(null)
|
|
||||||
onMounted(() => {
|
|
||||||
const activeEl = el.value?.querySelector<HTMLElement>('.router-link-active')
|
|
||||||
activeEl && activeEl.scrollIntoView(false)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<aside
|
|
||||||
:ref="(e) => (el = e as HTMLElement)"
|
|
||||||
class="plume-theme-sidebar-wrapper"
|
|
||||||
>
|
|
||||||
<SidebarItems
|
|
||||||
v-if="aside.length"
|
|
||||||
class="aside-navbar"
|
|
||||||
:sidebar-list="aside"
|
|
||||||
/>
|
|
||||||
<SidebarItems :sidebar-list="sidebarList" />
|
|
||||||
</aside>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/variables';
|
|
||||||
|
|
||||||
.plume-theme-sidebar-wrapper {
|
|
||||||
position: sticky;
|
|
||||||
top: calc(var(--navbar-height) + 1.25rem);
|
|
||||||
width: 18rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
height: calc(100vh - var(--navbar-height) - 1.25rem);
|
|
||||||
border-right: solid 1px var(--c-border);
|
|
||||||
font-size: 1.125rem;
|
|
||||||
padding-left: 1.25rem;
|
|
||||||
overflow-y: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
scrollbar-color: var(--c-brand) var(--c-border);
|
|
||||||
background-color: var(--c-bg-container);
|
|
||||||
transition: transform var(--t-color), background-color var(--t-color);
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background-color: var(--c-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 3.5px;
|
|
||||||
background-color: rgba(34, 34, 34, 0.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .aside-navbar {
|
|
||||||
position: relative;
|
|
||||||
padding-bottom: 0.75rem;
|
|
||||||
margin-bottom: 1.25rem;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: -1.25rem;
|
|
||||||
bottom: -0.25rem;
|
|
||||||
right: 0;
|
|
||||||
border-bottom: solid 4px var(--c-border);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $MQMobile) {
|
|
||||||
.plume-theme-sidebar-wrapper {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,157 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import AutoLink from '@theme-plume/AutoLink.vue'
|
|
||||||
import { computed, ref, watchEffect } from 'vue'
|
|
||||||
import type { PropType } from 'vue'
|
|
||||||
import type { SidebarItem, SidebarOptions } from '../../shared/index.js'
|
|
||||||
import { useThemeLocaleData } from '../composables/index.js'
|
|
||||||
import { hasOwn } from '../utils/index.js'
|
|
||||||
import { ArrowRightIcon } from './icons/index.js'
|
|
||||||
|
|
||||||
type SidebarListComputed = SidebarItem & { open: boolean }
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
sidebarList: {
|
|
||||||
type: Array as PropType<SidebarOptions>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
deep: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
|
|
||||||
const collapsible = computed(() => {
|
|
||||||
if (
|
|
||||||
!themeLocale.value.notes ||
|
|
||||||
!hasOwn(themeLocale.value.notes, 'collapsible')
|
|
||||||
) {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return themeLocale.value.notes.collapsible
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const sidebarList = ref<SidebarListComputed[]>([])
|
|
||||||
watchEffect(() => {
|
|
||||||
sidebarList.value = props.sidebarList.map((sidebar) => {
|
|
||||||
return { ...sidebar, open: !!collapsible.value }
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const sidebarClick = (sidebar: SidebarListComputed): void => {
|
|
||||||
if (props.deep === 1) {
|
|
||||||
sidebar.open = !sidebar.open
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<ul class="sidebar-items">
|
|
||||||
<li
|
|
||||||
v-for="sidebar in sidebarList"
|
|
||||||
:key="sidebar.text"
|
|
||||||
:class="{ line: deep === 1 }"
|
|
||||||
>
|
|
||||||
<p
|
|
||||||
:class="{
|
|
||||||
'sidebar-items-title': deep === 1,
|
|
||||||
'sidebar-items-subtitle':
|
|
||||||
deep > 1 && sidebar.children && sidebar.children.length,
|
|
||||||
}"
|
|
||||||
@click.self="sidebarClick(sidebar)"
|
|
||||||
>
|
|
||||||
<ArrowRightIcon
|
|
||||||
v-if="deep === 1 && sidebar.children && sidebar.children.length"
|
|
||||||
:class="{ open: sidebar.open }"
|
|
||||||
@click.self="sidebarClick(sidebar)"
|
|
||||||
/>
|
|
||||||
<AutoLink
|
|
||||||
v-if="sidebar.link"
|
|
||||||
:item="{ text: sidebar.text, link: sidebar.link }"
|
|
||||||
/>
|
|
||||||
<span v-else @click.self="sidebarClick(sidebar)">
|
|
||||||
{{ sidebar.text }}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<SidebarItems
|
|
||||||
v-if="sidebar.children && sidebar.children.length"
|
|
||||||
v-show="sidebar.open"
|
|
||||||
:sidebar-list="sidebar.children"
|
|
||||||
:deep="deep + 1"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
.plume-theme-sidebar-wrapper {
|
|
||||||
.sidebar-items {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
li {
|
|
||||||
a {
|
|
||||||
color: var(--c-sidebar-text);
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
font-weight: 500;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.router-link-active {
|
|
||||||
color: var(--c-text-accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.sidebar-items-title {
|
|
||||||
position: relative;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.25rem 0;
|
|
||||||
line-height: 1.55;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.arrow-right-icon {
|
|
||||||
position: absolute;
|
|
||||||
left: -1.5rem;
|
|
||||||
top: 8px;
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
transform: rotate(0);
|
|
||||||
transition: transform var(--t-color);
|
|
||||||
color: var(--c-text-quote);
|
|
||||||
|
|
||||||
&.open {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.sidebar-items-subtitle {
|
|
||||||
color: var(--c-text);
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.line {
|
|
||||||
// border-bottom: solid 1px var(--c-border);
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sidebar-items {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import BlogInfo from '@theme-plume/BlogInfo.vue'
|
|
||||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
|
||||||
import PostList from '@theme-plume/PostList.vue'
|
|
||||||
import { computed, ref, watch } from 'vue'
|
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
|
||||||
import type { PostListRef } from '../composables/index.js'
|
|
||||||
import { usePostAllIndex, useTagList } from '../composables/index.js'
|
|
||||||
import { normalizePath } from '../utils/index.js'
|
|
||||||
|
|
||||||
const tagList = useTagList()
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
const postList: PostListRef = ref([])
|
|
||||||
const postAllList = usePostAllIndex()
|
|
||||||
const currentTag = computed(() => {
|
|
||||||
return route.query.tag || ''
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
|
||||||
[currentTag, route],
|
|
||||||
([nowTag]) => {
|
|
||||||
if (nowTag) {
|
|
||||||
postList.value = postAllList.value.filter((post) => {
|
|
||||||
return post.tags.some((tag) => normalizePath(tag) === nowTag)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
postList.value = []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleTag = (tag: string): void => {
|
|
||||||
router.replace({
|
|
||||||
path: route.path,
|
|
||||||
query: { tag: normalizePath(tag) },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<main class="tag-wrapper">
|
|
||||||
<div class="tag-container">
|
|
||||||
<div class="tag-content">
|
|
||||||
<DropdownTransition>
|
|
||||||
<section class="tag-list">
|
|
||||||
<span
|
|
||||||
v-for="{ tag, color } in tagList"
|
|
||||||
:key="tag"
|
|
||||||
class="tag"
|
|
||||||
:style="{ 'background-color': color }"
|
|
||||||
@click="handleTag(tag)"
|
|
||||||
>{{ tag }}</span
|
|
||||||
>
|
|
||||||
</section>
|
|
||||||
</DropdownTransition>
|
|
||||||
<PostList :post-list="postList"></PostList>
|
|
||||||
</div>
|
|
||||||
<BlogInfo></BlogInfo>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_mixins';
|
|
||||||
|
|
||||||
.tag-wrapper {
|
|
||||||
@include wrapper;
|
|
||||||
|
|
||||||
.tag-container {
|
|
||||||
@include container_wrapper;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 1.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-list {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 1.25rem 0.75rem;
|
|
||||||
margin: 0 -0.25rem;
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
height: 1.75rem;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.75rem;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 0.75rem;
|
|
||||||
border-radius: 0.85rem;
|
|
||||||
margin: 0 0.25rem 0.5rem;
|
|
||||||
background-color: var(--c-bg-lighter);
|
|
||||||
cursor: pointer;
|
|
||||||
color: #fff;
|
|
||||||
// box-shadow: var(--shadow-sm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,148 +0,0 @@
|
|||||||
import { usePageData } from '@vuepress/client'
|
|
||||||
import type { PageHeader } from '@vuepress/client'
|
|
||||||
import type { PropType, VNode } from 'vue'
|
|
||||||
import { computed, defineComponent, h, toRefs } from 'vue'
|
|
||||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { scrollTo } from '../utils/index.js'
|
|
||||||
|
|
||||||
export type TocPropsHeaders = PageHeader[]
|
|
||||||
|
|
||||||
export interface TocPropsOptions {
|
|
||||||
containerTag: string
|
|
||||||
containerClass: string
|
|
||||||
listClass: string
|
|
||||||
itemClass: string
|
|
||||||
linkClass: string
|
|
||||||
linkActiveClass: string
|
|
||||||
linkChildrenActiveClass: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TocProps {
|
|
||||||
headers: TocPropsHeaders
|
|
||||||
options: TocPropsOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderLink = (
|
|
||||||
header: PageHeader,
|
|
||||||
options: TocPropsOptions,
|
|
||||||
route: RouteLocationNormalizedLoaded
|
|
||||||
): VNode => {
|
|
||||||
const hash = `#${header.slug}`
|
|
||||||
const linkClass = [options.linkClass]
|
|
||||||
|
|
||||||
if (options.linkActiveClass && route.hash === hash) {
|
|
||||||
linkClass.push(options.linkActiveClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
options.linkChildrenActiveClass &&
|
|
||||||
header.children.some((item) => `#${item.slug}` === route.hash)
|
|
||||||
) {
|
|
||||||
linkClass.push(options.linkChildrenActiveClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
const setActiveRouteHash = (): void => {
|
|
||||||
const headerAnchors: HTMLAnchorElement[] = Array.from(
|
|
||||||
document.querySelectorAll('.header-anchor')
|
|
||||||
)
|
|
||||||
const anchor = headerAnchors.find(
|
|
||||||
(anchor) => decodeURI(anchor.hash) === hash
|
|
||||||
)
|
|
||||||
if (!anchor) return
|
|
||||||
const el = document.documentElement
|
|
||||||
const top = anchor.getBoundingClientRect().top - 80 + el.scrollTop
|
|
||||||
scrollTo(document, top)
|
|
||||||
}
|
|
||||||
|
|
||||||
return h(
|
|
||||||
'a',
|
|
||||||
{
|
|
||||||
href: hash,
|
|
||||||
class: linkClass,
|
|
||||||
ariaLabel: header.title,
|
|
||||||
onClick: (e: MouseEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
setActiveRouteHash()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
header.title
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderHeaders = (
|
|
||||||
headers: PageHeader[],
|
|
||||||
options: TocPropsOptions,
|
|
||||||
route: RouteLocationNormalizedLoaded
|
|
||||||
): VNode[] => {
|
|
||||||
if (headers.length === 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
h(
|
|
||||||
'ul',
|
|
||||||
{ class: options.listClass },
|
|
||||||
headers.map((header) =>
|
|
||||||
h('li', { class: options.itemClass }, [
|
|
||||||
renderLink(header, options, route),
|
|
||||||
renderHeaders(header.children, options, route),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const Toc = defineComponent({
|
|
||||||
name: 'Toc',
|
|
||||||
props: {
|
|
||||||
headers: {
|
|
||||||
type: Array as PropType<TocPropsHeaders>,
|
|
||||||
required: false,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Object as PropType<TocPropsOptions>,
|
|
||||||
required: false,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const { headers: propsHeaders, options: propsOptions } = toRefs(props)
|
|
||||||
|
|
||||||
const defaultOptions: TocPropsOptions = {
|
|
||||||
containerTag: 'nav',
|
|
||||||
containerClass: 'theme-plume-toc',
|
|
||||||
listClass: 'theme-plume-toc-list',
|
|
||||||
itemClass: 'theme-plume-toc-item',
|
|
||||||
linkClass: 'theme-plume-toc-link',
|
|
||||||
linkActiveClass: 'active',
|
|
||||||
linkChildrenActiveClass: 'active',
|
|
||||||
}
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const page = usePageData()
|
|
||||||
const headers = computed<TocPropsHeaders>(() => {
|
|
||||||
const headerToUse = propsHeaders.value || page.value.headers
|
|
||||||
|
|
||||||
return headerToUse[0]?.level === 1 ? headerToUse[0].children : headerToUse
|
|
||||||
})
|
|
||||||
const options = computed<TocPropsOptions>(() => ({
|
|
||||||
...defaultOptions,
|
|
||||||
...propsOptions.value,
|
|
||||||
}))
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
const renderedHeaders = renderHeaders(headers.value, options.value, route)
|
|
||||||
if (options.value.containerTag) {
|
|
||||||
return h(
|
|
||||||
options.value.containerTag,
|
|
||||||
{ class: options.value.containerClass },
|
|
||||||
renderedHeaders
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return renderedHeaders
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export default Toc
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useThemeLocaleData } from '../composables/index.js'
|
|
||||||
|
|
||||||
defineEmits(['toggle'])
|
|
||||||
|
|
||||||
const themeLocale = useThemeLocaleData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="toggle-sidebar-button"
|
|
||||||
:title="themeLocale.toggleSidebar || ''"
|
|
||||||
aria-expanded="false"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
@click="$emit('toggle')"
|
|
||||||
>
|
|
||||||
<div class="icon" aria-hidden="true">
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
<span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../styles/_mixins';
|
|
||||||
|
|
||||||
.toggle-sidebar-button {
|
|
||||||
position: absolute;
|
|
||||||
top: 0.6rem;
|
|
||||||
left: 1rem;
|
|
||||||
display: none;
|
|
||||||
padding: 0.6rem;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-sidebar-button .icon {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
cursor: inherit;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
height: 2px;
|
|
||||||
border-radius: 2px;
|
|
||||||
background-color: var(--c-text);
|
|
||||||
transition: transform var(--t-transform);
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
|
||||||
margin: 6px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: $MQMobile) {
|
|
||||||
.toggle-sidebar-button {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
defineProps({
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: 'tip',
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
vertical: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<span class="badge" :class="type" :style="{ verticalAlign: vertical }">
|
|
||||||
<slot>{{ text }}</slot>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
.badge {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
height: 18px;
|
|
||||||
line-height: 18px;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 0 6px;
|
|
||||||
color: var(--c-bg);
|
|
||||||
vertical-align: top;
|
|
||||||
transition: color var(--t-color), background-color var(--t-color);
|
|
||||||
|
|
||||||
&.tip {
|
|
||||||
background-color: var(--c-badge-tip);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.warning {
|
|
||||||
background-color: var(--c-badge-warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.danger {
|
|
||||||
background-color: var(--c-badge-danger);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-of-contents & {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
& + & {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
import { defineComponent, h } from 'vue'
|
|
||||||
import type { VNode } from 'vue'
|
|
||||||
|
|
||||||
export const IconBase = defineComponent({
|
|
||||||
name: 'IconBase',
|
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: 'currentColor',
|
|
||||||
},
|
|
||||||
viewBox: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: '0 0 20 20',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup:
|
|
||||||
(props, { slots }) =>
|
|
||||||
(): VNode =>
|
|
||||||
h(
|
|
||||||
'svg',
|
|
||||||
{
|
|
||||||
xmlns: 'http://www.w3.org/2000/svg',
|
|
||||||
class: ['icon', `${props.name}-icon`],
|
|
||||||
viewBox: props.viewBox,
|
|
||||||
ariaLabelledby: props.name,
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('title', { id: props.name, lang: 'en' }, `${props.name}`),
|
|
||||||
h('g', { fill: props.color }, slots.default?.()),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
})
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
import { h } from 'vue'
|
|
||||||
import type { FunctionalComponent } from 'vue'
|
|
||||||
import { IconBase } from './IconBase.js'
|
|
||||||
|
|
||||||
export const UserIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'user' }, () =>
|
|
||||||
h('path', {
|
|
||||||
'fill-rule': 'evenodd',
|
|
||||||
'clip-rule': 'evenodd',
|
|
||||||
'd': 'M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
UserIcon.displayName = 'UserIcon'
|
|
||||||
|
|
||||||
export const FolderIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'folder' }, () =>
|
|
||||||
h('path', {
|
|
||||||
d: 'M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
FolderIcon.displayName = 'FolderIcon'
|
|
||||||
|
|
||||||
export const ClockIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'clock' }, () =>
|
|
||||||
h('path', {
|
|
||||||
'fill-rule': 'evenodd',
|
|
||||||
'clip-rule': 'evenodd',
|
|
||||||
'd': 'M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
ClockIcon.displayName = 'ClockIcon'
|
|
||||||
|
|
||||||
export const TagIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'tag' }, () =>
|
|
||||||
h('path', {
|
|
||||||
'fill-rule': 'evenodd',
|
|
||||||
'clip-rule': 'evenodd',
|
|
||||||
'd': 'M17.707 9.293a1 1 0 010 1.414l-7 7a1 1 0 01-1.414 0l-7-7A.997.997 0 012 10V5a3 3 0 013-3h5c.256 0 .512.098.707.293l7 7zM5 6a1 1 0 100-2 1 1 0 000 2z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
TagIcon.displayName = 'TagIcon'
|
|
||||||
|
|
||||||
export const TopIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'top', viewBox: '0 0 1024 1024' }, () => [
|
|
||||||
h('path', {
|
|
||||||
d: 'M80.96 449.194667l37.696-37.717334 19.626667 19.605334-37.717334 37.717333zM197.205333 541.44l116.16-116.138667 13.568 13.568-116.16 116.16zM220.565333 565.162667l116.16-116.16 13.568 13.589333-116.16 116.138667zM173.845333 517.738667l116.16-116.16 13.568 13.589333-116.16 116.138667zM245.354667 587.477333l116.202666-116.096 13.568 13.589334-116.202666 116.096z',
|
|
||||||
// fill: '#FA8D14',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M339.2 0L0 345.6V1024L1024 0H339.2z m-115.2 283.733333l46.933333 46.933334-14.933333 12.8-4.266667-4.266667-140.8 140.8 4.266667 4.266667-14.933333 14.933333-46.933334-46.933333 170.666667-168.533334z m2.133333 375.466667l-12.8-12.8 29.866667-29.866667L149.333333 520.533333l64-64-12.8-12.8L108.8 533.333333l-10.666667-10.666666 89.6-89.6-10.666666-10.666667 14.933333-14.933333 10.666667 10.666666 91.733333-91.733333 10.666667 10.666667-91.733334 91.733333 12.8 12.8 68.266667-68.266667 96 96 27.733333-27.733333 12.8 12.8-204.8 204.8z m232.533334-236.8l-17.066667 17.066667c-6.4-6.4-14.933333-10.666667-21.333333-14.933334 8.533333-4.266667 14.933333-10.666667 21.333333-17.066666 6.4-6.4 6.4-12.8 0-19.2l-136.533333-136.533334-34.133334 34.133334-14.933333-17.066667L332.8 192l14.933333 14.933333-25.6 27.733334 138.666667 138.666666c14.933333 14.933333 14.933333 32-2.133333 49.066667z m-81.066667-200.533333l38.4-38.4-21.333333-34.133334-46.933334 46.933334-14.933333-14.933334 123.733333-123.733333 12.8 17.066667-59.733333 59.733333 21.333333 34.133333 57.6-57.6 98.133334 98.133334-14.933334 14.933333-83.2-83.2-78.933333 78.933333 85.333333 85.333334-14.933333 14.933333-102.4-98.133333z m138.666667 162.133333c-6.4-2.133333-14.933333-4.266667-25.6-4.266667 19.2-34.133333 25.6-61.866667 23.466666-85.333333-2.133333-21.333333-17.066667-44.8-42.666666-70.4L448 200.533333l14.933333-14.933333 23.466667 23.466667c17.066667 17.066667 29.866667 34.133333 38.4 49.066666 38.4-8.533333 74.666667-14.933333 106.666667-19.2l2.133333 25.6c-34.133333 2.133333-68.266667 8.533333-100.266667 14.933334 2.133333 4.266667 2.133333 8.533333 2.133334 12.8 6.4 23.466667 0 55.466667-19.2 91.733333z',
|
|
||||||
// fill: '#FA8D14',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M183.765333 346.965333l37.696-37.717333 19.626667 19.584-37.696 37.738667zM132.288 398.037333l37.76-37.674666 19.584 19.626666-37.738667 37.674667z',
|
|
||||||
// fill: '#FA8D14',
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
TopIcon.displayName = 'TopIcon'
|
|
||||||
|
|
||||||
export const ArrowRightIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'arrow-right', viewBox: '0 0 1024 1024' }, () =>
|
|
||||||
h('path', {
|
|
||||||
d: 'M709.546667 560.256c-2.389333 2.474667-11.52 13.056-19.968 21.76-49.706667 54.741333-179.370667 144.341333-247.253334 171.690667-10.325333 4.394667-36.394667 13.696-50.304 14.293333-13.354667 0-26.026667-3.072-38.186666-9.301333a79.957333 79.957333 0 0 1-33.92-38.570667c-4.266667-11.221333-10.922667-44.8-10.922667-45.397333-6.656-36.736-10.325333-96.426667-10.325333-162.389334 0-62.848 3.669333-120.106667 9.088-157.397333 0.64-0.597333 7.253333-42.325333 14.549333-56.618667A76.16 76.16 0 0 1 389.632 256h2.389333c18.176 0.64 56.362667 16.853333 56.362667 17.450667 64.213333 27.392 190.890667 112.597333 241.834667 169.216 0 0 14.336 14.549333 20.565333 23.637333 9.728 13.056 14.549333 29.226667 14.549333 45.397333 0 18.048-5.461333 34.858667-15.786666 48.554667z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
ArrowRightIcon.displayName = 'ArrowRightIcon'
|
|
||||||
|
|
||||||
export const ArrowBottomIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'arrow-bottom', viewBox: '0 0 1024 1024' }, () =>
|
|
||||||
h('path', {
|
|
||||||
d: 'M150.001 502.111a22.487 22.487 0 0 1 13.185 4.245l348.86 250.152 348.858-250.152a22.577 22.577 0 0 1 26.28 36.665L525.14 802.656a22.577 22.577 0 0 1-26.28 0L136.816 543.02a22.577 22.577 0 0 1 13.185-40.91z m737.183-257.196L525.14 504.55a22.577 22.577 0 0 1-26.28 0L136.816 244.915a22.577 22.577 0 1 1 26.28-36.665l348.859 250.152L860.814 208.25a22.577 22.577 0 1 1 26.28 36.665z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
ArrowBottomIcon.displayName = 'ArrowBottomIcon'
|
|
||||||
|
|
||||||
export const BackTopIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'back-top', viewBox: '0 0 1024 1024' }, () =>
|
|
||||||
h('path', {
|
|
||||||
d: 'M725.902 498.916c18.205-251.45-93.298-410.738-205.369-475.592l-6.257-3.982-6.258 3.414c-111.502 64.853-224.711 224.142-204.8 475.59-55.751 53.476-80.214 116.623-80.214 204.8v15.36l179.2-35.27c11.378 40.39 58.596 69.973 113.21 69.973 54.613 0 101.262-29.582 112.64-68.836l180.337 36.41v-15.36c-0.569-89.885-25.031-153.6-82.489-206.507zM571.733 392.533c-33.564 31.29-87.04 28.445-118.329-5.12s-28.444-87.04 5.12-117.76c33.565-31.289 87.04-28.444 118.33 5.12s28.444 86.471-5.12 117.76z m-56.32 368.64c-35.84 0-64.284 29.014-64.284 64.285 0 35.84 54.044 182.613 64.284 182.613s64.285-146.773 64.285-182.613c0-35.271-29.014-64.285-64.285-64.285z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
BackTopIcon.displayName = 'BackTopIcon'
|
|
||||||
|
|
||||||
export const PostIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'post', viewBox: '0 0 1024 1024' }, () =>
|
|
||||||
h('path', {
|
|
||||||
d: 'M805.376 81.0496 188.7232 81.0496c-52.6336 0-94.8736 42.3936-94.8736 94.6176l0 664.576c0 52.2752 42.496 94.6176 94.8736 94.6176L805.376 934.8608c52.6336 0 94.8736-42.3936 94.8736-94.6176L900.2496 175.7184C900.2496 123.392 857.8048 81.0496 805.376 81.0496zM288.768 204.8c39.3216 0 71.168 31.5904 71.168 71.168 0 39.3216-31.5904 71.168-71.168 71.168-39.3216 0-71.168-31.5904-71.168-71.168C217.6 236.6464 249.1904 204.8 288.768 204.8zM506.368 741.0176 217.6 741.0176l0-47.4112L506.368 693.6064 506.368 741.0176zM671.3344 617.2672 217.6 617.2672 217.6 569.856l453.7344 0L671.3344 617.2672zM671.3344 493.568 217.6 493.568 217.6 446.1056l453.7344 0L671.3344 493.568z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
PostIcon.displayName = 'PostIcon'
|
|
||||||
|
|
||||||
export const ArrowDoubleRightIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'arrow-double-right', viewBox: '0 0 1024 1024' }, () =>
|
|
||||||
h('path', {
|
|
||||||
d: 'M160.117 212.026v-82.233a8 8 0 0 1 13.33-5.966l407.697 364.298c0.9 0.804 1.753 1.658 2.556 2.558 11.764 13.186 10.62 33.419-2.556 45.192L173.448 900.173a8 8 0 0 1-13.33-5.966v-82.233a16 16 0 0 1 5.338-11.93L487.814 512 165.456 223.957a16 16 0 0 1-5.339-11.931z m272.057 0v-82.233a8 8 0 0 1 13.33-5.966l407.697 364.298c0.9 0.804 1.753 1.658 2.556 2.558 11.764 13.186 10.62 33.419-2.556 45.192L445.505 900.173a8 8 0 0 1-13.33-5.966v-82.233a16 16 0 0 1 5.339-11.93L759.87 512 437.514 223.957a16 16 0 0 1-5.34-11.931z',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
ArrowDoubleRightIcon.displayName = 'ArrowDoubleRightIcon'
|
|
||||||
|
|
||||||
export const ArrowDoubleLeftIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'arrow-double-left', viewBox: '0 0 1024 1024' }, () => [
|
|
||||||
h('path', {
|
|
||||||
d: 'M495.976 476.195c19.777 17.656 21.494 48 3.837 67.774a48.003 48.003 0 0 1-3.837 3.836L536.082 512l-40.106-35.805zM864 212.083v-82.217a8 8 0 0 0-13.328-5.967L442.69 488.13c-0.9 0.804-1.754 1.657-2.558 2.557-11.772 13.184-10.626 33.412 2.558 45.183l407.983 364.231A8 8 0 0 0 864 894.134v-82.217a16 16 0 0 0-5.344-11.936L536.082 512l322.574-287.981A16 16 0 0 0 864 212.083zM495.976 476.195c19.777 17.656 21.494 48 3.837 67.774a48.003 48.003 0 0 1-3.837 3.836L536.082 512l-40.106-35.805zM864 212.083v-82.217a8 8 0 0 0-13.328-5.967L442.69 488.13c-0.9 0.804-1.754 1.657-2.558 2.557-11.772 13.184-10.626 33.412 2.558 45.183l407.983 364.231A8 8 0 0 0 864 894.134v-82.217a16 16 0 0 0-5.344-11.936L536.082 512l322.574-287.981A16 16 0 0 0 864 212.083z',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M223.976 476.195c19.777 17.656 21.494 48 3.837 67.774a48.003 48.003 0 0 1-3.837 3.836L264.082 512l-40.106-35.805zM592 212.083v-82.217a8 8 0 0 0-13.328-5.967L170.69 488.13c-0.9 0.804-1.754 1.657-2.558 2.557-11.772 13.184-10.626 33.412 2.558 45.183l407.983 364.231A8 8 0 0 0 592 894.134v-82.217a16 16 0 0 0-5.344-11.936L264.082 512l322.574-287.981A16 16 0 0 0 592 212.083zM223.976 476.195c19.777 17.656 21.494 48 3.837 67.774a48.003 48.003 0 0 1-3.837 3.836L264.082 512l-40.106-35.805zM592 212.083v-82.217a8 8 0 0 0-13.328-5.967L170.69 488.13c-0.9 0.804-1.754 1.657-2.558 2.557-11.772 13.184-10.626 33.412 2.558 45.183l407.983 364.231A8 8 0 0 0 592 894.134v-82.217a16 16 0 0 0-5.344-11.936L264.082 512l322.574-287.981A16 16 0 0 0 592 212.083z',
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
ArrowDoubleLeftIcon.displayName = 'ArrowDoubleLeftIcon'
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export * from './IconBase.js'
|
|
||||||
export * from './icon.js'
|
|
||||||
export * from './socialIcon.js'
|
|
||||||
@ -1,126 +0,0 @@
|
|||||||
import { h } from 'vue'
|
|
||||||
import type { FunctionalComponent } from 'vue'
|
|
||||||
import { IconBase } from './IconBase.js'
|
|
||||||
|
|
||||||
export const GithubIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'github', viewBox: '0 0 1024 1024' }, () =>
|
|
||||||
h('path', {
|
|
||||||
d: 'M512 0C229.283787 0 0.142041 234.942803 0.142041 524.867683c0 231.829001 146.647305 428.553077 350.068189 497.952484 25.592898 4.819996 34.976961-11.38884 34.976961-25.294314 0-12.45521-0.469203-45.470049-0.725133-89.276559-142.381822 31.735193-172.453477-70.380469-172.453477-70.380469-23.246882-60.569859-56.816233-76.693384-56.816234-76.693385-46.493765-32.58829 3.540351-31.948468 3.540351-31.948467 51.356415 3.71097 78.356923 54.086324 78.356923 54.086324 45.683323 80.19108 119.817417 57.072162 148.993321 43.593236 4.649376-33.91059 17.915029-57.029508 32.50298-70.167195-113.675122-13.222997-233.151301-58.223843-233.1513-259.341366 0-57.285437 19.919806-104.163095 52.678715-140.846248-5.246544-13.265652-22.820334-66.626844 4.990615-138.884127 0 0 42.996069-14.076094 140.760939 53.787741 40.863327-11.644769 84.627183-17.445825 128.177764-17.6591 43.465272 0.213274 87.271782 6.014331 128.135109 17.6591 97.679561-67.906489 140.59032-53.787741 140.59032-53.787741 27.938914 72.257282 10.407779 125.618474 5.118579 138.884127 32.844219 36.683154 52.593405 83.560812 52.593405 140.846248 0 201.586726-119.646798 245.990404-233.663158 258.957473 18.341577 16.208835 34.721032 48.199958 34.721032 97.210357 0 70.167195-0.639822 126.7275-0.639823 143.960051 0 14.033439 9.213443 30.370239 35.190235 25.209005 203.250265-69.527373 349.769606-266.123484 349.769605-497.867175C1023.857959 234.942803 794.673558 0 512 0',
|
|
||||||
fill: '#3E75C3',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
GithubIcon.displayName = 'GithubIcon'
|
|
||||||
|
|
||||||
export const EmailIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'email', viewBox: '0 0 1024 1024' }, () => [
|
|
||||||
[
|
|
||||||
h('path', {
|
|
||||||
d: 'M848.76288 333.62432H164.99712C99.32288 333.62432 46.08 386.87232 46.08 452.54144v297.28768c0 65.67424 53.24288 118.92224 118.91712 118.92224h683.77088c65.66912 0 118.91712-53.24288 118.91712-118.92224V452.54144c-0.00512-65.66912-53.248-118.91712-118.92224-118.91712z',
|
|
||||||
fill: '#96383D',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M639.8208 51.2h-474.8288a44.58496 44.58496 0 0 0-44.59008 44.59008v609.44896a44.57984 44.57984 0 0 0 44.59008 44.59008h683.776a44.58496 44.58496 0 0 0 44.59008-44.59008V304.73728L639.8208 51.2z',
|
|
||||||
fill: '#EBE2CE',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M551.4752 229.57568H209.59232v44.59008h341.88288v-44.59008zM209.59232 794.42432h594.58048v-44.5952H209.59232v44.5952z m0-89.18528h594.58048v-44.5952H209.59232v44.5952z m0-178.37568h594.58048v-44.5952H209.59232v44.5952z m0 89.18528h594.58048v-44.59008H209.59232v44.59008z m0-222.96576v44.59008h594.58048v-44.59008H209.59232z',
|
|
||||||
fill: '#C9C1B1',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M941.83936 393.31328L75.60704 955.02848c12.89216 10.93632 29.29664 17.77152 47.44192 17.77152H893.5936c40.91904 0 74.09152-33.4592 74.09152-74.74688V449.60768c-0.00512-22.58432-10.14784-42.58816-25.84576-56.2944z',
|
|
||||||
fill: '#D54D54',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M71.99232 396.5696C56.25344 410.18368 46.08 430.08512 46.08 452.54144v445.93152C46.08 939.53024 79.34976 972.8 120.40192 972.8h772.95104c18.20672 0 34.65216-6.79424 47.56992-17.664L71.99232 396.5696z',
|
|
||||||
fill: '#EA5455',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M655.52384 66.90816v236.8l237.82912 74.89024V304.73728z',
|
|
||||||
fill: '',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M640.66048 52.0448v207.2576a44.58496 44.58496 0 0 0 44.5952 44.5952h207.2576l-251.8528-251.8528z',
|
|
||||||
fill: '#FFFBF2',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
])
|
|
||||||
EmailIcon.displayName = 'EmailIcon'
|
|
||||||
|
|
||||||
export const ZhiHuIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'zhiHu', viewBox: '0 0 1024 1024' }, () => [
|
|
||||||
h('path', {
|
|
||||||
d: 'M512 73.28A438.72 438.72 0 1 0 950.72 512 438.72 438.72 0 0 0 512 73.28z m-98.56 458.88l-16.8 66.88 23.68-20.8s53.92 61.28 64 76.48 1.44 68.96 1.44 68.96l-92.48-113.12s-29.12 101.12-68.48 124.16a97.6 97.6 0 0 1-80 6.56 342.08 342.08 0 0 0 85.44-89.76 382.88 382.88 0 0 0 39.52-119.36h-115.04s8.8-40.48 24.16-41.6 90.88 0 90.88 0l-1.76-124.8-43.2 2.24a96 96 0 0 1-32 48c-24.16 17.44-38.4 10.88-38.4 10.88s42.72-118.24 55.84-141.28 50.4-25.12 50.4-25.12l-23.04 66.72h147.84c17.6 0 18.56 40.64 18.56 40.64h-90.56v122.56s61.28-2.24 81.12 0 19.68 41.6 19.68 41.6z m329.44 160h-91.52l-65.12 46.24-13.6-46.24h-36.96v-368h208z',
|
|
||||||
fill: '#49C0FB',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M602.88 691.68l54.88-41.44h43.04V364.64h-121.12v285.6h11.2l12 41.44z',
|
|
||||||
fill: '#49C0FB',
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
ZhiHuIcon.displayName = 'ZhiHuIcon'
|
|
||||||
|
|
||||||
export const WeiBoIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'weiBo', viewBox: '0 0 1024 1024' }, () => [
|
|
||||||
h('path', {
|
|
||||||
d: 'M448.698182 482.210909c-96.814545 4.654545-175.010909 56.785455-175.010909 121.949091s78.196364 114.501818 175.010909 109.847273S623.709091 647.912727 623.709091 582.749091c-0.930909-64.232727-79.127273-105.192727-175.010909-100.538182z m65.163636 164.770909c-29.789091 39.098182-88.436364 57.716364-145.221818 26.065455-26.996364-14.894545-26.065455-43.752727-26.065455-43.752728s-11.170909-92.16 85.643637-103.330909c97.745455-12.101818 115.432727 81.92 85.643636 121.018182z',
|
|
||||||
fill: '#EA5D5C',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M448.698182 584.610909c-6.516364 4.654545-7.447273 13.032727-3.723637 18.618182 2.792727 5.585455 11.170909 6.516364 16.756364 1.861818 5.585455-4.654545 8.378182-13.032727 4.654546-18.618182-2.792727-5.585455-10.24-6.516364-17.687273-1.861818zM403.083636 597.643636c-18.618182 1.861818-30.72 17.687273-30.72 33.512728 0 14.894545 14.894545 26.065455 32.581819 24.203636 17.687273-1.861818 32.581818-15.825455 32.581818-31.650909s-13.963636-27.927273-34.443637-26.065455z',
|
|
||||||
fill: '#EA5D5C',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M512 0C229.003636 0 0 229.003636 0 512s229.003636 512 512 512 512-229.003636 512-512S794.996364 0 512 0z m197.352727 626.501818C669.323636 712.145455 538.065455 754.036364 441.250909 746.589091c-92.16-7.447273-211.316364-38.167273-223.418182-151.738182 0 0-6.516364-51.2 42.821818-117.294545 0 0 70.749091-99.607273 152.669091-128.465455 82.850909-27.927273 92.16 19.549091 92.16 48.407273-4.654545 24.203636-12.101818 38.167273 18.618182 28.858182 0 0 80.989091-38.167273 114.501818-4.654546 26.996364 26.996364 4.654545 65.163636 4.654546 65.163637s-11.170909 12.101818 12.101818 16.756363c21.410909 3.723636 94.021818 37.236364 53.992727 122.88z m-80.058182-236.450909c-8.378182 0-15.825455-7.447273-15.825454-15.825454 0-9.309091 7.447273-15.825455 15.825454-15.825455 0 0 99.607273-18.618182 87.505455 89.367273v1.861818c-0.930909 7.447273-7.447273 13.963636-15.825455 13.963636-9.309091 0-15.825455-7.447273-15.825454-15.825454 0-1.861818 15.825455-73.541818-55.854546-57.716364zM797.789091 493.381818c-2.792727 18.618182-12.101818 11.170909-22.341818 11.170909-13.032727 0-23.272727-16.756364-23.272728-29.789091 0-11.170909 4.654545-22.341818 4.654546-22.341818 0.930909-4.654545 12.101818-34.443636-7.447273-78.196363-35.374545-60.509091-106.123636-60.509091-114.501818-57.716364-8.378182 3.723636-21.410909 5.585455-21.410909 5.585454-13.032727 0-23.272727-10.24-23.272727-23.272727 0-11.170909 7.447273-19.549091 16.756363-22.341818 0 0 0 0.930909 0.930909 0.930909s1.861818 0.930909 1.861819 0.930909c10.24-1.861818 45.614545-4.654545 79.127272 3.723637 62.370909 14.894545 146.152727 83.781818 108.916364 211.316363z',
|
|
||||||
fill: '#EA5D5C',
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
WeiBoIcon.displayName = 'WeiBoIcon'
|
|
||||||
|
|
||||||
// <svg t="1648887372594" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3602" width="200" height="200"><path d="" fill="#68A5E1" p-id="3603"></path></svg>
|
|
||||||
export const QQIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'qq', viewBox: '0 0 1024 1024' }, () =>
|
|
||||||
h('path', {
|
|
||||||
d: 'M512.268258 64.433103c-247.183323 0-447.569968 200.380501-447.569968 447.563825 0 247.189467 200.385621 447.570992 447.569968 447.570992s447.569968-200.380501 447.569968-447.569968c0-247.184347-200.386645-447.564849-447.569968-447.564849z m252.85872 584.692787c-18.997168 16.287968-43.668709-53.628042-47.2134-42.875198-8.642616 26.161294-12.695154 43.646184-38.148944 72.127602-1.35972 1.521494 29.43056 12.647032 38.148944 36.396051 8.346713 22.756875 24.596797 58.811973-81.725503 70.125906-62.389428 6.635801-107.471099-33.244533-111.964932-32.85648-8.325212 0.734126-4.618747 0-13.568528 0-7.321804 0-7.807126 0.534468-14.69685 0-1.899307-0.140272-22.632985 32.85648-115.364231 32.85648-71.878798 0-90.48177-45.243445-76.032701-70.125906 14.464428-24.877342 38.579999-32.122354 35.176604-36.06636-16.73643-19.39546-28.287904-40.1404-35.176604-58.882621-1.705793-4.666869-3.135137-9.209848-4.262434-13.574672-2.611931-10.008479-22.627866 58.76385-44.111028 42.875198-21.483162-15.883533-19.567472-56.309597-5.659014-95.003248 14.033372-39.006959 49.37687-76.562049 49.771065-84.854496 1.412962-30.849665-3.044011-35.975235 0-44.078263 6.780169-18.149391 15.034732-11.190043 15.034733-20.609788 0-118.64476 88.172909-214.829571 196.933079-214.829571 108.755051 0 196.928984 96.184811 196.928984 214.829571 0 4.554242 11.815637 0 17.474651 20.609788 1.165181 4.256291 1.968931 20.684531 0.58771 44.078263-0.658358 11.238165 29.954789 24.914202 45.777913 84.854496 15.845649 59.945414 0 88.215912-7.909514 95.003248z',
|
|
||||||
fill: '#68A5E1',
|
|
||||||
})
|
|
||||||
)
|
|
||||||
QQIcon.displayName = 'QQIcon'
|
|
||||||
|
|
||||||
export const TwitterIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'twitter', viewBox: '0 0 1024 1024' }, () => [
|
|
||||||
h('path', {
|
|
||||||
d: 'M512.274401 959.556658c247.17718 0 447.556658-200.366167 447.556658-447.556658 0-247.16387-200.379477-447.556658-447.556658-447.556658-247.188443 0-447.569968 200.392788-447.569968 447.556658 0 247.190491 200.382549 447.556658 447.569968 447.556658',
|
|
||||||
fill: '#78CBEF',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M736.810405 394.754891c-16.48353 7.310541-34.227463 12.256931-52.82122 14.478763 19.004336-11.383557 33.588558-29.396772 40.435279-50.868671-17.780793 10.536804-37.440415 18.183179-58.42392 22.294079-16.741549-17.872942-40.666677-29.038412-67.134113-29.038412-50.766282 0-91.948998 41.192954-91.948998 91.972548 0 7.220439 0.784296 14.222791 2.366199 20.943574-76.439183-3.841618-144.191723-40.421969-189.587726-96.109044-7.915657 13.630985-12.452493 29.422369-12.452493 46.282688 0 31.877646 16.241893 60.042683 40.924696 76.552835-15.072616-0.460748-29.26981-4.637177-41.682371-11.485946v1.131393c0 44.585086 31.698466 81.757243 73.804725 90.185867-7.723167 2.160398-15.841554 3.239573-24.246628 3.239574a91.24866 91.24866 0 0 1-17.294447-1.63105c11.691747 36.527109 45.654023 63.139936 85.90705 63.845394-31.477307 24.682804-71.144672 39.382725-114.227718 39.382725-7.42624 0-14.762379-0.410578-21.946982-1.270642 40.706609 26.070168 89.057546 41.308653 140.992081 41.308653 169.209337 0 261.697922-140.132017 261.697922-261.695874 0-3.997248-0.078839-7.979138-0.244709-11.899595a186.466924 186.466924 0 0 0 45.883373-47.618859',
|
|
||||||
fill: '#FFFFFF',
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
TwitterIcon.displayName = 'TwitterIcon'
|
|
||||||
|
|
||||||
export const FacebookIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'facebook', viewBox: '0 0 1024 1024' }, () => [
|
|
||||||
h('path', {
|
|
||||||
d: 'M512.262115 959.556658c247.175132 0 447.569968-200.366167 447.569968-447.556658 0-247.16387-200.394836-447.556658-447.569968-447.556658-247.17718 0-447.556658 200.392788-447.556658 447.556658-0.001024 247.190491 200.378454 447.556658 447.556658 447.556658',
|
|
||||||
fill: '#537BBC',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M404.292383 436.216104h46.269378v-44.969044c0-19.828563 0.499656-50.408946 14.904699-69.347753 15.172957-20.05689 36.000832-33.690947 71.826579-33.690946 58.371702 0 82.952117 8.326235 82.952118 8.326235l-11.564785 68.550147s-19.285904-5.576079-37.275569-5.57608c-17.99888 0-34.111763 6.449454-34.111764 24.438095v52.269346h73.791416l-5.152191 66.958004h-68.639225v232.604221h-86.731278V503.174108h-46.269378v-66.958004z',
|
|
||||||
fill: '#FFFFFF',
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
FacebookIcon.displayName = 'FacebookIcon'
|
|
||||||
|
|
||||||
export const LinkedinIcon: FunctionalComponent = () =>
|
|
||||||
h(IconBase, { name: 'linkedin', viewBox: '0 0 1024 1024' }, () => [
|
|
||||||
h('path', {
|
|
||||||
d: 'M512.267234 959.569968c247.223255 0 447.572016-200.400979 447.572016-447.582255 0-247.171037-200.347737-447.558705-447.572016-447.558705-247.194586 0-447.568944 200.387669-447.568944 447.558705 0 247.1823 200.373334 447.582255 447.568944 447.582255',
|
|
||||||
fill: '#1284C7',
|
|
||||||
}),
|
|
||||||
h('path', {
|
|
||||||
d: 'M387.013295 699.188763h-87.249365V419.999808h87.249365v279.188955z m-45.860848-314.114707h-0.628666c-31.57048 0-52.042043-21.341866-52.042043-48.378582 0-27.573232 21.086918-48.478922 53.286064-48.478922 32.175596 0 51.975491 20.852449 52.607228 48.402131 0.001024 27.046955-20.430608 48.455373-53.222583 48.455373z m394.899259 314.114707H637.125954V554.711376c0-37.815157-15.457597-63.618091-49.496664-63.61809-26.03126 0-40.488521 17.410146-47.233878 34.204937-2.518758 6.013279-2.133777 14.405043-2.133777 22.820356v151.06916h-98.001184s1.273713-255.921161 0-279.188955h98.001184v43.815125c5.794167-19.157918 37.097413-46.5018 87.093733-46.5018 61.986018 0 110.696338 40.168045 110.696338 126.630041v155.246613z',
|
|
||||||
fill: '#FFFFFF',
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
LinkedinIcon.displayName = 'LinkedinIcon'
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user