@rspress/plugin-preview 0.0.0-next-20230927072732 → 0.0.0-next-20231108104528
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 +4 -0
- package/dist/index.d.ts +21 -3
- package/dist/index.js +149 -2571
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +16 -0
- package/dist/utils.js +57 -0
- package/dist/utils.js.map +1 -0
- package/dist/virtual-demo.js +9 -21
- package/mdx-meta-loader.cjs +1 -0
- package/package.json +8 -5
- package/static/global-components/Container.scss +1 -37
- package/static/global-components/Container.tsx +77 -5
- package/static/global-components/Device.tsx +6 -10
- package/static/global-components/common/index.scss +37 -0
- package/static/global-components/common/mobile-operation.tsx +12 -2
- package/static/global-components/icons/Codesandbox.tsx +22 -0
- package/static/global-styles/web.css +0 -6
package/dist/utils.d.ts
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* Converts a string to a valid variable name. If the string is already a valid variable name, returns the original string.
|
3
|
+
* @param str - The string to convert.
|
4
|
+
* @returns The converted string.
|
5
|
+
*/
|
6
|
+
declare const toValidVarName: (str: string) => string;
|
7
|
+
declare const generateId: (pageName: string, index: number) => string;
|
8
|
+
/**
|
9
|
+
* remove .html extension and validate
|
10
|
+
* @param routePath id from pathname
|
11
|
+
* @returns normalized id
|
12
|
+
*/
|
13
|
+
declare const normalizeId: (routePath: string) => string;
|
14
|
+
declare const injectDemoBlockImport: (str: string, path: string) => string;
|
15
|
+
|
16
|
+
export { generateId, injectDemoBlockImport, normalizeId, toValidVarName };
|
package/dist/utils.js
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __defProp = Object.defineProperty;
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
6
|
+
var __export = (target, all) => {
|
7
|
+
for (var name in all)
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
9
|
+
};
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
12
|
+
for (let key of __getOwnPropNames(from))
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
15
|
+
}
|
16
|
+
return to;
|
17
|
+
};
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
19
|
+
|
20
|
+
// src/utils.ts
|
21
|
+
var utils_exports = {};
|
22
|
+
__export(utils_exports, {
|
23
|
+
generateId: () => generateId,
|
24
|
+
injectDemoBlockImport: () => injectDemoBlockImport,
|
25
|
+
normalizeId: () => normalizeId,
|
26
|
+
toValidVarName: () => toValidVarName
|
27
|
+
});
|
28
|
+
module.exports = __toCommonJS(utils_exports);
|
29
|
+
var toValidVarName = (str) => {
|
30
|
+
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(str)) {
|
31
|
+
return str;
|
32
|
+
} else {
|
33
|
+
return str.replace(/[^0-9a-zA-Z_$]/g, "_").replace(/^([0-9])/, "_$1");
|
34
|
+
}
|
35
|
+
};
|
36
|
+
var generateId = (pageName, index) => {
|
37
|
+
return `_${toValidVarName(pageName)}_${index}`;
|
38
|
+
};
|
39
|
+
var normalizeId = (routePath) => {
|
40
|
+
const result = routePath.replace(/\.(.*)?$/, "");
|
41
|
+
return toValidVarName(result);
|
42
|
+
};
|
43
|
+
var injectDemoBlockImport = (str, path) => {
|
44
|
+
return `
|
45
|
+
import DemoBlock from '${path}';
|
46
|
+
${str}
|
47
|
+
`;
|
48
|
+
};
|
49
|
+
// Annotate the CommonJS export names for ESM import in node:
|
50
|
+
0 && (module.exports = {
|
51
|
+
generateId,
|
52
|
+
injectDemoBlockImport,
|
53
|
+
normalizeId,
|
54
|
+
toValidVarName
|
55
|
+
});
|
56
|
+
|
57
|
+
//# sourceMappingURL=utils.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,IAAM,iBAAiB,CAAC,QAAwB;AAErD,MAAI,6BAA6B,KAAK,GAAG,GAAG;AAC1C,WAAO;AAAA,EACT,OAAO;AAEL,WAAO,IAAI,QAAQ,mBAAmB,GAAG,EAAE,QAAQ,YAAY,KAAK;AAAA,EACtE;AACF;AAEO,IAAM,aAAa,CAAC,UAAkB,UAAkB;AAC7D,SAAO,IAAI,eAAe,QAAQ,CAAC,IAAI,KAAK;AAC9C;AAOO,IAAM,cAAc,CAAC,cAAsB;AAChD,QAAM,SAAS,UAAU,QAAQ,YAAY,EAAE;AAC/C,SAAO,eAAe,MAAM;AAC9B;AAEO,IAAM,wBAAwB,CAAC,KAAa,SAAyB;AAC1E,SAAO;AAAA,6BACoB,IAAI;AAAA,MAC3B,GAAG;AAAA;AAET;","names":[],"sources":["../src/utils.ts"],"sourcesContent":["/**\n * Converts a string to a valid variable name. If the string is already a valid variable name, returns the original string.\n * @param str - The string to convert.\n * @returns The converted string.\n */\nexport const toValidVarName = (str: string): string => {\n // Check if the string is a valid variable name\n if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(str)) {\n return str; // If it is a valid variable name, return the original string\n } else {\n // If it is not a valid variable name, convert it to a valid variable name\n return str.replace(/[^0-9a-zA-Z_$]/g, '_').replace(/^([0-9])/, '_$1');\n }\n};\n\nexport const generateId = (pageName: string, index: number) => {\n return `_${toValidVarName(pageName)}_${index}`;\n};\n\n/**\n * remove .html extension and validate\n * @param routePath id from pathname\n * @returns normalized id\n */\nexport const normalizeId = (routePath: string) => {\n const result = routePath.replace(/\\.(.*)?$/, '');\n return toValidVarName(result);\n};\n\nexport const injectDemoBlockImport = (str: string, path: string): string => {\n return `\n import DemoBlock from '${path}';\n ${str}\n `;\n};\n"]}
|
package/dist/virtual-demo.js
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
// src/virtual-demo.tsx
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
3
2
|
import { demos } from "virtual-meta";
|
4
3
|
import { createElement } from "react";
|
5
4
|
import { NoSSR, useLocation } from "@rspress/core/runtime";
|
@@ -51,33 +50,22 @@ var css_248z = ":root {\n --rp-iframe-bg: var(--rp-code-block-bg);\n}\n\nbody {
|
|
51
50
|
style_inject_es_default(css_248z);
|
52
51
|
|
53
52
|
// src/virtual-demo.tsx
|
53
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
54
54
|
function Demo(props) {
|
55
55
|
const { pathname } = useLocation();
|
56
|
-
const
|
57
|
-
const normalizedId = normalizeId(
|
56
|
+
const pureDemoPath = pathname.split("/").filter(Boolean).slice(1)?.join("/");
|
57
|
+
const normalizedId = normalizeId(pureDemoPath || "");
|
58
58
|
if (props.iframePosition === "fixed") {
|
59
59
|
const renderDemos = demos.flat().filter((item) => new RegExp(`${normalizedId}_\\d+`).test(item.id));
|
60
|
-
return renderDemos.length > 0 ? /* @__PURE__ */
|
61
|
-
|
62
|
-
|
63
|
-
children:
|
64
|
-
/* @__PURE__ */ _jsx("div", {
|
65
|
-
className: "preview-nav",
|
66
|
-
children: renderDemos[0].title
|
67
|
-
}),
|
68
|
-
renderDemos.map((renderDemo) => {
|
69
|
-
return /* @__PURE__ */ _jsx("div", {
|
70
|
-
children: /* @__PURE__ */ createElement(renderDemo.component)
|
71
|
-
}, renderDemo.id);
|
72
|
-
})
|
73
|
-
]
|
60
|
+
return renderDemos.length > 0 ? /* @__PURE__ */ jsx(NoSSR, { children: /* @__PURE__ */ jsxs("div", { className: "preview-container", children: [
|
61
|
+
/* @__PURE__ */ jsx("div", { className: "preview-nav", children: renderDemos[0].title }),
|
62
|
+
renderDemos.map((renderDemo) => {
|
63
|
+
return /* @__PURE__ */ jsx("div", { children: createElement(renderDemo.component) }, renderDemo.id);
|
74
64
|
})
|
75
|
-
}) : /* @__PURE__ */
|
65
|
+
] }) }) : /* @__PURE__ */ jsx(NotFoundLayout, {});
|
76
66
|
} else {
|
77
67
|
const component = demos.flat().find((item) => item.id === normalizedId)?.component;
|
78
|
-
return component ? /* @__PURE__ */
|
79
|
-
children: /* @__PURE__ */ createElement(component)
|
80
|
-
}) : /* @__PURE__ */ _jsx(NotFoundLayout, {});
|
68
|
+
return component ? /* @__PURE__ */ jsx(NoSSR, { children: createElement(component) }) : /* @__PURE__ */ jsx(NotFoundLayout, {});
|
81
69
|
}
|
82
70
|
}
|
83
71
|
export {
|
package/mdx-meta-loader.cjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@rspress/plugin-preview",
|
3
|
-
"version": "0.0.0-next-
|
3
|
+
"version": "0.0.0-next-20231108104528",
|
4
4
|
"description": "A plugin for rspress to preview the code block in markdown/mdx file.",
|
5
5
|
"bugs": "https://github.com/web-infra-dev/rspress/issues",
|
6
6
|
"repository": {
|
@@ -21,14 +21,18 @@
|
|
21
21
|
],
|
22
22
|
"dependencies": {
|
23
23
|
"@mdx-js/mdx": "2.2.1",
|
24
|
-
"@modern-js/utils": "2.
|
24
|
+
"@modern-js/utils": "2.39.2",
|
25
25
|
"qrcode.react": "^3.1.0",
|
26
26
|
"remark-gfm": "3.0.1",
|
27
|
-
"
|
27
|
+
"rspack-plugin-virtual-module": "0.1.12",
|
28
|
+
"codesandbox": "2.2.3",
|
29
|
+
"lodash": "4.17.21",
|
30
|
+
"@rspress/shared": "0.0.0-next-20231108104528"
|
28
31
|
},
|
29
32
|
"devDependencies": {
|
30
33
|
"@types/mdast": "^3.0.10",
|
31
34
|
"@types/node": "^18.11.17",
|
35
|
+
"@types/lodash": "4.14.200",
|
32
36
|
"@types/react": "^18",
|
33
37
|
"@types/react-dom": "^18",
|
34
38
|
"mdast-util-mdxjs-esm": "^1.3.0",
|
@@ -36,7 +40,6 @@
|
|
36
40
|
"react": "^18",
|
37
41
|
"react-dom": "^18",
|
38
42
|
"react-router-dom": "^6.8.1",
|
39
|
-
"rspack-plugin-virtual-module": "0.1.11",
|
40
43
|
"typescript": "^5",
|
41
44
|
"unified": "^10.1.2",
|
42
45
|
"unist-util-visit": "^4.1.1"
|
@@ -44,7 +47,7 @@
|
|
44
47
|
"peerDependencies": {
|
45
48
|
"react": ">=17",
|
46
49
|
"react-router-dom": "^6.8.1",
|
47
|
-
"@rspress/core": "0.0.0-next-
|
50
|
+
"@rspress/core": "0.0.0-next-20231108104528"
|
48
51
|
},
|
49
52
|
"files": [
|
50
53
|
"dist",
|
@@ -11,43 +11,6 @@
|
|
11
11
|
.rspress-preview {
|
12
12
|
padding: 16px 0;
|
13
13
|
|
14
|
-
&-operations {
|
15
|
-
button {
|
16
|
-
width: 28px;
|
17
|
-
height: 28px;
|
18
|
-
padding: 0;
|
19
|
-
text-align: center;
|
20
|
-
border-radius: 50%;
|
21
|
-
border: 1px solid transparent;
|
22
|
-
background-color: var(--rp-c-bg-soft);
|
23
|
-
margin-left: 14px;
|
24
|
-
|
25
|
-
&:hover {
|
26
|
-
background-color: var(--rp-preview-button-hover-bg);
|
27
|
-
}
|
28
|
-
}
|
29
|
-
|
30
|
-
svg {
|
31
|
-
display: inline-block;
|
32
|
-
vertical-align: -2px;
|
33
|
-
}
|
34
|
-
|
35
|
-
&.mobile {
|
36
|
-
display: flex;
|
37
|
-
justify-content: flex-end;
|
38
|
-
width: 100%;
|
39
|
-
padding: 6px;
|
40
|
-
}
|
41
|
-
|
42
|
-
&.web {
|
43
|
-
display: flex;
|
44
|
-
justify-content: center;
|
45
|
-
position: absolute;
|
46
|
-
top: calc(50% - 14px);
|
47
|
-
right: 16px;
|
48
|
-
}
|
49
|
-
}
|
50
|
-
|
51
14
|
&-wrapper {
|
52
15
|
border: 1px solid #e6e6e6;
|
53
16
|
border-radius: 8px;
|
@@ -58,6 +21,7 @@
|
|
58
21
|
position: relative;
|
59
22
|
border: 1px solid #e6e6e6;
|
60
23
|
border-radius: 8px;
|
24
|
+
display: flex;
|
61
25
|
}
|
62
26
|
|
63
27
|
&-qrcode {
|
@@ -1,17 +1,23 @@
|
|
1
1
|
import { useCallback, useState } from 'react';
|
2
2
|
import { withBase, useLang, NoSSR } from '@rspress/core/runtime';
|
3
|
+
import { getParameters } from 'codesandbox/lib/api/define';
|
3
4
|
import MobileOperation from './common/mobile-operation';
|
4
5
|
import IconCode from './icons/Code';
|
6
|
+
import IconCodesandbox from './icons/Codesandbox';
|
5
7
|
import './Container.scss';
|
6
8
|
|
7
9
|
type ContainerProps = {
|
8
10
|
children: React.ReactNode[];
|
9
11
|
isMobile: 'true' | 'false';
|
12
|
+
enableCodesandbox: 'true' | 'false';
|
10
13
|
url: string;
|
14
|
+
content: string;
|
15
|
+
packageName: string;
|
11
16
|
};
|
12
17
|
|
13
18
|
const Container: React.FC<ContainerProps> = props => {
|
14
|
-
const { children, isMobile, url } =
|
19
|
+
const { children, isMobile, url, content, packageName, enableCodesandbox } =
|
20
|
+
props;
|
15
21
|
const [showCode, setShowCode] = useState(false);
|
16
22
|
const lang = useLang();
|
17
23
|
|
@@ -34,6 +40,54 @@ const Container: React.FC<ContainerProps> = props => {
|
|
34
40
|
setIframeKey(Math.random());
|
35
41
|
}, []);
|
36
42
|
|
43
|
+
const gotoCodeSandBox = () => {
|
44
|
+
const sandBoxConfig = {
|
45
|
+
files: {
|
46
|
+
'package.json': {
|
47
|
+
isBinary: false,
|
48
|
+
content: JSON.stringify({
|
49
|
+
dependencies: {
|
50
|
+
react: '18',
|
51
|
+
'react-dom': '18',
|
52
|
+
[packageName]: 'latest',
|
53
|
+
},
|
54
|
+
}),
|
55
|
+
},
|
56
|
+
[`demo.tsx`]: {
|
57
|
+
isBinary: false,
|
58
|
+
content,
|
59
|
+
},
|
60
|
+
[`index.tsx`]: {
|
61
|
+
isBinary: false,
|
62
|
+
content: [
|
63
|
+
`import React from 'react'`,
|
64
|
+
`import ReactDOM from 'react-dom'`,
|
65
|
+
`import Demo from './demo'`,
|
66
|
+
`ReactDOM.render(<Demo />, document.getElementById('root'))`,
|
67
|
+
].join('\n'),
|
68
|
+
},
|
69
|
+
},
|
70
|
+
};
|
71
|
+
|
72
|
+
// to specific demo file
|
73
|
+
const query = `file=/demo.tsx`;
|
74
|
+
const form = document.createElement('form');
|
75
|
+
form.action = `https://codesandbox.io/api/v1/sandboxes/define?query=${encodeURIComponent(
|
76
|
+
query,
|
77
|
+
)}`;
|
78
|
+
form.target = '_blank';
|
79
|
+
form.method = 'POST';
|
80
|
+
form.style.display = 'none';
|
81
|
+
const field = document.createElement('input');
|
82
|
+
field.name = 'parameters';
|
83
|
+
field.type = 'hidden';
|
84
|
+
field.setAttribute('value', getParameters(sandBoxConfig));
|
85
|
+
form.appendChild(field);
|
86
|
+
document.body.appendChild(form);
|
87
|
+
form.submit();
|
88
|
+
document.body.removeChild(form);
|
89
|
+
};
|
90
|
+
|
37
91
|
return (
|
38
92
|
<NoSSR>
|
39
93
|
<div className="rspress-preview">
|
@@ -42,7 +96,13 @@ const Container: React.FC<ContainerProps> = props => {
|
|
42
96
|
<div className="rspress-preview-code">{children?.[0]}</div>
|
43
97
|
<div className="rspress-preview-device">
|
44
98
|
<iframe src={getPageUrl()} key={iframeKey}></iframe>
|
45
|
-
<MobileOperation
|
99
|
+
<MobileOperation
|
100
|
+
url={url}
|
101
|
+
refresh={refresh}
|
102
|
+
gotoCodeSandBox={
|
103
|
+
enableCodesandbox === 'true' ? gotoCodeSandBox : undefined
|
104
|
+
}
|
105
|
+
/>
|
46
106
|
</div>
|
47
107
|
</div>
|
48
108
|
) : (
|
@@ -51,16 +111,28 @@ const Container: React.FC<ContainerProps> = props => {
|
|
51
111
|
<div
|
52
112
|
style={{
|
53
113
|
overflow: 'auto',
|
54
|
-
|
114
|
+
flex: 'auto',
|
55
115
|
}}
|
56
116
|
>
|
57
117
|
{children?.[1]}
|
58
118
|
</div>
|
59
119
|
<div className="rspress-preview-operations web">
|
120
|
+
{enableCodesandbox === 'true' && (
|
121
|
+
<button
|
122
|
+
onClick={gotoCodeSandBox}
|
123
|
+
aria-label={
|
124
|
+
lang === 'zh'
|
125
|
+
? '在 Codesandbox 打开'
|
126
|
+
: 'Open in Codesandbox'
|
127
|
+
}
|
128
|
+
>
|
129
|
+
<IconCodesandbox />
|
130
|
+
</button>
|
131
|
+
)}
|
60
132
|
<button
|
61
133
|
onClick={toggleCode}
|
62
|
-
aria-label={lang === 'zh' ? '收起代码' : ''}
|
63
|
-
className={showCode ? 'button-expanded' : '
|
134
|
+
aria-label={lang === 'zh' ? '收起代码' : 'Collapse Code'}
|
135
|
+
className={showCode ? 'button-expanded' : ''}
|
64
136
|
>
|
65
137
|
<IconCode />
|
66
138
|
</button>
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { usePageData, withBase } from '@rspress/core/runtime';
|
2
2
|
import { demos } from 'virtual-meta';
|
3
3
|
import { useCallback, useEffect, useState } from 'react';
|
4
|
+
import { normalizeId } from '../../dist/utils';
|
4
5
|
import MobileOperation from './common/mobile-operation';
|
5
6
|
import './Device.scss';
|
6
7
|
|
@@ -12,22 +13,17 @@ export default () => {
|
|
12
13
|
// Do nothing in ssr
|
13
14
|
return '';
|
14
15
|
};
|
15
|
-
const removeLeadingSlash = (url: string) => {
|
16
|
-
return url.charAt(0) === '/' ? url.slice(1) : url;
|
17
|
-
};
|
18
|
-
|
19
|
-
const getPageKey = (route: string) => {
|
20
|
-
const cleanRoute = removeLeadingSlash(route);
|
21
|
-
return cleanRoute.replace(/\//g, '_').replace(/\.[^.]+$/, '') || 'index';
|
22
|
-
};
|
23
16
|
const { page } = usePageData();
|
24
|
-
|
17
|
+
|
18
|
+
const pageName = `_${normalizeId(page.pagePath)}`;
|
25
19
|
const url = `~demo/${pageName}`;
|
26
20
|
const haveDemos =
|
27
21
|
demos.flat().filter(item => new RegExp(`${pageName}_\\d+`).test(item.id))
|
28
22
|
.length > 0;
|
23
|
+
const initialInnerWidth =
|
24
|
+
typeof window !== 'undefined' ? window.innerWidth : 0;
|
29
25
|
const [asideWidth, setAsideWidth] = useState('0px');
|
30
|
-
const [innerWidth, setInnerWidth] = useState(
|
26
|
+
const [innerWidth, setInnerWidth] = useState(initialInnerWidth);
|
31
27
|
const [iframeKey, setIframeKey] = useState(0);
|
32
28
|
const refresh = useCallback(() => {
|
33
29
|
setIframeKey(Math.random());
|
@@ -0,0 +1,37 @@
|
|
1
|
+
.rspress-preview {
|
2
|
+
&-operations {
|
3
|
+
button {
|
4
|
+
width: 28px;
|
5
|
+
height: 28px;
|
6
|
+
padding: 0;
|
7
|
+
text-align: center;
|
8
|
+
border-radius: 50%;
|
9
|
+
border: 1px solid transparent;
|
10
|
+
background-color: var(--rp-c-bg-soft);
|
11
|
+
margin-left: 14px;
|
12
|
+
|
13
|
+
&:hover {
|
14
|
+
background-color: var(--rp-preview-button-hover-bg);
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
svg {
|
19
|
+
display: inline-block;
|
20
|
+
vertical-align: -2px;
|
21
|
+
}
|
22
|
+
|
23
|
+
&.mobile {
|
24
|
+
display: flex;
|
25
|
+
justify-content: flex-end;
|
26
|
+
width: 100%;
|
27
|
+
padding: 6px;
|
28
|
+
}
|
29
|
+
|
30
|
+
&.web {
|
31
|
+
display: flex;
|
32
|
+
justify-content: center;
|
33
|
+
align-items: center;
|
34
|
+
flex: none;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
@@ -4,15 +4,19 @@ import { withBase, useLang } from '@rspress/core/runtime';
|
|
4
4
|
import IconLaunch from '../icons/Launch';
|
5
5
|
import IconQrcode from '../icons/Qrcode';
|
6
6
|
import IconRefresh from '../icons/Refresh';
|
7
|
+
import IconCodesandbox from '../icons/Codesandbox';
|
8
|
+
import './index.scss';
|
7
9
|
|
8
10
|
const locales = {
|
9
11
|
zh: {
|
10
12
|
refresh: '刷新页面',
|
11
13
|
open: '在新页面打开',
|
14
|
+
codesandbox: '在 Codesandbox 打开',
|
12
15
|
},
|
13
16
|
en: {
|
14
|
-
refresh: '
|
17
|
+
refresh: 'Refresh',
|
15
18
|
open: 'Open in new page',
|
19
|
+
codesandbox: 'Open in Codesandbox',
|
16
20
|
},
|
17
21
|
};
|
18
22
|
|
@@ -20,9 +24,10 @@ export default (props: {
|
|
20
24
|
url: string;
|
21
25
|
className?: string;
|
22
26
|
refresh: () => void;
|
27
|
+
gotoCodeSandBox?: () => void;
|
23
28
|
}) => {
|
24
29
|
const [showQRCode, setShowQRCode] = useState(false);
|
25
|
-
const { url, className = '', refresh } = props;
|
30
|
+
const { url, className = '', refresh, gotoCodeSandBox } = props;
|
26
31
|
const lang = useLang();
|
27
32
|
const triggerRef = useRef(null);
|
28
33
|
const t = lang === 'zh' ? locales.zh : locales.en;
|
@@ -85,6 +90,11 @@ export default (props: {
|
|
85
90
|
|
86
91
|
return (
|
87
92
|
<div className={`rspress-preview-operations mobile ${className}`}>
|
93
|
+
{gotoCodeSandBox && (
|
94
|
+
<button onClick={gotoCodeSandBox} aria-label={t.codesandbox}>
|
95
|
+
<IconCodesandbox />
|
96
|
+
</button>
|
97
|
+
)}
|
88
98
|
<button onClick={refresh} aria-label={t.refresh}>
|
89
99
|
<IconRefresh />
|
90
100
|
</button>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
const Codesandbox = ({ color = 'currentColor', ...props }) => (
|
2
|
+
<svg
|
3
|
+
width="1em"
|
4
|
+
height="1em"
|
5
|
+
viewBox="0 0 48 48"
|
6
|
+
fill="none"
|
7
|
+
stroke={color}
|
8
|
+
strokeWidth="4"
|
9
|
+
xmlns="http://www.w3.org/2000/svg"
|
10
|
+
{...props}
|
11
|
+
>
|
12
|
+
<path
|
13
|
+
d="M25.0016 1.5998L42.9016 11.8998C43.5016 12.2998 43.9016 12.8998 43.9016 13.5998V34.2998C43.9016 34.9998 43.5016 35.6998 42.9016 35.9998L25.0016 46.3998C24.4016 46.7998 23.6016 46.7998 23.0016 46.3998L5.10156 36.0998C4.50156 35.6998 4.10156 35.0998 4.10156 34.3998V13.6998C4.10156 12.9998 4.50156 12.2998 5.10156 11.9998L23.0016 1.5998C23.6016 1.1998 24.4016 1.1998 25.0016 1.5998ZM38.5016 13.9998L30.6016 9.49981L24.0016 13.9998L17.5016 9.9998L10.2016 14.2998L24.0016 22.9998L38.5016 13.9998ZM22.0016 40.3998V26.2998L8.00156 17.3998V25.2998L16.0016 30.7998V36.9998L22.0016 40.3998ZM26.0016 40.3998L32.0016 36.8998V31.6998L40.0016 26.1998V17.2998L26.0016 26.1998V40.3998Z"
|
14
|
+
fill="currentColor"
|
15
|
+
stroke="none"
|
16
|
+
stroke-width="none"
|
17
|
+
stroke-linecap="butt"
|
18
|
+
></path>
|
19
|
+
</svg>
|
20
|
+
);
|
21
|
+
|
22
|
+
export default Codesandbox;
|