mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 09:26:06 +08:00
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:
parent
f726e61d95
commit
32052bf5d8
@ -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}
|
||||
|
@ -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;
|
||||
|
@ -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 });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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} />
|
||||
|
@ -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;
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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'],
|
||||
|
@ -1,7 +1,3 @@
|
||||
/**
|
||||
* live: false
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
@ -1,7 +1,3 @@
|
||||
/**
|
||||
* live: false
|
||||
*/
|
||||
|
||||
import { EllipsisOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
import React, { useState } from 'react';
|
||||
|
@ -1,7 +1,3 @@
|
||||
/**
|
||||
* live: false
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
HomeOutlined,
|
||||
|
11
package.json
11
package.json
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user