vitepress-plugin-collapse 0.7.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 +168 -0
- package/dist/client/browser/index.d.ts +71 -0
- package/dist/client/browser/index.js +150 -0
- package/dist/client/ssr/index.d.ts +71 -0
- package/dist/client/ssr/index.js +157 -0
- package/dist/client/style.css +97 -0
- package/dist/node/index.d.ts +56 -0
- package/dist/node/index.js +174 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pengzhanbo
|
|
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,168 @@
|
|
|
1
|
+
# vitepress-plugin-collapse
|
|
2
|
+
|
|
3
|
+
Render collapsible sections in your VitePress site.
|
|
4
|
+
|
|
5
|
+
在 VitePress 中渲染可折叠的内容区块。
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
### With Vitepress-tuck
|
|
10
|
+
|
|
11
|
+
**Installation:**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# npm
|
|
15
|
+
npm install -D vitepress-tuck vitepress-plugin-collapse
|
|
16
|
+
# pnpm
|
|
17
|
+
pnpm add -D vitepress-tuck vitepress-plugin-collapse
|
|
18
|
+
# yarn
|
|
19
|
+
yarn add -D vitepress-tuck vitepress-plugin-collapse
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Configuration:**
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// .vitepress/config.ts
|
|
26
|
+
import collapse from 'vitepress-plugin-collapse'
|
|
27
|
+
import { defineConfig } from 'vitepress-tuck'
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
plugins: [collapse()],
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### With Vitepress
|
|
35
|
+
|
|
36
|
+
**Installation:**
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# npm
|
|
40
|
+
npm install -D vitepress-plugin-collapse
|
|
41
|
+
# pnpm
|
|
42
|
+
pnpm add -D vitepress-plugin-collapse
|
|
43
|
+
# yarn
|
|
44
|
+
yarn add -D vitepress-plugin-collapse
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Configuration:**
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
// .vitepress/config.ts
|
|
51
|
+
import { defineConfig } from 'vitepress'
|
|
52
|
+
import { collapseMarkdownPlugin } from 'vitepress-plugin-collapse'
|
|
53
|
+
|
|
54
|
+
export default defineConfig({
|
|
55
|
+
markdown: {
|
|
56
|
+
config: (md) => {
|
|
57
|
+
md.use(collapseMarkdownPlugin)
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// .vitepress/theme/index.ts
|
|
65
|
+
import type { Theme } from 'vitepress'
|
|
66
|
+
import { enhanceAppWithCollapse } from 'vitepress-plugin-collapse/client'
|
|
67
|
+
import 'vitepress-plugin-collapse/style.css'
|
|
68
|
+
import DefaultTheme from 'vitepress/theme'
|
|
69
|
+
|
|
70
|
+
export default {
|
|
71
|
+
extends: DefaultTheme,
|
|
72
|
+
enhanceApp(ctx) {
|
|
73
|
+
enhanceAppWithCollapse(ctx)
|
|
74
|
+
},
|
|
75
|
+
} satisfies Theme
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Syntax
|
|
79
|
+
|
|
80
|
+
Use `::: collapse` container with a list to create collapsible sections. Each
|
|
81
|
+
list item becomes a collapse panel: the first line is the title, and the
|
|
82
|
+
following indented content is the panel body.
|
|
83
|
+
|
|
84
|
+
使用 `::: collapse` 容器配合列表来创建可折叠区块。每个列表项成为一个折叠面板:
|
|
85
|
+
第一行为标题,后续缩进内容为面板正文。
|
|
86
|
+
|
|
87
|
+
### Basic
|
|
88
|
+
|
|
89
|
+
```md
|
|
90
|
+
::: collapse
|
|
91
|
+
- Title 1
|
|
92
|
+
Content of item 1.
|
|
93
|
+
|
|
94
|
+
- Title 2
|
|
95
|
+
Content of item 2.
|
|
96
|
+
:::
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Accordion
|
|
100
|
+
|
|
101
|
+
Add `accordion` to enable accordion mode — only one item can be expanded at a
|
|
102
|
+
time. Use `expand` to expand the first item by default when no explicit `:+`
|
|
103
|
+
flag is set.
|
|
104
|
+
|
|
105
|
+
添加 `accordion` 启用手风琴模式 —— 同时只能展开一项。使用 `expand` 可在没有
|
|
106
|
+
显式 `:+` 标记时默认展开第一项。
|
|
107
|
+
|
|
108
|
+
```md
|
|
109
|
+
::: collapse accordion expand
|
|
110
|
+
- Question 1
|
|
111
|
+
Answer 1.
|
|
112
|
+
|
|
113
|
+
- Question 2
|
|
114
|
+
Answer 2.
|
|
115
|
+
:::
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Card Style
|
|
119
|
+
|
|
120
|
+
Add `card` to render the container with a bordered, rounded card style.
|
|
121
|
+
|
|
122
|
+
添加 `card` 以带边框、圆角的卡片样式渲染容器。
|
|
123
|
+
|
|
124
|
+
```md
|
|
125
|
+
::: collapse accordion card
|
|
126
|
+
- Question 1
|
|
127
|
+
Answer 1.
|
|
128
|
+
|
|
129
|
+
- Question 2
|
|
130
|
+
Answer 2.
|
|
131
|
+
:::
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Per-item Expand Flag
|
|
135
|
+
|
|
136
|
+
Prefix a title with `:+` to expand an item, or `:-` to collapse it. In
|
|
137
|
+
accordion mode, only the first `:+` flag takes effect.
|
|
138
|
+
|
|
139
|
+
在标题前添加 `:+` 展开该项,或 `:-` 收起该项。手风琴模式下只有第一个 `:+`
|
|
140
|
+
标记生效。
|
|
141
|
+
|
|
142
|
+
```md
|
|
143
|
+
::: collapse
|
|
144
|
+
- :+ Expanded by default
|
|
145
|
+
Content.
|
|
146
|
+
|
|
147
|
+
- :- Collapsed by default
|
|
148
|
+
Content.
|
|
149
|
+
|
|
150
|
+
- No flag, follows container `expand` attribute
|
|
151
|
+
Content.
|
|
152
|
+
:::
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Container Attributes
|
|
156
|
+
|
|
157
|
+
| Attribute | Type | Description |
|
|
158
|
+
| ----------- | --------- | ------------------------------------------------------------------------- |
|
|
159
|
+
| `accordion` | `boolean` | Enable accordion mode (only one item expanded at a time) / 启用手风琴模式 |
|
|
160
|
+
| `card` | `boolean` | Render with card style (bordered and rounded) / 使用卡片样式 |
|
|
161
|
+
| `expand` | `boolean` | Default expanded state / 默认展开状态 |
|
|
162
|
+
|
|
163
|
+
### Item Flags
|
|
164
|
+
|
|
165
|
+
| Flag | Description |
|
|
166
|
+
| ---- | --------------------------------------------------------------------------------------------------- |
|
|
167
|
+
| `:+` | Expand this item (only the first one wins in accordion mode) / 展开该项(手风琴模式下仅第一个生效) |
|
|
168
|
+
| `:-` | Collapse this item / 收起该项 |
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { EnhanceAppContext } from "vitepress/client";
|
|
2
|
+
|
|
3
|
+
//#region src/client/VPCollapse.vue.d.ts
|
|
4
|
+
type __VLS_Props$1 = {
|
|
5
|
+
accordion?: boolean;
|
|
6
|
+
card?: boolean;
|
|
7
|
+
index?: number;
|
|
8
|
+
};
|
|
9
|
+
declare var __VLS_1$1: {};
|
|
10
|
+
type __VLS_Slots$1 = {} & {
|
|
11
|
+
default?: (props: typeof __VLS_1$1) => any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_base$1: import("vue").DefineComponent<__VLS_Props$1, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props$1> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const __VLS_export$1: __VLS_WithSlots$1<typeof __VLS_base$1, __VLS_Slots$1>;
|
|
15
|
+
declare const _default: typeof __VLS_export$1;
|
|
16
|
+
type __VLS_WithSlots$1<T, S> = T & {
|
|
17
|
+
new (): {
|
|
18
|
+
$slots: S;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/client/VPCollapseItem.vue.d.ts
|
|
23
|
+
type __VLS_Props = {
|
|
24
|
+
expand?: boolean;
|
|
25
|
+
index: number;
|
|
26
|
+
};
|
|
27
|
+
declare var __VLS_1: {}, __VLS_9: {};
|
|
28
|
+
type __VLS_Slots = {} & {
|
|
29
|
+
title?: (props: typeof __VLS_1) => any;
|
|
30
|
+
} & {
|
|
31
|
+
default?: (props: typeof __VLS_9) => any;
|
|
32
|
+
};
|
|
33
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
34
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
35
|
+
declare const _default$1: typeof __VLS_export;
|
|
36
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
37
|
+
new (): {
|
|
38
|
+
$slots: S;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/client/index.d.ts
|
|
43
|
+
/**
|
|
44
|
+
* Register `VPCollapse` and `VPCollapseItem` globally when using this plugin
|
|
45
|
+
* with VitePress directly (without `vitepress-tuck`).
|
|
46
|
+
*
|
|
47
|
+
* 当不使用 `vitepress-tuck` 而直接在 VitePress 中使用本插件时,
|
|
48
|
+
* 用于全局注册 `VPCollapse` 和 `VPCollapseItem` 组件。
|
|
49
|
+
*
|
|
50
|
+
* @param param0 - The VitePress enhance app context / VitePress 增强应用上下文
|
|
51
|
+
* @param param0.app - The Vue app instance to register components on / 要注册组件的 Vue 应用实例
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* // .vitepress/theme/index.ts
|
|
55
|
+
* import type { Theme } from 'vitepress'
|
|
56
|
+
* import DefaultTheme from 'vitepress/theme'
|
|
57
|
+
* import { enhanceAppWithCollapse } from 'vitepress-plugin-collapse/client'
|
|
58
|
+
*
|
|
59
|
+
* export default {
|
|
60
|
+
* extends: DefaultTheme,
|
|
61
|
+
* enhanceApp(ctx) {
|
|
62
|
+
* enhanceAppWithCollapse(ctx)
|
|
63
|
+
* },
|
|
64
|
+
* } satisfies Theme
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function enhanceAppWithCollapse({
|
|
68
|
+
app
|
|
69
|
+
}: EnhanceAppContext): void;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { _default as VPCollapse, _default$1 as VPCollapseItem, enhanceAppWithCollapse };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import "../style.css";
|
|
2
|
+
import { createElementBlock, createElementVNode, createVNode, defineComponent, inject, normalizeClass, openBlock, provide, ref, renderSlot, unref, vShow, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
|
|
3
|
+
import { FadeInExpandTransition } from "vitepress-plugin-toolkit/client";
|
|
4
|
+
//#region src/client/constants.ts
|
|
5
|
+
/**
|
|
6
|
+
* Injection key for the collapse container context, shared between
|
|
7
|
+
* `<VPCollapse>` and its child `<VPCollapseItem>` components.
|
|
8
|
+
*
|
|
9
|
+
* 折叠面板容器上下文的注入键,在 `<VPCollapse>` 与其子级 `<VPCollapseItem>`
|
|
10
|
+
* 组件之间共享。
|
|
11
|
+
*
|
|
12
|
+
* - `accordion`: whether accordion mode is enabled / 是否启用手风琴模式
|
|
13
|
+
* - `index`: the currently expanded item index (writable ref), used to
|
|
14
|
+
* coordinate mutual exclusion in accordion mode / 当前展开项的索引(可写
|
|
15
|
+
* ref),用于在手风琴模式下协调互斥展开
|
|
16
|
+
*/
|
|
17
|
+
const COLLAPSE_KEY = Symbol(import.meta.env.DEV ? "collapse" : "");
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/client/VPCollapse.vue
|
|
20
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
21
|
+
__name: "VPCollapse",
|
|
22
|
+
props: {
|
|
23
|
+
accordion: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
default: false
|
|
26
|
+
},
|
|
27
|
+
card: { type: Boolean },
|
|
28
|
+
index: {}
|
|
29
|
+
},
|
|
30
|
+
setup(__props) {
|
|
31
|
+
/**
|
|
32
|
+
* Root component of the collapse container.
|
|
33
|
+
*
|
|
34
|
+
* 折叠面板容器的根组件。
|
|
35
|
+
*
|
|
36
|
+
* Renders a list of `<VPCollapseItem>` children and provides the collapse
|
|
37
|
+
* context via `provide`. In accordion mode, `index` tracks the currently
|
|
38
|
+
* expanded item so children can coordinate mutual exclusion.
|
|
39
|
+
*
|
|
40
|
+
* 渲染一组 `<VPCollapseItem>` 子组件,并通过 `provide` 提供折叠面板上下文。
|
|
41
|
+
* 手风琴模式下,`index` 用于跟踪当前展开项,使子组件能够协调互斥展开。
|
|
42
|
+
*
|
|
43
|
+
* @prop accordion - Enable accordion mode / 是否启用手风琴模式
|
|
44
|
+
* @prop card - Render with card style / 是否使用卡片样式
|
|
45
|
+
* @prop index - Default expanded item index (accordion mode) / 默认展开项索引(手风琴模式)
|
|
46
|
+
*/
|
|
47
|
+
const currentIndex = ref(__props.index);
|
|
48
|
+
provide(COLLAPSE_KEY, {
|
|
49
|
+
accordion: __props.accordion,
|
|
50
|
+
index: currentIndex
|
|
51
|
+
});
|
|
52
|
+
return (_ctx, _cache) => {
|
|
53
|
+
return openBlock(), createElementBlock("div", { class: normalizeClass(["vp-collapse", { card: __props.card }]) }, [renderSlot(_ctx.$slots, "default")], 2);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/client/VPCollapseItem.vue
|
|
59
|
+
const _hoisted_1 = ["aria-expanded", "onKeydown"];
|
|
60
|
+
const _hoisted_2 = { class: "vp-collapse-title" };
|
|
61
|
+
const _hoisted_3 = { class: "vp-collapse-content" };
|
|
62
|
+
const _hoisted_4 = { class: "vp-collapse-content-inner" };
|
|
63
|
+
const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
64
|
+
__name: "VPCollapseItem",
|
|
65
|
+
props: {
|
|
66
|
+
expand: { type: Boolean },
|
|
67
|
+
index: {}
|
|
68
|
+
},
|
|
69
|
+
setup(__props) {
|
|
70
|
+
/**
|
|
71
|
+
* Single collapse item, must be used inside `<VPCollapse>`.
|
|
72
|
+
*
|
|
73
|
+
* 单个折叠面板项,必须在 `<VPCollapse>` 内部使用。
|
|
74
|
+
*
|
|
75
|
+
* - Renders a clickable header (with the `title` slot) and a collapsible
|
|
76
|
+
* content area (default slot).
|
|
77
|
+
* - In accordion mode, the item reads the shared `index` from the collapse
|
|
78
|
+
* context and toggles it; expanding one item collapses all others.
|
|
79
|
+
* - Outside accordion mode, each item toggles its own `expanded` state
|
|
80
|
+
* independently.
|
|
81
|
+
*
|
|
82
|
+
* - 渲染可点击的头部(使用 `title` 插槽)和可折叠的内容区域(默认插槽)。
|
|
83
|
+
* - 手风琴模式下,该项从折叠面板上下文读取共享的 `index` 并进行切换;
|
|
84
|
+
* 展开一项会自动收起其他项。
|
|
85
|
+
* - 非手风琴模式下,每项独立切换自身的 `expanded` 状态。
|
|
86
|
+
*
|
|
87
|
+
* @prop expand - Whether this item is expanded (ignored in accordion mode) / 是否展开该项(手风琴模式下忽略)
|
|
88
|
+
* @prop index - Zero-based index within the parent collapse / 在父级折叠面板中的索引(从 0 开始)
|
|
89
|
+
*/
|
|
90
|
+
const collapse = inject(COLLAPSE_KEY);
|
|
91
|
+
if (import.meta.env.DEV && !collapse) throw new Error("<VPCollapseItem /> must be used inside <VPCollapse />");
|
|
92
|
+
const expanded = ref(collapse?.accordion && typeof collapse.index.value !== "undefined" ? __props.index === collapse.index.value : __props.expand);
|
|
93
|
+
if (collapse?.accordion) watch(collapse?.index, () => {
|
|
94
|
+
expanded.value = collapse?.index.value === __props.index;
|
|
95
|
+
});
|
|
96
|
+
function toggle() {
|
|
97
|
+
if (collapse?.accordion) if (collapse.index.value === __props.index && expanded.value) expanded.value = false;
|
|
98
|
+
else {
|
|
99
|
+
collapse.index.value = __props.index;
|
|
100
|
+
expanded.value = true;
|
|
101
|
+
}
|
|
102
|
+
else expanded.value = !expanded.value;
|
|
103
|
+
}
|
|
104
|
+
return (_ctx, _cache) => {
|
|
105
|
+
return openBlock(), createElementBlock("div", { class: normalizeClass(["vp-collapse-item", { expanded: expanded.value }]) }, [createElementVNode("div", {
|
|
106
|
+
class: "vp-collapse-header",
|
|
107
|
+
tabindex: "0",
|
|
108
|
+
role: "button",
|
|
109
|
+
"aria-expanded": expanded.value ? "true" : "false",
|
|
110
|
+
onClick: toggle,
|
|
111
|
+
onKeydown: [withKeys(withModifiers(toggle, ["prevent"]), ["enter"]), withKeys(withModifiers(toggle, ["prevent"]), ["space"])]
|
|
112
|
+
}, [_cache[0] || (_cache[0] = createElementVNode("span", { class: "vpi-chevron-right" }, null, -1)), createElementVNode("p", _hoisted_2, [renderSlot(_ctx.$slots, "title")])], 40, _hoisted_1), createVNode(unref(FadeInExpandTransition), null, {
|
|
113
|
+
default: withCtx(() => [withDirectives(createElementVNode("div", _hoisted_3, [createElementVNode("div", _hoisted_4, [renderSlot(_ctx.$slots, "default")])], 512), [[vShow, expanded.value]])]),
|
|
114
|
+
_: 3
|
|
115
|
+
})], 2);
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/client/index.ts
|
|
121
|
+
/**
|
|
122
|
+
* Register `VPCollapse` and `VPCollapseItem` globally when using this plugin
|
|
123
|
+
* with VitePress directly (without `vitepress-tuck`).
|
|
124
|
+
*
|
|
125
|
+
* 当不使用 `vitepress-tuck` 而直接在 VitePress 中使用本插件时,
|
|
126
|
+
* 用于全局注册 `VPCollapse` 和 `VPCollapseItem` 组件。
|
|
127
|
+
*
|
|
128
|
+
* @param param0 - The VitePress enhance app context / VitePress 增强应用上下文
|
|
129
|
+
* @param param0.app - The Vue app instance to register components on / 要注册组件的 Vue 应用实例
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* // .vitepress/theme/index.ts
|
|
133
|
+
* import type { Theme } from 'vitepress'
|
|
134
|
+
* import DefaultTheme from 'vitepress/theme'
|
|
135
|
+
* import { enhanceAppWithCollapse } from 'vitepress-plugin-collapse/client'
|
|
136
|
+
*
|
|
137
|
+
* export default {
|
|
138
|
+
* extends: DefaultTheme,
|
|
139
|
+
* enhanceApp(ctx) {
|
|
140
|
+
* enhanceAppWithCollapse(ctx)
|
|
141
|
+
* },
|
|
142
|
+
* } satisfies Theme
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
function enhanceAppWithCollapse({ app }) {
|
|
146
|
+
app.component("VPCollapse", _sfc_main);
|
|
147
|
+
app.component("VPCollapseItem", _sfc_main$1);
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
export { _sfc_main as VPCollapse, _sfc_main$1 as VPCollapseItem, enhanceAppWithCollapse };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { EnhanceAppContext } from "vitepress/client";
|
|
2
|
+
|
|
3
|
+
//#region src/client/VPCollapse.vue.d.ts
|
|
4
|
+
type __VLS_Props$1 = {
|
|
5
|
+
accordion?: boolean;
|
|
6
|
+
card?: boolean;
|
|
7
|
+
index?: number;
|
|
8
|
+
};
|
|
9
|
+
declare var __VLS_1$1: {};
|
|
10
|
+
type __VLS_Slots$1 = {} & {
|
|
11
|
+
default?: (props: typeof __VLS_1$1) => any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_base$1: import("vue").DefineComponent<__VLS_Props$1, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props$1> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const __VLS_export$1: __VLS_WithSlots$1<typeof __VLS_base$1, __VLS_Slots$1>;
|
|
15
|
+
declare const _default: typeof __VLS_export$1;
|
|
16
|
+
type __VLS_WithSlots$1<T, S> = T & {
|
|
17
|
+
new (): {
|
|
18
|
+
$slots: S;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/client/VPCollapseItem.vue.d.ts
|
|
23
|
+
type __VLS_Props = {
|
|
24
|
+
expand?: boolean;
|
|
25
|
+
index: number;
|
|
26
|
+
};
|
|
27
|
+
declare var __VLS_1: {}, __VLS_9: {};
|
|
28
|
+
type __VLS_Slots = {} & {
|
|
29
|
+
title?: (props: typeof __VLS_1) => any;
|
|
30
|
+
} & {
|
|
31
|
+
default?: (props: typeof __VLS_9) => any;
|
|
32
|
+
};
|
|
33
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
34
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
35
|
+
declare const _default$1: typeof __VLS_export;
|
|
36
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
37
|
+
new (): {
|
|
38
|
+
$slots: S;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/client/index.d.ts
|
|
43
|
+
/**
|
|
44
|
+
* Register `VPCollapse` and `VPCollapseItem` globally when using this plugin
|
|
45
|
+
* with VitePress directly (without `vitepress-tuck`).
|
|
46
|
+
*
|
|
47
|
+
* 当不使用 `vitepress-tuck` 而直接在 VitePress 中使用本插件时,
|
|
48
|
+
* 用于全局注册 `VPCollapse` 和 `VPCollapseItem` 组件。
|
|
49
|
+
*
|
|
50
|
+
* @param param0 - The VitePress enhance app context / VitePress 增强应用上下文
|
|
51
|
+
* @param param0.app - The Vue app instance to register components on / 要注册组件的 Vue 应用实例
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* // .vitepress/theme/index.ts
|
|
55
|
+
* import type { Theme } from 'vitepress'
|
|
56
|
+
* import DefaultTheme from 'vitepress/theme'
|
|
57
|
+
* import { enhanceAppWithCollapse } from 'vitepress-plugin-collapse/client'
|
|
58
|
+
*
|
|
59
|
+
* export default {
|
|
60
|
+
* extends: DefaultTheme,
|
|
61
|
+
* enhanceApp(ctx) {
|
|
62
|
+
* enhanceAppWithCollapse(ctx)
|
|
63
|
+
* },
|
|
64
|
+
* } satisfies Theme
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function enhanceAppWithCollapse({
|
|
68
|
+
app
|
|
69
|
+
}: EnhanceAppContext): void;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { _default as VPCollapse, _default$1 as VPCollapseItem, enhanceAppWithCollapse };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createVNode, defineComponent, inject, mergeProps, provide, ref, renderSlot, unref, useSSRContext, vShow, watch, withCtx, withDirectives } from "vue";
|
|
2
|
+
import { ssrRenderAttr, ssrRenderAttrs, ssrRenderComponent, ssrRenderSlot, ssrRenderStyle } from "vue/server-renderer";
|
|
3
|
+
import { FadeInExpandTransition } from "vitepress-plugin-toolkit/client";
|
|
4
|
+
//#region src/client/constants.ts
|
|
5
|
+
/**
|
|
6
|
+
* Injection key for the collapse container context, shared between
|
|
7
|
+
* `<VPCollapse>` and its child `<VPCollapseItem>` components.
|
|
8
|
+
*
|
|
9
|
+
* 折叠面板容器上下文的注入键,在 `<VPCollapse>` 与其子级 `<VPCollapseItem>`
|
|
10
|
+
* 组件之间共享。
|
|
11
|
+
*
|
|
12
|
+
* - `accordion`: whether accordion mode is enabled / 是否启用手风琴模式
|
|
13
|
+
* - `index`: the currently expanded item index (writable ref), used to
|
|
14
|
+
* coordinate mutual exclusion in accordion mode / 当前展开项的索引(可写
|
|
15
|
+
* ref),用于在手风琴模式下协调互斥展开
|
|
16
|
+
*/
|
|
17
|
+
const COLLAPSE_KEY = Symbol(import.meta.env.DEV ? "collapse" : "");
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/client/VPCollapse.vue
|
|
20
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
21
|
+
__name: "VPCollapse",
|
|
22
|
+
__ssrInlineRender: true,
|
|
23
|
+
props: {
|
|
24
|
+
accordion: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
default: false
|
|
27
|
+
},
|
|
28
|
+
card: { type: Boolean },
|
|
29
|
+
index: {}
|
|
30
|
+
},
|
|
31
|
+
setup(__props) {
|
|
32
|
+
/**
|
|
33
|
+
* Root component of the collapse container.
|
|
34
|
+
*
|
|
35
|
+
* 折叠面板容器的根组件。
|
|
36
|
+
*
|
|
37
|
+
* Renders a list of `<VPCollapseItem>` children and provides the collapse
|
|
38
|
+
* context via `provide`. In accordion mode, `index` tracks the currently
|
|
39
|
+
* expanded item so children can coordinate mutual exclusion.
|
|
40
|
+
*
|
|
41
|
+
* 渲染一组 `<VPCollapseItem>` 子组件,并通过 `provide` 提供折叠面板上下文。
|
|
42
|
+
* 手风琴模式下,`index` 用于跟踪当前展开项,使子组件能够协调互斥展开。
|
|
43
|
+
*
|
|
44
|
+
* @prop accordion - Enable accordion mode / 是否启用手风琴模式
|
|
45
|
+
* @prop card - Render with card style / 是否使用卡片样式
|
|
46
|
+
* @prop index - Default expanded item index (accordion mode) / 默认展开项索引(手风琴模式)
|
|
47
|
+
*/
|
|
48
|
+
const currentIndex = ref(__props.index);
|
|
49
|
+
provide(COLLAPSE_KEY, {
|
|
50
|
+
accordion: __props.accordion,
|
|
51
|
+
index: currentIndex
|
|
52
|
+
});
|
|
53
|
+
return (_ctx, _push, _parent, _attrs) => {
|
|
54
|
+
_push(`<div${ssrRenderAttrs(mergeProps({ class: ["vp-collapse", { card: __props.card }] }, _attrs))}>`);
|
|
55
|
+
ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent);
|
|
56
|
+
_push(`</div>`);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
const _sfc_setup$1 = _sfc_main.setup;
|
|
61
|
+
_sfc_main.setup = (props, ctx) => {
|
|
62
|
+
const ssrContext = useSSRContext();
|
|
63
|
+
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/client/VPCollapse.vue");
|
|
64
|
+
return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
|
|
65
|
+
};
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/client/VPCollapseItem.vue
|
|
68
|
+
const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
69
|
+
__name: "VPCollapseItem",
|
|
70
|
+
__ssrInlineRender: true,
|
|
71
|
+
props: {
|
|
72
|
+
expand: { type: Boolean },
|
|
73
|
+
index: {}
|
|
74
|
+
},
|
|
75
|
+
setup(__props) {
|
|
76
|
+
/**
|
|
77
|
+
* Single collapse item, must be used inside `<VPCollapse>`.
|
|
78
|
+
*
|
|
79
|
+
* 单个折叠面板项,必须在 `<VPCollapse>` 内部使用。
|
|
80
|
+
*
|
|
81
|
+
* - Renders a clickable header (with the `title` slot) and a collapsible
|
|
82
|
+
* content area (default slot).
|
|
83
|
+
* - In accordion mode, the item reads the shared `index` from the collapse
|
|
84
|
+
* context and toggles it; expanding one item collapses all others.
|
|
85
|
+
* - Outside accordion mode, each item toggles its own `expanded` state
|
|
86
|
+
* independently.
|
|
87
|
+
*
|
|
88
|
+
* - 渲染可点击的头部(使用 `title` 插槽)和可折叠的内容区域(默认插槽)。
|
|
89
|
+
* - 手风琴模式下,该项从折叠面板上下文读取共享的 `index` 并进行切换;
|
|
90
|
+
* 展开一项会自动收起其他项。
|
|
91
|
+
* - 非手风琴模式下,每项独立切换自身的 `expanded` 状态。
|
|
92
|
+
*
|
|
93
|
+
* @prop expand - Whether this item is expanded (ignored in accordion mode) / 是否展开该项(手风琴模式下忽略)
|
|
94
|
+
* @prop index - Zero-based index within the parent collapse / 在父级折叠面板中的索引(从 0 开始)
|
|
95
|
+
*/
|
|
96
|
+
const collapse = inject(COLLAPSE_KEY);
|
|
97
|
+
if (import.meta.env.DEV && !collapse) throw new Error("<VPCollapseItem /> must be used inside <VPCollapse />");
|
|
98
|
+
const expanded = ref(collapse?.accordion && typeof collapse.index.value !== "undefined" ? __props.index === collapse.index.value : __props.expand);
|
|
99
|
+
if (collapse?.accordion) watch(collapse?.index, () => {
|
|
100
|
+
expanded.value = collapse?.index.value === __props.index;
|
|
101
|
+
});
|
|
102
|
+
return (_ctx, _push, _parent, _attrs) => {
|
|
103
|
+
_push(`<div${ssrRenderAttrs(mergeProps({ class: ["vp-collapse-item", { expanded: expanded.value }] }, _attrs))}><div class="vp-collapse-header" tabindex="0" role="button"${ssrRenderAttr("aria-expanded", expanded.value ? "true" : "false")}><span class="vpi-chevron-right"></span><p class="vp-collapse-title">`);
|
|
104
|
+
ssrRenderSlot(_ctx.$slots, "title", {}, null, _push, _parent);
|
|
105
|
+
_push(`</p></div>`);
|
|
106
|
+
_push(ssrRenderComponent(unref(FadeInExpandTransition), null, {
|
|
107
|
+
default: withCtx((_, _push, _parent, _scopeId) => {
|
|
108
|
+
if (_push) {
|
|
109
|
+
_push(`<div class="vp-collapse-content" style="${ssrRenderStyle(expanded.value ? null : { display: "none" })}"${_scopeId}><div class="vp-collapse-content-inner"${_scopeId}>`);
|
|
110
|
+
ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent, _scopeId);
|
|
111
|
+
_push(`</div></div>`);
|
|
112
|
+
} else return [withDirectives(createVNode("div", { class: "vp-collapse-content" }, [createVNode("div", { class: "vp-collapse-content-inner" }, [renderSlot(_ctx.$slots, "default")])], 512), [[vShow, expanded.value]])];
|
|
113
|
+
}),
|
|
114
|
+
_: 3
|
|
115
|
+
}, _parent));
|
|
116
|
+
_push(`</div>`);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
const _sfc_setup = _sfc_main$1.setup;
|
|
121
|
+
_sfc_main$1.setup = (props, ctx) => {
|
|
122
|
+
const ssrContext = useSSRContext();
|
|
123
|
+
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/client/VPCollapseItem.vue");
|
|
124
|
+
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
|
|
125
|
+
};
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/client/index.ts
|
|
128
|
+
/**
|
|
129
|
+
* Register `VPCollapse` and `VPCollapseItem` globally when using this plugin
|
|
130
|
+
* with VitePress directly (without `vitepress-tuck`).
|
|
131
|
+
*
|
|
132
|
+
* 当不使用 `vitepress-tuck` 而直接在 VitePress 中使用本插件时,
|
|
133
|
+
* 用于全局注册 `VPCollapse` 和 `VPCollapseItem` 组件。
|
|
134
|
+
*
|
|
135
|
+
* @param param0 - The VitePress enhance app context / VitePress 增强应用上下文
|
|
136
|
+
* @param param0.app - The Vue app instance to register components on / 要注册组件的 Vue 应用实例
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* // .vitepress/theme/index.ts
|
|
140
|
+
* import type { Theme } from 'vitepress'
|
|
141
|
+
* import DefaultTheme from 'vitepress/theme'
|
|
142
|
+
* import { enhanceAppWithCollapse } from 'vitepress-plugin-collapse/client'
|
|
143
|
+
*
|
|
144
|
+
* export default {
|
|
145
|
+
* extends: DefaultTheme,
|
|
146
|
+
* enhanceApp(ctx) {
|
|
147
|
+
* enhanceAppWithCollapse(ctx)
|
|
148
|
+
* },
|
|
149
|
+
* } satisfies Theme
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
function enhanceAppWithCollapse({ app }) {
|
|
153
|
+
app.component("VPCollapse", _sfc_main);
|
|
154
|
+
app.component("VPCollapseItem", _sfc_main$1);
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
export { _sfc_main as VPCollapse, _sfc_main$1 as VPCollapseItem, enhanceAppWithCollapse };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
@import url("vitepress-plugin-toolkit/styles/transition/fade-in-height-expand.css");
|
|
2
|
+
|
|
3
|
+
.vp-collapse {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: 16px;
|
|
7
|
+
margin-block: 16px;
|
|
8
|
+
margin-inline: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.vp-collapse.card {
|
|
12
|
+
padding-block-end: 16px;
|
|
13
|
+
border: solid 1px var(--vp-c-divider);
|
|
14
|
+
border-radius: 8px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.vp-collapse-item {
|
|
18
|
+
position: relative;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
padding-block-start: 16px;
|
|
22
|
+
border-top: solid 1px var(--vp-c-divider);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.vp-collapse-item:first-child,
|
|
26
|
+
.vp-collapse.card .vp-collapse-item {
|
|
27
|
+
border-top: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.vp-collapse:not(.card) .vp-collapse-item:first-child {
|
|
31
|
+
padding-block-start: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.vp-collapse.card .vp-collapse-item::after {
|
|
35
|
+
position: absolute;
|
|
36
|
+
right: 16px;
|
|
37
|
+
bottom: -16px;
|
|
38
|
+
left: 16px;
|
|
39
|
+
display: block;
|
|
40
|
+
height: 1px;
|
|
41
|
+
content: "";
|
|
42
|
+
background-color: var(--vp-c-divider);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.vp-collapse.card .vp-collapse-item:last-child::after {
|
|
46
|
+
display: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.vp-collapse-header {
|
|
50
|
+
display: flex;
|
|
51
|
+
gap: 6px;
|
|
52
|
+
align-items: first baseline;
|
|
53
|
+
font-size: 16px;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.vp-collapse-header .vpi-chevron-right {
|
|
59
|
+
position: relative;
|
|
60
|
+
top: 4px;
|
|
61
|
+
width: 20px;
|
|
62
|
+
height: 20px;
|
|
63
|
+
color: var(--vp-c-text-3);
|
|
64
|
+
transition: transform 0.25s ease-in-out;
|
|
65
|
+
transform: rotate(0deg);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.vp-collapse.card .vp-collapse-header {
|
|
69
|
+
gap: 0;
|
|
70
|
+
padding-inline: 4px 16px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.vp-collapse-item.expanded .vpi-chevron-right {
|
|
74
|
+
transform: rotate(90deg);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.vp-collapse-header .vp-collapse-title {
|
|
78
|
+
flex: 1 2;
|
|
79
|
+
margin: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.vp-collapse-content-inner {
|
|
83
|
+
padding-block-start: 12px;
|
|
84
|
+
padding-inline-start: 24px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.vp-collapse.card .vp-collapse-content-inner {
|
|
88
|
+
padding-inline-end: 16px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.vp-collapse-content-inner > *:first-child {
|
|
92
|
+
margin-top: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.vp-collapse-content-inner > *:last-child {
|
|
96
|
+
margin-bottom: 0;
|
|
97
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PluginSimple } from "markdown-it";
|
|
2
|
+
|
|
3
|
+
//#region src/node/plugin.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Collapse plugin factory.
|
|
6
|
+
*
|
|
7
|
+
* 折叠面板插件工厂函数。
|
|
8
|
+
*
|
|
9
|
+
* Registers the `::: collapse` markdown-it container and declares the
|
|
10
|
+
* `VPCollapse` and `VPCollapseItem` Vue components for auto-import. When used
|
|
11
|
+
* with `defineConfig` from `vitepress-tuck`, the components are auto-imported
|
|
12
|
+
* on demand — no manual `enhanceApp` registration is required.
|
|
13
|
+
*
|
|
14
|
+
* 注册 `::: collapse` markdown-it 容器,并声明 `VPCollapse` 和 `VPCollapseItem`
|
|
15
|
+
* Vue 组件以供自动导入。与 `vitepress-tuck` 的 `defineConfig` 配合使用时,
|
|
16
|
+
* 组件会按需自动导入,无需手动在 `enhanceApp` 中注册。
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // .vitepress/config.ts
|
|
21
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
22
|
+
* import collapse from 'vitepress-plugin-collapse'
|
|
23
|
+
*
|
|
24
|
+
* export default defineConfig({
|
|
25
|
+
* plugins: [collapse()],
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare const collapse: (options?: unknown) => import("vitepress-tuck").VitepressPlugin;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/node/markdown.d.ts
|
|
32
|
+
/**
|
|
33
|
+
* Collapse plugin - Enable collapse container in markdown-it.
|
|
34
|
+
*
|
|
35
|
+
* 折叠面板插件 - 在 markdown-it 中启用折叠面板容器。
|
|
36
|
+
*
|
|
37
|
+
* Registers the `::: collapse` container and custom renderer rules for
|
|
38
|
+
* `collapse_item_*` tokens. The container is converted into a `<VPCollapse>`
|
|
39
|
+
* Vue component, and each list item is converted into a `<VPCollapseItem>`.
|
|
40
|
+
*
|
|
41
|
+
* 注册 `::: collapse` 容器以及 `collapse_item_*` 令牌的自定义渲染规则。
|
|
42
|
+
* 容器会被转换为 `<VPCollapse>` Vue 组件,每个列表项会被转换为 `<VPCollapseItem>`。
|
|
43
|
+
*
|
|
44
|
+
* @param md - Markdown instance / Markdown 实例
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import MarkdownIt from 'markdown-it'
|
|
48
|
+
* import { collapseMarkdownPlugin } from 'vitepress-plugin-collapse'
|
|
49
|
+
*
|
|
50
|
+
* const md = new MarkdownIt()
|
|
51
|
+
* md.use(collapseMarkdownPlugin)
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare const collapseMarkdownPlugin: PluginSimple;
|
|
55
|
+
//#endregion
|
|
56
|
+
export { collapse, collapse as default, collapseMarkdownPlugin };
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { definePlugin } from "vitepress-tuck";
|
|
2
|
+
import { createContainerPlugin, resolveAttrs, stringifyAttrs } from "vitepress-plugin-toolkit";
|
|
3
|
+
//#region src/node/markdown.ts
|
|
4
|
+
/**
|
|
5
|
+
* Collapse plugin - Enable collapse container in markdown-it.
|
|
6
|
+
*
|
|
7
|
+
* 折叠面板插件 - 在 markdown-it 中启用折叠面板容器。
|
|
8
|
+
*
|
|
9
|
+
* Registers the `::: collapse` container and custom renderer rules for
|
|
10
|
+
* `collapse_item_*` tokens. The container is converted into a `<VPCollapse>`
|
|
11
|
+
* Vue component, and each list item is converted into a `<VPCollapseItem>`.
|
|
12
|
+
*
|
|
13
|
+
* 注册 `::: collapse` 容器以及 `collapse_item_*` 令牌的自定义渲染规则。
|
|
14
|
+
* 容器会被转换为 `<VPCollapse>` Vue 组件,每个列表项会被转换为 `<VPCollapseItem>`。
|
|
15
|
+
*
|
|
16
|
+
* @param md - Markdown instance / Markdown 实例
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import MarkdownIt from 'markdown-it'
|
|
20
|
+
* import { collapseMarkdownPlugin } from 'vitepress-plugin-collapse'
|
|
21
|
+
*
|
|
22
|
+
* const md = new MarkdownIt()
|
|
23
|
+
* md.use(collapseMarkdownPlugin)
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
const collapseMarkdownPlugin = (md) => {
|
|
27
|
+
createContainerPlugin(md, "collapse", {
|
|
28
|
+
before: (info, tokens, index) => {
|
|
29
|
+
const attrs = resolveAttrs(info);
|
|
30
|
+
const idx = parseCollapse(tokens, index, attrs);
|
|
31
|
+
const { accordion, card } = attrs;
|
|
32
|
+
return `<VPCollapse${stringifyAttrs({
|
|
33
|
+
accordion,
|
|
34
|
+
card,
|
|
35
|
+
index: idx
|
|
36
|
+
})}>`;
|
|
37
|
+
},
|
|
38
|
+
after: () => `</VPCollapse>`
|
|
39
|
+
});
|
|
40
|
+
md.renderer.rules.collapse_item_open = (tokens, idx) => {
|
|
41
|
+
const { expand, index } = tokens[idx].meta;
|
|
42
|
+
return `<VPCollapseItem${stringifyAttrs({
|
|
43
|
+
expand,
|
|
44
|
+
index
|
|
45
|
+
})}>`;
|
|
46
|
+
};
|
|
47
|
+
md.renderer.rules.collapse_item_close = () => "</VPCollapseItem>";
|
|
48
|
+
md.renderer.rules.collapse_item_title_open = () => "<template #title>";
|
|
49
|
+
md.renderer.rules.collapse_item_title_close = () => "</template>";
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Parse collapse tokens and transform list items into collapse items.
|
|
53
|
+
*
|
|
54
|
+
* 解析折叠面板令牌,并将列表项转换为折叠面板项。
|
|
55
|
+
*
|
|
56
|
+
* Walks the token stream starting after the `container_collapse_open` token,
|
|
57
|
+
* hides the wrapping list tokens, and rewrites root-level list item tokens
|
|
58
|
+
* into `collapse_item_*` tokens. Each title's leading `:+` or `:-` flag is
|
|
59
|
+
* stripped and used to determine the item's expand state:
|
|
60
|
+
* - `:+` expands the item (in accordion mode, only the first one wins).
|
|
61
|
+
* - `:-` collapses the item.
|
|
62
|
+
* - No flag falls back to the container's `expand` attribute.
|
|
63
|
+
*
|
|
64
|
+
* 从 `container_collapse_open` 令牌之后开始遍历令牌流,隐藏外层列表令牌,
|
|
65
|
+
* 并将根级列表项令牌重写为 `collapse_item_*` 令牌。每个标题前缀的 `:+` 或 `:-`
|
|
66
|
+
* 标记会被移除,用于决定该项的展开状态:
|
|
67
|
+
* - `:+` 展开该项(手风琴模式下仅第一个生效)。
|
|
68
|
+
* - `:-` 收起该项。
|
|
69
|
+
* - 无标记时回退到容器的 `expand` 属性。
|
|
70
|
+
*
|
|
71
|
+
* @param tokens - Token array / 令牌数组
|
|
72
|
+
* @param index - Start index (the `container_collapse_open` token) / 起始索引(`container_collapse_open` 令牌)
|
|
73
|
+
* @param attrs - Collapse metadata parsed from container info / 从容器信息解析得到的折叠面板元数据
|
|
74
|
+
* @returns Default expanded index for accordion mode, or undefined / 手风琴模式下默认展开项的索引,无则返回 undefined
|
|
75
|
+
*/
|
|
76
|
+
function parseCollapse(tokens, index, attrs) {
|
|
77
|
+
const listStack = [];
|
|
78
|
+
let idx = -1;
|
|
79
|
+
let defaultIndex;
|
|
80
|
+
let hashExpand = false;
|
|
81
|
+
for (let i = index + 1; i < tokens.length; i++) {
|
|
82
|
+
const token = tokens[i];
|
|
83
|
+
if (token.type === "container_collapse_close") break;
|
|
84
|
+
if (token.type === "bullet_list_open" || token.type === "ordered_list_open") {
|
|
85
|
+
listStack.push(0);
|
|
86
|
+
if (listStack.length === 1) token.hidden = true;
|
|
87
|
+
} else if (token.type === "bullet_list_close" || token.type === "ordered_list_close") {
|
|
88
|
+
listStack.pop();
|
|
89
|
+
if (listStack.length === 0) token.hidden = true;
|
|
90
|
+
} else if (token.type === "list_item_open") {
|
|
91
|
+
if (listStack.length === 1) {
|
|
92
|
+
token.type = "collapse_item_open";
|
|
93
|
+
tokens[i + 1].type = "collapse_item_title_open";
|
|
94
|
+
tokens[i + 3].type = "collapse_item_title_close";
|
|
95
|
+
idx++;
|
|
96
|
+
const firstToken = tokens[i + 2].children?.[0];
|
|
97
|
+
let flag = "";
|
|
98
|
+
let expand;
|
|
99
|
+
if (firstToken?.type === "text") firstToken.content = firstToken.content.trim().replace(/^:[+\-]\s*/, (match) => {
|
|
100
|
+
flag = match.trim();
|
|
101
|
+
return "";
|
|
102
|
+
});
|
|
103
|
+
if (attrs.accordion) {
|
|
104
|
+
if (!hashExpand && flag === ":+") {
|
|
105
|
+
expand = hashExpand = true;
|
|
106
|
+
defaultIndex = idx;
|
|
107
|
+
}
|
|
108
|
+
} else if (flag === ":+") expand = true;
|
|
109
|
+
else if (flag === ":-") expand = false;
|
|
110
|
+
else expand = !!attrs.expand;
|
|
111
|
+
token.meta = {
|
|
112
|
+
index: idx,
|
|
113
|
+
expand
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
} else if (token.type === "list_item_close") {
|
|
117
|
+
if (listStack.length === 1) token.type = "collapse_item_close";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (attrs.accordion && attrs.expand && !hashExpand) defaultIndex = 0;
|
|
121
|
+
return defaultIndex;
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/node/plugin.ts
|
|
125
|
+
/**
|
|
126
|
+
* Collapse plugin factory.
|
|
127
|
+
*
|
|
128
|
+
* 折叠面板插件工厂函数。
|
|
129
|
+
*
|
|
130
|
+
* Registers the `::: collapse` markdown-it container and declares the
|
|
131
|
+
* `VPCollapse` and `VPCollapseItem` Vue components for auto-import. When used
|
|
132
|
+
* with `defineConfig` from `vitepress-tuck`, the components are auto-imported
|
|
133
|
+
* on demand — no manual `enhanceApp` registration is required.
|
|
134
|
+
*
|
|
135
|
+
* 注册 `::: collapse` markdown-it 容器,并声明 `VPCollapse` 和 `VPCollapseItem`
|
|
136
|
+
* Vue 组件以供自动导入。与 `vitepress-tuck` 的 `defineConfig` 配合使用时,
|
|
137
|
+
* 组件会按需自动导入,无需手动在 `enhanceApp` 中注册。
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* // .vitepress/config.ts
|
|
142
|
+
* import { defineConfig } from 'vitepress-tuck'
|
|
143
|
+
* import collapse from 'vitepress-plugin-collapse'
|
|
144
|
+
*
|
|
145
|
+
* export default defineConfig({
|
|
146
|
+
* plugins: [collapse()],
|
|
147
|
+
* })
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
const collapse = definePlugin(() => ({
|
|
151
|
+
name: "vitepress-plugin-collapse",
|
|
152
|
+
componentResolver: ["VPCollapse", "VPCollapseItem"],
|
|
153
|
+
markdown: { config: (md) => {
|
|
154
|
+
md.use(collapseMarkdownPlugin);
|
|
155
|
+
} }
|
|
156
|
+
}));
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/node/index.ts
|
|
159
|
+
/**
|
|
160
|
+
* Node entry for vitepress-plugin-collapse.
|
|
161
|
+
*
|
|
162
|
+
* vitepress-plugin-collapse 的 Node 端入口。
|
|
163
|
+
*
|
|
164
|
+
* Re-exports the markdown-it plugin, the `collapse` plugin factory (as the
|
|
165
|
+
* default export) for use with `vitepress-tuck`'s `defineConfig`.
|
|
166
|
+
*
|
|
167
|
+
* 再导出 markdown-it 插件及 `collapse` 插件工厂函数(作为默认导出),
|
|
168
|
+
* 供 `vitepress-tuck` 的 `defineConfig` 使用。
|
|
169
|
+
*
|
|
170
|
+
* @module
|
|
171
|
+
*/
|
|
172
|
+
var node_default = collapse;
|
|
173
|
+
//#endregion
|
|
174
|
+
export { collapse, collapseMarkdownPlugin, node_default as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vitepress-plugin-collapse",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.7.0",
|
|
5
|
+
"description": "Render collapsible sections in your VitePress site.",
|
|
6
|
+
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://tuck.pengzhanbo.cn/plugins/collapse",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/pengzhanbo/vitepress-tuck.git",
|
|
12
|
+
"directory": "packages/plugin-collapse"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/pengzhanbo/vitepress-tuck/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"vitepress",
|
|
19
|
+
"vitepress-plugin",
|
|
20
|
+
"collapse"
|
|
21
|
+
],
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./dist/node/index.js",
|
|
24
|
+
"./client": {
|
|
25
|
+
"browser": "./dist/client/browser/index.js",
|
|
26
|
+
"default": "./dist/client/ssr/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./style.css": "./dist/client/style.css"
|
|
29
|
+
},
|
|
30
|
+
"module": "./dist/node/index.js",
|
|
31
|
+
"types": "./dist/node/index.d.ts",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"vitepress": "^1.6.4 || ^2.0.0-alpha.17",
|
|
37
|
+
"vue": "^3.5.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"vitepress-plugin-toolkit": "0.7.0",
|
|
41
|
+
"vitepress-tuck": "0.7.0"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"clean": "rimraf --glob ./dist",
|
|
48
|
+
"dev": "pnpm '/(tsdown|copy):watch/'",
|
|
49
|
+
"build": "pnpm tsdown && pnpm copy",
|
|
50
|
+
"copy": "cpx \"src/**/*.css\" dist",
|
|
51
|
+
"copy:watch": "pnpm copy -w",
|
|
52
|
+
"tsdown": "tsdown --config-loader unrun",
|
|
53
|
+
"tsdown:watch": "pnpm tsdown -w"
|
|
54
|
+
}
|
|
55
|
+
}
|