mirror of
https://github.com/ant-design/ant-design.git
synced 2025-08-06 07:56:28 +08:00
chore: merge master into feature
This commit is contained in:
commit
b241f68a33
@ -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)}
|
||||
|
@ -5,10 +5,8 @@ import stackblitzSdk from '@stackblitz/sdk';
|
||||
import { Alert, Badge, Flex, Tooltip } from 'antd';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage, LiveContext, useSiteData } from 'dumi';
|
||||
import LiveDemo from 'dumi/theme-default/slots/LiveDemo';
|
||||
import { FormattedMessage, useSiteData, useLiveDemo } from 'dumi';
|
||||
import LZString from 'lz-string';
|
||||
|
||||
import useLocation from '../../../hooks/useLocation';
|
||||
import BrowserFrame from '../../common/BrowserFrame';
|
||||
import ClientOnly from '../../common/ClientOnly';
|
||||
@ -108,14 +106,18 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
const { pkg } = useSiteData();
|
||||
const location = useLocation();
|
||||
|
||||
const { enabled: liveEnabled } = useContext(LiveContext);
|
||||
|
||||
const { styles } = useStyle();
|
||||
|
||||
const entryCode = asset.dependencies['index.tsx'].value;
|
||||
const entryName = 'index.tsx';
|
||||
const entryCode = asset.dependencies[entryName].value;
|
||||
const showRiddleButton = useShowRiddleButton();
|
||||
|
||||
const liveDemo = useRef<React.ReactNode>(null);
|
||||
const previewDemo = useRef<React.ReactNode>(null);
|
||||
const {
|
||||
node: liveDemoNode,
|
||||
error: liveDemoError,
|
||||
setSource: setLiveDemoSource,
|
||||
} = useLiveDemo(asset.id);
|
||||
const anchorRef = useRef<HTMLAnchorElement>(null);
|
||||
const codeSandboxIconRef = useRef<HTMLFormElement>(null);
|
||||
const riddleIconRef = useRef<HTMLFormElement>(null);
|
||||
@ -153,8 +155,8 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
|
||||
const mergedChildren = !iframe && clientOnly ? <ClientOnly>{children}</ClientOnly> : children;
|
||||
|
||||
if (!liveDemo.current) {
|
||||
liveDemo.current = iframe ? (
|
||||
if (!previewDemo.current) {
|
||||
previewDemo.current = iframe ? (
|
||||
<BrowserFrame>
|
||||
<iframe
|
||||
src={demoUrl}
|
||||
@ -366,12 +368,10 @@ createRoot(document.getElementById('container')).render(<Demo />);
|
||||
const codeBox: React.ReactNode = (
|
||||
<section className={codeBoxClass} id={asset.id}>
|
||||
<section className="code-box-demo" style={codeBoxDemoStyle}>
|
||||
{!liveEnabled ? (
|
||||
{liveDemoNode || (
|
||||
<ErrorBoundary>
|
||||
<React.StrictMode>{liveDemo.current}</React.StrictMode>
|
||||
<React.StrictMode>{previewDemo.current}</React.StrictMode>
|
||||
</ErrorBoundary>
|
||||
) : (
|
||||
<LiveDemo />
|
||||
)}
|
||||
</section>
|
||||
<section className="code-box-meta markdown">
|
||||
@ -513,7 +513,10 @@ createRoot(document.getElementById('container')).render(<Demo />);
|
||||
sourceCode={entryCode}
|
||||
jsxCode={jsx}
|
||||
styleCode={style}
|
||||
liveError={liveDemoError}
|
||||
entryName={entryName}
|
||||
onCodeTypeChange={setCodeType}
|
||||
onSourceTranspile={setLiveDemoSource}
|
||||
/>
|
||||
<div
|
||||
tabIndex={0}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import type { IPreviewerProps } from 'dumi';
|
||||
import { LiveProvider, useTabMeta } from 'dumi';
|
||||
import { useTabMeta } from 'dumi';
|
||||
|
||||
import CodePreviewer from './CodePreviewer';
|
||||
import DesignPreviewer from './DesignPreviewer';
|
||||
@ -16,23 +16,7 @@ const Previewer: React.FC<AntdPreviewerProps> = (props) => {
|
||||
return <DesignPreviewer {...props} />;
|
||||
}
|
||||
|
||||
const codePreviewer = <CodePreviewer {...props} />;
|
||||
|
||||
if (props.live === false || props.iframe) {
|
||||
return codePreviewer;
|
||||
}
|
||||
|
||||
return (
|
||||
<LiveProvider
|
||||
initialCode={
|
||||
Object.entries(props.asset.dependencies).filter(([, { type }]) => type === 'FILE')[0][1]
|
||||
.value
|
||||
}
|
||||
demoId={props.asset.id}
|
||||
>
|
||||
{codePreviewer}
|
||||
</LiveProvider>
|
||||
);
|
||||
return <CodePreviewer {...props} />;
|
||||
};
|
||||
|
||||
export default Previewer;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { type ComponentProps, useEffect, useMemo } from 'react';
|
||||
import { Button, Tabs, Typography } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { LiveContext } from 'dumi';
|
||||
import toReactElement from 'jsonml-to-react-element';
|
||||
import JsonML from 'jsonml.js/lib/utils';
|
||||
import Prism from 'prismjs';
|
||||
import React, { useContext, useEffect, useMemo } from 'react';
|
||||
|
||||
import LiveCode from './LiveCode';
|
||||
|
||||
const useStyle = createStyles(({ token, css }) => {
|
||||
@ -19,6 +19,7 @@ const useStyle = createStyles(({ token, css }) => {
|
||||
copyButton: css`
|
||||
color: ${colorIcon};
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 16px;
|
||||
inset-inline-end: 16px;
|
||||
width: 32px;
|
||||
@ -29,7 +30,19 @@ const useStyle = createStyles(({ token, css }) => {
|
||||
|
||||
copyIcon: css`
|
||||
${antCls}-typography-copy {
|
||||
position: relative;
|
||||
margin-inline-start: 0;
|
||||
|
||||
// expand clickable area
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: -9px;
|
||||
bottom: -5px;
|
||||
right: -9px;
|
||||
}
|
||||
}
|
||||
${antCls}-typography-copy:not(${antCls}-typography-copy-success) {
|
||||
color: ${colorIcon};
|
||||
@ -48,11 +61,13 @@ const LANGS = {
|
||||
style: 'CSS',
|
||||
};
|
||||
|
||||
interface CodePreviewProps {
|
||||
interface CodePreviewProps extends Omit<ComponentProps<typeof LiveCode>, 'initialValue' | 'lang'> {
|
||||
sourceCode?: string;
|
||||
jsxCode?: string;
|
||||
styleCode?: string;
|
||||
entryName: string;
|
||||
onCodeTypeChange?: (activeKey: string) => void;
|
||||
onSourceTranspile?: (source: Record<string, string>) => void;
|
||||
}
|
||||
|
||||
function toReactComponent(jsonML: any[]) {
|
||||
@ -75,7 +90,9 @@ const CodePreview: React.FC<CodePreviewProps> = ({
|
||||
sourceCode = '',
|
||||
jsxCode = '',
|
||||
styleCode = '',
|
||||
entryName,
|
||||
onCodeTypeChange,
|
||||
onSourceTranspile,
|
||||
}) => {
|
||||
// 避免 Tabs 数量不稳定的闪动问题
|
||||
const initialCodes = {} as Record<'tsx' | 'jsx' | 'style', string>;
|
||||
@ -90,9 +107,10 @@ const CodePreview: React.FC<CodePreviewProps> = ({
|
||||
}
|
||||
const [highlightedCodes, setHighlightedCodes] = React.useState(initialCodes);
|
||||
const sourceCodes = {
|
||||
tsx: sourceCode,
|
||||
jsx: jsxCode,
|
||||
style: styleCode,
|
||||
// omit trailing line break
|
||||
tsx: sourceCode?.trim(),
|
||||
jsx: jsxCode?.trim(),
|
||||
style: styleCode?.trim(),
|
||||
} as Record<'tsx' | 'jsx' | 'style', string>;
|
||||
useEffect(() => {
|
||||
const codes = {
|
||||
@ -109,12 +127,10 @@ const CodePreview: React.FC<CodePreviewProps> = ({
|
||||
setHighlightedCodes(codes);
|
||||
}, [jsxCode, sourceCode, styleCode]);
|
||||
|
||||
const langList = Object.keys(highlightedCodes);
|
||||
const langList = Object.keys(highlightedCodes) as ('tsx' | 'jsx' | 'style')[];
|
||||
|
||||
const { styles } = useStyle();
|
||||
|
||||
const { enabled: liveEnabled } = useContext(LiveContext);
|
||||
|
||||
const items = useMemo(
|
||||
() =>
|
||||
langList.map((lang: keyof typeof LANGS) => ({
|
||||
@ -122,8 +138,14 @@ const CodePreview: React.FC<CodePreviewProps> = ({
|
||||
key: lang,
|
||||
children: (
|
||||
<div className={styles.code}>
|
||||
{lang === 'tsx' && liveEnabled ? (
|
||||
<LiveCode />
|
||||
{lang === 'tsx' ? (
|
||||
<LiveCode
|
||||
lang={lang}
|
||||
initialValue={sourceCodes[lang]}
|
||||
onTranspile={(code) => {
|
||||
onSourceTranspile?.({ [entryName]: code });
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
toReactComponent(['pre', { lang, highlighted: highlightedCodes[lang] }])
|
||||
)}
|
||||
@ -141,17 +163,14 @@ const CodePreview: React.FC<CodePreviewProps> = ({
|
||||
}
|
||||
|
||||
if (langList.length === 1) {
|
||||
return liveEnabled ? (
|
||||
<LiveCode />
|
||||
) : (
|
||||
toReactComponent([
|
||||
'pre',
|
||||
{
|
||||
lang: langList[0],
|
||||
highlighted: highlightedCodes[langList[0] as keyof typeof LANGS],
|
||||
className: 'highlight',
|
||||
},
|
||||
])
|
||||
return (
|
||||
<LiveCode
|
||||
lang={langList[0]}
|
||||
initialValue={sourceCodes[langList[0]]}
|
||||
onTranspile={(code) => {
|
||||
onSourceTranspile?.({ [entryName]: code });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
import type { FC } from 'react';
|
||||
import type { ComponentProps, FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { createStyles } from 'antd-style';
|
||||
import LiveEditor from '../slots/LiveEditor';
|
||||
import LiveError from '../slots/LiveError';
|
||||
import { EditFilled } from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import SourceCodeEditor from 'dumi/theme-default/slots/SourceCodeEditor';
|
||||
|
||||
import useLocale from '../../hooks/useLocale';
|
||||
import LiveError from '../slots/LiveError';
|
||||
|
||||
const useStyle = createStyles(({ token, css }) => {
|
||||
const { colorPrimaryBorder, colorIcon, colorPrimary } = token;
|
||||
@ -23,10 +24,38 @@ const useStyle = createStyles(({ token, css }) => {
|
||||
box-shadow: inset 0 0 0 1px ${colorPrimary} !important;
|
||||
}
|
||||
}
|
||||
|
||||
// override dumi editor styles
|
||||
.dumi-default-source-code-editor {
|
||||
.dumi-default-source-code > pre,
|
||||
.dumi-default-source-code-editor-textarea {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.dumi-default-source-code > pre {
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
font-family: 'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
|
||||
// disable dumi default copy button
|
||||
.dumi-default-source-code-copy {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-textarea:hover {
|
||||
box-shadow: 0 0 0 1px ${token.colorPrimaryBorderHover} inset;
|
||||
}
|
||||
|
||||
&-textarea:focus {
|
||||
box-shadow: 0 0 0 1px ${token.colorPrimary} inset;
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
editableIcon: css`
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
display: flex;
|
||||
@ -50,8 +79,14 @@ const locales = {
|
||||
|
||||
const HIDE_LIVE_DEMO_TIP = 'hide-live-demo-tip';
|
||||
|
||||
const LiveCode: FC = () => {
|
||||
const LiveCode: FC<{
|
||||
lang: ComponentProps<typeof SourceCodeEditor>['lang'];
|
||||
initialValue: ComponentProps<typeof SourceCodeEditor>['initialValue'];
|
||||
liveError?: Error;
|
||||
onTranspile?: (code: string) => void;
|
||||
}> = (props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [error, setError] = useState<Error>();
|
||||
const { styles } = useStyle();
|
||||
const [locale] = useLocale(locales);
|
||||
|
||||
@ -72,8 +107,19 @@ const LiveCode: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.editor}>
|
||||
<LiveEditor />
|
||||
<LiveError />
|
||||
<SourceCodeEditor
|
||||
lang={props.lang}
|
||||
initialValue={props.initialValue}
|
||||
onTranspile={({ err, code }) => {
|
||||
if (err) {
|
||||
setError(err);
|
||||
} else {
|
||||
setError(undefined);
|
||||
props.onTranspile?.(code);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<LiveError error={props.liveError || error} />
|
||||
</div>
|
||||
<Tooltip title={locale.demoEditable} open={open} onOpenChange={handleOpenChange}>
|
||||
<EditFilled className={styles.editableIcon} />
|
||||
|
@ -1,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;
|
@ -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) {
|
||||
|
@ -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}
|
||||
|
@ -1,21 +0,0 @@
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import DumiLiveEditor from 'dumi/theme-default/slots/LiveEditor';
|
||||
|
||||
const LiveEditor: FC = () => (
|
||||
<DumiLiveEditor
|
||||
style={{
|
||||
fontSize: 13,
|
||||
lineHeight: 2,
|
||||
fontFamily: `'Lucida Console', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace`,
|
||||
}}
|
||||
padding={{
|
||||
top: 12,
|
||||
right: 16,
|
||||
bottom: 12,
|
||||
left: 16,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default LiveEditor;
|
@ -1,14 +1,12 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { LiveContext } from 'dumi';
|
||||
import React from 'react';
|
||||
import { Alert, theme } from 'antd';
|
||||
|
||||
const LiveError: FC = () => {
|
||||
const { error } = useContext(LiveContext);
|
||||
const LiveError: FC<{ error?: Error }> = ({ error }) => {
|
||||
const { token } = theme.useToken();
|
||||
|
||||
return error ? (
|
||||
<Alert banner type="error" message={error} style={{ color: token.colorError }} />
|
||||
<Alert banner type="error" message={error.toString()} style={{ color: token.colorError }} />
|
||||
) : null;
|
||||
};
|
||||
|
||||
|
@ -15,7 +15,6 @@ export default defineConfig({
|
||||
ssr: process.env.NODE_ENV === 'production' ? {} : false,
|
||||
hash: true,
|
||||
mfsu: false,
|
||||
live: true,
|
||||
crossorigin: {},
|
||||
outputPath: '_site',
|
||||
favicons: ['https://gw.alipayobjects.com/zos/rmsportal/rlpTLlbMzTNYuZGGCVYM.png'],
|
||||
|
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -25,4 +25,4 @@ updates:
|
||||
update-types: ["major", "minor"]
|
||||
dev-dependencies:
|
||||
dependency-type: "development"
|
||||
update-types: ["major", "minor"]
|
||||
update-types: ["major"]
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('button');
|
||||
extendTest('button', {
|
||||
skip: ['component-token.tsx'],
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('button');
|
||||
demoTest('button', {
|
||||
skip: ['component-token.tsx'],
|
||||
});
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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,
|
||||
),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
@ -1,7 +1,3 @@
|
||||
/**
|
||||
* live: false
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
@ -50,7 +50,7 @@ export interface ThemeConfig {
|
||||
cssVar?:
|
||||
| {
|
||||
/**
|
||||
* Prefix for css variable, default to `antd`.
|
||||
* Prefix for css variable, default to `ant`.
|
||||
*/
|
||||
prefix?: string;
|
||||
/**
|
||||
|
@ -1,7 +1,3 @@
|
||||
/**
|
||||
* live: false
|
||||
*/
|
||||
|
||||
import { EllipsisOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
import React, { useState } from 'react';
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('dropdown');
|
||||
extendTest('dropdown', {
|
||||
skip: ['sub-menu-debug.tsx'],
|
||||
});
|
||||
|
@ -3,6 +3,7 @@ import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('dropdown', {
|
||||
testRootProps: false,
|
||||
skip: ['sub-menu-debug.tsx'],
|
||||
});
|
||||
|
||||
rootPropsTest(
|
||||
|
7
components/dropdown/demo/sub-menu-debug.md
Normal file
7
components/dropdown/demo/sub-menu-debug.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
传入的菜单里有多个层级。
|
||||
|
||||
## en-US
|
||||
|
||||
The menu has multiple levels.
|
66
components/dropdown/demo/sub-menu-debug.tsx
Normal file
66
components/dropdown/demo/sub-menu-debug.tsx
Normal 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;
|
@ -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`} />
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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',
|
||||
},
|
||||
|
@ -1,7 +1,3 @@
|
||||
/**
|
||||
* live: false
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
HomeOutlined,
|
||||
|
@ -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) => {
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -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)`,
|
||||
|
@ -1279,6 +1279,7 @@ Array [
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
title="Orange"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
|
@ -1263,6 +1263,7 @@ Array [
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
title="Orange"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
@ -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' },
|
||||
|
@ -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}
|
||||
|
@ -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"]]
|
||||
|
@ -1,7 +0,0 @@
|
||||
## zh-CN
|
||||
|
||||
通过 `token` 进行动画效果配置。
|
||||
|
||||
## en-US
|
||||
|
||||
Config animation effect by `token`.
|
@ -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>
|
||||
);
|
||||
};
|
@ -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
|
||||
|
@ -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;
|
||||
```
|
||||
|
||||
## 进阶使用
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) 留言。
|
||||
|
||||
## 如何贡献
|
||||
|
||||
|
31
package.json
31
package.json
@ -67,15 +67,16 @@
|
||||
"install-react-16": "npm i --no-save --legacy-peer-deps react@16 react-dom@16 @testing-library/react@12",
|
||||
"install-react-17": "npm i --no-save --legacy-peer-deps react@17 react-dom@17 @testing-library/react@12",
|
||||
"install-react-18": "npm i --no-save --legacy-peer-deps react@18 react-dom@18",
|
||||
"prelint": "is-ci && dumi setup",
|
||||
"lint": "npm run version && npm run tsc && npm run lint:script && npm run lint:demo && npm run lint:md && npm run lint:style",
|
||||
"lint-fix": "npm run lint-fix:script && npm run lint-fix:demo",
|
||||
"lint-fix:demo": "npm run lint:demo -- --fix",
|
||||
"lint-fix:script": "npm run lint:script -- --fix",
|
||||
"lint:demo": "eslint components/*/demo/*.md",
|
||||
"lint:deps": "antd-tools run deps-lint",
|
||||
"lint:md": "remark . -f -q",
|
||||
"lint:script": "npm run component-changelog && eslint . --ext .js,.jsx,.ts,.tsx --cache",
|
||||
"lint:style": "tsx scripts/check-cssinjs.tsx",
|
||||
"lint-fix": "npm run lint-fix:script && npm run lint-fix:demo",
|
||||
"lint-fix:demo": "npm run lint:demo -- --fix",
|
||||
"lint-fix:script": "npm run lint:script -- --fix",
|
||||
"pre-publish": "npm run test-all -- --skip-build && tsx ./scripts/pre-publish-notice.ts",
|
||||
"prepare": "is-ci || husky install && dumi setup",
|
||||
"prepublishOnly": "antd-tools run guard",
|
||||
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user