vitepress-plugin-back2top 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 +75 -0
- package/dist/components/BackToTop.vue +106 -0
- package/dist/components/Icon.vue +33 -0
- package/dist/components/style.css +15 -0
- package/dist/components/util.d.ts +3 -0
- package/dist/components/util.js +23 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -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,75 @@
|
|
|
1
|
+
# vitepress-plugin-back2top
|
|
2
|
+
|
|
3
|
+
[English](https://github.com/ATQQ/sugar-blog/blob/master/packages/vitepress-plugin-back2top/README-en.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
VitePress 回到顶部插件,为页面添加一个回到顶部的按钮。
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## 安装
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add vitepress-plugin-back2top
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 使用
|
|
16
|
+
|
|
17
|
+
在 `.vitepress/config.ts` 中引入插件:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { defineConfig } from 'vitepress'
|
|
21
|
+
import { back2topPlugin } from 'vitepress-plugin-back2top'
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
vite: {
|
|
25
|
+
plugins: [
|
|
26
|
+
back2topPlugin({
|
|
27
|
+
// ...配置项
|
|
28
|
+
})
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Frontmatter 配置
|
|
35
|
+
|
|
36
|
+
你也可以在单篇文章的 `frontmatter` 中动态覆盖或关闭回到顶部配置:
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
---
|
|
40
|
+
# 关闭回到顶部
|
|
41
|
+
backToTop: false
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
# 覆盖回到顶部配置
|
|
46
|
+
backToTop:
|
|
47
|
+
top: 100
|
|
48
|
+
icon: <svg>...</svg>
|
|
49
|
+
---
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 选项
|
|
53
|
+
|
|
54
|
+
| 参数 | 说明 | 类型 | 默认值 |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| top | 距离顶部多少距离出现 | `number` | `450` |
|
|
57
|
+
| icon | 自定义 SVG 图标代码 | `string` | - |
|
|
58
|
+
|
|
59
|
+
## 类型定义
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
export interface BackToTopPluginOptions {
|
|
63
|
+
/**
|
|
64
|
+
* 距离顶部多少距离出现
|
|
65
|
+
* @default 450
|
|
66
|
+
*/
|
|
67
|
+
top?: number
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 设置展示图标,svg
|
|
71
|
+
* @recommend https://iconbuddy.app/search?q=fire
|
|
72
|
+
*/
|
|
73
|
+
icon?: string
|
|
74
|
+
}
|
|
75
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { useElementSize, useScroll } from '@vueuse/core'
|
|
3
|
+
import { computed, onMounted, ref } from 'vue'
|
|
4
|
+
import { useData } from 'vitepress'
|
|
5
|
+
|
|
6
|
+
// @ts-expect-error
|
|
7
|
+
import pluginOptions from 'virtual:back2top-plugin-options'
|
|
8
|
+
import Icon from './Icon.vue'
|
|
9
|
+
|
|
10
|
+
function handleBackRoTop() {
|
|
11
|
+
window.scrollTo({ top: 0, behavior: 'smooth' })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const $vpDoc = typeof document !== 'undefined' ? document.querySelector('.vp-doc') || document.body : null
|
|
15
|
+
const el = ref<any>($vpDoc)
|
|
16
|
+
const { width } = useElementSize(el)
|
|
17
|
+
const docWidth = computed(() => `${width.value}px`)
|
|
18
|
+
|
|
19
|
+
onMounted(() => {
|
|
20
|
+
const vpDoc = document.querySelector('.vp-doc')
|
|
21
|
+
if (vpDoc) {
|
|
22
|
+
el.value = vpDoc
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const { frontmatter } = useData()
|
|
27
|
+
|
|
28
|
+
const backToTopConfig = computed(() => {
|
|
29
|
+
// If explicitly disabled in frontmatter
|
|
30
|
+
if (frontmatter.value.backToTop === false) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const fmConfig = frontmatter.value.backToTop
|
|
35
|
+
if (typeof fmConfig === 'object') {
|
|
36
|
+
return { ...pluginOptions, ...fmConfig }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return pluginOptions
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const { y } = useScroll(typeof window !== 'undefined' ? window : null)
|
|
43
|
+
const defaultTriggerHeight = 450
|
|
44
|
+
const triggerTop = computed(() => backToTopConfig.value?.top ?? defaultTriggerHeight)
|
|
45
|
+
|
|
46
|
+
const show = computed(() => backToTopConfig.value && width.value && y.value > triggerTop.value)
|
|
47
|
+
|
|
48
|
+
const iconSVGStr = computed(() => backToTopConfig?.value?.icon)
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<div v-show="show" class="back-to-top">
|
|
53
|
+
<span class="icon-wrapper" @click="handleBackRoTop">
|
|
54
|
+
<Icon :size="20" :icon="iconSVGStr">
|
|
55
|
+
<svg width="512" height="512" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
56
|
+
<path
|
|
57
|
+
fill="currentColor"
|
|
58
|
+
d="m20 22l-3.86-1.55c.7-1.53 1.2-3.11 1.51-4.72zM7.86 20.45L4 22l2.35-6.27c.31 1.61.81 3.19 1.51 4.72M12 2s5 2 5 10c0 3.1-.75 5.75-1.67 7.83A2 2 0 0 1 13.5 21h-3a2 2 0 0 1-1.83-1.17C7.76 17.75 7 15.1 7 12c0-8 5-10 5-10m0 10c1.1 0 2-.9 2-2s-.9-2-2-2s-2 .9-2 2s.9 2 2 2"
|
|
59
|
+
/>
|
|
60
|
+
</svg>
|
|
61
|
+
</Icon>
|
|
62
|
+
</span>
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<style scoped>
|
|
67
|
+
.back-to-top {
|
|
68
|
+
position: fixed;
|
|
69
|
+
width: v-bind(docWidth);
|
|
70
|
+
text-align: right;
|
|
71
|
+
bottom: 80px;
|
|
72
|
+
font-size: 16px;
|
|
73
|
+
transition: all 0.3s ease-in-out;
|
|
74
|
+
opacity: 0.6;
|
|
75
|
+
display: flex;
|
|
76
|
+
justify-content: right;
|
|
77
|
+
z-index: 200;
|
|
78
|
+
}
|
|
79
|
+
.back-to-top:hover {
|
|
80
|
+
opacity: 1;
|
|
81
|
+
}
|
|
82
|
+
.back-to-top .icon-wrapper {
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
border-radius: 50%;
|
|
85
|
+
position: relative;
|
|
86
|
+
right: -80px;
|
|
87
|
+
background-color: var(--vp-c-bg);
|
|
88
|
+
box-shadow: var(--box-shadow);
|
|
89
|
+
padding: 4px;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
background-color: var(--vp-c-brand-soft);
|
|
94
|
+
color: var(--vp-c-brand-1);
|
|
95
|
+
}
|
|
96
|
+
.back-to-top .icon-wrapper:hover {
|
|
97
|
+
box-shadow: var(--box-shadow-hover);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@media screen and (max-width: 1200px) {
|
|
101
|
+
.back-to-top .icon-wrapper {
|
|
102
|
+
border-radius: 50%;
|
|
103
|
+
position: static;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
size?: string | number
|
|
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="sugar-theme-icon" :style="{ fontSize: size }" v-html="props.icon" />
|
|
14
|
+
<i v-else class="sugar-theme-icon" :style="{ fontSize: size }">
|
|
15
|
+
<slot />
|
|
16
|
+
</i>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<style lang="css" scoped>
|
|
20
|
+
.sugar-theme-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,15 @@
|
|
|
1
|
+
/* TODO:按实际情况添加不同模式的变量 */
|
|
2
|
+
html .demo {
|
|
3
|
+
--box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.1);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
html.dark .demo {
|
|
7
|
+
--box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.6);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.parent-demo{
|
|
11
|
+
text-align: center;
|
|
12
|
+
}
|
|
13
|
+
.parent-demo h1{
|
|
14
|
+
color: red;
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const listDelimiterRE = /;(?![^(]*\))/g;
|
|
2
|
+
const propertyDelimiterRE = /:([^]+)/;
|
|
3
|
+
const styleCommentRE = /\/\*[^]*?\*\//g;
|
|
4
|
+
export function parseStringStyle(cssText) {
|
|
5
|
+
const ret = {};
|
|
6
|
+
cssText.replace(styleCommentRE, '').split(listDelimiterRE).forEach((item) => {
|
|
7
|
+
if (item) {
|
|
8
|
+
const tmp = item.split(propertyDelimiterRE);
|
|
9
|
+
tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
return ret;
|
|
13
|
+
}
|
|
14
|
+
export function useDebounceFn(fn, delay) {
|
|
15
|
+
let timer;
|
|
16
|
+
return (...args) => {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
timer = setTimeout(() => {
|
|
19
|
+
fn(...args);
|
|
20
|
+
}, delay);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export const inBrowser = typeof document !== 'undefined';
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PluginOption } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface BackToTopPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 距离顶部多少距离出现
|
|
6
|
+
* @default 450
|
|
7
|
+
*/
|
|
8
|
+
top?: number;
|
|
9
|
+
/**
|
|
10
|
+
* 设置展示图标,svg
|
|
11
|
+
* @recommend https://iconbuddy.app/search?q=fire
|
|
12
|
+
*/
|
|
13
|
+
icon?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare function back2topPlugin(options?: BackToTopPluginOptions): PluginOption;
|
|
17
|
+
|
|
18
|
+
export { BackToTopPluginOptions, back2topPlugin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PluginOption } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface BackToTopPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 距离顶部多少距离出现
|
|
6
|
+
* @default 450
|
|
7
|
+
*/
|
|
8
|
+
top?: number;
|
|
9
|
+
/**
|
|
10
|
+
* 设置展示图标,svg
|
|
11
|
+
* @recommend https://iconbuddy.app/search?q=fire
|
|
12
|
+
*/
|
|
13
|
+
icon?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare function back2topPlugin(options?: BackToTopPluginOptions): PluginOption;
|
|
17
|
+
|
|
18
|
+
export { BackToTopPluginOptions, back2topPlugin };
|
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
|
+
back2topPlugin: () => back2topPlugin
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(src_exports);
|
|
36
|
+
var import_javascript_stringify = require("javascript-stringify");
|
|
37
|
+
|
|
38
|
+
// src/util.ts
|
|
39
|
+
var import_node_path = __toESM(require("path"));
|
|
40
|
+
var import_node_url = require("url");
|
|
41
|
+
var import_meta = {};
|
|
42
|
+
function isESM() {
|
|
43
|
+
return typeof __filename === "undefined" || typeof __dirname === "undefined";
|
|
44
|
+
}
|
|
45
|
+
function getDirname() {
|
|
46
|
+
return isESM() ? import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)) : __dirname;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/index.ts
|
|
50
|
+
var componentName = "BackToTop";
|
|
51
|
+
var componentFile = `${componentName}.vue`;
|
|
52
|
+
var aliasComponentFile = `${getDirname()}/components/${componentFile}`;
|
|
53
|
+
var virtualModuleId = "virtual:back2top-plugin-options";
|
|
54
|
+
var resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
55
|
+
var slots = ["doc-after"];
|
|
56
|
+
function back2topPlugin(options) {
|
|
57
|
+
const componentOptions = {
|
|
58
|
+
...options
|
|
59
|
+
};
|
|
60
|
+
const pluginOps = {
|
|
61
|
+
name: "vitepress-plugin-back2top",
|
|
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
|
+
let transformResult = code;
|
|
75
|
+
for (const element of slots) {
|
|
76
|
+
const slotPosition = `<slot name="${element}" />`;
|
|
77
|
+
transformResult = transformResult.replace(slotPosition, `${slotPosition}<ClientOnly><${componentName} /></ClientOnly>`);
|
|
78
|
+
}
|
|
79
|
+
const setupPosition = '<script setup lang="ts">';
|
|
80
|
+
transformResult = transformResult.replace(setupPosition, `${setupPosition}
|
|
81
|
+
import ${componentName} from './${componentName}.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)(componentOptions)}`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return pluginOps;
|
|
97
|
+
}
|
|
98
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
99
|
+
0 && (module.exports = {
|
|
100
|
+
back2topPlugin
|
|
101
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { stringify } from "javascript-stringify";
|
|
3
|
+
|
|
4
|
+
// src/util.ts
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
function isESM() {
|
|
8
|
+
return typeof __filename === "undefined" || typeof __dirname === "undefined";
|
|
9
|
+
}
|
|
10
|
+
function getDirname() {
|
|
11
|
+
return isESM() ? path.dirname(fileURLToPath(import.meta.url)) : __dirname;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/index.ts
|
|
15
|
+
var componentName = "BackToTop";
|
|
16
|
+
var componentFile = `${componentName}.vue`;
|
|
17
|
+
var aliasComponentFile = `${getDirname()}/components/${componentFile}`;
|
|
18
|
+
var virtualModuleId = "virtual:back2top-plugin-options";
|
|
19
|
+
var resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
20
|
+
var slots = ["doc-after"];
|
|
21
|
+
function back2topPlugin(options) {
|
|
22
|
+
const componentOptions = {
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
const pluginOps = {
|
|
26
|
+
name: "vitepress-plugin-back2top",
|
|
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
|
+
let transformResult = code;
|
|
40
|
+
for (const element of slots) {
|
|
41
|
+
const slotPosition = `<slot name="${element}" />`;
|
|
42
|
+
transformResult = transformResult.replace(slotPosition, `${slotPosition}<ClientOnly><${componentName} /></ClientOnly>`);
|
|
43
|
+
}
|
|
44
|
+
const setupPosition = '<script setup lang="ts">';
|
|
45
|
+
transformResult = transformResult.replace(setupPosition, `${setupPosition}
|
|
46
|
+
import ${componentName} from './${componentName}.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(componentOptions)}`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return pluginOps;
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
back2topPlugin
|
|
65
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vitepress-plugin-back2top",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "vitepress plugin back to top",
|
|
5
|
+
"author": "sugar",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/ATQQ/sugar-blog/tree/master/packages/vitepress-plugin-back2top",
|
|
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
|
+
"back2top"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": "./dist/index.mjs",
|
|
23
|
+
"require": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"main": "dist/index.js",
|
|
27
|
+
"module": "dist/index.mjs",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"vitepress": "^1 || ^2"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@vueuse/core": "^14.1.0",
|
|
37
|
+
"javascript-stringify": "^2.1.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"chokidar": "^3.6.0",
|
|
41
|
+
"fs-extra": "^11.1.1",
|
|
42
|
+
"tinyglobby": "^0.2.6",
|
|
43
|
+
"tsup": " ^7.2.0",
|
|
44
|
+
"typescript": "^5.5.4",
|
|
45
|
+
"vite": "^5",
|
|
46
|
+
"vitepress": "2.0.0-alpha.16",
|
|
47
|
+
"vue": "^3.5.24"
|
|
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
|
+
}
|