chore: merge master into feature

This commit is contained in:
栗嘉男 2024-01-11 14:43:16 +08:00
commit b241f68a33
48 changed files with 376 additions and 558 deletions

View File

@ -91,7 +91,7 @@ const ThemeColorPicker: React.FC<ColorPickerProps> = ({ value, onChange, id }) =
}, [value]);
return (
<Flex gap="large" align="center">
<Flex gap="large" align="center" wrap="wrap">
<Input
value={typeof value === 'string' ? value : value?.toHexString()}
onChange={(event) => onChange?.(event.target.value)}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,99 +0,0 @@
import { DownOutlined } from '@ant-design/icons';
import { createStyles } from 'antd-style';
import { FormattedMessage } from 'dumi';
import React from 'react';
import classnames from 'classnames';
import type { MenuProps } from 'antd';
import { Button, Dropdown } from 'antd';
import type { SharedProps } from './interface';
const useStyle = createStyles(({ css, token }) => ({
smallStyle: css`
font-size: 12px;
color: #777;
margin-left: 0.3em;
`,
down: css`
color: ${token.colorTextQuaternary};
`,
downOutlined: css`
font-size: 9px;
margin: -1px 0 0 2px;
vertical-align: middle;
`,
downOutlinedRTL: css`
font-size: 9px;
margin: -1px 2px 0 0;
vertical-align: middle;
`,
}));
const Community: React.FC = () => {
const { styles } = useStyle();
return (
<span className={styles.smallStyle}>
(<FormattedMessage id="app.implementation.community" />)
</span>
);
};
export const getEcosystemGroup = (): Exclude<MenuProps['items'], undefined> => [
{
label: (
<a href="https://charts.ant.design" target="_blank" rel="noopener noreferrer">
<FormattedMessage id="app.header.menu.charts" />
</a>
),
key: 'charts',
},
{
label: (
<a href="http://pro.ant.design" target="_blank" rel="noopener noreferrer">
<FormattedMessage id="app.header.menu.pro.v4" />
</a>
),
key: 'pro',
},
{
label: (
<a href="http://procomponents.ant.design" target="_blank" rel="noopener noreferrer">
<FormattedMessage id="app.header.menu.pro.components" />
</a>
),
key: 'procomponents',
},
{
label: (
<a href="http://ng.ant.design" target="_blank" rel="noopener noreferrer">
Ant Design of Angular
<Community />
</a>
),
key: 'ng',
},
{
label: (
<a href="http://antdv.com" target="_blank" rel="noopener noreferrer">
Ant Design of Vue
<Community />
</a>
),
key: 'vue',
},
];
const More: React.FC<SharedProps> = ({ isRTL }) => {
const { styles } = useStyle();
return (
<Dropdown menu={{ items: getEcosystemGroup() }} placement="bottomRight">
<Button size="small">
<FormattedMessage id="app.header.menu.more" />
<DownOutlined
className={classnames(isRTL ? styles.downOutlinedRTL : styles.downOutlined, styles.down)}
/>
</Button>
</Dropdown>
);
};
export default More;

View File

@ -4,7 +4,6 @@ import { MenuOutlined } from '@ant-design/icons';
import { createStyles, css } from 'antd-style';
import type { MenuProps } from 'antd';
import { Menu } from 'antd';
import { getEcosystemGroup } from './More';
import * as utils from '../../utils';
import type { SharedProps } from './interface';
import useLocale from '../../../hooks/useLocale';
@ -170,7 +169,6 @@ export default ({
onClick: onDirectionChange,
key: 'switch-direction',
},
...getEcosystemGroup(),
];
if (isMobile) {

View File

@ -16,7 +16,6 @@ import type { SiteContextProps } from '../SiteContext';
import SiteContext from '../SiteContext';
import type { SharedProps } from './interface';
import Logo from './Logo';
import More from './More';
import Navigation from './Navigation';
import SwitchBtn from './SwitchBtn';
@ -309,7 +308,6 @@ const Header: React.FC = () => {
getPopupContainer={(trigger) => trigger.parentNode}
options={versionOptions}
/>,
<More key="more" {...sharedProps} />,
<SwitchBtn
key="lang"
onClick={onLangChange}

View File

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

View File

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

View File

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

View File

@ -25,4 +25,4 @@ updates:
update-types: ["major", "minor"]
dev-dependencies:
dependency-type: "development"
update-types: ["major", "minor"]
update-types: ["major"]

View File

@ -312,74 +312,6 @@ exports[`renders components/button/demo/chinese-chars-loading.tsx extend context
exports[`renders components/button/demo/chinese-chars-loading.tsx extend context correctly 2`] = `[]`;
exports[`renders components/button/demo/component-token.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-gap-small ant-flex-vertical"
>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-text"
type="button"
>
<span>
TEXT
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
CONTAINED
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
OUTLINED
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-text"
disabled=""
type="button"
>
<span>
TEXT
</span>
</button>
<button
class="ant-btn ant-btn-primary"
disabled=""
type="button"
>
<span>
CONTAINED
</span>
</button>
<button
class="ant-btn ant-btn-default"
disabled=""
type="button"
>
<span>
OUTLINED
</span>
</button>
</div>
</div>
`;
exports[`renders components/button/demo/component-token.tsx extend context correctly 2`] = `[]`;
exports[`renders components/button/demo/danger.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"

View File

@ -301,72 +301,6 @@ exports[`renders components/button/demo/chinese-chars-loading.tsx correctly 1`]
</div>
`;
exports[`renders components/button/demo/component-token.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-gap-small ant-flex-vertical"
>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-text"
type="button"
>
<span>
TEXT
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
CONTAINED
</span>
</button>
<button
class="ant-btn ant-btn-default"
type="button"
>
<span>
OUTLINED
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-text"
disabled=""
type="button"
>
<span>
TEXT
</span>
</button>
<button
class="ant-btn ant-btn-primary"
disabled=""
type="button"
>
<span>
CONTAINED
</span>
</button>
<button
class="ant-btn ant-btn-default"
disabled=""
type="button"
>
<span>
OUTLINED
</span>
</button>
</div>
</div>
`;
exports[`renders components/button/demo/danger.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"

View File

@ -1,3 +1,5 @@
import { extendTest } from '../../../tests/shared/demoTest';
extendTest('button');
extendTest('button', {
skip: ['component-token.tsx'],
});

View File

@ -1,3 +1,5 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('button');
demoTest('button', {
skip: ['component-token.tsx'],
});

View File

@ -18,6 +18,7 @@ const App: React.FC = () => (
borderRadius: 4,
colorTextDisabled: 'rgba(0, 0, 0, 0.26)',
colorBgContainerDisabled: 'rgba(0, 0, 0, 0.12)',
contentFontSizeSM: 12,
},
},
}}
@ -37,6 +38,15 @@ const App: React.FC = () => (
</Button>
<Button disabled>OUTLINED</Button>
</Flex>
<Flex wrap="wrap" gap="small">
<Button type="text" size="small">
TEXT
</Button>
<Button type="primary" size="small">
CONTAINED
</Button>
<Button size="small">OUTLINED</Button>
</Flex>
</Flex>
</ConfigProvider>
);

View File

@ -658,52 +658,37 @@ export const prepareToken: (token: Parameters<GenStyleFn<'Button'>>[0]) => Butto
return buttonToken;
};
export const prepareComponentToken: GetDefaultToken<'Button'> = (token) => {
const contentFontSize = token.fontSize;
const contentFontSizeSM = token.fontSize;
const contentFontSizeLG = token.fontSizeLG;
return {
fontWeight: 400,
defaultShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`,
primaryShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`,
dangerShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`,
primaryColor: token.colorTextLightSolid,
dangerColor: token.colorTextLightSolid,
borderColorDisabled: token.colorBorder,
defaultGhostColor: token.colorBgContainer,
ghostBg: 'transparent',
defaultGhostBorderColor: token.colorBgContainer,
paddingInline: token.paddingContentHorizontal - token.lineWidth,
paddingInlineLG: token.paddingContentHorizontal - token.lineWidth,
paddingInlineSM: 8 - token.lineWidth,
paddingBlock: Math.max(
(token.controlHeight - contentFontSize * token.lineHeight) / 2 - token.lineWidth,
0,
),
paddingBlockSM: Math.max(
(token.controlHeightSM - contentFontSizeSM * token.lineHeight) / 2 - token.lineWidth,
0,
),
paddingBlockLG: Math.max(
(token.controlHeightLG - contentFontSizeLG * token.lineHeight) / 2 - token.lineWidth,
0,
),
onlyIconSize: token.fontSizeLG,
onlyIconSizeSM: token.fontSizeLG - 2,
onlyIconSizeLG: token.fontSizeLG + 2,
groupBorderColor: token.colorPrimaryHover,
linkHoverBg: 'transparent',
textHoverBg: token.colorBgTextHover,
defaultColor: token.colorText,
defaultBg: token.colorBgContainer,
defaultBorderColor: token.colorBorder,
defaultBorderColorDisabled: token.colorBorder,
contentFontSize,
contentFontSizeSM,
contentFontSizeLG,
};
};
export const prepareComponentToken: GetDefaultToken<'Button'> = (token) => ({
fontWeight: 400,
defaultShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`,
primaryShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`,
dangerShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`,
primaryColor: token.colorTextLightSolid,
dangerColor: token.colorTextLightSolid,
borderColorDisabled: token.colorBorder,
defaultGhostColor: token.colorBgContainer,
ghostBg: 'transparent',
defaultGhostBorderColor: token.colorBgContainer,
paddingInline: token.paddingContentHorizontal - token.lineWidth,
paddingInlineLG: token.paddingContentHorizontal - token.lineWidth,
paddingInlineSM: 8 - token.lineWidth,
paddingBlock: 0,
paddingBlockSM: 0,
paddingBlockLG: 0,
onlyIconSize: token.fontSizeLG,
onlyIconSizeSM: token.fontSizeLG - 2,
onlyIconSizeLG: token.fontSizeLG + 2,
groupBorderColor: token.colorPrimaryHover,
linkHoverBg: 'transparent',
textHoverBg: token.colorBgTextHover,
defaultColor: token.colorText,
defaultBg: token.colorBgContainer,
defaultBorderColor: token.colorBorder,
defaultBorderColorDisabled: token.colorBorder,
contentFontSize: token.fontSize,
contentFontSizeSM: token.fontSize,
contentFontSizeLG: token.fontSizeLG,
});
export default genStyleHooks(
'Button',
@ -734,5 +719,19 @@ export default genStyleHooks(
unitless: {
fontWeight: true,
},
format: (token) => ({
paddingBlock: Math.max(
(token.controlHeight - token.contentFontSize * token.lineHeight) / 2 - token.lineWidth,
0,
),
paddingBlockSM: Math.max(
(token.controlHeightSM - token.contentFontSizeSM * token.lineHeight) / 2 - token.lineWidth,
0,
),
paddingBlockLG: Math.max(
(token.controlHeightLG - token.contentFontSizeLG * token.lineHeight) / 2 - token.lineWidth,
0,
),
}),
},
);

View File

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

View File

@ -50,7 +50,7 @@ export interface ThemeConfig {
cssVar?:
| {
/**
* Prefix for css variable, default to `antd`.
* Prefix for css variable, default to `ant`.
*/
prefix?: string;
/**

View File

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

View File

@ -1,3 +1,5 @@
import { extendTest } from '../../../tests/shared/demoTest';
extendTest('dropdown');
extendTest('dropdown', {
skip: ['sub-menu-debug.tsx'],
});

View File

@ -3,6 +3,7 @@ import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
demoTest('dropdown', {
testRootProps: false,
skip: ['sub-menu-debug.tsx'],
});
rootPropsTest(

View File

@ -0,0 +1,7 @@
## zh-CN
传入的菜单里有多个层级。
## en-US
The menu has multiple levels.

View File

@ -0,0 +1,66 @@
import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Dropdown, Space } from 'antd';
const items: MenuProps['items'] = [
{
key: '1',
type: 'group',
label: 'Group title',
children: [
{
key: '1-1',
label: '1st menu item',
},
{
key: '1-2',
label: '2nd menu item',
},
],
},
{
key: '2',
label: 'sub menu',
children: [
{
key: '2-1',
label: '3rd menu item',
},
{
key: '2-2',
label: '4th menu item',
},
],
},
{
key: '3',
label: 'disabled sub menu',
disabled: true,
children: [
{
key: '3-1',
label: '5d menu item',
},
{
key: '3-2',
label: '6th menu item',
},
],
},
];
const App: React.FC = () => (
<div style={{ height: 200 }}>
<Dropdown menu={{ items, openKeys: ['2'] }} open autoAdjustOverflow={false}>
<a onClick={(e) => e.preventDefault()}>
<Space>
Cascading menu
<DownOutlined />
</Space>
</a>
</Dropdown>
</div>
);
export default App;

View File

@ -254,7 +254,7 @@ const Dropdown: CompoundedComponent = (props) => {
return (
<OverrideProvider
prefixCls={`${prefixCls}-menu`}
rootClassName={cssVarCls}
rootClassName={classNames(cssVarCls, rootCls)}
expandIcon={
<span className={`${prefixCls}-menu-submenu-arrow`}>
<RightOutlined className={`${prefixCls}-menu-submenu-arrow-icon`} />

View File

@ -27,6 +27,7 @@ When there are more than a few options to choose from, you can wrap them in a `D
<code src="./demo/dropdown-button.tsx">Button with dropdown menu</code>
<code src="./demo/custom-dropdown.tsx">Custom dropdown</code>
<code src="./demo/sub-menu.tsx">Cascading menu</code>
<code src="./demo/sub-menu-debug.tsx" debug>Cascading menu</code>
<code src="./demo/overlay-open.tsx">The way of hiding menu.</code>
<code src="./demo/context-menu.tsx">Context Menu</code>
<code src="./demo/loading.tsx">Loading</code>

View File

@ -31,6 +31,7 @@ demo:
<code src="./demo/dropdown-button.tsx">带下拉框的按钮</code>
<code src="./demo/custom-dropdown.tsx">扩展菜单</code>
<code src="./demo/sub-menu.tsx">多级菜单</code>
<code src="./demo/sub-menu-debug.tsx" debug>多级菜单</code>
<code src="./demo/overlay-open.tsx">菜单隐藏方式</code>
<code src="./demo/context-menu.tsx">右键菜单</code>
<code src="./demo/loading.tsx">加载中状态</code>

View File

@ -75,10 +75,6 @@ const resetForm = (token: AliasToken): CSSObject => ({
borderBottom: `${unit(token.lineWidth)} ${token.lineType} ${token.colorBorder}`,
},
label: {
fontSize: token.fontSize,
},
'input[type="search"]': {
boxSizing: 'border-box',
},

View File

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

View File

@ -1,16 +1,11 @@
import { unit } from '@ant-design/cssinjs';
import {
genBasicInputStyle,
genInputGroupStyle,
genPlaceholderStyle,
initInputToken,
} from '../../input/style';
import { resetComponent, resetIcon } from '../../style';
import { genCompactItemStyle } from '../../style/compact-item';
import type { GenerateStyle } from '../../theme/internal';
import { genStyleHooks, mergeToken } from '../../theme/internal';
import { unit } from '@ant-design/cssinjs';
import type { ComponentToken, InputNumberToken } from './token';
import { formatComponentToken, prepareComponentToken } from './token';
import {
genBorderlessStyle,
genFilledGroupStyle,
@ -18,6 +13,12 @@ import {
genOutlinedGroupStyle,
genOutlinedStyle,
} from '../../input/style/variants';
import { resetComponent, resetIcon } from '../../style';
import { genCompactItemStyle } from '../../style/compact-item';
import type { GenerateStyle } from '../../theme/internal';
import { genStyleHooks, mergeToken } from '../../theme/internal';
import type { ComponentToken, InputNumberToken } from './token';
import { formatComponentToken, prepareComponentToken } from './token';
export type { ComponentToken };
@ -463,7 +464,6 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
};
};
// ============================== Export ==============================
export default genStyleHooks(
'InputNumber',
(token) => {

View File

@ -1,7 +1,8 @@
import { TinyColor } from '@ctrl/tinycolor';
import type { SharedComponentToken, SharedInputToken } from '../../input/style/token';
import { initComponentToken } from '../../input/style/token';
import type { FullToken, GetDefaultToken, FormatComponentToken } from '../../theme/internal';
import { TinyColor } from '@ctrl/tinycolor';
import type { FormatComponentToken, FullToken, GetDefaultToken } from '../../theme/internal';
export interface ComponentToken extends SharedComponentToken {
/**
@ -75,6 +76,5 @@ export const prepareComponentToken: GetDefaultToken<'InputNumber'> = (token) =>
});
export const formatComponentToken: FormatComponentToken<'InputNumber'> = (token) => ({
...token,
handleOpacity: token.handleVisible === true ? 1 : 0,
});

View File

@ -1,10 +1,11 @@
import * as React from 'react';
import { useRef, useState } from 'react';
import EyeInvisibleOutlined from '@ant-design/icons/EyeInvisibleOutlined';
import EyeOutlined from '@ant-design/icons/EyeOutlined';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
import { useRef, useState } from 'react';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
@ -14,23 +15,25 @@ import Input from './Input';
const defaultIconRender = (visible: boolean): React.ReactNode =>
visible ? <EyeOutlined /> : <EyeInvisibleOutlined />;
type VisibilityToggle = {
interface VisibilityToggle {
visible?: boolean;
onVisibleChange?: (visible: boolean) => void;
};
}
export interface PasswordProps extends InputProps {
readonly inputPrefixCls?: string;
readonly action?: string;
readonly action?: 'click' | 'hover';
visibilityToggle?: boolean | VisibilityToggle;
iconRender?: (visible: boolean) => React.ReactNode;
}
const ActionMap: Record<string, string> = {
const actionMap: Record<PropertyKey, keyof React.DOMAttributes<HTMLSpanElement>> = {
click: 'onClick',
hover: 'onMouseOver',
};
type IconPropsType = React.HTMLAttributes<HTMLSpanElement> & React.Attributes;
const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
const { visibilityToggle = true } = props;
const visibilityControlled =
@ -68,24 +71,27 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
const getIcon = (prefixCls: string) => {
const { action = 'click', iconRender = defaultIconRender } = props;
const iconTrigger = ActionMap[action] || '';
const iconTrigger = actionMap[action] || '';
const icon = iconRender(visible);
const iconProps = {
const iconProps: IconPropsType = {
[iconTrigger]: onVisibleChange,
className: `${prefixCls}-icon`,
key: 'passwordIcon',
onMouseDown: (e: MouseEvent) => {
onMouseDown: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
// Prevent focused state lost
// https://github.com/ant-design/ant-design/issues/15173
e.preventDefault();
},
onMouseUp: (e: MouseEvent) => {
onMouseUp: (e: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
// Prevent caret position change
// https://github.com/ant-design/ant-design/issues/23524
e.preventDefault();
},
};
return React.cloneElement(React.isValidElement(icon) ? icon : <span>{icon}</span>, iconProps);
return React.cloneElement<IconPropsType>(
React.isValidElement<IconPropsType>(icon) ? icon : <span>{icon}</span>,
iconProps,
);
};
const {
@ -122,7 +128,7 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
});
if (process.env.NODE_ENV !== 'production') {
Password.displayName = 'Password';
Password.displayName = 'Input.Password';
}
export default Password;

View File

@ -1,7 +1,8 @@
import * as React from 'react';
import SearchOutlined from '@ant-design/icons/SearchOutlined';
import classNames from 'classnames';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
import { cloneElement } from '../_util/reactNode';
import Button from '../button';
import { ConfigContext } from '../config-provider';
@ -174,6 +175,7 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
/>
);
});
if (process.env.NODE_ENV !== 'production') {
Search.displayName = 'Search';
}

View File

@ -941,7 +941,6 @@ export const prepareComponentToken: GetDefaultToken<'Menu'> = (token) => {
};
export const formatComponentToken: FormatComponentToken<'Menu'> = (token) => ({
...token,
itemWidth: token.activeBarWidth
? `calc(100% + ${token.activeBarBorderWidth}px)`
: `calc(100% - ${token.itemMarginInline * 2}px)`,

View File

@ -1279,6 +1279,7 @@ Array [
</label>
<label
class="ant-radio-button-wrapper"
title="Orange"
>
<span
class="ant-radio-button"

View File

@ -1263,6 +1263,7 @@ Array [
</label>
<label
class="ant-radio-button-wrapper"
title="Orange"
>
<span
class="ant-radio-button"

View File

@ -260,4 +260,17 @@ describe('Radio Group', () => {
);
expect(container.querySelector('#bamboo')).toBeTruthy();
});
it('options support title', () => {
const { container } = render(
<Radio.Group options={[{ label: 'bamboo', title: 'bamboo', value: 'bamboo' }]} />,
);
const select = container.querySelector('.ant-radio-group label > span');
expect(select).toBeTruthy();
// https://github.com/ant-design/ant-design/issues/46739
expect(select!.getAttribute('title')).toBeFalsy();
// fix 46739 solution
expect(container.querySelector('.ant-radio-group label')).toHaveAttribute('title', 'bamboo');
});
});

View File

@ -6,7 +6,7 @@ const plainOptions = ['Apple', 'Pear', 'Orange'];
const options = [
{ label: 'Apple', value: 'Apple' },
{ label: 'Pear', value: 'Pear' },
{ label: 'Orange', value: 'Orange' },
{ label: 'Orange', value: 'Orange', title: 'Orange' },
];
const optionsWithDisabled = [
{ label: 'Apple', value: 'Apple' },

View File

@ -40,6 +40,7 @@ const InternalRadio: React.ForwardRefRenderFunction<RadioRef, RadioProps> = (pro
rootClassName,
children,
style,
title,
...restProps
} = props;
const radioPrefixCls = getPrefixCls('radio', customizePrefixCls);
@ -88,6 +89,7 @@ const InternalRadio: React.ForwardRefRenderFunction<RadioRef, RadioProps> = (pro
style={{ ...radio?.style, ...style }}
onMouseEnter={props.onMouseEnter}
onMouseLeave={props.onMouseLeave}
title={title}
>
<RcCheckbox
{...radioProps}

View File

@ -8,6 +8,7 @@ import { warning } from 'rc-util';
import { ConfigContext } from '../../config-provider/context';
import { genCommonStyle, genLinkStyle } from '../../style';
import type {
AliasToken,
ComponentTokenMap,
GlobalToken,
OverrideToken,
@ -64,11 +65,11 @@ export type GenStyleFn<C extends OverrideComponent> = (
export type GetDefaultToken<C extends OverrideComponent> =
| null
| OverrideTokenWithoutDerivative[C]
| ((token: GlobalToken) => OverrideTokenWithoutDerivative[C]);
| ((token: AliasToken) => OverrideTokenWithoutDerivative[C]);
export type FormatComponentToken<C extends OverrideComponent> = (
token: NonNullable<OverrideTokenWithoutDerivative[C]>,
) => NonNullable<OverrideTokenWithoutDerivative[C]>;
token: NonNullable<OverrideTokenWithoutDerivative[C]> & AliasToken,
) => Partial<OverrideTokenWithoutDerivative[C]>;
const getDefaultComponentToken = <C extends OverrideComponent>(
component: C,
@ -76,7 +77,7 @@ const getDefaultComponentToken = <C extends OverrideComponent>(
getDefaultToken: GetDefaultToken<C>,
) => {
if (typeof getDefaultToken === 'function') {
return getDefaultToken(mergeToken<GlobalToken>(token, token[component] ?? {}));
return getDefaultToken(mergeToken<any>(token, token[component] ?? {}));
}
return getDefaultToken ?? {};
};
@ -97,9 +98,9 @@ const getComponentToken = <C extends OverrideComponent>(
if (process.env.NODE_ENV !== 'production') {
warning(
!customToken?.[oldTokenKey],
`The token '${String(oldTokenKey)}' of ${component} had deprecated, use '${String(
newTokenKey,
)}' instead.`,
`Component Token \`${String(
oldTokenKey,
)}\` of ${component} is deprecated. Please use \`${String(newTokenKey)}\` instead.`,
);
}
@ -111,7 +112,7 @@ const getComponentToken = <C extends OverrideComponent>(
}
let mergedToken: any = { ...defaultToken, ...customToken };
if (options?.format) {
mergedToken = options.format(mergedToken);
mergedToken = { ...mergedToken, ...options.format(mergeToken<any>(token, mergedToken)) };
}
// Remove same value as global token to minimize size
@ -135,10 +136,7 @@ const getCompVarPrefix = (component: string, prefix?: string) =>
export default function genComponentStyleHook<C extends OverrideComponent>(
componentName: C | [C, string],
styleFn: GenStyleFn<C>,
getDefaultToken?:
| null
| OverrideTokenWithoutDerivative[C]
| ((token: GlobalToken) => OverrideTokenWithoutDerivative[C]),
getDefaultToken?: GetDefaultToken<C>,
options: {
resetStyle?: boolean;
// Deprecated token key map [["oldTokenKey", "newTokenKey"], ["oldTokenKey", "newTokenKey"]]

View File

@ -1,7 +0,0 @@
## zh-CN
通过 `token` 进行动画效果配置。
## en-US
Config animation effect by `token`.

View File

@ -1,42 +0,0 @@
import React from 'react';
import { Switch, ConfigProvider, Space, Checkbox, Radio, Row, Col } from 'antd';
export default () => {
const [checked, setChecked] = React.useState(false);
React.useEffect(() => {
const id = setInterval(() => {
setChecked((prev) => !prev);
}, 500);
return () => {
clearInterval(id);
};
}, []);
const nodes = (
<Space>
<Checkbox checked={checked}>Checkbox</Checkbox>
<Radio checked={checked}>Radio</Radio>
<Switch checked={checked} />
</Space>
);
return (
<Row gutter={[24, 24]}>
<Col span={24}>{nodes}</Col>
<Col span={24}>
<ConfigProvider
theme={{
token: {
motion: false,
},
}}
>
{nodes}
</ConfigProvider>
</Col>
</Row>
);
};

View File

@ -169,47 +169,41 @@ antd has built-in interaction animations to make enterprise-level pages more det
```sandpack
import React from 'react';
import { Switch, ConfigProvider, Space, Checkbox, Radio, Row, Col } from 'antd';
export default () => {
const [checked, setChecked] = React.useState(false);
import { Checkbox, Col, ConfigProvider, Flex, Radio, Row, Switch } from 'antd';
const App: React.FC = () => {
const [checked, setChecked] = React.useState<boolean>(false);
const timerRef = React.useRef<ReturnType<typeof setInterval>>();
React.useEffect(() => {
const id = setInterval(() => {
timerRef.current = setInterval(() => {
setChecked((prev) => !prev);
}, 1000);
}, 500);
return () => {
clearInterval(id);
if (timerRef.current) {
clearInterval(timerRef.current);
}
};
}, []);
const nodes = (
<Space>
<Flex gap="small">
<Checkbox checked={checked}>Checkbox</Checkbox>
<Radio checked={checked}>Radio</Radio>
<Switch checked={checked} />
</Space>
</Flex>
);
return (
<Row gutter={[24, 24]}>
<Col span={24}>{nodes}</Col>
<Col span={24}>
<ConfigProvider
theme={{
token: {
motion: false,
},
}}
>
{nodes}
</ConfigProvider>
<ConfigProvider theme={{ token: { motion: false } }}>{nodes}</ConfigProvider>
</Col>
</Row>
);
};
export default App;
```
## Advanced

View File

@ -169,47 +169,41 @@ antd 默认内置了一些组件交互动效让企业级页面更加富有细节
```sandpack
import React from 'react';
import { Switch, ConfigProvider, Space, Checkbox, Radio, Row, Col } from 'antd';
export default () => {
const [checked, setChecked] = React.useState(false);
import { Checkbox, Col, ConfigProvider, Flex, Radio, Row, Switch } from 'antd';
const App: React.FC = () => {
const [checked, setChecked] = React.useState<boolean>(false);
const timerRef = React.useRef<ReturnType<typeof setInterval>>();
React.useEffect(() => {
const id = setInterval(() => {
timerRef.current = setInterval(() => {
setChecked((prev) => !prev);
}, 1000);
}, 500);
return () => {
clearInterval(id);
if (timerRef.current) {
clearInterval(timerRef.current);
}
};
}, []);
const nodes = (
<Space>
<Flex gap="small">
<Checkbox checked={checked}>Checkbox</Checkbox>
<Radio checked={checked}>Radio</Radio>
<Switch checked={checked} />
</Space>
</Flex>
);
return (
<Row gutter={[24, 24]}>
<Col span={24}>{nodes}</Col>
<Col span={24}>
<ConfigProvider
theme={{
token: {
motion: false,
},
}}
>
{nodes}
</ConfigProvider>
<ConfigProvider theme={{ token: { motion: false } }}>{nodes}</ConfigProvider>
</Col>
</Row>
);
};
export default App;
```
## 进阶使用

View File

@ -108,18 +108,13 @@ export default App;
- [Customize Theme](/docs/react/customize-theme)
- [How to Apply for Being A Collaborator](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
## Non-React Implementations
React is used to encapsulate a library of components which embody our design language. We welcome the community to implement our design system [in other front-end frameworks](/docs/spec/introduce#front-end-implementation) of their choice.
## Companies using antd
- [Ant Group](https://www.antgroup.com/index.htm?locale=en_US)
- [Alibaba](https://www.alibaba.com/)
- [Tencent](https://www.tencent.com)
- [Baidu](https://www.baidu.com)
- [Koubei](https://www.koubei.com/)
- [Meituan](https://www.meituan.com)
- [Didi](https://www.xiaojukeji.com/)
- [Eleme](https://www.ele.me/)
> If your company or product uses Ant Design, let us know [here](https://github.com/ant-design/ant-design/issues/477)!
Ant Design is widely used for building enterprise-level websites both domestically and internationally. You can refer to wappalyzer for reference data. If your company or product uses Ant Design, let us know [here](https://github.com/ant-design/ant-design/issues/477)!
## Contributing

View File

@ -86,15 +86,15 @@ export default App;
## 链接
- [首页](/index-cn)
- [组件](/components/overview-cn)
- [所有组件](/components/overview-cn)
- [国内镜像](https://github.com/ant-design/ant-design/issues/25661)
- [Ant Design Pro](https://pro.ant.design/)
- [Ant Design Pro Components](https://procomponents.ant.design/)
- [Ant Design Charts](https://charts.ant.design)
- [更新日志](/changelog)
- [React 底层基础组件](https://react-component.github.io/)
- [移动端组件库](https://mobile.ant.design)
- [小程序组件库](https://mini.ant.design)
- [Ant Design Mobile](https://mobile.ant.design)
- [Ant Design Mini](https://mini.ant.design)
- [Ant Design 图标](https://github.com/ant-design/ant-design-icons)
- [Ant Design 色彩](https://github.com/ant-design/ant-design-colors)
- [首页模板集](https://landing.ant.design)
@ -109,18 +109,13 @@ export default App;
- [定制主题](/docs/react/customize-theme)
- [成为社区协作成员](https://github.com/ant-design/ant-design/wiki/Collaborators#how-to-apply-for-being-a-collaborator)
## 非 React 的实现
我们采用 React 封装了一套 Ant Design 的组件库,其他语言的 UI 实现可以参考[此处](/docs/spec/introduce-cn#%E5%89%8D%E7%AB%AF%E5%AE%9E%E7%8E%B0)。
## 谁在使用
- [蚂蚁集团](https://www.antgroup.com/)
- [阿里巴巴](https://www.alibaba.com/)
- [腾讯](https://www.tencent.com)
- [百度](https://www.baidu.com)
- [口碑](https://www.koubei.com/)
- [美团](https://www.meituan.com)
- [滴滴](https://www.xiaojukeji.com/)
- [饿了么](https://www.ele.me/)
> 如果你的公司和产品使用了 Ant Design欢迎到 [这里](https://github.com/ant-design/ant-design/issues/477) 留言。
Ant Design 广泛用于国内外的企业级网站搭建,可以查看 [wappalyzer](https://www.wappalyzer.com/technologies/ui-frameworks/ant-design) 作为参考数据。如果你的公司和产品使用了 Ant Design欢迎到 [这里](https://github.com/ant-design/ant-design/issues/477) 留言。
## 如何贡献

View File

@ -67,15 +67,16 @@
"install-react-16": "npm i --no-save --legacy-peer-deps react@16 react-dom@16 @testing-library/react@12",
"install-react-17": "npm i --no-save --legacy-peer-deps react@17 react-dom@17 @testing-library/react@12",
"install-react-18": "npm i --no-save --legacy-peer-deps react@18 react-dom@18",
"prelint": "is-ci && dumi setup",
"lint": "npm run version && npm run tsc && npm run lint:script && npm run lint:demo && npm run lint:md && npm run lint:style",
"lint-fix": "npm run lint-fix:script && npm run lint-fix:demo",
"lint-fix:demo": "npm run lint:demo -- --fix",
"lint-fix:script": "npm run lint:script -- --fix",
"lint:demo": "eslint components/*/demo/*.md",
"lint:deps": "antd-tools run deps-lint",
"lint:md": "remark . -f -q",
"lint:script": "npm run component-changelog && eslint . --ext .js,.jsx,.ts,.tsx --cache",
"lint:style": "tsx scripts/check-cssinjs.tsx",
"lint-fix": "npm run lint-fix:script && npm run lint-fix:demo",
"lint-fix:demo": "npm run lint:demo -- --fix",
"lint-fix:script": "npm run lint:script -- --fix",
"pre-publish": "npm run test-all -- --skip-build && tsx ./scripts/pre-publish-notice.ts",
"prepare": "is-ci || husky install && dumi setup",
"prepublishOnly": "antd-tools run guard",
@ -168,7 +169,7 @@
"@ant-design/tools": "^18.0.2",
"@antv/g6": "^4.8.24",
"@babel/eslint-plugin": "^7.23.5",
"@biomejs/biome": "^1.4.1",
"@biomejs/biome": "^1.5.0",
"@codesandbox/sandpack-react": "^2.11.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
@ -181,7 +182,7 @@
"@qixian.cs/github-contributors-list": "^1.1.0",
"@size-limit/file": "^11.0.1",
"@stackblitz/sdk": "^1.9.0",
"@testing-library/dom": "^9.3.3",
"@testing-library/dom": "^9.3.4",
"@testing-library/jest-dom": "^6.2.0",
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.2",
@ -199,14 +200,14 @@
"@types/jsdom": "^21.1.6",
"@types/lodash": "^4.14.202",
"@types/minimist": "^1.2.5",
"@types/node": "^20.10.6",
"@types/node": "^20.10.7",
"@types/nprogress": "^0.2.3",
"@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4",
"@types/prismjs": "^1.26.3",
"@types/progress": "^2.0.7",
"@types/qs": "^6.9.11",
"@types/react": "^18.2.46",
"@types/react": "^18.2.47",
"@types/react-copy-to-clipboard": "^5.0.7",
"@types/react-dom": "^18.2.18",
"@types/react-highlight-words": "^0.16.7",
@ -215,20 +216,20 @@
"@types/tar": "^6.1.10",
"@types/throttle-debounce": "^5.0.2",
"@types/warning": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"ali-oss": "^6.19.0",
"antd-img-crop": "^4.21.0",
"antd-style": "^3.6.1",
"antd-token-previewer": "^2.0.5",
"chalk": "^4.1.0",
"chalk": "^4.1.2",
"cheerio": "1.0.0-rc.12",
"circular-dependency-plugin": "^5.2.2",
"cross-env": "^7.0.3",
"cross-fetch": "^4.0.0",
"crypto": "^1.0.1",
"dekko": "^0.2.1",
"dumi": "2.3.0-alpha.9",
"dumi": "^2.3.0-beta.3",
"dumi-plugin-color-chunk": "^1.1.0",
"esbuild-loader": "^4.0.2",
"eslint": "^8.56.0",
@ -265,7 +266,7 @@
"jest-image-snapshot": "^6.4.0",
"jest-puppeteer": "^9.0.2",
"jquery": "^3.7.1",
"jsdom": "^23.0.1",
"jsdom": "^23.2.0",
"jsonml-to-react-element": "^1.1.11",
"jsonml.js": "^0.1.0",
"lint-staged": "^15.2.0",
@ -277,7 +278,7 @@
"node-fetch": "^3.3.2",
"node-notifier": "^10.0.1",
"nprogress": "^0.2.0",
"open": "^10.0.2",
"open": "^10.0.3",
"pixelmatch": "^5.3.0",
"pngjs": "^7.0.0",
"prettier": "^3.1.1",
@ -324,7 +325,7 @@
"tar-fs": "^3.0.4",
"terser": "^5.26.0",
"tsx": "^4.7.0",
"typedoc": "^0.25.6",
"typedoc": "^0.25.7",
"typescript": "~5.3.3",
"vanilla-jsoneditor": "^0.21.1",
"vanilla-tilt": "^1.8.1",
@ -341,7 +342,7 @@
},
"overrides": {
"dumi-plugin-color-chunk": {
"dumi": "^2.3.0-alpha.4"
"dumi": "^2.3.0-beta.3"
}
},
"packageManager": "npm@10.2.5",