docs: update live demo logic with dumi 2.3 beta (#46805)

* docs: update live demo api with dumi 2.3 beta

* docs: lint fix

* refactor: update naming of demo source

* feat: optimize souce code editor style

* chore: update dumi version

* chore: restore dumi overrides

* ci: setup before lint only for ci

* chore: trigger ci re-run

* chore: bump dumi version

---------

Signed-off-by: lijianan <574980606@qq.com>
Co-authored-by: MadCcc <1075746765@qq.com>
Co-authored-by: lijianan <574980606@qq.com>
This commit is contained in:
Peach 2024-01-09 22:51:55 +08:00 committed by GitHub
parent f726e61d95
commit 32052bf5d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 121 additions and 104 deletions

View File

@ -5,10 +5,8 @@ import stackblitzSdk from '@stackblitz/sdk';
import { Alert, Badge, Flex, Tooltip } from 'antd';
import { createStyles, css } from 'antd-style';
import classNames from 'classnames';
import { FormattedMessage, LiveContext, useSiteData } from 'dumi';
import LiveDemo from 'dumi/theme-default/slots/LiveDemo';
import { FormattedMessage, useSiteData, useLiveDemo } from 'dumi';
import LZString from 'lz-string';
import useLocation from '../../../hooks/useLocation';
import BrowserFrame from '../../common/BrowserFrame';
import ClientOnly from '../../common/ClientOnly';
@ -108,14 +106,18 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
const { pkg } = useSiteData();
const location = useLocation();
const { enabled: liveEnabled } = useContext(LiveContext);
const { styles } = useStyle();
const entryCode = asset.dependencies['index.tsx'].value;
const entryName = 'index.tsx';
const entryCode = asset.dependencies[entryName].value;
const showRiddleButton = useShowRiddleButton();
const liveDemo = useRef<React.ReactNode>(null);
const previewDemo = useRef<React.ReactNode>(null);
const {
node: liveDemoNode,
error: liveDemoError,
setSource: setLiveDemoSource,
} = useLiveDemo(asset.id);
const anchorRef = useRef<HTMLAnchorElement>(null);
const codeSandboxIconRef = useRef<HTMLFormElement>(null);
const riddleIconRef = useRef<HTMLFormElement>(null);
@ -153,8 +155,8 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
const mergedChildren = !iframe && clientOnly ? <ClientOnly>{children}</ClientOnly> : children;
if (!liveDemo.current) {
liveDemo.current = iframe ? (
if (!previewDemo.current) {
previewDemo.current = iframe ? (
<BrowserFrame>
<iframe
src={demoUrl}
@ -366,12 +368,10 @@ createRoot(document.getElementById('container')).render(<Demo />);
const codeBox: React.ReactNode = (
<section className={codeBoxClass} id={asset.id}>
<section className="code-box-demo" style={codeBoxDemoStyle}>
{!liveEnabled ? (
{liveDemoNode || (
<ErrorBoundary>
<React.StrictMode>{liveDemo.current}</React.StrictMode>
<React.StrictMode>{previewDemo.current}</React.StrictMode>
</ErrorBoundary>
) : (
<LiveDemo />
)}
</section>
<section className="code-box-meta markdown">
@ -513,7 +513,10 @@ createRoot(document.getElementById('container')).render(<Demo />);
sourceCode={entryCode}
jsxCode={jsx}
styleCode={style}
liveError={liveDemoError}
entryName={entryName}
onCodeTypeChange={setCodeType}
onSourceTranspile={setLiveDemoSource}
/>
<div
tabIndex={0}

View File

@ -1,6 +1,6 @@
import React from 'react';
import type { IPreviewerProps } from 'dumi';
import { LiveProvider, useTabMeta } from 'dumi';
import { useTabMeta } from 'dumi';
import CodePreviewer from './CodePreviewer';
import DesignPreviewer from './DesignPreviewer';
@ -16,23 +16,7 @@ const Previewer: React.FC<AntdPreviewerProps> = (props) => {
return <DesignPreviewer {...props} />;
}
const codePreviewer = <CodePreviewer {...props} />;
if (props.live === false || props.iframe) {
return codePreviewer;
}
return (
<LiveProvider
initialCode={
Object.entries(props.asset.dependencies).filter(([, { type }]) => type === 'FILE')[0][1]
.value
}
demoId={props.asset.id}
>
{codePreviewer}
</LiveProvider>
);
return <CodePreviewer {...props} />;
};
export default Previewer;

View File

@ -1,10 +1,10 @@
import React, { type ComponentProps, useEffect, useMemo } from 'react';
import { Button, Tabs, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { LiveContext } from 'dumi';
import toReactElement from 'jsonml-to-react-element';
import JsonML from 'jsonml.js/lib/utils';
import Prism from 'prismjs';
import React, { useContext, useEffect, useMemo } from 'react';
import LiveCode from './LiveCode';
const useStyle = createStyles(({ token, css }) => {
@ -19,6 +19,7 @@ const useStyle = createStyles(({ token, css }) => {
copyButton: css`
color: ${colorIcon};
position: absolute;
z-index: 2;
top: 16px;
inset-inline-end: 16px;
width: 32px;
@ -29,7 +30,19 @@ const useStyle = createStyles(({ token, css }) => {
copyIcon: css`
${antCls}-typography-copy {
position: relative;
margin-inline-start: 0;
// expand clickable area
&::before {
content: '';
display: block;
position: absolute;
top: -5px;
left: -9px;
bottom: -5px;
right: -9px;
}
}
${antCls}-typography-copy:not(${antCls}-typography-copy-success) {
color: ${colorIcon};
@ -48,11 +61,13 @@ const LANGS = {
style: 'CSS',
};
interface CodePreviewProps {
interface CodePreviewProps extends Omit<ComponentProps<typeof LiveCode>, 'initialValue' | 'lang'> {
sourceCode?: string;
jsxCode?: string;
styleCode?: string;
entryName: string;
onCodeTypeChange?: (activeKey: string) => void;
onSourceTranspile?: (source: Record<string, string>) => void;
}
function toReactComponent(jsonML: any[]) {
@ -75,7 +90,9 @@ const CodePreview: React.FC<CodePreviewProps> = ({
sourceCode = '',
jsxCode = '',
styleCode = '',
entryName,
onCodeTypeChange,
onSourceTranspile,
}) => {
// 避免 Tabs 数量不稳定的闪动问题
const initialCodes = {} as Record<'tsx' | 'jsx' | 'style', string>;
@ -90,9 +107,10 @@ const CodePreview: React.FC<CodePreviewProps> = ({
}
const [highlightedCodes, setHighlightedCodes] = React.useState(initialCodes);
const sourceCodes = {
tsx: sourceCode,
jsx: jsxCode,
style: styleCode,
// omit trailing line break
tsx: sourceCode?.trim(),
jsx: jsxCode?.trim(),
style: styleCode?.trim(),
} as Record<'tsx' | 'jsx' | 'style', string>;
useEffect(() => {
const codes = {
@ -109,12 +127,10 @@ const CodePreview: React.FC<CodePreviewProps> = ({
setHighlightedCodes(codes);
}, [jsxCode, sourceCode, styleCode]);
const langList = Object.keys(highlightedCodes);
const langList = Object.keys(highlightedCodes) as ('tsx' | 'jsx' | 'style')[];
const { styles } = useStyle();
const { enabled: liveEnabled } = useContext(LiveContext);
const items = useMemo(
() =>
langList.map((lang: keyof typeof LANGS) => ({
@ -122,8 +138,14 @@ const CodePreview: React.FC<CodePreviewProps> = ({
key: lang,
children: (
<div className={styles.code}>
{lang === 'tsx' && liveEnabled ? (
<LiveCode />
{lang === 'tsx' ? (
<LiveCode
lang={lang}
initialValue={sourceCodes[lang]}
onTranspile={(code) => {
onSourceTranspile?.({ [entryName]: code });
}}
/>
) : (
toReactComponent(['pre', { lang, highlighted: highlightedCodes[lang] }])
)}
@ -141,17 +163,14 @@ const CodePreview: React.FC<CodePreviewProps> = ({
}
if (langList.length === 1) {
return liveEnabled ? (
<LiveCode />
) : (
toReactComponent([
'pre',
{
lang: langList[0],
highlighted: highlightedCodes[langList[0] as keyof typeof LANGS],
className: 'highlight',
},
])
return (
<LiveCode
lang={langList[0]}
initialValue={sourceCodes[langList[0]]}
onTranspile={(code) => {
onSourceTranspile?.({ [entryName]: code });
}}
/>
);
}

View File

@ -1,11 +1,12 @@
import type { FC } from 'react';
import type { ComponentProps, FC } from 'react';
import React, { useEffect, useState } from 'react';
import { createStyles } from 'antd-style';
import LiveEditor from '../slots/LiveEditor';
import LiveError from '../slots/LiveError';
import { EditFilled } from '@ant-design/icons';
import { Tooltip } from 'antd';
import { createStyles } from 'antd-style';
import SourceCodeEditor from 'dumi/theme-default/slots/SourceCodeEditor';
import useLocale from '../../hooks/useLocale';
import LiveError from '../slots/LiveError';
const useStyle = createStyles(({ token, css }) => {
const { colorPrimaryBorder, colorIcon, colorPrimary } = token;
@ -23,10 +24,38 @@ const useStyle = createStyles(({ token, css }) => {
box-shadow: inset 0 0 0 1px ${colorPrimary} !important;
}
}
// override dumi editor styles
.dumi-default-source-code-editor {
.dumi-default-source-code > pre,
.dumi-default-source-code-editor-textarea {
padding: 12px 16px;
}
.dumi-default-source-code > pre {
font-size: 13px;
line-height: 2;
font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
}
// disable dumi default copy button
.dumi-default-source-code-copy {
display: none;
}
&-textarea:hover {
box-shadow: 0 0 0 1px ${token.colorPrimaryBorderHover} inset;
}
&-textarea:focus {
box-shadow: 0 0 0 1px ${token.colorPrimary} inset;
}
}
`,
editableIcon: css`
position: absolute;
z-index: 2;
height: 32px;
width: 32px;
display: flex;
@ -50,8 +79,14 @@ const locales = {
const HIDE_LIVE_DEMO_TIP = 'hide-live-demo-tip';
const LiveCode: FC = () => {
const LiveCode: FC<{
lang: ComponentProps<typeof SourceCodeEditor>['lang'];
initialValue: ComponentProps<typeof SourceCodeEditor>['initialValue'];
liveError?: Error;
onTranspile?: (code: string) => void;
}> = (props) => {
const [open, setOpen] = useState(false);
const [error, setError] = useState<Error>();
const { styles } = useStyle();
const [locale] = useLocale(locales);
@ -72,8 +107,19 @@ const LiveCode: FC = () => {
return (
<>
<div className={styles.editor}>
<LiveEditor />
<LiveError />
<SourceCodeEditor
lang={props.lang}
initialValue={props.initialValue}
onTranspile={({ err, code }) => {
if (err) {
setError(err);
} else {
setError(undefined);
props.onTranspile?.(code);
}
}}
/>
<LiveError error={props.liveError || error} />
</div>
<Tooltip title={locale.demoEditable} open={open} onOpenChange={handleOpenChange}>
<EditFilled className={styles.editableIcon} />

View File

@ -1,21 +0,0 @@
import type { FC } from 'react';
import React from 'react';
import DumiLiveEditor from 'dumi/theme-default/slots/LiveEditor';
const LiveEditor: FC = () => (
<DumiLiveEditor
style={{
fontSize: 13,
lineHeight: 2,
fontFamily: `'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace`,
}}
padding={{
top: 12,
right: 16,
bottom: 12,
left: 16,
}}
/>
);
export default LiveEditor;

View File

@ -1,14 +1,12 @@
import type { FC } from 'react';
import React, { useContext } from 'react';
import { LiveContext } from 'dumi';
import React from 'react';
import { Alert, theme } from 'antd';
const LiveError: FC = () => {
const { error } = useContext(LiveContext);
const LiveError: FC<{ error?: Error }> = ({ error }) => {
const { token } = theme.useToken();
return error ? (
<Alert banner type="error" message={error} style={{ color: token.colorError }} />
<Alert banner type="error" message={error.toString()} style={{ color: token.colorError }} />
) : null;
};

View File

@ -15,7 +15,6 @@ export default defineConfig({
ssr: process.env.NODE_ENV === 'production' ? {} : false,
hash: true,
mfsu: false,
live: true,
crossorigin: {},
outputPath: '_site',
favicons: ['https://gw.alipayobjects.com/zos/rmsportal/rlpTLlbMzTNYuZGGCVYM.png'],

View File

@ -1,7 +1,3 @@
/**
* live: false
*/
import React from 'react';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';

View File

@ -1,7 +1,3 @@
/**
* live: false
*/
import { EllipsisOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import React, { useState } from 'react';

View File

@ -1,7 +1,3 @@
/**
* live: false
*/
import React from 'react';
import {
HomeOutlined,

View File

@ -67,15 +67,16 @@
"install-react-16": "npm i --no-save --legacy-peer-deps react@16 react-dom@16 @testing-library/react@12",
"install-react-17": "npm i --no-save --legacy-peer-deps react@17 react-dom@17 @testing-library/react@12",
"install-react-18": "npm i --no-save --legacy-peer-deps react@18 react-dom@18",
"prelint": "is-ci && dumi setup",
"lint": "npm run version && npm run tsc && npm run lint:script && npm run lint:demo && npm run lint:md && npm run lint:style",
"lint-fix": "npm run lint-fix:script && npm run lint-fix:demo",
"lint-fix:demo": "npm run lint:demo -- --fix",
"lint-fix:script": "npm run lint:script -- --fix",
"lint:demo": "eslint components/*/demo/*.md",
"lint:deps": "antd-tools run deps-lint",
"lint:md": "remark . -f -q",
"lint:script": "npm run component-changelog && eslint . --ext .js,.jsx,.ts,.tsx --cache",
"lint:style": "tsx scripts/check-cssinjs.tsx",
"lint-fix": "npm run lint-fix:script && npm run lint-fix:demo",
"lint-fix:demo": "npm run lint:demo -- --fix",
"lint-fix:script": "npm run lint:script -- --fix",
"pre-publish": "npm run test-all -- --skip-build && tsx ./scripts/pre-publish-notice.ts",
"prepare": "is-ci || husky install && dumi setup",
"prepublishOnly": "antd-tools run guard",
@ -228,7 +229,7 @@
"cross-fetch": "^4.0.0",
"crypto": "^1.0.1",
"dekko": "^0.2.1",
"dumi": "2.3.0-alpha.9",
"dumi": "^2.3.0-beta.3",
"dumi-plugin-color-chunk": "^1.1.0",
"esbuild-loader": "^4.0.2",
"eslint": "^8.56.0",
@ -341,7 +342,7 @@
},
"overrides": {
"dumi-plugin-color-chunk": {
"dumi": "^2.3.0-alpha.4"
"dumi": "^2.3.0-beta.3"
}
},
"packageManager": "npm@10.2.5",