@rspress/plugin-preview 1.43.13 → 1.45.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/dist/index.d.ts +1 -1
- package/dist/index.js +93 -70
- package/dist/utils.js +14 -8
- package/package.json +6 -6
- package/static/global-components/Container.tsx +1 -1
- package/static/global-components/ContainerFixedPerComp.tsx +101 -0
- package/static/global-components/Device.scss +5 -5
- package/static/global-components/Device.tsx +3 -3
- package/static/global-components/DeviceFixedPerComp.tsx +106 -0
- package/static/global-components/common/index.scss +1 -0
- package/static/global-components/common/mobile-operation.tsx +36 -8
- package/static/global-components/icons/Back.tsx +31 -0
- package/static/global-components/useIframeUrlPerComp.ts +39 -0
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
"use strict";
|
2
2
|
var __webpack_modules__ = {
|
3
3
|
"@rsbuild/plugin-less": function(module) {
|
4
|
-
module.exports = import("@rsbuild/plugin-less")
|
4
|
+
module.exports = import("@rsbuild/plugin-less").then(function(module) {
|
5
|
+
return module;
|
6
|
+
});
|
5
7
|
},
|
6
8
|
"@rsbuild/plugin-sass": function(module) {
|
7
|
-
module.exports = import("@rsbuild/plugin-sass")
|
9
|
+
module.exports = import("@rsbuild/plugin-sass").then(function(module) {
|
10
|
+
return module;
|
11
|
+
});
|
8
12
|
}
|
9
13
|
};
|
10
14
|
var __webpack_module_cache__ = {};
|
@@ -18,12 +22,8 @@ function __webpack_require__(moduleId) {
|
|
18
22
|
return module.exports;
|
19
23
|
}
|
20
24
|
(()=>{
|
21
|
-
__webpack_require__.n =
|
22
|
-
var getter = module && module.__esModule ?
|
23
|
-
return module['default'];
|
24
|
-
} : function() {
|
25
|
-
return module;
|
26
|
-
};
|
25
|
+
__webpack_require__.n = (module)=>{
|
26
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
27
27
|
__webpack_require__.d(getter, {
|
28
28
|
a: getter
|
29
29
|
});
|
@@ -31,7 +31,7 @@ function __webpack_require__(moduleId) {
|
|
31
31
|
};
|
32
32
|
})();
|
33
33
|
(()=>{
|
34
|
-
__webpack_require__.d =
|
34
|
+
__webpack_require__.d = (exports1, definition)=>{
|
35
35
|
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
36
36
|
enumerable: true,
|
37
37
|
get: definition[key]
|
@@ -39,12 +39,10 @@ function __webpack_require__(moduleId) {
|
|
39
39
|
};
|
40
40
|
})();
|
41
41
|
(()=>{
|
42
|
-
__webpack_require__.o =
|
43
|
-
return Object.prototype.hasOwnProperty.call(obj, prop);
|
44
|
-
};
|
42
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
45
43
|
})();
|
46
44
|
(()=>{
|
47
|
-
__webpack_require__.r =
|
45
|
+
__webpack_require__.r = (exports1)=>{
|
48
46
|
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
49
47
|
value: 'Module'
|
50
48
|
});
|
@@ -83,60 +81,63 @@ var __webpack_exports__ = {};
|
|
83
81
|
import DemoBlock from ${JSON.stringify(path)};
|
84
82
|
${str}
|
85
83
|
`;
|
86
|
-
function
|
84
|
+
function generateEntryForPerComponent(demos, entryCssPath, framework) {
|
87
85
|
const sourceEntry = {};
|
88
|
-
|
89
|
-
if ('follow' === position) Object.values(demos).forEach((routes)=>{
|
86
|
+
Object.values(demos).forEach((routes)=>{
|
90
87
|
routes.forEach((route)=>{
|
91
88
|
const { id, path: demoPath } = route;
|
92
89
|
const entry = (0, external_node_path_namespaceObject.join)(virtualDir, `${id}.entry.tsx`);
|
93
90
|
const solidEntry = `
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
91
|
+
import { render } from 'solid-js/web';
|
92
|
+
import ${JSON.stringify(entryCssPath)};
|
93
|
+
import Demo from ${JSON.stringify(demoPath)};
|
94
|
+
render(() => <Demo />, document.getElementById('root'));
|
95
|
+
`;
|
99
96
|
const reactEntry = `
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
97
|
+
import { render } from 'react-dom';
|
98
|
+
import ${JSON.stringify(entryCssPath)};
|
99
|
+
import Demo from ${JSON.stringify(demoPath)};
|
100
|
+
render(<Demo />, document.getElementById('root'));
|
101
|
+
`;
|
105
102
|
const entryContent = 'react' === framework ? reactEntry : solidEntry;
|
106
103
|
(0, external_node_fs_namespaceObject.writeFileSync)(entry, entryContent);
|
107
104
|
sourceEntry[id] = entry;
|
108
105
|
});
|
109
106
|
});
|
110
|
-
|
107
|
+
return sourceEntry;
|
108
|
+
}
|
109
|
+
function generateEntryForPage(demos, entryCssPath, framework) {
|
110
|
+
const sourceEntry = {};
|
111
|
+
Object.entries(demos).forEach(([key, routes])=>{
|
111
112
|
if (0 === routes.length) return;
|
112
113
|
const reactContent = `
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
114
|
+
import { render } from 'react-dom';
|
115
|
+
import ${JSON.stringify(entryCssPath)};
|
116
|
+
${routes.map((demo, index)=>`import Demo_${index} from ${JSON.stringify(demo.path)}`).join('\n')}
|
117
|
+
function App() {
|
118
|
+
return (
|
119
|
+
<div className="preview-container">
|
120
|
+
<div className="preview-nav">{"${routes[0].title}"}</div>
|
121
|
+
${routes.map((_demo, index)=>`<Demo_${index} />`).join('\n')}
|
122
|
+
</div>
|
123
|
+
)
|
124
|
+
}
|
125
|
+
render(<App /> , document.getElementById('root'));
|
126
|
+
`;
|
126
127
|
const solidContent = `
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
128
|
+
import { render } from 'solid-js/web';
|
129
|
+
import ${JSON.stringify(entryCssPath)};
|
130
|
+
${routes.map((demo, index)=>`import Demo_${index} from ${JSON.stringify(demo.path)}`).join('\n')}
|
131
|
+
function App() {
|
132
|
+
return (
|
133
|
+
<div class="preview-container">
|
134
|
+
<div class="preview-nav">{"${routes[0].title}"}</div>
|
135
|
+
${routes.map((_, index)=>`<Demo_${index} />`).join('\n')}
|
136
|
+
</div>
|
137
|
+
)
|
138
|
+
}
|
139
|
+
render(() => <App /> , document.getElementById('root'));
|
140
|
+
`;
|
140
141
|
const renderContent = 'solid' === framework ? solidContent : reactContent;
|
141
142
|
const id = `_${toValidVarName(key)}`;
|
142
143
|
const entry = (0, external_node_path_namespaceObject.join)(virtualDir, `${id}.entry.tsx`);
|
@@ -145,6 +146,23 @@ var __webpack_exports__ = {};
|
|
145
146
|
});
|
146
147
|
return sourceEntry;
|
147
148
|
}
|
149
|
+
function generateEntry(demos, framework, position) {
|
150
|
+
const sourceEntry = {};
|
151
|
+
const entryCssPath = (0, external_node_path_namespaceObject.join)(staticPath, 'global-styles', 'entry.css');
|
152
|
+
if ('follow' === position) {
|
153
|
+
const entries = generateEntryForPerComponent(demos, entryCssPath, framework);
|
154
|
+
Object.assign(sourceEntry, entries);
|
155
|
+
} else if ('fixed' === position) {
|
156
|
+
const entries = generateEntryForPage(demos, entryCssPath, framework);
|
157
|
+
Object.assign(sourceEntry, entries);
|
158
|
+
} else if ('fixed-with-per-comp' === position) {
|
159
|
+
const entries = generateEntryForPerComponent(demos, entryCssPath, framework);
|
160
|
+
Object.assign(sourceEntry, entries);
|
161
|
+
const perPageEntries = generateEntryForPage(demos, entryCssPath, framework);
|
162
|
+
Object.assign(sourceEntry, perPageEntries);
|
163
|
+
}
|
164
|
+
return sourceEntry;
|
165
|
+
}
|
148
166
|
const node_utils_namespaceObject = require("@rspress/shared/node-utils");
|
149
167
|
const convert = function(test) {
|
150
168
|
if (null == test) return ok;
|
@@ -361,7 +379,7 @@ var __webpack_exports__ = {};
|
|
361
379
|
if (isMobileMode && 'fixed' === position) void 0 !== externalDemoIndex && Object.assign(currentNode, getExternalDemoContent(tempVar));
|
362
380
|
else Object.assign(currentNode, {
|
363
381
|
type: 'mdxJsxFlowElement',
|
364
|
-
name: 'Container',
|
382
|
+
name: 'fixed-with-per-comp' === position ? 'ContainerFixedPerComp' : 'Container',
|
365
383
|
attributes: [
|
366
384
|
{
|
367
385
|
type: 'mdxJsxAttribute',
|
@@ -411,12 +429,12 @@ var __webpack_exports__ = {};
|
|
411
429
|
if ('hasVisited' in node) return;
|
412
430
|
if (node.lang && previewLanguages.includes(node.lang)) {
|
413
431
|
var _node_meta, _node_meta1, _node_meta2, _node_meta3, _node_meta4, _node_meta5;
|
414
|
-
if ((null
|
432
|
+
if ((null == (_node_meta = node.meta) ? void 0 : _node_meta.includes('pure')) || !(null == (_node_meta1 = node.meta) ? void 0 : _node_meta1.includes('preview')) && 'pure' === defaultRenderMode) return;
|
415
433
|
const value = injectDemoBlockImport(previewCodeTransform({
|
416
434
|
language: node.lang,
|
417
435
|
code: node.value
|
418
436
|
}), demoBlockComponentPath);
|
419
|
-
const isMobileMode = (null
|
437
|
+
const isMobileMode = (null == (_node_meta2 = node.meta) ? void 0 : _node_meta2.includes('mobile')) || (null == (_node_meta3 = node.meta) ? void 0 : _node_meta3.includes('iframe')) || !(null == (_node_meta4 = node.meta) ? void 0 : _node_meta4.includes('web')) && !(null == (_node_meta5 = node.meta) ? void 0 : _node_meta5.includes('internal')) && 'iframe' === previewMode;
|
420
438
|
const id = generateId(pageName, index++);
|
421
439
|
const virtualModulePath = (0, external_node_path_namespaceObject.join)(virtualDir, `${id}.tsx`);
|
422
440
|
constructDemoNode(id, virtualModulePath, node, isMobileMode);
|
@@ -431,7 +449,7 @@ var __webpack_exports__ = {};
|
|
431
449
|
if (remarkPlugin_demos[pageName].length > 0) data.pageMeta.haveDemos = true;
|
432
450
|
};
|
433
451
|
};
|
434
|
-
let
|
452
|
+
let src_routeMeta;
|
435
453
|
const DEFAULT_PREVIEW_LANGUAGES = [
|
436
454
|
'jsx',
|
437
455
|
'tsx'
|
@@ -440,10 +458,7 @@ var __webpack_exports__ = {};
|
|
440
458
|
const { isMobile = false, iframeOptions = {}, iframePosition = 'follow', defaultRenderMode = 'preview', previewLanguages = DEFAULT_PREVIEW_LANGUAGES, previewCodeTransform = ({ code })=>code } = options ?? {};
|
441
459
|
const previewMode = (null == options ? void 0 : options.previewMode) ?? (isMobile ? 'iframe' : 'internal');
|
442
460
|
const { devPort = 7890, framework = 'react', position = iframePosition, builderConfig = {} } = iframeOptions;
|
443
|
-
const
|
444
|
-
(0, external_node_path_namespaceObject.join)(staticPath, 'global-components', 'Device.tsx')
|
445
|
-
] : [];
|
446
|
-
const getRouteMeta = ()=>src_rslib_entry_routeMeta;
|
461
|
+
const getRouteMeta = ()=>src_routeMeta;
|
447
462
|
let lastDemos;
|
448
463
|
let devServer;
|
449
464
|
let clientConfig;
|
@@ -456,7 +471,7 @@ var __webpack_exports__ = {};
|
|
456
471
|
return config;
|
457
472
|
},
|
458
473
|
routeGenerated (routes) {
|
459
|
-
|
474
|
+
src_routeMeta = routes;
|
460
475
|
},
|
461
476
|
async beforeBuild (_, isProd) {
|
462
477
|
if (!isProd) try {
|
@@ -480,7 +495,7 @@ var __webpack_exports__ = {};
|
|
480
495
|
var _devServer_server;
|
481
496
|
if ((0, external_lodash_namespaceObject.isEqual)(remarkPlugin_demos, lastDemos)) return;
|
482
497
|
lastDemos = (0, external_lodash_namespaceObject.cloneDeep)(remarkPlugin_demos);
|
483
|
-
await (null == devServer ? void 0 : null
|
498
|
+
await (null == devServer ? void 0 : null == (_devServer_server = devServer.server) ? void 0 : _devServer_server.close());
|
484
499
|
devServer = void 0;
|
485
500
|
const sourceEntry = generateEntry(remarkPlugin_demos, framework, position);
|
486
501
|
const outDir = (0, external_node_path_namespaceObject.join)(config.outDir ?? 'doc_build', '~demo');
|
@@ -515,6 +530,7 @@ var __webpack_exports__ = {};
|
|
515
530
|
plugins: null == config ? void 0 : config.builderPlugins
|
516
531
|
}, builderConfig);
|
517
532
|
const rsbuildInstance = await (0, core_namespaceObject.createRsbuild)({
|
533
|
+
callerName: 'rspress',
|
518
534
|
rsbuildConfig
|
519
535
|
});
|
520
536
|
const { pluginSass } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "@rsbuild/plugin-sass"));
|
@@ -558,11 +574,11 @@ var __webpack_exports__ = {};
|
|
558
574
|
setup: (api)=>{
|
559
575
|
api.modifyRsbuildConfig((config)=>{
|
560
576
|
var _config_output;
|
561
|
-
if ((null
|
577
|
+
if ((null == (_config_output = config.output) ? void 0 : _config_output.target) === 'web') clientConfig = config;
|
562
578
|
});
|
563
579
|
api.onCloseDevServer(async ()=>{
|
564
580
|
var _devServer_server;
|
565
|
-
await (null == devServer ? void 0 : null
|
581
|
+
await (null == devServer ? void 0 : null == (_devServer_server = devServer.server) ? void 0 : _devServer_server.close());
|
566
582
|
devServer = void 0;
|
567
583
|
});
|
568
584
|
}
|
@@ -588,16 +604,23 @@ var __webpack_exports__ = {};
|
|
588
604
|
]
|
589
605
|
],
|
590
606
|
globalComponents: [
|
591
|
-
(0, external_node_path_namespaceObject.join)(staticPath, 'global-components', 'Container.tsx')
|
607
|
+
(0, external_node_path_namespaceObject.join)(staticPath, 'global-components', 'Container.tsx'),
|
608
|
+
(0, external_node_path_namespaceObject.join)(staticPath, 'global-components', 'ContainerFixedPerComp.tsx')
|
592
609
|
]
|
593
610
|
},
|
594
|
-
globalUIComponents
|
611
|
+
globalUIComponents: 'fixed-with-per-comp' === position ? [
|
612
|
+
(0, external_node_path_namespaceObject.join)(staticPath, 'global-components', 'DeviceFixedPerComp.tsx')
|
613
|
+
] : 'fixed' === position ? [
|
614
|
+
(0, external_node_path_namespaceObject.join)(staticPath, 'global-components', 'Device.tsx')
|
615
|
+
] : [],
|
595
616
|
globalStyles: (0, external_node_path_namespaceObject.join)(staticPath, 'global-styles', `${previewMode}.css`)
|
596
617
|
};
|
597
618
|
}
|
598
619
|
})();
|
599
|
-
|
600
|
-
for(var __webpack_i__ in __webpack_exports__)
|
601
|
-
|
620
|
+
exports.pluginPreview = __webpack_exports__.pluginPreview;
|
621
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
622
|
+
"pluginPreview"
|
623
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
624
|
+
Object.defineProperty(exports, '__esModule', {
|
602
625
|
value: true
|
603
626
|
});
|
package/dist/utils.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
"use strict";
|
2
2
|
var __webpack_require__ = {};
|
3
3
|
(()=>{
|
4
|
-
__webpack_require__.d =
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
5
5
|
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
6
6
|
enumerable: true,
|
7
7
|
get: definition[key]
|
@@ -9,12 +9,10 @@ var __webpack_require__ = {};
|
|
9
9
|
};
|
10
10
|
})();
|
11
11
|
(()=>{
|
12
|
-
__webpack_require__.o =
|
13
|
-
return Object.prototype.hasOwnProperty.call(obj, prop);
|
14
|
-
};
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
15
13
|
})();
|
16
14
|
(()=>{
|
17
|
-
__webpack_require__.r =
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
18
16
|
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
19
17
|
value: 'Module'
|
20
18
|
});
|
@@ -44,8 +42,16 @@ const injectDemoBlockImport = (str, path)=>`
|
|
44
42
|
import DemoBlock from ${JSON.stringify(path)};
|
45
43
|
${str}
|
46
44
|
`;
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
exports.generateId = __webpack_exports__.generateId;
|
46
|
+
exports.injectDemoBlockImport = __webpack_exports__.injectDemoBlockImport;
|
47
|
+
exports.normalizeId = __webpack_exports__.normalizeId;
|
48
|
+
exports.toValidVarName = __webpack_exports__.toValidVarName;
|
49
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
50
|
+
"generateId",
|
51
|
+
"injectDemoBlockImport",
|
52
|
+
"normalizeId",
|
53
|
+
"toValidVarName"
|
54
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
55
|
+
Object.defineProperty(exports, '__esModule', {
|
50
56
|
value: true
|
51
57
|
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@rspress/plugin-preview",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.45.0",
|
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": {
|
@@ -17,7 +17,7 @@
|
|
17
17
|
"static"
|
18
18
|
],
|
19
19
|
"dependencies": {
|
20
|
-
"@rsbuild/core": "1.3.
|
20
|
+
"@rsbuild/core": "~1.3.18",
|
21
21
|
"@rsbuild/plugin-babel": "~1.0.5",
|
22
22
|
"@rsbuild/plugin-less": "~1.2.4",
|
23
23
|
"@rsbuild/plugin-react": "~1.3.1",
|
@@ -25,11 +25,11 @@
|
|
25
25
|
"@rsbuild/plugin-solid": "~1.0.5",
|
26
26
|
"lodash": "4.17.21",
|
27
27
|
"qrcode.react": "^3.2.0",
|
28
|
-
"@rspress/shared": "1.
|
29
|
-
"@rspress/theme-default": "1.
|
28
|
+
"@rspress/shared": "1.45.0",
|
29
|
+
"@rspress/theme-default": "1.45.0"
|
30
30
|
},
|
31
31
|
"devDependencies": {
|
32
|
-
"@rslib/core": "0.
|
32
|
+
"@rslib/core": "~0.6.9",
|
33
33
|
"@types/lodash": "^4.17.15",
|
34
34
|
"@types/mdast": "^3.0.15",
|
35
35
|
"@types/node": "^18.11.17",
|
@@ -45,7 +45,7 @@
|
|
45
45
|
"unist-util-visit": "^4.1.2"
|
46
46
|
},
|
47
47
|
"peerDependencies": {
|
48
|
-
"@rspress/core": "^1.
|
48
|
+
"@rspress/core": "^1.45.0",
|
49
49
|
"react": ">=17.0.0",
|
50
50
|
"react-router-dom": "^6.8.1"
|
51
51
|
},
|
@@ -45,7 +45,7 @@ const Container: React.FC<ContainerProps> = props => {
|
|
45
45
|
<NoSSR>
|
46
46
|
<div className="rspress-preview">
|
47
47
|
{isMobile === 'true' ? (
|
48
|
-
<div className="rspress-preview-wrapper
|
48
|
+
<div className="rspress-preview-wrapper">
|
49
49
|
<div className="rspress-preview-code">{children?.[0]}</div>
|
50
50
|
<div className="rspress-preview-device">
|
51
51
|
<iframe src={getPageUrl()} key={iframeKey}></iframe>
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import { NoSSR, useLang, usePageData, withBase } from '@rspress/core/runtime';
|
2
|
+
import { type MouseEvent, useCallback, useState } from 'react';
|
3
|
+
import IconCode from './icons/Code';
|
4
|
+
import { publishIframeUrl } from './useIframeUrlPerComp';
|
5
|
+
|
6
|
+
type ContainerProps = {
|
7
|
+
children: React.ReactNode[];
|
8
|
+
isMobile: 'true' | 'false';
|
9
|
+
demoId: string;
|
10
|
+
};
|
11
|
+
|
12
|
+
const MobileContainerFixedPerComp: React.FC<ContainerProps> = props => {
|
13
|
+
const { children, demoId } = props;
|
14
|
+
const { page } = usePageData();
|
15
|
+
const url = `/~demo/${demoId}`;
|
16
|
+
|
17
|
+
const getPageUrl = () => {
|
18
|
+
if (page?.devPort) {
|
19
|
+
return `http://localhost:${page.devPort}/${demoId}`;
|
20
|
+
}
|
21
|
+
if (typeof window !== 'undefined') {
|
22
|
+
return `${window.location.origin}${withBase(url)}`;
|
23
|
+
}
|
24
|
+
// Do nothing in ssr
|
25
|
+
return '';
|
26
|
+
};
|
27
|
+
|
28
|
+
const setIframeUrl = () => {
|
29
|
+
const url = getPageUrl();
|
30
|
+
const fixedIframe = document.querySelector('.rspress-fixed-iframe');
|
31
|
+
fixedIframe?.setAttribute('src', url);
|
32
|
+
publishIframeUrl(url);
|
33
|
+
};
|
34
|
+
|
35
|
+
return (
|
36
|
+
<div className="rspress-preview-code" onClick={setIframeUrl}>
|
37
|
+
{children}
|
38
|
+
</div>
|
39
|
+
);
|
40
|
+
};
|
41
|
+
|
42
|
+
const ContainerFixedPerComp = (props: ContainerProps) => {
|
43
|
+
const { children, isMobile } = props;
|
44
|
+
const [showCode, setShowCode] = useState(false);
|
45
|
+
const lang = useLang();
|
46
|
+
|
47
|
+
const toggleCode = useCallback(
|
48
|
+
(ev: MouseEvent<HTMLButtonElement>) => {
|
49
|
+
if (!showCode) {
|
50
|
+
ev.currentTarget.blur();
|
51
|
+
}
|
52
|
+
setShowCode(!showCode);
|
53
|
+
},
|
54
|
+
[showCode],
|
55
|
+
);
|
56
|
+
|
57
|
+
return (
|
58
|
+
<>
|
59
|
+
{isMobile === 'true' ? (
|
60
|
+
<MobileContainerFixedPerComp {...props} />
|
61
|
+
) : (
|
62
|
+
<NoSSR>
|
63
|
+
<div className="rspress-preview">
|
64
|
+
<div>
|
65
|
+
<div className="rspress-preview-card">
|
66
|
+
<div
|
67
|
+
style={{
|
68
|
+
overflow: 'auto',
|
69
|
+
flex: 'auto',
|
70
|
+
}}
|
71
|
+
>
|
72
|
+
{children?.[1]}
|
73
|
+
</div>
|
74
|
+
<div className="rspress-preview-operations web">
|
75
|
+
<button
|
76
|
+
onClick={toggleCode}
|
77
|
+
aria-label={lang === 'zh' ? '收起代码' : 'Collapse Code'}
|
78
|
+
className={showCode ? 'button-expanded' : ''}
|
79
|
+
>
|
80
|
+
<IconCode />
|
81
|
+
</button>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
<div
|
85
|
+
className={`${
|
86
|
+
showCode
|
87
|
+
? 'rspress-preview-code-show'
|
88
|
+
: 'rspress-preview-code-hide'
|
89
|
+
}`}
|
90
|
+
>
|
91
|
+
{children?.[0]}
|
92
|
+
</div>
|
93
|
+
</div>
|
94
|
+
</div>
|
95
|
+
</NoSSR>
|
96
|
+
)}
|
97
|
+
</>
|
98
|
+
);
|
99
|
+
};
|
100
|
+
|
101
|
+
export default ContainerFixedPerComp;
|
@@ -1,11 +1,11 @@
|
|
1
|
-
.fixed-device {
|
1
|
+
.rspress-fixed-device {
|
2
2
|
display: none;
|
3
3
|
position: fixed;
|
4
4
|
top: calc(var(--rp-nav-height) + var(--rp-preview-padding));
|
5
5
|
overflow: hidden;
|
6
6
|
}
|
7
7
|
|
8
|
-
.fixed-iframe {
|
8
|
+
.rspress-fixed-iframe {
|
9
9
|
height: var(--rp-device-height);
|
10
10
|
max-height: calc(
|
11
11
|
100vh - var(--rp-preview-padding) * 2 - var(--rp-nav-height)
|
@@ -17,7 +17,7 @@
|
|
17
17
|
border: var(--rp-device-border);
|
18
18
|
}
|
19
19
|
|
20
|
-
.fixed-operation {
|
20
|
+
.rspress-fixed-operation {
|
21
21
|
border: var(--rp-device-border);
|
22
22
|
border-top: 0;
|
23
23
|
border-radius: 0 0 var(--rp-device-border-radius)
|
@@ -32,7 +32,7 @@
|
|
32
32
|
}
|
33
33
|
|
34
34
|
@media (min-width: 960px) {
|
35
|
-
.fixed-device {
|
35
|
+
.rspress-fixed-device {
|
36
36
|
display: inline;
|
37
37
|
left: calc(1280px - var(--rp-device-width) - var(--rp-preview-padding));
|
38
38
|
right: auto;
|
@@ -40,7 +40,7 @@
|
|
40
40
|
}
|
41
41
|
|
42
42
|
@media (min-width: 1280px) {
|
43
|
-
.fixed-device {
|
43
|
+
.rspress-fixed-device {
|
44
44
|
display: inline;
|
45
45
|
right: var(--rp-preview-padding);
|
46
46
|
left: auto;
|
@@ -72,18 +72,18 @@ export default () => {
|
|
72
72
|
}, [haveDemos, asideWidth, innerWidth]);
|
73
73
|
|
74
74
|
return haveDemos ? (
|
75
|
-
<div className="fixed-device">
|
75
|
+
<div className="rspress-fixed-device">
|
76
76
|
<NoSSR>
|
77
77
|
<iframe
|
78
78
|
// refresh when load the iframe, then remove NoSSR
|
79
79
|
src={getPageUrl(url)}
|
80
|
-
className="fixed-iframe"
|
80
|
+
className="rspress-fixed-iframe"
|
81
81
|
key={iframeKey}
|
82
82
|
></iframe>
|
83
83
|
</NoSSR>
|
84
84
|
<MobileOperation
|
85
85
|
url={getPageUrl(url)}
|
86
|
-
className="fixed-operation"
|
86
|
+
className="rspress-fixed-operation"
|
87
87
|
refresh={refresh}
|
88
88
|
/>
|
89
89
|
</div>
|
@@ -0,0 +1,106 @@
|
|
1
|
+
import {
|
2
|
+
NoSSR,
|
3
|
+
usePageData,
|
4
|
+
useWindowSize,
|
5
|
+
withBase,
|
6
|
+
} from '@rspress/core/runtime';
|
7
|
+
import { useCallback, useEffect, useState } from 'react';
|
8
|
+
// @ts-ignore
|
9
|
+
import { normalizeId } from '../../dist/utils';
|
10
|
+
import MobileOperation from './common/mobile-operation';
|
11
|
+
import { publishIframeUrl, useIframeUrlPerComp } from './useIframeUrlPerComp';
|
12
|
+
import './Device.scss';
|
13
|
+
|
14
|
+
export default () => {
|
15
|
+
const { page } = usePageData();
|
16
|
+
const pageName = `${normalizeId(page.pagePath)}`;
|
17
|
+
const demoId = `_${pageName}`;
|
18
|
+
const url = `~demo/${demoId}`;
|
19
|
+
const { haveDemos } = page;
|
20
|
+
|
21
|
+
const getPageUrl = (url: string) => {
|
22
|
+
if (page?.devPort) {
|
23
|
+
return `http://localhost:${page.devPort}/${demoId}`;
|
24
|
+
}
|
25
|
+
if (typeof window !== 'undefined') {
|
26
|
+
return `${window.location.origin}${withBase(url)}`;
|
27
|
+
}
|
28
|
+
// Do nothing in ssr
|
29
|
+
return '';
|
30
|
+
};
|
31
|
+
|
32
|
+
const initialUrl = getPageUrl(url);
|
33
|
+
|
34
|
+
const iframeUrl = useIframeUrlPerComp() ?? initialUrl;
|
35
|
+
|
36
|
+
const resetIframeUrl = useCallback(() => {
|
37
|
+
publishIframeUrl(initialUrl);
|
38
|
+
}, [initialUrl, url]);
|
39
|
+
|
40
|
+
useEffect(() => {
|
41
|
+
publishIframeUrl(initialUrl);
|
42
|
+
}, []);
|
43
|
+
|
44
|
+
const [asideWidth, setAsideWidth] = useState('0px');
|
45
|
+
const { width: innerWidth } = useWindowSize();
|
46
|
+
const [iframeKey, setIframeKey] = useState(0);
|
47
|
+
const refresh = useCallback(() => {
|
48
|
+
setIframeKey(Math.random());
|
49
|
+
}, []);
|
50
|
+
|
51
|
+
// get default value from root
|
52
|
+
// listen resize and re-render
|
53
|
+
useEffect(() => {
|
54
|
+
const root = document.querySelector(':root');
|
55
|
+
if (root) {
|
56
|
+
const defaultAsideWidth =
|
57
|
+
getComputedStyle(root).getPropertyValue('--rp-aside-width');
|
58
|
+
setAsideWidth(defaultAsideWidth);
|
59
|
+
}
|
60
|
+
}, []);
|
61
|
+
|
62
|
+
useEffect(() => {
|
63
|
+
const node = document.querySelector('.rspress-doc-container');
|
64
|
+
const { style } = document.documentElement;
|
65
|
+
if (haveDemos) {
|
66
|
+
if (innerWidth > 1280) {
|
67
|
+
node?.setAttribute(
|
68
|
+
'style',
|
69
|
+
'padding-right: calc(var(--rp-device-width) + var(--rp-preview-padding) * 2)',
|
70
|
+
);
|
71
|
+
} else if (innerWidth > 960) {
|
72
|
+
node?.setAttribute(
|
73
|
+
'style',
|
74
|
+
`padding-right: calc(${
|
75
|
+
innerWidth - 1280
|
76
|
+
}px + var(--rp-device-width) + var(--rp-preview-padding) * 2)`,
|
77
|
+
);
|
78
|
+
} else {
|
79
|
+
node?.removeAttribute('style');
|
80
|
+
}
|
81
|
+
style.setProperty('--rp-aside-width', '0px');
|
82
|
+
} else {
|
83
|
+
node?.removeAttribute('style');
|
84
|
+
style.setProperty('--rp-aside-width', asideWidth);
|
85
|
+
}
|
86
|
+
}, [haveDemos, asideWidth, innerWidth]);
|
87
|
+
|
88
|
+
return haveDemos ? (
|
89
|
+
<div className="rspress-fixed-device">
|
90
|
+
<NoSSR>
|
91
|
+
<iframe
|
92
|
+
// refresh when load the iframe, then remove NoSSR
|
93
|
+
src={iframeUrl}
|
94
|
+
className="rspress-fixed-iframe"
|
95
|
+
key={iframeKey}
|
96
|
+
></iframe>
|
97
|
+
</NoSSR>
|
98
|
+
<MobileOperation
|
99
|
+
url={iframeUrl}
|
100
|
+
className="rspress-fixed-operation"
|
101
|
+
refresh={refresh}
|
102
|
+
goBack={iframeUrl !== initialUrl ? resetIframeUrl : undefined}
|
103
|
+
/>
|
104
|
+
</div>
|
105
|
+
) : null;
|
106
|
+
};
|
@@ -7,6 +7,7 @@ import {
|
|
7
7
|
useRef,
|
8
8
|
useState,
|
9
9
|
} from 'react';
|
10
|
+
import IconBack from '../icons/Back';
|
10
11
|
import IconLaunch from '../icons/Launch';
|
11
12
|
import IconQrcode from '../icons/Qrcode';
|
12
13
|
import IconRefresh from '../icons/Refresh';
|
@@ -15,21 +16,24 @@ import './index.scss';
|
|
15
16
|
const locales = {
|
16
17
|
zh: {
|
17
18
|
refresh: '刷新页面',
|
19
|
+
goBack: '返回',
|
18
20
|
open: '在新页面打开',
|
19
21
|
},
|
20
22
|
en: {
|
21
23
|
refresh: 'Refresh',
|
24
|
+
goBack: 'Go back',
|
22
25
|
open: 'Open in new page',
|
23
26
|
},
|
24
27
|
};
|
25
28
|
|
26
|
-
|
29
|
+
const MobileOperation = (props: {
|
27
30
|
url: string;
|
28
31
|
className?: string;
|
29
|
-
refresh
|
32
|
+
refresh?: () => void;
|
33
|
+
goBack?: () => void;
|
30
34
|
}) => {
|
35
|
+
const { url, className = '', refresh, goBack } = props;
|
31
36
|
const [showQRCode, setShowQRCode] = useState(false);
|
32
|
-
const { url, className = '', refresh } = props;
|
33
37
|
const lang = useLang();
|
34
38
|
const triggerRef = useRef(null);
|
35
39
|
const t = lang === 'zh' ? locales.zh : locales.en;
|
@@ -80,22 +84,46 @@ export default (props: {
|
|
80
84
|
|
81
85
|
return (
|
82
86
|
<div className={`rspress-preview-operations mobile ${className}`}>
|
83
|
-
|
84
|
-
<
|
85
|
-
|
87
|
+
{goBack && (
|
88
|
+
<button
|
89
|
+
onClick={goBack}
|
90
|
+
aria-label={t.goBack}
|
91
|
+
className="rspress-preview-operations-back"
|
92
|
+
>
|
93
|
+
<IconBack />
|
94
|
+
</button>
|
95
|
+
)}
|
96
|
+
{refresh && (
|
97
|
+
<button
|
98
|
+
onClick={refresh}
|
99
|
+
aria-label={t.refresh}
|
100
|
+
className="rspress-preview-operations-refresh"
|
101
|
+
>
|
102
|
+
<IconRefresh />
|
103
|
+
</button>
|
104
|
+
)}
|
86
105
|
<div className="relative" ref={triggerRef}>
|
87
106
|
{showQRCode && (
|
88
107
|
<div className="rspress-preview-qrcode">
|
89
108
|
<QRCodeSVG value={url} size={96} />
|
90
109
|
</div>
|
91
110
|
)}
|
92
|
-
<button
|
111
|
+
<button
|
112
|
+
onClick={toggleQRCode}
|
113
|
+
className="rspress-preview-operations-qrcode"
|
114
|
+
>
|
93
115
|
<IconQrcode />
|
94
116
|
</button>
|
95
117
|
</div>
|
96
|
-
<button
|
118
|
+
<button
|
119
|
+
onClick={openNewPage}
|
120
|
+
aria-label={t.open}
|
121
|
+
className="rspress-preview-operations-open"
|
122
|
+
>
|
97
123
|
<IconLaunch />
|
98
124
|
</button>
|
99
125
|
</div>
|
100
126
|
);
|
101
127
|
};
|
128
|
+
|
129
|
+
export default MobileOperation;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
const Back = ({ color = 'currentColor', ...props }) => {
|
2
|
+
return (
|
3
|
+
<svg
|
4
|
+
width="1em"
|
5
|
+
height="1em"
|
6
|
+
viewBox="0 0 48 48"
|
7
|
+
fill="none"
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
9
|
+
stroke={color}
|
10
|
+
strokeWidth="4"
|
11
|
+
{...props}
|
12
|
+
>
|
13
|
+
<path
|
14
|
+
d="M12.9998 8L6 14L12.9998 21"
|
15
|
+
stroke="#333"
|
16
|
+
stroke-width="4"
|
17
|
+
stroke-linecap="round"
|
18
|
+
stroke-linejoin="round"
|
19
|
+
/>
|
20
|
+
<path
|
21
|
+
d="M6 14H28.9938C35.8768 14 41.7221 19.6204 41.9904 26.5C42.2739 33.7696 36.2671 40 28.9938 40H11.9984"
|
22
|
+
stroke="#333"
|
23
|
+
stroke-width="4"
|
24
|
+
stroke-linecap="round"
|
25
|
+
stroke-linejoin="round"
|
26
|
+
/>
|
27
|
+
</svg>
|
28
|
+
);
|
29
|
+
};
|
30
|
+
|
31
|
+
export default Back;
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
2
|
+
|
3
|
+
const useForceRefresh = () => {
|
4
|
+
const [_, setKey] = useState(0);
|
5
|
+
|
6
|
+
const forceRefresh = useCallback(() => {
|
7
|
+
return setKey(prevKey => prevKey + 1);
|
8
|
+
}, [setKey]);
|
9
|
+
|
10
|
+
return forceRefresh;
|
11
|
+
};
|
12
|
+
|
13
|
+
let iframeUrl: string | undefined;
|
14
|
+
|
15
|
+
const observer = new Map<string, () => void>();
|
16
|
+
|
17
|
+
const publishIframeUrl = (url: string) => {
|
18
|
+
iframeUrl = url;
|
19
|
+
for (const [, refresh] of observer) {
|
20
|
+
refresh();
|
21
|
+
}
|
22
|
+
};
|
23
|
+
|
24
|
+
const useIframeUrlPerComp = () => {
|
25
|
+
const forceRefresh = useForceRefresh();
|
26
|
+
|
27
|
+
useEffect(() => {
|
28
|
+
const id = Math.random().toString(36).slice(5);
|
29
|
+
observer.set(id, forceRefresh);
|
30
|
+
|
31
|
+
return () => {
|
32
|
+
observer.delete(id);
|
33
|
+
};
|
34
|
+
}, []);
|
35
|
+
|
36
|
+
return iframeUrl;
|
37
|
+
};
|
38
|
+
|
39
|
+
export { useIframeUrlPerComp, publishIframeUrl };
|