vitepress-plugin-sfc 2.0.0-beta.1 → 2.0.0-beta.3
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/README.md +28 -118
- package/dist/index.d.mts +17 -1065
- package/dist/index.mjs +9 -5
- package/package.json +42 -35
- package/src/client/ComponentView.vue +37 -172
- package/src/client/GroupTabs.vue +126 -0
- package/src/client/index.ts +42 -16
- package/src/client/useLocaleMappings.ts +0 -36
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import e from"node:crypto";import{createMarkdownRenderer as t}from"vitepress";function n(e,t,n){function r(e){return e.trim().split(` `,2)[0]===t}function i(e,n,r,i,a){return e[n].nesting===1&&e[n].attrJoin(`class`,t),a.renderToken(e,n,r,i,a)}n||={};let a=n.marker||`:`,o=a.charCodeAt(0),s=a.length,c=n.validate||r,l=n.render||i;function u(e,n,r,i){let l,u=!1,d=e.bMarks[n]+e.tShift[n],f=e.eMarks[n];if(o!==e.src.charCodeAt(d))return!1;for(l=d+1;l<=f&&a[(l-d)%s]===e.src[l];l++);let p=Math.floor((l-d)/s);if(p<3)return!1;l-=(l-d)%s;let m=e.src.slice(d,l),h=e.src.slice(l,f);if(!c(h,m))return!1;if(i)return!0;let g=n;for(;g++,!(g>=r||(d=e.bMarks[g]+e.tShift[g],f=e.eMarks[g],d<f&&e.sCount[g]<e.blkIndent));)if(o===e.src.charCodeAt(d)&&!(e.sCount[g]-e.blkIndent>=4)){for(l=d+1;l<=f&&a[(l-d)%s]===e.src[l];l++);if(!(Math.floor((l-d)/s)<p)&&(l-=(l-d)%s,l=e.skipSpaces(l),!(l<f))){u=!0;break}}let _=e.parentType,v=e.lineMax;e.parentType=`container`,e.lineMax=g;let y=e.push(`container_`+t+`_open`,`div`,1);y.markup=m,y.block=!0,y.info=h,y.map=[n,g],e.md.block.tokenize(e,n+1,g);let b=e.push(`container_`+t+`_close`,`div`,-1);return b.markup=e.src.slice(d,l),b.block=!0,e.parentType=_,e.lineMax=v,e.line=g+ +!!u,!0}e.block.ruler.before(`fence`,`container_`+t,u,{alt:[`paragraph`,`reference`,`blockquote`,`list`]}),e.renderer.rules[`container_`+t+`_open`]=l,e.renderer.rules[`container_`+t+`_close`]=l}const r=/<script\b[^>]*>([\s\S]*?)<\/script>/,i=/^virtual:VP_SFC_/;function a(){return{content:`<script setup>
|
|
2
|
+
<\/script>`,tagOpen:`<script setup>`,type:`script`,contentStripped:`
|
|
3
|
+
`,tagClose:`<\/script>`}}function o(e){let t=s(e),n=`VP_SFC_${t}`,r=`virtual:${n}.vue`;return{hash:t,name:n,importer:r,renderedContent:`// [vitepress-plugin-sfc] injected vue virtual imports
|
|
4
|
+
// REGION START
|
|
5
|
+
import ${n} from '${r}'
|
|
6
|
+
// REGION END
|
|
7
|
+
`}}function s(t){return e.createHash(`sha256`).update(t).digest(`hex`)}function c(e){if(!e)return{};let t=e.split(`,`);return Object.fromEntries(t.map(e=>{let[,t,n]=e.match(/([^=]+)=?(.*)/)||[];return t===`disableRender`?[`disableRender`,!n||n===`true`?`true`:`false`]:t&&n?[t,n]:void 0}).filter(Boolean))}function l(e){e.use(n,`sfc`,{validate:e=>RegExp(`^sfc`).test(e.trim()),render:(e,t,n,r)=>{let{__matched_sfc_set__:i,sfcBlocks:o}=r;o.scripts.length||o.scripts.push(a());let[s]=o.scripts??[],c=e[t];if(c.nesting===1){let{hash:e,name:t,content:r,renderedContent:a,markupParams:o}=c.meta.sfc;if(!i.has(e)){let{tagOpen:t,tagClose:n,contentStripped:r}=s,o=r+a;s.contentStripped=o,s.content=t+o+n,i.add(e)}let{render:l=``,disableRender:u}=o;return`<ComponentView render="${l}" :disableRender="${u}">\n<${t} />\n<template #highlight>${n.highlight(r,`vue`,``)}</template>\n</ComponentView>\n`}else return``}}),e.core.ruler.after(`block`,`extract_sfc`,e=>{let t=e.src.split(`
|
|
8
|
+
`),n=e.env.sfcs||[],r;function i(e){let{type:n,nesting:r,map:i,info:a}=e;if(n!==`container_sfc_open`||r!==1||!i)return;let[s,l]=i,u=t.slice(s+1,l).join(`
|
|
9
|
+
`),d=o(u),[,f]=(a??``).split(` `);return{...d,content:u,markupParams:c(f)}}for(let t of e.tokens){let{map:e}=t,a=i(t);if(a){r=t,t.meta={...t.meta,sfc:a};let e=n.some(({name:e})=>a?.name===e);a&&!e&&n.push(a)}if(r&&r!==t){let[n]=e||[-1],[i,a]=r.map;n>i&&n<a?t.content=``:r=void 0}}let a=e.env;a.sfcs=[...n],a.__matched_sfc_set__=new Set})}const u=l;async function d(){let e=await t(``,{html:!0});e.use(u);let n=new Map;return{name:`vite-plugin-sfc-registry`,enforce:`pre`,resolveId:{filter:{id:new RegExp(i,`gi`)},handler(e){if(n.has(e))return e}},load:{filter:{id:new RegExp(i,`gi`)},handler(e){if(n.has(e))return n.get(e)}},transform(t,r){if(/.md$/gi.test(r)){let r={};e.render(t,r);let i=r.sfcs||[];for(let{importer:e,content:t}of i)n.set(e,t)}}}}export{r as SCRIPT_RE,i as SFC_IMPORTER_RE,a as createDefaultSfcBlock,o as createVirtualImport,u as sfcPlugin,d as sfcRegistryVitePlugin};
|
package/package.json
CHANGED
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitepress-plugin-sfc",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0-beta.3",
|
|
4
|
+
"description": "Allows you to write Vue SFC within Markdown files and render them in VitePress.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"component",
|
|
7
|
+
"plugin",
|
|
8
|
+
"tabs",
|
|
9
|
+
"vitepress",
|
|
10
|
+
"vitepress plugin",
|
|
11
|
+
"vitepress-plugin",
|
|
12
|
+
"vue"
|
|
13
|
+
],
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/heroui-vue/vitepress-plugin-sfc/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "hotdogc1017@gmail.com",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/heroui-vue/vitepress-plugin-sfc"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src/client"
|
|
26
|
+
],
|
|
5
27
|
"type": "module",
|
|
6
|
-
"main": "dist/index.
|
|
7
|
-
"module": "dist/index.
|
|
28
|
+
"main": "dist/index.mjs",
|
|
29
|
+
"module": "dist/index.mjs",
|
|
8
30
|
"exports": {
|
|
9
31
|
".": {
|
|
10
32
|
"types": "./dist/index.d.mts",
|
|
@@ -12,50 +34,35 @@
|
|
|
12
34
|
},
|
|
13
35
|
"./client": "./src/client/index.ts"
|
|
14
36
|
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"src/client"
|
|
18
|
-
],
|
|
19
|
-
"types": "./dist/index.d.mts",
|
|
20
|
-
"scripts": {
|
|
21
|
-
"dev": "tsdown -w",
|
|
22
|
-
"build": "tsdown --minify",
|
|
23
|
-
"bump": "bumpp --sign",
|
|
24
|
-
"pub": "npm run build && npm publish --registry=https://registry.npmjs.org/",
|
|
25
|
-
"dev:docs": "pnpm -C playground dev"
|
|
26
|
-
},
|
|
27
|
-
"keywords": [
|
|
28
|
-
"vitepress",
|
|
29
|
-
"vitepress-plugin",
|
|
30
|
-
"vitepress plugin",
|
|
31
|
-
"plugin",
|
|
32
|
-
"vue",
|
|
33
|
-
"component",
|
|
34
|
-
"tabs"
|
|
35
|
-
],
|
|
36
37
|
"publishConfig": {
|
|
37
38
|
"access": "public",
|
|
38
39
|
"registry": "https://registry.npmjs.org/"
|
|
39
40
|
},
|
|
40
|
-
"author": "hotdogc1017@gmail.com",
|
|
41
|
-
"license": "MIT",
|
|
42
|
-
"repository": {
|
|
43
|
-
"type": "git",
|
|
44
|
-
"url": "https://github.com/heroui-vue/vitepress-plugin-sfc"
|
|
45
|
-
},
|
|
46
41
|
"devDependencies": {
|
|
47
|
-
"@
|
|
48
|
-
"@babel/traverse": "8.0.0-rc.5",
|
|
49
|
-
"@rolldown/pluginutils": "^1.0.1",
|
|
42
|
+
"@tsdown/css": "^0.23.2",
|
|
50
43
|
"@types/markdown-it": "^14.2.0",
|
|
51
44
|
"@types/markdown-it-container": "^2.0.10",
|
|
52
45
|
"@types/node": "^22.17.0",
|
|
53
46
|
"bumpp": "^10.1.0",
|
|
54
47
|
"markdown-it-container": "^4.0.0",
|
|
48
|
+
"oxfmt": "^0.73.0",
|
|
49
|
+
"oxlint": "^1.88.0",
|
|
55
50
|
"tsdown": "0.21.10",
|
|
56
51
|
"typescript": "^5.8.3",
|
|
57
52
|
"unplugin-vue": "^6.2.0",
|
|
58
53
|
"vitepress": "next",
|
|
54
|
+
"vitest": "^5.0.4",
|
|
59
55
|
"vue": "^3.5.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"dev": "tsdown -w",
|
|
59
|
+
"build": "tsdown --minify",
|
|
60
|
+
"bump": "bumpp --sign",
|
|
61
|
+
"pub": "npm run build && npm publish --registry=https://registry.npmjs.org/",
|
|
62
|
+
"dev:docs": "pnpm -C playground dev",
|
|
63
|
+
"fmt": "oxfmt",
|
|
64
|
+
"fmt:check": "oxfmt --check",
|
|
65
|
+
"lint": "oxlint",
|
|
66
|
+
"test": "vitest run -r __tests__"
|
|
60
67
|
}
|
|
61
|
-
}
|
|
68
|
+
}
|
|
@@ -1,181 +1,46 @@
|
|
|
1
|
-
<script setup lang="ts"
|
|
2
|
-
import { computed,
|
|
3
|
-
import {
|
|
4
|
-
import { useLocaleMappings } from "./useLocaleMappings";
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, computed, getCurrentInstance, withDefaults, type Component } from "vue";
|
|
3
|
+
import { configSymbol, type Config, type ComponentViewProps } from "./index";
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
en: {
|
|
10
|
-
previewLabel: "Preview",
|
|
11
|
-
codeLabel: "Source Code",
|
|
12
|
-
},
|
|
13
|
-
zh: {
|
|
14
|
-
previewLabel: "预览",
|
|
15
|
-
codeLabel: "源代码",
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const props = defineProps<{
|
|
20
|
-
src: string;
|
|
21
|
-
highlightedSrc?: string;
|
|
22
|
-
previewLabel?: string;
|
|
23
|
-
codeLabel?: string;
|
|
24
|
-
}>();
|
|
25
|
-
|
|
26
|
-
const { lang } = useData();
|
|
27
|
-
const { get } = useLocaleMappings();
|
|
5
|
+
interface Props extends Omit<ComponentViewProps, "render"> {
|
|
6
|
+
render: Component | undefined;
|
|
7
|
+
}
|
|
28
8
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
// Default to English mappings if no locale-specific mappings are found
|
|
32
|
-
const { previewLabel: preview, codeLabel: code } =
|
|
33
|
-
mappings[lang.value] ?? localMappings.en;
|
|
34
|
-
return [
|
|
35
|
-
{
|
|
36
|
-
label: preview,
|
|
37
|
-
value: "preview",
|
|
38
|
-
},
|
|
39
|
-
{ label: code, value: "code" },
|
|
40
|
-
] as const;
|
|
9
|
+
const props = withDefaults(defineProps<ComponentViewProps>(), {
|
|
10
|
+
disableRender: false,
|
|
41
11
|
});
|
|
42
12
|
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
atob(src);
|
|
66
|
-
} catch (e) {}
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
immediate: true,
|
|
70
|
-
},
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
async function copyCode() {
|
|
74
|
-
await navigator.clipboard.writeText(decodedSrc.value);
|
|
75
|
-
copied.value = true;
|
|
76
|
-
setTimeout(() => {
|
|
77
|
-
copied.value = false;
|
|
78
|
-
}, 2000);
|
|
79
|
-
}
|
|
13
|
+
const config = computed<Props>(() => {
|
|
14
|
+
const { render, disableRender } = inject<Config>(configSymbol);
|
|
15
|
+
const resolveConfig = {
|
|
16
|
+
render: render || props.render,
|
|
17
|
+
disableRender:
|
|
18
|
+
disableRender === null || disableRender === undefined ? props.disableRender : disableRender,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const renderInstance = getCurrentInstance().appContext.app.component(
|
|
22
|
+
resolveConfig.render as string,
|
|
23
|
+
);
|
|
24
|
+
if (!renderInstance) {
|
|
25
|
+
console.warn(`[vitepress-plugin-sfc] Unregistered component: ${resolveConfig.render}`);
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
...resolveConfig,
|
|
29
|
+
render: renderInstance,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
80
32
|
</script>
|
|
81
33
|
|
|
82
34
|
<template>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
/>
|
|
93
|
-
<label
|
|
94
|
-
:for="`tab-${tabGroupId}-${value}`"
|
|
95
|
-
@click="activedTab = value"
|
|
96
|
-
>{{ label }}</label
|
|
97
|
-
>
|
|
98
|
-
</template>
|
|
99
|
-
</header>
|
|
100
|
-
|
|
101
|
-
<main v-show="activedTab === 'preview'">
|
|
102
|
-
<!-- <slot /> -->
|
|
103
|
-
<slot name="v3" />
|
|
104
|
-
</main>
|
|
105
|
-
|
|
106
|
-
<div
|
|
107
|
-
v-show="activedTab === 'code'"
|
|
108
|
-
:class="`language-vue${activedTab === 'code' ? ' active' : ''}`"
|
|
109
|
-
style="margin: 0px"
|
|
110
|
-
>
|
|
111
|
-
<button title="" class="copy"></button>
|
|
112
|
-
<span class="lang">Vue</span>
|
|
113
|
-
<span v-html="highlightedSrc" />
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
35
|
+
<div>
|
|
36
|
+
<slot v-if="!config.render || config.disableRender"></slot>
|
|
37
|
+
<component v-else :is="config.render">
|
|
38
|
+
<slot />
|
|
39
|
+
<template #highlight>
|
|
40
|
+
<slot name="highlight"></slot>
|
|
41
|
+
</template>
|
|
42
|
+
</component>
|
|
43
|
+
</div>
|
|
116
44
|
</template>
|
|
117
45
|
|
|
118
|
-
<style>
|
|
119
|
-
.vp-component-tabs {
|
|
120
|
-
margin: 16px 0;
|
|
121
|
-
display: grid;
|
|
122
|
-
grid-template-columns: 1fr;
|
|
123
|
-
grid-template-rows: min-content 1fr;
|
|
124
|
-
row-gap: 16px;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.vp-component-tabs > header {
|
|
128
|
-
display: flex;
|
|
129
|
-
position: relative;
|
|
130
|
-
border-radius: 8px;
|
|
131
|
-
overflow-x: auto;
|
|
132
|
-
overflow-y: hidden;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.vp-component-tabs > header input + label {
|
|
136
|
-
box-sizing: border-box;
|
|
137
|
-
display: inline-block;
|
|
138
|
-
position: relative;
|
|
139
|
-
padding: 8px 12px;
|
|
140
|
-
font-size: 14px;
|
|
141
|
-
font-weight: 500;
|
|
142
|
-
color: var(--vp-code-tab-text-color);
|
|
143
|
-
white-space: nowrap;
|
|
144
|
-
cursor: pointer;
|
|
145
|
-
transition:
|
|
146
|
-
color 0.25s,
|
|
147
|
-
background-color 0.25s;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.vp-component-tabs > header input + label::after {
|
|
151
|
-
content: "";
|
|
152
|
-
position: absolute;
|
|
153
|
-
height: 4px;
|
|
154
|
-
border-radius: 2px;
|
|
155
|
-
left: 8px;
|
|
156
|
-
right: 8px;
|
|
157
|
-
bottom: -1px;
|
|
158
|
-
transition: background-color 0.25s;
|
|
159
|
-
}
|
|
160
|
-
.vp-component-tabs > header input[data-checked="true"] + label::after {
|
|
161
|
-
background-color: var(--vp-code-tab-active-bar-color);
|
|
162
|
-
}
|
|
163
|
-
.vp-component-tabs > header input[data-checked="true"] + label,
|
|
164
|
-
.vp-component-tabs > header label:hover {
|
|
165
|
-
color: var(--vp-code-tab-active-text-color);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.vp-component-tabs > main {
|
|
169
|
-
padding: 20px;
|
|
170
|
-
background-color: var(--vp-nav-bg-color);
|
|
171
|
-
border-radius: 8px;
|
|
172
|
-
outline: 1px solid var(--vp-c-divider);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
.vp-component-tabs > section {
|
|
176
|
-
background-color: var(--vp-code-block-bg);
|
|
177
|
-
border-radius: 8px;
|
|
178
|
-
padding: 24px;
|
|
179
|
-
overflow: auto;
|
|
180
|
-
}
|
|
181
|
-
</style>
|
|
46
|
+
<style></style>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, useId, onMounted } from "vue";
|
|
3
|
+
import { useData } from "vitepress";
|
|
4
|
+
import { type GroupTabsProps } from "./index";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_LOCALE: GroupTabsProps["locale"] = {
|
|
7
|
+
en: {
|
|
8
|
+
label: {
|
|
9
|
+
preview: "Preview",
|
|
10
|
+
code: "Source Code",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
zh: {
|
|
14
|
+
label: {
|
|
15
|
+
preview: "预览",
|
|
16
|
+
code: "源代码",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const props = defineProps<GroupTabsProps>();
|
|
22
|
+
|
|
23
|
+
const { lang } = useData();
|
|
24
|
+
|
|
25
|
+
const currentLocale = computed(() => {
|
|
26
|
+
const locale = props.locale;
|
|
27
|
+
return locale?.[lang.value] || DEFAULT_LOCALE[lang.value];
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const activedTab = ref<"preview" | "code">("preview");
|
|
31
|
+
const tabGroupId = useId();
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<div class="vp-component-tabs">
|
|
36
|
+
<header>
|
|
37
|
+
<template v-for="(value, key) in currentLocale.label" :key="key">
|
|
38
|
+
<input
|
|
39
|
+
:id="`tab-${tabGroupId}-${key}`"
|
|
40
|
+
type="radio"
|
|
41
|
+
:name="`vp-component-tabs-${tabGroupId}`"
|
|
42
|
+
style="position: fixed; opacity: 0; pointer-events: none"
|
|
43
|
+
:data-checked="activedTab === key" />
|
|
44
|
+
<label :for="`tab-${tabGroupId}-${key}`" @click="activedTab = key">{{ value }}</label>
|
|
45
|
+
</template>
|
|
46
|
+
</header>
|
|
47
|
+
|
|
48
|
+
<main v-show="activedTab === 'preview'">
|
|
49
|
+
<slot />
|
|
50
|
+
</main>
|
|
51
|
+
|
|
52
|
+
<div
|
|
53
|
+
v-show="activedTab === 'code'"
|
|
54
|
+
:class="`language-vue${activedTab === 'code' ? ' active' : ''}`"
|
|
55
|
+
style="margin: 0px">
|
|
56
|
+
<button title="copy source code" class="copy"></button>
|
|
57
|
+
<span class="lang">Vue</span>
|
|
58
|
+
<slot name="highlight" />
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.vp-component-tabs {
|
|
65
|
+
margin: 16px 0;
|
|
66
|
+
display: grid;
|
|
67
|
+
grid-template-columns: 1fr;
|
|
68
|
+
grid-template-rows: min-content 1fr;
|
|
69
|
+
row-gap: 16px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.vp-component-tabs > header {
|
|
73
|
+
display: flex;
|
|
74
|
+
position: relative;
|
|
75
|
+
border-radius: 8px;
|
|
76
|
+
overflow-x: auto;
|
|
77
|
+
overflow-y: hidden;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.vp-component-tabs > header input + label {
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
display: inline-block;
|
|
83
|
+
position: relative;
|
|
84
|
+
padding: 8px 12px;
|
|
85
|
+
font-size: 14px;
|
|
86
|
+
font-weight: 500;
|
|
87
|
+
color: var(--vp-code-tab-text-color);
|
|
88
|
+
white-space: nowrap;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
transition:
|
|
91
|
+
color 0.25s,
|
|
92
|
+
background-color 0.25s;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.vp-component-tabs > header input + label::after {
|
|
96
|
+
content: "";
|
|
97
|
+
position: absolute;
|
|
98
|
+
height: 4px;
|
|
99
|
+
border-radius: 2px;
|
|
100
|
+
left: 8px;
|
|
101
|
+
right: 8px;
|
|
102
|
+
bottom: -1px;
|
|
103
|
+
transition: background-color 0.25s;
|
|
104
|
+
}
|
|
105
|
+
.vp-component-tabs > header input[data-checked="true"] + label::after {
|
|
106
|
+
background-color: var(--vp-code-tab-active-bar-color);
|
|
107
|
+
}
|
|
108
|
+
.vp-component-tabs > header input[data-checked="true"] + label,
|
|
109
|
+
.vp-component-tabs > header label:hover {
|
|
110
|
+
color: var(--vp-code-tab-active-text-color);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.vp-component-tabs > main {
|
|
114
|
+
padding: 20px;
|
|
115
|
+
background-color: var(--vp-nav-bg-color);
|
|
116
|
+
border-radius: 8px;
|
|
117
|
+
outline: 1px solid var(--vp-c-divider);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.vp-component-tabs > section {
|
|
121
|
+
background-color: var(--vp-code-block-bg);
|
|
122
|
+
border-radius: 8px;
|
|
123
|
+
padding: 24px;
|
|
124
|
+
overflow: auto;
|
|
125
|
+
}
|
|
126
|
+
</style>
|
package/src/client/index.ts
CHANGED
|
@@ -1,23 +1,49 @@
|
|
|
1
1
|
import ComponentView from "./ComponentView.vue";
|
|
2
|
-
import {
|
|
2
|
+
import type { EnhanceAppContext } from "vitepress";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
export interface ComponentViewProps {
|
|
5
|
+
render?: "GroupTabs" | "SourceCodeButton" | (string & {});
|
|
6
|
+
disableRender?: boolean;
|
|
7
|
+
}
|
|
6
8
|
|
|
7
|
-
export
|
|
9
|
+
export interface GroupTabsProps {
|
|
10
|
+
locale?: Record<string, { label: { preview: string; code: string } }>;
|
|
11
|
+
}
|
|
8
12
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
localeMappings?: LocaleMappings;
|
|
13
|
-
},
|
|
14
|
-
): void {
|
|
15
|
-
const { localeMappings = {} } = options || {};
|
|
13
|
+
// export interface SourceCodeButtonProps {
|
|
14
|
+
// icon: string;
|
|
15
|
+
// }
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
export type Config = ComponentViewProps & {
|
|
18
|
+
groupTabs?: GroupTabsProps;
|
|
19
|
+
};
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
export const configSymbol = Symbol("__vitepress_plugin_sfc_config");
|
|
22
|
+
|
|
23
|
+
const DEFUALT_CONFIG: Config = {
|
|
24
|
+
render: "GroupTabs",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export async function enhanceAppWithComponentView(app: EnhanceAppContext["app"], config?: Config) {
|
|
28
|
+
app.component("ComponentView", ComponentView);
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
const resolvedConfig = { ...DEFUALT_CONFIG, ...config };
|
|
31
|
+
|
|
32
|
+
if (app) {
|
|
33
|
+
app.provide(configSymbol, resolvedConfig);
|
|
34
|
+
} else {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"[vitepress-plugin-sfc] The `useConfig` must be called within a Vue component if not providing an app",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const { render, disableRender } = resolvedConfig;
|
|
41
|
+
if (disableRender) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (render === "GroupTabs") {
|
|
46
|
+
const importer = await import("./GroupTabs.vue");
|
|
47
|
+
app.component("GroupTabs", importer.default);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { provide, inject, getCurrentInstance } from "vue";
|
|
2
|
-
import type { App } from "vue";
|
|
3
|
-
|
|
4
|
-
export type LocaleMappings = Record<
|
|
5
|
-
string,
|
|
6
|
-
{ previewLabel: string; codeLabel: string }
|
|
7
|
-
>;
|
|
8
|
-
|
|
9
|
-
const localeMappingSymbol = Symbol(
|
|
10
|
-
"__vitepress_plugin_component_localeMapping",
|
|
11
|
-
);
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Initialize locale mappings for the application.
|
|
15
|
-
* @param app - The Vue application instance. If not provided, the mappings will be provided to the current component.
|
|
16
|
-
*/
|
|
17
|
-
export function useLocaleMappings(app?: App) {
|
|
18
|
-
return {
|
|
19
|
-
set: (localeMapping: LocaleMappings) => {
|
|
20
|
-
const instance = getCurrentInstance();
|
|
21
|
-
if (app) {
|
|
22
|
-
app?.provide(localeMappingSymbol, localeMapping);
|
|
23
|
-
} else {
|
|
24
|
-
if (!instance) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
"useLocaleMappings must be called within a Vue component if not providing an app",
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
provide(localeMappingSymbol, localeMapping);
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
get: () => {
|
|
33
|
-
return inject<LocaleMappings | undefined>(localeMappingSymbol, undefined);
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
}
|