mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 02:59:58 +08:00
chore: resolve merge conflict
This commit is contained in:
commit
86a5de1f37
181
components/_util/__tests__/hooks.test.tsx
Normal file
181
components/_util/__tests__/hooks.test.tsx
Normal file
@ -0,0 +1,181 @@
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { render } from '@testing-library/react';
|
||||
import React, { useEffect } from 'react';
|
||||
import type { UseClosableParams } from '../hooks/useClosable';
|
||||
import useClosable from '../hooks/useClosable';
|
||||
|
||||
type ParamsOfUseClosable = [
|
||||
UseClosableParams['closable'],
|
||||
UseClosableParams['closeIcon'],
|
||||
UseClosableParams['defaultClosable'],
|
||||
];
|
||||
|
||||
describe('hooks test', () => {
|
||||
const useClosableParams: { params: ParamsOfUseClosable; res: [boolean, string] }[] = [
|
||||
// test case like: <Component />
|
||||
{
|
||||
params: [undefined, undefined, undefined],
|
||||
res: [false, ''],
|
||||
},
|
||||
{
|
||||
params: [undefined, undefined, true],
|
||||
res: [true, 'anticon-close'],
|
||||
},
|
||||
{
|
||||
params: [undefined, undefined, false],
|
||||
res: [false, ''],
|
||||
},
|
||||
|
||||
// test case like: <Component closable={false | true} />
|
||||
{
|
||||
params: [false, undefined, undefined],
|
||||
res: [false, ''],
|
||||
},
|
||||
{
|
||||
params: [true, undefined, true],
|
||||
res: [true, 'anticon-close'],
|
||||
},
|
||||
{
|
||||
params: [true, undefined, false],
|
||||
res: [true, 'anticon-close'],
|
||||
},
|
||||
|
||||
// test case like: <Component closable={false | true} closeIcon={null | false | element} />
|
||||
{
|
||||
params: [false, null, undefined],
|
||||
res: [false, ''],
|
||||
},
|
||||
{
|
||||
params: [false, false, undefined],
|
||||
res: [false, ''],
|
||||
},
|
||||
{
|
||||
params: [true, null, true],
|
||||
res: [true, 'anticon-close'],
|
||||
},
|
||||
{
|
||||
params: [true, false, true],
|
||||
res: [true, 'anticon-close'],
|
||||
},
|
||||
{
|
||||
params: [true, null, false],
|
||||
res: [true, 'anticon-close'],
|
||||
},
|
||||
{
|
||||
params: [true, false, false],
|
||||
res: [true, 'anticon-close'],
|
||||
},
|
||||
{
|
||||
params: [
|
||||
true,
|
||||
<div className="custom-close" key="close">
|
||||
close
|
||||
</div>,
|
||||
false,
|
||||
],
|
||||
res: [true, 'custom-close'],
|
||||
},
|
||||
{
|
||||
params: [false, <div key="close">close</div>, false],
|
||||
res: [false, ''],
|
||||
},
|
||||
|
||||
// test case like: <Component closeIcon={null | false | element} />
|
||||
{
|
||||
params: [undefined, null, undefined],
|
||||
res: [false, ''],
|
||||
},
|
||||
{
|
||||
params: [undefined, false, undefined],
|
||||
res: [false, ''],
|
||||
},
|
||||
{
|
||||
params: [
|
||||
undefined,
|
||||
<div className="custom-close" key="close">
|
||||
close
|
||||
</div>,
|
||||
undefined,
|
||||
],
|
||||
res: [true, 'custom-close'],
|
||||
},
|
||||
{
|
||||
params: [
|
||||
undefined,
|
||||
<div className="custom-close" key="close">
|
||||
close
|
||||
</div>,
|
||||
true,
|
||||
],
|
||||
res: [true, 'custom-close'],
|
||||
},
|
||||
{
|
||||
params: [
|
||||
undefined,
|
||||
<div className="custom-close" key="close">
|
||||
close
|
||||
</div>,
|
||||
false,
|
||||
],
|
||||
res: [true, 'custom-close'],
|
||||
},
|
||||
];
|
||||
|
||||
useClosableParams.forEach(({ params, res }) => {
|
||||
it(`useClosable with closable=${params[0]},closeIcon=${
|
||||
React.isValidElement(params[1]) ? 'element' : params[1]
|
||||
},defaultClosable=${params[2]}. the result should be ${res}`, () => {
|
||||
const App = () => {
|
||||
const [closable, closeIcon] = useClosable(
|
||||
params[0],
|
||||
params[1],
|
||||
undefined,
|
||||
undefined,
|
||||
params[2],
|
||||
);
|
||||
useEffect(() => {
|
||||
expect(closable).toBe(res[0]);
|
||||
}, [closable]);
|
||||
return <div>hooks test {closeIcon}</div>;
|
||||
};
|
||||
const { container } = render(<App />);
|
||||
if (res[1] === '') {
|
||||
expect(container.querySelector('.anticon-close')).toBeFalsy();
|
||||
} else {
|
||||
expect(container.querySelector(`.${res[1]}`)).toBeTruthy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('useClosable with defaultCloseIcon', () => {
|
||||
const App = () => {
|
||||
const [closable, closeIcon] = useClosable(
|
||||
true,
|
||||
undefined,
|
||||
undefined,
|
||||
<CloseOutlined className="custom-close-icon" />,
|
||||
);
|
||||
useEffect(() => {
|
||||
expect(closable).toBe(true);
|
||||
}, [closable]);
|
||||
return <div>hooks test {closeIcon}</div>;
|
||||
};
|
||||
const { container } = render(<App />);
|
||||
expect(container.querySelector('.custom-close-icon')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('useClosable with customCloseIconRender', () => {
|
||||
const App = () => {
|
||||
const customCloseIconRender = (icon: React.ReactNode) => (
|
||||
<span className="custom-close-wrapper">{icon}</span>
|
||||
);
|
||||
const [closable, closeIcon] = useClosable(true, undefined, customCloseIconRender);
|
||||
useEffect(() => {
|
||||
expect(closable).toBe(true);
|
||||
}, [closable]);
|
||||
return <div>hooks test {closeIcon}</div>;
|
||||
};
|
||||
const { container } = render(<App />);
|
||||
expect(container.querySelector('.custom-close-wrapper')).toBeTruthy();
|
||||
});
|
||||
});
|
43
components/_util/hooks/useClosable.tsx
Normal file
43
components/_util/hooks/useClosable.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import type { ReactNode } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
function useInnerClosable(
|
||||
closable?: boolean,
|
||||
closeIcon?: boolean | ReactNode,
|
||||
defaultClosable?: boolean,
|
||||
): boolean {
|
||||
if (typeof closable === 'boolean') {
|
||||
return closable;
|
||||
}
|
||||
if (closeIcon === undefined) {
|
||||
return !!defaultClosable;
|
||||
}
|
||||
return closeIcon !== false && closeIcon !== null;
|
||||
}
|
||||
|
||||
export type UseClosableParams = {
|
||||
closable?: boolean;
|
||||
closeIcon?: boolean | ReactNode;
|
||||
defaultClosable?: boolean;
|
||||
defaultCloseIcon?: ReactNode;
|
||||
customCloseIconRender?: (closeIcon: ReactNode) => ReactNode;
|
||||
};
|
||||
|
||||
export default function useClosable(
|
||||
closable?: boolean,
|
||||
closeIcon?: boolean | ReactNode,
|
||||
customCloseIconRender?: (closeIcon: ReactNode) => ReactNode,
|
||||
defaultCloseIcon: ReactNode = <CloseOutlined />,
|
||||
defaultClosable = false,
|
||||
): [closable: boolean, closeIcon: React.ReactNode | null] {
|
||||
const mergedClosable = useInnerClosable(closable, closeIcon, defaultClosable);
|
||||
if (!mergedClosable) {
|
||||
return [false, null];
|
||||
}
|
||||
const mergedCloseIcon =
|
||||
typeof closeIcon === 'boolean' || closeIcon === undefined || closeIcon === null
|
||||
? defaultCloseIcon
|
||||
: closeIcon;
|
||||
return [true, customCloseIconRender ? customCloseIconRender(mergedCloseIcon) : mergedCloseIcon];
|
||||
}
|
@ -576,35 +576,6 @@ exports[`renders components/alert/demo/closable.tsx extend context correctly 1`]
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/alert/demo/close-text.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-info ant-alert-no-icon"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Info Text
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-alert-close-text"
|
||||
>
|
||||
Close Now
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/alert/demo/custom-icon.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
|
@ -576,35 +576,6 @@ exports[`renders components/alert/demo/closable.tsx correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/alert/demo/close-text.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-info ant-alert-no-icon"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Info Text
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-alert-close-text"
|
||||
>
|
||||
Close Now
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/alert/demo/custom-icon.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
|
@ -1,4 +1,5 @@
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
import React from 'react';
|
||||
import Alert from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
@ -141,4 +142,30 @@ describe('Alert', () => {
|
||||
const { container } = render(<Alert description="description" />);
|
||||
expect(!!container.querySelector('.ant-alert-message')).toBe(false);
|
||||
});
|
||||
|
||||
it('close button should be hidden when closeIcon setting to null or false', () => {
|
||||
const { container, rerender } = render(<Alert closeIcon={null} />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')).toBeFalsy();
|
||||
rerender(<Alert closeIcon={false} />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')).toBeFalsy();
|
||||
rerender(<Alert closeIcon />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')).toBeTruthy();
|
||||
rerender(<Alert />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should warning when using closeText', () => {
|
||||
resetWarned();
|
||||
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const { container } = render(<Alert closeText="close" />);
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
`Warning: [antd: Alert] \`closeText\` is deprecated. Please use \`closeIcon\` instead.`,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-alert-close-icon')?.textContent).toBe('close');
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert, Space } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const onClose = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
console.log(e, 'I was closed.');
|
||||
@ -10,14 +10,14 @@ const App: React.FC = () => (
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
closable
|
||||
closeIcon
|
||||
onClose={onClose}
|
||||
/>
|
||||
<Alert
|
||||
message="Error Text"
|
||||
description="Error Description Error Description Error Description Error Description Error Description Error Description"
|
||||
type="error"
|
||||
closable
|
||||
closeIcon
|
||||
onClose={onClose}
|
||||
/>
|
||||
</Space>
|
||||
|
@ -1,7 +0,0 @@
|
||||
## zh-CN
|
||||
|
||||
可以自定义关闭,自定义的文字会替换原先的关闭 `Icon`。
|
||||
|
||||
## en-US
|
||||
|
||||
Replace the default icon with customized text.
|
@ -1,6 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Alert } from 'antd';
|
||||
|
||||
const App: React.FC = () => <Alert message="Info Text" type="info" closeText="Close Now" />;
|
||||
|
||||
export default App;
|
@ -25,7 +25,6 @@ Alert component for feedback.
|
||||
<code src="./demo/closable.tsx">Closable</code>
|
||||
<code src="./demo/description.tsx">Description</code>
|
||||
<code src="./demo/icon.tsx">Icon</code>
|
||||
<code src="./demo/close-text.tsx">Customized Close Text</code>
|
||||
<code src="./demo/banner.tsx" iframe="250">Banner</code>
|
||||
<code src="./demo/loop-banner.tsx">Loop Banner</code>
|
||||
<code src="./demo/smooth-closed.tsx">Smoothly Unmount</code>
|
||||
@ -40,9 +39,7 @@ Alert component for feedback.
|
||||
| action | The action of Alert | ReactNode | - | 4.9.0 |
|
||||
| afterClose | Called when close animation is finished | () => void | - | |
|
||||
| banner | Whether to show as banner | boolean | false | |
|
||||
| closable | Whether Alert can be closed | boolean | - | |
|
||||
| closeText | Close text to show | ReactNode | - | |
|
||||
| closeIcon | Custom close icon | ReactNode | `<CloseOutlined />` | 4.18.0 |
|
||||
| closeIcon | Custom close icon, >=5.7.0: close button will be hidden when setting to `null` or `false` | boolean \| ReactNode | `<CloseOutlined />` | |
|
||||
| description | Additional content of Alert | ReactNode | - | |
|
||||
| icon | Custom icon, effective when `showIcon` is true | ReactNode | - | |
|
||||
| message | Content of Alert | ReactNode | - | |
|
||||
|
@ -9,6 +9,7 @@ import pickAttrs from 'rc-util/lib/pickAttrs';
|
||||
import type { ReactElement } from 'react';
|
||||
import * as React from 'react';
|
||||
import { replaceElement } from '../_util/reactNode';
|
||||
import warning from '../_util/warning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
|
||||
@ -20,7 +21,10 @@ export interface AlertProps {
|
||||
type?: 'success' | 'info' | 'warning' | 'error';
|
||||
/** Whether Alert can be closed */
|
||||
closable?: boolean;
|
||||
/** Close text to show */
|
||||
/**
|
||||
* @deprecated please use `closeIcon` instead.
|
||||
* Close text to show
|
||||
*/
|
||||
closeText?: React.ReactNode;
|
||||
/** Content of Alert */
|
||||
message?: React.ReactNode;
|
||||
@ -41,7 +45,7 @@ export interface AlertProps {
|
||||
banner?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
/** Custom closeIcon */
|
||||
closeIcon?: React.ReactNode;
|
||||
closeIcon?: boolean | React.ReactNode;
|
||||
action?: React.ReactNode;
|
||||
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
||||
@ -78,16 +82,17 @@ const IconNode: React.FC<IconNodeProps> = (props) => {
|
||||
interface CloseIconProps {
|
||||
isClosable: boolean;
|
||||
prefixCls: AlertProps['prefixCls'];
|
||||
closeText: AlertProps['closeText'];
|
||||
closeIcon: AlertProps['closeIcon'];
|
||||
handleClose: AlertProps['onClose'];
|
||||
}
|
||||
|
||||
const CloseIcon: React.FC<CloseIconProps> = (props) => {
|
||||
const { isClosable, closeText, prefixCls, closeIcon, handleClose } = props;
|
||||
const { isClosable, prefixCls, closeIcon, handleClose } = props;
|
||||
const mergedCloseIcon =
|
||||
closeIcon === true || closeIcon === undefined ? <CloseOutlined /> : closeIcon;
|
||||
return isClosable ? (
|
||||
<button type="button" onClick={handleClose} className={`${prefixCls}-close-icon`} tabIndex={0}>
|
||||
{closeText ? <span className={`${prefixCls}-close-text`}>{closeText}</span> : closeIcon}
|
||||
{mergedCloseIcon}
|
||||
</button>
|
||||
) : null;
|
||||
};
|
||||
@ -111,12 +116,14 @@ const Alert: CompoundedComponent = ({
|
||||
showIcon,
|
||||
closable,
|
||||
closeText,
|
||||
closeIcon = <CloseOutlined />,
|
||||
closeIcon,
|
||||
action,
|
||||
...props
|
||||
}) => {
|
||||
const [closed, setClosed] = React.useState(false);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(!closeText, 'Alert', '`closeText` is deprecated. Please use `closeIcon` instead.');
|
||||
}
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('alert', customizePrefixCls);
|
||||
@ -136,8 +143,18 @@ const Alert: CompoundedComponent = ({
|
||||
return banner ? 'warning' : 'info';
|
||||
};
|
||||
|
||||
// closeable when closeText is assigned
|
||||
const isClosable = closeText ? true : closable;
|
||||
// closeable when closeText or closeIcon is assigned
|
||||
const isClosable = React.useMemo(() => {
|
||||
if (closeText) {
|
||||
return true;
|
||||
}
|
||||
if (typeof closable === 'boolean') {
|
||||
return closable;
|
||||
}
|
||||
// should be true when closeIcon is 0 or ''
|
||||
return closeIcon !== false && closeIcon !== null && closeIcon !== undefined;
|
||||
}, [closeText, closeIcon, closable]);
|
||||
|
||||
const type = getType();
|
||||
|
||||
// banner mode defaults to Icon
|
||||
@ -199,10 +216,9 @@ const Alert: CompoundedComponent = ({
|
||||
</div>
|
||||
{action ? <div className={`${prefixCls}-action`}>{action}</div> : null}
|
||||
<CloseIcon
|
||||
isClosable={!!isClosable}
|
||||
closeText={closeText}
|
||||
isClosable={isClosable}
|
||||
prefixCls={prefixCls}
|
||||
closeIcon={closeIcon}
|
||||
closeIcon={closeText || closeIcon}
|
||||
handleClose={handleClose}
|
||||
/>
|
||||
</div>
|
||||
|
@ -26,7 +26,6 @@ group:
|
||||
<code src="./demo/closable.tsx">可关闭的警告提示</code>
|
||||
<code src="./demo/description.tsx">含有辅助性文字介绍</code>
|
||||
<code src="./demo/icon.tsx">图标</code>
|
||||
<code src="./demo/close-text.tsx">自定义关闭</code>
|
||||
<code src="./demo/banner.tsx" iframe="250">顶部公告</code>
|
||||
<code src="./demo/loop-banner.tsx">轮播的公告</code>
|
||||
<code src="./demo/smooth-closed.tsx">平滑地卸载</code>
|
||||
@ -41,9 +40,7 @@ group:
|
||||
| action | 自定义操作项 | ReactNode | - | 4.9.0 |
|
||||
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | |
|
||||
| banner | 是否用作顶部公告 | boolean | false | |
|
||||
| closable | 默认不显示关闭按钮 | boolean | - | |
|
||||
| closeText | 自定义关闭按钮 | ReactNode | - | |
|
||||
| closeIcon | 自定义关闭 Icon | ReactNode | `<CloseOutlined />` | 4.18.0 |
|
||||
| closeIcon | 自定义关闭 Icon,>=5.7.0: 设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | `<CloseOutlined />` | |
|
||||
| description | 警告提示的辅助性文字介绍 | ReactNode | - | |
|
||||
| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | |
|
||||
| message | 警告提示内容 | ReactNode | - | |
|
||||
|
@ -3,12 +3,12 @@ import useEvent from 'rc-util/lib/hooks/useEvent';
|
||||
import * as React from 'react';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
|
||||
import Affix from '../affix';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import scrollTo from '../_util/scrollTo';
|
||||
import warning from '../_util/warning';
|
||||
import Affix from '../affix';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { AnchorLinkBaseProps } from './AnchorLink';
|
||||
import AnchorLink from './AnchorLink';
|
||||
import AnchorContext from './context';
|
||||
@ -77,6 +77,7 @@ export interface AnchorProps {
|
||||
onChange?: (currentActiveLink: string) => void;
|
||||
items?: AnchorLinkItemProps[];
|
||||
direction?: AnchorDirection;
|
||||
replace?: boolean;
|
||||
}
|
||||
|
||||
interface InternalAnchorProps extends AnchorProps {
|
||||
@ -127,6 +128,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
onChange,
|
||||
getContainer,
|
||||
getCurrentAnchor,
|
||||
replace,
|
||||
} = props;
|
||||
|
||||
// =================== Warning =====================
|
||||
@ -296,7 +298,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
const createNestedLink = (options?: AnchorLinkItemProps[]) =>
|
||||
Array.isArray(options)
|
||||
? options.map((item) => (
|
||||
<AnchorLink {...item} key={item.key}>
|
||||
<AnchorLink replace={replace} {...item} key={item.key}>
|
||||
{anchorDirection === 'vertical' && createNestedLink(item.children)}
|
||||
</AnchorLink>
|
||||
))
|
||||
|
@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { AntAnchor } from './Anchor';
|
||||
import AnchorContext from './context';
|
||||
|
||||
@ -11,6 +11,7 @@ export interface AnchorLinkBaseProps {
|
||||
target?: string;
|
||||
title: React.ReactNode;
|
||||
className?: string;
|
||||
replace?: boolean;
|
||||
}
|
||||
|
||||
export interface AnchorLinkProps extends AnchorLinkBaseProps {
|
||||
@ -18,7 +19,15 @@ export interface AnchorLinkProps extends AnchorLinkBaseProps {
|
||||
}
|
||||
|
||||
const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
|
||||
const { href = '#', title, prefixCls: customizePrefixCls, children, className, target } = props;
|
||||
const {
|
||||
href,
|
||||
title,
|
||||
prefixCls: customizePrefixCls,
|
||||
children,
|
||||
className,
|
||||
target,
|
||||
replace,
|
||||
} = props;
|
||||
|
||||
const context = React.useContext<AntAnchor | undefined>(AnchorContext);
|
||||
|
||||
@ -32,6 +41,10 @@ const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
|
||||
}, [href]);
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
||||
if (replace) {
|
||||
e.preventDefault();
|
||||
window.location.replace(href);
|
||||
}
|
||||
onClick?.(e, { title, href });
|
||||
scrollTo?.(href);
|
||||
};
|
||||
|
@ -20,6 +20,12 @@ const getHashUrl = () => `Anchor-API-${idCounter++}`;
|
||||
|
||||
jest.mock('scroll-into-view-if-needed', () => jest.fn());
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
replace: jest.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
describe('Anchor Render', () => {
|
||||
const getBoundingClientRectMock = jest.spyOn(
|
||||
HTMLHeadingElement.prototype,
|
||||
@ -344,6 +350,17 @@ describe('Anchor Render', () => {
|
||||
expect(link).toEqual({ href, title });
|
||||
});
|
||||
|
||||
it('replaces item href in browser history', () => {
|
||||
const hash = getHashUrl();
|
||||
|
||||
const href = `#${hash}`;
|
||||
const title = hash;
|
||||
const { container } = render(<Anchor replace items={[{ key: hash, href, title }]} />);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="${href}"]`)!);
|
||||
expect(window.location.replace).toHaveBeenCalledWith(href);
|
||||
});
|
||||
|
||||
it('onChange event', () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
|
@ -559,6 +559,85 @@ exports[`renders components/anchor/demo/onClick.tsx extend context correctly 1`]
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/replace.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-16"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height: 100vh; background: rgba(255, 0, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height: 100vh; background: rgba(0, 255, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height: 100vh; background: rgba(0, 0, 255, 0.02);"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-8"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink ant-anchor-ink-visible"
|
||||
style="top: 0px; height: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link ant-anchor-link-active"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title ant-anchor-link-title-active"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/static.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
|
@ -555,6 +555,84 @@ exports[`renders components/anchor/demo/onClick.tsx correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/replace.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-16"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height:100vh;background:rgba(255,0,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height:100vh;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height:100vh;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-8"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/static.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
|
7
components/anchor/demo/replace.md
Normal file
7
components/anchor/demo/replace.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
替换浏览器历史记录中的路径,后退按钮将返回到上一页而不是上一个锚点。
|
||||
|
||||
## en-US
|
||||
|
||||
Replace path in browser history, so back button returns to previous page instead of previous anchor item.
|
36
components/anchor/demo/replace.tsx
Normal file
36
components/anchor/demo/replace.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { Anchor, Col, Row } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<div id="part-1" style={{ height: '100vh', background: 'rgba(255,0,0,0.02)' }} />
|
||||
<div id="part-2" style={{ height: '100vh', background: 'rgba(0,255,0,0.02)' }} />
|
||||
<div id="part-3" style={{ height: '100vh', background: 'rgba(0,0,255,0.02)' }} />
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Anchor
|
||||
replace
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
{
|
||||
key: 'part-2',
|
||||
href: '#part-2',
|
||||
title: 'Part 2',
|
||||
},
|
||||
{
|
||||
key: 'part-3',
|
||||
href: '#part-3',
|
||||
title: 'Part 3',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
export default App;
|
@ -29,6 +29,7 @@ For displaying anchor hyperlinks on page and jumping between them.
|
||||
<code src="./demo/customizeHighlight.tsx">Customize the anchor highlight</code>
|
||||
<code src="./demo/targetOffset.tsx" iframe="200">Set Anchor scroll offset</code>
|
||||
<code src="./demo/onChange.tsx">Listening for anchor link change</code>
|
||||
<code src="./demo/replace.tsx" iframe="200">Replace href in history</code>
|
||||
<code src="./demo/legacy-anchor.tsx" debug>Deprecated JSX demo</code>
|
||||
<code src="./demo/component-token.tsx" debug>Component Token</code>
|
||||
|
||||
@ -49,6 +50,7 @@ For displaying anchor hyperlinks on page and jumping between them.
|
||||
| onClick | Set the handler to handle `click` event | (e: MouseEvent, link: object) => void | - | |
|
||||
| items | Data configuration option content, support nesting through children | { key, href, title, target, children }\[] [see](#anchoritem) | - | 5.1.0 |
|
||||
| direction | Set Anchor direction | `vertical` \| `horizontal` | `vertical` | 5.2.0 |
|
||||
| replace | Replace items' href in browser history instead of pushing it | boolean | false | 5.7.0 |
|
||||
|
||||
### AnchorItem
|
||||
|
||||
@ -59,6 +61,7 @@ For displaying anchor hyperlinks on page and jumping between them.
|
||||
| target | Specifies where to display the linked URL | string | | |
|
||||
| title | The content of hyperlink | ReactNode | | |
|
||||
| children | Nested Anchor Link, `Attention: This attribute does not support horizontal orientation` | [AnchorItem](#anchoritem)\[] | - | |
|
||||
| replace | Replace item href in browser history instead of pushing it | boolean | false | 5.7.0 |
|
||||
|
||||
### Link Props
|
||||
|
||||
|
@ -30,6 +30,7 @@ group:
|
||||
<code src="./demo/customizeHighlight.tsx">自定义锚点高亮</code>
|
||||
<code src="./demo/targetOffset.tsx" iframe="200">设置锚点滚动偏移量</code>
|
||||
<code src="./demo/onChange.tsx">监听锚点链接改变</code>
|
||||
<code src="./demo/replace.tsx" iframe="200">替换历史中的 href</code>
|
||||
<code src="./demo/legacy-anchor.tsx" debug>废弃的 JSX 示例</code>
|
||||
<code src="./demo/component-token.tsx" debug>组件 Token</code>
|
||||
|
||||
@ -50,6 +51,7 @@ group:
|
||||
| onClick | `click` 事件的 handler | (e: MouseEvent, link: object) => void | - | |
|
||||
| items | 数据化配置选项内容,支持通过 children 嵌套 | { key, href, title, target, children }\[] [具体见](#anchoritem) | - | 5.1.0 |
|
||||
| direction | 设置导航方向 | `vertical` \| `horizontal` | `vertical` | 5.2.0 |
|
||||
| replace | 替换浏览器历史记录中项目的 href 而不是推送它 | boolean | false | 5.7.0 |
|
||||
|
||||
### AnchorItem
|
||||
|
||||
@ -60,6 +62,7 @@ group:
|
||||
| target | 该属性指定在何处显示链接的资源 | string | - | |
|
||||
| title | 文字内容 | ReactNode | - | |
|
||||
| children | 嵌套的 Anchor Link,`注意:水平方向该属性不支持` | [AnchorItem](#anchoritem)\[] | - | |
|
||||
| replace | 替换浏览器历史记录中的项目 href 而不是推送它 | boolean | false | 5.7.0 |
|
||||
|
||||
### Link Props
|
||||
|
||||
|
@ -7,8 +7,10 @@ import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import type { CSSProperties } from 'react';
|
||||
import React, { useContext, useRef, useState } from 'react';
|
||||
import genPurePanel from '../_util/PurePanel';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import type { ConfigConsumerProps } from '../config-provider/context';
|
||||
import { ConfigContext } from '../config-provider/context';
|
||||
import useSize from '../config-provider/hooks/useSize';
|
||||
import type { PopoverProps } from '../popover';
|
||||
import Popover from '../popover';
|
||||
import theme from '../theme';
|
||||
@ -26,8 +28,10 @@ import type {
|
||||
import useStyle from './style/index';
|
||||
import { customizePrefixCls, generateColor } from './util';
|
||||
|
||||
export interface ColorPickerProps
|
||||
extends Omit<RcColorPickerProps, 'onChange' | 'value' | 'defaultValue' | 'panelRender'> {
|
||||
export type ColorPickerProps = Omit<
|
||||
RcColorPickerProps,
|
||||
'onChange' | 'value' | 'defaultValue' | 'panelRender'
|
||||
> & {
|
||||
value?: Color | string;
|
||||
defaultValue?: Color | string;
|
||||
children?: React.ReactNode;
|
||||
@ -39,15 +43,15 @@ export interface ColorPickerProps
|
||||
allowClear?: boolean;
|
||||
presets?: PresetsItem[];
|
||||
arrow?: boolean | { pointAtCenter: boolean };
|
||||
showText?: boolean | ((color: Color) => React.ReactNode);
|
||||
styles?: { popup?: CSSProperties };
|
||||
size?: SizeType;
|
||||
rootClassName?: string;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
onFormatChange?: (format: ColorFormat) => void;
|
||||
onChange?: (value: Color, hex: string) => void;
|
||||
onClear?: () => void;
|
||||
getPopupContainer?: PopoverProps['getPopupContainer'];
|
||||
autoAdjustOverflow?: PopoverProps['autoAdjustOverflow'];
|
||||
}
|
||||
} & Pick<PopoverProps, 'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide'>;
|
||||
|
||||
type CompoundedComponent = React.FC<ColorPickerProps> & {
|
||||
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
|
||||
@ -66,8 +70,10 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
disabled,
|
||||
placement = 'bottomLeft',
|
||||
arrow = true,
|
||||
showText,
|
||||
style,
|
||||
className,
|
||||
size: customizeSize,
|
||||
rootClassName,
|
||||
styles,
|
||||
onFormatChange,
|
||||
@ -76,6 +82,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
onOpenChange,
|
||||
getPopupContainer,
|
||||
autoAdjustOverflow = true,
|
||||
destroyTooltipOnHide,
|
||||
} = props;
|
||||
|
||||
const { getPrefixCls, direction } = useContext<ConfigConsumerProps>(ConfigContext);
|
||||
@ -90,14 +97,29 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
postState: (openData) => !disabled && openData,
|
||||
onChange: onOpenChange,
|
||||
});
|
||||
const [formatValue, setFormatValue] = useMergedState(format, {
|
||||
value: format,
|
||||
onChange: onFormatChange,
|
||||
});
|
||||
|
||||
const [colorCleared, setColorCleared] = useState(false);
|
||||
|
||||
const prefixCls = getPrefixCls('color-picker', customizePrefixCls);
|
||||
|
||||
// ===================== Style =====================
|
||||
const mergedSize = useSize(customizeSize);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
const rtlCls = { [`${prefixCls}-rtl`]: direction };
|
||||
const mergeRootCls = classNames(rootClassName, rtlCls);
|
||||
const mergeCls = classNames(mergeRootCls, className, hashId);
|
||||
const mergeCls = classNames(
|
||||
{
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
},
|
||||
mergeRootCls,
|
||||
className,
|
||||
hashId,
|
||||
);
|
||||
const mergePopupCls = classNames(prefixCls, rtlCls);
|
||||
|
||||
const popupAllowCloseRef = useRef(true);
|
||||
@ -140,6 +162,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
rootClassName,
|
||||
getPopupContainer,
|
||||
autoAdjustOverflow,
|
||||
destroyTooltipOnHide,
|
||||
};
|
||||
|
||||
const colorBaseProps: ColorPickerBaseProps = {
|
||||
@ -149,8 +172,8 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
colorCleared,
|
||||
disabled,
|
||||
presets,
|
||||
format,
|
||||
onFormatChange,
|
||||
format: formatValue,
|
||||
onFormatChange: setFormatValue,
|
||||
};
|
||||
|
||||
return wrapSSR(
|
||||
@ -181,6 +204,8 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
prefixCls={prefixCls}
|
||||
disabled={disabled}
|
||||
colorCleared={colorCleared}
|
||||
showText={showText}
|
||||
format={formatValue}
|
||||
/>
|
||||
)}
|
||||
</Popover>,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -57,6 +57,11 @@ exports[`renders components/color-picker/demo/disabled.tsx correctly 1`] = `
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-trigger-text"
|
||||
>
|
||||
#1677FF
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -244,6 +249,201 @@ exports[`renders components/color-picker/demo/pure-panel.tsx correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/color-picker/demo/size.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-right:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger ant-color-picker-sm"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger ant-color-picker-lg"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger ant-color-picker-sm"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-trigger-text"
|
||||
>
|
||||
#1677FF
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-trigger-text"
|
||||
>
|
||||
#1677FF
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger ant-color-picker-lg"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-trigger-text"
|
||||
>
|
||||
#1677FF
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/color-picker/demo/text-render.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-trigger-text"
|
||||
>
|
||||
#1677FF
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-trigger-text"
|
||||
>
|
||||
<span>
|
||||
Custom Text (
|
||||
<!-- -->
|
||||
#1677ff
|
||||
<!-- -->
|
||||
)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/color-picker/demo/trigger.tsx correctly 1`] = `
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
|
@ -329,4 +329,48 @@ describe('ColorPicker', () => {
|
||||
|
||||
expect(container.querySelector('.ant-color-picker-presets-color-bright')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Should showText as render function work', async () => {
|
||||
const { container } = render(<ColorPicker showText={(color) => color.toHexString()} />);
|
||||
const targetEle = container.querySelector('.ant-color-picker-trigger-text');
|
||||
expect(targetEle).toBeTruthy();
|
||||
expect(targetEle?.innerHTML).toBe('#1677ff');
|
||||
});
|
||||
|
||||
it('Should showText work', async () => {
|
||||
const { container } = render(<ColorPicker open showText />);
|
||||
const targetEle = container.querySelector('.ant-color-picker-trigger-text');
|
||||
expect(targetEle).toBeTruthy();
|
||||
|
||||
fireEvent.mouseDown(
|
||||
container.querySelector('.ant-color-picker-format-select .ant-select-selector')!,
|
||||
);
|
||||
await waitFakeTimer();
|
||||
fireEvent.click(container.querySelector('.ant-select-item[title="HSB"]')!);
|
||||
await waitFakeTimer();
|
||||
expect(targetEle?.innerHTML).toEqual('hsb(215, 91%, 100%)');
|
||||
|
||||
fireEvent.mouseDown(
|
||||
container.querySelector('.ant-color-picker-format-select .ant-select-selector')!,
|
||||
);
|
||||
await waitFakeTimer();
|
||||
fireEvent.click(container.querySelector('.ant-select-item[title="RGB"]')!);
|
||||
await waitFakeTimer();
|
||||
expect(targetEle?.innerHTML).toEqual('rgb(22, 119, 255)');
|
||||
|
||||
fireEvent.mouseDown(
|
||||
container.querySelector('.ant-color-picker-format-select .ant-select-selector')!,
|
||||
);
|
||||
await waitFakeTimer();
|
||||
fireEvent.click(container.querySelector('.ant-select-item[title="HEX"]')!);
|
||||
await waitFakeTimer();
|
||||
expect(targetEle?.innerHTML).toEqual('#1677FF');
|
||||
});
|
||||
|
||||
it('Should size work', async () => {
|
||||
const { container: lg } = render(<ColorPicker size="large" />);
|
||||
expect(lg.querySelector('.ant-color-picker-lg')).toBeTruthy();
|
||||
const { container: sm } = render(<ColorPicker size="small" />);
|
||||
expect(sm.querySelector('.ant-color-picker-sm')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,11 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
import type { ColorGenInput } from '@rc-component/color-picker';
|
||||
import { Color as RcColor } from '@rc-component/color-picker';
|
||||
import { getHex } from './util';
|
||||
|
||||
export const toHexFormat = (value?: string, alpha?: boolean) =>
|
||||
value?.replace(/[^\w/]/gi, '').slice(0, alpha ? 8 : 6) || '';
|
||||
|
||||
export const getHex = (value?: string, alpha?: boolean) => (value ? toHexFormat(value, alpha) : '');
|
||||
|
||||
export interface Color
|
||||
extends Pick<
|
||||
|
@ -2,8 +2,9 @@ import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Input from '../../input';
|
||||
import type { Color } from '../color';
|
||||
import { toHexFormat } from '../color';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import { generateColor, toHexFormat } from '../util';
|
||||
import { generateColor } from '../util';
|
||||
|
||||
interface ColorHexInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||
value?: Color;
|
||||
|
@ -2,13 +2,16 @@ import { ColorBlock } from '@rc-component/color-picker';
|
||||
import classNames from 'classnames';
|
||||
import type { CSSProperties, MouseEventHandler } from 'react';
|
||||
import React, { forwardRef, useMemo } from 'react';
|
||||
import type { ColorPickerProps } from '../ColorPicker';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import { getAlphaColor } from '../util';
|
||||
import ColorClear from './ColorClear';
|
||||
|
||||
interface colorTriggerProps
|
||||
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'colorCleared' | 'disabled'> {
|
||||
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'colorCleared' | 'disabled' | 'format'> {
|
||||
color: Exclude<ColorPickerBaseProps['color'], undefined>;
|
||||
open?: boolean;
|
||||
showText?: ColorPickerProps['showText'];
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||
@ -17,7 +20,8 @@ interface colorTriggerProps
|
||||
}
|
||||
|
||||
const ColorTrigger = forwardRef<HTMLDivElement, colorTriggerProps>((props, ref) => {
|
||||
const { color, prefixCls, open, colorCleared, disabled, className, ...rest } = props;
|
||||
const { color, prefixCls, open, colorCleared, disabled, format, className, showText, ...rest } =
|
||||
props;
|
||||
const colorTriggerPrefixCls = `${prefixCls}-trigger`;
|
||||
|
||||
const containerNode = useMemo<React.ReactNode>(
|
||||
@ -30,6 +34,29 @@ const ColorTrigger = forwardRef<HTMLDivElement, colorTriggerProps>((props, ref)
|
||||
[color, colorCleared, prefixCls],
|
||||
);
|
||||
|
||||
const genColorString = () => {
|
||||
const hexString = color.toHexString().toUpperCase();
|
||||
const alpha = getAlphaColor(color);
|
||||
switch (format) {
|
||||
case 'rgb':
|
||||
return color.toRgbString();
|
||||
case 'hsb':
|
||||
return color.toHsbString();
|
||||
case 'hex':
|
||||
default:
|
||||
return alpha < 100 ? `${hexString.slice(0, 7)},${alpha}%` : hexString;
|
||||
}
|
||||
};
|
||||
|
||||
const renderText = () => {
|
||||
if (typeof showText === 'function') {
|
||||
return showText(color);
|
||||
}
|
||||
if (showText) {
|
||||
return genColorString();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
@ -40,6 +67,7 @@ const ColorTrigger = forwardRef<HTMLDivElement, colorTriggerProps>((props, ref)
|
||||
{...rest}
|
||||
>
|
||||
{containerNode}
|
||||
{showText && <div className={`${colorTriggerPrefixCls}-text`}>{renderText()}</div>}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ColorPicker } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
export default () => <ColorPicker disabled />;
|
||||
export default () => <ColorPicker showText disabled />;
|
||||
|
7
components/color-picker/demo/size.md
Normal file
7
components/color-picker/demo/size.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
触发器有大、中、小三种尺寸,若不设置 size,则尺寸为中。
|
||||
|
||||
## en-US
|
||||
|
||||
The trigger has three sizes: large, medium and small. If size is not set, the size will be medium.
|
19
components/color-picker/demo/size.tsx
Normal file
19
components/color-picker/demo/size.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { ColorPicker, Space } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const Demo = () => (
|
||||
<Space>
|
||||
<Space direction="vertical">
|
||||
<ColorPicker size="small" />
|
||||
<ColorPicker />
|
||||
<ColorPicker size="large" />
|
||||
</Space>
|
||||
<Space direction="vertical">
|
||||
<ColorPicker size="small" showText />
|
||||
<ColorPicker showText />
|
||||
<ColorPicker size="large" showText />
|
||||
</Space>
|
||||
</Space>
|
||||
);
|
||||
|
||||
export default Demo;
|
7
components/color-picker/demo/text-render.md
Normal file
7
components/color-picker/demo/text-render.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
渲染触发器的默认文本, `showText` 为 `true` 时生效。自定义文本时,可以使用 `showText` 为函数的方式,返回自定义的文本。
|
||||
|
||||
## en-US
|
||||
|
||||
Renders the default text of the trigger, effective when `showText` is `true`. When customizing text, you can use `showText` as a function to return custom text.
|
11
components/color-picker/demo/text-render.tsx
Normal file
11
components/color-picker/demo/text-render.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { ColorPicker, Space } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const Demo = () => (
|
||||
<Space direction="vertical">
|
||||
<ColorPicker showText />
|
||||
<ColorPicker showText={(color) => <span>Custom Text ({color.toHexString()})</span>} />
|
||||
</Space>
|
||||
);
|
||||
|
||||
export default Demo;
|
@ -20,8 +20,10 @@ Used when the user needs to customize the color selection.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/base.tsx">Basic Usage</code>
|
||||
<code src="./demo/size.tsx">Trigger size</code>
|
||||
<code src="./demo/controlled.tsx">controlled mode</code>
|
||||
<code src="./demo/disabled.tsx" debug>Disable</code>
|
||||
<code src="./demo/text-render.tsx">Rendering Trigger Text</code>
|
||||
<code src="./demo/disabled.tsx">Disable</code>
|
||||
<code src="./demo/allowClear.tsx">Clear Color</code>
|
||||
<code src="./demo/trigger.tsx">Custom Trigger</code>
|
||||
<code src="./demo/trigger-event.tsx">Custom Trigger Event</code>
|
||||
@ -34,23 +36,26 @@ Used when the user needs to customize the color selection.
|
||||
> This component is available since `antd@5.5.0`.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
| Property | Description | Type | Default |
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| format | Format of color | `rgb` \| `hex` \| `hsb` | `hex` |
|
||||
| value | Value of color | string \| `Color` | - |
|
||||
| defaultValue | Default value of color | string \| `Color` | - |
|
||||
| allowClear | Allow clearing color selected | boolean | false |
|
||||
| presets | Preset colors | `{ label: ReactNode, colors: Array<string \| Color> }[]` | - |
|
||||
| children | Trigger of ColorPicker | React.ReactNode | - |
|
||||
| trigger | ColorPicker trigger mode | `hover` \| `click` | `click` |
|
||||
| open | Whether to show popup | boolean | - |
|
||||
| disabled | Disable ColorPicker | boolean | - |
|
||||
| placement | Placement of popup | `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | `bottomLeft` |
|
||||
| arrow | Configuration for popup arrow | `boolean \| { pointAtCenter: boolean }` | `true` | - |
|
||||
| onChange | Callback when `value` is changed | `(value: Color, hex: string) => void` | - |
|
||||
| onFormatChange | Callback when `format` is changed | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - |
|
||||
| onOpenChange | Callback when `open` is changed | `(open: boolean) => void` | - |
|
||||
| onClear | Called when clear | `() => void` | - |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| :-- | :-- | :-- | :-- | :-- |
|
||||
| format | Format of color | `rgb` \| `hex` \| `hsb` | `hex` | |
|
||||
| value | Value of color | string \| `Color` | - | |
|
||||
| defaultValue | Default value of color | string \| `Color` | - | |
|
||||
| allowClear | Allow clearing color selected | boolean | false | |
|
||||
| presets | Preset colors | `{ label: ReactNode, colors: Array<string \| Color> }[]` | - | |
|
||||
| children | Trigger of ColorPicker | React.ReactNode | - | |
|
||||
| trigger | ColorPicker trigger mode | `hover` \| `click` | `click` | |
|
||||
| open | Whether to show popup | boolean | - | |
|
||||
| disabled | Disable ColorPicker | boolean | - | |
|
||||
| placement | Placement of popup | `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | `bottomLeft` | |
|
||||
| arrow | Configuration for popup arrow | `boolean \| { pointAtCenter: boolean }` | true | |
|
||||
| destroyTooltipOnHide | Whether destroy popover when hidden | `boolean` | false | 5.7.0 |
|
||||
| showText | show color text | boolean \| `(color: Color) => React.ReactNode` | - | 5.7.0 |
|
||||
| size | Setting the trigger size | `large` \| `middle` \| `small` | `middle` | 5.7.0 |
|
||||
| onChange | Callback when `value` is changed | `(value: Color, hex: string) => void` | - | |
|
||||
| onFormatChange | Callback when `format` is changed | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - | |
|
||||
| onOpenChange | Callback when `open` is changed | `(open: boolean) => void` | - | |
|
||||
| onClear | Called when clear | `() => void` | - | 5.6.0 |
|
||||
|
||||
### Color
|
||||
|
||||
|
@ -21,8 +21,10 @@ group:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/base.tsx">基本使用</code>
|
||||
<code src="./demo/size.tsx">触发器尺寸大小</code>
|
||||
<code src="./demo/controlled.tsx">受控模式</code>
|
||||
<code src="./demo/disabled.tsx" debug>禁用</code>
|
||||
<code src="./demo/text-render.tsx">渲染触发器文本</code>
|
||||
<code src="./demo/disabled.tsx">禁用</code>
|
||||
<code src="./demo/allowClear.tsx">清除颜色</code>
|
||||
<code src="./demo/trigger.tsx">自定义触发器</code>
|
||||
<code src="./demo/trigger-event.tsx">自定义触发事件</code>
|
||||
@ -35,23 +37,26 @@ group:
|
||||
> 自 `antd@5.5.0` 版本开始提供该组件。
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | `hex` |
|
||||
| value | 颜色的值 | string \| `Color` | - |
|
||||
| defaultValue | 颜色默认的值 | string \| `Color` | - |
|
||||
| allowClear | 允许清除选择的颜色 | boolean | false |
|
||||
| presets | 预设的颜色 | `{ label: ReactNode, colors: Array<string \| Color> }[]` | - |
|
||||
| children | 颜色选择器的触发器 | React.ReactNode | - |
|
||||
| trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` |
|
||||
| open | 是否显示弹出窗口 | boolean | - |
|
||||
| disabled | 禁用颜色选择器 | boolean | - |
|
||||
| placement | 弹出窗口的位置 | `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | `bottomLeft` |
|
||||
| arrow | 配置弹出的箭头 | `boolean \| { pointAtCenter: boolean }` | `true` | - |
|
||||
| onChange | 颜色变化的回调 | `(value: Color, hex: string) => void` | - |
|
||||
| onFormatChange | 颜色格式变化的回调 | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - |
|
||||
| onOpenChange | 当 `open` 被改变时的回调 | `(open: boolean) => void` | - |
|
||||
| onClear | 清除的回调 | `() => void` | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| :-- | :-- | :-- | :-- | :-- |
|
||||
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | `hex` | |
|
||||
| value | 颜色的值 | string \| `Color` | - | |
|
||||
| defaultValue | 颜色默认的值 | string \| `Color` | - | |
|
||||
| allowClear | 允许清除选择的颜色 | boolean | false | |
|
||||
| presets | 预设的颜色 | `{ label: ReactNode, colors: Array<string \| Color> }[]` | - | |
|
||||
| children | 颜色选择器的触发器 | React.ReactNode | - | |
|
||||
| trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` | |
|
||||
| open | 是否显示弹出窗口 | boolean | - | |
|
||||
| disabled | 禁用颜色选择器 | boolean | - | |
|
||||
| placement | 弹出窗口的位置 | `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | `bottomLeft` | |
|
||||
| arrow | 配置弹出的箭头 | `boolean \| { pointAtCenter: boolean }` | true | |
|
||||
| destroyTooltipOnHide | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
|
||||
| showText | 显示颜色文本 | boolean \| `(color: Color) => React.ReactNode` | - | 5.7.0 |
|
||||
| size | 设置触发器大小 | `large` \| `middle` \| `small` | `middle` | 5.7.0 |
|
||||
| onChange | 颜色变化的回调 | `(value: Color, hex: string) => void` | - | |
|
||||
| onFormatChange | 颜色格式变化的回调 | `(format: 'hex' \| 'rgb' \| 'hsb') => void` | - | |
|
||||
| onOpenChange | 当 `open` 被改变时的回调 | `(open: boolean) => void` | - | |
|
||||
| onClear | 清除的回调 | `() => void` | - | 5.6.0 |
|
||||
|
||||
### Color
|
||||
|
||||
|
@ -72,6 +72,46 @@ const genClearStyle = (token: ColorPickerToken, size: number): CSSObject => {
|
||||
};
|
||||
};
|
||||
|
||||
const genSizeStyle = (token: ColorPickerToken): CSSObject => {
|
||||
const {
|
||||
componentCls,
|
||||
controlHeightLG,
|
||||
controlHeightSM,
|
||||
controlHeight,
|
||||
controlHeightXS,
|
||||
borderRadius,
|
||||
borderRadiusSM,
|
||||
borderRadiusXS,
|
||||
borderRadiusLG,
|
||||
fontSizeLG,
|
||||
} = token;
|
||||
return {
|
||||
[`&${componentCls}-lg`]: {
|
||||
minWidth: controlHeightLG,
|
||||
height: controlHeightLG,
|
||||
borderRadius: borderRadiusLG,
|
||||
[`${componentCls}-color-block`]: {
|
||||
width: controlHeight,
|
||||
height: controlHeight,
|
||||
borderRadius,
|
||||
},
|
||||
[`${componentCls}-trigger-text`]: {
|
||||
fontSize: fontSizeLG,
|
||||
},
|
||||
},
|
||||
[`&${componentCls}-sm`]: {
|
||||
minWidth: controlHeightSM,
|
||||
height: controlHeightSM,
|
||||
borderRadius: borderRadiusSM,
|
||||
[`${componentCls}-color-block`]: {
|
||||
width: controlHeightXS,
|
||||
height: controlHeightXS,
|
||||
borderRadius: borderRadiusXS,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
||||
const {
|
||||
componentCls,
|
||||
@ -80,6 +120,7 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
||||
motionDurationMid,
|
||||
colorBgElevated,
|
||||
colorTextDisabled,
|
||||
colorText,
|
||||
colorBgContainerDisabled,
|
||||
borderRadius,
|
||||
marginXS,
|
||||
@ -90,6 +131,8 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
||||
colorPickerPresetColorSize,
|
||||
lineWidth,
|
||||
colorBorder,
|
||||
paddingXXS,
|
||||
fontSize,
|
||||
} = token;
|
||||
|
||||
return [
|
||||
@ -117,16 +160,23 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
||||
},
|
||||
|
||||
'&-trigger': {
|
||||
width: controlHeight,
|
||||
minWidth: controlHeight,
|
||||
height: controlHeight,
|
||||
borderRadius,
|
||||
border: `${lineWidth}px solid ${colorBorder}`,
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: `all ${motionDurationMid}`,
|
||||
background: colorBgElevated,
|
||||
padding: paddingXXS - lineWidth,
|
||||
[`${componentCls}-trigger-text`]: {
|
||||
marginInlineStart: marginXS,
|
||||
marginInlineEnd: marginXS - (paddingXXS - lineWidth),
|
||||
fontSize,
|
||||
color: colorText,
|
||||
},
|
||||
'&-active': {
|
||||
...genActiveStyle(token),
|
||||
borderColor: colorPrimary,
|
||||
@ -141,9 +191,13 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
||||
'&:hover': {
|
||||
borderColor: colorBgTextActive,
|
||||
},
|
||||
[`${componentCls}-trigger-text`]: {
|
||||
color: colorTextDisabled,
|
||||
},
|
||||
},
|
||||
...genClearStyle(token, controlHeightSM),
|
||||
...genColorBlockStyle(token, controlHeightSM),
|
||||
...genSizeStyle(token),
|
||||
},
|
||||
...genRtlStyle(token),
|
||||
},
|
||||
|
@ -13,8 +13,3 @@ export const generateColor = (color: ColorGenInput<Color>): Color => {
|
||||
};
|
||||
|
||||
export const getAlphaColor = (color: Color) => getRoundNumber(color.toHsb().a * 100);
|
||||
|
||||
export const toHexFormat = (value?: string, alpha?: boolean) =>
|
||||
value?.replace(/[^\w/]/gi, '').slice(0, alpha ? 8 : 6) || '';
|
||||
|
||||
export const getHex = (value?: string, alpha?: boolean) => (value ? toHexFormat(value, alpha) : '');
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import useSize from './hooks/useSize';
|
||||
|
||||
export type SizeType = 'small' | 'middle' | 'large' | undefined;
|
||||
|
||||
@ -11,8 +10,8 @@ export interface SizeContextProps {
|
||||
}
|
||||
|
||||
export const SizeContextProvider: React.FC<SizeContextProps> = ({ children, size }) => {
|
||||
const mergedSize = useSize(size);
|
||||
return <SizeContext.Provider value={mergedSize}>{children}</SizeContext.Provider>;
|
||||
const originSize = React.useContext<SizeType>(SizeContext);
|
||||
return <SizeContext.Provider value={size || originSize}>{children}</SizeContext.Provider>;
|
||||
};
|
||||
|
||||
export default SizeContext;
|
||||
|
@ -7,7 +7,6 @@ import { fireEvent, render } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import Input from '../../input';
|
||||
import Select from '../../select';
|
||||
import Space from '../../space';
|
||||
import Table from '../../table';
|
||||
|
||||
describe('ConfigProvider', () => {
|
||||
@ -124,78 +123,4 @@ describe('ConfigProvider', () => {
|
||||
expect(rendered).toBeTruthy();
|
||||
expect(cacheRenderEmpty).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Should Space classNames works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
space={{
|
||||
classNames: {
|
||||
item: 'test-classNames',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>Text1</span>
|
||||
<span>Text2</span>
|
||||
</Space>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-space-item.test-classNames')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Should Space className works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
space={{
|
||||
className: 'test-classNames',
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>Text1</span>
|
||||
<span>Text2</span>
|
||||
</Space>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-space.test-classNames')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Should Space styles works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
space={{
|
||||
styles: {
|
||||
item: {
|
||||
color: 'red',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>Text1</span>
|
||||
<span>Text2</span>
|
||||
</Space>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-space-item')?.getAttribute('style')).toEqual(
|
||||
'margin-right: 8px; color: red;',
|
||||
);
|
||||
});
|
||||
|
||||
it('Should Space style works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
space={{
|
||||
style: {
|
||||
color: 'red',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>Text1</span>
|
||||
<span>Text2</span>
|
||||
</Space>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-space')?.getAttribute('style')).toEqual('color: red;');
|
||||
});
|
||||
});
|
||||
|
152
components/config-provider/__tests__/style.test.tsx
Normal file
152
components/config-provider/__tests__/style.test.tsx
Normal file
@ -0,0 +1,152 @@
|
||||
import React from 'react';
|
||||
import ConfigProvider from '..';
|
||||
import { render } from '../../../tests/utils';
|
||||
import Divider from '../../divider';
|
||||
import Segmented from '../../segmented';
|
||||
import Space from '../../space';
|
||||
import Spin from '../../spin';
|
||||
import Typography from '../../typography';
|
||||
|
||||
describe('ConfigProvider support style and className props', () => {
|
||||
it('Should Space classNames works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
space={{
|
||||
classNames: {
|
||||
item: 'test-classNames',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>Text1</span>
|
||||
<span>Text2</span>
|
||||
</Space>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-space-item')).toHaveClass('test-classNames');
|
||||
});
|
||||
|
||||
it('Should Space className works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
space={{
|
||||
className: 'test-classNames',
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>Text1</span>
|
||||
<span>Text2</span>
|
||||
</Space>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-space')).toHaveClass('test-classNames');
|
||||
});
|
||||
|
||||
it('Should Space styles works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
space={{
|
||||
styles: {
|
||||
item: {
|
||||
color: 'red',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>Text1</span>
|
||||
<span>Text2</span>
|
||||
</Space>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-space-item')).toHaveStyle(
|
||||
'margin-right: 8px; color: red;',
|
||||
);
|
||||
});
|
||||
|
||||
it('Should Space style works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
space={{
|
||||
style: {
|
||||
color: 'red',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>Text1</span>
|
||||
<span>Text2</span>
|
||||
</Space>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-space')).toHaveStyle('color: red;');
|
||||
});
|
||||
|
||||
it('Should Divider className works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
divider={{
|
||||
className: 'config-provider-className',
|
||||
}}
|
||||
>
|
||||
<Divider />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-divider')).toHaveClass('config-provider-className');
|
||||
});
|
||||
|
||||
it('Should Divider style works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
divider={{
|
||||
style: {
|
||||
color: 'red',
|
||||
height: 80,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Divider />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-divider'))?.toHaveStyle({ color: 'red', height: '80px' });
|
||||
});
|
||||
|
||||
it('Should Typography className & style works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
typography={{ className: 'cp-typography', style: { backgroundColor: 'red' } }}
|
||||
>
|
||||
<Typography>test</Typography>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
const element = container.querySelector<HTMLElement>('.ant-typography');
|
||||
expect(element).toHaveClass('cp-typography');
|
||||
expect(element).toHaveStyle({ backgroundColor: 'red' });
|
||||
});
|
||||
|
||||
it('Should Spin className & style works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
spin={{ className: 'config-provider-spin', style: { backgroundColor: 'red' } }}
|
||||
>
|
||||
<Spin />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
const element = container.querySelector<HTMLDivElement>('.ant-spin');
|
||||
expect(element).toHaveClass('config-provider-spin');
|
||||
expect(element).toHaveStyle({ backgroundColor: 'red' });
|
||||
});
|
||||
|
||||
it('Should Segmented className & style works', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
segmented={{ className: 'config-provider-segmented', style: { backgroundColor: 'red' } }}
|
||||
>
|
||||
<Segmented options={['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']} />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
const element = container.querySelector<HTMLDivElement>('.ant-segmented');
|
||||
expect(element).toHaveClass('config-provider-segmented');
|
||||
expect(element).toHaveStyle({ backgroundColor: 'red' });
|
||||
});
|
||||
});
|
@ -36,15 +36,16 @@ export interface ThemeConfig {
|
||||
inherit?: boolean;
|
||||
}
|
||||
|
||||
interface componentStyleConfig {
|
||||
export interface componentStyleConfig {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export interface ButtonConfig extends componentStyleConfig {
|
||||
classNames?: ButtonProps['classNames'];
|
||||
styles?: ButtonProps['styles'];
|
||||
}
|
||||
|
||||
export interface ButtonConfig extends componentStyleConfig {}
|
||||
|
||||
export type PopupOverflow = 'viewport' | 'scroll';
|
||||
|
||||
export interface ConfigConsumerProps {
|
||||
@ -87,11 +88,25 @@ export interface ConfigConsumerProps {
|
||||
showSearch?: boolean;
|
||||
};
|
||||
button?: ButtonConfig;
|
||||
divider?: componentStyleConfig;
|
||||
typography?: {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
spin?: {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
segmented?: {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
}
|
||||
|
||||
const defaultGetPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {
|
||||
if (customizePrefixCls) return customizePrefixCls;
|
||||
|
||||
if (customizePrefixCls) {
|
||||
return customizePrefixCls;
|
||||
}
|
||||
return suffixCls ? `ant-${suffixCls}` : 'ant';
|
||||
};
|
||||
|
||||
|
@ -55,19 +55,14 @@ Some components use dynamic style to support wave effect. You can config `csp` p
|
||||
| componentSize | Config antd component size | `small` \| `middle` \| `large` | - | |
|
||||
| csp | Set [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) config | { nonce: string } | - | |
|
||||
| direction | Set direction of layout. See [demo](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
|
||||
| popupMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | - | 5.5.0 |
|
||||
| popupOverflow | Select like component popup logic. Can set to show in viewport or follow window scroll | 'viewport' \| 'scroll' <InlinePopover previewURL="https://user-images.githubusercontent.com/5378891/230344474-5b9f7e09-0a5d-49e8-bae8-7d2abed6c837.png"></InlinePopover> | 'viewport' | 5.5.0 |
|
||||
| form | Set Form common props | { validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) } | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0 |
|
||||
| getPopupContainer | To set the container of the popup element. The default is to create a `div` element in `body` | function(triggerNode) | () => document.body | |
|
||||
| getTargetContainer | Config Affix, Anchor scroll target container | () => HTMLElement | () => window | 4.2.0 |
|
||||
| iconPrefixCls | Set icon prefix className | string | `anticon` | 4.11.0 |
|
||||
| input | Set Input common props | { autoComplete?: string } | - | 4.2.0 |
|
||||
| select | Set Select common props | { showSearch?: boolean } | - | |
|
||||
| button | Set Select common props | { className?: string, style?: React.CSSProperties, classNames?: { icon: string }, styles?: { icon: React.CSSProperties } } | - | 5.6.0 |
|
||||
| locale | Language package setting, you can find the packages in [antd/locale](http://unpkg.com/antd/locale/) | object | - | |
|
||||
| popupMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | - | 5.5.0 |
|
||||
| popupOverflow | Select like component popup logic. Can set to show in viewport or follow window scroll | 'viewport' \| 'scroll' <InlinePopover previewURL="https://user-images.githubusercontent.com/5378891/230344474-5b9f7e09-0a5d-49e8-bae8-7d2abed6c837.png"></InlinePopover> | 'viewport' | 5.5.0 |
|
||||
| prefixCls | Set prefix className | string | `ant` | |
|
||||
| renderEmpty | Set empty content of components. Ref [Empty](/components/empty/) | function(componentName: string): ReactNode | - | |
|
||||
| space | Set Space common props, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: { item: string }, styles?: { item: React.CSSProperties } } | - | 5.6.0 |
|
||||
| theme | Set theme, ref [Customize Theme](/docs/react/customize-theme) | - | - | 5.0.0 |
|
||||
| virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 |
|
||||
|
||||
@ -102,6 +97,20 @@ const {
|
||||
| componentDisabled | antd component disabled state | boolean | - | 5.3.0 |
|
||||
| componentSize | antd component size state | `small` \| `middle` \| `large` | - | 5.3.0 |
|
||||
|
||||
### Component Config
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| button | Set Button common props | { className?: string, style?: React.CSSProperties, classNames?: { icon: string }, styles?: { icon: React.CSSProperties } } | - | 5.6.0 |
|
||||
| divider | Set Divider common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| form | Set Form common props | { validateMessages?: [ValidateMessages](/components/form/#validatemessages), requiredMark?: boolean \| `optional`, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options) } | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0 |
|
||||
| input | Set Input common props | { autoComplete?: string } | - | 4.2.0 |
|
||||
| segmented | Set Segmented common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| select | Set Select common props | { showSearch?: boolean } | - | |
|
||||
| space | Set Space common props, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: { item: string }, styles?: { item: React.CSSProperties } } | - | 5.6.0 |
|
||||
| spin | Set Spin common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| typography | Set Typography common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
|
||||
## FAQ
|
||||
|
||||
#### How to contribute a new language?
|
||||
|
@ -19,6 +19,7 @@ import { DesignTokenContext } from '../theme/internal';
|
||||
import defaultSeedToken from '../theme/themes/seed';
|
||||
import type {
|
||||
ButtonConfig,
|
||||
componentStyleConfig,
|
||||
ConfigConsumerProps,
|
||||
CSPConfig,
|
||||
DirectionType,
|
||||
@ -38,8 +39,8 @@ import SizeContext, { SizeContextProvider } from './SizeContext';
|
||||
import useStyle from './style';
|
||||
|
||||
/**
|
||||
* Since too many feedback using static method like `Modal.confirm` not getting theme,
|
||||
* we record the theme register info here to help developer get warning info.
|
||||
* Since too many feedback using static method like `Modal.confirm` not getting theme, we record the
|
||||
* theme register info here to help developer get warning info.
|
||||
*/
|
||||
let existThemeConfig = false;
|
||||
|
||||
@ -136,6 +137,19 @@ export interface ConfigProviderProps {
|
||||
popupOverflow?: PopupOverflow;
|
||||
theme?: ThemeConfig;
|
||||
button?: ButtonConfig;
|
||||
divider?: componentStyleConfig;
|
||||
typography?: {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
spin?: {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
segmented?: {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
}
|
||||
|
||||
interface ProviderChildrenProps extends ConfigProviderProps {
|
||||
@ -223,6 +237,10 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
|
||||
iconPrefixCls: customIconPrefixCls,
|
||||
theme,
|
||||
componentDisabled,
|
||||
segmented,
|
||||
spin,
|
||||
typography,
|
||||
divider,
|
||||
} = props;
|
||||
|
||||
// =================================== Warning ===================================
|
||||
@ -239,7 +257,9 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
|
||||
(suffixCls: string, customizePrefixCls?: string) => {
|
||||
const { prefixCls } = props;
|
||||
|
||||
if (customizePrefixCls) return customizePrefixCls;
|
||||
if (customizePrefixCls) {
|
||||
return customizePrefixCls;
|
||||
}
|
||||
|
||||
const mergedPrefixCls = prefixCls || parentContext.getPrefixCls('');
|
||||
|
||||
@ -272,6 +292,10 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = (props) => {
|
||||
getPrefixCls,
|
||||
iconPrefixCls,
|
||||
theme: mergedTheme,
|
||||
segmented,
|
||||
spin,
|
||||
typography,
|
||||
divider,
|
||||
};
|
||||
|
||||
const config = {
|
||||
|
@ -56,19 +56,14 @@ export default Demo;
|
||||
| componentSize | 设置 antd 组件大小 | `small` \| `middle` \| `large` | - | |
|
||||
| csp | 设置 [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) 配置 | { nonce: string } | - | |
|
||||
| direction | 设置文本展示方向。 [示例](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
|
||||
| popupMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`,当值小于选择框宽度时会被忽略。`false` 时会关闭虚拟滚动 | boolean \| number | - | 5.5.0 |
|
||||
| popupOverflow | Select 类组件弹层展示逻辑,默认为可视区域滚动,可配置成滚动区域滚动 | 'viewport' \| 'scroll' <InlinePopover previewURL="https://user-images.githubusercontent.com/5378891/230344474-5b9f7e09-0a5d-49e8-bae8-7d2abed6c837.png"></InlinePopover> | 'viewport' | 5.5.0 |
|
||||
| form | 设置 Form 组件的通用属性 | { validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)} | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0 |
|
||||
| getPopupContainer | 弹出框(Select, Tooltip, Menu 等等)渲染父节点,默认渲染到 body 上。 | function(triggerNode) | () => document.body | |
|
||||
| getTargetContainer | 配置 Affix、Anchor 滚动监听容器。 | () => HTMLElement | () => window | 4.2.0 |
|
||||
| iconPrefixCls | 设置图标统一样式前缀 | string | `anticon` | 4.11.0 |
|
||||
| input | 设置 Input 组件的通用属性 | { autoComplete?: string } | - | 4.2.0 |
|
||||
| select | 设置 Select 组件的通用属性 | { showSearch?: boolean } | - | |
|
||||
| button | 设置 Button 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: { icon: string }, styles?: { icon: React.CSSProperties } } | - | 5.6.0 |
|
||||
| locale | 语言包配置,语言包可到 [antd/locale](http://unpkg.com/antd/locale/) 目录下寻找 | object | - | |
|
||||
| popupMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`,当值小于选择框宽度时会被忽略。`false` 时会关闭虚拟滚动 | boolean \| number | - | 5.5.0 |
|
||||
| popupOverflow | Select 类组件弹层展示逻辑,默认为可视区域滚动,可配置成滚动区域滚动 | 'viewport' \| 'scroll' <InlinePopover previewURL="https://user-images.githubusercontent.com/5378891/230344474-5b9f7e09-0a5d-49e8-bae8-7d2abed6c837.png"></InlinePopover> | 'viewport' | 5.5.0 |
|
||||
| prefixCls | 设置统一样式前缀 | string | `ant` | |
|
||||
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty-cn) | function(componentName: string): ReactNode | - | |
|
||||
| space | 设置 Space 的通用属性,参考 [Space](/components/space-cn) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: { item: string }, styles?: { item: React.CSSProperties } } | - | 5.6.0 |
|
||||
| theme | 设置主题,参考 [定制主题](/docs/react/customize-theme-cn) | - | - | 5.0.0 |
|
||||
| virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 4.3.0 |
|
||||
|
||||
@ -104,6 +99,20 @@ const {
|
||||
| componentDisabled | antd 组件禁用状态 | boolean | - | 5.3.0 |
|
||||
| componentSize | antd 组件大小状态 | `small` \| `middle` \| `large` | - | 5.3.0 |
|
||||
|
||||
### 组件配置
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| button | 设置 Button 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: { icon: string }, styles?: { icon: React.CSSProperties } } | - | 5.6.0 |
|
||||
| divider | 设置 Divider 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| form | 设置 Form 组件的通用属性 | { validateMessages?: [ValidateMessages](/components/form-cn#validatemessages), requiredMark?: boolean \| `optional`, colon?: boolean, scrollToFirstError?: boolean \| [Options](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)} | - | requiredMark: 4.8.0; colon: 4.18.0; scrollToFirstError: 5.2.0 |
|
||||
| input | 设置 Input 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| segmented | 设置 Segmented 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| select | 设置 Select 组件的通用属性 | { showSearch?: boolean } | - | |
|
||||
| space | 设置 Space 的通用属性,参考 [Space](/components/space-cn) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: { item: string }, styles?: { item: React.CSSProperties } } | - | 5.6.0 |
|
||||
| spin | 设置 Spin 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| typography | 设置 Typography 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
|
||||
## FAQ
|
||||
|
||||
#### 如何增加一个新的语言包?
|
||||
|
5
components/date-picker/generatePicker/Components.ts
Normal file
5
components/date-picker/generatePicker/Components.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import PickerButton from '../PickerButton';
|
||||
|
||||
const Components = { button: PickerButton };
|
||||
|
||||
export default Components;
|
@ -8,7 +8,6 @@ import type { GenerateConfig } from 'rc-picker/lib/generate/index';
|
||||
import * as React from 'react';
|
||||
import { forwardRef, useContext, useImperativeHandle } from 'react';
|
||||
import type { RangePickerProps } from '.';
|
||||
import { Components, getTimeProps } from '.';
|
||||
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
|
||||
import warning from '../../_util/warning';
|
||||
import { ConfigContext } from '../../config-provider';
|
||||
@ -19,7 +18,8 @@ import { useLocale } from '../../locale';
|
||||
import { useCompactItemContext } from '../../space/Compact';
|
||||
import enUS from '../locale/en_US';
|
||||
import useStyle from '../style';
|
||||
import { getRangePlaceholder, transPlacement2DropdownAlign } from '../util';
|
||||
import { getRangePlaceholder, getTimeProps, transPlacement2DropdownAlign } from '../util';
|
||||
import Components from './Components';
|
||||
import type { CommonPickerMethods, PickerComponentClass } from './interface';
|
||||
|
||||
export default function generateRangePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
|
@ -8,7 +8,6 @@ import type { PickerMode } from 'rc-picker/lib/interface';
|
||||
import * as React from 'react';
|
||||
import { forwardRef, useContext, useImperativeHandle } from 'react';
|
||||
import type { PickerProps, PickerTimeProps } from '.';
|
||||
import { Components, getTimeProps } from '.';
|
||||
import type { InputStatus } from '../../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
|
||||
import warning from '../../_util/warning';
|
||||
@ -20,7 +19,8 @@ import { useLocale } from '../../locale';
|
||||
import { useCompactItemContext } from '../../space/Compact';
|
||||
import enUS from '../locale/en_US';
|
||||
import useStyle from '../style';
|
||||
import { getPlaceholder, transPlacement2DropdownAlign } from '../util';
|
||||
import { getPlaceholder, getTimeProps, transPlacement2DropdownAlign } from '../util';
|
||||
import Components from './Components';
|
||||
import type { CommonPickerMethods, DatePickRef, PickerComponentClass } from './interface';
|
||||
|
||||
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
|
@ -9,71 +9,13 @@ import type {
|
||||
RangePickerTimeProps as RCRangePickerTimeProps,
|
||||
} from 'rc-picker/lib/RangePicker';
|
||||
import type { GenerateConfig } from 'rc-picker/lib/generate/index';
|
||||
import type { PickerMode, Locale as RcPickerLocale } from 'rc-picker/lib/interface';
|
||||
import type { SharedTimeProps } from 'rc-picker/lib/panels/TimePanel';
|
||||
import type { Locale as RcPickerLocale } from 'rc-picker/lib/interface';
|
||||
import type { InputStatus } from '../../_util/statusUtils';
|
||||
import type { SizeType } from '../../config-provider/SizeContext';
|
||||
import type { TimePickerLocale } from '../../time-picker';
|
||||
import PickerButton from '../PickerButton';
|
||||
import generateRangePicker from './generateRangePicker';
|
||||
import generateSinglePicker from './generateSinglePicker';
|
||||
|
||||
export const Components = { button: PickerButton };
|
||||
|
||||
function toArray<T>(list: T | T[]): T[] {
|
||||
if (!list) {
|
||||
return [];
|
||||
}
|
||||
return Array.isArray(list) ? list : [list];
|
||||
}
|
||||
|
||||
export function getTimeProps<DateType, DisabledTime>(
|
||||
props: { format?: string; picker?: PickerMode } & Omit<
|
||||
SharedTimeProps<DateType>,
|
||||
'disabledTime'
|
||||
> & {
|
||||
disabledTime?: DisabledTime;
|
||||
},
|
||||
) {
|
||||
const { format, picker, showHour, showMinute, showSecond, use12Hours } = props;
|
||||
|
||||
const firstFormat = toArray(format)[0];
|
||||
const showTimeObj = { ...props };
|
||||
|
||||
if (firstFormat && typeof firstFormat === 'string') {
|
||||
if (!firstFormat.includes('s') && showSecond === undefined) {
|
||||
showTimeObj.showSecond = false;
|
||||
}
|
||||
if (!firstFormat.includes('m') && showMinute === undefined) {
|
||||
showTimeObj.showMinute = false;
|
||||
}
|
||||
if (
|
||||
!firstFormat.includes('H') &&
|
||||
!firstFormat.includes('h') &&
|
||||
!firstFormat.includes('K') &&
|
||||
!firstFormat.includes('k') &&
|
||||
showHour === undefined
|
||||
) {
|
||||
showTimeObj.showHour = false;
|
||||
}
|
||||
if ((firstFormat.includes('a') || firstFormat.includes('A')) && use12Hours === undefined) {
|
||||
showTimeObj.use12Hours = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (picker === 'time') {
|
||||
return showTimeObj;
|
||||
}
|
||||
|
||||
if (typeof firstFormat === 'function') {
|
||||
// format of showTime should use default when format is custom format function
|
||||
delete showTimeObj.format;
|
||||
}
|
||||
|
||||
return {
|
||||
showTime: showTimeObj,
|
||||
};
|
||||
}
|
||||
const DataPickerPlacements = ['bottomLeft', 'bottomRight', 'topLeft', 'topRight'] as const;
|
||||
type DataPickerPlacement = (typeof DataPickerPlacements)[number];
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { AlignType } from '@rc-component/trigger';
|
||||
import type { PickerMode } from 'rc-picker/lib/interface';
|
||||
import type { SharedTimeProps } from 'rc-picker/lib/panels/TimePanel';
|
||||
import type { SelectCommonPlacement } from '../_util/motion';
|
||||
import type { DirectionType } from '../config-provider';
|
||||
import type { PickerLocale } from './generatePicker';
|
||||
@ -104,3 +105,58 @@ export function transPlacement2DropdownAlign(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toArray<T>(list: T | T[]): T[] {
|
||||
if (!list) {
|
||||
return [];
|
||||
}
|
||||
return Array.isArray(list) ? list : [list];
|
||||
}
|
||||
|
||||
export function getTimeProps<DateType, DisabledTime>(
|
||||
props: { format?: string; picker?: PickerMode } & Omit<
|
||||
SharedTimeProps<DateType>,
|
||||
'disabledTime'
|
||||
> & {
|
||||
disabledTime?: DisabledTime;
|
||||
},
|
||||
) {
|
||||
const { format, picker, showHour, showMinute, showSecond, use12Hours } = props;
|
||||
|
||||
const firstFormat = toArray(format)[0];
|
||||
const showTimeObj = { ...props };
|
||||
|
||||
if (firstFormat && typeof firstFormat === 'string') {
|
||||
if (!firstFormat.includes('s') && showSecond === undefined) {
|
||||
showTimeObj.showSecond = false;
|
||||
}
|
||||
if (!firstFormat.includes('m') && showMinute === undefined) {
|
||||
showTimeObj.showMinute = false;
|
||||
}
|
||||
if (
|
||||
!firstFormat.includes('H') &&
|
||||
!firstFormat.includes('h') &&
|
||||
!firstFormat.includes('K') &&
|
||||
!firstFormat.includes('k') &&
|
||||
showHour === undefined
|
||||
) {
|
||||
showTimeObj.showHour = false;
|
||||
}
|
||||
if ((firstFormat.includes('a') || firstFormat.includes('A')) && use12Hours === undefined) {
|
||||
showTimeObj.use12Hours = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (picker === 'time') {
|
||||
return showTimeObj;
|
||||
}
|
||||
|
||||
if (typeof firstFormat === 'function') {
|
||||
// format of showTime should use default when format is custom format function
|
||||
delete showTimeObj.format;
|
||||
}
|
||||
|
||||
return {
|
||||
showTime: showTimeObj,
|
||||
};
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export interface DividerProps {
|
||||
}
|
||||
|
||||
const Divider: React.FC<DividerProps> = (props) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const { getPrefixCls, direction, divider } = React.useContext(ConfigContext);
|
||||
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
@ -31,6 +31,7 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
children,
|
||||
dashed,
|
||||
plain,
|
||||
style,
|
||||
...restProps
|
||||
} = props;
|
||||
const prefixCls = getPrefixCls('divider', customizePrefixCls);
|
||||
@ -42,6 +43,7 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
const hasCustomMarginRight = orientation === 'right' && orientationMargin != null;
|
||||
const classString = classNames(
|
||||
prefixCls,
|
||||
divider?.className,
|
||||
hashId,
|
||||
`${prefixCls}-${type}`,
|
||||
{
|
||||
@ -82,7 +84,12 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
}
|
||||
|
||||
return wrapSSR(
|
||||
<div className={classString} {...restProps} role="separator">
|
||||
<div
|
||||
className={classString}
|
||||
style={{ ...divider?.style, ...style }}
|
||||
{...restProps}
|
||||
role="separator"
|
||||
>
|
||||
{children && type !== 'vertical' && (
|
||||
<span className={`${prefixCls}-inner-text`} style={innerStyle}>
|
||||
{children}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import classNames from 'classnames';
|
||||
import type { DrawerProps as RCDrawerProps } from 'rc-drawer';
|
||||
import * as React from 'react';
|
||||
import useClosable from '../_util/hooks/useClosable';
|
||||
|
||||
export interface DrawerPanelProps {
|
||||
prefixCls: string;
|
||||
@ -9,9 +9,15 @@ export interface DrawerPanelProps {
|
||||
title?: React.ReactNode;
|
||||
footer?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
|
||||
/**
|
||||
* Recommend to use closeIcon instead
|
||||
*
|
||||
* e.g.
|
||||
*
|
||||
* `<Drawer closeIcon={false} />`
|
||||
*/
|
||||
closable?: boolean;
|
||||
closeIcon?: React.ReactNode;
|
||||
closeIcon?: boolean | React.ReactNode;
|
||||
onClose?: RCDrawerProps['onClose'];
|
||||
|
||||
/** Wrapper dom node style of header and body */
|
||||
@ -28,8 +34,8 @@ const DrawerPanel: React.FC<DrawerPanelProps> = (props) => {
|
||||
title,
|
||||
footer,
|
||||
extra,
|
||||
closable = true,
|
||||
closeIcon = <CloseOutlined />,
|
||||
closeIcon,
|
||||
closable,
|
||||
onClose,
|
||||
headerStyle,
|
||||
drawerStyle,
|
||||
@ -38,31 +44,41 @@ const DrawerPanel: React.FC<DrawerPanelProps> = (props) => {
|
||||
children,
|
||||
} = props;
|
||||
|
||||
const closeIconNode = closable && (
|
||||
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
|
||||
{closeIcon}
|
||||
</button>
|
||||
const customCloseIconRender = React.useCallback(
|
||||
(icon: React.ReactNode) => (
|
||||
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
|
||||
{icon}
|
||||
</button>
|
||||
),
|
||||
[onClose],
|
||||
);
|
||||
const [mergedClosable, mergedCloseIcon] = useClosable(
|
||||
closable,
|
||||
closeIcon,
|
||||
customCloseIconRender,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
const headerNode = React.useMemo<React.ReactNode>(() => {
|
||||
if (!title && !closable) {
|
||||
if (!title && !mergedClosable) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
style={headerStyle}
|
||||
className={classNames(`${prefixCls}-header`, {
|
||||
[`${prefixCls}-header-close-only`]: closable && !title && !extra,
|
||||
[`${prefixCls}-header-close-only`]: mergedClosable && !title && !extra,
|
||||
})}
|
||||
>
|
||||
<div className={`${prefixCls}-header-title`}>
|
||||
{closeIconNode}
|
||||
{mergedCloseIcon}
|
||||
{title && <div className={`${prefixCls}-title`}>{title}</div>}
|
||||
</div>
|
||||
{extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
|
||||
</div>
|
||||
);
|
||||
}, [closable, closeIconNode, extra, headerStyle, prefixCls, title]);
|
||||
}, [mergedClosable, mergedCloseIcon, extra, headerStyle, prefixCls, title]);
|
||||
|
||||
const footerNode = React.useMemo<React.ReactNode>(() => {
|
||||
if (!footer) {
|
||||
|
@ -242,5 +242,72 @@ describe('Drawer', () => {
|
||||
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should hide close button when closeIcon is null or false', async () => {
|
||||
const { baseElement, rerender } = render(
|
||||
<Drawer open closeIcon={null}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.ant-drawer-close')).toBeNull();
|
||||
|
||||
rerender(
|
||||
<Drawer open closeIcon={false}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.ant-drawer-close')).toBeNull();
|
||||
|
||||
rerender(
|
||||
<Drawer open closeIcon={<span className="custom-close">Close</span>}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.custom-close')).not.toBeNull();
|
||||
|
||||
rerender(
|
||||
<Drawer open closable={false} closeIcon={<span className="custom-close2">Close</span>}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.custom-close2')).toBeNull();
|
||||
|
||||
rerender(
|
||||
<Drawer open closable closeIcon={<span className="custom-close3">Close</span>}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.custom-close3')).not.toBeNull();
|
||||
|
||||
rerender(
|
||||
<Drawer open closeIcon={0} className="custom-drawer1">
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.custom-drawer1 .ant-drawer-close')).not.toBeNull();
|
||||
expect(baseElement.querySelector('.custom-drawer1 .anticon-close')).toBeNull();
|
||||
|
||||
rerender(
|
||||
<Drawer open closeIcon="" className="custom-drawer2">
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.custom-drawer2 .ant-drawer-close')).not.toBeNull();
|
||||
expect(baseElement.querySelector('.custom-drawer2 .anticon-close')).toBeNull();
|
||||
|
||||
rerender(
|
||||
<Drawer open closeIcon className="custom-drawer3">
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.custom-drawer3 .anticon-close')).not.toBeNull();
|
||||
|
||||
rerender(
|
||||
<Drawer open closable>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(baseElement.querySelector('.anticon-close')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -46,8 +46,7 @@ A Drawer is a panel that is typically overlaid on top of a page and slides in fr
|
||||
| afterOpenChange | Callback after the animation ends when switching drawers | function(open) | - | |
|
||||
| bodyStyle | Style of the drawer content part | CSSProperties | - | |
|
||||
| className | Config Drawer Panel className. Use `rootClassName` if want to config top dom style | string | - | |
|
||||
| closable | Whether a close (x) button is visible on top left of the Drawer dialog or not | boolean | true | |
|
||||
| closeIcon | Custom close icon | ReactNode | <CloseOutlined /> | |
|
||||
| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \| ReactNode | <CloseOutlined /> | |
|
||||
| contentWrapperStyle | Style of the drawer wrapper of content part | CSSProperties | - | |
|
||||
| destroyOnClose | Whether to unmount child components on closing drawer or not | boolean | false | |
|
||||
| extra | Extra actions area at corner | ReactNode | - | 4.17.0 |
|
||||
|
@ -45,8 +45,7 @@ demo:
|
||||
| afterOpenChange | 切换抽屉时动画结束后的回调 | function(open) | - | |
|
||||
| bodyStyle | 可用于设置 Drawer 内容部分的样式 | CSSProperties | - | |
|
||||
| className | Drawer 容器外层 className 设置,如果需要设置最外层,请使用 rootClassName | string | - | |
|
||||
| closable | 是否显示左上角的关闭按钮 | boolean | true | |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | <CloseOutlined /> | |
|
||||
| closeIcon | 自定义关闭图标。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | <CloseOutlined /> | |
|
||||
| contentWrapperStyle | 可用于设置 Drawer 包裹内容部分的样式 | CSSProperties | - | |
|
||||
| destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false | |
|
||||
| extra | 抽屉右上角的操作区域 | ReactNode | - | 4.17.0 |
|
||||
|
@ -298,6 +298,47 @@ exports[`renders components/image/demo/fallback.tsx extend context correctly 1`]
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/imageRender.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-image"
|
||||
style="width: 200px;"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||
width="200"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/placeholder.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
@ -576,158 +617,44 @@ Array [
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/preview-group-visible.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-image"
|
||||
style="width: 200px;"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||
width="200"
|
||||
/>
|
||||
<div
|
||||
class="ant-image"
|
||||
style="width: 200px;"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||
width="200"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="display: none;"
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image"
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
<div
|
||||
class="ant-image"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/cV16ZqzMjW/photo-1473091540282-9b846e7965e3.webp"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-image"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/x43I27A55%26/photo-1438109491414-7198515b166b.webp"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/preview-mask.tsx extend context correctly 1`] = `
|
||||
@ -820,3 +747,44 @@ exports[`renders components/image/demo/previewSrc.tsx extend context correctly 1
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/toolbarRender.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-image"
|
||||
style="width: 200px;"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||
width="200"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -299,6 +299,47 @@ exports[`renders components/image/demo/fallback.tsx correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/imageRender.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-image"
|
||||
style="width:200px"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||
width="200"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/placeholder.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center"
|
||||
@ -577,158 +618,44 @@ Array [
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/preview-group-visible.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-image"
|
||||
style="width:200px"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||
width="200"
|
||||
/>
|
||||
<div
|
||||
class="ant-image"
|
||||
style="width:200px"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||
width="200"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="display:none"
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image"
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
<div
|
||||
class="ant-image"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/cV16ZqzMjW/photo-1473091540282-9b846e7965e3.webp"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-image"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/x43I27A55%26/photo-1438109491414-7198515b166b.webp"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/preview-mask.tsx correctly 1`] = `
|
||||
@ -821,3 +748,44 @@ exports[`renders components/image/demo/previewSrc.tsx correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/image/demo/toolbarRender.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-image"
|
||||
style="width:200px"
|
||||
>
|
||||
<img
|
||||
class="ant-image-img"
|
||||
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||
width="200"
|
||||
/>
|
||||
<div
|
||||
class="ant-image-mask"
|
||||
>
|
||||
<div
|
||||
class="ant-image-mask-info"
|
||||
>
|
||||
<span
|
||||
aria-label="eye"
|
||||
class="anticon anticon-eye"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="eye"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -52,39 +52,40 @@ exports[`Image Default Group preview props 1`] = `
|
||||
1 / 1
|
||||
</li>
|
||||
<li
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-close"
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-flipY"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-image-preview-operations-icon"
|
||||
aria-label="swap"
|
||||
class="anticon anticon-swap ant-image-preview-operations-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
data-icon="swap"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
style="transform: rotate(90deg);"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
|
||||
d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-zoomIn"
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-flipX"
|
||||
>
|
||||
<span
|
||||
aria-label="zoom-in"
|
||||
class="anticon anticon-zoom-in ant-image-preview-operations-icon"
|
||||
aria-label="swap"
|
||||
class="anticon anticon-swap ant-image-preview-operations-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="zoom-in"
|
||||
data-icon="swap"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
@ -92,59 +93,7 @@ exports[`Image Default Group preview props 1`] = `
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-zoomOut ant-image-preview-operations-operation-disabled"
|
||||
>
|
||||
<span
|
||||
aria-label="zoom-out"
|
||||
class="anticon anticon-zoom-out ant-image-preview-operations-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="zoom-out"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M637 443H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h312c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-rotateRight"
|
||||
>
|
||||
<span
|
||||
aria-label="rotate-right"
|
||||
class="anticon anticon-rotate-right ant-image-preview-operations-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="rotate-right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<defs>
|
||||
<style />
|
||||
</defs>
|
||||
<path
|
||||
d="M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-.4-12.6 6.1l-.2 64c-118.6.5-235.8 53.4-314.6 154.2A399.75 399.75 0 00123.5 631h74.9c-.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8z"
|
||||
/>
|
||||
<path
|
||||
d="M880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32zm-44 402H396V494h440v326z"
|
||||
d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
@ -179,48 +128,99 @@ exports[`Image Default Group preview props 1`] = `
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-flipX"
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-rotateRight"
|
||||
>
|
||||
<span
|
||||
aria-label="swap"
|
||||
class="anticon anticon-swap ant-image-preview-operations-icon"
|
||||
aria-label="rotate-right"
|
||||
class="anticon anticon-rotate-right ant-image-preview-operations-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="swap"
|
||||
data-icon="rotate-right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<defs>
|
||||
<style />
|
||||
</defs>
|
||||
<path
|
||||
d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
|
||||
d="M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-.4-12.6 6.1l-.2 64c-118.6.5-235.8 53.4-314.6 154.2A399.75 399.75 0 00123.5 631h74.9c-.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8z"
|
||||
/>
|
||||
<path
|
||||
d="M880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32zm-44 402H396V494h440v326z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-flipY"
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-zoomOut ant-image-preview-operations-operation-disabled"
|
||||
>
|
||||
<span
|
||||
aria-label="swap"
|
||||
class="anticon anticon-swap ant-image-preview-operations-icon"
|
||||
aria-label="zoom-out"
|
||||
class="anticon anticon-zoom-out ant-image-preview-operations-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="swap"
|
||||
data-icon="zoom-out"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
style="transform: rotate(90deg);"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
|
||||
d="M637 443H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h312c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-zoomIn"
|
||||
>
|
||||
<span
|
||||
aria-label="zoom-in"
|
||||
class="anticon anticon-zoom-in ant-image-preview-operations-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="zoom-in"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-image-preview-operations-operation ant-image-preview-operations-operation-close"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-image-preview-operations-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
|
7
components/image/demo/imageRender.md
Normal file
7
components/image/demo/imageRender.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
可以自定义预览内容。
|
||||
|
||||
## en-US
|
||||
|
||||
You can customize the preview content.
|
24
components/image/demo/imageRender.tsx
Normal file
24
components/image/demo/imageRender.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { Image } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Image
|
||||
width={200}
|
||||
preview={{
|
||||
imageRender: () => (
|
||||
<video
|
||||
muted
|
||||
width="100%"
|
||||
controls
|
||||
src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/file/A*uYT7SZwhJnUAAAAAAAAAAAAADgCCAQ"
|
||||
/>
|
||||
),
|
||||
toolbarRender: (_, { icons: { closeIcon } }) => (
|
||||
<ul className="ant-image-preview-operations">{closeIcon}</ul>
|
||||
),
|
||||
}}
|
||||
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
@ -1,26 +1,19 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Image } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
preview={{ visible: false }}
|
||||
width={200}
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||
onClick={() => setVisible(true)}
|
||||
/>
|
||||
<div style={{ display: 'none' }}>
|
||||
<Image.PreviewGroup preview={{ visible, onVisibleChange: (vis) => setVisible(vis) }}>
|
||||
<Image src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp" />
|
||||
<Image src="https://gw.alipayobjects.com/zos/antfincdn/cV16ZqzMjW/photo-1473091540282-9b846e7965e3.webp" />
|
||||
<Image src="https://gw.alipayobjects.com/zos/antfincdn/x43I27A55%26/photo-1438109491414-7198515b166b.webp" />
|
||||
</Image.PreviewGroup>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
const App: React.FC = () => (
|
||||
<Image.PreviewGroup
|
||||
items={[
|
||||
'https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp',
|
||||
'https://gw.alipayobjects.com/zos/antfincdn/cV16ZqzMjW/photo-1473091540282-9b846e7965e3.webp',
|
||||
'https://gw.alipayobjects.com/zos/antfincdn/x43I27A55%26/photo-1438109491414-7198515b166b.webp',
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
width={200}
|
||||
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
|
||||
/>
|
||||
</Image.PreviewGroup>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
7
components/image/demo/toolbarRender.md
Normal file
7
components/image/demo/toolbarRender.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
可以自定义工具栏。
|
||||
|
||||
## en-US
|
||||
|
||||
You can customize the toolbar.
|
60
components/image/demo/toolbarRender.tsx
Normal file
60
components/image/demo/toolbarRender.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import { DownloadOutlined } from '@ant-design/icons';
|
||||
import { Image } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const src = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const onDownload = () => {
|
||||
fetch(src)
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const url = URL.createObjectURL(new Blob([blob]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'image.jpg';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
link.remove();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Image
|
||||
width={200}
|
||||
preview={{
|
||||
toolbarRender: (
|
||||
_,
|
||||
{
|
||||
icons: {
|
||||
flipYIcon,
|
||||
flipXIcon,
|
||||
rotateLeftIcon,
|
||||
rotateRightIcon,
|
||||
zoomOutIcon,
|
||||
zoomInIcon,
|
||||
closeIcon,
|
||||
},
|
||||
},
|
||||
) => (
|
||||
<ul className="ant-image-preview-operations">
|
||||
<li className="ant-image-preview-operations-operation" onClick={onDownload}>
|
||||
<DownloadOutlined className="ant-image-preview-operations-icon" />
|
||||
</li>
|
||||
{flipYIcon}
|
||||
{flipXIcon}
|
||||
{rotateLeftIcon}
|
||||
{rotateRightIcon}
|
||||
{zoomOutIcon}
|
||||
{zoomInIcon}
|
||||
{closeIcon}
|
||||
</ul>
|
||||
),
|
||||
}}
|
||||
src={src}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -24,43 +24,138 @@ Previewable image.
|
||||
<code src="./demo/preview-group-visible.tsx">Preview from one image</code>
|
||||
<code src="./demo/previewSrc.tsx">Custom preview image</code>
|
||||
<code src="./demo/controlled-preview.tsx">Controlled Preview</code>
|
||||
<code src="./demo/toolbarRender.tsx">Custom toolbar render</code>
|
||||
<code src="./demo/imageRender.tsx">Custom preview render</code>
|
||||
<code src="./demo/preview-mask.tsx" debug>Custom preview mask</code>
|
||||
<code src="./demo/preview-group-top-progress.tsx" debug>Top progress customization when previewing multiple images</code>
|
||||
<code src="./demo/component-token.tsx" debug>Custom component token</code>
|
||||
|
||||
## API
|
||||
|
||||
### Image
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| alt | Image description | string | - | 4.6.0 |
|
||||
| fallback | Load failure fault-tolerant src | string | - | 4.6.0 |
|
||||
| height | Image height | string \| number | - | 4.6.0 |
|
||||
| placeholder | Load placeholder, use default placeholder when set `true` | ReactNode | - | 4.6.0 |
|
||||
| preview | preview config, disabled when `false` | boolean \| [previewType](#previewtype) | true | 4.6.0 [previewType](#previewtype):4.7.0 |
|
||||
| preview | preview config, disabled when `false` | boolean \| [PreviewType](#PreviewType) | true | 4.6.0 [PreviewType](#PreviewType):4.7.0 |
|
||||
| src | Image path | string | - | 4.6.0 |
|
||||
| width | Image width | string \| number | - | 4.6.0 |
|
||||
| onError | Load failed callback | (event: Event) => void | - | 4.12.0 |
|
||||
| rootClassName | add custom className for image root DOM and preview mode root DOM | string | - | 4.20.0 |
|
||||
| rootClassName | Add custom className for image root DOM and preview mode root DOM | string | - | 4.20.0 |
|
||||
|
||||
### previewType
|
||||
Other attributes [<img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
|
||||
|
||||
### PreviewType
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| visible | Whether the preview dialog is visible or not | boolean | - | - |
|
||||
| src | Custom preview src | string | - | 4.10.0 |
|
||||
| getContainer | The mounted node for preview dialog but still display at fullScreen | string \| HTMLElement \| (() => HTMLElement) \| false | - | 4.8.0 |
|
||||
| mask | Thumbnail mask | ReactNode | - | 4.9.0 |
|
||||
| maskClassName | The className of the mask | string | - | 4.11.0 |
|
||||
| rootClassName | The classname of the preview root DOM | string | - | 5.4.0 |
|
||||
| scaleStep | `1 + scaleStep` is the step to increase or decrease the scale | number | 0.5 | - |
|
||||
| minScale | Min scale | number | 1 | 5.7.0 |
|
||||
| maxScale | Max scale | number | 50 | 5.7.0 |
|
||||
| forceRender | Force render preview dialog | boolean | - | - |
|
||||
| toolbarRender | Custom toolbar render | (originalNode: React.ReactNode, info: Omit<[ToolbarRenderInfoType](#ToolbarRenderInfoType), 'current' \| 'total'>) => React.ReactNode | - | 5.7.0 |
|
||||
| imageRender | Custom preview content | (originalNode: React.ReactNode, info: { transform: [TransformType](#TransformType) }) => React.ReactNode | - | 5.7.0 |
|
||||
| onTransform | Callback when the transform of image changed | { transform: [TransformType](#TransformType), action: [TransformAction](#TransformAction) } | - | 5.7.0 |
|
||||
| onVisibleChange | Callback when `visible` changed | (visible: boolean, prevVisible: boolean) => void | - | - |
|
||||
|
||||
## PreviewGroup
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| preview | Preview config, `disabled` when false | boolean \| [PreviewGroupType](#PreviewGroupType) | true | 4.6.0 [PreviewGroupType](#PreviewGroupType):4.7.0 |
|
||||
| items | Preview items | string[] \| { src: string, crossOrigin: string, ... }[] | - | 5.7.0 |
|
||||
|
||||
### PreviewGroupType
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| visible | Whether the preview dialog is visible or not | boolean | - | - |
|
||||
| getContainer | The mounted node for preview dialog but still display at fullScreen | string \| HTMLElement \| (() => HTMLElement) \| false | - | 4.8.0 |
|
||||
| current | The index of the current preview | number | - | 4.12.0 |
|
||||
| mask | Thumbnail mask | ReactNode | - | 4.9.0 |
|
||||
| maskClassName | The className of the mask | string | - | 4.11.0 |
|
||||
| rootClassName | The classname of the preview root DOM | string | - | 5.4.0 |
|
||||
| scaleStep | `1 + scaleStep` is the step to increase or decrease the scale | number | 0.5 | - |
|
||||
| minScale | Min scale | number | 1 | 5.7.0 |
|
||||
| maxScale | Max scale | number | 50 | 5.7.0 |
|
||||
| forceRender | Force render preview dialog | boolean | - | - |
|
||||
| countRender | Custom preview count content | (current: number, total: number) => string | - | 4.20.0 |
|
||||
| toolbarRender | Custom toolbar render | (originalNode: React.ReactNode, info: [ToolbarRenderInfoType](#ToolbarRenderInfoType)) => React.ReactNode | - | 5.7.0 |
|
||||
| imageRender | Custom preview content | (originalNode: React.ReactNode, info: { transform: [TransformType](#TransformType), current: number }) => React.ReactNode | - | 5.7.0 |
|
||||
| onTransform | Callback when the transform of image changed | { transform: [TransformType](#TransformType), action: [TransformAction](#TransformAction) } | - | 5.7.0 |
|
||||
| onChange | Callback when switch preview image | (current: number, prevCurrent: number) => void | - | 5.3.0 |
|
||||
| onVisibleChange | Callback when `visible` changed | (visible: boolean, prevVisible: boolean, current: number) => void | - | current 参数 5.3.0 |
|
||||
|
||||
## Interface
|
||||
|
||||
### TransformType
|
||||
|
||||
```typescript
|
||||
{
|
||||
visible?: boolean;
|
||||
onVisibleChange?: (visible, prevVisible, current: number) => void; // `current` only support after v5.3.0
|
||||
getContainer?: string | HTMLElement | (() => HTMLElement); // v4.8.0 The mounted node for preview dialog but still display at fullScreen
|
||||
src?: string; // v4.10.0
|
||||
mask?: ReactNode; // v4.9.0
|
||||
maskClassName?: string; // v4.11.0
|
||||
rootClassName?: string; // only support after v5.4.0
|
||||
current?: number; // v4.12.0 Only support PreviewGroup
|
||||
countRender?: (current: number, total: number) => string // v4.20.0 Only support PreviewGroup
|
||||
scaleStep?: number;
|
||||
onChange?: (current: number, prevCurrent: number) => void; // only support after v5.3.0
|
||||
x: number;
|
||||
y: number;
|
||||
rotate: number;
|
||||
scale: number;
|
||||
flipX: boolean;
|
||||
flipY: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
Other attributes [<img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
|
||||
### TransformAction
|
||||
|
||||
```typescript
|
||||
type TransformAction =
|
||||
| 'flipY'
|
||||
| 'flipX'
|
||||
| 'rotateLeft'
|
||||
| 'rotateRight'
|
||||
| 'zoomIn'
|
||||
| 'zoomOut'
|
||||
| 'close'
|
||||
| 'prev'
|
||||
| 'next'
|
||||
| 'wheel'
|
||||
| 'doubleClick'
|
||||
| 'move'
|
||||
| 'dragRebound';
|
||||
```
|
||||
|
||||
### ToolbarRenderInfoType
|
||||
|
||||
```typescript
|
||||
{
|
||||
icons: {
|
||||
flipYIcon: React.ReactNode;
|
||||
flipXIcon: React.ReactNode;
|
||||
rotateLeftIcon: React.ReactNode;
|
||||
rotateRightIcon: React.ReactNode;
|
||||
zoomOutIcon: React.ReactNode;
|
||||
zoomInIcon: React.ReactNode;
|
||||
closeIcon: React.ReactNode;
|
||||
};
|
||||
actions: {
|
||||
onFlipY: () => void;
|
||||
onFlipX: () => void;
|
||||
onRotateLeft: () => void;
|
||||
onRotateRight: () => void;
|
||||
onZoomOut: () => void;
|
||||
onZoomIn: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
transform: TransformType,
|
||||
current: number;
|
||||
total: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Design Token
|
||||
|
||||
|
@ -25,44 +25,138 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LVQ3R5JjjJEAAA
|
||||
<code src="./demo/preview-group-visible.tsx">相册模式</code>
|
||||
<code src="./demo/previewSrc.tsx">自定义预览图片</code>
|
||||
<code src="./demo/controlled-preview.tsx">受控的预览</code>
|
||||
<code src="./demo/toolbarRender.tsx">自定义工具栏</code>
|
||||
<code src="./demo/imageRender.tsx">自定义预览内容</code>
|
||||
<code src="./demo/preview-mask.tsx" debug>自定义预览文本</code>
|
||||
<code src="./demo/preview-group-top-progress.tsx" debug>多图预览时顶部进度自定义</code>
|
||||
<code src="./demo/component-token.tsx" debug>自定义组件 Token</code>
|
||||
|
||||
## API
|
||||
|
||||
### Image
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| alt | 图像描述 | string | - | 4.6.0 |
|
||||
| fallback | 加载失败容错地址 | string | - | 4.6.0 |
|
||||
| height | 图像高度 | string \| number | - | 4.6.0 |
|
||||
| placeholder | 加载占位, 为 `true` 时使用默认占位 | ReactNode | - | 4.6.0 |
|
||||
| preview | 预览参数,为 `false` 时禁用 | boolean \| [previewType](#previewtype) | true | 4.6.0 [previewType](#previewtype):4.7.0 |
|
||||
| preview | 预览参数,为 `false` 时禁用 | boolean \| [PreviewType](#PreviewType) | true | 4.6.0 [PreviewType](#PreviewType):4.7.0 |
|
||||
| src | 图片地址 | string | - | 4.6.0 |
|
||||
| width | 图像宽度 | string \| number | - | 4.6.0 |
|
||||
| onError | 加载错误回调 | (event: Event) => void | - | 4.12.0 |
|
||||
| rootClassName | 为展示图片根 DOM 和预览大图根 DOM 提供自定义 className | string | - | 4.20.0 |
|
||||
|
||||
### previewType
|
||||
其他属性见 [<img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
|
||||
|
||||
### PreviewType
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| visible | 是否显示 | boolean | - | - |
|
||||
| src | 自定义预览 src | string | - | 4.10.0 |
|
||||
| getContainer | 指定预览挂载的节点,但依旧为全屏展示,false 为挂载在当前位置 | string \| HTMLElement \| (() => HTMLElement) \| false | - | 4.8.0 |
|
||||
| mask | 缩略图遮罩 | ReactNode | - | 4.9.0 |
|
||||
| maskClassName | 缩略图遮罩类名 | string | - | 4.11.0 |
|
||||
| rootClassName | 预览图的根 DOM 类名 | string | - | 5.4.0 |
|
||||
| scaleStep | `1 + scaleStep` 为缩放放大的每步倍数 | number | 0.5 | - |
|
||||
| minScale | 最小缩放倍数 | number | 1 | 5.7.0 |
|
||||
| maxScale | 最大放大倍数 | number | 50 | 5.7.0 |
|
||||
| forceRender | 强制渲染预览图 | boolean | - | - |
|
||||
| toolbarRender | 自定义工具栏 | (originalNode: React.ReactNode, info: Omit<[ToolbarRenderInfoType](#ToolbarRenderInfoType), 'current' \| 'total'>) => React.ReactNode | - | 5.7.0 |
|
||||
| imageRender | 自定义预览内容 | (originalNode: React.ReactNode, info: { transform: [TransformType](#TransformType) }) => React.ReactNode | - | 5.7.0 |
|
||||
| onTransform | 预览图 transform 变化的回调 | { transform: [TransformType](#TransformType), action: [TransformAction](#TransformAction) } | - | 5.7.0 |
|
||||
| onVisibleChange | 当 `visible` 发生改变时的回调 | (visible: boolean, prevVisible: boolean) => void | - | - |
|
||||
|
||||
## PreviewGroup
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| preview | 预览参数,为 `false` 时禁用 | boolean \| [PreviewGroupType](#PreviewGroupType) | true | 4.6.0 [PreviewGroupType](#PreviewGroupType):4.7.0 |
|
||||
| items | 预览数组 | string[] \| { src: string, crossOrigin: string, ... }[] | - | 5.7.0 |
|
||||
|
||||
### PreviewGroupType
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| visible | 是否显示 | boolean | - | - |
|
||||
| getContainer | 指定预览挂载的节点,但依旧为全屏展示,false 为挂载在当前位置 | string \| HTMLElement \| (() => HTMLElement) \| false | - | 4.8.0 |
|
||||
| current | 当前预览图的 index | number | - | 4.12.0 |
|
||||
| mask | 缩略图遮罩 | ReactNode | - | 4.9.0 |
|
||||
| maskClassName | 缩略图遮罩类名 | string | - | 4.11.0 |
|
||||
| rootClassName | 预览图的根 DOM 类名 | string | - | 5.4.0 |
|
||||
| scaleStep | `1 + scaleStep` 为缩放放大的每步倍数 | number | 0.5 | - |
|
||||
| minScale | 最小缩放倍数 | number | 1 | 5.7.0 |
|
||||
| maxScale | 最大放大倍数 | number | 50 | 5.7.0 |
|
||||
| forceRender | 强制渲染预览图 | boolean | - | - |
|
||||
| countRender | 自定义预览计数内容 | (current: number, total: number) => string | - | 4.20.0 |
|
||||
| toolbarRender | 自定义工具栏 | (originalNode: React.ReactNode, info: [ToolbarRenderInfoType](#ToolbarRenderInfoType)) => React.ReactNode | - | 5.7.0 |
|
||||
| imageRender | 自定义预览内容 | (originalNode: React.ReactNode, info: { transform: [TransformType](#TransformType), current: number }) => React.ReactNode | - | 5.7.0 |
|
||||
| onTransform | 预览图 transform 变化的回调 | { transform: [TransformType](#TransformType), action: [TransformAction](#TransformAction) } | - | 5.7.0 |
|
||||
| onChange | 切换预览图的回调 | (current: number, prevCurrent: number) => void | - | 5.3.0 |
|
||||
| onVisibleChange | 当 `visible` 发生改变时的回调 | (visible: boolean, prevVisible: boolean, current: number) => void | - | current 参数 5.3.0 |
|
||||
|
||||
## Interface
|
||||
|
||||
### TransformType
|
||||
|
||||
```typescript
|
||||
{
|
||||
visible?: boolean;
|
||||
onVisibleChange?: (visible, prevVisible, current: number) => void; // current 参数 v5.3.0 后支持
|
||||
getContainer?: string | HTMLElement | (() => HTMLElement); // v4.8.0 指定预览窗口挂载的节点,但依旧为全屏展示
|
||||
src?: string; // v4.10.0
|
||||
mask?: ReactNode; // v4.9.0
|
||||
maskClassName?: string; // v4.11.0
|
||||
rootClassName?: string; // v5.4.0 后支持
|
||||
current?: number; // v4.12.0 仅支持 PreviewGroup
|
||||
countRender?: (current: number, total: number) => string // v4.20.0 仅支持 PreviewGroup
|
||||
scaleStep?: number;
|
||||
forceRender?: boolean;
|
||||
onChange?: (current: number, prevCurrent: number) => void; // v5.3.0 后支持
|
||||
x: number;
|
||||
y: number;
|
||||
rotate: number;
|
||||
scale: number;
|
||||
flipX: boolean;
|
||||
flipY: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
其他属性见 [<img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
|
||||
### TransformAction
|
||||
|
||||
```typescript
|
||||
type TransformAction =
|
||||
| 'flipY'
|
||||
| 'flipX'
|
||||
| 'rotateLeft'
|
||||
| 'rotateRight'
|
||||
| 'zoomIn'
|
||||
| 'zoomOut'
|
||||
| 'close'
|
||||
| 'prev'
|
||||
| 'next'
|
||||
| 'wheel'
|
||||
| 'doubleClick'
|
||||
| 'move'
|
||||
| 'dragRebound';
|
||||
```
|
||||
|
||||
### ToolbarRenderInfoType
|
||||
|
||||
```typescript
|
||||
{
|
||||
icons: {
|
||||
flipYIcon: React.ReactNode;
|
||||
flipXIcon: React.ReactNode;
|
||||
rotateLeftIcon: React.ReactNode;
|
||||
rotateRightIcon: React.ReactNode;
|
||||
zoomOutIcon: React.ReactNode;
|
||||
zoomInIcon: React.ReactNode;
|
||||
closeIcon: React.ReactNode;
|
||||
};
|
||||
actions: {
|
||||
onFlipY: () => void;
|
||||
onFlipX: () => void;
|
||||
onRotateLeft: () => void;
|
||||
onRotateRight: () => void;
|
||||
onZoomOut: () => void;
|
||||
onZoomIn: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
transform: TransformType,
|
||||
current: number;
|
||||
total: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Design Token
|
||||
|
||||
|
@ -64,7 +64,7 @@ export const genPreviewOperationsStyle = (token: ImageToken): CSSObject => {
|
||||
[`${previewCls}-operations`]: {
|
||||
...resetComponent(token),
|
||||
display: 'flex',
|
||||
flexDirection: 'row-reverse',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
color: token.previewOperationColor,
|
||||
listStyle: 'none',
|
||||
@ -87,7 +87,7 @@ export const genPreviewOperationsStyle = (token: ImageToken): CSSObject => {
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
|
||||
'&:last-of-type': {
|
||||
'&:first-of-type': {
|
||||
marginInlineStart: 0,
|
||||
},
|
||||
},
|
||||
@ -192,7 +192,6 @@ export const genImagePreviewStyle: GenerateStyle<ImageToken> = (token: ImageToke
|
||||
cursor: 'grab',
|
||||
transition: `transform ${motionDurationSlow} ${motionEaseOut} 0s`,
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'auto',
|
||||
|
||||
'&-wrapper': {
|
||||
...genBoxStyle(),
|
||||
@ -205,6 +204,10 @@ export const genImagePreviewStyle: GenerateStyle<ImageToken> = (token: ImageToke
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
'& > *': {
|
||||
pointerEvents: 'auto',
|
||||
},
|
||||
|
||||
'&::before': {
|
||||
display: 'inline-block',
|
||||
width: 1,
|
||||
|
@ -18,7 +18,7 @@ describe('prefix', () => {
|
||||
const { container } = render(<InputNumber prefix={<i>123</i>} />);
|
||||
|
||||
const mockFocus = jest.spyOn(container.querySelector('input')!, 'focus');
|
||||
fireEvent.mouseUp(container.querySelector('i')!);
|
||||
fireEvent.click(container.querySelector('i')!);
|
||||
expect(mockFocus).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -5,7 +5,6 @@ import classNames from 'classnames';
|
||||
import type { InputNumberProps as RcInputNumberProps } from 'rc-input-number';
|
||||
import RcInputNumber from 'rc-input-number';
|
||||
import * as React from 'react';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import ConfigProvider, { ConfigContext } from '../config-provider';
|
||||
@ -33,7 +32,6 @@ export interface InputNumberProps<T extends ValueType = ValueType>
|
||||
const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props, ref) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
const [focused, setFocus] = React.useState(false);
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
React.useImperativeHandle(ref, () => inputRef.current!);
|
||||
@ -89,9 +87,6 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
|
||||
const mergedSize = useSize((ctx) => customizeSize ?? compactSize ?? ctx);
|
||||
|
||||
const hasPrefix = prefix != null || hasFeedback;
|
||||
const hasAddon = !!(addonBefore || addonAfter);
|
||||
|
||||
// ===================== Disabled =====================
|
||||
const disabled = React.useContext(DisabledContext);
|
||||
const mergedDisabled = customDisabled ?? disabled;
|
||||
@ -107,116 +102,74 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
compactItemClassnames,
|
||||
hashId,
|
||||
className,
|
||||
!hasPrefix && !hasAddon && rootClassName,
|
||||
);
|
||||
const wrapperClassName = `${prefixCls}-group`;
|
||||
|
||||
let element = (
|
||||
const element = (
|
||||
<RcInputNumber
|
||||
ref={inputRef}
|
||||
disabled={mergedDisabled}
|
||||
className={inputNumberClass}
|
||||
className={classNames(className, rootClassName)}
|
||||
upHandler={upIcon}
|
||||
downHandler={downIcon}
|
||||
prefixCls={prefixCls}
|
||||
readOnly={readOnly}
|
||||
controls={controlsTemp}
|
||||
prefix={prefix}
|
||||
suffix={hasFeedback && feedbackIcon}
|
||||
addonAfter={
|
||||
addonAfter && (
|
||||
<NoCompactStyle>
|
||||
<NoFormStyle override status>
|
||||
{addonAfter}
|
||||
</NoFormStyle>
|
||||
</NoCompactStyle>
|
||||
)
|
||||
}
|
||||
addonBefore={
|
||||
addonBefore && (
|
||||
<NoCompactStyle>
|
||||
<NoFormStyle override status>
|
||||
{addonBefore}
|
||||
</NoFormStyle>
|
||||
</NoCompactStyle>
|
||||
)
|
||||
}
|
||||
classNames={{
|
||||
input: inputNumberClass,
|
||||
}}
|
||||
classes={{
|
||||
affixWrapper: classNames(
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
},
|
||||
hashId,
|
||||
),
|
||||
wrapper: classNames(
|
||||
{
|
||||
[`${wrapperClassName}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-wrapper-disabled`]: mergedDisabled,
|
||||
},
|
||||
hashId,
|
||||
),
|
||||
group: classNames(
|
||||
{
|
||||
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
|
||||
},
|
||||
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
|
||||
hashId,
|
||||
),
|
||||
}}
|
||||
{...others}
|
||||
/>
|
||||
);
|
||||
|
||||
if (hasPrefix) {
|
||||
const affixWrapperCls = classNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-focused`]: focused,
|
||||
[`${prefixCls}-affix-wrapper-disabled`]: props.disabled,
|
||||
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
},
|
||||
|
||||
// className will go to addon wrapper
|
||||
!hasAddon && className,
|
||||
!hasAddon && rootClassName,
|
||||
hashId,
|
||||
);
|
||||
|
||||
element = (
|
||||
<div
|
||||
className={affixWrapperCls}
|
||||
style={props.style}
|
||||
onMouseUp={() => inputRef.current!.focus()}
|
||||
>
|
||||
{prefix && <span className={`${prefixCls}-prefix`}>{prefix}</span>}
|
||||
{cloneElement(element, {
|
||||
style: null,
|
||||
value: props.value,
|
||||
onFocus: (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
setFocus(true);
|
||||
props.onFocus?.(event);
|
||||
},
|
||||
onBlur: (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
setFocus(false);
|
||||
props.onBlur?.(event);
|
||||
},
|
||||
})}
|
||||
{hasFeedback && <span className={`${prefixCls}-suffix`}>{feedbackIcon}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (hasAddon) {
|
||||
const wrapperClassName = `${prefixCls}-group`;
|
||||
const addonClassName = `${wrapperClassName}-addon`;
|
||||
const addonBeforeNode = addonBefore ? (
|
||||
<div className={addonClassName}>{addonBefore}</div>
|
||||
) : null;
|
||||
const addonAfterNode = addonAfter ? <div className={addonClassName}>{addonAfter}</div> : null;
|
||||
|
||||
const mergedWrapperClassName = classNames(`${prefixCls}-wrapper`, wrapperClassName, hashId, {
|
||||
[`${wrapperClassName}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-wrapper-disabled`]: mergedDisabled,
|
||||
});
|
||||
|
||||
const mergedGroupClassName = classNames(
|
||||
`${prefixCls}-group-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
|
||||
},
|
||||
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
|
||||
hashId,
|
||||
className,
|
||||
rootClassName,
|
||||
);
|
||||
element = (
|
||||
<div className={mergedGroupClassName} style={props.style}>
|
||||
<div className={mergedWrapperClassName}>
|
||||
{addonBeforeNode && (
|
||||
<NoCompactStyle>
|
||||
<NoFormStyle status override>
|
||||
{addonBeforeNode}
|
||||
</NoFormStyle>
|
||||
</NoCompactStyle>
|
||||
)}
|
||||
{cloneElement(element, { style: null, disabled: mergedDisabled })}
|
||||
{addonAfterNode && (
|
||||
<NoCompactStyle>
|
||||
<NoFormStyle status override>
|
||||
{addonAfterNode}
|
||||
</NoFormStyle>
|
||||
</NoCompactStyle>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return wrapSSR(element);
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import classNames from 'classnames';
|
||||
import Dialog from 'rc-dialog';
|
||||
import * as React from 'react';
|
||||
import useClosable from '../_util/hooks/useClosable';
|
||||
import { getTransitionName } from '../_util/motion';
|
||||
import { canUseDocElement } from '../_util/styleChecker';
|
||||
import warning from '../_util/warning';
|
||||
@ -64,6 +66,7 @@ const Modal: React.FC<ModalProps> = (props) => {
|
||||
centered,
|
||||
getContainer,
|
||||
closeIcon,
|
||||
closable,
|
||||
focusTriggerAfterClose = true,
|
||||
|
||||
// Deprecated
|
||||
@ -91,6 +94,14 @@ const Modal: React.FC<ModalProps> = (props) => {
|
||||
const dialogFooter =
|
||||
footer === undefined ? <Footer {...props} onOk={handleOk} onCancel={handleCancel} /> : footer;
|
||||
|
||||
const [mergedClosable, mergedCloseIcon] = useClosable(
|
||||
closable,
|
||||
closeIcon,
|
||||
(icon) => renderCloseIcon(prefixCls, icon),
|
||||
<CloseOutlined className={`${prefixCls}-close-icon`} />,
|
||||
true,
|
||||
);
|
||||
|
||||
return wrapSSR(
|
||||
<NoCompactStyle>
|
||||
<NoFormStyle status override>
|
||||
@ -105,7 +116,8 @@ const Modal: React.FC<ModalProps> = (props) => {
|
||||
visible={open ?? visible}
|
||||
mousePosition={restProps.mousePosition ?? mousePosition}
|
||||
onClose={handleCancel}
|
||||
closeIcon={renderCloseIcon(prefixCls, closeIcon)}
|
||||
closable={mergedClosable}
|
||||
closeIcon={mergedCloseIcon}
|
||||
focusTriggerAfterClose={focusTriggerAfterClose}
|
||||
transitionName={getTransitionName(rootPrefixCls, 'zoom', props.transitionName)}
|
||||
maskTransitionName={getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName)}
|
||||
|
@ -33,6 +33,13 @@ describe('Modal', () => {
|
||||
expect(document.body.querySelectorAll('.ant-modal-root')[0]).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('support hide close button when setting closeIcon to null or false', () => {
|
||||
const { baseElement, rerender } = render(<Modal closeIcon={null} open />);
|
||||
expect(baseElement.querySelector('.ant-modal-close')).toBeFalsy();
|
||||
rerender(<Modal closeIcon={false} open />);
|
||||
expect(baseElement.querySelector('.ant-modal-close')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('render correctly', () => {
|
||||
const { asFragment } = render(<ModalTester />);
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
|
@ -47,8 +47,7 @@ Additionally, if you need show a simple confirmation dialog, you can use [`App.u
|
||||
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#api) | - | |
|
||||
| cancelText | Text of the Cancel button | ReactNode | `Cancel` | |
|
||||
| centered | Centered Modal | boolean | false | |
|
||||
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | true | |
|
||||
| closeIcon | Custom close icon | ReactNode | <CloseOutlined /> | |
|
||||
| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \| ReactNode | <CloseOutlined /> | |
|
||||
| confirmLoading | Whether to apply loading visual effect for OK button or not | boolean | false | |
|
||||
| destroyOnClose | Whether to unmount child components on onClose | boolean | false | |
|
||||
| focusTriggerAfterClose | Whether need to focus trigger element after dialog is closed | boolean | true | 4.9.0 |
|
||||
@ -100,8 +99,7 @@ The items listed above are all functions, expecting a settings object as paramet
|
||||
| cancelText | Text of the Cancel button with Modal.confirm | string | `Cancel` | |
|
||||
| centered | Centered Modal | boolean | false | |
|
||||
| className | The className of container | string | - | |
|
||||
| closable | Whether a close (x) button is visible on top right of the confirm dialog or not | boolean | false | 4.9.0 |
|
||||
| closeIcon | Custom close icon | ReactNode | undefined | 4.9.0 |
|
||||
| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \| ReactNode | <CloseOutlined /> | |
|
||||
| content | Content | ReactNode | - | |
|
||||
| footer | Footer content, set as `footer: null` when you don't need default buttons | ReactNode | - | 5.1.0 |
|
||||
| getContainer | Return the mount node for Modal | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
||||
|
@ -48,8 +48,7 @@ demo:
|
||||
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button-cn#api) | - | |
|
||||
| cancelText | 取消按钮文字 | ReactNode | `取消` | |
|
||||
| centered | 垂直居中展示 Modal | boolean | false | |
|
||||
| closable | 是否显示右上角的关闭按钮 | boolean | true | |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | <CloseOutlined /> | |
|
||||
| closeIcon | 自定义关闭图标。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | <CloseOutlined /> | |
|
||||
| confirmLoading | 确定按钮 loading | boolean | false | |
|
||||
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false | |
|
||||
| focusTriggerAfterClose | 对话框关闭后是否需要聚焦触发元素 | boolean | true | 4.9.0 |
|
||||
@ -101,8 +100,7 @@ demo:
|
||||
| cancelText | 设置 Modal.confirm 取消按钮文字 | string | `取消` | |
|
||||
| centered | 垂直居中展示 Modal | boolean | false | |
|
||||
| className | 容器类名 | string | - | |
|
||||
| closable | 是否显示右上角的关闭按钮 | boolean | false | 4.9.0 |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | undefined | 4.9.0 |
|
||||
| closeIcon | 自定义关闭图标。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | <CloseOutlined /> | |
|
||||
| content | 内容 | ReactNode | - | |
|
||||
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer: null` | ReactNode | - | 5.1.0 |
|
||||
| getContainer | 指定 Modal 挂载的 HTML 节点, false 为挂载在当前 dom | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
||||
|
@ -8,7 +8,7 @@ export interface ModalProps {
|
||||
confirmLoading?: boolean;
|
||||
/** The modal dialog's title */
|
||||
title?: React.ReactNode;
|
||||
/** Whether a close (x) button is visible on top right of the modal dialog or not */
|
||||
/** Whether a close (x) button is visible on top right of the modal dialog or not. Recommend to use closeIcon instead. */
|
||||
closable?: boolean;
|
||||
/** Specify a function that will be called when a user clicks the OK button */
|
||||
onOk?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
@ -50,7 +50,7 @@ export interface ModalProps {
|
||||
keyboard?: boolean;
|
||||
wrapProps?: any;
|
||||
prefixCls?: string;
|
||||
closeIcon?: React.ReactNode;
|
||||
closeIcon?: boolean | React.ReactNode;
|
||||
modalRender?: (node: React.ReactNode) => React.ReactNode;
|
||||
focusTriggerAfterClose?: boolean;
|
||||
children?: React.ReactNode;
|
||||
|
@ -21,6 +21,7 @@ export const TypeIcon = {
|
||||
};
|
||||
|
||||
export function getCloseIcon(prefixCls: string, closeIcon?: React.ReactNode) {
|
||||
if (closeIcon === null || closeIcon === false) return null;
|
||||
return (
|
||||
closeIcon || (
|
||||
<span className={`${prefixCls}-close-x`}>
|
||||
|
@ -313,4 +313,39 @@ describe('notification', () => {
|
||||
|
||||
expect(document.querySelectorAll('[role="status"]').length).toBe(1);
|
||||
});
|
||||
it('should hide close btn when closeIcon setting to null or false', async () => {
|
||||
notification.config({
|
||||
closeIcon: undefined,
|
||||
});
|
||||
act(() => {
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
className: 'normal',
|
||||
});
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
className: 'custom',
|
||||
closeIcon: <span className="custom-close-icon">Close</span>,
|
||||
});
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
closeIcon: null,
|
||||
className: 'with-null',
|
||||
});
|
||||
notification.open({
|
||||
message: 'Notification Title',
|
||||
duration: 0,
|
||||
closeIcon: false,
|
||||
className: 'with-false',
|
||||
});
|
||||
});
|
||||
await awaitPromise();
|
||||
expect(document.querySelectorAll('.normal .ant-notification-notice-close').length).toBe(1);
|
||||
expect(document.querySelectorAll('.custom .custom-close-icon').length).toBe(1);
|
||||
expect(document.querySelectorAll('.with-null .ant-notification-notice-close').length).toBe(0);
|
||||
expect(document.querySelectorAll('.with-false .ant-notification-notice-close').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -48,7 +48,7 @@ The properties of config are as follows:
|
||||
| --- | --- | --- | --- | --- |
|
||||
| btn | Customized close button | ReactNode | - | - |
|
||||
| className | Customized CSS class | string | - | - |
|
||||
| closeIcon | Custom close icon | ReactNode | - | - |
|
||||
| closeIcon | Custom close icon | boolean \| ReactNode | true | 5.7.0: close button will be hidden when setting to null or false |
|
||||
| description | The content of notification box (required) | ReactNode | - | - |
|
||||
| duration | Time in seconds before Notification is closed. When set to 0 or null, it will never be closed automatically | number | 4.5 | - |
|
||||
| icon | Customized icon | ReactNode | - | - |
|
||||
@ -68,8 +68,8 @@ The properties of config are as follows:
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| bottom | Distance from the bottom of the viewport, when `placement` is `bottomRight` or `bottomLeft` (unit: pixels) | number | 24 | |
|
||||
| closeIcon | Custom close icon | ReactNode | - | |
|
||||
| getContainer | Return the mount node for Notification, but still display at fullScreen | () => HTMLNode | () => document.body | |
|
||||
| closeIcon | Custom close icon | boolean \| ReactNode | true | 5.7.0: close button will be hidden when setting to null or false |
|
||||
| getContainer | Return the mount node for Notification | () => HTMLNode | () => document.body | |
|
||||
| placement | Position of Notification, can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | `topRight` | |
|
||||
| rtl | Whether to enable RTL mode | boolean | false | |
|
||||
| top | Distance from the top of the viewport, when `placement` is `topRight` or `topLeft` (unit: pixels) | number | 24 | |
|
||||
@ -99,7 +99,7 @@ notification.config({
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| bottom | Distance from the bottom of the viewport, when `placement` is `bottomRight` or `bottomLeft` (unit: pixels) | number | 24 | |
|
||||
| closeIcon | Custom close icon | ReactNode | - | |
|
||||
| closeIcon | Custom close icon | boolean \| ReactNode | true | 5.7.0: close button will be hidden when setting to null or false |
|
||||
| duration | Time in seconds before Notification is closed. When set to 0 or null, it will never be closed automatically | number | 4.5 | |
|
||||
| getContainer | Return the mount node for Notification, but still display at fullScreen | () => HTMLNode | () => document.body | |
|
||||
| placement | Position of Notification, can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | `topRight` | |
|
||||
|
@ -49,7 +49,7 @@ config 参数如下:
|
||||
| --- | --- | --- | --- | --- |
|
||||
| btn | 自定义关闭按钮 | ReactNode | - | - |
|
||||
| className | 自定义 CSS class | string | - | - |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | - | - |
|
||||
| closeIcon | 自定义关闭图标 | boolean \| ReactNode | true | 5.7.0:设置为 null 或 false 时隐藏关闭按钮 |
|
||||
| description | 通知提醒内容,必选 | ReactNode | - | - |
|
||||
| duration | 默认 4.5 秒后自动关闭,配置为 null 则不自动关闭 | number | 4.5 | - |
|
||||
| icon | 自定义图标 | ReactNode | - | - |
|
||||
@ -69,8 +69,8 @@ config 参数如下:
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| bottom | 消息从底部弹出时,距离底部的位置,单位像素 | number | 24 | |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | - | |
|
||||
| getContainer | 配置渲染节点的输出位置,但依旧为全屏展示 | () => HTMLNode | () => document.body | |
|
||||
| closeIcon | 自定义关闭图标 | boolean \| ReactNode | true | 5.7.0:设置为 null 或 false 时隐藏关闭按钮 |
|
||||
| getContainer | 配置渲染节点的输出位置 | () => HTMLNode | () => document.body | |
|
||||
| placement | 弹出位置,可选 `top` `topLeft` `topRight` `bottom` `bottomLeft` `bottomRight` | string | `topRight` | |
|
||||
| rtl | 是否开启 RTL 模式 | boolean | false | |
|
||||
| top | 消息从顶部弹出时,距离顶部的位置,单位像素 | number | 24 | |
|
||||
@ -100,7 +100,7 @@ notification.config({
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| bottom | 消息从底部弹出时,距离底部的位置,单位像素 | number | 24 | |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | - | |
|
||||
| closeIcon | 自定义关闭图标 | boolean \| ReactNode | true | 5.7.0:设置为 null 或 false 时隐藏关闭按钮 |
|
||||
| duration | 默认自动关闭延时,单位秒 | number | 4.5 | |
|
||||
| getContainer | 配置渲染节点的输出位置,但依旧为全屏展示 | () => HTMLNode | () => document.body | |
|
||||
| placement | 弹出位置,可选 `top` `topLeft` `topRight` `bottom` `bottomLeft` `bottomRight` | string | `topRight` | |
|
||||
|
@ -27,7 +27,7 @@ export interface ArgsProps {
|
||||
className?: string;
|
||||
readonly type?: IconType;
|
||||
onClick?: () => void;
|
||||
closeIcon?: React.ReactNode;
|
||||
closeIcon?: boolean | React.ReactNode;
|
||||
props?: DivProps;
|
||||
role?: 'alert' | 'status';
|
||||
}
|
||||
|
@ -113,9 +113,12 @@ export function useInternalNotification(
|
||||
btn,
|
||||
className,
|
||||
role = 'alert',
|
||||
closeIcon,
|
||||
...restConfig
|
||||
} = config;
|
||||
|
||||
const realCloseIcon = getCloseIcon(noticePrefixCls, closeIcon);
|
||||
|
||||
return originOpen({
|
||||
placement: 'topRight',
|
||||
...restConfig,
|
||||
@ -131,6 +134,8 @@ export function useInternalNotification(
|
||||
/>
|
||||
),
|
||||
className: classNames(type && `${noticePrefixCls}-${type}`, hashId, className),
|
||||
closeIcon: realCloseIcon,
|
||||
closable: !!realCloseIcon,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -50,10 +50,11 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>((props, ref)
|
||||
block,
|
||||
options = [],
|
||||
size: customSize = 'middle',
|
||||
style,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const { getPrefixCls, direction, segmented } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('segmented', customizePrefixCls);
|
||||
// Style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
@ -82,19 +83,25 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>((props, ref)
|
||||
[options, prefixCls],
|
||||
);
|
||||
|
||||
const cls = classNames(
|
||||
className,
|
||||
rootClassName,
|
||||
segmented?.className,
|
||||
{
|
||||
[`${prefixCls}-block`]: block,
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
},
|
||||
hashId,
|
||||
);
|
||||
|
||||
const mergeStyle: React.CSSProperties = { ...segmented?.style, ...style };
|
||||
|
||||
return wrapSSR(
|
||||
<RcSegmented
|
||||
{...restProps}
|
||||
className={classNames(
|
||||
className,
|
||||
rootClassName,
|
||||
{
|
||||
[`${prefixCls}-block`]: block,
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
},
|
||||
hashId,
|
||||
)}
|
||||
className={cls}
|
||||
style={mergeStyle}
|
||||
options={extendedOptions}
|
||||
ref={ref}
|
||||
prefixCls={prefixCls}
|
||||
|
@ -141,6 +141,371 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/slider/demo/component-token.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-slider ant-slider-disabled ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left: 0%; width: 30%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="true"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="30"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left: 30%; transform: translateX(-50%);"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-top"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; bottom: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
30
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track ant-slider-track-1"
|
||||
style="left: 20%; width: 30%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="20"
|
||||
class="ant-slider-handle ant-slider-handle-1"
|
||||
role="slider"
|
||||
style="left: 20%; transform: translateX(-50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-top"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; bottom: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
20
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="50"
|
||||
class="ant-slider-handle ant-slider-handle-2"
|
||||
role="slider"
|
||||
style="left: 50%; transform: translateX(-50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-top"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; bottom: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
50
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="display: inline-block; height: 300px; margin-left: 70px;"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="bottom: 0%; height: 30%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="30"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="bottom: 30%; transform: translateY(50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-right"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; top: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
30
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="display: inline-block; height: 300px; margin-left: 70px;"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track ant-slider-track-1"
|
||||
style="bottom: 20%; height: 30%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="20"
|
||||
class="ant-slider-handle ant-slider-handle-1"
|
||||
role="slider"
|
||||
style="bottom: 20%; transform: translateY(50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-right"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; top: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
20
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="50"
|
||||
class="ant-slider-handle ant-slider-handle-2"
|
||||
role="slider"
|
||||
style="bottom: 50%; transform: translateY(50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-right"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; top: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
50
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="display: inline-block; height: 300px; margin-left: 70px;"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-vertical ant-slider-with-marks"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track ant-slider-track-1"
|
||||
style="bottom: 26%; height: 11%;"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
>
|
||||
<span
|
||||
class="ant-slider-dot"
|
||||
style="bottom: 0%; transform: translateY(50%);"
|
||||
/>
|
||||
<span
|
||||
class="ant-slider-dot ant-slider-dot-active"
|
||||
style="bottom: 26%; transform: translateY(50%);"
|
||||
/>
|
||||
<span
|
||||
class="ant-slider-dot ant-slider-dot-active"
|
||||
style="bottom: 37%; transform: translateY(50%);"
|
||||
/>
|
||||
<span
|
||||
class="ant-slider-dot"
|
||||
style="bottom: 100%; transform: translateY(50%);"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="26"
|
||||
class="ant-slider-handle ant-slider-handle-1"
|
||||
role="slider"
|
||||
style="bottom: 26%; transform: translateY(50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-right"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; top: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
26
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="37"
|
||||
class="ant-slider-handle ant-slider-handle-2"
|
||||
role="slider"
|
||||
style="bottom: 37%; transform: translateY(50%);"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-right"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
style="position: absolute; top: 0px; left: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
37
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-slider-mark"
|
||||
>
|
||||
<span
|
||||
class="ant-slider-mark-text"
|
||||
style="bottom: 0%; transform: translateY(50%);"
|
||||
>
|
||||
0°C
|
||||
</span>
|
||||
<span
|
||||
class="ant-slider-mark-text ant-slider-mark-text-active"
|
||||
style="bottom: 26%; transform: translateY(50%);"
|
||||
>
|
||||
26°C
|
||||
</span>
|
||||
<span
|
||||
class="ant-slider-mark-text ant-slider-mark-text-active"
|
||||
style="bottom: 37%; transform: translateY(50%);"
|
||||
>
|
||||
37°C
|
||||
</span>
|
||||
<span
|
||||
class="ant-slider-mark-text"
|
||||
style="bottom: 100%; transform: translateY(50%); color: rgb(255, 85, 0);"
|
||||
>
|
||||
<strong>
|
||||
100°C
|
||||
</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/slider/demo/draggableTrack.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
|
@ -84,6 +84,219 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/slider/demo/component-token.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-slider ant-slider-disabled ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="left:0%;width:30%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="true"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="30"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="left:30%;transform:translateX(-50%)"
|
||||
/>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track ant-slider-track-1"
|
||||
style="left:20%;width:30%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="20"
|
||||
class="ant-slider-handle ant-slider-handle-1"
|
||||
role="slider"
|
||||
style="left:20%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="50"
|
||||
class="ant-slider-handle ant-slider-handle-2"
|
||||
role="slider"
|
||||
style="left:50%;transform:translateX(-50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>,
|
||||
<div
|
||||
style="display:inline-block;height:300px;margin-left:70px"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track"
|
||||
style="bottom:0%;height:30%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="30"
|
||||
class="ant-slider-handle"
|
||||
role="slider"
|
||||
style="bottom:30%;transform:translateY(50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="display:inline-block;height:300px;margin-left:70px"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track ant-slider-track-1"
|
||||
style="bottom:20%;height:30%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="20"
|
||||
class="ant-slider-handle ant-slider-handle-1"
|
||||
role="slider"
|
||||
style="bottom:20%;transform:translateY(50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="50"
|
||||
class="ant-slider-handle ant-slider-handle-2"
|
||||
role="slider"
|
||||
style="bottom:50%;transform:translateY(50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
style="display:inline-block;height:300px;margin-left:70px"
|
||||
>
|
||||
<div
|
||||
class="ant-slider ant-slider-vertical ant-slider-with-marks"
|
||||
>
|
||||
<div
|
||||
class="ant-slider-rail"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-track ant-slider-track-1"
|
||||
style="bottom:26%;height:11%"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-step"
|
||||
>
|
||||
<span
|
||||
class="ant-slider-dot"
|
||||
style="bottom:0%;transform:translateY(50%)"
|
||||
/>
|
||||
<span
|
||||
class="ant-slider-dot ant-slider-dot-active"
|
||||
style="bottom:26%;transform:translateY(50%)"
|
||||
/>
|
||||
<span
|
||||
class="ant-slider-dot ant-slider-dot-active"
|
||||
style="bottom:37%;transform:translateY(50%)"
|
||||
/>
|
||||
<span
|
||||
class="ant-slider-dot"
|
||||
style="bottom:100%;transform:translateY(50%)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="26"
|
||||
class="ant-slider-handle ant-slider-handle-1"
|
||||
role="slider"
|
||||
style="bottom:26%;transform:translateY(50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="37"
|
||||
class="ant-slider-handle ant-slider-handle-2"
|
||||
role="slider"
|
||||
style="bottom:37%;transform:translateY(50%)"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="ant-slider-mark"
|
||||
>
|
||||
<span
|
||||
class="ant-slider-mark-text"
|
||||
style="bottom:0%;transform:translateY(50%)"
|
||||
>
|
||||
0°C
|
||||
</span>
|
||||
<span
|
||||
class="ant-slider-mark-text ant-slider-mark-text-active"
|
||||
style="bottom:26%;transform:translateY(50%)"
|
||||
>
|
||||
26°C
|
||||
</span>
|
||||
<span
|
||||
class="ant-slider-mark-text ant-slider-mark-text-active"
|
||||
style="bottom:37%;transform:translateY(50%)"
|
||||
>
|
||||
37°C
|
||||
</span>
|
||||
<span
|
||||
class="ant-slider-mark-text"
|
||||
style="bottom:100%;transform:translateY(50%);color:#f50"
|
||||
>
|
||||
<strong>
|
||||
100°C
|
||||
</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/slider/demo/draggableTrack.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-slider ant-slider-horizontal"
|
||||
|
7
components/slider/demo/component-token.md
Normal file
7
components/slider/demo/component-token.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
Component Token Debug.
|
||||
|
||||
## en-US
|
||||
|
||||
Component Token Debug.
|
59
components/slider/demo/component-token.tsx
Normal file
59
components/slider/demo/component-token.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { ConfigProvider, Slider } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
display: 'inline-block',
|
||||
height: 300,
|
||||
marginLeft: 70,
|
||||
};
|
||||
|
||||
const marks = {
|
||||
0: '0°C',
|
||||
26: '26°C',
|
||||
37: '37°C',
|
||||
100: {
|
||||
style: { color: '#f50' },
|
||||
label: <strong>100°C</strong>,
|
||||
},
|
||||
};
|
||||
|
||||
const App: React.FC = () => (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Slider: {
|
||||
controlSize: 20,
|
||||
railSize: 4,
|
||||
handleSize: 22,
|
||||
handleSizeHover: 18,
|
||||
dotSize: 8,
|
||||
handleLineWidth: 6,
|
||||
handleLineWidthHover: 2,
|
||||
railBg: '#9f3434',
|
||||
railHoverBg: '#8d2424',
|
||||
trackBg: '#b0b0ef',
|
||||
trackHoverBg: '#c77195',
|
||||
handleColor: '#e6f6a2',
|
||||
handleActiveColor: '#d22bc4',
|
||||
dotBorderColor: '#303030',
|
||||
dotActiveBorderColor: '#918542',
|
||||
trackBgDisabled: '#1a1b80',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Slider defaultValue={30} disabled />
|
||||
<Slider range={{ draggableTrack: true }} defaultValue={[20, 50]} />
|
||||
<div style={style}>
|
||||
<Slider vertical defaultValue={30} />
|
||||
</div>
|
||||
<div style={style}>
|
||||
<Slider vertical range step={10} defaultValue={[20, 50]} />
|
||||
</div>
|
||||
<div style={style}>
|
||||
<Slider vertical range marks={marks} defaultValue={[26, 37]} />
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
);
|
||||
|
||||
export default App;
|
@ -27,6 +27,7 @@ To input a value in a range.
|
||||
<code src="./demo/show-tooltip.tsx">Control visible of ToolTip</code>
|
||||
<code src="./demo/reverse.tsx">Reverse</code>
|
||||
<code src="./demo/draggableTrack.tsx">Draggable track</code>
|
||||
<code src="./demo/component-token.tsx" debug>Component Token</code>
|
||||
|
||||
## API
|
||||
|
||||
|
@ -28,6 +28,7 @@ demo:
|
||||
<code src="./demo/show-tooltip.tsx">控制 ToolTip 的显示</code>
|
||||
<code src="./demo/reverse.tsx">反向</code>
|
||||
<code src="./demo/draggableTrack.tsx">范围可拖拽</code>
|
||||
<code src="./demo/component-token.tsx" debug>组件 Token</code>
|
||||
|
||||
## API
|
||||
|
||||
|
@ -19,6 +19,15 @@ export interface ComponentToken {
|
||||
handleLineWidth: number;
|
||||
handleLineWidthHover: number;
|
||||
dotSize: number;
|
||||
railBg: string;
|
||||
railHoverBg: string;
|
||||
trackBg: string;
|
||||
trackHoverBg: string;
|
||||
handleColor: string;
|
||||
handleActiveColor: string;
|
||||
dotBorderColor: string;
|
||||
dotActiveBorderColor: string;
|
||||
trackBgDisabled: string;
|
||||
}
|
||||
|
||||
interface SliderToken extends FullToken<'Slider'> {
|
||||
@ -49,25 +58,25 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
|
||||
|
||||
[`${componentCls}-rail`]: {
|
||||
position: 'absolute',
|
||||
backgroundColor: token.colorFillTertiary,
|
||||
backgroundColor: token.railBg,
|
||||
borderRadius: token.borderRadiusXS,
|
||||
transition: `background-color ${token.motionDurationMid}`,
|
||||
},
|
||||
|
||||
[`${componentCls}-track`]: {
|
||||
position: 'absolute',
|
||||
backgroundColor: token.colorPrimaryBorder,
|
||||
backgroundColor: token.trackBg,
|
||||
borderRadius: token.borderRadiusXS,
|
||||
transition: `background-color ${token.motionDurationMid}`,
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
[`${componentCls}-rail`]: {
|
||||
backgroundColor: token.colorFillSecondary,
|
||||
backgroundColor: token.railHoverBg,
|
||||
},
|
||||
|
||||
[`${componentCls}-track`]: {
|
||||
backgroundColor: token.colorPrimaryBorderHover,
|
||||
backgroundColor: token.trackHoverBg,
|
||||
},
|
||||
|
||||
[`${componentCls}-dot`]: {
|
||||
@ -79,7 +88,7 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
|
||||
},
|
||||
|
||||
[`${componentCls}-dot-active`]: {
|
||||
borderColor: token.colorPrimary,
|
||||
borderColor: token.dotActiveBorderColor,
|
||||
},
|
||||
},
|
||||
|
||||
@ -112,7 +121,7 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
|
||||
width: token.handleSize,
|
||||
height: token.handleSize,
|
||||
backgroundColor: token.colorBgElevated,
|
||||
boxShadow: `0 0 0 ${token.handleLineWidth}px ${token.colorPrimaryBorder}`,
|
||||
boxShadow: `0 0 0 ${token.handleLineWidth}px ${token.handleColor}`,
|
||||
borderRadius: '50%',
|
||||
cursor: 'pointer',
|
||||
transition: `
|
||||
@ -139,7 +148,7 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
|
||||
},
|
||||
|
||||
'&::after': {
|
||||
boxShadow: `0 0 0 ${token.handleLineWidthHover}px ${token.colorPrimary}`,
|
||||
boxShadow: `0 0 0 ${token.handleLineWidthHover}px ${token.handleActiveColor}`,
|
||||
width: token.handleSizeHover,
|
||||
height: token.handleSizeHover,
|
||||
insetInlineStart: (token.handleSize - token.handleSizeHover) / 2,
|
||||
@ -178,14 +187,14 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
|
||||
width: dotSize,
|
||||
height: dotSize,
|
||||
backgroundColor: token.colorBgElevated,
|
||||
border: `${token.handleLineWidth}px solid ${token.colorBorderSecondary}`,
|
||||
border: `${token.handleLineWidth}px solid ${token.dotBorderColor}`,
|
||||
borderRadius: '50%',
|
||||
cursor: 'pointer',
|
||||
transition: `border-color ${token.motionDurationSlow}`,
|
||||
pointerEvents: 'auto',
|
||||
|
||||
'&-active': {
|
||||
borderColor: token.colorPrimaryBorder,
|
||||
borderColor: token.dotActiveBorderColor,
|
||||
},
|
||||
},
|
||||
|
||||
@ -193,18 +202,18 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
|
||||
cursor: 'not-allowed',
|
||||
|
||||
[`${componentCls}-rail`]: {
|
||||
backgroundColor: `${token.colorFillSecondary} !important`,
|
||||
backgroundColor: `${token.railBg} !important`,
|
||||
},
|
||||
|
||||
[`${componentCls}-track`]: {
|
||||
backgroundColor: `${token.colorTextDisabled} !important`,
|
||||
backgroundColor: `${token.trackBgDisabled} !important`,
|
||||
},
|
||||
|
||||
[`
|
||||
${componentCls}-dot
|
||||
`]: {
|
||||
backgroundColor: token.colorBgElevated,
|
||||
borderColor: token.colorTextDisabled,
|
||||
borderColor: token.trackBgDisabled,
|
||||
boxShadow: 'none',
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
@ -339,6 +348,15 @@ export default genComponentStyleHook(
|
||||
dotSize: 8,
|
||||
handleLineWidth,
|
||||
handleLineWidthHover,
|
||||
railBg: token.colorFillTertiary,
|
||||
railHoverBg: token.colorFillSecondary,
|
||||
trackBg: token.colorPrimaryBorder,
|
||||
trackHoverBg: token.colorPrimaryBorderHover,
|
||||
handleColor: token.colorPrimaryBorder,
|
||||
handleActiveColor: token.colorPrimary,
|
||||
dotBorderColor: token.colorBorderSecondary,
|
||||
dotActiveBorderColor: token.colorPrimaryBorder,
|
||||
trackBgDisabled: token.colorBgContainerDisabled,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
@ -76,13 +76,14 @@ const Space = React.forwardRef<HTMLDivElement, SpaceProps>((props, ref) => {
|
||||
|
||||
const cn = classNames(
|
||||
prefixCls,
|
||||
space?.className,
|
||||
hashId,
|
||||
`${prefixCls}-${direction}`,
|
||||
{
|
||||
[`${prefixCls}-rtl`]: directionConfig === 'rtl',
|
||||
[`${prefixCls}-align-${mergedAlign}`]: mergedAlign,
|
||||
},
|
||||
className ?? space?.className,
|
||||
className,
|
||||
rootClassName,
|
||||
);
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { ConfigContext } from '../config-provider';
|
||||
import useStyle from './style/index';
|
||||
|
||||
const SpinSizes = ['small', 'default', 'large'] as const;
|
||||
export type SpinSize = typeof SpinSizes[number];
|
||||
export type SpinSize = (typeof SpinSizes)[number];
|
||||
export type SpinIndicator = React.ReactElement<HTMLElement>;
|
||||
|
||||
export interface SpinProps {
|
||||
@ -113,10 +113,11 @@ const Spin: React.FC<SpinClassProps> = (props) => {
|
||||
warning(!tip || isNestedPattern, 'Spin', '`tip` only work in nest pattern.');
|
||||
}
|
||||
|
||||
const { direction } = React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
const { direction, spin } = React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
|
||||
const spinClassName = classNames(
|
||||
prefixCls,
|
||||
spin?.className,
|
||||
{
|
||||
[`${prefixCls}-sm`]: size === 'small',
|
||||
[`${prefixCls}-lg`]: size === 'large',
|
||||
@ -136,10 +137,12 @@ const Spin: React.FC<SpinClassProps> = (props) => {
|
||||
// fix https://fb.me/react-unknown-prop
|
||||
const divProps = omit(restProps, ['indicator', 'prefixCls']);
|
||||
|
||||
const mergeStyle: React.CSSProperties = { ...spin?.style, ...style };
|
||||
|
||||
const spinElement: React.ReactNode = (
|
||||
<div
|
||||
{...divProps}
|
||||
style={style}
|
||||
style={mergeStyle}
|
||||
className={spinClassName}
|
||||
aria-live="polite"
|
||||
aria-busy={spinning}
|
||||
|
@ -72,9 +72,7 @@ More option at [rc-tabs tabs](https://github.com/react-component/tabs#tabs)
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| closable | Set tab closable. Only works while `type="editable-card"` | boolean | true |
|
||||
| closeIcon | Customize close icon in TabPane's head. Only works while `type="editable-card"` | ReactNode | - |
|
||||
| closable | Whether the Tab can be closed, Only works while `type="editable-card"` | boolean | true |
|
||||
| closeIcon | Customize close icon in TabPane's head. Only works while `type="editable-card"`. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \| ReactNode | - |
|
||||
| disabled | Set TabPane disabled | boolean | false |
|
||||
| forceRender | Forced render of content in tabs, not lazy render after clicking on tabs | boolean | false |
|
||||
| key | TabPane's key | string | - |
|
||||
|
@ -72,15 +72,14 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
|
||||
|
||||
### TabItemType
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ----------- | -------------------------------------------------------- | --------- | ------ |
|
||||
| closeIcon | 自定义关闭图标,在 `type="editable-card"` 时有效 | ReactNode | - |
|
||||
| closable | 当前选项卡是否可被关闭,在 `type="editable-card"` 时有效 | boolean | true |
|
||||
| disabled | 禁用某一项 | boolean | false |
|
||||
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false |
|
||||
| key | 对应 activeKey | string | - |
|
||||
| label | 选项卡头显示文字 | ReactNode | - |
|
||||
| children | 选项卡头显示内容 | ReactNode | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| closeIcon | 自定义关闭图标,在 `type="editable-card"` 时有效。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | - |
|
||||
| disabled | 禁用某一项 | boolean | false |
|
||||
| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false |
|
||||
| key | 对应 activeKey | string | - |
|
||||
| label | 选项卡头显示文字 | ReactNode | - |
|
||||
| children | 选项卡头显示内容 | ReactNode | - |
|
||||
|
||||
## Design Token
|
||||
|
||||
|
@ -165,7 +165,7 @@ exports[`renders components/tag/demo/basic.tsx extend context correctly 1`] = `
|
||||
<span
|
||||
class="ant-tag"
|
||||
>
|
||||
Tag 2
|
||||
Prevent Default
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-tag-close-icon"
|
||||
@ -195,26 +195,32 @@ exports[`renders components/tag/demo/basic.tsx extend context correctly 1`] = `
|
||||
<span
|
||||
class="ant-tag"
|
||||
>
|
||||
Prevent Default
|
||||
Tag 2
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-tag-close-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
class="ant-tag-close-icon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<path
|
||||
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 00-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"
|
||||
/>
|
||||
<path
|
||||
d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -165,7 +165,7 @@ exports[`renders components/tag/demo/basic.tsx correctly 1`] = `
|
||||
<span
|
||||
class="ant-tag"
|
||||
>
|
||||
Tag 2
|
||||
Prevent Default
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-tag-close-icon"
|
||||
@ -195,26 +195,32 @@ exports[`renders components/tag/demo/basic.tsx correctly 1`] = `
|
||||
<span
|
||||
class="ant-tag"
|
||||
>
|
||||
Prevent Default
|
||||
Tag 2
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close ant-tag-close-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
class="ant-tag-close-icon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle"
|
||||
role="img"
|
||||
>
|
||||
<path
|
||||
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 00-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"
|
||||
/>
|
||||
<path
|
||||
d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -62,6 +62,33 @@ describe('Tag', () => {
|
||||
expect(container.querySelectorAll('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
|
||||
});
|
||||
|
||||
it('show close button by closeIcon', () => {
|
||||
const { container } = render(
|
||||
<>
|
||||
<Tag className="tag1" closable closeIcon="close" />
|
||||
<Tag className="tag2" closable closeIcon />
|
||||
<Tag className="tag3" closable closeIcon={false} />
|
||||
<Tag className="tag4" closable closeIcon={null} />
|
||||
<Tag className="tag5" closable={false} closeIcon="close" />
|
||||
<Tag className="tag6" closable={false} closeIcon />
|
||||
<Tag className="tag7" closable={false} closeIcon={false} />
|
||||
<Tag className="tag8" closable={false} closeIcon={null} />
|
||||
<Tag className="tag9" closeIcon="close" />
|
||||
<Tag className="tag10" closeIcon />
|
||||
<Tag className="tag11" closeIcon={false} />
|
||||
<Tag className="tag12" closeIcon={null} />
|
||||
</>,
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll('.ant-tag-close-icon').length).toBe(6);
|
||||
['tag1', 'tag2', 'tag3', 'tag4', 'tag9', 'tag10'].forEach((tag) => {
|
||||
expect(container.querySelector(`.${tag} .ant-tag-close-icon`)).toBeTruthy();
|
||||
});
|
||||
['tag5', 'tag6', 'tag7', 'tag8', 'tag11', 'tag12'].forEach((tag) => {
|
||||
expect(container.querySelector(`.${tag} .ant-tag-close-icon`)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should trigger onClick', () => {
|
||||
const onClick = jest.fn();
|
||||
const { container } = render(<Tag onClick={onClick} />);
|
||||
|
@ -1,7 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
基本标签的用法,可以通过添加 `closable` 变为可关闭标签。可关闭标签具有 `onClose` 事件。
|
||||
基本标签的用法,可以通过设置 `closeIcon` 变为可关闭标签并自定义关闭按钮,设置为 `true` 时将使用默认关闭按钮。可关闭标签具有 `onClose` 事件。
|
||||
|
||||
## en-US
|
||||
|
||||
Usage of basic Tag, and it could be closable by set `closable` property. Closable Tag supports `onClose` events.
|
||||
Usage of basic Tag, and it could be closable and customize close button by set `closeIcon` property, will display default close button when `closeIcon` is setting to `true`. Closable Tag supports `onClose` events.
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||
import { Space, Tag } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
@ -16,12 +17,12 @@ const App: React.FC = () => (
|
||||
<Tag>
|
||||
<a href="https://github.com/ant-design/ant-design/issues/1862">Link</a>
|
||||
</Tag>
|
||||
<Tag closable onClose={log}>
|
||||
Tag 2
|
||||
</Tag>
|
||||
<Tag closable onClose={preventDefault}>
|
||||
<Tag closeIcon onClose={preventDefault}>
|
||||
Prevent Default
|
||||
</Tag>
|
||||
<Tag closeIcon={<CloseCircleOutlined />} onClose={log}>
|
||||
Tag 2
|
||||
</Tag>
|
||||
</Space>
|
||||
);
|
||||
|
||||
|
@ -37,14 +37,13 @@ Tag for categorizing or markup.
|
||||
|
||||
### Tag
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --------- | ------------------------------------ | ----------- | ------- | ------- |
|
||||
| closable | Whether the Tag can be closed | boolean | false | |
|
||||
| closeIcon | Custom close icon | ReactNode | - | 4.4.0 |
|
||||
| color | Color of the Tag | string | - | |
|
||||
| icon | Set the icon of tag | ReactNode | - | |
|
||||
| bordered | Whether has border style | boolean | true | 5.4.0 |
|
||||
| onClose | Callback executed when tag is closed | (e) => void | - | |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | ReactNode | false | 4.4.0 |
|
||||
| color | Color of the Tag | string | - | |
|
||||
| icon | Set the icon of tag | ReactNode | - | |
|
||||
| bordered | Whether has border style | boolean | true | 5.4.0 |
|
||||
| onClose | Callback executed when tag is closed | (e) => void | - | |
|
||||
|
||||
### Tag.CheckableTag
|
||||
|
||||
|
@ -3,6 +3,7 @@ import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import type { PresetColorType, PresetStatusColorType } from '../_util/colors';
|
||||
import { isPresetColor, isPresetStatusColor } from '../_util/colors';
|
||||
import useClosable from '../_util/hooks/useClosable';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
import warning from '../_util/warning';
|
||||
import Wave from '../_util/wave';
|
||||
@ -18,7 +19,8 @@ export interface TagProps extends React.HTMLAttributes<HTMLSpanElement> {
|
||||
rootClassName?: string;
|
||||
color?: LiteralUnion<PresetColorType | PresetStatusColorType>;
|
||||
closable?: boolean;
|
||||
closeIcon?: React.ReactNode;
|
||||
/** Advised to use closeIcon instead. */
|
||||
closeIcon?: boolean | React.ReactNode;
|
||||
/** @deprecated `visible` will be removed in next major version. */
|
||||
visible?: boolean;
|
||||
onClose?: (e: React.MouseEvent<HTMLElement>) => void;
|
||||
@ -43,7 +45,7 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
|
||||
color,
|
||||
onClose,
|
||||
closeIcon,
|
||||
closable = false,
|
||||
closable,
|
||||
bordered = true,
|
||||
...props
|
||||
} = tagProps;
|
||||
@ -100,18 +102,20 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const closeIconNode = React.useMemo<React.ReactNode>(() => {
|
||||
if (closable) {
|
||||
return closeIcon ? (
|
||||
<span className={`${prefixCls}-close-icon`} onClick={handleCloseClick}>
|
||||
{closeIcon}
|
||||
</span>
|
||||
) : (
|
||||
const [, mergedCloseIcon] = useClosable(
|
||||
closable,
|
||||
closeIcon,
|
||||
(iconNode: React.ReactNode) =>
|
||||
iconNode === null ? (
|
||||
<CloseOutlined className={`${prefixCls}-close-icon`} onClick={handleCloseClick} />
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}, [closable, closeIcon, prefixCls, handleCloseClick]);
|
||||
) : (
|
||||
<span className={`${prefixCls}-close-icon`} onClick={handleCloseClick}>
|
||||
{iconNode}
|
||||
</span>
|
||||
),
|
||||
null,
|
||||
false,
|
||||
);
|
||||
|
||||
const isNeedWave =
|
||||
typeof props.onClick === 'function' ||
|
||||
@ -131,7 +135,7 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
|
||||
const tagNode = (
|
||||
<span {...props} ref={ref} className={tagClassName} style={tagStyle}>
|
||||
{kids}
|
||||
{closeIconNode}
|
||||
{mergedCloseIcon}
|
||||
</span>
|
||||
);
|
||||
|
||||
|
@ -39,8 +39,7 @@ demo:
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| closable | 标签是否可以关闭(点击默认关闭) | boolean | false | |
|
||||
| closeIcon | 自定义关闭按钮 | ReactNode | - | 4.4.0 |
|
||||
| closeIcon | 自定义关闭按钮。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | false | 4.4.0 |
|
||||
| color | 标签色 | string | - | |
|
||||
| icon | 设置图标 | ReactNode | - | |
|
||||
| bordered | 是否有边框 | boolean | true | 5.4.0 |
|
||||
|
22
components/theme/context.ts
Normal file
22
components/theme/context.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import type { Theme } from '@ant-design/cssinjs';
|
||||
import { createTheme } from '@ant-design/cssinjs';
|
||||
import React from 'react';
|
||||
import type { AliasToken, MapToken, OverrideToken, SeedToken } from './interface';
|
||||
import defaultDerivative from './themes/default';
|
||||
import defaultSeedToken from './themes/seed';
|
||||
|
||||
export const defaultTheme = createTheme(defaultDerivative);
|
||||
|
||||
// ================================ Context =================================
|
||||
// To ensure snapshot stable. We disable hashed in test env.
|
||||
export const defaultConfig = {
|
||||
token: defaultSeedToken,
|
||||
hashed: true,
|
||||
};
|
||||
|
||||
export const DesignTokenContext = React.createContext<{
|
||||
token: Partial<AliasToken>;
|
||||
theme?: Theme<SeedToken, MapToken>;
|
||||
components?: OverrideToken;
|
||||
hashed?: string | boolean;
|
||||
}>(defaultConfig);
|
@ -1,5 +1,6 @@
|
||||
import type { ComponentTokenMap } from './components';
|
||||
import type { CSSInterpolation } from '@ant-design/cssinjs';
|
||||
import type { AliasToken } from './alias';
|
||||
import type { ComponentTokenMap } from './components';
|
||||
|
||||
export type OverrideToken = {
|
||||
[key in keyof ComponentTokenMap]: Partial<ComponentTokenMap[key]> & Partial<AliasToken>;
|
||||
@ -8,23 +9,30 @@ export type OverrideToken = {
|
||||
/** Final token which contains the components level override */
|
||||
export type GlobalToken = AliasToken & ComponentTokenMap;
|
||||
|
||||
export { PresetColors } from './presetColors';
|
||||
export type { AliasToken } from './alias';
|
||||
export type { ComponentTokenMap } from './components';
|
||||
export type {
|
||||
PresetColorType,
|
||||
ColorPalettes,
|
||||
LegacyColorPalettes,
|
||||
PresetColorKey,
|
||||
} from './presetColors';
|
||||
export type { SeedToken } from './seeds';
|
||||
export type {
|
||||
MapToken,
|
||||
ColorMapToken,
|
||||
ColorNeutralMapToken,
|
||||
CommonMapToken,
|
||||
HeightMapToken,
|
||||
SizeMapToken,
|
||||
FontMapToken,
|
||||
HeightMapToken,
|
||||
MapToken,
|
||||
SizeMapToken,
|
||||
StyleMapToken,
|
||||
} from './maps';
|
||||
export type { AliasToken } from './alias';
|
||||
export type { ComponentTokenMap } from './components';
|
||||
export { PresetColors } from './presetColors';
|
||||
export type {
|
||||
ColorPalettes,
|
||||
LegacyColorPalettes,
|
||||
PresetColorKey,
|
||||
PresetColorType,
|
||||
} from './presetColors';
|
||||
export type { SeedToken } from './seeds';
|
||||
|
||||
export type UseComponentStyleResult = [(node: React.ReactNode) => React.ReactElement, string];
|
||||
|
||||
export type GenerateStyle<
|
||||
ComponentToken extends object = AliasToken,
|
||||
ReturnType = CSSInterpolation,
|
||||
> = (token: ComponentToken) => ReturnType;
|
||||
|
@ -1,90 +1,38 @@
|
||||
import type { CSSInterpolation, Theme } from '@ant-design/cssinjs';
|
||||
import { createTheme, useCacheToken, useStyleRegister } from '@ant-design/cssinjs';
|
||||
import React from 'react';
|
||||
import version from '../version';
|
||||
import { useStyleRegister } from '@ant-design/cssinjs';
|
||||
import type {
|
||||
AliasToken,
|
||||
GlobalToken,
|
||||
MapToken,
|
||||
OverrideToken,
|
||||
PresetColorType,
|
||||
GenerateStyle,
|
||||
PresetColorKey,
|
||||
PresetColorType,
|
||||
SeedToken,
|
||||
UseComponentStyleResult,
|
||||
} from './interface';
|
||||
import { PresetColors } from './interface';
|
||||
import defaultDerivative from './themes/default';
|
||||
import defaultSeedToken from './themes/seed';
|
||||
import formatToken from './util/alias';
|
||||
import useToken from './useToken';
|
||||
import type { FullToken } from './util/genComponentStyleHook';
|
||||
import genComponentStyleHook from './util/genComponentStyleHook';
|
||||
import statisticToken, { merge as mergeToken } from './util/statistic';
|
||||
import genPresetColor from './util/genPresetColor';
|
||||
import statisticToken, { merge as mergeToken } from './util/statistic';
|
||||
|
||||
const defaultTheme = createTheme(defaultDerivative);
|
||||
|
||||
export { DesignTokenContext, defaultConfig } from './context';
|
||||
export {
|
||||
// colors
|
||||
PresetColors,
|
||||
statisticToken,
|
||||
mergeToken,
|
||||
// hooks
|
||||
useStyleRegister,
|
||||
genComponentStyleHook,
|
||||
genPresetColor,
|
||||
mergeToken,
|
||||
statisticToken,
|
||||
// hooks
|
||||
useStyleRegister,
|
||||
useToken,
|
||||
};
|
||||
export type {
|
||||
SeedToken,
|
||||
AliasToken,
|
||||
PresetColorType,
|
||||
PresetColorKey,
|
||||
// FIXME: Remove this type
|
||||
AliasToken as DerivativeToken,
|
||||
FullToken,
|
||||
GenerateStyle,
|
||||
PresetColorKey,
|
||||
PresetColorType,
|
||||
SeedToken,
|
||||
UseComponentStyleResult,
|
||||
};
|
||||
|
||||
// ================================ Context =================================
|
||||
// To ensure snapshot stable. We disable hashed in test env.
|
||||
export const defaultConfig = {
|
||||
token: defaultSeedToken,
|
||||
hashed: true,
|
||||
};
|
||||
|
||||
export const DesignTokenContext = React.createContext<{
|
||||
token: Partial<AliasToken>;
|
||||
theme?: Theme<SeedToken, MapToken>;
|
||||
components?: OverrideToken;
|
||||
hashed?: string | boolean;
|
||||
}>(defaultConfig);
|
||||
|
||||
// ================================== Hook ==================================
|
||||
export function useToken(): [Theme<SeedToken, MapToken>, GlobalToken, string] {
|
||||
const {
|
||||
token: rootDesignToken,
|
||||
hashed,
|
||||
theme,
|
||||
components,
|
||||
} = React.useContext(DesignTokenContext);
|
||||
|
||||
const salt = `${version}-${hashed || ''}`;
|
||||
|
||||
const mergedTheme = theme || defaultTheme;
|
||||
|
||||
const [token, hashId] = useCacheToken<GlobalToken, SeedToken>(
|
||||
mergedTheme,
|
||||
[defaultSeedToken, rootDesignToken],
|
||||
{
|
||||
salt,
|
||||
override: { override: rootDesignToken, ...components },
|
||||
formatToken,
|
||||
},
|
||||
);
|
||||
|
||||
return [mergedTheme, token, hashed ? hashId : ''];
|
||||
}
|
||||
|
||||
export type UseComponentStyleResult = [(node: React.ReactNode) => React.ReactElement, string];
|
||||
|
||||
export type GenerateStyle<
|
||||
ComponentToken extends object = AliasToken,
|
||||
ReturnType = CSSInterpolation,
|
||||
> = (token: ComponentToken) => ReturnType;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user