chore: auto merge branches (#40808)

chore: feature merge master
This commit is contained in:
github-actions[bot] 2023-02-20 03:18:59 +00:00 committed by GitHub
commit da441ae35f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 1777 additions and 847 deletions

View File

@ -7,7 +7,7 @@ version: 2.1
jobs:
test-argos-ci:
docker:
- image: cimg/node:16.19.0-browsers
- image: cimg/node:16.19.1-browsers
steps:
- checkout
- run:

View File

@ -0,0 +1,556 @@
import {
CheckOutlined,
LinkOutlined,
SnippetsOutlined,
ThunderboltOutlined,
} from '@ant-design/icons';
import type { Project } from '@stackblitz/sdk';
import stackblitzSdk from '@stackblitz/sdk';
import { Alert, Badge, Space, Tooltip } from 'antd';
import classNames from 'classnames';
import LZString from 'lz-string';
import React, { useContext, useEffect, useRef, useState } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import type { IPreviewerProps } from 'dumi';
import { FormattedMessage } from 'dumi';
import Prism from 'prismjs';
import JsonML from 'jsonml.js/lib/utils';
import toReactElement from 'jsonml-to-react-element';
import { ping } from '../../utils';
import ClientOnly from '../../common/ClientOnly';
import BrowserFrame from '../../common/BrowserFrame';
import EditButton from '../../common/EditButton';
import CodePenIcon from '../../common/CodePenIcon';
import CodePreview from '../../common/CodePreview';
import CodeSandboxIcon from '../../common/CodeSandboxIcon';
import RiddleIcon from '../../common/RiddleIcon';
import ExternalLinkIcon from '../../common/ExternalLinkIcon';
import type { SiteContextProps } from '../../slots/SiteContext';
import SiteContext from '../../slots/SiteContext';
import useLocation from '../../../hooks/useLocation';
const { ErrorBoundary } = Alert;
function toReactComponent(jsonML: any) {
return toReactElement(jsonML, [
[
(node: any) => JsonML.isElement(node) && JsonML.getTagName(node) === 'pre',
(node: any, index: any) => {
// ref: https://github.com/benjycui/bisheng/blob/master/packages/bisheng/src/bisheng-plugin-highlight/lib/browser.js#L7
const attr = JsonML.getAttributes(node);
return React.createElement(
'pre',
{
key: index,
className: `language-${attr.lang}`,
},
React.createElement('code', {
dangerouslySetInnerHTML: { __html: attr.highlighted },
}),
);
},
],
]);
}
function compress(string: string): string {
return LZString.compressToBase64(string)
.replace(/\+/g, '-') // Convert '+' to '-'
.replace(/\//g, '_') // Convert '/' to '_'
.replace(/=+$/, ''); // Remove ending '='
}
const track = ({ type, demo }: { type: string; demo: string }) => {
if (!window.gtag) {
return;
}
window.gtag('event', 'demo', { event_category: type, event_label: demo });
};
let pingDeferrer: PromiseLike<boolean>;
function useShowRiddleButton() {
const [showRiddleButton, setShowRiddleButton] = useState(false);
useEffect(() => {
pingDeferrer ??= new Promise<boolean>((resolve) => {
ping((status) => {
if (status !== 'timeout' && status !== 'error') {
return resolve(true);
}
return resolve(false);
});
});
pingDeferrer.then(setShowRiddleButton);
}, []);
return showRiddleButton;
}
const CodePreviewer: React.FC<IPreviewerProps> = (props) => {
const {
asset,
expand,
iframe,
demoUrl,
children,
title,
description,
debug,
jsx,
style,
compact,
background,
filePath,
version,
} = props;
const location = useLocation();
const entryCode = asset.dependencies['index.tsx'].value;
const showRiddleButton = useShowRiddleButton();
const liveDemo = useRef<React.ReactNode>(null);
const anchorRef = useRef<HTMLAnchorElement>(null);
const codeSandboxIconRef = useRef<HTMLFormElement>(null);
const riddleIconRef = useRef<HTMLFormElement>(null);
const codepenIconRef = useRef<HTMLFormElement>(null);
const [codeExpand, setCodeExpand] = useState<boolean>(false);
const [copyTooltipOpen, setCopyTooltipOpen] = useState<boolean>(false);
const [copied, setCopied] = useState<boolean>(false);
const [codeType, setCodeType] = useState<string>('tsx');
const { theme } = useContext<SiteContextProps>(SiteContext);
const { hash, pathname, search } = location;
const docsOnlineUrl = `https://ant.design${pathname}${search}#${asset.id}`;
const [showOnlineUrl, setShowOnlineUrl] = useState<boolean>(false);
const highlightedCodes = {
jsx: Prism.highlight(jsx, Prism.languages.javascript, 'jsx'),
tsx: Prism.highlight(entryCode, Prism.languages.javascript, 'jsx'),
};
const highlightedStyle = style ? Prism.highlight(style, Prism.languages.css, 'css') : '';
useEffect(() => {
const regexp = /preview-(\d+)-ant-design/; // matching PR preview addresses
setShowOnlineUrl(
process.env.NODE_ENV === 'development' || regexp.test(window.location.hostname),
);
}, []);
const handleCodeExpand = (demo: string) => {
setCodeExpand((prev) => !prev);
track({ type: 'expand', demo });
};
const handleCodeCopied = (demo: string) => {
setCopied(true);
track({ type: 'copy', demo });
};
const onCopyTooltipOpenChange = (open: boolean) => {
setCopyTooltipOpen(open);
if (open) {
setCopied(false);
}
};
useEffect(() => {
if (asset.id === hash.slice(1)) {
anchorRef.current?.click();
}
}, []);
useEffect(() => {
setCodeExpand(expand);
}, [expand]);
if (!liveDemo.current) {
liveDemo.current = iframe ? (
<BrowserFrame>
<iframe
src={demoUrl}
height={iframe === true ? undefined : iframe}
title="demo"
className="iframe-demo"
/>
</BrowserFrame>
) : (
children
);
}
const codeBoxClass = classNames('code-box', {
expand: codeExpand,
'code-box-debug': debug,
});
const localizedTitle = title;
const introChildren = <div dangerouslySetInnerHTML={{ __html: description }} />;
const highlightClass = classNames('highlight-wrapper', {
'highlight-wrapper-expand': codeExpand,
});
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
</head>
<body>
<div id="container" style="padding: 24px" />
<script>const mountNode = document.getElementById('container');</script>
</body>
</html>
`;
const tsconfig = `
{
"compilerOptions": {
"jsx": "react-jsx",
"target": "esnext",
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
}
}
`;
const suffix = codeType === 'tsx' ? 'tsx' : 'js';
const dependencies: Record<PropertyKey, string> = jsx.split('\n').reduce(
(acc, line) => {
const matches = line.match(/import .+? from '(.+)';$/);
if (matches && matches[1] && !line.includes('antd')) {
const paths = matches[1].split('/');
if (paths.length) {
const dep = paths[0].startsWith('@') ? `${paths[0]}/${paths[1]}` : paths[0];
acc[dep] = 'latest';
}
}
return acc;
},
{ antd: version },
);
dependencies['@ant-design/icons'] = 'latest';
if (suffix === 'tsx') {
dependencies['@types/react'] = '^18.0.0';
dependencies['@types/react-dom'] = '^18.0.0';
}
dependencies.react = '^18.0.0';
dependencies['react-dom'] = '^18.0.0';
const codepenPrefillConfig = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
html,
js: `const { createRoot } = ReactDOM;\n${jsx
.replace(/import\s+(?:React,\s+)?{(\s+[^}]*\s+)}\s+from\s+'react'/, `const { $1 } = React;`)
.replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'antd';/, 'const { $1 } = antd;')
.replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'@ant-design\/icons';/, 'const { $1 } = icons;')
.replace("import moment from 'moment';", '')
.replace("import React from 'react';", '')
.replace(/import\s+{\s+(.*)\s+}\s+from\s+'react-router';/, 'const { $1 } = ReactRouter;')
.replace(
/import\s+{\s+(.*)\s+}\s+from\s+'react-router-dom';/,
'const { $1 } = ReactRouterDOM;',
)
.replace(/([A-Za-z]*)\s+as\s+([A-Za-z]*)/, '$1:$2')
.replace(
/export default/,
'const ComponentDemo =',
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
editors: '001',
css: '',
js_external: [
'react@18/umd/react.development.js',
'react-dom@18/umd/react-dom.development.js',
'dayjs@1/dayjs.min.js',
`antd@${version}/dist/antd-with-locales.js`,
`@ant-design/icons/dist/index.umd.js`,
'react-router-dom/dist/umd/react-router-dom.production.min.js',
'react-router/dist/umd/react-router.production.min.js',
]
.map((url) => `https://unpkg.com/${url}`)
.join(';'),
js_pre_processor: 'typescript',
};
const riddlePrefillConfig = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
js: `${
/import React(\D*)from 'react';/.test(jsx) ? '' : `import React from 'react';\n`
}import { createRoot } from 'react-dom/client';\n${jsx.replace(
/export default/,
'const ComponentDemo =',
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
css: '',
json: JSON.stringify({ name: 'antd-demo', dependencies }, null, 2),
};
// Reorder source code
let parsedSourceCode = suffix === 'tsx' ? entryCode : jsx;
let importReactContent = "import React from 'react';";
const importReactReg = /import React(\D*)from 'react';/;
const matchImportReact = parsedSourceCode.match(importReactReg);
if (matchImportReact) {
[importReactContent] = matchImportReact;
parsedSourceCode = parsedSourceCode.replace(importReactReg, '').trim();
}
const demoJsContent = `
${importReactContent}
import './index.css';
${parsedSourceCode}
`.trim();
const indexCssContent = (style || '')
.trim()
.replace(new RegExp(`#${asset.id}\\s*`, 'g'), '')
.replace('</style>', '')
.replace('<style>', '');
const indexJsContent = `
import React from 'react';
import { createRoot } from 'react-dom/client';
import Demo from './demo';
createRoot(document.getElementById('container')).render(<Demo />);
`;
const codesandboxPackage = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
main: 'index.js',
dependencies: {
...dependencies,
react: '^18.0.0',
'react-dom': '^18.0.0',
'react-scripts': '^4.0.0',
},
devDependencies: {
typescript: '^4.0.5',
},
scripts: {
start: 'react-scripts start',
build: 'react-scripts build',
test: 'react-scripts test --env=jsdom',
eject: 'react-scripts eject',
},
browserslist: ['>0.2%', 'not dead'],
};
const codesanboxPrefillConfig = {
files: {
'package.json': { content: codesandboxPackage },
'index.css': { content: indexCssContent },
[`index.${suffix}`]: { content: indexJsContent },
[`demo.${suffix}`]: { content: demoJsContent },
'index.html': {
content: html,
},
},
};
const stackblitzPrefillConfig: Project = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
template: 'create-react-app',
dependencies,
description: '',
files: {
'index.css': indexCssContent,
[`index.${suffix}`]: indexJsContent,
[`demo.${suffix}`]: demoJsContent,
'index.html': html,
},
};
if (suffix === 'tsx') {
stackblitzPrefillConfig.files['tsconfig.json'] = tsconfig;
}
const backgroundGrey = theme.includes('dark') ? '#303030' : '#f0f2f5';
const codeBoxDemoStyle: React.CSSProperties = {
padding: iframe || compact ? 0 : undefined,
overflow: iframe || compact ? 'hidden' : undefined,
backgroundColor: background === 'grey' ? backgroundGrey : undefined,
};
const codeBox: React.ReactNode = (
<section className={codeBoxClass} id={asset.id}>
<section className="code-box-demo" style={codeBoxDemoStyle}>
<ErrorBoundary>
<React.StrictMode>{liveDemo.current}</React.StrictMode>
</ErrorBoundary>
{style ? <style dangerouslySetInnerHTML={{ __html: style }} /> : null}
</section>
<section className="code-box-meta markdown">
<div className="code-box-title">
<Tooltip title={debug ? <FormattedMessage id="app.demo.debug" /> : ''}>
<a href={`#${asset.id}`} ref={anchorRef}>
{localizedTitle}
</a>
</Tooltip>
<EditButton title={<FormattedMessage id="app.content.edit-demo" />} filename={filePath} />
</div>
<div className="code-box-description">{introChildren}</div>
<Space wrap size="middle" className="code-box-actions">
{showOnlineUrl && (
<Tooltip title={<FormattedMessage id="app.demo.online" />}>
<a
className="code-box-code-action"
target="_blank"
rel="noreferrer"
href={docsOnlineUrl}
>
<LinkOutlined className="code-box-online" />
</a>
</Tooltip>
)}
{showRiddleButton ? (
<form
className="code-box-code-action"
action="//riddle.alibaba-inc.com/riddles/define"
method="POST"
target="_blank"
ref={riddleIconRef}
onClick={() => {
track({ type: 'riddle', demo: asset.id });
riddleIconRef.current?.submit();
}}
>
<input type="hidden" name="data" value={JSON.stringify(riddlePrefillConfig)} />
<Tooltip title={<FormattedMessage id="app.demo.riddle" />}>
<RiddleIcon className="code-box-riddle" />
</Tooltip>
</form>
) : null}
<form
className="code-box-code-action"
action="https://codesandbox.io/api/v1/sandboxes/define"
method="POST"
target="_blank"
ref={codeSandboxIconRef}
onClick={() => {
track({ type: 'codesandbox', demo: asset.id });
codeSandboxIconRef.current?.submit();
}}
>
<input
type="hidden"
name="parameters"
value={compress(JSON.stringify(codesanboxPrefillConfig))}
/>
<Tooltip title={<FormattedMessage id="app.demo.codesandbox" />}>
<CodeSandboxIcon className="code-box-codesandbox" />
</Tooltip>
</form>
<form
className="code-box-code-action"
action="https://codepen.io/pen/define"
method="POST"
target="_blank"
ref={codepenIconRef}
onClick={() => {
track({ type: 'codepen', demo: asset.id });
codepenIconRef.current?.submit();
}}
>
<ClientOnly>
<input type="hidden" name="data" value={JSON.stringify(codepenPrefillConfig)} />
</ClientOnly>
<Tooltip title={<FormattedMessage id="app.demo.codepen" />}>
<CodePenIcon className="code-box-codepen" />
</Tooltip>
</form>
<Tooltip title={<FormattedMessage id="app.demo.stackblitz" />}>
<span
className="code-box-code-action"
onClick={() => {
track({ type: 'stackblitz', demo: asset.id });
stackblitzSdk.openProject(stackblitzPrefillConfig, {
openFile: [`demo.${suffix}`],
});
}}
>
<ThunderboltOutlined className="code-box-stackblitz" />
</span>
</Tooltip>
<CopyToClipboard text={entryCode} onCopy={() => handleCodeCopied(asset.id)}>
<Tooltip
open={copyTooltipOpen as boolean}
onOpenChange={onCopyTooltipOpenChange}
title={<FormattedMessage id={`app.demo.${copied ? 'copied' : 'copy'}`} />}
>
{React.createElement(copied && copyTooltipOpen ? CheckOutlined : SnippetsOutlined, {
className: 'code-box-code-copy code-box-code-action',
})}
</Tooltip>
</CopyToClipboard>
<Tooltip title={<FormattedMessage id="app.demo.separate" />}>
<a className="code-box-code-action" target="_blank" rel="noreferrer" href={demoUrl}>
<ExternalLinkIcon className="code-box-separate" />
</a>
</Tooltip>
<Tooltip
title={<FormattedMessage id={`app.demo.code.${codeExpand ? 'hide' : 'show'}`} />}
>
<div className="code-expand-icon code-box-code-action">
<img
alt="expand code"
src={
theme?.includes('dark')
? 'https://gw.alipayobjects.com/zos/antfincdn/btT3qDZn1U/wSAkBuJFbdxsosKKpqyq.svg'
: 'https://gw.alipayobjects.com/zos/antfincdn/Z5c7kzvi30/expand.svg'
}
className={codeExpand ? 'code-expand-icon-hide' : 'code-expand-icon-show'}
onClick={() => handleCodeExpand(asset.id)}
/>
<img
alt="expand code"
src={
theme?.includes('dark')
? 'https://gw.alipayobjects.com/zos/antfincdn/CjZPwcKUG3/OpROPHYqWmrMDBFMZtKF.svg'
: 'https://gw.alipayobjects.com/zos/antfincdn/4zAaozCvUH/unexpand.svg'
}
className={codeExpand ? 'code-expand-icon-show' : 'code-expand-icon-hide'}
onClick={() => handleCodeExpand(asset.id)}
/>
</div>
</Tooltip>
</Space>
</section>
<section className={highlightClass} key="code">
<CodePreview
codes={highlightedCodes}
toReactComponent={toReactComponent}
onCodeTypeChange={(type) => setCodeType(type)}
/>
{highlightedStyle ? (
<div key="style" className="highlight">
<pre>
<code className="css" dangerouslySetInnerHTML={{ __html: highlightedStyle }} />
</pre>
</div>
) : null}
</section>
</section>
);
if (version) {
return (
<Badge.Ribbon text={version} color={version.includes('<') ? 'red' : null}>
{codeBox}
</Badge.Ribbon>
);
}
return codeBox;
};
export default CodePreviewer;

View File

@ -0,0 +1,102 @@
import type { FC } from 'react';
import React, { useRef } from 'react';
import type { IPreviewerProps } from 'dumi';
import { createStyles, css } from 'antd-style';
import { CheckOutlined, SketchOutlined } from '@ant-design/icons';
import { nodeToGroup } from 'html2sketch';
import copy from 'copy-to-clipboard';
import { App } from 'antd';
const useStyle = createStyles(({ token }) => ({
wrapper: css`
border: 1px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadius}px;
padding: 20px 24px 40px;
position: relative;
margin-bottom: ${token.marginLG}px;
`,
title: css`
font-size: ${token.fontSizeLG}px;
font-weight: ${token.fontWeightStrong};
color: ${token.colorTextHeading};
&:hover {
color: ${token.colorTextHeading};
}
`,
description: css`
margin-top: ${token.margin}px;
`,
demo: css`
margin-top: ${token.marginLG}px;
display: flex;
justify-content: center;
`,
copy: css`
position: absolute;
inset-inline-end: 20px;
inset-block-start: 20px;
cursor: pointer;
`,
copyTip: css`
color: ${token.colorTextTertiary};
`,
copiedTip: css`
.anticon {
color: ${token.colorSuccess};
}
`,
tip: css`
color: ${token.colorTextTertiary};
margin-top: 40px;
`,
}));
const DesignPreviewer: FC<IPreviewerProps> = ({ children, title, description, tip, asset }) => {
const { styles } = useStyle();
const demoRef = useRef<HTMLDivElement>(null);
const [copied, setCopied] = React.useState<boolean>(false);
const { message } = App.useApp();
const handleCopy = async () => {
try {
const group = await nodeToGroup(demoRef.current);
copy(JSON.stringify(group.toSketchJSON()));
setCopied(true);
setTimeout(() => {
setCopied(false);
}, 5000);
} catch (e) {
console.error(e);
message.error('复制失败');
}
};
return (
<div className={styles.wrapper} id={asset.id}>
<a className={styles.title} href={`#${asset.id}`}>
{title}
</a>
<div className={styles.description} dangerouslySetInnerHTML={{ __html: description }} />
<div className={styles.copy}>
{copied ? (
<div className={styles.copiedTip}>
<CheckOutlined />
<span style={{ marginLeft: 8 }}>使 Kitchen </span>
</div>
) : (
<div onClick={handleCopy} className={styles.copyTip}>
<SketchOutlined />
<span style={{ marginLeft: 8 }}> Sketch JSON</span>
</div>
)}
</div>
<div className={styles.demo} ref={demoRef}>
{children}
</div>
<div className={styles.tip}>{tip}</div>
</div>
);
};
export default DesignPreviewer;

View File

@ -1,95 +0,0 @@
import React, { useEffect, useState } from 'react';
import JsonML from 'jsonml.js/lib/utils';
import toReactComponent from 'jsonml-to-react-element';
import Prism from 'prismjs';
import 'prismjs/components/prism-typescript';
import { useLocation, useIntl, type IPreviewerProps } from 'dumi';
import { ping } from '../../utils';
let pingDeferrer: PromiseLike<boolean>;
function useShowRiddleButton() {
const [showRiddleButton, setShowRiddleButton] = useState(false);
useEffect(() => {
pingDeferrer ??= new Promise<boolean>((resolve) => {
ping((status) => {
if (status !== 'timeout' && status !== 'error') {
return resolve(true);
}
return resolve(false);
});
});
pingDeferrer.then(setShowRiddleButton);
}, []);
return showRiddleButton;
}
/**
* HOC for convert dumi previewer props to bisheng previewer props
*/
export default function fromDumiProps<P extends object>(
WrappedComponent: React.ComponentType<P>,
): React.FC<IPreviewerProps> {
const hoc = function DumiPropsAntdPreviewer(props: IPreviewerProps) {
const showRiddleButton = useShowRiddleButton();
const location = useLocation();
const { asset, children, demoUrl, expand, description = '', ...meta } = props;
const intl = useIntl();
const entryCode = asset.dependencies['index.tsx'].value;
const transformedProps = {
meta: {
id: asset.id,
title: '',
filename: meta.filePath,
...meta,
},
content: description,
preview: () => children,
utils: {
toReactComponent(jsonML: any) {
return toReactComponent(jsonML, [
[
(node: any) => JsonML.isElement(node) && JsonML.getTagName(node) === 'pre',
(node: any, index: any) => {
// ref: https://github.com/benjycui/bisheng/blob/master/packages/bisheng/src/bisheng-plugin-highlight/lib/browser.js#L7
const attr = JsonML.getAttributes(node);
return React.createElement(
'pre',
{
key: index,
className: `language-${attr.lang}`,
},
React.createElement('code', {
dangerouslySetInnerHTML: { __html: attr.highlighted },
}),
);
},
],
]);
},
},
intl: { locale: intl.locale },
showRiddleButton,
sourceCodes: {
jsx: meta.jsx,
tsx: entryCode,
},
highlightedCodes: {
jsx: Prism.highlight(meta.jsx, Prism.languages.javascript, 'jsx'),
tsx: Prism.highlight(entryCode, Prism.languages.typescript, 'tsx'),
},
style: meta.style,
location,
src: demoUrl,
expand,
highlightedStyle: meta.style ? Prism.highlight(meta.style, Prism.languages.css, 'css') : '',
} as P;
return <WrappedComponent {...transformedProps} />;
};
return hoc;
}

View File

@ -1,512 +1,18 @@
import {
CheckOutlined,
SnippetsOutlined,
ThunderboltOutlined,
LinkOutlined,
} from '@ant-design/icons';
import stackblitzSdk from '@stackblitz/sdk';
import type { Project } from '@stackblitz/sdk';
import { Alert, Badge, Tooltip, Space } from 'antd';
import classNames from 'classnames';
import LZString from 'lz-string';
import React, { useContext, useEffect, useRef, useState } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import ReactDOM from 'react-dom';
import { FormattedMessage } from 'dumi';
import ClientOnly from '../../common/ClientOnly';
import BrowserFrame from '../../common/BrowserFrame';
import EditButton from '../../common/EditButton';
import CodePenIcon from '../../common/CodePenIcon';
import CodePreview from '../../common/CodePreview';
import CodeSandboxIcon from '../../common/CodeSandboxIcon';
import RiddleIcon from '../../common/RiddleIcon';
import ExternalLinkIcon from '../../common/ExternalLinkIcon';
import fromDumiProps from './fromDumiProps';
import type { SiteContextProps } from '../../slots/SiteContext';
import SiteContext from '../../slots/SiteContext';
import { version } from '../../../../package.json';
import type { FC } from 'react';
import React from 'react';
import type { IPreviewerProps } from 'dumi';
import { useTabMeta } from 'dumi';
import CodePreviewer from './CodePreviewer';
import DesignPreviewer from './DesignPreviewer';
const { ErrorBoundary } = Alert;
const Previewer: FC<IPreviewerProps> = ({ ...props }) => {
const tab = useTabMeta();
function compress(string: string): string {
return LZString.compressToBase64(string)
.replace(/\+/g, '-') // Convert '+' to '-'
.replace(/\//g, '_') // Convert '/' to '_'
.replace(/=+$/, ''); // Remove ending '='
}
const track = ({ type, demo }: { type: string; demo: string }) => {
if (!window.gtag) {
return;
if (tab?.frontmatter.title === 'Design') {
return <DesignPreviewer {...props} />;
}
window.gtag('event', 'demo', { event_category: type, event_label: demo });
return <CodePreviewer {...props} />;
};
interface DemoProps {
meta: any;
intl: any;
utils?: any;
src: string;
content: string;
highlightedCodes: Record<PropertyKey, string>;
style: string;
highlightedStyle: string;
expand: boolean;
sourceCodes: Record<'jsx' | 'tsx', string>;
location: Location;
showRiddleButton: boolean;
preview: (react: typeof React, reactDOM: typeof ReactDOM) => React.ReactNode;
}
const Demo: React.FC<DemoProps> = (props) => {
const {
location,
sourceCodes,
meta,
src,
utils,
content,
highlightedCodes,
style,
highlightedStyle,
expand,
intl: { locale },
showRiddleButton,
preview,
} = props;
const liveDemo = useRef<React.ReactNode>(null);
const anchorRef = useRef<HTMLAnchorElement>(null);
const codeSandboxIconRef = useRef<HTMLFormElement>(null);
const riddleIconRef = useRef<HTMLFormElement>(null);
const codepenIconRef = useRef<HTMLFormElement>(null);
const [codeExpand, setCodeExpand] = useState<boolean>(false);
const [copyTooltipOpen, setCopyTooltipOpen] = useState<boolean>(false);
const [copied, setCopied] = useState<boolean>(false);
const [codeType, setCodeType] = useState<string>('tsx');
const { theme } = useContext<SiteContextProps>(SiteContext);
const { hash, pathname, search } = location;
const docsOnlineUrl = `https://ant.design${pathname}${search}#${meta.id}`;
const [showOnlineUrl, setShowOnlineUrl] = useState<boolean>(false);
useEffect(() => {
const regexp = /preview-(\d+)-ant-design/; // matching PR preview addresses
setShowOnlineUrl(
process.env.NODE_ENV === 'development' || regexp.test(window.location.hostname),
);
}, []);
const handleCodeExpand = (demo: string) => {
setCodeExpand((prev) => !prev);
track({ type: 'expand', demo });
};
const handleCodeCopied = (demo: string) => {
setCopied(true);
track({ type: 'copy', demo });
};
const onCopyTooltipOpenChange = (open: boolean) => {
setCopyTooltipOpen(open);
if (open) {
setCopied(false);
}
};
useEffect(() => {
if (meta.id === hash.slice(1)) {
anchorRef.current?.click();
}
}, []);
useEffect(() => {
setCodeExpand(expand);
}, [expand]);
if (!liveDemo.current) {
liveDemo.current = meta.iframe ? (
<BrowserFrame>
<iframe src={src} height={meta.iframe} title="demo" className="iframe-demo" />
</BrowserFrame>
) : (
preview(React, ReactDOM)
);
}
const codeBoxClass = classNames('code-box', {
expand: codeExpand,
'code-box-debug': meta.originDebug,
});
const localizedTitle = meta?.title[locale] || meta?.title;
const localizeIntro = content[locale] || content;
const introChildren = <div dangerouslySetInnerHTML={{ __html: localizeIntro }} />;
const highlightClass = classNames('highlight-wrapper', {
'highlight-wrapper-expand': codeExpand,
});
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
</head>
<body>
<div id="container" style="padding: 24px" />
<script>const mountNode = document.getElementById('container');</script>
</body>
</html>
`;
const tsconfig = `
{
"compilerOptions": {
"jsx": "react-jsx",
"target": "esnext",
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
}
}
`;
const suffix = codeType === 'tsx' ? 'tsx' : 'js';
const dependencies: Record<PropertyKey, string> = sourceCodes?.jsx.split('\n').reduce(
(acc, line) => {
const matches = line.match(/import .+? from '(.+)';$/);
if (matches && matches[1] && !line.includes('antd')) {
const paths = matches[1].split('/');
if (paths.length) {
const dep = paths[0].startsWith('@') ? `${paths[0]}/${paths[1]}` : paths[0];
acc[dep] = 'latest';
}
}
return acc;
},
{ antd: version },
);
dependencies['@ant-design/icons'] = 'latest';
if (suffix === 'tsx') {
dependencies['@types/react'] = '^18.0.0';
dependencies['@types/react-dom'] = '^18.0.0';
}
dependencies.react = '^18.0.0';
dependencies['react-dom'] = '^18.0.0';
const codepenPrefillConfig = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
html,
js: `const { createRoot } = ReactDOM;\n${sourceCodes?.jsx
.replace(/import\s+(?:React,\s+)?{(\s+[^}]*\s+)}\s+from\s+'react'/, `const { $1 } = React;`)
.replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'antd';/, 'const { $1 } = antd;')
.replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'@ant-design\/icons';/, 'const { $1 } = icons;')
.replace("import moment from 'moment';", '')
.replace("import React from 'react';", '')
.replace(/import\s+{\s+(.*)\s+}\s+from\s+'react-router';/, 'const { $1 } = ReactRouter;')
.replace(
/import\s+{\s+(.*)\s+}\s+from\s+'react-router-dom';/,
'const { $1 } = ReactRouterDOM;',
)
.replace(/([A-Za-z]*)\s+as\s+([A-Za-z]*)/, '$1:$2')
.replace(
/export default/,
'const ComponentDemo =',
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
editors: '001',
css: '',
js_external: [
'react@18/umd/react.development.js',
'react-dom@18/umd/react-dom.development.js',
'dayjs@1/dayjs.min.js',
`antd@${version}/dist/antd-with-locales.js`,
`@ant-design/icons/dist/index.umd.js`,
'react-router-dom/dist/umd/react-router-dom.production.min.js',
'react-router/dist/umd/react-router.production.min.js',
]
.map((url) => `https://unpkg.com/${url}`)
.join(';'),
js_pre_processor: 'typescript',
};
const riddlePrefillConfig = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
js: `${
/import React(\D*)from 'react';/.test(sourceCodes?.jsx) ? '' : `import React from 'react';\n`
}import { createRoot } from 'react-dom/client';\n${sourceCodes?.jsx.replace(
/export default/,
'const ComponentDemo =',
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
css: '',
json: JSON.stringify({ name: 'antd-demo', dependencies }, null, 2),
};
// Reorder source code
let parsedSourceCode = suffix === 'tsx' ? sourceCodes?.tsx : sourceCodes?.jsx;
let importReactContent = "import React from 'react';";
const importReactReg = /import React(\D*)from 'react';/;
const matchImportReact = parsedSourceCode.match(importReactReg);
if (matchImportReact) {
[importReactContent] = matchImportReact;
parsedSourceCode = parsedSourceCode.replace(importReactReg, '').trim();
}
const demoJsContent = `
${importReactContent}
import './index.css';
${parsedSourceCode}
`.trim();
const indexCssContent = (style || '')
.trim()
.replace(new RegExp(`#${meta.id}\\s*`, 'g'), '')
.replace('</style>', '')
.replace('<style>', '');
const indexJsContent = `
import React from 'react';
import { createRoot } from 'react-dom/client';
import Demo from './demo';
createRoot(document.getElementById('container')).render(<Demo />);
`;
const codesandboxPackage = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
main: 'index.js',
dependencies: {
...dependencies,
react: '^18.0.0',
'react-dom': '^18.0.0',
'react-scripts': '^4.0.0',
},
devDependencies: {
typescript: '^4.0.5',
},
scripts: {
start: 'react-scripts start',
build: 'react-scripts build',
test: 'react-scripts test --env=jsdom',
eject: 'react-scripts eject',
},
browserslist: ['>0.2%', 'not dead'],
};
const codesanboxPrefillConfig = {
files: {
'package.json': { content: codesandboxPackage },
'index.css': { content: indexCssContent },
[`index.${suffix}`]: { content: indexJsContent },
[`demo.${suffix}`]: { content: demoJsContent },
'index.html': {
content: html,
},
},
};
const stackblitzPrefillConfig: Project = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
template: 'create-react-app',
dependencies,
description: '',
files: {
'index.css': indexCssContent,
[`index.${suffix}`]: indexJsContent,
[`demo.${suffix}`]: demoJsContent,
'index.html': html,
},
};
if (suffix === 'tsx') {
stackblitzPrefillConfig.files['tsconfig.json'] = tsconfig;
}
const backgroundGrey = theme.includes('dark') ? '#303030' : '#f0f2f5';
const codeBoxDemoStyle: React.CSSProperties = {
padding: meta.iframe || meta.compact ? 0 : undefined,
overflow: meta.iframe || meta.compact ? 'hidden' : undefined,
backgroundColor: meta.background === 'grey' ? backgroundGrey : undefined,
};
const codeBox: React.ReactNode = (
<section className={codeBoxClass} id={meta.id}>
<section className="code-box-demo" style={codeBoxDemoStyle}>
<ErrorBoundary>
<React.StrictMode>{liveDemo.current}</React.StrictMode>
</ErrorBoundary>
{style ? <style dangerouslySetInnerHTML={{ __html: style }} /> : null}
</section>
<section className="code-box-meta markdown">
<div className="code-box-title">
<Tooltip title={meta.originDebug ? <FormattedMessage id="app.demo.debug" /> : ''}>
<a href={`#${meta.id}`} ref={anchorRef}>
{localizedTitle}
</a>
</Tooltip>
<EditButton
title={<FormattedMessage id="app.content.edit-demo" />}
filename={meta.filename}
/>
</div>
<div className="code-box-description">{introChildren}</div>
<Space wrap size="middle" className="code-box-actions">
{showOnlineUrl && (
<Tooltip title={<FormattedMessage id="app.demo.online" />}>
<a
className="code-box-code-action"
target="_blank"
rel="noreferrer"
href={docsOnlineUrl}
>
<LinkOutlined className="code-box-online" />
</a>
</Tooltip>
)}
{showRiddleButton ? (
<form
className="code-box-code-action"
action="//riddle.alibaba-inc.com/riddles/define"
method="POST"
target="_blank"
ref={riddleIconRef}
onClick={() => {
track({ type: 'riddle', demo: meta.id });
riddleIconRef.current?.submit();
}}
>
<input type="hidden" name="data" value={JSON.stringify(riddlePrefillConfig)} />
<Tooltip title={<FormattedMessage id="app.demo.riddle" />}>
<RiddleIcon className="code-box-riddle" />
</Tooltip>
</form>
) : null}
<form
className="code-box-code-action"
action="https://codesandbox.io/api/v1/sandboxes/define"
method="POST"
target="_blank"
ref={codeSandboxIconRef}
onClick={() => {
track({ type: 'codesandbox', demo: meta.id });
codeSandboxIconRef.current?.submit();
}}
>
<input
type="hidden"
name="parameters"
value={compress(JSON.stringify(codesanboxPrefillConfig))}
/>
<Tooltip title={<FormattedMessage id="app.demo.codesandbox" />}>
<CodeSandboxIcon className="code-box-codesandbox" />
</Tooltip>
</form>
<form
className="code-box-code-action"
action="https://codepen.io/pen/define"
method="POST"
target="_blank"
ref={codepenIconRef}
onClick={() => {
track({ type: 'codepen', demo: meta.id });
codepenIconRef.current?.submit();
}}
>
<ClientOnly>
<input type="hidden" name="data" value={JSON.stringify(codepenPrefillConfig)} />
</ClientOnly>
<Tooltip title={<FormattedMessage id="app.demo.codepen" />}>
<CodePenIcon className="code-box-codepen" />
</Tooltip>
</form>
<Tooltip title={<FormattedMessage id="app.demo.stackblitz" />}>
<span
className="code-box-code-action"
onClick={() => {
track({ type: 'stackblitz', demo: meta.id });
stackblitzSdk.openProject(stackblitzPrefillConfig, {
openFile: [`demo.${suffix}`],
});
}}
>
<ThunderboltOutlined className="code-box-stackblitz" />
</span>
</Tooltip>
<CopyToClipboard text={sourceCodes?.tsx} onCopy={() => handleCodeCopied(meta.id)}>
<Tooltip
open={copyTooltipOpen as boolean}
onOpenChange={onCopyTooltipOpenChange}
title={<FormattedMessage id={`app.demo.${copied ? 'copied' : 'copy'}`} />}
>
{React.createElement(copied && copyTooltipOpen ? CheckOutlined : SnippetsOutlined, {
className: 'code-box-code-copy code-box-code-action',
})}
</Tooltip>
</CopyToClipboard>
<Tooltip title={<FormattedMessage id="app.demo.separate" />}>
<a className="code-box-code-action" target="_blank" rel="noreferrer" href={src}>
<ExternalLinkIcon className="code-box-separate" />
</a>
</Tooltip>
<Tooltip
title={<FormattedMessage id={`app.demo.code.${codeExpand ? 'hide' : 'show'}`} />}
>
<div className="code-expand-icon code-box-code-action">
<img
alt="expand code"
src={
theme?.includes('dark')
? 'https://gw.alipayobjects.com/zos/antfincdn/btT3qDZn1U/wSAkBuJFbdxsosKKpqyq.svg'
: 'https://gw.alipayobjects.com/zos/antfincdn/Z5c7kzvi30/expand.svg'
}
className={codeExpand ? 'code-expand-icon-hide' : 'code-expand-icon-show'}
onClick={() => handleCodeExpand(meta.id)}
/>
<img
alt="expand code"
src={
theme?.includes('dark')
? 'https://gw.alipayobjects.com/zos/antfincdn/CjZPwcKUG3/OpROPHYqWmrMDBFMZtKF.svg'
: 'https://gw.alipayobjects.com/zos/antfincdn/4zAaozCvUH/unexpand.svg'
}
className={codeExpand ? 'code-expand-icon-show' : 'code-expand-icon-hide'}
onClick={() => handleCodeExpand(meta.id)}
/>
</div>
</Tooltip>
</Space>
</section>
<section className={highlightClass} key="code">
<CodePreview
codes={highlightedCodes}
toReactComponent={utils?.toReactComponent}
onCodeTypeChange={(type) => setCodeType(type)}
/>
{highlightedStyle ? (
<div key="style" className="highlight">
<pre>
<code className="css" dangerouslySetInnerHTML={{ __html: highlightedStyle }} />
</pre>
</div>
) : null}
</section>
</section>
);
if (meta.version) {
return (
<Badge.Ribbon text={meta.version} color={meta.version.includes('<') ? 'red' : null}>
{codeBox}
</Badge.Ribbon>
);
}
return codeBox;
};
export default fromDumiProps(Demo);
export default Previewer;

View File

@ -0,0 +1,334 @@
import type { FC } from 'react';
import React, { useEffect, useRef } from 'react';
import G6 from '@antv/g6';
import { createStyles, css } from 'antd-style';
import { useRouteMeta } from 'dumi';
G6.registerNode('behavior-start-node', {
draw: (cfg, group) => {
const textWidth = G6.Util.getTextSize(cfg!.label, 16)[0];
const size = [textWidth + 20 * 2, 48];
const keyShape = group!.addShape('rect', {
name: 'start-node',
attrs: {
width: size[0],
height: size[1],
y: -size[1] / 2,
radius: 8,
fill: '#fff',
},
});
group!.addShape('text', {
attrs: {
text: `${cfg!.label}`,
fill: 'rgba(0, 0, 0, 0.88)',
fontSize: 16,
fontWeight: 500,
x: 20,
textBaseline: 'middle',
},
name: 'start-node-text',
});
return keyShape;
},
getAnchorPoints() {
return [
[0, 0.5],
[1, 0.5],
];
},
});
G6.registerNode(
'behavior-sub-node',
{
draw: (cfg, group) => {
const textWidth = G6.Util.getTextSize(cfg!.label, 14)[0];
const padding = 16;
const size = [textWidth + 16 * 2 + (cfg!.targetType ? 12 : 0) + (cfg!.link ? 20 : 0), 40];
const keyShape = group!.addShape('rect', {
name: 'sub-node',
attrs: {
width: size[0],
height: size[1],
y: -size[1] / 2,
radius: 8,
fill: '#fff',
cursor: 'pointer',
},
});
group!.addShape('text', {
attrs: {
text: `${cfg!.label}`,
x: cfg!.targetType ? 12 + 16 : padding,
fill: 'rgba(0, 0, 0, 0.88)',
fontSize: 14,
textBaseline: 'middle',
cursor: 'pointer',
},
name: 'sub-node-text',
});
if (cfg!.targetType) {
group!.addShape('rect', {
name: 'sub-node-type',
attrs: {
width: 8,
height: 8,
radius: 4,
y: -4,
x: 12,
fill: cfg!.targetType === 'mvp' ? '#1677ff' : '#A0A0A0',
cursor: 'pointer',
},
});
}
if (cfg!.children) {
const { length } = cfg!.children as any;
group!.addShape('rect', {
name: 'sub-node-children-length',
attrs: {
width: 20,
height: 20,
radius: 10,
y: -10,
x: size[0] - 4,
fill: '#404040',
cursor: 'pointer',
},
});
group!.addShape('text', {
name: 'sub-node-children-length-text',
attrs: {
text: `${length}`,
x: size[0] + 6 - G6.Util.getTextSize(`${length}`, 12)[0] / 2,
textBaseline: 'middle',
fill: '#fff',
fontSize: 12,
cursor: 'pointer',
},
});
}
if (cfg!.link) {
group!.addShape('dom', {
attrs: {
width: 16,
height: 16,
x: size[0] - 12 - 16,
y: -8,
cursor: 'pointer',
// DOM's html
html: `
<div style="width: 16px; height: 16px;">
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="DatePicker" transform="translate(-890.000000, -441.000000)" fill-rule="nonzero">
<g id="编组-30" transform="translate(288.000000, 354.000000)">
<g id="编组-7备份-7" transform="translate(522.000000, 79.000000)">
<g id="right-circle-outlinedd" transform="translate(80.000000, 8.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M10.4171875,7.8984375 L6.5734375,5.1171875 C6.490625,5.0578125 6.375,5.115625 6.375,5.21875 L6.375,5.9515625 C6.375,6.1109375 6.4515625,6.2625 6.58125,6.35625 L8.853125,8 L6.58125,9.64375 C6.4515625,9.7375 6.375,9.8875 6.375,10.0484375 L6.375,10.78125 C6.375,10.8828125 6.490625,10.9421875 6.5734375,10.8828125 L10.4171875,8.1015625 C10.4859375,8.0515625 10.4859375,7.9484375 10.4171875,7.8984375 Z" id="路径" fill="#BFBFBF"></path>
<path d="M8,1 C4.134375,1 1,4.134375 1,8 C1,11.865625 4.134375,15 8,15 C11.865625,15 15,11.865625 15,8 C15,4.134375 11.865625,1 8,1 Z M8,13.8125 C4.790625,13.8125 2.1875,11.209375 2.1875,8 C2.1875,4.790625 4.790625,2.1875 8,2.1875 C11.209375,2.1875 13.8125,4.790625 13.8125,8 C13.8125,11.209375 11.209375,13.8125 8,13.8125 Z" id="形状" fill="#BFBFBF"></path>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
`,
},
// 在 G6 3.3 及之后的版本中,必须指定 name可以是任意字符串但需要在同一个自定义元素类型中保持唯一性
name: 'sub-node-link',
});
}
return keyShape;
},
getAnchorPoints() {
return [
[0, 0.5],
[1, 0.5],
];
},
options: {
stateStyles: {
hover: {
stroke: '#1677ff',
'sub-node-link': {
html: `
<div style="width: 16px; height: 16px;">
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="DatePicker" transform="translate(-890.000000, -441.000000)" fill-rule="nonzero">
<g id="编组-30" transform="translate(288.000000, 354.000000)">
<g id="编组-7备份-7" transform="translate(522.000000, 79.000000)">
<g id="right-circle-outlinedd" transform="translate(80.000000, 8.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M10.4171875,7.8984375 L6.5734375,5.1171875 C6.490625,5.0578125 6.375,5.115625 6.375,5.21875 L6.375,5.9515625 C6.375,6.1109375 6.4515625,6.2625 6.58125,6.35625 L8.853125,8 L6.58125,9.64375 C6.4515625,9.7375 6.375,9.8875 6.375,10.0484375 L6.375,10.78125 C6.375,10.8828125 6.490625,10.9421875 6.5734375,10.8828125 L10.4171875,8.1015625 C10.4859375,8.0515625 10.4859375,7.9484375 10.4171875,7.8984375 Z" id="路径" fill="#1677ff"></path>
<path d="M8,1 C4.134375,1 1,4.134375 1,8 C1,11.865625 4.134375,15 8,15 C11.865625,15 15,11.865625 15,8 C15,4.134375 11.865625,1 8,1 Z M8,13.8125 C4.790625,13.8125 2.1875,11.209375 2.1875,8 C2.1875,4.790625 4.790625,2.1875 8,2.1875 C11.209375,2.1875 13.8125,4.790625 13.8125,8 C13.8125,11.209375 11.209375,13.8125 8,13.8125 Z" id="形状" fill="#1677ff"></path>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
`,
},
},
},
},
},
'rect',
);
const dataTransform = (data: BehaviorMapItem) => {
const changeData = (d: any, level = 0) => {
const clonedData: any = {
...d,
};
switch (level) {
case 0:
clonedData.type = 'behavior-start-node';
break;
case 1:
clonedData.type = 'behavior-sub-node';
clonedData.collapsed = true;
break;
default:
clonedData.type = 'behavior-sub-node';
break;
}
if (d.children) {
clonedData.children = d.children.map((child: any) => changeData(child, level + 1));
}
return clonedData;
};
return changeData(data);
};
type BehaviorMapItem = {
id: string;
label: string;
targetType?: 'mvp' | 'extension';
children?: BehaviorMapItem[];
link?: string;
};
const useStyle = createStyles(() => ({
container: css`
width: 100%;
height: 600px;
background-color: #f5f5f5;
border: 1px solid #e8e8e8;
border-radius: 8px;
overflow: hidden;
position: relative;
`,
title: css`
position: absolute;
top: 20px;
left: 20px;
font-size: 16px;
`,
tips: css`
display: flex;
position: absolute;
bottom: 20px;
right: 20px;
`,
mvp: css`
margin-right: 20px;
display: flex;
align-items: center;
&::before {
content: '';
display: block;
width: 8px;
height: 8px;
background-color: #1677ff;
border-radius: 50%;
margin-right: 8px;
}
`,
extension: css`
display: flex;
align-items: center;
&::before {
content: '';
display: block;
width: 8px;
height: 8px;
background-color: #A0A0A0;
border-radius: 50%;
margin-right: 8px;
}
`,
}));
export type BehaviorMapProps = {
data: BehaviorMapItem;
};
const BehaviorMap: FC<BehaviorMapProps> = ({ data }) => {
const ref = useRef<HTMLDivElement>(null);
const { styles } = useStyle();
const meta = useRouteMeta();
useEffect(() => {
const graph = new G6.TreeGraph({
container: ref.current!,
width: ref.current!.scrollWidth,
height: ref.current!.scrollHeight,
renderer: 'svg',
modes: {
default: ['collapse-expand', 'drag-canvas'],
},
defaultEdge: {
type: 'cubic-horizontal',
style: {
lineWidth: 1,
stroke: '#BFBFBF',
},
},
layout: {
type: 'mindmap',
direction: 'LR',
getHeight: () => 48,
getWidth: (node: any) => G6.Util.getTextSize(node.label, 16)[0] + 20 * 2,
getVGap: () => 10,
getHGap: () => 60,
getSide: (node: any) => node.data.direction,
},
});
graph.on('node:mouseenter', (e) => {
graph.setItemState(e.item!, 'hover', true);
});
graph.on('node:mouseleave', (e) => {
graph.setItemState(e.item!, 'hover', false);
});
graph.on('node:click', (e) => {
const { link } = e.item!.getModel();
if (link) {
window.location.hash = link as string;
}
});
graph.data(dataTransform(data));
graph.render();
graph.fitCenter();
}, []);
return (
<div ref={ref} className={styles.container}>
<div className={styles.title}>{`${meta.frontmatter.title} 行为模式地图`}</div>
<div className={styles.tips}>
<div className={styles.mvp}>MVP </div>
<div className={styles.extension}></div>
</div>
</div>
);
};
export default BehaviorMap;

View File

@ -5,7 +5,7 @@ import {
parentSelectorLinter,
StyleProvider,
} from '@ant-design/cssinjs';
import { ConfigProvider, theme as antdTheme } from 'antd';
import { ConfigProvider, theme as antdTheme, App } from 'antd';
import type { DirectionType } from 'antd/es/config-provider';
import { createSearchParams, useOutlet, useSearchParams } from 'dumi';
import React, { startTransition, useCallback, useEffect, useMemo } from 'react';
@ -118,6 +118,7 @@ const GlobalLayout: React.FC = () => {
algorithm: getAlgorithm(theme),
}}
>
<App>
{outlet}
{!pathname.startsWith('/~demos') && (
<ThemeSwitch
@ -125,6 +126,7 @@ const GlobalLayout: React.FC = () => {
onChange={(nextTheme) => updateSiteConfig({ theme: nextTheme })}
/>
)}
</App>
</ConfigProvider>
</SiteContext.Provider>
</StyleProvider>

View File

@ -4,7 +4,7 @@ import ContributorsList from '@qixian.cs/github-contributors-list';
import { Affix, Anchor, Avatar, Col, Skeleton, Space, Tooltip, Typography } from 'antd';
import classNames from 'classnames';
import DayJS from 'dayjs';
import { FormattedMessage, useIntl, useRouteMeta } from 'dumi';
import { FormattedMessage, useIntl, useRouteMeta, useTabMeta } from 'dumi';
import type { ReactNode } from 'react';
import React, { useContext, useLayoutEffect, useMemo, useState } from 'react';
import useLocation from '../../../hooks/useLocation';
@ -107,6 +107,7 @@ type AnchorItem = {
const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
const meta = useRouteMeta();
const tab = useTabMeta();
const { pathname, hash } = useLocation();
const { formatMessage } = useIntl();
const styles = useStyle();
@ -132,7 +133,7 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
const anchorItems = useMemo(
() =>
meta.toc.reduce<AnchorItem[]>((result, item) => {
(tab?.toc || meta.toc).reduce<AnchorItem[]>((result, item) => {
if (item.depth === 2) {
result.push({ ...item });
} else if (item.depth === 3) {
@ -144,7 +145,7 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
}
return result;
}, []),
[meta.toc],
[tab?.toc, meta.toc],
);
const isRTL = direction === 'rtl';
@ -219,6 +220,7 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
</Space>
</Typography.Paragraph>
) : null}
{meta.frontmatter.description !== meta.texts[0]?.value && meta.frontmatter.description}
{children}
{meta.frontmatter.filename && (
<ContributorsList

View File

@ -0,0 +1,57 @@
import type { FC, ReactNode } from 'react';
import React from 'react';
import { CodeOutlined, SkinOutlined } from '@ant-design/icons';
import { Tabs } from 'antd';
import { useRouteMeta } from 'dumi';
import type { IContentTabsProps } from 'dumi/theme-default/slots/ContentTabs';
import type { TabsProps } from 'rc-tabs';
const titleMap: Record<string, string> = {
design: '设计',
};
const iconMap: Record<string, ReactNode> = {
design: <SkinOutlined />,
};
const ContentTabs: FC<IContentTabsProps> = ({ tabs, tabKey, onChange }) => {
const meta = useRouteMeta();
if (!meta.tabs) {
return null;
}
const items: TabsProps['items'] = [
{
label: (
<span>
<CodeOutlined />
</span>
),
key: 'development',
},
];
tabs?.forEach((tab) => {
items.push({
label: (
<span>
{iconMap[tab.key]}
{titleMap[tab.key]}
</span>
),
key: tab.key,
});
});
return (
<Tabs
items={items}
activeKey={tabKey || 'development'}
onChange={(key) => onChange(tabs.find((tab) => tab.key === key))}
style={{ margin: '32px 0 -16px' }}
/>
);
};
export default ContentTabs;

View File

@ -14,6 +14,7 @@ server
.dumi/tmp-production
!.dumi/
node_modules
.eslintcache
_site
dist
coverage

View File

@ -1,74 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master", "feature" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master", "feature" ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@ -25,6 +25,10 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: cache package-lock.json
uses: actions/cache@v3
with:

View File

@ -19,6 +19,10 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: cache package-lock.json
uses: actions/cache@v3
with:
@ -51,6 +55,10 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: restore cache from package-lock.json
uses: actions/cache@v3
with:
@ -73,6 +81,10 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: restore cache from package-lock.json
uses: actions/cache@v3
with:
@ -97,6 +109,10 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: restore cache from package-lock.json
uses: actions/cache@v3
with:
@ -136,6 +152,10 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: restore cache from package-lock.json
uses: actions/cache@v3
with:
@ -209,6 +229,11 @@ jobs:
needs: [normal-test]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: actions/download-artifact@v3
with:
name: coverage-artifacts
@ -231,6 +256,10 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: restore cache from package-lock.json
uses: actions/cache@v3
with:
@ -278,6 +307,10 @@ jobs:
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- name: restore cache from package-lock.json
# lib only run in master branch not in pull request
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}

View File

@ -52,6 +52,7 @@ module.exports = {
'!components/*/__tests__/image.test.{ts,tsx}',
'!components/__tests__/node.test.tsx',
'!components/*/demo/*.tsx',
'!components/*/design/**',
],
transformIgnorePatterns,
globals: {

View File

@ -15,9 +15,29 @@ timeline: true
---
## 5.2.2
`2023-02-19`
- DatePicker
- 💄 Optimize DatePicker date panel style. [#40768](https://github.com/ant-design/ant-design/pull/40768)
- 🐞 Fix RangePicker hover style on wrong date. [#40785](https://github.com/ant-design/ant-design/pull/40785) [@Yuiai01](https://github.com/Yuiai01)
- Form
- 🐞 Fixed inconsistency between Checkbox and Radio in table when Form is `disabled`. [#40728](https://github.com/ant-design/ant-design/pull/40728) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 Fix Radio/Checkbox under Form `disabled` property don't works correctly. [#40741](https://github.com/ant-design/ant-design/pull/40741) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 Fix List extra padding when enable `grid` property. [#40806](https://github.com/ant-design/ant-design/pull/40806)
- 🐞 Fix Upload actions icon alignment issue. [#40805](https://github.com/ant-design/ant-design/pull/40805)
- 💄 Tweak Table filter dropdown radius style. [#40802](https://github.com/ant-design/ant-design/pull/40802)
- 🐞 Fix Button `loading.delay` not delay at first time. [#40759](https://github.com/ant-design/ant-design/pull/40759) [@RedJue](https://github.com/RedJue)
- 🐞 Fix Input status style when using `addonAfter` and `addonBefore`. [#40744](https://github.com/ant-design/ant-design/pull/40744) [@carla-cn](https://github.com/carla-cn)
- 🐞 Fix Skeleton `active` flicky animation in Safari. [#40692](https://github.com/ant-design/ant-design/pull/40692) [@slotDumpling](https://github.com/slotDumpling)
- Locales
- 🇫🇷 Added french locale for Tour component. [#40750](https://github.com/ant-design/ant-design/pull/40750) [@RedJue](https://github.com/RedJue)
- 🇰🇷 Update ko_KR locale. [#40716](https://github.com/ant-design/ant-design/pull/40716) [@owjs3901](https://github.com/owjs3901)
## 5.2.1
`2023-2-13`
`2023-02-13`
- 🛠 Rewrite `panelRender` in Tour to function component。[#40670](https://github.com/ant-design/ant-design/pull/40670) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 Fix `className` property wrongly passed to child nodes in TimeLine。[#40700](https://github.com/ant-design/ant-design/pull/40700) [@any1024](https://github.com/any1024)
@ -32,7 +52,7 @@ timeline: true
## 5.2.0
`2023-2-8`
`2023-02-08`
- 🔥 Add `picture-circle` to Upload's `listType` prop. [#40134](https://github.com/ant-design/ant-design/pull/40134) [@ds1371dani](https://github.com/ds1371dani)
- 🔥 Anchor component add `direction`, which supports vertical. [#39372](https://github.com/ant-design/ant-design/pull/39372) [@foryuki](https://github.com/foryuki)
@ -94,7 +114,7 @@ timeline: true
## 5.1.7
`2023-1-31`
`2023-01-31`
- Input
- 🐞 Fix Input that unexpected cancel button is shown when `type="search"`. [#40457](https://github.com/ant-design/ant-design/pull/40457) [@MadCcc](https://github.com/MadCcc)
@ -113,7 +133,7 @@ timeline: true
## 5.1.6
`2023-1-20`
`2023-01-20`
- 🐞 Fix DatePicker animation timing function. [#40133](https://github.com/ant-design/ant-design/pull/40133) [@MadCcc](https://github.com/MadCcc)
- Menu
@ -131,7 +151,7 @@ timeline: true
## 5.1.5
`2023-1-15`
`2023-01-15`
- 🐞 Fix Checkbox that label not aligned with checkbox. [#40208](https://github.com/ant-design/ant-design/pull/40208)
- 🐞 Fix Button wave effect sometime makes layout shaking. [#40192](https://github.com/ant-design/ant-design/pull/40192)
@ -148,7 +168,7 @@ timeline: true
## 5.1.4
`2023-1-9`
`2023-01-09`
- 🐞 Fix missing locale file. [#40116](https://github.com/ant-design/ant-design/pull/40116)
- 🐞 Fix Cascader dropdown `placement` in RTL mode. [#40109](https://github.com/ant-design/ant-design/pull/40109) [@3hson](https://github.com/3hson)
@ -156,7 +176,7 @@ timeline: true
## 5.1.3
`2023-1-9`
`2023-01-09`
- Table
- 🛠 Optimize the Table `shouldCellUpdate` logic to increase the secondary rendering speed. [#40063](https://github.com/ant-design/ant-design/pull/40063)

View File

@ -15,9 +15,29 @@ timeline: true
---
## 5.2.2
`2023-02-19`
- DatePicker
- 💄 调整 DatePicker 组件日期面板的间距样式。[#40768](https://github.com/ant-design/ant-design/pull/40768)
- 🐞 修复 RangePicker `hover` 日期错位的问题。[#40785](https://github.com/ant-design/ant-design/pull/40785) [@Yuiai01](https://github.com/Yuiai01)
- Form
- 🐞 修复 Form 下 Radio/Checkbox 的 disabled 优先级问题。[#40741](https://github.com/ant-design/ant-design/pull/40741) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 修复 Form 为 `disabled` 时 Checkbox 和 Radio 表现不一致的问题。[#40728](https://github.com/ant-design/ant-design/pull/40728) [@Yuiai01](https://github.com/Yuiai01)
- 🐞 修复 List 启用 `grid` 时下额外 `padding` 样式。[#40806](https://github.com/ant-design/ant-design/pull/40806)
- 🐞 修复 Upload 操作图标不对齐的问题。[#40805](https://github.com/ant-design/ant-design/pull/40805)
- 💄 调整 Table 筛选菜单的底部圆角样式。[#40802](https://github.com/ant-design/ant-design/pull/40802)
- 🐞 修复 Button 组件 `loading.delay` 第一次不生效的问题。[#40759](https://github.com/ant-design/ant-design/pull/40759) [@RedJue](https://github.com/RedJue)
- 🐞 修复 Input `addonAfter``addonBefore` 的各种状态样式。[#40744](https://github.com/ant-design/ant-design/pull/40744) [@carla-cn](https://github.com/carla-cn)
- 🐞 修复 Skeleton 在 Safari 下 `active` 效果闪烁的问题。[#40692](https://github.com/ant-design/ant-design/pull/40692) [@slotDumpling](https://github.com/slotDumpling)
- 国际化
- 🇫🇷 补充 Tour 法语本地化文案。[#40750](https://github.com/ant-design/ant-design/pull/40750) [@RedJue](https://github.com/RedJue)
- 🇰🇷 更新韩国本地化文案。[#40716](https://github.com/ant-design/ant-design/pull/40716) [@owjs3901](https://github.com/owjs3901)
## 5.2.1
`2023-2-13`
`2023-02-13`
- 🛠 重构 Tour 中 `panelRender` 为函数式组件。[#40670](https://github.com/ant-design/ant-design/pull/40670) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 修复 TimeLine 中 `className` 传给子节点的问题。[#40700](https://github.com/ant-design/ant-design/pull/40700) [@any1024](https://github.com/any1024)
@ -32,7 +52,7 @@ timeline: true
## 5.2.0
`2023-2-8`
`2023-02-08`
- 🔥 Upload 的 `listType` 属性添加 `picture-circle` 支持。[#40134](https://github.com/ant-design/ant-design/pull/40134) [@ds1371dani](https://github.com/ds1371dani)
- 🔥 Anchor 组件新增 `direction` 属性,支持 vertical。[#39372](https://github.com/ant-design/ant-design/pull/39372) [@foryuki](https://github.com/foryuki)
@ -94,7 +114,7 @@ timeline: true
## 5.1.7
`2023-1-31`
`2023-01-31`
- Input
- 🐞 修复 Input 组件 `type="search"` 时未隐藏浏览器原生取消按钮的问题。[#40457](https://github.com/ant-design/ant-design/pull/40457) [@MadCcc](https://github.com/MadCcc)
@ -113,7 +133,7 @@ timeline: true
## 5.1.6
`2023-1-20`
`2023-01-20`
- 🐞 修复 DatePicker 等组件动画 timing function 错误的问题。[#40133](https://github.com/ant-design/ant-design/pull/40133) [@MadCcc](https://github.com/MadCcc)
- Menu
@ -131,7 +151,7 @@ timeline: true
## 5.1.5
`2023-1-15`
`2023-01-15`
- 🐞 修复 Checkbox 组件 label 不对齐的问题。 [#40208](https://github.com/ant-design/ant-design/pull/40208)
- 🐞 修复 Button 水波纹效果有时会使得布局抖动的问题。[#40192](https://github.com/ant-design/ant-design/pull/40192)
@ -148,7 +168,7 @@ timeline: true
## 5.1.4
`2023-1-9`
`2023-01-09`
- 🐞 修复 locale 文件丢失的问题。[#40116](https://github.com/ant-design/ant-design/pull/40116)
- 🐞 修复 Cascader 组件 RTL 模式中下拉菜单位置问题。[#40109](https://github.com/ant-design/ant-design/pull/40109) [@3hson](https://github.com/3hson)
@ -156,7 +176,7 @@ timeline: true
## 5.1.3
`2023-1-9`
`2023-01-09`
- Table
- 🛠 优化 Table `shouldCellUpdate` 逻辑,提升二次渲染速度。[#40063](https://github.com/ant-design/ant-design/pull/40063)

View File

@ -53,7 +53,7 @@ describe('Avatar Render', () => {
it('should handle onError correctly', () => {
const LOAD_FAILURE_SRC = 'http://error.url/';
const LOAD_SUCCESS_SRC = 'https://joeschmoe.io/api/v1/random';
const LOAD_SUCCESS_SRC = 'https://joesch.moe/api/v1/random';
const Foo: React.FC = () => {
const [avatarSrc, setAvatarSrc] = useState<typeof LOAD_FAILURE_SRC | typeof LOAD_SUCCESS_SRC>(
LOAD_FAILURE_SRC,
@ -75,7 +75,7 @@ describe('Avatar Render', () => {
it('should show image on success after a failure state', () => {
const LOAD_FAILURE_SRC = 'http://error.url';
const LOAD_SUCCESS_SRC = 'https://joeschmoe.io/api/v1/random';
const LOAD_SUCCESS_SRC = 'https://joesch.moe/api/v1/random';
const div = global.document.createElement('div');
global.document.body.appendChild(div);
@ -172,7 +172,7 @@ describe('Avatar Render', () => {
});
it('should exist crossorigin attribute', () => {
const LOAD_SUCCESS_SRC = 'https://joeschmoe.io/api/v1/random';
const LOAD_SUCCESS_SRC = 'https://joesch.moe/api/v1/random';
const crossOrigin = 'anonymous';
const { container } = render(
<Avatar src={LOAD_SUCCESS_SRC} crossOrigin={crossOrigin}>
@ -184,7 +184,7 @@ describe('Avatar Render', () => {
});
it('should not exist crossorigin attribute', () => {
const LOAD_SUCCESS_SRC = 'https://joeschmoe.io/api/v1/random';
const LOAD_SUCCESS_SRC = 'https://joesch.moe/api/v1/random';
const { container } = render(<Avatar src={LOAD_SUCCESS_SRC}>crossorigin</Avatar>);
expect(container.querySelector('img')?.crossOrigin).toBeFalsy();
expect(container.querySelector('img')?.crossOrigin).toEqual('');

View File

@ -140,7 +140,7 @@ exports[`Avatar Render should handle onError correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
`;
@ -163,7 +163,7 @@ exports[`Avatar Render should show image on success after a failure state 2`] =
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
`;

View File

@ -415,7 +415,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
<a
@ -512,7 +512,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
<span
@ -638,7 +638,7 @@ Array [
class="ant-avatar ant-avatar-lg ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=3"
/>
</span>
<span

View File

@ -415,7 +415,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
<a
@ -493,7 +493,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
<span
@ -530,7 +530,7 @@ Array [
class="ant-avatar ant-avatar-lg ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=3"
/>
</span>
<span

View File

@ -1,11 +1,11 @@
import React from 'react';
import { AntDesignOutlined, UserOutlined } from '@ant-design/icons';
import { Avatar, Divider, Tooltip } from 'antd';
import React from 'react';
const App: React.FC = () => (
<>
<Avatar.Group>
<Avatar src="https://joeschmoe.io/api/v1/random" />
<Avatar src="https://joesch.moe/api/v1/random?key=1" />
<a href="https://ant.design">
<Avatar style={{ backgroundColor: '#f56a00' }}>K</Avatar>
</a>
@ -16,7 +16,7 @@ const App: React.FC = () => (
</Avatar.Group>
<Divider />
<Avatar.Group maxCount={2} maxStyle={{ color: '#f56a00', backgroundColor: '#fde3cf' }}>
<Avatar src="https://joeschmoe.io/api/v1/random" />
<Avatar src="https://joesch.moe/api/v1/random?key=2" />
<Avatar style={{ backgroundColor: '#f56a00' }}>K</Avatar>
<Tooltip title="Ant User" placement="top">
<Avatar style={{ backgroundColor: '#87d068' }} icon={<UserOutlined />} />
@ -29,7 +29,7 @@ const App: React.FC = () => (
size="large"
maxStyle={{ color: '#f56a00', backgroundColor: '#fde3cf' }}
>
<Avatar src="https://joeschmoe.io/api/v1/random" />
<Avatar src="https://joesch.moe/api/v1/random?key=3" />
<Avatar style={{ backgroundColor: '#f56a00' }}>K</Avatar>
<Tooltip title="Ant User" placement="top">
<Avatar style={{ backgroundColor: '#87d068' }} icon={<UserOutlined />} />

View File

@ -91,3 +91,8 @@ it('Delay loading timer in Button component', () => {
jest.restoreAllMocks();
});
it('Delay loading while use loading delay at first time', () => {
const Demo = () => <Button loading={{ delay: specialDelay }} />;
const wrapper = render(<Demo />);
expect(wrapper.container.firstChild).not.toHaveClass('ant-btn-loading');
});

View File

@ -2,19 +2,19 @@
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
import warning from '../_util/warning';
import Wave from '../_util/wave';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import SizeContext from '../config-provider/SizeContext';
import { useCompactItemContext } from '../space/Compact';
import warning from '../_util/warning';
import Wave from '../_util/wave';
import Group, { GroupSizeContext } from './button-group';
import { isTwoCNChar, isUnBorderedButtonType, spaceChildren } from './buttonHelpers';
import LoadingIcon from './LoadingIcon';
import useStyle from './style';
import type { ButtonType, ButtonHTMLType, ButtonShape } from './buttonHelpers';
import type { SizeType } from '../config-provider/SizeContext';
import type { ButtonHTMLType, ButtonShape, ButtonType } from './buttonHelpers';
export type LegacyButtonType = ButtonType | 'danger';
@ -66,6 +66,27 @@ type CompoundedComponent = React.ForwardRefExoticComponent<
type Loading = number | boolean;
type LoadingConfigType = {
loading: boolean;
delay: number;
};
function getLoadingConfig(loading: BaseButtonProps['loading']): LoadingConfigType {
if (typeof loading === 'object' && loading) {
const delay = loading?.delay;
const isDelay = !Number.isNaN(delay) && typeof delay === 'number';
return {
loading: false,
delay: isDelay ? delay : 0,
};
}
return {
loading: !!loading,
delay: 0,
};
}
const InternalButton: React.ForwardRefRenderFunction<
HTMLButtonElement | HTMLAnchorElement,
ButtonProps
@ -99,7 +120,11 @@ const InternalButton: React.ForwardRefRenderFunction<
const mergedDisabled = customDisabled ?? disabled;
const groupSize = React.useContext(GroupSizeContext);
const [innerLoading, setLoading] = React.useState<Loading>(!!loading);
const loadingOrDelay: LoadingConfigType = React.useMemo(
() => getLoadingConfig(loading),
[loading],
);
const [innerLoading, setLoading] = React.useState<Loading>(loadingOrDelay.loading);
const [hasTwoCNChar, setHasTwoCNChar] = React.useState(false);
const buttonRef = (ref as any) || React.createRef<HTMLAnchorElement | HTMLButtonElement>();
@ -121,18 +146,16 @@ const InternalButton: React.ForwardRefRenderFunction<
}
};
const loadingOrDelay: Loading = typeof loading === 'boolean' ? loading : loading?.delay || true;
React.useEffect(() => {
let delayTimer: number | null = null;
if (typeof loadingOrDelay === 'number') {
if (loadingOrDelay.delay > 0) {
delayTimer = window.setTimeout(() => {
delayTimer = null;
setLoading(loadingOrDelay);
}, loadingOrDelay);
setLoading(true);
}, loadingOrDelay.delay);
} else {
setLoading(loadingOrDelay);
setLoading(loadingOrDelay.loading);
}
function cleanupTimer() {

View File

@ -601,7 +601,7 @@ exports[`renders ./components/card/demo/meta.tsx extend context correctly 1`] =
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>

View File

@ -601,7 +601,7 @@ exports[`renders ./components/card/demo/meta.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
import { Avatar, Card, Skeleton, Switch } from 'antd';
import React, { useState } from 'react';
const { Meta } = Card;
@ -14,15 +14,13 @@ const App: React.FC = () => {
return (
<>
<Switch checked={!loading} onChange={onChange} />
<Card style={{ width: 300, marginTop: 16 }} loading={loading}>
<Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
avatar={<Avatar src="https://joesch.moe/api/v1/random?key=1" />}
title="Card title"
description="This is the description"
/>
</Card>
<Card
style={{ width: 300, marginTop: 16 }}
actions={[
@ -33,7 +31,7 @@ const App: React.FC = () => {
>
<Skeleton loading={loading} avatar active>
<Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
avatar={<Avatar src="https://joesch.moe/api/v1/random?key=2" />}
title="Card title"
description="This is the description"
/>

View File

@ -1,6 +1,6 @@
import React from 'react';
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
import { Avatar, Card } from 'antd';
import { EditOutlined,EllipsisOutlined,SettingOutlined } from '@ant-design/icons';
import { Avatar,Card } from 'antd';
const { Meta } = Card;
@ -20,7 +20,7 @@ const App: React.FC = () => (
]}
>
<Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
avatar={<Avatar src="https://joesch.moe/api/v1/random" />}
title="Card title"
description="This is the description"
/>

View File

@ -2,8 +2,8 @@ import React, { useState } from 'react';
import { Cascader } from 'antd';
interface Option {
value: string;
label: string;
value?: string | number | null;
label: React.ReactNode;
children?: Option[];
isLeaf?: boolean;
loading?: boolean;
@ -25,7 +25,7 @@ const optionLists: Option[] = [
const App: React.FC = () => {
const [options, setOptions] = useState<Option[]>(optionLists);
const onChange = (value: string[], selectedOptions: Option[]) => {
const onChange = (value: (string | number)[], selectedOptions: Option[]) => {
console.log(value, selectedOptions);
};

View File

@ -16015,7 +16015,7 @@ exports[`ConfigProvider components Input configProvider componentDisabled 1`] =
value=""
/>
<span
class="config-input-group-wrapper config-input-search"
class="config-input-group-wrapper config-input-search config-input-group-wrapper-disabled"
>
<span
class="config-input-wrapper config-input-group"
@ -17327,7 +17327,7 @@ exports[`ConfigProvider components List configProvider 1`] = `
class="config-avatar config-avatar-circle config-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>
@ -17379,7 +17379,7 @@ exports[`ConfigProvider components List configProvider componentDisabled 1`] = `
class="config-avatar config-avatar-circle config-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>
@ -17431,7 +17431,7 @@ exports[`ConfigProvider components List configProvider componentSize large 1`] =
class="config-avatar config-avatar-circle config-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>
@ -17483,7 +17483,7 @@ exports[`ConfigProvider components List configProvider componentSize middle 1`]
class="config-avatar config-avatar-circle config-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>
@ -17535,7 +17535,7 @@ exports[`ConfigProvider components List configProvider virtual and dropdownMatch
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>
@ -17587,7 +17587,7 @@ exports[`ConfigProvider components List normal 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>
@ -17639,7 +17639,7 @@ exports[`ConfigProvider components List prefixCls 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
</div>

View File

@ -40,6 +40,7 @@ import Skeleton from '../../skeleton';
import type { SliderTooltipProps } from '../../slider';
import Slider from '../../slider';
// eslint-disable-next-line import/no-named-as-default
import { render } from '../../../tests/utils';
import Spin from '../../spin';
import Statistic from '../../statistic';
import Steps from '../../steps';
@ -55,7 +56,6 @@ import Transfer from '../../transfer';
import Tree from '../../tree';
import TreeSelect from '../../tree-select';
import Upload from '../../upload';
import { render } from '../../../tests/utils';
dayjs.extend(customParseFormat);
jest.mock('rc-util/lib/Portal');
@ -351,7 +351,7 @@ describe('ConfigProvider', () => {
<List.Item {...props}>
<List.Item.Meta
{...props}
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
avatar={<Avatar src="https://joesch.moe/api/v1/random" />}
title="Ant Design"
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>

View File

@ -0,0 +1,112 @@
import React from 'react';
import BehaviorMap from '../../../.dumi/theme/common/BehaviorMap';
const BehaviorPattern = () => (
<BehaviorMap
data={{
id: '200000004',
label: '选择(输入)日期数据',
children: [
{
id: '500000061',
label: '选择时间点',
targetType: 'mvp',
children: [
{
id: '707000085',
label: '选择某天',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-date',
},
{
id: '707000086',
label: '选择某周',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-week',
},
{
id: '707000087',
label: '选择某月',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-month',
},
{
id: '707000088',
label: '选择某季度',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-quarter',
},
{
id: '707000089',
label: '选择某年',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-year',
},
{
id: '707000090',
label: '选择某时间',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-time',
},
],
},
{
id: '200000005',
label: '选择时间段',
targetType: 'mvp',
children: [
{
id: '7070000851',
label: '选择某天至某天',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-date-range',
},
{
id: '7070000861',
label: '选择某周至某周',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-week-range',
},
{
id: '7070000871',
label: '选择某月至某月',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-month-range',
},
{
id: '7070000881',
label: '选择某季度至某季度',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-quarter-range',
},
{
id: '7070000891',
label: '选择某年至某年',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-year-range',
},
{
id: '7070000901',
label: '选择某时间至某时间',
link: 'components-date-picker-index-tab-design-zh-cn-demo-pick-time-range',
},
],
},
{
id: '200000006',
label: '快捷选择日期数据',
targetType: 'extension',
children: [
{
id: '70700008912',
label: '快捷选择时间点',
link: 'components-date-picker-index-tab-design-zh-cn-demo-preset-time',
},
{
id: '70700009012',
label: '快捷选择时间段',
link: 'components-date-picker-index-tab-design-zh-cn-demo-preset-range',
},
],
},
{
id: '200000007',
label: '查看日期附属信息',
targetType: 'extension',
link: 'components-date-picker-index-tab-design-zh-cn-demo-date-extra-info',
},
],
}}
/>
);
export default BehaviorPattern;

View File

@ -0,0 +1,120 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
import type { Dayjs } from 'dayjs';
import { createStyles, css } from 'antd-style';
import classNames from 'classnames';
const { _InternalPanelDoNotUseOrYouWillBeFired: PureDatePicker } = DatePicker;
const useStyle = createStyles(({ token }) => ({
weekendCell: css`
color: #ff4d4f40;
.ant-picker-cell-in-view & {
color: #ff4d4f;
}
`,
detailedCell: css`
width: 40px;
height: 40px !important;
`,
detailedPicker: css`
.ant-picker-date-panel {
width: auto;
.ant-picker-content {
width: auto;
}
}
`,
extraInfo: css`
font-size: 12px;
line-height: 12px;
transform: scale(${10 / 12});
color: ${token.colorTextQuaternary};
.ant-picker-cell-in-view & {
color: ${token.colorTextSecondary};
}
.ant-picker-cell-selected & {
color: #fff;
}
`,
add: css`
color: #ff4d4f80;
.ant-picker-cell-in-view & {
color: #ff4d4f;
}
.ant-picker-cell-selected & {
color: #fff;
}
`,
minus: css`
color: #52C41A80;
.ant-picker-cell-in-view & {
color: #52C41A;
}
.ant-picker-cell-selected & {
color: #fff;
}
`,
}));
const seeds = Array(30)
.fill(1)
.map(() => Math.random());
const getSales = (date: Dayjs) => Math.floor(seeds[date.date() % 30] * 10000);
const getData = (date: Dayjs) => (Math.floor(seeds[date.date() % 30] * 10000) - 5000) / 5000;
const Demo: FC = () => {
const { styles } = useStyle();
const dateRender = (current: Dayjs) => (
<div
className={classNames(
'ant-picker-cell-inner',
[6, 0].includes(current.day()) && styles.weekendCell,
)}
>
{current.date()}
</div>
);
const saleDateRender = (current: Dayjs) => (
<div className={classNames('ant-picker-cell-inner', styles.detailedCell)}>
{current.date()}
<div className={styles.extraInfo}>{getSales(current)}</div>
</div>
);
const dataDateRender = (current: Dayjs) => {
const data = getData(current);
return (
<div className={classNames('ant-picker-cell-inner', styles.detailedCell)}>
{current.date()}
<div className={classNames(styles.extraInfo, data > 0 ? styles.add : styles.minus)}>
{data.toFixed(2)}%
</div>
</div>
);
};
return (
<div style={{ width: '100%' }}>
<div style={{ color: 'rgba(0,0,0,0.45)', marginBottom: 32 }}></div>
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 40 }}>
<PureDatePicker dateRender={dateRender} popupClassName={styles.detailedPicker} />
</div>
<div style={{ color: 'rgba(0,0,0,0.45)', marginBottom: 32 }}></div>
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 40 }}>
<PureDatePicker dateRender={saleDateRender} popupClassName={styles.detailedPicker} />
</div>
<div style={{ color: 'rgba(0,0,0,0.45)', marginBottom: 32 }}></div>
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 40 }}>
<PureDatePicker dateRender={dataDateRender} popupClassName={styles.detailedPicker} />
</div>
</div>
);
};
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalRangePanelDoNotUseOrYouWillBeFired: PureRangePicker } = DatePicker;
const Demo: FC = () => <PureRangePicker />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalPanelDoNotUseOrYouWillBeFired: PureDatePicker } = DatePicker;
const Demo: FC = () => <PureDatePicker />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalRangePanelDoNotUseOrYouWillBeFired: PureRangePicker } = DatePicker;
const Demo: FC = () => <PureRangePicker picker="month" />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalPanelDoNotUseOrYouWillBeFired: PureDatePicker } = DatePicker;
const Demo: FC = () => <PureDatePicker picker="month" />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalRangePanelDoNotUseOrYouWillBeFired: PureRangePicker } = DatePicker;
const Demo: FC = () => <PureRangePicker picker="quarter" />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalPanelDoNotUseOrYouWillBeFired: PureDatePicker } = DatePicker;
const Demo: FC = () => <PureDatePicker picker="quarter" />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalRangePanelDoNotUseOrYouWillBeFired: PureRangePicker } = DatePicker;
const Demo: FC = () => <PureRangePicker showTime />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalPanelDoNotUseOrYouWillBeFired: PureDatePicker } = DatePicker;
const Demo: FC = () => <PureDatePicker showTime />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalRangePanelDoNotUseOrYouWillBeFired: PureRangePicker } = DatePicker;
const Demo: FC = () => <PureRangePicker picker="week" />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalPanelDoNotUseOrYouWillBeFired: PureDatePicker } = DatePicker;
const Demo: FC = () => <PureDatePicker picker="week" />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalRangePanelDoNotUseOrYouWillBeFired: PureRangePicker } = DatePicker;
const Demo: FC = () => <PureRangePicker picker="year" />;
export default Demo;

View File

@ -0,0 +1,9 @@
import type { FC } from 'react';
import React from 'react';
import { DatePicker } from 'antd';
const { _InternalPanelDoNotUseOrYouWillBeFired: PureDatePicker } = DatePicker;
const Demo: FC = () => <PureDatePicker picker="year" />;
export default Demo;

View File

@ -0,0 +1,18 @@
import React from 'react';
import { DatePicker } from 'antd';
import dayjs from 'dayjs';
const { _InternalRangePanelDoNotUseOrYouWillBeFired: PureRangePicker } = DatePicker;
const App: React.FC = () => (
<PureRangePicker
presets={[
{ label: 'Last 7 Days', value: [dayjs().add(-7, 'd'), dayjs()] },
{ label: 'Last 14 Days', value: [dayjs().add(-14, 'd'), dayjs()] },
{ label: 'Last 30 Days', value: [dayjs().add(-30, 'd'), dayjs()] },
{ label: 'Last 90 Days', value: [dayjs().add(-90, 'd'), dayjs()] },
]}
/>
);
export default App;

View File

@ -0,0 +1,17 @@
import React from 'react';
import { DatePicker } from 'antd';
import dayjs from 'dayjs';
const { _InternalPanelDoNotUseOrYouWillBeFired: PureDatePicker } = DatePicker;
const App: React.FC = () => (
<PureDatePicker
presets={[
{ label: 'Yesterday', value: dayjs().add(-1, 'd') },
{ label: 'Last Week', value: dayjs().add(-7, 'd') },
{ label: 'Last Month', value: dayjs().add(-1, 'month') },
]}
/>
);
export default App;

View File

@ -0,0 +1,39 @@
## 组件定义
DatePicker 的本质是选择(输入)日期型数据。
<code src="./design/behavior-pattern.tsx" inline></code>
## 基础使用
<code src="./design/demo/pick-date.tsx" description="用于具体日期的选择。用户仅需要输入非常具体的日期信息时使用。">选择某天</code>
<code src="./design/demo/pick-week.tsx" description="用于周的选择。用户仅需输入年份 + 周信息时使用。">选择某周</code>
<code src="./design/demo/pick-month.tsx" description="用于月份的选择。用户仅需输入年份 + 月份信息时使用。">选择某月</code>
<code src="./design/demo/pick-quarter.tsx" description="用于季度的选择。用户仅需输入年份 + 季度信息时使用。">选择某季度</code>
<code src="./design/demo/pick-year.tsx" description="用于年的选择。用户仅需输入年份时使用。">选择某年</code>
<code src="./design/demo/pick-time.tsx" description="用于具体时刻的选择。用户需输入年份+月份+日期+时间信息时使用。">选择某时刻</code>
<code src="./design/demo/pick-date-range.tsx" description="用于具体日期范围的选择。">选择某天至某天</code>
<code src="./design/demo/pick-week-range.tsx" description="用于周范围的选择。">选择某周至某周</code>
<code src="./design/demo/pick-month-range.tsx" description="用于月范围的选择。">选择某月至某月</code>
<code src="./design/demo/pick-quarter-range.tsx" description="用于季度范围的选择。">选择某季度至某季度</code>
<code src="./design/demo/pick-year-range.tsx" description="用于年范围的选择。">选择某年至某年</code>
<code src="./design/demo/pick-time-range.tsx" description="用于具体时刻范围的选择。">选择某时刻至某时刻</code>
## 交互变体
<code src="./design/demo/preset-time.tsx" description="通过面板左侧区域提供的预置项,帮助用户快速完成时间点的选择。" tip="根据希克定律建议快捷选项的个数不超过8个。">快捷选择时间点</code>
<code src="./design/demo/preset-range.tsx" description="通过面板左侧区域提供的预置项,帮助用户快速完成时间段的选择。" tip="根据希克定律建议快捷选项的个数不超过8个。">快捷选择时间段</code>
<code src="./design/demo/date-extra-info.tsx" description="通过定义日期单元格内容及样式,为用户展示更多业务场景相关信息作为选择参考。">查看日期附属信息</code>

View File

@ -2,14 +2,13 @@
category: Components
group: Data Entry
title: DatePicker
description: To select or input a date.
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*xXA9TJ8BTioAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3OpRQKcygo8AAAAAAAAAAAAADrJ8AQ/original
demo:
cols: 2
---
To select or input a date.
## When To Use
By clicking the input box, you can select a date from a popup calendar.

View File

@ -19,7 +19,10 @@ const DatePicker = generatePicker<Dayjs>(dayjsGenerateConfig);
/* istanbul ignore next */
const PurePanel = genPurePanel(DatePicker, 'picker');
(DatePicker as any)._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;
const PureRangePanel = genPurePanel(DatePicker.RangePicker, 'picker');
(DatePicker as any)._InternalRangePanelDoNotUseOrYouWillBeFired = PureRangePanel;
export default DatePicker as typeof DatePicker & {
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
_InternalRangePanelDoNotUseOrYouWillBeFired: typeof PureRangePanel;
};

View File

@ -3,14 +3,13 @@ category: Components
group: 数据录入
title: DatePicker
subtitle: 日期选择框
description: 输入或选择日期的控件。
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*xXA9TJ8BTioAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3OpRQKcygo8AAAAAAAAAAAAADrJ8AQ/original
demo:
cols: 2
---
输入或选择日期的控件。
## 何时使用
当用户需要输入一个日期,可以点击标准输入框,弹出日期面板进行选择。

View File

@ -206,6 +206,8 @@ const genPickerCellInnerStyle = (token: SharedPickerToken): CSSObject => {
// Hover with in range
[`&-in-view${pickerCellCls}-in-range${pickerCellCls}-range-hover::before,
&-in-view${pickerCellCls}-in-range${pickerCellCls}-range-hover-start::before,
&-in-view${pickerCellCls}-in-range${pickerCellCls}-range-hover-end::before,
&-in-view${pickerCellCls}-range-start${pickerCellCls}-range-hover::before,
&-in-view${pickerCellCls}-range-end${pickerCellCls}-range-hover::before,
&-in-view${pickerCellCls}-range-start:not(${pickerCellCls}-range-start-single)${pickerCellCls}-range-hover-start::before,

View File

@ -1,6 +1,6 @@
import { TinyColor } from '@ctrl/tinycolor';
import * as React from 'react';
import { useMemo } from 'react';
import { TinyColor } from '@ctrl/tinycolor';
import { useToken } from '../theme/internal';
const Simple = () => {
@ -10,9 +10,13 @@ const Simple = () => {
const { borderColor, shadowColor, contentColor } = useMemo(
() => ({
borderColor: new TinyColor(colorFill).onBackground(colorBgContainer).toHexString(),
shadowColor: new TinyColor(colorFillTertiary).onBackground(colorBgContainer).toHexString(),
contentColor: new TinyColor(colorFillQuaternary).onBackground(colorBgContainer).toHexString(),
borderColor: new TinyColor(colorFill).onBackground(colorBgContainer).toHexShortString(),
shadowColor: new TinyColor(colorFillTertiary)
.onBackground(colorBgContainer)
.toHexShortString(),
contentColor: new TinyColor(colorFillQuaternary)
.onBackground(colorBgContainer)
.toHexShortString(),
}),
[colorFill, colorFillTertiary, colorFillQuaternary, colorBgContainer],
);

View File

@ -5526,7 +5526,7 @@ exports[`renders ./components/form/demo/disabled-input-debug.tsx extend context
class="ant-form-item-control-input-content"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-status-error"
class="ant-input-group-wrapper ant-input-group-wrapper-disabled ant-input-group-wrapper-status-error"
>
<span
class="ant-input-wrapper ant-input-group"

View File

@ -2999,7 +2999,7 @@ exports[`renders ./components/form/demo/disabled-input-debug.tsx correctly 1`] =
class="ant-form-item-control-input-content"
>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-status-error"
class="ant-input-group-wrapper ant-input-group-wrapper-disabled ant-input-group-wrapper-status-error"
>
<span
class="ant-input-wrapper ant-input-group"

View File

@ -20,9 +20,6 @@ const { TextArea } = Input;
const FormDisabledDemo: React.FC = () => {
const [componentDisabled, setComponentDisabled] = useState<boolean>(true);
const onFormLayoutChange = ({ disabled }: { disabled: boolean }) => {
setComponentDisabled(disabled);
};
return (
<>
@ -36,7 +33,6 @@ const FormDisabledDemo: React.FC = () => {
labelCol={{ span: 4 }}
wrapperCol={{ span: 14 }}
layout="horizontal"
onValuesChange={onFormLayoutChange}
disabled={componentDisabled}
style={{ maxWidth: 600 }}
>

View File

@ -2,6 +2,7 @@
category: Components
group: General
title: Icon
description: Semantic vector graphics.
toc: false
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*PdAYS7anRpoAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*xEDOTJx2DEkAAAAAAAAAAAAADrJ8AQ/original
@ -9,7 +10,9 @@ demo:
cols: 2
---
Semantic vector graphics. Before use icons, you need to install `@ant-design/icons` package:
## How to use
Before use icons, you need to install `@ant-design/icons` package:
```bash
npm install --save @ant-design/icons

View File

@ -1,6 +1,7 @@
---
category: Components
subtitle: 图标
description: 语义化的矢量图形。
group: 通用
title: Icon
toc: false
@ -10,7 +11,9 @@ demo:
cols: 2
---
语义化的矢量图形。使用图标组件,你需要安装 `@ant-design/icons` 图标组件包:
## 使用方法
使用图标组件,你需要安装 `@ant-design/icons` 图标组件包:
```bash
npm install --save @ant-design/icons

View File

@ -219,6 +219,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-group-wrapper-disabled`]: mergedDisabled,
},
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
hashId,

View File

@ -1,9 +1,9 @@
import type { CSSObject } from '@ant-design/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import type { GlobalToken } from '../../theme/interface';
import { clearFix, resetComponent } from '../../style';
import { genCompactItemStyle } from '../../style/compact-item';
import type { GlobalToken } from '../../theme/interface';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
export type InputToken<T extends GlobalToken = FullToken<'Input'>> = T & {
inputAffixPadding: number;
@ -669,7 +669,7 @@ const genAffixStyle: GenerateStyle<InputToken> = (token: InputToken) => {
};
const genGroupStyle: GenerateStyle<InputToken> = (token: InputToken) => {
const { componentCls, colorError, colorSuccess, borderRadiusLG, borderRadiusSM } = token;
const { componentCls, colorError, colorWarning, borderRadiusLG, borderRadiusSM } = token;
return {
[`${componentCls}-group`]: {
@ -711,9 +711,15 @@ const genGroupStyle: GenerateStyle<InputToken> = (token: InputToken) => {
},
},
'&-status-warning': {
[`${componentCls}-group-addon:last-child`]: {
color: colorSuccess,
borderColor: colorSuccess,
[`${componentCls}-group-addon`]: {
color: colorWarning,
borderColor: colorWarning,
},
},
'&-disabled': {
[`${componentCls}-group-addon`]: {
...genDisabledStyle(token),
},
},
},

View File

@ -8,8 +8,8 @@ describe('List Item Layout', () => {
{
key: 1,
href: 'https://ant.design',
title: `ant design`,
avatar: 'https://joeschmoe.io/api/v1/random',
title: 'ant design',
avatar: 'https://joesch.moe/api/v1/random',
description:
'Ant Design, a design language for background applications, is refined by Ant UED Team.',
content:

View File

@ -26,7 +26,7 @@ exports[`renders ./components/list/demo/basic.tsx extend context correctly 1`] =
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=0"
/>
</span>
</div>
@ -63,7 +63,7 @@ exports[`renders ./components/list/demo/basic.tsx extend context correctly 1`] =
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
</div>
@ -100,7 +100,7 @@ exports[`renders ./components/list/demo/basic.tsx extend context correctly 1`] =
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
</div>
@ -137,7 +137,7 @@ exports[`renders ./components/list/demo/basic.tsx extend context correctly 1`] =
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=3"
/>
</span>
</div>
@ -1360,7 +1360,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=0"
/>
</span>
</div>
@ -1397,7 +1397,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
</div>
@ -1434,7 +1434,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
</div>
@ -1471,7 +1471,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=3"
/>
</span>
</div>
@ -2072,7 +2072,7 @@ exports[`renders ./components/list/demo/vertical.tsx extend context correctly 1`
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=0"
/>
</span>
</div>
@ -2238,7 +2238,7 @@ exports[`renders ./components/list/demo/vertical.tsx extend context correctly 1`
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
</div>
@ -2404,7 +2404,7 @@ exports[`renders ./components/list/demo/vertical.tsx extend context correctly 1`
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
</div>

View File

@ -26,7 +26,7 @@ exports[`renders ./components/list/demo/basic.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=0"
/>
</span>
</div>
@ -63,7 +63,7 @@ exports[`renders ./components/list/demo/basic.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
</div>
@ -100,7 +100,7 @@ exports[`renders ./components/list/demo/basic.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
</div>
@ -137,7 +137,7 @@ exports[`renders ./components/list/demo/basic.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=3"
/>
</span>
</div>
@ -1360,7 +1360,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=0"
/>
</span>
</div>
@ -1397,7 +1397,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
</div>
@ -1434,7 +1434,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
</div>
@ -1471,7 +1471,7 @@ Array [
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=3"
/>
</span>
</div>
@ -2072,7 +2072,7 @@ exports[`renders ./components/list/demo/vertical.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=0"
/>
</span>
</div>
@ -2238,7 +2238,7 @@ exports[`renders ./components/list/demo/vertical.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
</div>
@ -2404,7 +2404,7 @@ exports[`renders ./components/list/demo/vertical.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
</div>

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Avatar, List } from 'antd';
import React from 'react';
const data = [
{
@ -20,10 +20,10 @@ const App: React.FC = () => (
<List
itemLayout="horizontal"
dataSource={data}
renderItem={(item) => (
renderItem={(item, index) => (
<List.Item>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
avatar={<Avatar src={`https://joesch.moe/api/v1/random?key=${index}`} />}
title={<a href="https://ant.design">{item.title}</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>

View File

@ -1,5 +1,5 @@
import { Avatar, List, Radio, Space } from 'antd';
import React, { useState } from 'react';
import { Avatar, List, Space, Radio } from 'antd';
type PaginationPosition = 'top' | 'bottom' | 'both';
@ -30,7 +30,7 @@ const App: React.FC = () => {
return (
<>
<Space direction='vertical' style={{ marginBottom: '20px' }} size="middle">
<Space direction="vertical" style={{ marginBottom: '20px' }} size="middle">
<Space>
<span>Pagination Position:</span>
<Radio.Group
@ -67,10 +67,10 @@ const App: React.FC = () => {
<List
pagination={{ position, align }}
dataSource={data}
renderItem={(item) => (
renderItem={(item, index) => (
<List.Item>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
avatar={<Avatar src={`https://joesch.moe/api/v1/random?key=${index}`} />}
title={<a href="https://ant.design">{item.title}</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>

View File

@ -1,11 +1,11 @@
import React from 'react';
import { LikeOutlined, MessageOutlined, StarOutlined } from '@ant-design/icons';
import { Avatar, List, Space } from 'antd';
import React from 'react';
const data = Array.from({ length: 23 }).map((_, i) => ({
href: 'https://ant.design',
title: `ant design part ${i}`,
avatar: 'https://joeschmoe.io/api/v1/random',
avatar: `https://joesch.moe/api/v1/random?key=${i}`,
description:
'Ant Design, a design language for background applications, is refined by Ant UED Team.',
content:

View File

@ -351,7 +351,7 @@ export default genComponentStyleHook(
const listToken = mergeToken<ListToken>(token, {
listBorderedCls: `${token.componentCls}-bordered`,
minHeight: token.controlHeightLG,
listItemPadding: `${token.paddingContentVertical}px ${token.paddingContentHorizontalLG}px`,
listItemPadding: `${token.paddingContentVertical}px 0`,
listItemPaddingSM: `${token.paddingContentVerticalSM}px ${token.paddingContentHorizontal}px`,
listItemPaddingLG: `${token.paddingContentVerticalLG}px ${token.paddingContentHorizontalLG}px`,
});

View File

@ -330,7 +330,7 @@ exports[`renders ./components/segmented/demo/custom.tsx extend context correctly
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
<div>

View File

@ -330,7 +330,7 @@ exports[`renders ./components/segmented/demo/custom.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random"
/>
</span>
<div>

View File

@ -9,7 +9,7 @@ const App: React.FC = () => (
{
label: (
<div style={{ padding: 4 }}>
<Avatar src="https://joeschmoe.io/api/v1/random" />
<Avatar src="https://joesch.moe/api/v1/random" />
<div>User 1</div>
</div>
),

View File

@ -1,7 +1,7 @@
// TODO: 4.0 - codemod should help to change `filterOption` to support node props.
import classNames from 'classnames';
import type { BaseSelectRef, SelectProps as RcSelectProps } from 'rc-select';
import RcSelect, { OptGroup, Option } from 'rc-select';
import type { SelectProps as RcSelectProps, BaseSelectRef } from 'rc-select';
import type { OptionProps } from 'rc-select/lib/Option';
import type { BaseOptionType, DefaultOptionType } from 'rc-select/lib/Select';
import omit from 'rc-util/lib/omit';
@ -18,10 +18,10 @@ import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import getIcons from './utils/iconUtil';
import useStyle from './style';
import { useCompactItemContext } from '../space/Compact';
import genPurePanel from '../_util/PurePanel';
import warning from '../_util/warning';
import { useCompactItemContext } from '../space/Compact';
import useStyle from './style';
type RawValue = string | number;
@ -182,13 +182,11 @@ const InternalSelect = <OptionType extends BaseOptionType | DefaultOptionType =
);
// ===================== Placement =====================
const getPlacement = () => {
const getPlacement = (): SelectCommonPlacement => {
if (placement !== undefined) {
return placement;
}
return direction === 'rtl'
? ('bottomRight' as SelectCommonPlacement)
: ('bottomLeft' as SelectCommonPlacement);
return direction === 'rtl' ? 'bottomRight' : 'bottomLeft';
};
// ====================== Warning ======================

View File

@ -11,7 +11,7 @@ interface IconTextProps {
const listData = Array.from({ length: 3 }).map((_, i) => ({
href: 'https://ant.design',
title: `ant design part ${i + 1}`,
avatar: 'https://joeschmoe.io/api/v1/random',
avatar: `https://joesch.moe/api/v1/random?key=${i}`,
description:
'Ant Design, a design language for background applications, is refined by Ant UED Team.',
content:

View File

@ -1,9 +1,9 @@
import type { CSSObject } from '@ant-design/cssinjs';
import type * as React from 'react';
import { TinyColor } from '@ctrl/tinycolor';
import type * as React from 'react';
import { resetComponent } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../style';
// Direction naming standard:
// Horizontal base:
@ -214,7 +214,7 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
height: token.handleSize,
boxShadow: `0 0 0 ${token.handleLineWidth}px ${new TinyColor(token.colorTextDisabled)
.onBackground(token.colorBgContainer)
.toHexString()}`,
.toHexShortString()}`,
insetInlineStart: 0,
insetBlockStart: 0,
},

View File

@ -875,7 +875,7 @@ exports[`renders ./components/steps/demo/inline.tsx extend context correctly 1`]
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=0"
/>
</span>
</div>
@ -1082,7 +1082,7 @@ exports[`renders ./components/steps/demo/inline.tsx extend context correctly 1`]
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
</div>
@ -1289,7 +1289,7 @@ exports[`renders ./components/steps/demo/inline.tsx extend context correctly 1`]
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
</div>
@ -1496,7 +1496,7 @@ exports[`renders ./components/steps/demo/inline.tsx extend context correctly 1`]
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=3"
/>
</span>
</div>

View File

@ -751,7 +751,7 @@ exports[`renders ./components/steps/demo/inline.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=0"
/>
</span>
</div>
@ -901,7 +901,7 @@ exports[`renders ./components/steps/demo/inline.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=1"
/>
</span>
</div>
@ -1051,7 +1051,7 @@ exports[`renders ./components/steps/demo/inline.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=2"
/>
</span>
</div>
@ -1201,7 +1201,7 @@ exports[`renders ./components/steps/demo/inline.tsx correctly 1`] = `
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
src="https://joesch.moe/api/v1/random?key=3"
/>
</span>
</div>

View File

@ -1,6 +1,6 @@
import React from 'react';
import type { StepsProps } from 'antd';
import { Steps, List, Avatar } from 'antd';
import { Avatar, List, Steps } from 'antd';
import React from 'react';
const data = [
{
@ -42,10 +42,10 @@ const App: React.FC = () => (
<List
itemLayout="horizontal"
dataSource={data}
renderItem={(item) => (
renderItem={(item, index) => (
<List.Item>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
avatar={<Avatar src={`https://joesch.moe/api/v1/random?key=${index}`} />}
title={<a href="https://ant.design">{item.title}</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>

View File

@ -142,7 +142,6 @@ const genFilterStyle: GenerateStyle<TableToken> = (token) => {
justifyContent: 'space-between',
padding: `${paddingXS - lineWidth}px ${paddingXS}px`,
overflow: 'hidden',
backgroundColor: 'inherit',
borderTop: tableBorder,
},
},

View File

@ -1,5 +1,6 @@
import type { CSSObject } from '@ant-design/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import { clearFix, resetComponent } from '../../style';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import genBorderedStyle from './bordered';
@ -16,7 +17,6 @@ import genSizeStyle from './size';
import genSorterStyle from './sorter';
import genStickyStyle from './sticky';
import genSummaryStyle from './summary';
import { clearFix, resetComponent } from '../../style';
export interface ComponentToken {}
@ -257,14 +257,14 @@ export default genComponentStyleHook('Table', (token) => {
const colorFillSecondarySolid = new TinyColor(colorFillSecondary)
.onBackground(colorBgContainer)
.toHexString();
.toHexShortString();
const colorFillContentSolid = new TinyColor(colorFillContent)
.onBackground(colorBgContainer)
.toHexString();
.toHexShortString();
const colorFillAlterSolid = new TinyColor(colorFillAlter)
.onBackground(colorBgContainer)
.toHexString();
.toHexShortString();
const tableToken = mergeToken<TableToken>(token, {
tableFontSize: fontSize,

View File

@ -50,7 +50,7 @@ export interface SeedToken extends PresetColorType {
/**
* @nameZH
* @nameEN Seed Text Color
* @desc v5 **使 Seed Token**
* @desc v5 使 Seed Token
* @descEN Used to derive the base variable of the text color gradient. In v5, we added a layer of text color derivation algorithm to produce gradient variables of text color gradient. But please do not use this Seed Token directly in the code!
*/
colorTextBase: string;
@ -58,7 +58,7 @@ export interface SeedToken extends PresetColorType {
/**
* @nameZH
* @nameEN Seed Background Color
* @desc v5 **使 Seed Token**
* @desc v5 使 Seed Token
* @descEN Used to derive the base variable of the background color gradient. In v5, we added a layer of background color derivation algorithm to produce map token of background color. But PLEASE DO NOT USE this Seed Token directly in the code!
*/
colorBgBase: string;

View File

@ -185,6 +185,10 @@ const genPictureCardStyle: GenerateStyle<UploadToken> = (token) => {
fontSize: fontSizeLG,
cursor: 'pointer',
transition: `all ${token.motionDurationSlow}`,
svg: {
verticalAlign: 'baseline',
},
},
},

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "5.2.1",
"version": "5.2.2",
"description": "An enterprise-class UI design language and React components implementation",
"title": "Ant Design",
"keywords": [
@ -76,9 +76,9 @@
"lint:deps": "antd-tools run deps-lint",
"lint:md": "remark . -f -q",
"lint:style": "ts-node --project tsconfig.node.json scripts/check-cssinjs.js",
"lint:script": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:script": "eslint . --ext .js,.jsx,.ts,.tsx --cache",
"pre-publish": "npm run test-all -- --skip-build",
"prettier": "prettier -c --write **/*",
"prettier": "prettier -c --write **/* --cache",
"pub": "npm run version && npm run collect-token-statistic && npm run token-meta && antd-tools run pub",
"rome:format": "rome format --write .",
"prepublishOnly": "antd-tools run guard",
@ -112,7 +112,7 @@
"@ant-design/icons": "^5.0.0",
"@ant-design/react-slick": "~1.0.0",
"@babel/runtime": "^7.18.3",
"@ctrl/tinycolor": "^3.4.0",
"@ctrl/tinycolor": "^3.6.0",
"@rc-component/mutate-observer": "^1.0.0",
"@rc-component/tour": "~1.6.0",
"classnames": "^2.2.6",
@ -158,6 +158,7 @@
},
"devDependencies": {
"@ant-design/tools": "^17.0.0",
"@antv/g6": "^4.8.5",
"@babel/eslint-plugin": "^7.19.1",
"@dnd-kit/core": "^6.0.7",
"@dnd-kit/sortable": "^7.0.2",
@ -169,9 +170,9 @@
"@qixian.cs/github-contributors-list": "^1.0.3",
"@size-limit/file": "^8.1.0",
"@stackblitz/sdk": "^1.3.0",
"@testing-library/dom": "^8.17.1",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^13.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.2",
"@types/gtag.js": "^0.0.12",
"@types/jest": "^29.0.0",
@ -196,6 +197,7 @@
"@typescript-eslint/eslint-plugin": "^5.40.0",
"@typescript-eslint/parser": "^5.40.0",
"antd-img-crop": "^4.2.8",
"antd-style": "^2.0.2",
"antd-token-previewer": "^1.1.0-21",
"chalk": "^4.0.0",
"cheerio": "1.0.0-rc.12",
@ -221,6 +223,7 @@
"fs-extra": "^11.0.0",
"gh-pages": "^5.0.0",
"glob": "^8.0.1",
"html2sketch": "^1.0.0",
"http-server": "^14.0.0",
"husky": "^8.0.1",
"identity-obj-proxy": "^3.0.0",

View File

@ -1,3 +1,4 @@
/* eslint-disable no-console */
const React = require('react');
const util = require('util');