vitepress-plugin-announcement 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/components/Announcement.vue +185 -0
- package/dist/components/AnnouncementButton.vue +85 -0
- package/dist/components/AnnouncementIcon.vue +33 -0
- package/dist/components/directives.d.ts +3 -0
- package/dist/components/directives.js +5 -0
- package/dist/components/style.css +103 -0
- package/dist/components/util.d.ts +7 -0
- package/dist/components/util.js +46 -0
- package/dist/index.d.mts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +101 -0
- package/dist/index.mjs +65 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 sugar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# vitepress-plugin-announcement
|
|
2
|
+
|
|
3
|
+
[English](https://github.com/ATQQ/sugar-blog/blob/master/packages/vitepress-plugin-announcement/README-en.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
## 使用
|
|
6
|
+
安装依赖 `pnpm/npm/yarn`
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add vitepress-plugin-announcement
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
引入插件在 `.vitepress/config.ts` 配置文件中
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { defineConfig } from 'vitepress'
|
|
15
|
+
import { AnnouncementPlugin } from 'vitepress-plugin-announcement'
|
|
16
|
+
|
|
17
|
+
export default defineConfig({
|
|
18
|
+
vite: {
|
|
19
|
+
// ↓↓↓↓↓
|
|
20
|
+
plugins: [
|
|
21
|
+
AnnouncementPlugin({
|
|
22
|
+
title: '公告',
|
|
23
|
+
body: [
|
|
24
|
+
{ type: 'text', content: '下方插入了二维码' },
|
|
25
|
+
{
|
|
26
|
+
type: 'image',
|
|
27
|
+
src: 'https://img.cdn.sugarat.top/mdImg/MTYxNTAxODc2NTIxMA==615018765210~fmt.webp'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'button',
|
|
31
|
+
content: '作者博客',
|
|
32
|
+
link: 'https://sugarat.top'
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
footer: [
|
|
36
|
+
{
|
|
37
|
+
type: 'text',
|
|
38
|
+
content: '底部内容'
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
})
|
|
42
|
+
]
|
|
43
|
+
// ↑↑↑↑↑
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 更多用法
|
|
49
|
+
### 关闭后不再弹出
|
|
50
|
+
*tip:下次配置修改后再展示*
|
|
51
|
+
```js
|
|
52
|
+
AnnouncementPlugin({
|
|
53
|
+
duration: -1
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 不展示再次打开按钮
|
|
58
|
+
```js
|
|
59
|
+
AnnouncementPlugin({
|
|
60
|
+
reopen: false
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 引导闪烁
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
AnnouncementPlugin({
|
|
68
|
+
twinkle: true
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 完整配置
|
|
73
|
+
```ts
|
|
74
|
+
import type { Ref } from 'vue'
|
|
75
|
+
import type { Route } from 'vitepress'
|
|
76
|
+
|
|
77
|
+
export interface AnnouncementOptions {
|
|
78
|
+
/**
|
|
79
|
+
* 公告标题
|
|
80
|
+
*/
|
|
81
|
+
title: string
|
|
82
|
+
/**
|
|
83
|
+
* 公告主要内容
|
|
84
|
+
*/
|
|
85
|
+
body?: Announcement.Value[]
|
|
86
|
+
/**
|
|
87
|
+
* 公告底部内容
|
|
88
|
+
*/
|
|
89
|
+
footer?: Announcement.Value[]
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 是否只在浏览器环境渲染组件
|
|
93
|
+
* @default false
|
|
94
|
+
* @doc https://vitepress.dev/guide/ssr-compat#clientonly
|
|
95
|
+
*/
|
|
96
|
+
clientOnly?: boolean
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 展示时机控制
|
|
100
|
+
*
|
|
101
|
+
* -1 只展示1次;>= 0 每次都展示,一定时间后自动消失,0 不自动消失
|
|
102
|
+
*
|
|
103
|
+
* 配置发生改变时,会重新触发此规则
|
|
104
|
+
* @default 0
|
|
105
|
+
*/
|
|
106
|
+
duration?: number
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 移动端自动最小化
|
|
110
|
+
* @default false
|
|
111
|
+
*/
|
|
112
|
+
mobileMinify?: boolean
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 支持重新打开(右上角 icon 悬浮)
|
|
116
|
+
* @default true
|
|
117
|
+
*/
|
|
118
|
+
reopen?: boolean
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 是否打开闪烁提示,通常需要和 reopen 搭配使用
|
|
122
|
+
* @default false
|
|
123
|
+
*/
|
|
124
|
+
twinkle?: boolean
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 设置展示图标,svg
|
|
128
|
+
*/
|
|
129
|
+
icon?: string
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 设置关闭图标,svg
|
|
133
|
+
*/
|
|
134
|
+
closeIcon?: string
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 自定义展示策略
|
|
138
|
+
* @param to 切换到的目标路由
|
|
139
|
+
*/
|
|
140
|
+
onRouteChanged?: (to: Route, show: Ref<boolean>) => void
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export declare namespace Announcement {
|
|
144
|
+
export interface Title {
|
|
145
|
+
type: 'title'
|
|
146
|
+
content: string
|
|
147
|
+
style?: string
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface Text {
|
|
151
|
+
type: 'text'
|
|
152
|
+
content: string
|
|
153
|
+
style?: string
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface Image {
|
|
157
|
+
type: 'image'
|
|
158
|
+
src: string
|
|
159
|
+
style?: string
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface Button {
|
|
163
|
+
type: 'button'
|
|
164
|
+
link: string
|
|
165
|
+
content: string
|
|
166
|
+
style?: string
|
|
167
|
+
props?: any
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export type Value = Title | Text | Image | Button
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Thanks
|
|
175
|
+
样式参考了 [reco-1.x 主题](https://github.com/vuepress-reco/vuepress-theme-reco-1.x) 中的 [@vuepress-reco/vuepress-plugin-bulletin-popover](https://github.com/vuepress-reco/vuepress-theme-reco-1.x/tree/master/packages/%40vuepress-reco/vuepress-plugin-bulletin-popover) 插件
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, h, onMounted, ref, watch } from 'vue'
|
|
3
|
+
import { useRoute, useRouter } from 'vitepress'
|
|
4
|
+
import type { Announcement, AnnouncementOptions } from 'vitepress-plugin-announcement'
|
|
5
|
+
|
|
6
|
+
// @ts-expect-error
|
|
7
|
+
import announcementOptions from 'virtual:announcement-options'
|
|
8
|
+
import AnnouncementButton from './AnnouncementButton.vue'
|
|
9
|
+
import AnnouncementIcon from './AnnouncementIcon.vue'
|
|
10
|
+
import { inBrowser, parseStringStyle, useDebounceFn, useWindowSize } from './util'
|
|
11
|
+
|
|
12
|
+
const popoverProps: AnnouncementOptions = announcementOptions
|
|
13
|
+
|
|
14
|
+
const show = ref((popoverProps?.duration ?? 0) >= 0)
|
|
15
|
+
|
|
16
|
+
const bodyContent = computed(() => {
|
|
17
|
+
return popoverProps?.body || []
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const footerContent = computed(() => {
|
|
21
|
+
return popoverProps?.footer || []
|
|
22
|
+
})
|
|
23
|
+
const storageKey = 'vitepress-plugin-announcement'
|
|
24
|
+
const closeFlag = `${storageKey}-close`
|
|
25
|
+
|
|
26
|
+
// 移动端最小化
|
|
27
|
+
const { width } = useWindowSize()
|
|
28
|
+
const router = useRouter()
|
|
29
|
+
const route = useRoute()
|
|
30
|
+
onMounted(() => {
|
|
31
|
+
if (!popoverProps?.title) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!inBrowser) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 取旧值
|
|
40
|
+
const oldValue = localStorage.getItem(storageKey)
|
|
41
|
+
const newValue = JSON.stringify(popoverProps)
|
|
42
|
+
localStorage.setItem(storageKey, newValue)
|
|
43
|
+
|
|
44
|
+
// 移动端最小化
|
|
45
|
+
if (width.value < 768 && popoverProps?.mobileMinify) {
|
|
46
|
+
show.value = false
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// >= 0 每次都展示,区别是否自动消失
|
|
51
|
+
if (Number(popoverProps?.duration ?? '') >= 0) {
|
|
52
|
+
show.value = true
|
|
53
|
+
if (popoverProps?.duration) {
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
show.value = false
|
|
56
|
+
}, popoverProps?.duration)
|
|
57
|
+
}
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (oldValue !== newValue && popoverProps?.duration === -1) {
|
|
62
|
+
// 当做新值处理
|
|
63
|
+
show.value = true
|
|
64
|
+
localStorage.removeItem(closeFlag)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 新旧相等,判断是否点击过close,没点击关闭依然展示
|
|
69
|
+
if (oldValue === newValue && popoverProps?.duration === -1 && !localStorage.getItem(closeFlag)) {
|
|
70
|
+
show.value = true
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const onAfterRouteChanged = useDebounceFn(() => {
|
|
75
|
+
popoverProps?.onRouteChanged?.(route, show)
|
|
76
|
+
}, 10)
|
|
77
|
+
|
|
78
|
+
watch(route, onAfterRouteChanged, { immediate: true })
|
|
79
|
+
|
|
80
|
+
function handleClose() {
|
|
81
|
+
show.value = false
|
|
82
|
+
if (popoverProps?.duration === -1) {
|
|
83
|
+
localStorage.setItem(closeFlag, `${+new Date()}`)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function PopoverValue(props: { key: number; item: Announcement.Value },
|
|
88
|
+
{ slots }: any) {
|
|
89
|
+
const { key, item } = props
|
|
90
|
+
if (item.type === 'title') {
|
|
91
|
+
return h(
|
|
92
|
+
'h4',
|
|
93
|
+
{
|
|
94
|
+
style: parseStringStyle(item.style || '')
|
|
95
|
+
},
|
|
96
|
+
item.content
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
if (item.type === 'text') {
|
|
100
|
+
return h(
|
|
101
|
+
'p',
|
|
102
|
+
{
|
|
103
|
+
style: parseStringStyle(item.style || '')
|
|
104
|
+
},
|
|
105
|
+
item.content
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
if (item.type === 'image') {
|
|
109
|
+
return h('img', {
|
|
110
|
+
src: item.src,
|
|
111
|
+
style: parseStringStyle(item.style || '')
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
if (item.type === 'button') {
|
|
115
|
+
return h(
|
|
116
|
+
AnnouncementButton,
|
|
117
|
+
{
|
|
118
|
+
type: 'primary',
|
|
119
|
+
onClick: () => {
|
|
120
|
+
if (/^\s*http(s)?:\/\//.test(item.link)) {
|
|
121
|
+
window.open(item.link)
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
router.go(item.link)
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
style: parseStringStyle(item.style || ''),
|
|
128
|
+
...item.props
|
|
129
|
+
},
|
|
130
|
+
slots
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
return h(
|
|
134
|
+
'div',
|
|
135
|
+
{
|
|
136
|
+
key
|
|
137
|
+
},
|
|
138
|
+
''
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const showReopen = computed(() => {
|
|
143
|
+
return !show.value && (popoverProps?.reopen ?? true) && !!popoverProps.title
|
|
144
|
+
})
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<template>
|
|
148
|
+
<div v-show="show" class="theme-blog-popover" data-pagefind-ignore="all">
|
|
149
|
+
<div class="header">
|
|
150
|
+
<div class="title-wrapper">
|
|
151
|
+
<AnnouncementIcon size="20px" :icon="popoverProps.icon">
|
|
152
|
+
<svg t="1716085184855" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4274" width="200" height="200"><path d="M660.48 872.448q6.144 0-3.584 15.36t-29.696 33.792-47.104 33.792-57.856 15.36q-27.648 0-53.248-15.36t-45.056-33.792-29.696-33.792-6.144-15.36l272.384 0zM914.432 785.408q7.168 9.216 6.656 17.92t-4.608 14.848-10.24 9.728-12.288 3.584l-747.52 0q-14.336 0-20.992-11.776t4.608-29.184q17.408-30.72 40.96-68.608t44.544-81.408 36.352-92.16 15.36-101.888q0-51.2 14.336-92.16t37.376-71.68 53.248-52.224 62.976-32.768q-16.384-26.624-16.384-55.296 0-41.984 28.672-70.656t70.656-28.672 70.656 28.672 28.672 70.656q0 14.336-4.096 28.16t-11.264 25.088q34.816 11.264 66.048 32.768t54.272 53.248 36.864 72.704 13.824 91.136q0 51.2 15.36 100.864t36.864 94.208 45.568 81.408 43.52 63.488zM478.208 142.336q0 16.384 11.264 28.16t27.648 11.776l2.048 0q16.384-1.024 27.648-12.288t11.264-27.648q0-17.408-11.264-28.672t-28.672-11.264-28.672 11.264-11.264 28.672z" p-id="4275" /></svg>
|
|
153
|
+
</AnnouncementIcon>
|
|
154
|
+
<span class="title">{{ popoverProps?.title }}</span>
|
|
155
|
+
</div>
|
|
156
|
+
<AnnouncementIcon class="close-icon" size="20px" :icon="popoverProps?.closeIcon" @click="handleClose">
|
|
157
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896m0 393.664L407.936 353.6a38.4 38.4 0 1 0-54.336 54.336L457.664 512 353.6 616.064a38.4 38.4 0 1 0 54.336 54.336L512 566.336 616.064 670.4a38.4 38.4 0 1 0 54.336-54.336L566.336 512 670.4 407.936a38.4 38.4 0 1 0-54.336-54.336z" /></svg>
|
|
158
|
+
</AnnouncementIcon>
|
|
159
|
+
</div>
|
|
160
|
+
<div v-if="bodyContent.length" class="body content">
|
|
161
|
+
<PopoverValue v-for="(v, idx) in bodyContent" :key="idx" :item="v">
|
|
162
|
+
{{ v.type !== 'image' ? v.content : '' }}
|
|
163
|
+
</PopoverValue>
|
|
164
|
+
<hr v-if="footerContent.length">
|
|
165
|
+
</div>
|
|
166
|
+
<div class="footer content">
|
|
167
|
+
<PopoverValue v-for="(v, idx) in footerContent" :key="idx" :item="v">
|
|
168
|
+
{{ v.type !== 'image' ? v.content : '' }}
|
|
169
|
+
</PopoverValue>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
<div
|
|
173
|
+
v-show="showReopen" class="theme-blog-popover-close"
|
|
174
|
+
:class="{ twinkle: popoverProps?.twinkle }"
|
|
175
|
+
@click="show = true"
|
|
176
|
+
>
|
|
177
|
+
<AnnouncementIcon :icon="popoverProps?.icon">
|
|
178
|
+
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4274" width="200" height="200"><path d="M660.48 872.448q6.144 0-3.584 15.36t-29.696 33.792-47.104 33.792-57.856 15.36q-27.648 0-53.248-15.36t-45.056-33.792-29.696-33.792-6.144-15.36l272.384 0zM914.432 785.408q7.168 9.216 6.656 17.92t-4.608 14.848-10.24 9.728-12.288 3.584l-747.52 0q-14.336 0-20.992-11.776t4.608-29.184q17.408-30.72 40.96-68.608t44.544-81.408 36.352-92.16 15.36-101.888q0-51.2 14.336-92.16t37.376-71.68 53.248-52.224 62.976-32.768q-16.384-26.624-16.384-55.296 0-41.984 28.672-70.656t70.656-28.672 70.656 28.672 28.672 70.656q0 14.336-4.096 28.16t-11.264 25.088q34.816 11.264 66.048 32.768t54.272 53.248 36.864 72.704 13.824 91.136q0 51.2 15.36 100.864t36.864 94.208 45.568 81.408 43.52 63.488zM478.208 142.336q0 16.384 11.264 28.16t27.648 11.776l2.048 0q16.384-1.024 27.648-12.288t11.264-27.648q0-17.408-11.264-28.672t-28.672-11.264-28.672 11.264-11.264 28.672z" p-id="4275" /></svg>
|
|
179
|
+
</AnnouncementIcon>
|
|
180
|
+
</div>
|
|
181
|
+
</template>
|
|
182
|
+
|
|
183
|
+
<style lang="css" scoped>
|
|
184
|
+
@import url(./style.css);
|
|
185
|
+
</style>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
type: 'primary' | 'success' | 'danger' | 'warning' | 'info' | 'text' | 'default'
|
|
6
|
+
onClick?: () => void
|
|
7
|
+
style?: Record<string, any>
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
const type = computed(() => props.type ?? 'primary')
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<button class="announcement-button" :class="`announcement-button-${type}`" @click="props.onClick">
|
|
15
|
+
<span>
|
|
16
|
+
<slot />
|
|
17
|
+
</span>
|
|
18
|
+
</button>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<style lang="css" scoped>
|
|
22
|
+
.announcement-button+.announcement-button{
|
|
23
|
+
margin-left: 12px;
|
|
24
|
+
}
|
|
25
|
+
.announcement-button{
|
|
26
|
+
--bgc: #409eff;
|
|
27
|
+
--bc:var(--bgc);
|
|
28
|
+
--c: #fff;
|
|
29
|
+
}
|
|
30
|
+
.announcement-button {
|
|
31
|
+
align-items: center;
|
|
32
|
+
-webkit-appearance: none;
|
|
33
|
+
background-color: var(--bgc);
|
|
34
|
+
border: 1px solid #dcdfe6;
|
|
35
|
+
border-color: var(--bc);
|
|
36
|
+
border-radius: 4px;
|
|
37
|
+
box-sizing: border-box;
|
|
38
|
+
color: var(--c);
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
display: inline-flex;
|
|
41
|
+
font-size: 14px;
|
|
42
|
+
font-weight: 500;
|
|
43
|
+
height: 32px;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
line-height: 1;
|
|
46
|
+
outline: none;
|
|
47
|
+
padding: 8px 15px;
|
|
48
|
+
text-align: center;
|
|
49
|
+
transition: .1s;
|
|
50
|
+
-webkit-user-select: none;
|
|
51
|
+
-moz-user-select: none;
|
|
52
|
+
-ms-user-select: none;
|
|
53
|
+
user-select: none;
|
|
54
|
+
vertical-align: middle;
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.announcement-button-primary {
|
|
59
|
+
--bgc: var(--vp-c-brand-2);
|
|
60
|
+
}
|
|
61
|
+
.announcement-button-success {
|
|
62
|
+
--bgc: #67c23a;
|
|
63
|
+
}
|
|
64
|
+
.announcement-button-danger {
|
|
65
|
+
--bgc: #f56c6c;
|
|
66
|
+
}
|
|
67
|
+
.announcement-button-warning {
|
|
68
|
+
--bgc: #e6a23c;
|
|
69
|
+
}
|
|
70
|
+
.announcement-button-info {
|
|
71
|
+
--bgc: #909399;
|
|
72
|
+
}
|
|
73
|
+
.announcement-button-text {
|
|
74
|
+
--bgc: transparent;
|
|
75
|
+
--bc: transparent;
|
|
76
|
+
--c: var(--vp-c-brand-2);
|
|
77
|
+
padding-left: 0;
|
|
78
|
+
padding-right: 0;
|
|
79
|
+
}
|
|
80
|
+
.announcement-button-default {
|
|
81
|
+
--bgc: #fff;
|
|
82
|
+
--bc: #dcdfe6;
|
|
83
|
+
--c: #606266;
|
|
84
|
+
}
|
|
85
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
size?: string
|
|
6
|
+
icon?: string
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
const size = computed(() => props.size && (typeof props.size === 'number' ? `${props.size}px` : props.size))
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<i v-if="props.icon" class="announcement-icon" :style="{ fontSize: size }" v-html="props.icon" />
|
|
14
|
+
<i v-else class="announcement-icon" :style="{ fontSize: size }">
|
|
15
|
+
<slot />
|
|
16
|
+
</i>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<style lang="css" scoped>
|
|
20
|
+
.announcement-icon {
|
|
21
|
+
--color: inherit;
|
|
22
|
+
align-items: center;
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
height: 1em;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
line-height: 1em;
|
|
27
|
+
position: relative;
|
|
28
|
+
width: 1em;
|
|
29
|
+
fill: currentColor;
|
|
30
|
+
color: var(--color);
|
|
31
|
+
font-size: inherit;
|
|
32
|
+
}
|
|
33
|
+
</style>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
.theme-blog-popover {
|
|
2
|
+
width: 258px;
|
|
3
|
+
position: fixed;
|
|
4
|
+
top: 80px;
|
|
5
|
+
right: 20px;
|
|
6
|
+
z-index: 22;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
border: 1px solid var(--vp-c-brand-3);
|
|
9
|
+
border-radius: 6px;
|
|
10
|
+
background-color: rgba(var(--bg-gradient-home));
|
|
11
|
+
box-shadow: var(--box-shadow);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.header {
|
|
15
|
+
background-color: var(--vp-c-brand-3);
|
|
16
|
+
color: #fff;
|
|
17
|
+
padding: 6px 4px;
|
|
18
|
+
display: flex;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
align-items: center;
|
|
21
|
+
}
|
|
22
|
+
.header .close-icon {
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.title-wrapper {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
}
|
|
30
|
+
.title-wrapper .title {
|
|
31
|
+
font-size: 14px;
|
|
32
|
+
padding-left: 6px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.body {
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
padding: 10px 10px 0;
|
|
38
|
+
}
|
|
39
|
+
.body hr {
|
|
40
|
+
border: none;
|
|
41
|
+
border-bottom: 1px solid #eaecef;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.footer {
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
padding: 10px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.body.content,
|
|
50
|
+
.footer.content {
|
|
51
|
+
text-align: center;
|
|
52
|
+
}
|
|
53
|
+
.body.content h4,
|
|
54
|
+
.footer.content h4 {
|
|
55
|
+
text-align: center;
|
|
56
|
+
font-size: 12px;
|
|
57
|
+
}
|
|
58
|
+
.body.content p,
|
|
59
|
+
.footer.content p {
|
|
60
|
+
text-align: center;
|
|
61
|
+
padding: 10px 0;
|
|
62
|
+
font-size: 14px;
|
|
63
|
+
}
|
|
64
|
+
.body.content img,
|
|
65
|
+
.footer.content img {
|
|
66
|
+
width: 100%;
|
|
67
|
+
height: 100px;
|
|
68
|
+
object-fit: contain;
|
|
69
|
+
margin: 0 auto;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.theme-blog-popover-close {
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
opacity: 0.5;
|
|
75
|
+
position: fixed;
|
|
76
|
+
z-index: 22;
|
|
77
|
+
top: 80px;
|
|
78
|
+
right: 10px;
|
|
79
|
+
position: fixed;
|
|
80
|
+
background-color: var(--vp-c-brand-3);
|
|
81
|
+
padding: 8px;
|
|
82
|
+
color: #fff;
|
|
83
|
+
font-size: 12px;
|
|
84
|
+
border-radius: 50%;
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.theme-blog-popover-close.twinkle {
|
|
90
|
+
animation: twinkle 1s ease-in-out infinite;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@keyframes twinkle {
|
|
94
|
+
0% {
|
|
95
|
+
opacity: 0.5;
|
|
96
|
+
}
|
|
97
|
+
50% {
|
|
98
|
+
opacity: 0;
|
|
99
|
+
}
|
|
100
|
+
100% {
|
|
101
|
+
opacity: 0.5;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function parseStringStyle(cssText: string): Record<string, string | number>;
|
|
2
|
+
export declare function useDebounceFn<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: any[]) => void;
|
|
3
|
+
export declare const inBrowser: boolean;
|
|
4
|
+
export declare function useWindowSize(): {
|
|
5
|
+
width: import("vue").Ref<number, number>;
|
|
6
|
+
height: import("vue").Ref<number, number>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { onMounted, onUnmounted, ref } from 'vue';
|
|
2
|
+
const listDelimiterRE = /;(?![^(]*\))/g;
|
|
3
|
+
const propertyDelimiterRE = /:([^]+)/;
|
|
4
|
+
const styleCommentRE = /\/\*[^]*?\*\//g;
|
|
5
|
+
export function parseStringStyle(cssText) {
|
|
6
|
+
const ret = {};
|
|
7
|
+
cssText.replace(styleCommentRE, '').split(listDelimiterRE).forEach((item) => {
|
|
8
|
+
if (item) {
|
|
9
|
+
const tmp = item.split(propertyDelimiterRE);
|
|
10
|
+
tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
return ret;
|
|
14
|
+
}
|
|
15
|
+
export function useDebounceFn(fn, delay) {
|
|
16
|
+
let timer;
|
|
17
|
+
return (...args) => {
|
|
18
|
+
clearTimeout(timer);
|
|
19
|
+
timer = setTimeout(() => {
|
|
20
|
+
fn(...args);
|
|
21
|
+
}, delay);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export const inBrowser = typeof document !== 'undefined';
|
|
25
|
+
export function useWindowSize() {
|
|
26
|
+
const width = ref(Number.POSITIVE_INFINITY);
|
|
27
|
+
const height = ref(Number.POSITIVE_INFINITY);
|
|
28
|
+
const updateSize = useDebounceFn(() => {
|
|
29
|
+
if (inBrowser) {
|
|
30
|
+
width.value = window.innerWidth;
|
|
31
|
+
height.value = window.innerHeight;
|
|
32
|
+
}
|
|
33
|
+
}, 100);
|
|
34
|
+
onMounted(() => {
|
|
35
|
+
if (inBrowser) {
|
|
36
|
+
window.addEventListener('resize', updateSize, { passive: true });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
onUnmounted(() => {
|
|
40
|
+
if (inBrowser) {
|
|
41
|
+
window.removeEventListener('resize', updateSize);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
updateSize();
|
|
45
|
+
return { width, height };
|
|
46
|
+
}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { Route } from 'vitepress';
|
|
3
|
+
|
|
4
|
+
interface AnnouncementOptions {
|
|
5
|
+
/**
|
|
6
|
+
* 公告标题
|
|
7
|
+
*/
|
|
8
|
+
title: string;
|
|
9
|
+
/**
|
|
10
|
+
* 公告主要内容
|
|
11
|
+
*/
|
|
12
|
+
body?: Announcement.Value[];
|
|
13
|
+
/**
|
|
14
|
+
* 公告底部内容
|
|
15
|
+
*/
|
|
16
|
+
footer?: Announcement.Value[];
|
|
17
|
+
/**
|
|
18
|
+
* 是否只在浏览器环境渲染组件
|
|
19
|
+
* @default false
|
|
20
|
+
* @doc https://vitepress.dev/guide/ssr-compat#clientonly
|
|
21
|
+
*/
|
|
22
|
+
clientOnly?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 展示时机控制
|
|
25
|
+
*
|
|
26
|
+
* -1 只展示1次;>= 0 每次都展示,一定时间后自动消失,0 不自动消失
|
|
27
|
+
*
|
|
28
|
+
* 配置发生改变时,会重新触发此规则
|
|
29
|
+
* @default 0
|
|
30
|
+
*/
|
|
31
|
+
duration?: number;
|
|
32
|
+
/**
|
|
33
|
+
* 移动端自动最小化
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
mobileMinify?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 支持重新打开(右上角 icon 悬浮)
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
reopen?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 是否打开闪烁提示,通常需要和 reopen 搭配使用
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
twinkle?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* 设置展示图标,svg
|
|
49
|
+
*/
|
|
50
|
+
icon?: string;
|
|
51
|
+
/**
|
|
52
|
+
* 设置关闭图标,svg
|
|
53
|
+
*/
|
|
54
|
+
closeIcon?: string;
|
|
55
|
+
/**
|
|
56
|
+
* 自定义展示策略
|
|
57
|
+
* @param to 切换到的目标路由
|
|
58
|
+
*/
|
|
59
|
+
onRouteChanged?: (to: Route, show: Ref<boolean>) => void;
|
|
60
|
+
}
|
|
61
|
+
declare namespace Announcement {
|
|
62
|
+
interface Title {
|
|
63
|
+
type: 'title';
|
|
64
|
+
content: string;
|
|
65
|
+
style?: string;
|
|
66
|
+
}
|
|
67
|
+
interface Text {
|
|
68
|
+
type: 'text';
|
|
69
|
+
content: string;
|
|
70
|
+
style?: string;
|
|
71
|
+
}
|
|
72
|
+
interface Image {
|
|
73
|
+
type: 'image';
|
|
74
|
+
src: string;
|
|
75
|
+
style?: string;
|
|
76
|
+
}
|
|
77
|
+
interface Button {
|
|
78
|
+
type: 'button';
|
|
79
|
+
link: string;
|
|
80
|
+
content: string;
|
|
81
|
+
style?: string;
|
|
82
|
+
props?: any;
|
|
83
|
+
}
|
|
84
|
+
type Value = Title | Text | Image | Button;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare function AnnouncementPlugin(options: AnnouncementOptions): any;
|
|
88
|
+
|
|
89
|
+
export { Announcement, AnnouncementOptions, AnnouncementPlugin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { Route } from 'vitepress';
|
|
3
|
+
|
|
4
|
+
interface AnnouncementOptions {
|
|
5
|
+
/**
|
|
6
|
+
* 公告标题
|
|
7
|
+
*/
|
|
8
|
+
title: string;
|
|
9
|
+
/**
|
|
10
|
+
* 公告主要内容
|
|
11
|
+
*/
|
|
12
|
+
body?: Announcement.Value[];
|
|
13
|
+
/**
|
|
14
|
+
* 公告底部内容
|
|
15
|
+
*/
|
|
16
|
+
footer?: Announcement.Value[];
|
|
17
|
+
/**
|
|
18
|
+
* 是否只在浏览器环境渲染组件
|
|
19
|
+
* @default false
|
|
20
|
+
* @doc https://vitepress.dev/guide/ssr-compat#clientonly
|
|
21
|
+
*/
|
|
22
|
+
clientOnly?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 展示时机控制
|
|
25
|
+
*
|
|
26
|
+
* -1 只展示1次;>= 0 每次都展示,一定时间后自动消失,0 不自动消失
|
|
27
|
+
*
|
|
28
|
+
* 配置发生改变时,会重新触发此规则
|
|
29
|
+
* @default 0
|
|
30
|
+
*/
|
|
31
|
+
duration?: number;
|
|
32
|
+
/**
|
|
33
|
+
* 移动端自动最小化
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
mobileMinify?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 支持重新打开(右上角 icon 悬浮)
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
reopen?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 是否打开闪烁提示,通常需要和 reopen 搭配使用
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
twinkle?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* 设置展示图标,svg
|
|
49
|
+
*/
|
|
50
|
+
icon?: string;
|
|
51
|
+
/**
|
|
52
|
+
* 设置关闭图标,svg
|
|
53
|
+
*/
|
|
54
|
+
closeIcon?: string;
|
|
55
|
+
/**
|
|
56
|
+
* 自定义展示策略
|
|
57
|
+
* @param to 切换到的目标路由
|
|
58
|
+
*/
|
|
59
|
+
onRouteChanged?: (to: Route, show: Ref<boolean>) => void;
|
|
60
|
+
}
|
|
61
|
+
declare namespace Announcement {
|
|
62
|
+
interface Title {
|
|
63
|
+
type: 'title';
|
|
64
|
+
content: string;
|
|
65
|
+
style?: string;
|
|
66
|
+
}
|
|
67
|
+
interface Text {
|
|
68
|
+
type: 'text';
|
|
69
|
+
content: string;
|
|
70
|
+
style?: string;
|
|
71
|
+
}
|
|
72
|
+
interface Image {
|
|
73
|
+
type: 'image';
|
|
74
|
+
src: string;
|
|
75
|
+
style?: string;
|
|
76
|
+
}
|
|
77
|
+
interface Button {
|
|
78
|
+
type: 'button';
|
|
79
|
+
link: string;
|
|
80
|
+
content: string;
|
|
81
|
+
style?: string;
|
|
82
|
+
props?: any;
|
|
83
|
+
}
|
|
84
|
+
type Value = Title | Text | Image | Button;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare function AnnouncementPlugin(options: AnnouncementOptions): any;
|
|
88
|
+
|
|
89
|
+
export { Announcement, AnnouncementOptions, AnnouncementPlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
AnnouncementPlugin: () => AnnouncementPlugin
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(src_exports);
|
|
36
|
+
var import_node_path = __toESM(require("path"));
|
|
37
|
+
var import_node_url = require("url");
|
|
38
|
+
var import_javascript_stringify = require("javascript-stringify");
|
|
39
|
+
var import_meta = {};
|
|
40
|
+
function isESM() {
|
|
41
|
+
return typeof __filename === "undefined" || typeof __dirname === "undefined";
|
|
42
|
+
}
|
|
43
|
+
function getDirname() {
|
|
44
|
+
return isESM() ? import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)) : __dirname;
|
|
45
|
+
}
|
|
46
|
+
var componentName = "Announcement";
|
|
47
|
+
var componentFile = `${componentName}.vue`;
|
|
48
|
+
var aliasComponentFile = `${getDirname()}/components/${componentFile}`;
|
|
49
|
+
var virtualModuleId = "virtual:announcement-options";
|
|
50
|
+
var resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
51
|
+
function AnnouncementPlugin(options) {
|
|
52
|
+
const componentOptions = {
|
|
53
|
+
clientOnly: false,
|
|
54
|
+
duration: 0,
|
|
55
|
+
mobileMinify: false,
|
|
56
|
+
reopen: true,
|
|
57
|
+
twinkle: false,
|
|
58
|
+
...options
|
|
59
|
+
};
|
|
60
|
+
const pluginOps = {
|
|
61
|
+
name: "vitepress-plugin-announcement",
|
|
62
|
+
enforce: "pre",
|
|
63
|
+
config: () => {
|
|
64
|
+
return {
|
|
65
|
+
resolve: {
|
|
66
|
+
alias: {
|
|
67
|
+
[`./${componentFile}`]: aliasComponentFile
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
transform(code, id) {
|
|
73
|
+
if (id.endsWith("vitepress/dist/client/theme-default/Layout.vue")) {
|
|
74
|
+
const slotPosition = '<slot name="layout-top" />';
|
|
75
|
+
let transformResult = code.replace(slotPosition, `${slotPosition}<Announcement/>`);
|
|
76
|
+
if (componentOptions.clientOnly) {
|
|
77
|
+
transformResult = transformResult.replace("<Announcement/>", "<ClientOnly><Announcement/></ClientOnly>");
|
|
78
|
+
}
|
|
79
|
+
const setupPosition = '<script setup lang="ts">';
|
|
80
|
+
transformResult = transformResult.replace(setupPosition, `${setupPosition}
|
|
81
|
+
import Announcement from './Announcement.vue'`);
|
|
82
|
+
return transformResult;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
resolveId(id) {
|
|
86
|
+
if (id === virtualModuleId) {
|
|
87
|
+
return resolvedVirtualModuleId;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
load(id) {
|
|
91
|
+
if (id === resolvedVirtualModuleId) {
|
|
92
|
+
return `export default ${(0, import_javascript_stringify.stringify)(options)}`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return pluginOps;
|
|
97
|
+
}
|
|
98
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
99
|
+
0 && (module.exports = {
|
|
100
|
+
AnnouncementPlugin
|
|
101
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { stringify } from "javascript-stringify";
|
|
5
|
+
function isESM() {
|
|
6
|
+
return typeof __filename === "undefined" || typeof __dirname === "undefined";
|
|
7
|
+
}
|
|
8
|
+
function getDirname() {
|
|
9
|
+
return isESM() ? path.dirname(fileURLToPath(import.meta.url)) : __dirname;
|
|
10
|
+
}
|
|
11
|
+
var componentName = "Announcement";
|
|
12
|
+
var componentFile = `${componentName}.vue`;
|
|
13
|
+
var aliasComponentFile = `${getDirname()}/components/${componentFile}`;
|
|
14
|
+
var virtualModuleId = "virtual:announcement-options";
|
|
15
|
+
var resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
16
|
+
function AnnouncementPlugin(options) {
|
|
17
|
+
const componentOptions = {
|
|
18
|
+
clientOnly: false,
|
|
19
|
+
duration: 0,
|
|
20
|
+
mobileMinify: false,
|
|
21
|
+
reopen: true,
|
|
22
|
+
twinkle: false,
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
const pluginOps = {
|
|
26
|
+
name: "vitepress-plugin-announcement",
|
|
27
|
+
enforce: "pre",
|
|
28
|
+
config: () => {
|
|
29
|
+
return {
|
|
30
|
+
resolve: {
|
|
31
|
+
alias: {
|
|
32
|
+
[`./${componentFile}`]: aliasComponentFile
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
transform(code, id) {
|
|
38
|
+
if (id.endsWith("vitepress/dist/client/theme-default/Layout.vue")) {
|
|
39
|
+
const slotPosition = '<slot name="layout-top" />';
|
|
40
|
+
let transformResult = code.replace(slotPosition, `${slotPosition}<Announcement/>`);
|
|
41
|
+
if (componentOptions.clientOnly) {
|
|
42
|
+
transformResult = transformResult.replace("<Announcement/>", "<ClientOnly><Announcement/></ClientOnly>");
|
|
43
|
+
}
|
|
44
|
+
const setupPosition = '<script setup lang="ts">';
|
|
45
|
+
transformResult = transformResult.replace(setupPosition, `${setupPosition}
|
|
46
|
+
import Announcement from './Announcement.vue'`);
|
|
47
|
+
return transformResult;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
resolveId(id) {
|
|
51
|
+
if (id === virtualModuleId) {
|
|
52
|
+
return resolvedVirtualModuleId;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
load(id) {
|
|
56
|
+
if (id === resolvedVirtualModuleId) {
|
|
57
|
+
return `export default ${stringify(options)}`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return pluginOps;
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
AnnouncementPlugin
|
|
65
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vitepress-plugin-announcement",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "vitepress plugin, Announcement, 公告窗口",
|
|
5
|
+
"author": "sugar",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/ATQQ/sugar-blog/tree/master/packages/vitepress-plugin-announcement",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/ATQQ/sugar-blog.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/ATQQ/sugar-blog/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"vitepress",
|
|
17
|
+
"plugin",
|
|
18
|
+
"announcement",
|
|
19
|
+
"公告窗口",
|
|
20
|
+
"公告"
|
|
21
|
+
],
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/index.mjs",
|
|
25
|
+
"require": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"main": "dist/index.js",
|
|
29
|
+
"module": "dist/index.mjs",
|
|
30
|
+
"types": "dist/index.d.ts",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"vitepress": "^1"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"javascript-stringify": "^2.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"chokidar": "^3.6.0",
|
|
42
|
+
"fs-extra": "^11.1.1",
|
|
43
|
+
"tinyglobby": "^0.2.6",
|
|
44
|
+
"typescript": "^5.5.4",
|
|
45
|
+
"vite": "^5",
|
|
46
|
+
"vitepress": "^1.3.4",
|
|
47
|
+
"vue": "^3.4.26"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"dev": "pnpm run /^dev:.*/",
|
|
51
|
+
"dev:plugin": "npx tsup src/index.ts --dts --watch --format esm,cjs --external vitepress",
|
|
52
|
+
"dev:component": "tsc --sourcemap -w --preserveWatchOutput -p src/components",
|
|
53
|
+
"dev:watch": "node scripts/watchAndCopy.mjs",
|
|
54
|
+
"build": "pnpm run /^build:.*/",
|
|
55
|
+
"build:plugin": "npx tsup src/index.ts --dts --format esm,cjs --external vitepress --silent",
|
|
56
|
+
"build:component": "tsc -p src/components && node scripts/copyComponents.mjs"
|
|
57
|
+
}
|
|
58
|
+
}
|