mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 17:44:35 +08:00
Merge branch 'master' into feat/addComponentToken
This commit is contained in:
commit
9dfeda75b0
94
.cursor/rules/locale.mdc
Normal file
94
.cursor/rules/locale.mdc
Normal file
@ -0,0 +1,94 @@
|
||||
---
|
||||
description: 本地化规范文档
|
||||
globs: ["components/locale/*_*.ts", "components/locale/index.tsx"]
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# 本地化规范
|
||||
|
||||
antd 中所有的本地化配置都应该在 `components/locale` 目录中完成定义,主要分为两步:类型定义和本地化配置
|
||||
|
||||
## 类型定义
|
||||
|
||||
antd 的本地化配置的类型定义的入口文件是 `components/locale/index.tsx`, 当需要添加新的本地化配置时,需要检查对应组件或全局配置的类型是否存在,如果不存在,则需要增加相应的类型描述。
|
||||
如果新增或修改的本地化配置时组件配置,那么具体的本地化类型应该在相应的组件目录定义,定义好后在 `components/locale/index.tsx` 引入对应组件的类型定义。
|
||||
|
||||
## 本地化配置
|
||||
|
||||
### 纯字符串配置
|
||||
|
||||
antd 中的本地化配置文件命名规则是:`*_*.ts`,如:`zh_CN.ts`,文件默认导出一个 `Locale` 类型对象。
|
||||
通常在为 antd 添加后修改某一项本地化配置时,如无特殊说明,需要同时修改所有语言的本地化配置。
|
||||
|
||||
本地化配置文件列表如下(包括但不限于):
|
||||
|
||||
```json
|
||||
["components/locale/ar_EG.ts","components/locale/az_AZ.ts","components/locale/bg_BG.ts","components/locale/bn_BD.ts","components/locale/by_BY.ts","components/locale/ca_ES.ts","components/locale/cs_CZ.ts","components/locale/da_DK.ts","components/locale/de_DE.ts","components/locale/el_GR.ts","components/locale/en_GB.ts","components/locale/en_US.ts","components/locale/es_ES.ts","components/locale/et_EE.ts","components/locale/eu_ES.ts","components/locale/fa_IR.ts","components/locale/fi_FI.ts","components/locale/fr_BE.ts","components/locale/fr_CA.ts","components/locale/fr_FR.ts","components/locale/ga_IE.ts","components/locale/gl_ES.ts","components/locale/he_IL.ts","components/locale/hi_IN.ts","components/locale/hr_HR.ts","components/locale/hu_HU.ts","components/locale/hy_AM.ts","components/locale/id_ID.ts","components/locale/is_IS.ts","components/locale/it_IT.ts","components/locale/ja_JP.ts","components/locale/ka_GE.ts","components/locale/kk_KZ.ts","components/locale/km_KH.ts","components/locale/kmr_IQ.ts","components/locale/kn_IN.ts","components/locale/ko_KR.ts","components/locale/ku_IQ.ts","components/locale/lt_LT.ts","components/locale/lv_LV.ts","components/locale/mk_MK.ts","components/locale/ml_IN.ts","components/locale/mn_MN.ts","components/locale/ms_MY.ts","components/locale/my_MM.ts","components/locale/nb_NO.ts","components/locale/ne_NP.ts","components/locale/nl_BE.ts","components/locale/nl_NL.ts","components/locale/pl_PL.ts","components/locale/pt_BR.ts","components/locale/pt_PT.ts","components/locale/ro_RO.ts","components/locale/ru_RU.ts","components/locale/si_LK.ts","components/locale/sk_SK.ts","components/locale/sl_SI.ts","components/locale/sr_RS.ts","components/locale/sv_SE.ts","components/locale/ta_IN.ts","components/locale/th_TH.ts","components/locale/tk_TK.ts","components/locale/tr_TR.ts","components/locale/uk_UA.ts","components/locale/ur_PK.ts","components/locale/uz_UZ.ts","components/locale/vi_VN.ts","components/locale/zh_CN.ts","components/locale/zh_HK.ts","components/locale/zh_TW.ts"]
|
||||
```
|
||||
|
||||
本地化配置的内容通常是纯字符串,如:
|
||||
|
||||
```typescript
|
||||
{
|
||||
// ...
|
||||
Modal: {
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
justOkText: '知道了',
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 字符串模版配置
|
||||
|
||||
当然,也有一些需要配置需要再运行时实时替换变量的模版配置,带有 `${}` 的变量将在实际使用的地方被实时替换成对应的变量内容:
|
||||
```typescript
|
||||
{
|
||||
// ...
|
||||
date: {
|
||||
format: '${label}日期格式无效',
|
||||
parse: '${label}不能转换为日期',
|
||||
invalid: '${label}是一个无效日期',
|
||||
}
|
||||
// ...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 全局配置
|
||||
|
||||
如果某个本地化配置不独属于某个组件,而是数据全局的本地化配置,此时应该在 `global` 中添加相关属性,如:
|
||||
|
||||
```typescript
|
||||
{
|
||||
// ...
|
||||
// locales for all components
|
||||
global: {
|
||||
placeholder: '请选择',
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
# 使用本地化
|
||||
|
||||
antd 中具体使用本地化配置时,可以使用 `components/locale/index.tsx` 文件中导出的 `useLocale` 获取全局上下文中配置的本地化,并跟组件属性中传入的本地化配置合并后得到最完整的本地化配置,如:
|
||||
|
||||
```tsx
|
||||
import { useLocale } from "../locale";
|
||||
import enUS from '../locale/en_US';
|
||||
|
||||
export function TestComp(props) {
|
||||
const { locale: propLocale } = props;
|
||||
const [contextLocale] = useLocale("TestComp", enUs);
|
||||
|
||||
const locale = {...contextLocale, ...propLocale};
|
||||
|
||||
return (
|
||||
<div title={locale?.title}>
|
||||
{locale?.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
Basically, antd naming requires **FULL NAME** instead of Abbreviation.
|
||||
@ -31,7 +31,7 @@ Basically, antd naming requires **FULL NAME** instead of Abbreviation.
|
||||
* Multiple icons: `FunctionName` + `Icon`
|
||||
* Trigger: `trigger`
|
||||
* Sub function trigger: `Sub Function` + `Trigger`
|
||||
* Trigger on the time point: `xxx` + `On` + `EventName` (e.g. `destroyOnClose`)
|
||||
* Trigger on the time point: `xxx` + `On` + `EventName` (e.g. `destroyOnHidden`)
|
||||
* Component use other component config. Naming as component.(e.g. `<Table pagination={{...}} />`)
|
||||
* ClassName: `className`
|
||||
* Additional classes should be merged into `classes` (e.g. `<Button classes={{ inner: 'custom-inner' }} />`)
|
||||
@ -62,15 +62,15 @@ ComponentRef {
|
||||
`variant (optional)` + `semantic part` + `semantic part variant (optional)` + `css property` + `size/disabled (optional)`
|
||||
|
||||
All component tokens should follow the structure above, and should not conflict with Global Token.
|
||||
* `variant` means this token only works in certain variant, like `horizontal` `borderless`.
|
||||
* `variant` means this token only works in certain variant, like `horizontal` `borderless`.
|
||||
* `semantic part` means the typical element of component, like `item` `header`. If there's.
|
||||
* `semantic part status` means the variant of the semantic part before it, like `hover` `selected`.
|
||||
* `css property` means the exact property where the token is used, like `fontSize` `width`.
|
||||
* `css property` means the exact property where the token is used, like `fontSize` `width`.
|
||||
|
||||
For example:
|
||||
| v4 | v5 | Note |
|
||||
| --- | --- | --- |
|
||||
| `@menu-item-color` | `itemColor` | Remove the component prefix |
|
||||
| `@menu-item-color` | `itemColor` | Remove the component prefix |
|
||||
| `@select-item-selected-bg` | `itemSelectedBg` | `selected` is variant of item |
|
||||
| `@select-single-item-height-lg` | `itemHeightLG` | `single` is variant of Select (by default), `LG` is size of Select |
|
||||
|
||||
@ -86,13 +86,13 @@ ref: [#16048](mdc:https:/github.com/ant-design/ant-design/issues/16048)
|
||||
| Property | Description | Type | Default |
|
||||
| --------- | ---------------------- | ------------------------------ | ------ |
|
||||
| htmlType | xxx | string | `button ` |
|
||||
| type | xxx | `horizontal ` \| `vertical ` | `horizontal` |
|
||||
| type | xxx | `horizontal ` \| `vertical ` | `horizontal` |
|
||||
| disabled | xxx | boolean | false |
|
||||
| minLength | xxx | number | 0 |
|
||||
| style | xxx | CSSProperties | - |
|
||||
| character | xxx | (props) => ReactNode | - |
|
||||
| offset | xxx| \[number, number] | \[0, 0] |
|
||||
| value | xxx | string \| number | `small` |
|
||||
| value | xxx | string \| number | `small` |
|
||||
|
||||
### Promise
|
||||
- When string type, the **Default** use ` `` `.
|
||||
@ -105,4 +105,4 @@ ref: [#16048](mdc:https:/github.com/ant-design/ant-design/issues/16048)
|
||||
- No period at the end of the **Description**.
|
||||
- API order is arranged in alphabetical order, and can be put together under special circumstances (such as: xs sm md).
|
||||
|
||||
ref: [#25066](mdc:https:/github.com/ant-design/ant-design/issues/25066)
|
||||
ref: [#25066](mdc:https:/github.com/ant-design/ant-design/issues/25066)
|
||||
|
87
.dumi/components/SelectSemanticTemplate.tsx
Normal file
87
.dumi/components/SelectSemanticTemplate.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
|
||||
import useLocale from '../hooks/useLocale';
|
||||
import SemanticPreview from './SemanticPreview';
|
||||
|
||||
export const locales = {
|
||||
cn: {
|
||||
root: '根元素',
|
||||
'popup.root': '弹出菜单元素',
|
||||
},
|
||||
en: {
|
||||
root: 'Root element',
|
||||
'popup.root': 'Popup element',
|
||||
},
|
||||
};
|
||||
|
||||
interface BlockProps {
|
||||
component: React.ComponentType<any>;
|
||||
options?: { value: string; label: string }[];
|
||||
defaultValue?: string;
|
||||
style?: React.CSSProperties;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const Block: React.FC<BlockProps> = ({ component: Component, options, defaultValue, ...props }) => {
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<div ref={divRef} style={{ position: 'absolute', marginBottom: 80 }}>
|
||||
<Component
|
||||
{...props}
|
||||
open
|
||||
placement="bottomLeft"
|
||||
defaultValue={defaultValue}
|
||||
getPopupContainer={() => divRef.current}
|
||||
options={options}
|
||||
styles={{
|
||||
popup: { zIndex: 1 },
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface SelectSemanticTemplateProps {
|
||||
component: React.ComponentType<any>;
|
||||
componentName: string;
|
||||
defaultValue?: string;
|
||||
options?: { value: string; label: string }[];
|
||||
height?: number;
|
||||
onSearch?: (text: string) => void;
|
||||
placeholder?: string;
|
||||
style?: React.CSSProperties;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const SelectSemanticTemplate: React.FC<SelectSemanticTemplateProps> = ({
|
||||
component,
|
||||
defaultValue,
|
||||
options,
|
||||
height,
|
||||
style,
|
||||
componentName,
|
||||
...restProps
|
||||
}) => {
|
||||
const [locale] = useLocale(locales);
|
||||
|
||||
return (
|
||||
<SemanticPreview
|
||||
componentName={componentName}
|
||||
semantics={[
|
||||
{ name: 'root', desc: locale.root, version: '5.25.0' },
|
||||
{ name: 'popup.root', desc: locale['popup.root'], version: '5.25.0' },
|
||||
]}
|
||||
height={height}
|
||||
>
|
||||
<Block
|
||||
component={component}
|
||||
defaultValue={defaultValue}
|
||||
options={options}
|
||||
style={style}
|
||||
{...restProps}
|
||||
/>
|
||||
</SemanticPreview>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectSemanticTemplate;
|
@ -1,9 +1,12 @@
|
||||
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
|
||||
import React from 'react';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import get from 'rc-util/lib/utils/get';
|
||||
import set from 'rc-util/lib/utils/set';
|
||||
import { Col, ConfigProvider, Flex, Popover, Row, Tag, theme, Typography } from 'antd';
|
||||
import { createStyles, css } from 'antd-style';
|
||||
import classnames from 'classnames';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import Prism from 'prismjs';
|
||||
|
||||
const MARK_BORDER_SIZE = 2;
|
||||
|
||||
@ -19,6 +22,9 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
|
||||
padding: ${token.paddingMD}px;
|
||||
overflow: hidden;
|
||||
`,
|
||||
colWrapPaddingLess: css`
|
||||
padding: 0;
|
||||
`,
|
||||
listWrap: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -66,37 +72,77 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
|
||||
`,
|
||||
}));
|
||||
|
||||
function getSemanticCells(semanticPath: string) {
|
||||
return semanticPath.split('.');
|
||||
}
|
||||
|
||||
function HighlightExample(props: { componentName: string; semanticName: string }) {
|
||||
const { componentName, semanticName } = props;
|
||||
|
||||
const highlightCode = React.useMemo(() => {
|
||||
const classNames = set({}, getSemanticCells(semanticName), `my-classname`);
|
||||
const styles = set({}, getSemanticCells(semanticName), { color: 'red' });
|
||||
|
||||
function format(obj: object) {
|
||||
const str = JSON.stringify(obj, null, 2);
|
||||
return (
|
||||
str
|
||||
// Add space
|
||||
.split('\n')
|
||||
.map((line) => ` ${line}`)
|
||||
.join('\n')
|
||||
.trim()
|
||||
// Replace quotes
|
||||
.replace(/"/g, "'")
|
||||
// Remove key quotes
|
||||
.replace(/'([^']+)':/g, '$1:')
|
||||
);
|
||||
}
|
||||
|
||||
const code = `
|
||||
<${componentName}
|
||||
classNames={${format(classNames)}}
|
||||
styles={${format(styles)}}
|
||||
/>`.trim();
|
||||
|
||||
return Prism.highlight(code, Prism.languages.javascript, 'jsx');
|
||||
}, [componentName, semanticName]);
|
||||
|
||||
return (
|
||||
// biome-ignore lint: lint/security/noDangerouslySetInnerHtml
|
||||
<div dangerouslySetInnerHTML={{ __html: highlightCode }} />
|
||||
);
|
||||
}
|
||||
|
||||
export interface SemanticPreviewProps {
|
||||
componentName: string;
|
||||
semantics: { name: string; desc: string; version?: string }[];
|
||||
children: React.ReactElement<any>;
|
||||
height?: number;
|
||||
padding?: false;
|
||||
}
|
||||
|
||||
const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
|
||||
const { semantics = [], children, height, componentName = 'Component' } = props;
|
||||
const { semantics = [], children, height, padding, componentName = 'Component' } = props;
|
||||
const { token } = theme.useToken();
|
||||
|
||||
// ======================= Semantic =======================
|
||||
const getMarkClassName = React.useCallback(
|
||||
(semanticKey: string) => `semantic-mark-${semanticKey}`,
|
||||
(semanticKey: string) => `semantic-mark-${semanticKey}`.replace(/\./g, '-'),
|
||||
[],
|
||||
);
|
||||
|
||||
const semanticClassNames = React.useMemo<Record<string, string>>(() => {
|
||||
const classNames: Record<string, string> = {};
|
||||
let classNames: Record<string, string> = {};
|
||||
|
||||
semantics.forEach((semantic) => {
|
||||
classNames[semantic.name] = getMarkClassName(semantic.name);
|
||||
const pathCell = getSemanticCells(semantic.name);
|
||||
classNames = set(classNames, pathCell, getMarkClassName(semantic.name));
|
||||
});
|
||||
|
||||
return classNames;
|
||||
}, [semantics]);
|
||||
|
||||
const cloneNode = React.cloneElement(children, {
|
||||
classNames: semanticClassNames,
|
||||
});
|
||||
|
||||
// ======================== Hover =========================
|
||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -137,11 +183,33 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
|
||||
};
|
||||
}, [hoverSemantic]);
|
||||
|
||||
const hoveredSemanticClassNames = React.useMemo(() => {
|
||||
if (!hoverSemantic) {
|
||||
return semanticClassNames;
|
||||
}
|
||||
|
||||
const hoverCell = getSemanticCells(hoverSemantic);
|
||||
const clone = set(
|
||||
semanticClassNames,
|
||||
hoverCell,
|
||||
classnames(get(semanticClassNames, hoverCell), getMarkClassName('active')),
|
||||
);
|
||||
|
||||
return clone;
|
||||
}, [semanticClassNames, hoverSemantic]);
|
||||
|
||||
// ======================== Render ========================
|
||||
const cloneNode = React.cloneElement(children, {
|
||||
classNames: hoveredSemanticClassNames,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classnames(styles.container)} ref={containerRef}>
|
||||
<Row style={{ minHeight: height }}>
|
||||
<Col span={16} className={classnames(styles.colWrap)}>
|
||||
<Col
|
||||
span={16}
|
||||
className={classnames(styles.colWrap, padding === false && styles.colWrapPaddingLess)}
|
||||
>
|
||||
<ConfigProvider theme={{ token: { motion: false } }}>{cloneNode}</ConfigProvider>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
@ -166,16 +234,10 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
|
||||
<Typography style={{ fontSize: 12, minWidth: 300 }}>
|
||||
<pre dir="ltr">
|
||||
<code dir="ltr">
|
||||
{`<${componentName}
|
||||
classNames={{
|
||||
${semantic.name}: 'my-${componentName.toLowerCase()}',
|
||||
}}
|
||||
styles={{
|
||||
${semantic.name}: { color: 'red' },
|
||||
}}
|
||||
>
|
||||
...
|
||||
</${componentName}>`}
|
||||
<HighlightExample
|
||||
componentName={componentName}
|
||||
semanticName={semantic.name}
|
||||
/>
|
||||
</code>
|
||||
</pre>
|
||||
</Typography>
|
||||
|
@ -160,7 +160,7 @@ const SubTokenTable: React.FC<SubTokenTableProps> = (props) => {
|
||||
{title}
|
||||
<Popover
|
||||
title={null}
|
||||
destroyTooltipOnHide
|
||||
destroyOnHidden
|
||||
styles={{ root: { width: 400 } }}
|
||||
content={
|
||||
<Typography>
|
||||
|
@ -277,7 +277,7 @@ const ComponentChangelog: React.FC<Readonly<React.PropsWithChildren>> = (props)
|
||||
{version}
|
||||
{bugVersionInfo.match && (
|
||||
<Popover
|
||||
destroyTooltipOnHide
|
||||
destroyOnHidden
|
||||
placement="right"
|
||||
title={<span className={styles.bugReasonTitle}>{locale.bugList}</span>}
|
||||
content={
|
||||
@ -327,7 +327,7 @@ const ComponentChangelog: React.FC<Readonly<React.PropsWithChildren>> = (props)
|
||||
onClick: () => setShow(true),
|
||||
})}
|
||||
<Drawer
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
className={styles.drawerContent}
|
||||
title={locale.changelog}
|
||||
extra={
|
||||
|
@ -306,7 +306,7 @@ const Header: React.FC = () => {
|
||||
className={styles.versionSelect}
|
||||
defaultValue={pkg.version}
|
||||
onChange={handleVersionChange}
|
||||
dropdownStyle={getDropdownStyle}
|
||||
styles={{ popup: { root: getDropdownStyle } }}
|
||||
popupMatchSelectWidth={false}
|
||||
getPopupContainer={(trigger) => trigger.parentNode}
|
||||
options={versionOptions}
|
||||
@ -338,7 +338,7 @@ const Header: React.FC = () => {
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Tooltip title="GitHub" destroyTooltipOnHide>
|
||||
<Tooltip title="GitHub" destroyOnHidden>
|
||||
<Button type="text" icon={<GithubOutlined />} style={{ fontSize: 16 }} />
|
||||
</Tooltip>
|
||||
</a>,
|
||||
|
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,8 +1,8 @@
|
||||
<!--
|
||||
First of all, thank you for your contribution! 😄
|
||||
For requesting to pull a new feature or bugfix, please send it from a feature/bugfix branch based on the `master` branch.
|
||||
Before submitting your pull request, please make sure the checklist below is confirmed.
|
||||
Your pull requests will be merged after one of the collaborators approve.
|
||||
Before submitting your pull request, please make sure the checklist below is filled out.
|
||||
Your pull requests will be merged after one of the collaborators approves.
|
||||
Thank you!
|
||||
-->
|
||||
|
||||
@ -41,7 +41,7 @@ Thank you!
|
||||
|
||||
### 📝 Change Log
|
||||
|
||||
> - Read [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) like a cat tracks a laser pointer.
|
||||
> - Read [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)! Track your changes, like a cat tracks a laser pointer.
|
||||
> - Describe the impact of the changes on developers, not the solution approach.
|
||||
> - Reference: https://ant.design/changelog
|
||||
|
||||
|
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
@ -9,26 +9,22 @@ updates:
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "05:00"
|
||||
timezone: Asia/Shanghai
|
||||
groups:
|
||||
rc-component-patch:
|
||||
dependency-type: production
|
||||
patterns:
|
||||
- "rc-*"
|
||||
- "@rc-component*"
|
||||
update-types: [patch]
|
||||
dependencies:
|
||||
dependency-type: production
|
||||
exclude-patterns:
|
||||
- "rc-*"
|
||||
- "@rc-component*"
|
||||
update-types: [major, minor]
|
||||
dev-dependencies:
|
||||
dependency-type: development
|
||||
update-types: [major]
|
||||
ignore:
|
||||
- dependency-name: "@ant-design/cssinjs"
|
||||
- dependency-name: "rc-*"
|
||||
- dependency-name: "@rc-component*"
|
||||
- dependency-name: "@ant-design*"
|
||||
- dependency-name: dayjs
|
||||
versions: [1.x]
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -38,6 +38,7 @@ package-lock.json
|
||||
pnpm-lock.yaml
|
||||
bun.lock*
|
||||
.pnpm-debug.log
|
||||
.pnpm-store
|
||||
components/**/*.js
|
||||
components/**/*.jsx
|
||||
!components/**/__tests__/**/*.js
|
||||
|
@ -75,5 +75,6 @@
|
||||
"https://github.com/ant-design/ant-design/issues/51420",
|
||||
"https://github.com/ant-design/ant-design/issues/51430"
|
||||
],
|
||||
"5.22.6": ["https://github.com/ant-design/ant-design/issues/52124"]
|
||||
"5.22.6": ["https://github.com/ant-design/ant-design/issues/52124"],
|
||||
"5.24.8": ["https://github.com/ant-design/ant-design/issues/53652"]
|
||||
}
|
||||
|
@ -9,16 +9,29 @@ tag: vVERSION
|
||||
|
||||
#### Release Schedule
|
||||
|
||||
- Weekly release: patch version at the end of every week for routine bugfix (anytime for urgent bugfix).
|
||||
- Weekly release: patch version at the end of every week for routine bugfixes (anytime for an urgent bugfix).
|
||||
- Monthly release: minor version at the end of every month for new features.
|
||||
- Major version release is not included in this schedule for breaking change and new features.
|
||||
- Major version release is not included in this schedule for breaking changes and new features.
|
||||
|
||||
---
|
||||
|
||||
## 5.24.9
|
||||
|
||||
`2025-04-29`
|
||||
|
||||
- 🐞 Fix Splitter mask not hiding correctly in lazy mode. [#53653](https://github.com/ant-design/ant-design/pull/53653) [@wanpan11](https://github.com/wanpan11)
|
||||
- 🐞 Fix issue when modifying `offsetBottom` and `offsetTop` of the Affix does not take effect. [#53607](https://github.com/ant-design/ant-design/pull/53607) [@yellowryan](https://github.com/yellowryan)
|
||||
- ⚡️ Fix Select keeps showing `clearIcon` when it has a value on mobile devices. [#53576](https://github.com/ant-design/ant-design/pull/53576) [@EmilyLiu](https://github.com/EmilyLiu)
|
||||
- 🐞 Fix Slider formatter returns fixed content, Tooltip position abnormal after dragging. [#53460](https://github.com/ant-design/ant-design/pull/53460) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
|
||||
- 🐞 Fix Tabs keyboard operation not working. [#53692](https://github.com/ant-design/ant-design/pull/53692) [@afc163](https://github.com/afc163)
|
||||
- RTL
|
||||
- 💄 Fix counter element direction of Image in RTL mode. [#53593](https://github.com/ant-design/ant-design/pull/53593) [@aojunhao123](https://github.com/aojunhao123)
|
||||
|
||||
## 5.24.8
|
||||
|
||||
`2025-04-21`
|
||||
|
||||
- 📖 Release [llms.txt](/llms.txt) and [llms-full.txt](/llms-full.txt), help LLM or agent to access detailed information during inference.
|
||||
- 🐞 Fix Tabs throwing `Maximum update depth exceeded` error in some cases. [#53521](https://github.com/ant-design/ant-design/pull/53521) [@afc163](https://github.com/afc163)
|
||||
- Splitter
|
||||
- 💄 Make Splitter collapse icon always visible on mobile devices. [#53575](https://github.com/ant-design/ant-design/pull/53575) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
|
||||
@ -33,7 +46,7 @@ tag: vVERSION
|
||||
- 🐞 MISC: Fix custom token `colorIcon` not taking effect for some components. [#53511](https://github.com/ant-design/ant-design/pull/53511) [@dengfuping](https://github.com/dengfuping)
|
||||
- 🐞 Fix message/notification runtime dynamic configuration changes not taking effect. [#53579](https://github.com/ant-design/ant-design/pull/53579) [@Wxh16144](https://github.com/Wxh16144)
|
||||
- RTL
|
||||
- 💄 Fix counter element direction of Image and Input.TextArea in RTL mode. [#53530](https://github.com/ant-design/ant-design/pull/53530) [@aojunhao123](https://github.com/aojunhao123)
|
||||
- 💄 Fix counter element direction of Input.TextArea in RTL mode. [#53530](https://github.com/ant-design/ant-design/pull/53530) [@aojunhao123](https://github.com/aojunhao123)
|
||||
- 💄 Fix incorrect direction of left and right switch icons in Image.PreviewGroup in RTL mode. [#53525](https://github.com/ant-design/ant-design/pull/53525) [@aojunhao123](https://github.com/aojunhao123)
|
||||
|
||||
## 5.24.7
|
||||
|
@ -15,10 +15,24 @@ tag: vVERSION
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 5.24.9
|
||||
|
||||
`2025-04-29`
|
||||
|
||||
- 🐞 修复 Splitter 在 `lazy` 模式下遮罩未能正确关闭的问题。[#53653](https://github.com/ant-design/ant-design/pull/53653) [@wanpan11](https://github.com/wanpan11)
|
||||
- 🐞 修复 Affix 在动态调整 `offsetTop` 和 `offsetBottom` 属性值后固定效果失效的问题。[#53607](https://github.com/ant-design/ant-design/pull/53607) [@yellowryan](https://github.com/yellowryan)
|
||||
- 💄 移动端场景下 Select 当有选中值时始终显示清除按钮。[#53576](https://github.com/ant-design/ant-design/pull/53576) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
|
||||
- 🐞 修复 Slider 设置 `formatter` 后,拖拽滑块导致 Tooltip 位置异常的问题。[#53460](https://github.com/ant-design/ant-design/pull/53460) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
|
||||
- 🐞 修复 Tabs 键盘操作失效的问题。[#53692](https://github.com/ant-design/ant-design/pull/53692) [@afc163](https://github.com/afc163)
|
||||
- RTL
|
||||
- 💄 修复 Image 计数元素在 RTL 模式下的显示方向问题。[#53593](https://github.com/ant-design/ant-design/pull/53593) [@aojunhao123](https://github.com/aojunhao123)
|
||||
|
||||
## 5.24.8
|
||||
|
||||
`2025-04-21`
|
||||
|
||||
- 📖 发布 [llms.txt](/llms.txt) 和 [llms-full.txt](/llms-full.txt),提供大模型友好的文本信息。
|
||||
- 🐞 修复 Tabs 有时抛出 `Maximum update depth exceeded` 错误的问题。[#53521](https://github.com/ant-design/ant-design/pull/53521) [@afc163](https://github.com/afc163)
|
||||
- Splitter
|
||||
- 💄 Splitter 折叠图标在移动设备上将始终展示。[#53575](https://github.com/ant-design/ant-design/pull/53575) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
|
||||
@ -33,7 +47,7 @@ tag: vVERSION
|
||||
- 🐞 MISC: 修复自定义 token `colorIcon` 对部分组件不生效的问题。[#53511](https://github.com/ant-design/ant-design/pull/53511) [@dengfuping](https://github.com/dengfuping)
|
||||
- 🐞 修复 message/notification 运行时动态修改配置无法生效的问题。[#53579](https://github.com/ant-design/ant-design/pull/53579) [@Wxh16144](https://github.com/Wxh16144)
|
||||
- RTL
|
||||
- 💄 修复 Image 和 Input.TextArea 计数元素在 RTL 模式中的方向问题。[#53530](https://github.com/ant-design/ant-design/pull/53530) [@aojunhao123](https://github.com/aojunhao123)
|
||||
- 💄 修复 Input.TextArea 计数元素在 RTL 模式中的方向问题。[#53530](https://github.com/ant-design/ant-design/pull/53530) [@aojunhao123](https://github.com/aojunhao123)
|
||||
- 💄 修复 Image.PreviewGroup 在 RTL 模式下左右切换图标方向不正确的问题。[#53525](https://github.com/ant-design/ant-design/pull/53525) [@aojunhao123](https://github.com/aojunhao123)
|
||||
|
||||
## 5.24.7
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
[![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
|
||||
|
||||
[![][bundlephobia-image]][bundlephobia-url] [![][jsdelivr-image]][jsdelivr-url] [![FOSSA Status][fossa-image]][fossa-url]
|
||||
[![][bundlephobia-image]][bundlephobia-url] [![][jsdelivr-image]][jsdelivr-url] [![FOSSA Status][fossa-image]][fossa-url] [![DeepWiki][deepwiki-image]][deepwiki-url]
|
||||
|
||||
[![Follow Twitter][twitter-image]][twitter-url] [![Renovate status][renovate-image]][renovate-dashboard-url] [![][issues-helper-image]][issues-helper-url] [![dumi][dumi-image]][dumi-url] [![Issues need help][help-wanted-image]][help-wanted-url]
|
||||
|
||||
@ -43,6 +43,8 @@
|
||||
[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square
|
||||
[dumi-url]: https://github.com/umijs/dumi
|
||||
[github-issues-url]: https://new-issue.ant.design
|
||||
[deepwiki-url]: https://deepwiki.com/ant-design/ant-design
|
||||
[deepwiki-image]: https://img.shields.io/badge/Chat%20with-DeepWiki%20🤖-20B2AA?style=flat-square
|
||||
|
||||
</div>
|
||||
|
||||
@ -191,8 +193,7 @@ $ npm start
|
||||
|
||||
<a href="https://openomy.app/github/ant-design/ant-design" target="_blank" style="display: block; width: 100%;" align="center">
|
||||
<img src="https://openomy.app/svg?repo=ant-design/ant-design&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||
</a>
|
||||
|
||||
</a>
|
||||
|
||||
请参考[贡献指南](https://ant.design/docs/react/contributing-cn).
|
||||
|
||||
@ -214,8 +215,6 @@ $ npm start
|
||||
|
||||
## Issue 赞助
|
||||
|
||||
我们使用 [Polar.sh](https://polar.sh/ant-design) 和 [Issuehunt](https://issuehunt.io/repos/3452688) 来推动您希望看到的针对 antd 的修复和改进,请查看我们的赞助列表:
|
||||
|
||||
<a href="https://polar.sh/ant-design"><img src="https://polar.sh/embed/fund-our-backlog.svg?org=ant-design" /></a>
|
||||
我们使用 [Issuehunt](https://issuehunt.io/repos/3452688) 来推动您希望看到的针对 antd 的修复和改进,请查看我们的赞助列表:
|
||||
|
||||
[](https://issuehunt.io/repos/34526884)
|
||||
|
15
README.md
15
README.md
@ -8,7 +8,7 @@ An enterprise-class UI design language and React UI library.
|
||||
|
||||
[![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
|
||||
|
||||
[![][bundlephobia-image]][bundlephobia-url] [![][jsdelivr-image]][jsdelivr-url] [![FOSSA Status][fossa-image]][fossa-url]
|
||||
[![][bundlephobia-image]][bundlephobia-url] [![][jsdelivr-image]][jsdelivr-url] [![FOSSA Status][fossa-image]][fossa-url] [![DeepWiki][deepwiki-image]][deepwiki-url]
|
||||
|
||||
[![Follow Twitter][twitter-image]][twitter-url] [![Renovate status][renovate-image]][renovate-dashboard-url] [![][issues-helper-image]][issues-helper-url] [![dumi][dumi-image]][dumi-url] [![Issues need help][help-wanted-image]][help-wanted-url]
|
||||
|
||||
@ -43,6 +43,8 @@ An enterprise-class UI design language and React UI library.
|
||||
[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square
|
||||
[dumi-url]: https://github.com/umijs/dumi
|
||||
[github-issues-url]: https://new-issue.ant.design
|
||||
[deepwiki-url]: https://deepwiki.com/ant-design/ant-design
|
||||
[deepwiki-image]: https://img.shields.io/badge/Chat%20with-DeepWiki%20🤖-20B2AA?style=flat-square
|
||||
|
||||
</div>
|
||||
|
||||
@ -136,7 +138,7 @@ $ npm install
|
||||
$ npm start
|
||||
```
|
||||
|
||||
Open your browser and visit http://127.0.0.1:8001 , see more at [Development](https://github.com/ant-design/ant-design/wiki/Development).
|
||||
Open your browser and visit http://127.0.0.1:8001, see more at [Development](https://github.com/ant-design/ant-design/wiki/Development).
|
||||
|
||||
## 🤝 Contributing [](https://makeapullrequest.com)
|
||||
|
||||
@ -173,19 +175,16 @@ Open your browser and visit http://127.0.0.1:8001 , see more at [Development](ht
|
||||
|
||||
<a href="https://openomy.app/github/ant-design/ant-design" target="_blank" style="display: block; width: 100%;" align="center">
|
||||
<img src="https://openomy.app/svg?repo=ant-design/ant-design&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||
</a>
|
||||
|
||||
</a>
|
||||
|
||||
Let's build a better antd together.
|
||||
|
||||
We warmly invite contributions from everyone. Before you get started, please take a moment to review our [Contributing Guide](https://ant.design/docs/react/contributing). Feel free to share your ideas through [Pull Requests](https://github.com/ant-design/ant-design/pulls) or [GitHub Issues](https://github.com/ant-design/ant-design/issues). If you're interested in enhancing our codebase, explore the [Development Instructions](https://github.com/ant-design/ant-design/wiki/Development) and enjoy your coding journey! :)
|
||||
We warmly invite contributions from everyone. Before you get started, please take a moment to review our [Contribution Guide](https://ant.design/docs/react/contributing). Feel free to share your ideas through [Pull Requests](https://github.com/ant-design/ant-design/pulls) or [GitHub Issues](https://github.com/ant-design/ant-design/issues). If you're interested in enhancing our codebase, explore the [Development Instructions](https://github.com/ant-design/ant-design/wiki/Development) and enjoy your coding journey! :)
|
||||
|
||||
For collaborators, adhere to our [Pull Request Principle](https://github.com/ant-design/ant-design/wiki/PR-principle) and utilize our [Pull Request Template](https://github.com/ant-design/ant-design/wiki/PR-principle#pull-request-template) when creating a Pull Request.
|
||||
|
||||
## Issue funding
|
||||
|
||||
We use [Polar.sh](https://polar.sh/ant-design) and [Issuehunt](https://issuehunt.io/repos/3452688) to up-vote and promote specific features that you would like to see and implement. Check our backlog and help us:
|
||||
|
||||
<a href="https://polar.sh/ant-design"><img src="https://polar.sh/embed/fund-our-backlog.svg?org=ant-design" /></a>
|
||||
We use [Issuehunt](https://issuehunt.io/repos/3452688) to up-vote and promote specific features that you would like to see and implement. Check our backlog and help us:
|
||||
|
||||
[](https://issuehunt.io/repos/34526884)
|
||||
|
@ -41,4 +41,9 @@ describe('unstable', () => {
|
||||
expect(document.querySelector('.ant-modal')).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it('unstableSetRender without param', async () => {
|
||||
const currentRender = unstableSetRender();
|
||||
expect(currentRender).toBeInstanceOf(Function);
|
||||
});
|
||||
});
|
||||
|
@ -5,23 +5,20 @@ import useResponsiveObserver from '../responsiveObserver';
|
||||
|
||||
describe('Test ResponsiveObserve', () => {
|
||||
it('test ResponsiveObserve subscribe and unsubscribe', () => {
|
||||
let responsiveObserveRef: any;
|
||||
const Demo = () => {
|
||||
let responsiveRef: any = null;
|
||||
const Demo: React.FC = () => {
|
||||
const responsiveObserver = useResponsiveObserver();
|
||||
responsiveObserveRef = responsiveObserver;
|
||||
responsiveRef = responsiveObserver;
|
||||
return null;
|
||||
};
|
||||
render(<Demo />);
|
||||
const subscribeFunc = jest.fn();
|
||||
const token = responsiveObserveRef.subscribe(subscribeFunc);
|
||||
expect(
|
||||
responsiveObserveRef.matchHandlers[responsiveObserveRef.responsiveMap.xs].mql.matches,
|
||||
).toBeTruthy();
|
||||
const token = responsiveRef.subscribe(subscribeFunc);
|
||||
expect(responsiveRef.matchHandlers[responsiveRef.responsiveMap.xs].mql.matches).toBeTruthy();
|
||||
expect(subscribeFunc).toHaveBeenCalledTimes(1);
|
||||
|
||||
responsiveObserveRef.unsubscribe(token);
|
||||
responsiveRef.unsubscribe(token);
|
||||
expect(
|
||||
responsiveObserveRef.matchHandlers[responsiveObserveRef.responsiveMap.xs].mql.removeListener,
|
||||
responsiveRef.matchHandlers[responsiveRef.responsiveMap.xs].mql?.removeEventListener,
|
||||
).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
20
components/_util/convertToTooltipProps.ts
Normal file
20
components/_util/convertToTooltipProps.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { isValidElement } from 'react';
|
||||
import type { TooltipProps } from '../tooltip';
|
||||
|
||||
function convertToTooltipProps<P extends TooltipProps>(tooltip: P | ReactNode): P | null {
|
||||
// isNil
|
||||
if (tooltip === undefined || tooltip === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof tooltip === 'object' && !isValidElement(tooltip)) {
|
||||
return tooltip as P;
|
||||
}
|
||||
|
||||
return {
|
||||
title: tooltip,
|
||||
} as P;
|
||||
}
|
||||
|
||||
export default convertToTooltipProps;
|
@ -1,21 +1,19 @@
|
||||
import type { AnyObject } from './type';
|
||||
|
||||
const extendsObject = <T extends AnyObject = AnyObject>(...list: T[]) => {
|
||||
const result: AnyObject = { ...list[0] };
|
||||
|
||||
for (let i = 1; i < list.length; i++) {
|
||||
const obj = list[i];
|
||||
if (obj) {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const val = obj[key];
|
||||
if (val !== undefined) {
|
||||
result[key] = val;
|
||||
// copied https://github.com/ant-design/ant-design-mobile/blob/d3b3bae/src/utils/with-default-props.tsx
|
||||
function mergeProps<A, B>(a: A, b: B): B & A;
|
||||
function mergeProps<A, B, C>(a: A, b: B, c: C): C & B & A;
|
||||
function mergeProps<A, B, C, D>(a: A, b: B, c: C, d: D): D & C & B & A;
|
||||
function mergeProps(...items: any[]) {
|
||||
const ret: any = {};
|
||||
items.forEach((item) => {
|
||||
if (item) {
|
||||
Object.keys(item).forEach((key) => {
|
||||
if (item[key] !== undefined) {
|
||||
ret[key] = item[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export default extendsObject;
|
||||
export default mergeProps;
|
||||
|
@ -3,6 +3,11 @@ import React from 'react';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import type { DialogProps } from 'rc-dialog';
|
||||
import pickAttrs from 'rc-util/lib/pickAttrs';
|
||||
import extendsObject from '../extendsObject';
|
||||
|
||||
import { useLocale } from '../../locale';
|
||||
import defaultLocale from '../../locale/en_US';
|
||||
import type { HTMLAriaDataAttributes } from '../aria-data-attrs';
|
||||
|
||||
export type ClosableType = DialogProps['closable'];
|
||||
|
||||
@ -58,56 +63,42 @@ function useClosableConfig(closableCollection?: ClosableCollection | null) {
|
||||
...closable,
|
||||
};
|
||||
}
|
||||
|
||||
return closableConfig;
|
||||
}, [closable, closeIcon]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign object without `undefined` field. Will skip if is `false`.
|
||||
* This helps to handle both closableConfig or false
|
||||
*/
|
||||
function assignWithoutUndefined<T extends object>(
|
||||
...objList: (Partial<T> | false | null | undefined)[]
|
||||
): Partial<T> {
|
||||
const target: Partial<T> = {};
|
||||
|
||||
objList.forEach((obj) => {
|
||||
if (obj) {
|
||||
(Object.keys(obj) as (keyof T)[]).forEach((key) => {
|
||||
if (obj[key] !== undefined) {
|
||||
target[key] = obj[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/** Collection contains the all the props related with closable. e.g. `closable`, `closeIcon` */
|
||||
interface ClosableCollection {
|
||||
closable?: ClosableType;
|
||||
closeIcon?: ReactNode;
|
||||
}
|
||||
|
||||
interface FallbackCloseCollection extends ClosableCollection {
|
||||
/**
|
||||
* Some components need to wrap CloseIcon twice,
|
||||
* this method will be executed once after the final CloseIcon is calculated
|
||||
*/
|
||||
closeIconRender?: (closeIcon: ReactNode) => ReactNode;
|
||||
}
|
||||
|
||||
/** Use same object to support `useMemo` optimization */
|
||||
const EmptyFallbackCloseCollection: ClosableCollection = {};
|
||||
const EmptyFallbackCloseCollection: FallbackCloseCollection = {};
|
||||
|
||||
export default function useClosable(
|
||||
propCloseCollection?: ClosableCollection,
|
||||
contextCloseCollection?: ClosableCollection | null,
|
||||
fallbackCloseCollection: ClosableCollection & {
|
||||
/**
|
||||
* Some components need to wrap CloseIcon twice,
|
||||
* this method will be executed once after the final CloseIcon is calculated
|
||||
*/
|
||||
closeIconRender?: (closeIcon: ReactNode) => ReactNode;
|
||||
} = EmptyFallbackCloseCollection,
|
||||
): [closable: boolean, closeIcon: React.ReactNode, closeBtnIsDisabled: boolean] {
|
||||
fallbackCloseCollection: FallbackCloseCollection = EmptyFallbackCloseCollection,
|
||||
): [
|
||||
closable: boolean,
|
||||
closeIcon: React.ReactNode,
|
||||
closeBtnIsDisabled: boolean,
|
||||
ariaOrDataProps?: HTMLAriaDataAttributes,
|
||||
] {
|
||||
// Align the `props`, `context` `fallback` to config object first
|
||||
const propCloseConfig = useClosableConfig(propCloseCollection);
|
||||
const contextCloseConfig = useClosableConfig(contextCloseCollection);
|
||||
|
||||
const [contextLocale] = useLocale('global', defaultLocale.global);
|
||||
const closeBtnIsDisabled =
|
||||
typeof propCloseConfig !== 'boolean' ? !!propCloseConfig?.disabled : false;
|
||||
const mergedFallbackCloseCollection = React.useMemo(
|
||||
@ -127,11 +118,7 @@ export default function useClosable(
|
||||
}
|
||||
|
||||
if (propCloseConfig) {
|
||||
return assignWithoutUndefined(
|
||||
mergedFallbackCloseCollection,
|
||||
contextCloseConfig,
|
||||
propCloseConfig,
|
||||
);
|
||||
return extendsObject(mergedFallbackCloseCollection, contextCloseConfig, propCloseConfig);
|
||||
}
|
||||
|
||||
// =============== Context Second ==============
|
||||
@ -141,7 +128,7 @@ export default function useClosable(
|
||||
}
|
||||
|
||||
if (contextCloseConfig) {
|
||||
return assignWithoutUndefined(mergedFallbackCloseCollection, contextCloseConfig);
|
||||
return extendsObject(mergedFallbackCloseCollection, contextCloseConfig);
|
||||
}
|
||||
|
||||
// ============= Fallback Default ==============
|
||||
@ -151,30 +138,34 @@ export default function useClosable(
|
||||
// Calculate the final closeIcon
|
||||
return React.useMemo(() => {
|
||||
if (mergedClosableConfig === false) {
|
||||
return [false, null, closeBtnIsDisabled];
|
||||
return [false, null, closeBtnIsDisabled, {}];
|
||||
}
|
||||
|
||||
const { closeIconRender } = mergedFallbackCloseCollection;
|
||||
const { closeIcon } = mergedClosableConfig;
|
||||
|
||||
let mergedCloseIcon: ReactNode = closeIcon;
|
||||
|
||||
// Wrap the closeIcon with aria props
|
||||
const ariaOrDataProps = pickAttrs(mergedClosableConfig, true);
|
||||
|
||||
if (mergedCloseIcon !== null && mergedCloseIcon !== undefined) {
|
||||
// Wrap the closeIcon if needed
|
||||
if (closeIconRender) {
|
||||
mergedCloseIcon = closeIconRender(closeIcon);
|
||||
}
|
||||
|
||||
// Wrap the closeIcon with aria props
|
||||
const ariaProps = pickAttrs(mergedClosableConfig, true);
|
||||
if (Object.keys(ariaProps).length) {
|
||||
mergedCloseIcon = React.isValidElement(mergedCloseIcon) ? (
|
||||
React.cloneElement(mergedCloseIcon, ariaProps)
|
||||
) : (
|
||||
<span {...ariaProps}>{mergedCloseIcon}</span>
|
||||
);
|
||||
}
|
||||
mergedCloseIcon = React.isValidElement(mergedCloseIcon) ? (
|
||||
React.cloneElement(mergedCloseIcon, {
|
||||
'aria-label': contextLocale.close,
|
||||
...ariaOrDataProps,
|
||||
} as HTMLAriaDataAttributes)
|
||||
) : (
|
||||
<span aria-label={contextLocale.close} {...ariaOrDataProps}>
|
||||
{mergedCloseIcon}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return [true, mergedCloseIcon, closeBtnIsDisabled];
|
||||
return [true, mergedCloseIcon, closeBtnIsDisabled, ariaOrDataProps];
|
||||
}, [mergedClosableConfig, mergedFallbackCloseCollection]);
|
||||
}
|
||||
|
19
components/_util/mediaQueryUtil.ts
Normal file
19
components/_util/mediaQueryUtil.ts
Normal file
@ -0,0 +1,19 @@
|
||||
type MQListenerHandler = (mql: MediaQueryList, handler: (e: MediaQueryListEvent) => void) => void;
|
||||
|
||||
export const addMediaQueryListener: MQListenerHandler = (mql, handler) => {
|
||||
// Don't delete here, please keep the code compatible
|
||||
if (typeof mql?.addEventListener !== 'undefined') {
|
||||
mql.addEventListener('change', handler);
|
||||
} else if (typeof mql?.addListener !== 'undefined') {
|
||||
mql.addListener(handler);
|
||||
}
|
||||
};
|
||||
|
||||
export const removeMediaQueryListener: MQListenerHandler = (mql, handler) => {
|
||||
// Don't delete here, please keep the code compatible
|
||||
if (typeof mql?.removeEventListener !== 'undefined') {
|
||||
mql.removeEventListener('change', handler);
|
||||
} else if (typeof mql?.removeListener !== 'undefined') {
|
||||
mql.removeListener(handler);
|
||||
}
|
||||
};
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
|
||||
import type { GlobalToken } from '../theme/internal';
|
||||
import { useToken } from '../theme/internal';
|
||||
import { addMediaQueryListener, removeMediaQueryListener } from './mediaQueryUtil';
|
||||
|
||||
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
|
||||
export type BreakpointMap = Record<Breakpoint, string>;
|
||||
@ -121,22 +122,20 @@ const useResponsiveObserver = () => {
|
||||
}
|
||||
},
|
||||
register() {
|
||||
Object.keys(responsiveMap).forEach((screen) => {
|
||||
const matchMediaQuery = responsiveMap[screen as Breakpoint];
|
||||
Object.entries(responsiveMap).forEach(([screen, mediaQuery]) => {
|
||||
const listener = ({ matches }: { matches: boolean }) => {
|
||||
this.dispatch({ ...screens, [screen]: matches });
|
||||
};
|
||||
const mql = window.matchMedia(matchMediaQuery);
|
||||
mql.addListener(listener);
|
||||
this.matchHandlers[matchMediaQuery] = { mql, listener };
|
||||
const mql = window.matchMedia(mediaQuery);
|
||||
addMediaQueryListener(mql, listener);
|
||||
this.matchHandlers[mediaQuery] = { mql, listener };
|
||||
listener(mql);
|
||||
});
|
||||
},
|
||||
unregister() {
|
||||
Object.keys(responsiveMap).forEach((screen) => {
|
||||
const matchMediaQuery = responsiveMap[screen as Breakpoint];
|
||||
const handler = this.matchHandlers[matchMediaQuery];
|
||||
handler?.mql.removeListener(handler?.listener);
|
||||
Object.values(responsiveMap).forEach((mediaQuery) => {
|
||||
const handler = this.matchHandlers[mediaQuery];
|
||||
removeMediaQueryListener(handler?.mql, handler?.listener);
|
||||
});
|
||||
subscribers.clear();
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ import CSSMotion from 'rc-motion';
|
||||
import raf from 'rc-util/lib/raf';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
|
||||
import { getReactRender } from '../../config-provider/UnstableContext';
|
||||
import { unstableSetRender } from '../../config-provider/UnstableContext';
|
||||
import type { UnmountType } from '../../config-provider/UnstableContext';
|
||||
import { TARGET_CLS } from './interface';
|
||||
import type { ShowWaveEffect } from './interface';
|
||||
@ -160,7 +160,7 @@ const showWaveEffect: ShowWaveEffect = (target, info) => {
|
||||
holder.style.top = '0px';
|
||||
target?.insertBefore(holder, target?.firstChild);
|
||||
|
||||
const reactRender = getReactRender();
|
||||
const reactRender = unstableSetRender();
|
||||
|
||||
let unmountCallback: UnmountType | null = null;
|
||||
|
||||
|
@ -238,7 +238,8 @@ const Affix = React.forwardRef<AffixRef, InternalAffixProps>((props, ref) => {
|
||||
|
||||
React.useEffect(() => {
|
||||
addListeners();
|
||||
}, [target, affixStyle, lastAffix]);
|
||||
return () => removeListeners();
|
||||
}, [target, affixStyle, lastAffix, offsetTop, offsetBottom]);
|
||||
|
||||
React.useEffect(() => {
|
||||
updatePosition();
|
||||
|
@ -44,10 +44,26 @@ const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
|
||||
const handleClick = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
||||
onClick?.(e, { title, href });
|
||||
scrollTo?.(href);
|
||||
if (replace) {
|
||||
e.preventDefault();
|
||||
window.location.replace(href);
|
||||
|
||||
// Support clicking on an anchor does not record history.
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isExternalLink = href.startsWith('http://') || href.startsWith('https://');
|
||||
// Support external link
|
||||
if (isExternalLink) {
|
||||
if (replace) {
|
||||
e.preventDefault();
|
||||
window.location.replace(href);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handling internal anchor link
|
||||
e.preventDefault();
|
||||
const historyMethod = replace ? 'replaceState' : 'pushState';
|
||||
window.history[historyMethod](null, '', href);
|
||||
};
|
||||
|
||||
// =================== Warning =====================
|
||||
|
@ -268,6 +268,27 @@ describe('Anchor Render', () => {
|
||||
expect(container.querySelector(`a[href="#${hash}_1"]`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not proceed when event is default prevented', () => {
|
||||
const hash = getHashUrl();
|
||||
const handleClick = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
const pushStateSpy = jest.spyOn(window.history, 'pushState');
|
||||
const replaceStateSpy = jest.spyOn(window.history, 'replaceState');
|
||||
const { container } = render(
|
||||
<Anchor items={[{ key: hash, href: `#${hash}`, title: hash }]} onClick={handleClick} />,
|
||||
);
|
||||
|
||||
const link = container.querySelector(`a[href="#${hash}"]`)!;
|
||||
|
||||
fireEvent.click(link);
|
||||
|
||||
expect(scrollToSpy).toHaveBeenCalled();
|
||||
expect(pushStateSpy).not.toHaveBeenCalled();
|
||||
expect(replaceStateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('targetOffset prop', async () => {
|
||||
const hash = getHashUrl();
|
||||
|
||||
@ -350,13 +371,27 @@ describe('Anchor Render', () => {
|
||||
expect(link).toEqual({ href, title });
|
||||
});
|
||||
|
||||
it('replaces item href in browser history', () => {
|
||||
it('replaces item href in browser history (hash href)', () => {
|
||||
const hash = getHashUrl();
|
||||
|
||||
const href = `#${hash}`;
|
||||
const title = hash;
|
||||
const { container } = render(<Anchor replace items={[{ key: hash, href, title }]} />);
|
||||
|
||||
jest.spyOn(window.history, 'replaceState').mockImplementation(() => {});
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="${href}"]`)!);
|
||||
|
||||
expect(window.history.replaceState).toHaveBeenCalledWith(null, '', href);
|
||||
});
|
||||
|
||||
it('replaces item href in browser history (external href)', () => {
|
||||
const hash = getHashUrl();
|
||||
|
||||
const href = `http://www.example.com/#${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);
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import cls from 'classnames';
|
||||
import type { BaseSelectRef } from 'rc-select';
|
||||
import toArray from 'rc-util/lib/Children/toArray';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
@ -36,12 +36,21 @@ export interface AutoCompleteProps<
|
||||
/** @deprecated Please use `options` instead */
|
||||
dataSource?: DataSourceItemType[];
|
||||
status?: InputStatus;
|
||||
/** @deprecated Please use `classNames.popup.root` instead */
|
||||
popupClassName?: string;
|
||||
/** @deprecated Please use `popupClassName` instead */
|
||||
/** @deprecated Please use `classNames.popup.root` instead */
|
||||
dropdownClassName?: string;
|
||||
/** @deprecated Please use `popupMatchSelectWidth` instead */
|
||||
dropdownMatchSelectWidth?: boolean | number;
|
||||
popupMatchSelectWidth?: boolean | number;
|
||||
/** @deprecated Please use `popupRender` instead */
|
||||
dropdownRender?: (menu: React.ReactElement) => React.ReactElement;
|
||||
popupRender?: (menu: React.ReactElement) => React.ReactElement;
|
||||
/** @deprecated Please use `styles.popup.root` instead */
|
||||
dropdownStyle?: React.CSSProperties;
|
||||
/** @deprecated Please use `onOpenChange` instead */
|
||||
onDropdownVisibleChange?: (visible: boolean) => void;
|
||||
onOpenChange?: (visible: boolean) => void;
|
||||
}
|
||||
|
||||
function isSelectOptionOrSelectOptGroup(child: any): boolean {
|
||||
@ -59,9 +68,21 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
|
||||
dropdownClassName,
|
||||
children,
|
||||
dataSource,
|
||||
dropdownStyle,
|
||||
dropdownRender,
|
||||
popupRender,
|
||||
onDropdownVisibleChange,
|
||||
onOpenChange,
|
||||
styles,
|
||||
classNames,
|
||||
} = props;
|
||||
const childNodes: React.ReactElement[] = toArray(children);
|
||||
|
||||
const mergedPopupStyle = styles?.popup?.root || dropdownStyle;
|
||||
const mergedPopupClassName = classNames?.popup?.root || popupClassName || dropdownClassName;
|
||||
const mergedPopupRender = popupRender || dropdownRender;
|
||||
const mergedOnOpenChange = onOpenChange || onDropdownVisibleChange;
|
||||
|
||||
// ============================= Input =============================
|
||||
let customizeInput: React.ReactElement | undefined;
|
||||
|
||||
@ -112,15 +133,25 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('AutoComplete');
|
||||
|
||||
warning.deprecated(!('dataSource' in props), 'dataSource', 'options');
|
||||
|
||||
warning(
|
||||
!customizeInput || !('size' in props),
|
||||
'usage',
|
||||
'You need to control style self instead of setting `size` when using customize input.',
|
||||
);
|
||||
|
||||
warning.deprecated(!dropdownClassName, 'dropdownClassName', 'popupClassName');
|
||||
const deprecatedProps = {
|
||||
dropdownMatchSelectWidth: 'popupMatchSelectWidth',
|
||||
dropdownStyle: 'styles.popup.root',
|
||||
dropdownClassName: 'classNames.popup.root',
|
||||
popupClassName: 'classNames.popup.root',
|
||||
dropdownRender: 'popupRender',
|
||||
onDropdownVisibleChange: 'onOpenChange',
|
||||
dataSource: 'options',
|
||||
};
|
||||
|
||||
Object.entries(deprecatedProps).forEach(([oldProp, newProp]) => {
|
||||
warning.deprecated(!(oldProp in props), oldProp, newProp);
|
||||
});
|
||||
}
|
||||
|
||||
const { getPrefixCls } = React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
@ -128,21 +159,33 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
|
||||
const prefixCls = getPrefixCls('select', customizePrefixCls);
|
||||
|
||||
// ============================ zIndex ============================
|
||||
const [zIndex] = useZIndex('SelectLike', props.dropdownStyle?.zIndex as number);
|
||||
const [zIndex] = useZIndex('SelectLike', mergedPopupStyle?.zIndex as number);
|
||||
|
||||
return (
|
||||
<Select
|
||||
ref={ref}
|
||||
suffixIcon={null}
|
||||
{...omit(props, ['dataSource', 'dropdownClassName'])}
|
||||
{...omit(props, ['dataSource', 'dropdownClassName', 'popupClassName'])}
|
||||
prefixCls={prefixCls}
|
||||
popupClassName={popupClassName || dropdownClassName}
|
||||
dropdownStyle={{
|
||||
...props.dropdownStyle,
|
||||
zIndex,
|
||||
classNames={{
|
||||
popup: {
|
||||
root: mergedPopupClassName,
|
||||
},
|
||||
root: classNames?.root,
|
||||
}}
|
||||
className={classNames(`${prefixCls}-auto-complete`, className)}
|
||||
styles={{
|
||||
popup: {
|
||||
root: {
|
||||
...mergedPopupStyle,
|
||||
zIndex,
|
||||
},
|
||||
},
|
||||
root: styles?.root,
|
||||
}}
|
||||
className={cls(`${prefixCls}-auto-complete`, className)}
|
||||
mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE as SelectProps['mode']}
|
||||
popupRender={mergedPopupRender}
|
||||
onOpenChange={mergedOnOpenChange}
|
||||
{...{
|
||||
// Internal api
|
||||
getInputElement,
|
||||
|
@ -728,7 +728,7 @@ exports[`renders components/auto-complete/demo/basic.tsx extend context correctl
|
||||
|
||||
exports[`renders components/auto-complete/demo/certain-category.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-lg ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
class="ant-select ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
style="width: 250px;"
|
||||
>
|
||||
<div
|
||||
@ -1096,11 +1096,7 @@ exports[`renders components/auto-complete/demo/certain-category.tsx extend conte
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/auto-complete/demo/certain-category.tsx extend context correctly 2`] = `
|
||||
[
|
||||
"Warning: [antd: AutoComplete] You need to control style self instead of setting \`size\` when using customize input.",
|
||||
]
|
||||
`;
|
||||
exports[`renders components/auto-complete/demo/certain-category.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/auto-complete/demo/custom.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
@ -2899,7 +2895,7 @@ exports[`renders components/auto-complete/demo/status.tsx extend context correct
|
||||
|
||||
exports[`renders components/auto-complete/demo/uncertain-category.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-lg ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
class="ant-select ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
style="width: 300px;"
|
||||
>
|
||||
<div
|
||||
@ -2986,11 +2982,7 @@ exports[`renders components/auto-complete/demo/uncertain-category.tsx extend con
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/auto-complete/demo/uncertain-category.tsx extend context correctly 2`] = `
|
||||
[
|
||||
"Warning: [antd: AutoComplete] You need to control style self instead of setting \`size\` when using customize input.",
|
||||
]
|
||||
`;
|
||||
exports[`renders components/auto-complete/demo/uncertain-category.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/auto-complete/demo/variant.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
|
@ -445,7 +445,7 @@ Array [
|
||||
|
||||
exports[`renders components/auto-complete/demo/certain-category.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-lg ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
class="ant-select ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
style="width:250px"
|
||||
>
|
||||
<div
|
||||
@ -1669,7 +1669,7 @@ exports[`renders components/auto-complete/demo/status.tsx correctly 1`] = `
|
||||
|
||||
exports[`renders components/auto-complete/demo/uncertain-category.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-lg ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
class="ant-select ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
style="width:300px"
|
||||
>
|
||||
<div
|
||||
|
@ -111,10 +111,101 @@ describe('AutoComplete', () => {
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: AutoComplete] `dropdownClassName` is deprecated. Please use `popupClassName` instead.',
|
||||
'Warning: [antd: AutoComplete] `dropdownClassName` is deprecated. Please use `classNames.popup.root` instead.',
|
||||
);
|
||||
expect(container.querySelector('.legacy')).toBeTruthy();
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('deprecated popupClassName', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const { container } = render(
|
||||
<AutoComplete
|
||||
popupClassName="legacy"
|
||||
open
|
||||
options={[{ label: 'little', value: 'little' }]}
|
||||
searchValue="l"
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: AutoComplete] `popupClassName` is deprecated. Please use `classNames.popup.root` instead.',
|
||||
);
|
||||
expect(container.querySelector('.legacy')).toBeTruthy();
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('deprecated dropdownMatchSelectWidth', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(
|
||||
<AutoComplete
|
||||
dropdownMatchSelectWidth
|
||||
open
|
||||
options={[{ label: 'little', value: 'little' }]}
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: AutoComplete] `dropdownMatchSelectWidth` is deprecated. Please use `popupMatchSelectWidth` instead.',
|
||||
);
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('deprecated dropdownStyle', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(
|
||||
<AutoComplete
|
||||
dropdownStyle={{ color: 'red' }}
|
||||
open
|
||||
options={[{ label: 'little', value: 'little' }]}
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: AutoComplete] `dropdownStyle` is deprecated. Please use `styles.popup.root` instead.',
|
||||
);
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('deprecated dropdownRender', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(
|
||||
<AutoComplete
|
||||
dropdownRender={(menu) => <div>{menu}</div>}
|
||||
open
|
||||
options={[{ label: 'little', value: 'little' }]}
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: AutoComplete] `dropdownRender` is deprecated. Please use `popupRender` instead.',
|
||||
);
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('deprecated onDropdownVisibleChange', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(
|
||||
<AutoComplete
|
||||
onDropdownVisibleChange={() => {}}
|
||||
options={[{ label: 'little', value: 'little' }]}
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: AutoComplete] `onDropdownVisibleChange` is deprecated. Please use `onOpenChange` instead.',
|
||||
);
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
32
components/auto-complete/demo/_semantic.tsx
Normal file
32
components/auto-complete/demo/_semantic.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { AutoComplete } from 'antd';
|
||||
|
||||
import SelectSemanticTemplate from '../../../.dumi/components/SelectSemanticTemplate';
|
||||
|
||||
const mockVal = (str: string, repeat = 1) => ({
|
||||
value: str.repeat(repeat),
|
||||
label: str.repeat(repeat),
|
||||
});
|
||||
|
||||
const getPanelValue = (searchText: string) =>
|
||||
!searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)];
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [options, setOptions] = React.useState([
|
||||
{ value: 'aojunhao123', label: 'aojunhao123' },
|
||||
{ value: 'thinkasany', label: 'thinkasany' },
|
||||
]);
|
||||
|
||||
return (
|
||||
<SelectSemanticTemplate
|
||||
component={AutoComplete}
|
||||
componentName="AutoComplete"
|
||||
style={{ width: 200 }}
|
||||
options={options}
|
||||
onSearch={(text: string) => setOptions(getPanelValue(text))}
|
||||
placeholder="input here"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -40,11 +40,10 @@ const options = [
|
||||
|
||||
const App: React.FC = () => (
|
||||
<AutoComplete
|
||||
popupClassName="certain-category-search-dropdown"
|
||||
classNames={{ popup: { root: 'certain-category-search-dropdown' } }}
|
||||
popupMatchSelectWidth={500}
|
||||
style={{ width: 250 }}
|
||||
options={options}
|
||||
size="large"
|
||||
>
|
||||
<Input.Search size="large" placeholder="input here" />
|
||||
</AutoComplete>
|
||||
|
@ -53,7 +53,6 @@ const App: React.FC = () => {
|
||||
options={options}
|
||||
onSelect={onSelect}
|
||||
onSearch={handleSearch}
|
||||
size="large"
|
||||
>
|
||||
<Input.Search size="large" placeholder="input here" enterButton />
|
||||
</AutoComplete>
|
||||
|
@ -48,12 +48,15 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| backfill | If backfill selected item the input when using keyboard | boolean | false | |
|
||||
| children (for customize input element) | Customize input element | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement<InputProps> | <Input /> | |
|
||||
| children (for dataSource) | Data source to auto complete | React.ReactElement<OptionProps> \| Array<React.ReactElement<OptionProps>> | - | |
|
||||
| classNames | Semantic DOM class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
|
||||
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |
|
||||
| defaultOpen | Initial open state of dropdown | boolean | - | |
|
||||
| defaultValue | Initial selected option | string | - | |
|
||||
| disabled | Whether disabled select | boolean | false | |
|
||||
| dropdownRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | 4.24.0 |
|
||||
| popupClassName | The className of dropdown menu | string | - | 4.23.0 |
|
||||
| ~~dropdownRender~~ | Customize dropdown content, use `popupRender` instead | (originNode: ReactNode) => ReactNode | - | 4.24.0 |
|
||||
| popupRender | Customize dropdown content | (originNode: ReactNode) => ReactNode | - | |
|
||||
| ~~dropdownStyle~~ | The style of dropdown menu, use `styles.popup.root` instead | CSSProperties | - | |
|
||||
| ~~popupClassName~~ | The className of dropdown menu, use `classNames.popup.root` instead | string | - | 4.23.0 |
|
||||
| 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 | true | |
|
||||
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns true, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \| function(inputValue, option) | true | |
|
||||
| getPopupContainer | Parent node of the dropdown. Default to body, if you encountered positioning problems during scroll, try changing to the scrollable area and position relative to it. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
|
||||
@ -64,11 +67,13 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| size | The size of the input box | `large` \| `middle` \| `small` | - | |
|
||||
| value | Selected option | string | - | |
|
||||
| styles | Semantic DOM style | [Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | 5.25.0 |
|
||||
| variant | Variants of input | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
||||
| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |
|
||||
| onBlur | Called when leaving the component | function() | - | |
|
||||
| onChange | Called when selecting an option or changing an input value | function(value) | - | |
|
||||
| onDropdownVisibleChange | Call when dropdown open | function(open) | - | |
|
||||
| ~~onDropdownVisibleChange~~ | Called when dropdown open, use `onOpenChange` instead | (open: boolean) => void | - | |
|
||||
| onOpenChange | Called when dropdown open | (open: boolean) => void | - | |
|
||||
| onFocus | Called when entering the component | function() | - | |
|
||||
| onSearch | Called when searching items | function(value) | - | |
|
||||
| onSelect | Called when a option is selected. param is option's value and option instance | function(value, option) | - | |
|
||||
@ -83,6 +88,10 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| blur() | Remove focus | |
|
||||
| focus() | Get focus | |
|
||||
|
||||
## Semantic DOM
|
||||
|
||||
<code src="./demo/_semantic.tsx" simplify="true"></code>
|
||||
|
||||
## Design Token
|
||||
|
||||
<ComponentTokenTable component="Select"></ComponentTokenTable>
|
||||
|
@ -49,12 +49,15 @@ demo:
|
||||
| backfill | 使用键盘选择选项的时候把选中项回填到输入框中 | boolean | false | |
|
||||
| children (自动完成的数据源) | 自动完成的数据源,不能和自定义输入框同时配置 | React.ReactElement<OptionProps> \| Array<React.ReactElement<OptionProps>> | - | |
|
||||
| children (自定义输入框) | 自定义输入框,不能和自动完成的数据源同时配置 | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement<InputProps> | <Input /> | |
|
||||
| classNames | 语义化结构 class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
|
||||
| defaultActiveFirstOption | 是否默认高亮第一个选项 | boolean | true | |
|
||||
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
|
||||
| defaultValue | 指定默认选中的条目 | string | - | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| dropdownRender | 自定义下拉框内容 | (menus: ReactNode) => ReactNode | - | 4.24.0 |
|
||||
| popupClassName | 下拉菜单的 className 属性 | string | - | 4.23.0 |
|
||||
| ~~dropdownRender~~ | 自定义下拉框内容,使用 `popupRender` 替换 | (originNode: ReactNode) => ReactNode | - | 4.24.0 |
|
||||
| popupRender | 自定义下拉框内容 | (originNode: ReactNode) => ReactNode | - | |
|
||||
| ~~popupClassName~~ | 下拉菜单的 className 属性,使用 `classNames.popup.root` 替换 | string | - | 4.23.0 |
|
||||
| ~~dropdownStyle~~ | 下拉菜单的 style 属性,使用 `styles.popup.root` 替换 | CSSProperties | - | |
|
||||
| popupMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`,当值小于选择框宽度时会被忽略。false 时会关闭虚拟滚动 | boolean \| number | true | |
|
||||
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true,反之则返回 false | boolean \| function(inputValue, option) | true | |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
|
||||
@ -64,12 +67,14 @@ demo:
|
||||
| placeholder | 输入框提示 | string | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| size | 控件大小 | `large` \| `middle` \| `small` | - | |
|
||||
| styles | 语义化结构 style | [Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | 5.25.0 |
|
||||
| value | 指定当前选中的条目 | string | - | |
|
||||
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
||||
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 |
|
||||
| onBlur | 失去焦点时的回调 | function() | - | |
|
||||
| onChange | 选中 option,或 input 的 value 变化时,调用此函数 | function(value) | - | |
|
||||
| onDropdownVisibleChange | 展开下拉菜单的回调 | function(open) | - | |
|
||||
| ~~onDropdownVisibleChange~~ | 展开下拉菜单的回调,使用 `onOpenChange` 替换 | (open: boolean) => void | - | |
|
||||
| onOpenChange | 展开下拉菜单的回调 | (open: boolean) => void | - | |
|
||||
| onFocus | 获得焦点时的回调 | function() | - | |
|
||||
| onSearch | 搜索补全项的时候调用 | function(value) | - | |
|
||||
| onSelect | 被选中时调用,参数为选中项的 value 值 | function(value, option) | - | |
|
||||
@ -84,6 +89,10 @@ demo:
|
||||
| blur() | 移除焦点 | |
|
||||
| focus() | 获取焦点 | |
|
||||
|
||||
## Semantic DOM
|
||||
|
||||
<code src="./demo/_semantic.tsx" simplify="true"></code>
|
||||
|
||||
## 主题变量(Design Token)
|
||||
|
||||
<ComponentTokenTable component="Select"></ComponentTokenTable>
|
||||
|
@ -127,7 +127,7 @@ const AvatarGroup: React.FC<AvatarGroupProps> = (props) => {
|
||||
};
|
||||
|
||||
childrenShow.push(
|
||||
<Popover key="avatar-popover-key" destroyTooltipOnHide {...mergeProps}>
|
||||
<Popover key="avatar-popover-key" destroyOnHidden {...mergeProps}>
|
||||
<Avatar style={mergeStyle}>{`+${numOfChildren - mergeCount}`}</Avatar>
|
||||
</Popover>,
|
||||
);
|
||||
|
@ -245,7 +245,6 @@ describe('Breadcrumb', () => {
|
||||
items={[
|
||||
{
|
||||
title: 'xxx',
|
||||
// @ts-ignore
|
||||
'data-custom': 'custom-item',
|
||||
},
|
||||
{
|
||||
|
@ -508,9 +508,46 @@ describe('Button', () => {
|
||||
const { getByRole } = render(
|
||||
<Button href="https://example.com" onClick={handleClick}>
|
||||
Link
|
||||
</Button>
|
||||
</Button>,
|
||||
);
|
||||
fireEvent.click(getByRole('link'));
|
||||
expect(handleClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ConfigProvider support button variant', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider button={{ variant: 'dashed', color: 'blue' }}>
|
||||
<Button>Button</Button>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toHaveClass('ant-btn-variant-dashed');
|
||||
expect(container.firstChild).toHaveClass('ant-btn-color-blue');
|
||||
});
|
||||
|
||||
it('should show the component internal properties', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider button={{ variant: 'dashed', color: 'blue' }}>
|
||||
<Button variant="filled" color="green">
|
||||
Button
|
||||
</Button>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
expect(container.firstChild).toHaveClass('ant-btn-variant-filled');
|
||||
expect(container.firstChild).toHaveClass('ant-btn-color-green');
|
||||
});
|
||||
|
||||
it('button type win the context', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider button={{ variant: 'dashed', color: 'green' }}>
|
||||
<Button type="primary" danger>
|
||||
Button
|
||||
</Button>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-btn-variant-solid')).toBeTruthy();
|
||||
expect(container.querySelector('.ant-btn-color-dangerous')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ import { useComposeRef } from 'rc-util/lib/ref';
|
||||
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import Wave from '../_util/wave';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import { ConfigContext, useComponentConfig } from '../config-provider/context';
|
||||
import DisabledContext from '../config-provider/DisabledContext';
|
||||
import useSize from '../config-provider/hooks/useSize';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
@ -127,20 +127,31 @@ const InternalCompoundedButton = React.forwardRef<
|
||||
// https://github.com/ant-design/ant-design/issues/47605
|
||||
// Compatible with original `type` behavior
|
||||
const mergedType = type || 'default';
|
||||
const { button } = React.useContext(ConfigContext);
|
||||
|
||||
const [mergedColor, mergedVariant] = useMemo<ColorVariantPairType>(() => {
|
||||
// >>>>> Local
|
||||
// Color & Variant
|
||||
if (color && variant) {
|
||||
return [color, variant];
|
||||
}
|
||||
|
||||
const colorVariantPair = ButtonTypeMap[mergedType] || [];
|
||||
|
||||
if (danger) {
|
||||
return ['danger', colorVariantPair[1]];
|
||||
// Sugar syntax
|
||||
if (type || danger) {
|
||||
const colorVariantPair = ButtonTypeMap[mergedType] || [];
|
||||
if (danger) {
|
||||
return ['danger', colorVariantPair[1]];
|
||||
}
|
||||
return colorVariantPair;
|
||||
}
|
||||
|
||||
return colorVariantPair;
|
||||
}, [type, color, variant, danger]);
|
||||
// >>> Context fallback
|
||||
if (button?.color && button?.variant) {
|
||||
return [button.color, button.variant];
|
||||
}
|
||||
|
||||
return ['default', 'outlined'];
|
||||
}, [type, color, variant, danger, button?.variant, button?.color]);
|
||||
|
||||
const isDanger = mergedColor === 'danger';
|
||||
const mergedColorText = isDanger ? 'dangerous' : mergedColor;
|
||||
@ -291,9 +302,10 @@ const InternalCompoundedButton = React.forwardRef<
|
||||
cssVarCls,
|
||||
{
|
||||
[`${prefixCls}-${shape}`]: shape !== 'default' && shape,
|
||||
// line(253 - 254): Compatible with versions earlier than 5.21.0
|
||||
// Compatible with versions earlier than 5.21.0
|
||||
[`${prefixCls}-${mergedType}`]: mergedType,
|
||||
[`${prefixCls}-dangerous`]: danger,
|
||||
|
||||
[`${prefixCls}-color-${mergedColorText}`]: mergedColorText,
|
||||
[`${prefixCls}-variant-${mergedVariant}`]: mergedVariant,
|
||||
[`${prefixCls}-${sizeCls}`]: sizeCls,
|
||||
|
@ -98,8 +98,8 @@ const useStyle = createStyles(({ token, css, cx }) => {
|
||||
const App: React.FC = () => {
|
||||
const { styles } = useStyle({ test: true });
|
||||
|
||||
const [selectDate, setSelectDate] = React.useState<Dayjs>(dayjs());
|
||||
const [panelDateDate, setPanelDate] = React.useState<Dayjs>(dayjs());
|
||||
const [selectDate, setSelectDate] = React.useState<Dayjs>(() => dayjs());
|
||||
const [panelDateDate, setPanelDate] = React.useState<Dayjs>(() => dayjs());
|
||||
|
||||
const onPanelChange = (value: Dayjs, mode: CalendarProps<Dayjs>['mode']) => {
|
||||
console.log(value.format('YYYY-MM-DD'), mode);
|
||||
|
@ -93,13 +93,11 @@ describe('Cascader', () => {
|
||||
});
|
||||
|
||||
it('popup correctly when panel is open', () => {
|
||||
const onPopupVisibleChange = jest.fn();
|
||||
const { container } = render(
|
||||
<Cascader options={options} onPopupVisibleChange={onPopupVisibleChange} />,
|
||||
);
|
||||
const onOpenChange = jest.fn();
|
||||
const { container } = render(<Cascader options={options} onOpenChange={onOpenChange} />);
|
||||
toggleOpen(container);
|
||||
expect(isOpen(container)).toBeTruthy();
|
||||
expect(onPopupVisibleChange).toHaveBeenCalledWith(true);
|
||||
expect(onOpenChange).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('support controlled mode', () => {
|
||||
@ -548,13 +546,87 @@ describe('Cascader', () => {
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const { container } = render(<Cascader dropdownClassName="legacy" open />);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] `dropdownClassName` is deprecated. Please use `popupClassName` instead.',
|
||||
'Warning: [antd: Cascader] `dropdownClassName` is deprecated. Please use `classNames.popup.root` instead.',
|
||||
);
|
||||
expect(container.querySelector('.legacy')).toBeTruthy();
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('legacy dropdownStyle', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const customStyle = { background: 'red' };
|
||||
const { container } = render(<Cascader dropdownStyle={customStyle} open />);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] `dropdownStyle` is deprecated. Please use `styles.popup.root` instead.',
|
||||
);
|
||||
expect(container.querySelector('.ant-select-dropdown')?.getAttribute('style')).toContain(
|
||||
'background: red',
|
||||
);
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('legacy dropdownRender', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const customContent = <div className="custom-dropdown-content">Custom Content</div>;
|
||||
const dropdownRender = (menu: React.ReactElement) => (
|
||||
<>
|
||||
{menu}
|
||||
{customContent}
|
||||
</>
|
||||
);
|
||||
|
||||
const { container } = render(<Cascader dropdownRender={dropdownRender} open />);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] `dropdownRender` is deprecated. Please use `popupRender` instead.',
|
||||
);
|
||||
expect(container.querySelector('.custom-dropdown-content')).toBeTruthy();
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('legacy dropdownMenuColumnStyle', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const columnStyle = { background: 'red' };
|
||||
const { getByRole } = render(
|
||||
<Cascader
|
||||
options={[{ label: 'test', value: 1 }]}
|
||||
dropdownMenuColumnStyle={columnStyle}
|
||||
open
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] `dropdownMenuColumnStyle` is deprecated. Please use `popupMenuColumnStyle` instead.',
|
||||
);
|
||||
const menuColumn = getByRole('menuitemcheckbox');
|
||||
expect(menuColumn.style.background).toBe('red');
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('legacy onDropdownVisibleChange', () => {
|
||||
resetWarned();
|
||||
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const onDropdownVisibleChange = jest.fn();
|
||||
const { container } = render(<Cascader onDropdownVisibleChange={onDropdownVisibleChange} />);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] `onDropdownVisibleChange` is deprecated. Please use `onOpenChange` instead.',
|
||||
);
|
||||
|
||||
toggleOpen(container);
|
||||
expect(onDropdownVisibleChange).toHaveBeenCalledWith(true);
|
||||
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should support showCheckedStrategy child', () => {
|
||||
const multipleOptions = [
|
||||
{
|
||||
|
78
components/cascader/demo/_semantic.tsx
Normal file
78
components/cascader/demo/_semantic.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import { Cascader } from 'antd';
|
||||
|
||||
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
|
||||
import useLocale from '../../../.dumi/hooks/useLocale';
|
||||
|
||||
const locales = {
|
||||
cn: {
|
||||
root: '根元素',
|
||||
'popup.root': '弹出菜单元素',
|
||||
},
|
||||
en: {
|
||||
root: 'Root element',
|
||||
'popup.root': 'Popup element',
|
||||
},
|
||||
};
|
||||
const options = [
|
||||
{
|
||||
value: 'contributors',
|
||||
label: 'contributors',
|
||||
children: [
|
||||
{
|
||||
value: 'aojunhao123',
|
||||
label: 'aojunhao123',
|
||||
},
|
||||
{
|
||||
value: 'thinkasany',
|
||||
label: 'thinkasany',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const Block = (props: any) => {
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
const [value, setValue] = React.useState<string[]>(['contributors', 'aojunhao123']);
|
||||
const onChange = (newValue: string[]) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
return (
|
||||
<div ref={divRef} style={{ marginBottom: 60 }}>
|
||||
<Cascader
|
||||
{...props}
|
||||
open
|
||||
styles={{
|
||||
popup: {
|
||||
root: {
|
||||
zIndex: 1,
|
||||
height: 70,
|
||||
},
|
||||
},
|
||||
}}
|
||||
getPopupContainer={() => divRef.current}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
placement="bottomLeft"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const App: React.FC = () => {
|
||||
const [locale] = useLocale(locales);
|
||||
|
||||
return (
|
||||
<SemanticPreview
|
||||
componentName="Cascader"
|
||||
semantics={[
|
||||
{ name: 'root', desc: locale.root, version: '5.25.0' },
|
||||
{ name: 'popup.root', desc: locale['popup.root'], version: '5.25.0' },
|
||||
]}
|
||||
>
|
||||
<Block />
|
||||
</SemanticPreview>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -42,7 +42,7 @@ const options: Option[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const dropdownRender = (menus: React.ReactNode) => (
|
||||
const popupRender = (menus: React.ReactNode) => (
|
||||
<div>
|
||||
{menus}
|
||||
<Divider style={{ margin: 0 }} />
|
||||
@ -51,7 +51,7 @@ const dropdownRender = (menus: React.ReactNode) => (
|
||||
);
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Cascader options={options} dropdownRender={dropdownRender} placeholder="Please select" />
|
||||
<Cascader options={options} popupRender={popupRender} placeholder="Please select" />
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
@ -55,13 +55,16 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| autoFocus | If get focus when component mounted | boolean | false | |
|
||||
| changeOnSelect | Change value on each selection if set to true, see above demo for details | boolean | false | |
|
||||
| className | The additional css class | string | - | |
|
||||
| classNames | Semantic DOM class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
|
||||
| defaultOpen | Initial visible of cascader popup | boolean | - | |
|
||||
| defaultValue | Initial selected value | string\[] \| number\[] | \[] | |
|
||||
| disabled | Whether disabled select | boolean | false | |
|
||||
| displayRender | The render function of displaying selected options | (label, selectedOptions) => ReactNode | label => label.join(`/`) | `multiple`: 4.18.0 |
|
||||
| tagRender | Custom render function for tags in `multiple` mode | (label: string, onClose: function, value: string) => ReactNode | - | |
|
||||
| popupClassName | The additional className of popup overlay | string | - | 4.23.0 |
|
||||
| dropdownRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | 4.4.0 |
|
||||
| ~~popupClassName~~ | The additional className of popup overlay, use `classNames.popup.root` instead | string | - | 4.23.0 |
|
||||
| ~~dropdownRender~~ | Customize dropdown content, use `popupRender` instead | (menus: ReactNode) => ReactNode | - | 4.4.0 |
|
||||
| popupRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | |
|
||||
| ~~dropdownStyle~~ | The style of dropdown menu, use `styles.popup.root` instead | CSSProperties | - | |
|
||||
| expandIcon | Customize the current item expand icon | ReactNode | - | 4.4.0 |
|
||||
| expandTrigger | expand current item when click or hover, one of `click` `hover` | string | `click` | |
|
||||
| fieldNames | Custom field name for label and value and children | object | { label: `label`, value: `value`, children: `children` } | |
|
||||
@ -79,18 +82,20 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| showSearch | Whether show search input in single mode | boolean \| [Object](#showsearch) | false | |
|
||||
| size | The input size | `large` \| `middle` \| `small` | - | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | The additional style | CSSProperties | - | |
|
||||
| styles | Semantic DOM style | [Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | 5.25.0 |
|
||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||
| value | The selected value | string\[] \| number\[] | - | |
|
||||
| variant | Variants of selector | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | 5.13.0 \| `underlined`: 5.24.0 |
|
||||
| onChange | Callback when finishing cascader select | (value, selectedOptions) => void | - | |
|
||||
| onDropdownVisibleChange | Callback when popup shown or hidden | (value) => void | - | 4.17.0 |
|
||||
| ~~onDropdownVisibleChange~~ | Callback when popup shown or hidden, use `onOpenChange` instead | (value) => void | - | 4.17.0 |
|
||||
| onOpenChange | Callback when popup shown or hidden | (value) => void | - | |
|
||||
| multiple | Support multiple or not | boolean | - | 4.17.0 |
|
||||
| removeIcon | The custom remove icon | ReactNode | - | |
|
||||
| showCheckedStrategy | The way show selected item in box. ** `SHOW_CHILD`: ** just show child treeNode. **`Cascader.SHOW_PARENT`:** just show parent treeNode (when all child treeNode under the parent treeNode are checked) | `Cascader.SHOW_PARENT` \| `Cascader.SHOW_CHILD` | `Cascader.SHOW_PARENT` | 4.20.0 |
|
||||
| searchValue | Set search value, Need work with `showSearch` | string | - | 4.17.0 |
|
||||
| onSearch | The callback function triggered when input changed | (search: string) => void | - | 4.17.0 |
|
||||
| dropdownMenuColumnStyle | The style of the drop-down menu column | CSSProperties | - | |
|
||||
| ~~dropdownMenuColumnStyle~~ | The style of the drop-down menu column, use `popupMenuColumnStyle` instead | CSSProperties | - | |
|
||||
| popupMenuColumnStyle | The style of the drop-down menu column | CSSProperties | - | |
|
||||
| loadingIcon | The appearance of lazy loading (now is useless) | ReactNode | - | |
|
||||
| optionRender | Customize the rendering dropdown options | (option: Option) => React.ReactNode | - | 5.16.0 |
|
||||
|
||||
@ -126,6 +131,10 @@ interface Option {
|
||||
| blur() | Remove focus | |
|
||||
| focus() | Get focus | |
|
||||
|
||||
## Semantic DOM
|
||||
|
||||
<code src="./demo/_semantic.tsx" simplify="true"></code>
|
||||
|
||||
## Design Token
|
||||
|
||||
<ComponentTokenTable component="Cascader"></ComponentTokenTable>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import cls from 'classnames';
|
||||
import type {
|
||||
BaseOptionType,
|
||||
DefaultOptionType,
|
||||
@ -50,6 +50,9 @@ export type FieldNamesType = FieldNames;
|
||||
|
||||
export type FilledFieldNamesType = Required<FieldNamesType>;
|
||||
|
||||
type SemanticName = 'root';
|
||||
type PopupSemantic = 'root';
|
||||
|
||||
const { SHOW_CHILD, SHOW_PARENT } = RcCascader;
|
||||
|
||||
function highlightKeyword(str: string, lowerKeyword: string, prefixCls?: string) {
|
||||
@ -127,14 +130,32 @@ export interface CascaderProps<
|
||||
autoClearSearchValue?: boolean;
|
||||
|
||||
rootClassName?: string;
|
||||
/** @deprecated Please use `classNames.popup.root` instead */
|
||||
popupClassName?: string;
|
||||
/** @deprecated Please use `popupClassName` instead */
|
||||
/** @deprecated Please use `classNames.popup.root` instead */
|
||||
dropdownClassName?: string;
|
||||
/** @deprecated Please use `styles.popup.root` instead */
|
||||
dropdownStyle?: React.CSSProperties;
|
||||
/** @deprecated Please use `popupRender` instead */
|
||||
dropdownRender?: (menu: React.ReactElement) => React.ReactElement;
|
||||
popupRender?: (menu: React.ReactElement) => React.ReactElement;
|
||||
/** @deprecated Please use `popupMenuColumnStyle` instead */
|
||||
dropdownMenuColumnStyle?: React.CSSProperties;
|
||||
popupMenuColumnStyle?: React.CSSProperties;
|
||||
/** @deprecated Please use `onOpenChange` instead */
|
||||
onDropdownVisibleChange?: (visible: boolean) => void;
|
||||
onOpenChange?: (visible: boolean) => void;
|
||||
/**
|
||||
* @since 5.13.0
|
||||
* @default "outlined"
|
||||
*/
|
||||
variant?: Variant;
|
||||
classNames?: Partial<Record<SemanticName, string>> & {
|
||||
popup?: Partial<Record<PopupSemantic, string>>;
|
||||
};
|
||||
styles?: Partial<Record<SemanticName, React.CSSProperties>> & {
|
||||
popup?: Partial<Record<PopupSemantic, React.CSSProperties>>;
|
||||
};
|
||||
}
|
||||
export type CascaderAutoProps<
|
||||
OptionType extends DefaultOptionType = DefaultOptionType,
|
||||
@ -173,6 +194,15 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
builtinPlacements,
|
||||
style,
|
||||
variant: customVariant,
|
||||
dropdownRender,
|
||||
onDropdownVisibleChange,
|
||||
dropdownMenuColumnStyle,
|
||||
popupRender,
|
||||
dropdownStyle,
|
||||
popupMenuColumnStyle,
|
||||
onOpenChange,
|
||||
styles,
|
||||
classNames,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@ -183,6 +213,8 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
getPopupContainer: getContextPopupContainer,
|
||||
className: contextClassName,
|
||||
style: contextStyle,
|
||||
classNames: contextClassNames,
|
||||
styles: contextStyles,
|
||||
} = useComponentConfig('cascader');
|
||||
|
||||
const { popupOverflow } = React.useContext(ConfigContext);
|
||||
@ -200,15 +232,25 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Cascader');
|
||||
|
||||
warning.deprecated(!dropdownClassName, 'dropdownClassName', 'popupClassName');
|
||||
// v5 deprecated dropdown api
|
||||
const deprecatedProps = {
|
||||
dropdownClassName: 'classNames.popup.root',
|
||||
dropdownStyle: 'styles.popup.root',
|
||||
dropdownRender: 'popupRender',
|
||||
dropdownMenuColumnStyle: 'popupMenuColumnStyle',
|
||||
onDropdownVisibleChange: 'onOpenChange',
|
||||
bordered: 'variant',
|
||||
};
|
||||
|
||||
Object.entries(deprecatedProps).forEach(([oldProp, newProp]) => {
|
||||
warning.deprecated(!(oldProp in props), oldProp, newProp);
|
||||
});
|
||||
|
||||
warning(
|
||||
!('showArrow' in props),
|
||||
'deprecated',
|
||||
'`showArrow` is deprecated which will be removed in next major version. It will be a default behavior, you can hide it by setting `suffixIcon` to null.',
|
||||
);
|
||||
|
||||
warning.deprecated(!('bordered' in props), 'bordered', 'variant');
|
||||
}
|
||||
|
||||
// ==================== Prefix =====================
|
||||
@ -235,19 +277,26 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
);
|
||||
|
||||
// =================== Dropdown ====================
|
||||
const mergedDropdownClassName = classNames(
|
||||
popupClassName || dropdownClassName,
|
||||
const mergedPopupClassName = cls(
|
||||
classNames?.popup?.root || contextClassNames.popup?.root || popupClassName || dropdownClassName,
|
||||
`${cascaderPrefixCls}-dropdown`,
|
||||
{
|
||||
[`${cascaderPrefixCls}-dropdown-rtl`]: mergedDirection === 'rtl',
|
||||
},
|
||||
rootClassName,
|
||||
rootCls,
|
||||
contextClassNames.root,
|
||||
classNames?.root,
|
||||
cascaderRootCls,
|
||||
hashId,
|
||||
cssVarCls,
|
||||
);
|
||||
|
||||
const mergedPopupRender = popupRender || dropdownRender;
|
||||
const mergedPopupMenuColumnStyle = popupMenuColumnStyle || dropdownMenuColumnStyle;
|
||||
const mergedOnOpenChange = onOpenChange || onDropdownVisibleChange;
|
||||
const mergedPopupStyle = styles?.popup?.root || contextStyles.popup?.root || dropdownStyle;
|
||||
|
||||
// ==================== Search =====================
|
||||
const mergedShowSearch = React.useMemo(() => {
|
||||
if (!showSearch) {
|
||||
@ -304,13 +353,13 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
const mergedAllowClear = allowClear === true ? { clearIcon } : allowClear;
|
||||
|
||||
// ============================ zIndex ============================
|
||||
const [zIndex] = useZIndex('SelectLike', restProps.dropdownStyle?.zIndex as number);
|
||||
const [zIndex] = useZIndex('SelectLike', mergedPopupStyle?.zIndex as number);
|
||||
|
||||
// ==================== Render =====================
|
||||
const renderNode = (
|
||||
<RcCascader
|
||||
prefixCls={prefixCls}
|
||||
className={classNames(
|
||||
className={cls(
|
||||
!customizePrefixCls && cascaderPrefixCls,
|
||||
{
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
@ -324,13 +373,15 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
contextClassName,
|
||||
className,
|
||||
rootClassName,
|
||||
classNames?.root,
|
||||
contextClassNames.root,
|
||||
rootCls,
|
||||
cascaderRootCls,
|
||||
hashId,
|
||||
cssVarCls,
|
||||
)}
|
||||
disabled={mergedDisabled}
|
||||
style={{ ...contextStyle, ...style }}
|
||||
style={{ ...contextStyles.root, ...styles?.root, ...contextStyle, ...style }}
|
||||
{...(restProps as any)}
|
||||
builtinPlacements={mergedBuiltinPlacements(builtinPlacements, popupOverflow)}
|
||||
direction={mergedDirection}
|
||||
@ -343,9 +394,12 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
|
||||
removeIcon={removeIcon}
|
||||
loadingIcon={loadingIcon}
|
||||
checkable={checkable}
|
||||
dropdownClassName={mergedDropdownClassName}
|
||||
dropdownClassName={mergedPopupClassName}
|
||||
dropdownPrefixCls={customizePrefixCls || cascaderPrefixCls}
|
||||
dropdownStyle={{ ...restProps.dropdownStyle, zIndex }}
|
||||
dropdownStyle={{ ...mergedPopupStyle, zIndex }}
|
||||
dropdownRender={mergedPopupRender}
|
||||
dropdownMenuColumnStyle={mergedPopupMenuColumnStyle}
|
||||
onOpenChange={mergedOnOpenChange}
|
||||
choiceTransitionName={getTransitionName(rootPrefixCls, '', choiceTransitionName)}
|
||||
transitionName={getTransitionName(rootPrefixCls, 'slide-up', transitionName)}
|
||||
getPopupContainer={getPopupContainer || getContextPopupContainer}
|
||||
|
@ -56,13 +56,16 @@ demo:
|
||||
| autoFocus | 自动获取焦点 | boolean | false | |
|
||||
| changeOnSelect | 单选时生效(multiple 下始终都可以选择),点选每级菜单选项值都会发生变化。 | boolean | false | |
|
||||
| className | 自定义类名 | string | - | |
|
||||
| classNames | 语义化结构 class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
|
||||
| defaultOpen | 是否默认展示浮层 | boolean | - | |
|
||||
| defaultValue | 默认的选中项 | string\[] \| number\[] | \[] | |
|
||||
| disabled | 禁用 | boolean | false | |
|
||||
| displayRender | 选择后展示的渲染函数 | (label, selectedOptions) => ReactNode | label => label.join(`/`) | `multiple`: 4.18.0 |
|
||||
| tagRender | 自定义 tag 内容 render,仅在多选时生效 | ({ label: string, onClose: function, value: string }) => ReactNode | - | |
|
||||
| popupClassName | 自定义浮层类名 | string | - | 4.23.0 |
|
||||
| dropdownRender | 自定义下拉框内容 | (menus: ReactNode) => ReactNode | - | 4.4.0 |
|
||||
| ~~popupClassName~~ | 自定义浮层类名,使用 `classNames.popup.root` 替换 | string | - | 4.23.0 |
|
||||
| ~~dropdownRender~~ | 自定义下拉框内容,请使用 `popupRender` 替换 | (menus: ReactNode) => ReactNode | - | 4.4.0 |
|
||||
| popupRender | 自定义下拉框内容 | (menus: ReactNode) => ReactNode | - | |
|
||||
| ~~dropdownStyle~~ | 下拉菜单的 style 属性,使用 `styles.popup.root` 替换 | CSSProperties | - | |
|
||||
| expandIcon | 自定义次级菜单展开图标 | ReactNode | - | 4.4.0 |
|
||||
| expandTrigger | 次级菜单的展开方式,可选 'click' 和 'hover' | string | `click` | |
|
||||
| fieldNames | 自定义 options 中 label value children 的字段 | object | { label: `label`, value: `value`, children: `children` } | |
|
||||
@ -80,18 +83,20 @@ demo:
|
||||
| showSearch | 在选择框中显示搜索框 | boolean \| [Object](#showsearch) | false | |
|
||||
| size | 输入框大小 | `large` \| `middle` \| `small` | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | 自定义样式 | CSSProperties | - | |
|
||||
| styles | 语义化结构 style | [Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | 5.25.0 |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||
| value | 指定选中项 | string\[] \| number\[] | - | |
|
||||
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | 5.13.0 \| `underlined`: 5.24.0 |
|
||||
| onChange | 选择完成后的回调 | (value, selectedOptions) => void | - | |
|
||||
| onDropdownVisibleChange | 显示/隐藏浮层的回调 | (value) => void | - | 4.17.0 |
|
||||
| ~~onDropdownVisibleChange~~ | 显示/隐藏浮层的回调,请使用 `onOpenChange` 替换 | (value) => void | - | 4.17.0 |
|
||||
| onOpenChange | 显示/隐藏浮层的回调 | (value) => void | - | |
|
||||
| multiple | 支持多选节点 | boolean | - | 4.17.0 |
|
||||
| showCheckedStrategy | 定义选中项回填的方式。`Cascader.SHOW_CHILD`: 只显示选中的子节点。`Cascader.SHOW_PARENT`: 只显示父节点(当父节点下所有子节点都选中时)。 | `Cascader.SHOW_PARENT` \| `Cascader.SHOW_CHILD` | `Cascader.SHOW_PARENT` | 4.20.0 |
|
||||
| removeIcon | 自定义的多选框清除图标 | ReactNode | - | |
|
||||
| searchValue | 设置搜索的值,需要与 `showSearch` 配合使用 | string | - | 4.17.0 |
|
||||
| onSearch | 监听搜索,返回输入的值 | (search: string) => void | - | 4.17.0 |
|
||||
| dropdownMenuColumnStyle | 下拉菜单列的样式 | CSSProperties | - | |
|
||||
| ~~dropdownMenuColumnStyle~~ | 下拉菜单列的样式,请使用 `popupMenuColumnStyle` 替换 | CSSProperties | - | |
|
||||
| popupMenuColumnStyle | 下拉菜单列的样式 | CSSProperties | - | |
|
||||
| optionRender | 自定义渲染下拉选项 | (option: Option) => React.ReactNode | - | 5.16.0 |
|
||||
|
||||
### showSearch
|
||||
@ -129,6 +134,10 @@ interface Option {
|
||||
|
||||
> 注意,如果需要获得中国省市区数据,可以参考 [china-division](https://gist.github.com/afc163/7582f35654fd03d5be7009444345ea17)。
|
||||
|
||||
## Semantic DOM
|
||||
|
||||
<code src="./demo/_semantic.tsx" simplify="true"></code>
|
||||
|
||||
## 主题变量(Design Token)
|
||||
|
||||
<ComponentTokenTable component="Cascader"></ComponentTokenTable>
|
||||
|
@ -177,7 +177,9 @@ const InternalCheckbox: React.ForwardRefRenderFunction<CheckboxRef, CheckboxProp
|
||||
disabled={mergedDisabled}
|
||||
ref={mergedRef}
|
||||
/>
|
||||
{children !== undefined && <span className={`${prefixCls}-label`}>{children}</span>}
|
||||
{children !== undefined && children !== null && (
|
||||
<span className={`${prefixCls}-label`}>{children}</span>
|
||||
)}
|
||||
</label>
|
||||
</Wave>,
|
||||
);
|
||||
|
@ -14,6 +14,7 @@ export interface CheckboxOptionType<T = any> {
|
||||
label: React.ReactNode;
|
||||
value: T;
|
||||
style?: React.CSSProperties;
|
||||
className?: string; // 👈 5.25.0+
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
id?: string;
|
||||
@ -125,7 +126,7 @@ const CheckboxGroup = React.forwardRef(
|
||||
value={option.value}
|
||||
checked={value.includes(option.value)}
|
||||
onChange={option.onChange}
|
||||
className={`${groupPrefixCls}-item`}
|
||||
className={classNames(`${groupPrefixCls}-item`, option.className)}
|
||||
style={option.style}
|
||||
title={option.title}
|
||||
id={option.id}
|
||||
@ -136,15 +137,19 @@ const CheckboxGroup = React.forwardRef(
|
||||
))
|
||||
: children;
|
||||
|
||||
const context: CheckboxGroupContext<any> = {
|
||||
toggleOption,
|
||||
value,
|
||||
disabled: restProps.disabled,
|
||||
name: restProps.name,
|
||||
// https://github.com/ant-design/ant-design/issues/16376
|
||||
registerValue,
|
||||
cancelValue,
|
||||
};
|
||||
const memoizedContext = React.useMemo<CheckboxGroupContext<any>>(
|
||||
() => ({
|
||||
toggleOption,
|
||||
value,
|
||||
disabled: restProps.disabled,
|
||||
name: restProps.name,
|
||||
// https://github.com/ant-design/ant-design/issues/16376
|
||||
registerValue,
|
||||
cancelValue,
|
||||
}),
|
||||
[toggleOption, value, restProps.disabled, restProps.name, registerValue, cancelValue],
|
||||
);
|
||||
|
||||
const classString = classNames(
|
||||
groupPrefixCls,
|
||||
{
|
||||
@ -156,9 +161,10 @@ const CheckboxGroup = React.forwardRef(
|
||||
rootCls,
|
||||
hashId,
|
||||
);
|
||||
|
||||
return wrapCSSVar(
|
||||
<div className={classString} style={style} {...domProps} ref={ref}>
|
||||
<GroupContext.Provider value={context}>{childrenNode}</GroupContext.Provider>
|
||||
<GroupContext.Provider value={memoizedContext}>{childrenNode}</GroupContext.Provider>
|
||||
</div>,
|
||||
);
|
||||
},
|
||||
|
@ -691,7 +691,7 @@ Array [
|
||||
class="ant-checkbox-group"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-group-item label-1"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target"
|
||||
@ -712,7 +712,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item label-2"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target ant-checkbox-checked"
|
||||
@ -734,7 +734,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-group-item label-3"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target"
|
||||
@ -761,7 +761,7 @@ Array [
|
||||
class="ant-checkbox-group"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item label-1"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target ant-checkbox-checked ant-checkbox-disabled"
|
||||
@ -784,7 +784,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item label-2"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target ant-checkbox-disabled"
|
||||
@ -806,7 +806,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item label-3"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target ant-checkbox-disabled"
|
||||
|
@ -649,7 +649,7 @@ Array [
|
||||
class="ant-checkbox-group"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-group-item label-1"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target"
|
||||
@ -670,7 +670,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item label-2"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target ant-checkbox-checked"
|
||||
@ -692,7 +692,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-group-item label-3"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target"
|
||||
@ -719,7 +719,7 @@ Array [
|
||||
class="ant-checkbox-group"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item label-1"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target ant-checkbox-checked ant-checkbox-disabled"
|
||||
@ -742,7 +742,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item label-2"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target ant-checkbox-disabled"
|
||||
@ -764,7 +764,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item label-3"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-wave-target ant-checkbox-disabled"
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Checkbox } from 'antd';
|
||||
import type { GetProp } from 'antd';
|
||||
import type { CheckboxOptionType, GetProp } from 'antd';
|
||||
|
||||
const onChange: GetProp<typeof Checkbox.Group, 'onChange'> = (checkedValues) => {
|
||||
console.log('checked = ', checkedValues);
|
||||
@ -8,16 +8,16 @@ const onChange: GetProp<typeof Checkbox.Group, 'onChange'> = (checkedValues) =>
|
||||
|
||||
const plainOptions = ['Apple', 'Pear', 'Orange'];
|
||||
|
||||
const options = [
|
||||
{ label: 'Apple', value: 'Apple' },
|
||||
{ label: 'Pear', value: 'Pear' },
|
||||
{ label: 'Orange', value: 'Orange' },
|
||||
const options: CheckboxOptionType<string>[] = [
|
||||
{ label: 'Apple', value: 'Apple', className: 'label-1' },
|
||||
{ label: 'Pear', value: 'Pear', className: 'label-2' },
|
||||
{ label: 'Orange', value: 'Orange', className: 'label-3' },
|
||||
];
|
||||
|
||||
const optionsWithDisabled = [
|
||||
{ label: 'Apple', value: 'Apple' },
|
||||
{ label: 'Pear', value: 'Pear' },
|
||||
{ label: 'Orange', value: 'Orange', disabled: false },
|
||||
const optionsWithDisabled: CheckboxOptionType<string>[] = [
|
||||
{ label: 'Apple', value: 'Apple', className: 'label-1' },
|
||||
{ label: 'Pear', value: 'Pear', className: 'label-2' },
|
||||
{ label: 'Orange', value: 'Orange', className: 'label-3', disabled: false },
|
||||
];
|
||||
|
||||
const App: React.FC = () => (
|
||||
|
@ -44,7 +44,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| onBlur | Called when leaving the component | function() | - | |
|
||||
| onFocus | Called when entering the component | function() | - | |
|
||||
|
||||
#### Checkbox Group
|
||||
#### Checkbox.Group
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@ -53,6 +53,9 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| name | The `name` property of all `input[type="checkbox"]` children | string | - | |
|
||||
| options | Specifies options | string\[] \| number\[] \| Option\[] | \[] | |
|
||||
| value | Used for setting the currently selected value | (string \| number \| boolean)\[] | \[] | |
|
||||
| title | title of the option | `string` | - | |
|
||||
| className | className of the option | `string` | - | 5.25.0 |
|
||||
| style | styles of the option | `React.CSSProperties` | - | |
|
||||
| onChange | The callback function that is triggered when the state changes | (checkedValue: T[]) => void | - | |
|
||||
|
||||
##### Option
|
||||
|
@ -45,7 +45,7 @@ demo:
|
||||
| onBlur | 失去焦点时的回调 | function() | - | |
|
||||
| onFocus | 获得焦点时的回调 | function() | - | |
|
||||
|
||||
#### Checkbox Group
|
||||
#### Checkbox.Group
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@ -54,6 +54,9 @@ demo:
|
||||
| name | CheckboxGroup 下所有 `input[type="checkbox"]` 的 `name` 属性 | string | - | |
|
||||
| options | 指定可选项 | string\[] \| number\[] \| Option\[] | \[] | |
|
||||
| value | 指定选中的选项 | (string \| number \| boolean)\[] | \[] | |
|
||||
| title | 选项的 title | `string` | - | |
|
||||
| className | 选项的类名 | `string` | - | 5.25.0 |
|
||||
| style | 选项的样式 | `React.CSSProperties` | - | |
|
||||
| onChange | 变化时的回调函数 | (checkedValue: T[]) => void | - | |
|
||||
|
||||
##### Option
|
||||
|
@ -26,7 +26,12 @@ export interface CollapseProps extends Pick<RcCollapseProps, 'items'> {
|
||||
defaultActiveKey?: Array<string | number> | string | number;
|
||||
/** 手风琴效果 */
|
||||
accordion?: boolean;
|
||||
/** @deprecated Please use `destroyOnHidden` instead */
|
||||
destroyInactivePanel?: boolean;
|
||||
/**
|
||||
* @since 5.25.0
|
||||
*/
|
||||
destroyOnHidden?: boolean;
|
||||
onChange?: (key: string[]) => void;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
@ -76,6 +81,8 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
|
||||
size: customizeSize,
|
||||
expandIconPosition = 'start',
|
||||
children,
|
||||
destroyInactivePanel,
|
||||
destroyOnHidden,
|
||||
expandIcon,
|
||||
} = props;
|
||||
|
||||
@ -93,6 +100,11 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
|
||||
'deprecated',
|
||||
'`expandIconPosition` with `left` or `right` is deprecated. Please use `start` or `end` instead.',
|
||||
);
|
||||
warning.deprecated(
|
||||
!('destroyInactivePanel' in props),
|
||||
'destroyInactivePanel',
|
||||
'destroyOnHidden',
|
||||
);
|
||||
}
|
||||
|
||||
// Align with logic position
|
||||
@ -185,6 +197,8 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
|
||||
prefixCls={prefixCls}
|
||||
className={collapseClassName}
|
||||
style={{ ...contextStyle, ...style }}
|
||||
// TODO: In the future, destroyInactivePanel in rc-collapse needs to be upgrade to destroyOnHidden
|
||||
destroyInactivePanel={destroyOnHidden ?? destroyInactivePanel}
|
||||
>
|
||||
{items}
|
||||
</RcCollapse>,
|
||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
|
||||
describe('Collapse', () => {
|
||||
const Collapse = require('..').default;
|
||||
@ -275,6 +276,52 @@ describe('Collapse', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should support borderlessContentBg component token', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Collapse: {
|
||||
borderlessContentBg: 'red',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Collapse bordered={false} defaultActiveKey={['1']}>
|
||||
<Collapse.Panel header="This is panel header 1" key="1">
|
||||
content
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-collapse-content')).toHaveStyle({
|
||||
backgroundColor: 'red',
|
||||
});
|
||||
});
|
||||
|
||||
it('should support borderlessContentPadding component token', () => {
|
||||
const { container } = render(
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Collapse: {
|
||||
borderlessContentPadding: '10px',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Collapse bordered={false} defaultActiveKey={['1']}>
|
||||
<Collapse.Panel header="This is panel header 1" key="1">
|
||||
content
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector('.ant-collapse-content-box')).toHaveStyle({
|
||||
padding: '10px',
|
||||
});
|
||||
});
|
||||
|
||||
it('should support styles and classNames', () => {
|
||||
const { container } = render(
|
||||
<Collapse
|
||||
|
@ -83,7 +83,8 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| bordered | Toggles rendering of the border around the collapse block | boolean | true | |
|
||||
| collapsible | Specify how to trigger Collapse. Either by clicking icon or by clicking any area in header or disable collapse functionality itself | `header` \| `icon` \| `disabled` | - | 4.9.0 |
|
||||
| defaultActiveKey | Key of the initial active panel | string\[] \| string <br/> number\[] \| number | - | |
|
||||
| destroyInactivePanel | Destroy Inactive Panel | boolean | false | |
|
||||
| ~~destroyInactivePanel~~ | Destroy Inactive Panel | boolean | false | |
|
||||
| destroyOnHidden | Destroy Inactive Panel | boolean | false | 5.25.0 |
|
||||
| expandIcon | Allow to customize collapse icon | (panelProps) => ReactNode | - | |
|
||||
| expandIconPosition | Set expand icon position | `start` \| `end` | - | 4.21.0 |
|
||||
| ghost | Make the collapse borderless and its background transparent | boolean | false | 4.4.0 |
|
||||
|
@ -84,7 +84,8 @@ const items: CollapseProps['items'] = [
|
||||
| bordered | 带边框风格的折叠面板 | boolean | true | |
|
||||
| collapsible | 所有子面板是否可折叠或指定可折叠触发区域 | `header` \| `icon` \| `disabled` | - | 4.9.0 |
|
||||
| defaultActiveKey | 初始化选中面板的 key | string\[] \| string<br/> number\[] \| number | - | |
|
||||
| destroyInactivePanel | 销毁折叠隐藏的面板 | boolean | false | |
|
||||
| ~~destroyInactivePanel~~ | 销毁折叠隐藏的面板 | boolean | false | |
|
||||
| destroyOnHidden | 销毁折叠隐藏的面板 | boolean | false | 5.25.0 |
|
||||
| expandIcon | 自定义切换图标 | (panelProps) => ReactNode | - | |
|
||||
| expandIconPosition | 设置图标位置 | `start` \| `end` | - | 4.21.0 |
|
||||
| ghost | 使折叠面板透明且无边框 | boolean | false | 4.4.0 |
|
||||
|
@ -29,6 +29,16 @@ export interface ComponentToken {
|
||||
* @descEN Background of content
|
||||
*/
|
||||
contentBg: string;
|
||||
/**
|
||||
* @desc 简约风格折叠面板的内容内边距
|
||||
* @descEN Padding of content in borderless style
|
||||
*/
|
||||
borderlessContentPadding: CSSProperties['padding'];
|
||||
/**
|
||||
* @desc 简约风格折叠面板的内容背景
|
||||
* @descEN Background of content in borderless style
|
||||
*/
|
||||
borderlessContentBg: string;
|
||||
}
|
||||
|
||||
type CollapseToken = FullToken<'Collapse'> & {
|
||||
@ -271,13 +281,8 @@ const genArrowStyle: GenerateStyle<CollapseToken> = (token) => {
|
||||
};
|
||||
|
||||
const genBorderlessStyle: GenerateStyle<CollapseToken> = (token) => {
|
||||
const {
|
||||
componentCls,
|
||||
headerBg,
|
||||
paddingXXS,
|
||||
|
||||
colorBorder,
|
||||
} = token;
|
||||
const { componentCls, headerBg, borderlessContentPadding, borderlessContentBg, colorBorder } =
|
||||
token;
|
||||
|
||||
return {
|
||||
[`${componentCls}-borderless`]: {
|
||||
@ -300,12 +305,12 @@ const genBorderlessStyle: GenerateStyle<CollapseToken> = (token) => {
|
||||
},
|
||||
|
||||
[`> ${componentCls}-item > ${componentCls}-content`]: {
|
||||
backgroundColor: 'transparent',
|
||||
backgroundColor: borderlessContentBg,
|
||||
borderTop: 0,
|
||||
},
|
||||
|
||||
[`> ${componentCls}-item > ${componentCls}-content > ${componentCls}-content-box`]: {
|
||||
paddingTop: paddingXXS,
|
||||
padding: borderlessContentPadding,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -337,6 +342,8 @@ export const prepareComponentToken: GetDefaultToken<'Collapse'> = (token) => ({
|
||||
headerBg: token.colorFillAlter,
|
||||
contentPadding: `${token.padding}px 16px`, // Fixed Value
|
||||
contentBg: token.colorBgContainer,
|
||||
borderlessContentPadding: `${token.paddingXXS}px 16px ${token.padding}px`,
|
||||
borderlessContentBg: 'transparent',
|
||||
});
|
||||
|
||||
export default genStyleHooks(
|
||||
|
@ -60,6 +60,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
getPopupContainer,
|
||||
autoAdjustOverflow = true,
|
||||
destroyTooltipOnHide,
|
||||
destroyOnHidden,
|
||||
disabledFormat,
|
||||
...rest
|
||||
} = props;
|
||||
@ -211,7 +212,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
rootClassName,
|
||||
getPopupContainer,
|
||||
autoAdjustOverflow,
|
||||
destroyTooltipOnHide,
|
||||
destroyOnHidden: destroyOnHidden ?? !!destroyTooltipOnHide,
|
||||
};
|
||||
|
||||
const mergedStyle: React.CSSProperties = { ...colorPicker?.style, ...style };
|
||||
|
@ -685,7 +685,7 @@ describe('ColorPicker', () => {
|
||||
value,
|
||||
)}]`, async () => {
|
||||
const Demo = () => {
|
||||
const [color, setColor] = useState<ColorValueType>(generateColor('red'));
|
||||
const [color, setColor] = useState<ColorValueType>(() => generateColor('red'));
|
||||
useEffect(() => {
|
||||
setColor(value);
|
||||
}, []);
|
||||
@ -699,7 +699,7 @@ describe('ColorPicker', () => {
|
||||
|
||||
it('Controlled string value should work with allowClear correctly', async () => {
|
||||
const Demo = (props: any) => {
|
||||
const [color, setColor] = useState<ColorValueType>(generateColor('#FF0000'));
|
||||
const [color, setColor] = useState<ColorValueType>(() => generateColor('#FF0000'));
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof props.value !== 'undefined') {
|
||||
@ -738,7 +738,7 @@ describe('ColorPicker', () => {
|
||||
|
||||
it('Controlled value should work with allowClear correctly', async () => {
|
||||
const Demo = (props: any) => {
|
||||
const [color, setColor] = useState<ColorValueType>(generateColor('red'));
|
||||
const [color, setColor] = useState<ColorValueType>(() => generateColor('red'));
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof props.value !== 'undefined') {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import type { AggregationColor } from '../color';
|
||||
import { generateColor, getColorAlpha } from '../util';
|
||||
@ -13,22 +13,19 @@ interface ColorAlphaInputProps {
|
||||
|
||||
const ColorAlphaInput: FC<ColorAlphaInputProps> = ({ prefixCls, value, onChange }) => {
|
||||
const colorAlphaInputPrefixCls = `${prefixCls}-alpha-input`;
|
||||
const [alphaValue, setAlphaValue] = useState<AggregationColor>(generateColor(value || '#000'));
|
||||
const [internalValue, setInternalValue] = useState<AggregationColor>(() =>
|
||||
generateColor(value || '#000'),
|
||||
);
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
setAlphaValue(value);
|
||||
}
|
||||
}, [value]);
|
||||
const alphaValue = value || internalValue;
|
||||
|
||||
const handleAlphaChange = (step: number | null) => {
|
||||
const hsba = alphaValue.toHsb();
|
||||
hsba.a = (step || 0) / 100;
|
||||
const genColor = generateColor(hsba);
|
||||
if (!value) {
|
||||
setAlphaValue(genColor);
|
||||
}
|
||||
|
||||
setInternalValue(genColor);
|
||||
|
||||
onChange?.(genColor);
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import Input from '../../input/Input';
|
||||
import type { AggregationColor } from '../color';
|
||||
@ -17,20 +17,14 @@ const isHexString = (hex?: string) => hexReg.test(`#${hex}`);
|
||||
|
||||
const ColorHexInput: FC<ColorHexInputProps> = ({ prefixCls, value, onChange }) => {
|
||||
const colorHexInputPrefixCls = `${prefixCls}-hex-input`;
|
||||
const [hexValue, setHexValue] = useState(() =>
|
||||
value ? toHexFormat(value.toHexString()) : undefined,
|
||||
);
|
||||
const [internalValue, setInternalValue] = useState('');
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
setHexValue(toHexFormat(value.toHexString()));
|
||||
}
|
||||
}, [value]);
|
||||
const hexValue = value ? toHexFormat(value.toHexString()) : internalValue;
|
||||
|
||||
const handleHexChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const originValue = e.target.value;
|
||||
setHexValue(toHexFormat(originValue));
|
||||
setInternalValue(toHexFormat(originValue));
|
||||
|
||||
if (isHexString(toHexFormat(originValue, true))) {
|
||||
onChange?.(generateColor(originValue));
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import type { HSB } from '@rc-component/color-picker';
|
||||
|
||||
import type { AggregationColor } from '../color';
|
||||
@ -14,22 +14,19 @@ interface ColorHsbInputProps {
|
||||
|
||||
const ColorHsbInput: FC<ColorHsbInputProps> = ({ prefixCls, value, onChange }) => {
|
||||
const colorHsbInputPrefixCls = `${prefixCls}-hsb-input`;
|
||||
const [hsbValue, setHsbValue] = useState<AggregationColor>(generateColor(value || '#000'));
|
||||
const [internalValue, setInternalValue] = useState<AggregationColor>(() =>
|
||||
generateColor(value || '#000'),
|
||||
);
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
setHsbValue(value);
|
||||
}
|
||||
}, [value]);
|
||||
const hsbValue = value || internalValue;
|
||||
|
||||
const handleHsbChange = (step: number, type: keyof HSB) => {
|
||||
const hsb = hsbValue.toHsb();
|
||||
hsb[type] = type === 'h' ? step : (step || 0) / 100;
|
||||
const genColor = generateColor(hsb);
|
||||
if (!value) {
|
||||
setHsbValue(genColor);
|
||||
}
|
||||
|
||||
setInternalValue(genColor);
|
||||
|
||||
onChange?.(genColor);
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import type { RGB } from '@rc-component/color-picker';
|
||||
|
||||
import type { AggregationColor } from '../color';
|
||||
@ -14,22 +14,19 @@ interface ColorRgbInputProps {
|
||||
|
||||
const ColorRgbInput: FC<ColorRgbInputProps> = ({ prefixCls, value, onChange }) => {
|
||||
const colorRgbInputPrefixCls = `${prefixCls}-rgb-input`;
|
||||
const [rgbValue, setRgbValue] = useState<AggregationColor>(generateColor(value || '#000'));
|
||||
const [internalValue, setInternalValue] = useState<AggregationColor>(() =>
|
||||
generateColor(value || '#000'),
|
||||
);
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
setRgbValue(value);
|
||||
}
|
||||
}, [value]);
|
||||
const rgbValue = value || internalValue;
|
||||
|
||||
const handleRgbChange = (step: number | null, type: keyof RGB) => {
|
||||
const rgb = rgbValue.toRgb();
|
||||
rgb[type] = step || 0;
|
||||
const genColor = generateColor(rgb);
|
||||
if (!value) {
|
||||
setRgbValue(genColor);
|
||||
}
|
||||
|
||||
setInternalValue(genColor);
|
||||
|
||||
onChange?.(genColor);
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import type { InputNumberProps } from '../../input-number';
|
||||
@ -26,14 +26,9 @@ const ColorSteppers: FC<ColorSteppersProps> = ({
|
||||
formatter,
|
||||
}) => {
|
||||
const colorSteppersPrefixCls = `${prefixCls}-steppers`;
|
||||
const [stepValue, setStepValue] = useState(value);
|
||||
const [internalValue, setInternalValue] = useState<number | undefined>(0);
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
if (!Number.isNaN(value)) {
|
||||
setStepValue(value);
|
||||
}
|
||||
}, [value]);
|
||||
const stepValue = !Number.isNaN(value) ? value : internalValue;
|
||||
|
||||
return (
|
||||
<InputNumber
|
||||
@ -44,9 +39,7 @@ const ColorSteppers: FC<ColorSteppersProps> = ({
|
||||
formatter={formatter}
|
||||
size="small"
|
||||
onChange={(step) => {
|
||||
if (!value) {
|
||||
setStepValue(step || 0);
|
||||
}
|
||||
setInternalValue(step || 0);
|
||||
onChange?.(step);
|
||||
}}
|
||||
/>
|
||||
|
@ -50,7 +50,8 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| disabled | Disable ColorPicker | boolean | - | |
|
||||
| disabledAlpha | Disable Alpha | boolean | - | 5.8.0 |
|
||||
| disabledFormat | Disable format of color | boolean | - | |
|
||||
| destroyTooltipOnHide | Whether destroy popover when hidden | `boolean` | false | 5.7.0 |
|
||||
| ~~destroyTooltipOnHide~~ | Whether destroy dom when close | `boolean` | false | 5.7.0 |
|
||||
| destroyOnHidden | Whether destroy dom when close | `boolean` | false | 5.25.0 |
|
||||
| format | Format of color | `rgb` \| `hex` \| `hsb` | - | |
|
||||
| mode | Configure single or gradient color | `'single' \| 'gradient' \| ('single' \| 'gradient')[]` | `single` | 5.20.0 |
|
||||
| open | Whether to show popup | boolean | - | |
|
||||
|
@ -51,7 +51,8 @@ group:
|
||||
| disabled | 禁用颜色选择器 | boolean | - | |
|
||||
| disabledAlpha | 禁用透明度 | boolean | - | 5.8.0 |
|
||||
| disabledFormat | 禁用选择颜色格式 | boolean | - | |
|
||||
| destroyTooltipOnHide | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
|
||||
| ~~destroyTooltipOnHide~~ | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
|
||||
| destroyOnHidden | 关闭后是否销毁弹窗 | `boolean` | false | 5.25.0 |
|
||||
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | - | |
|
||||
| mode | 选择器模式,用于配置单色与渐变 | `'single' \| 'gradient' \| ('single' \| 'gradient')[]` | `single` | 5.20.0 |
|
||||
| open | 是否显示弹出窗口 | boolean | - | |
|
||||
|
@ -94,4 +94,7 @@ export type ColorPickerProps = Omit<
|
||||
onClear?: () => void;
|
||||
onChangeComplete?: (value: AggregationColor) => void;
|
||||
disabledFormat?: boolean;
|
||||
} & Pick<PopoverProps, 'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide'>;
|
||||
} & Pick<
|
||||
PopoverProps,
|
||||
'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide' | 'destroyOnHidden'
|
||||
>;
|
||||
|
@ -37,10 +37,9 @@ let unstableRender: RenderType = defaultReactRender;
|
||||
* This is internal usage only compatible with React 19.
|
||||
* And will be removed in next major version.
|
||||
*/
|
||||
export function unstableSetRender(render: RenderType) {
|
||||
unstableRender = render;
|
||||
}
|
||||
|
||||
export function getReactRender() {
|
||||
export function unstableSetRender(render?: RenderType) {
|
||||
if (render) {
|
||||
unstableRender = render;
|
||||
}
|
||||
return unstableRender;
|
||||
}
|
||||
|
@ -14405,14 +14405,14 @@ exports[`ConfigProvider components Divider configProvider componentSize large 1`
|
||||
|
||||
exports[`ConfigProvider components Divider configProvider componentSize middle 1`] = `
|
||||
<div
|
||||
class="config-divider config-divider-horizontal"
|
||||
class="config-divider config-divider-horizontal config-divider-md"
|
||||
role="separator"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`ConfigProvider components Divider configProvider componentSize small 1`] = `
|
||||
<div
|
||||
class="config-divider config-divider-horizontal"
|
||||
class="config-divider config-divider-horizontal config-divider-sm"
|
||||
role="separator"
|
||||
/>
|
||||
`;
|
||||
@ -18568,6 +18568,7 @@ exports[`ConfigProvider components Modal configProvider 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="Close"
|
||||
class="config-modal-close-x"
|
||||
>
|
||||
<span
|
||||
@ -18660,6 +18661,7 @@ exports[`ConfigProvider components Modal configProvider componentDisabled 1`] =
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="Close"
|
||||
class="config-modal-close-x"
|
||||
>
|
||||
<span
|
||||
@ -18752,6 +18754,7 @@ exports[`ConfigProvider components Modal configProvider componentSize large 1`]
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="Close"
|
||||
class="config-modal-close-x"
|
||||
>
|
||||
<span
|
||||
@ -18844,6 +18847,7 @@ exports[`ConfigProvider components Modal configProvider componentSize middle 1`]
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="Close"
|
||||
class="config-modal-close-x"
|
||||
>
|
||||
<span
|
||||
@ -18936,6 +18940,7 @@ exports[`ConfigProvider components Modal configProvider componentSize small 1`]
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="Close"
|
||||
class="config-modal-close-x"
|
||||
>
|
||||
<span
|
||||
@ -19028,6 +19033,7 @@ exports[`ConfigProvider components Modal normal 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="Close"
|
||||
class="ant-modal-close-x"
|
||||
>
|
||||
<span
|
||||
@ -19120,6 +19126,7 @@ exports[`ConfigProvider components Modal prefixCls 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="Close"
|
||||
class="prefix-Modal-close-x"
|
||||
>
|
||||
<span
|
||||
|
@ -161,7 +161,7 @@ export type TextAreaConfig = ComponentStyleConfig &
|
||||
Pick<TextAreaProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear' | 'variant'>;
|
||||
|
||||
export type ButtonConfig = ComponentStyleConfig &
|
||||
Pick<ButtonProps, 'classNames' | 'styles' | 'autoInsertSpace'>;
|
||||
Pick<ButtonProps, 'classNames' | 'styles' | 'autoInsertSpace' | 'variant' | 'color'>;
|
||||
|
||||
export type NotificationConfig = ComponentStyleConfig & Pick<ArgsProps, 'closeIcon'>;
|
||||
|
||||
@ -184,7 +184,8 @@ export type FloatButtonGroupConfig = Pick<FloatButtonGroupProps, 'closeIcon'>;
|
||||
|
||||
export type PaginationConfig = ComponentStyleConfig & Pick<PaginationProps, 'showSizeChanger'>;
|
||||
|
||||
export type SelectConfig = ComponentStyleConfig & Pick<SelectProps, 'showSearch' | 'variant'>;
|
||||
export type SelectConfig = ComponentStyleConfig &
|
||||
Pick<SelectProps, 'showSearch' | 'variant' | 'classNames' | 'styles'>;
|
||||
|
||||
export type SpaceConfig = ComponentStyleConfig & Pick<SpaceProps, 'size' | 'classNames' | 'styles'>;
|
||||
|
||||
@ -203,9 +204,11 @@ export type SpinConfig = ComponentStyleConfig & Pick<SpinProps, 'indicator'>;
|
||||
|
||||
export type InputNumberConfig = ComponentStyleConfig & Pick<InputNumberProps, 'variant'>;
|
||||
|
||||
export type CascaderConfig = ComponentStyleConfig & Pick<CascaderProps, 'variant'>;
|
||||
export type CascaderConfig = ComponentStyleConfig &
|
||||
Pick<CascaderProps, 'variant' | 'styles' | 'classNames'>;
|
||||
|
||||
export type TreeSelectConfig = ComponentStyleConfig & Pick<TreeSelectProps, 'variant'>;
|
||||
export type TreeSelectConfig = ComponentStyleConfig &
|
||||
Pick<TreeSelectProps, 'variant' | 'styles' | 'classNames'>;
|
||||
|
||||
export type DatePickerConfig = ComponentStyleConfig & Pick<DatePickerProps, 'variant'>;
|
||||
|
||||
|
@ -4,4 +4,4 @@
|
||||
|
||||
## en-US
|
||||
|
||||
Use `holderRender` to set the `Provider` for the static methods `message` 、`modal` 、`notification`.
|
||||
Use `holderRender` to set the `Provider` for the static methods `message`,`modal`,`notification`.
|
||||
|
@ -71,7 +71,7 @@ Some components use dynamic style to support wave effect. You can config `csp` p
|
||||
|
||||
### ConfigProvider.config()
|
||||
|
||||
Setting `Modal`、`Message`、`Notification` static config. Not work on hooks.
|
||||
Setting `Modal`, `Message`, `Notification` static config. Not work on hooks.
|
||||
|
||||
```tsx
|
||||
ConfigProvider.config({
|
||||
@ -113,11 +113,11 @@ const {
|
||||
| avatar | Set Avatar common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| badge | Set Badge common props | { className?: string, style?: React.CSSProperties, classNames?: [BadgeProps\["classNames"\]](/components/badge#api), styles?: [BadgeProps\["styles"\]](/components/badge#api) } | - | 5.7.0 |
|
||||
| breadcrumb | Set Breadcrumb common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| button | Set Button common props | { className?: string, style?: React.CSSProperties, classNames?: [ButtonProps\["classNames"\]](/components/button#api), styles?: [ButtonProps\["styles"\]](/components/button#api), autoInsertSpace?: boolean } | - | 5.6.0, `autoInsertSpace`: 5.17.0 |
|
||||
| button | Set Button common props | { className?: string, style?: React.CSSProperties, classNames?: [ButtonProps\["classNames"\]](/components/button#api), styles?: [ButtonProps\["styles"\]](/components/button#api), autoInsertSpace?: boolean, variant?: ButtonVariantType, color?: ButtonColorType } | - | 5.6.0, `autoInsertSpace`: 5.17.0, `variant` and `color`: 5.25.0 |
|
||||
| card | Set Card common props | { className?: string, style?: React.CSSProperties, classNames?: [CardProps\["classNames"\]](/components/card#api), styles?: [CardProps\["styles"\]](/components/card#api) } | - | 5.7.0, `classNames` and `styles`: 5.14.0 |
|
||||
| calendar | Set Calendar common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| carousel | Set Carousel common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| cascader | Set Cascader common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| cascader | Set Cascader common props | { className?: string, style?: React.CSSProperties, classNames?: [CascaderProps\["classNames"\]](/components/cascader#semantic-dom), styles?: [CascaderProps\["styles"\]](/components/cascader#semantic-dom) } | - | 5.7.0, `classNames` and `styles`: 5.25.0 |
|
||||
| checkbox | Set Checkbox common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| collapse | Set Collapse common props | { className?: string, style?: React.CSSProperties, expandIcon?: (props) => ReactNode } | - | 5.7.0, `expandIcon`: 5.15.0 |
|
||||
| colorPicker | Set ColorPicker common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
@ -148,7 +148,7 @@ const {
|
||||
| result | Set Result common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| skeleton | Set Skeleton common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| segmented | Set Segmented common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| select | Set Select common props | { className?: string, showSearch?: boolean, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| select | Set Select common props | { className?: string, showSearch?: boolean, style?: React.CSSProperties, classNames?: [SelectProps\["classNames"\]](/components/select#api), styles?: [SelectProps\["styles"\]](/components/select#api) } | - | 5.7.0, `classNames` and `styles`: 5.25.0 |
|
||||
| slider | Set Slider common props | { className?: string, style?: React.CSSProperties, classNames?: [SliderProps\["classNames"\]](/components/slider#api), styles?: [SliderProps\["styles"\]](/components/slider#api) } | - | 5.7.0, `classNames` and `styles`: 5.23.0 |
|
||||
| switch | Set Switch common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| space | Set Space common props, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: [SpaceProps\["classNames"\]](/components/space#api), styles?: [SpaceProps\["styles"\]](/components/space#api) } | - | 5.6.0 |
|
||||
@ -167,6 +167,7 @@ const {
|
||||
| popconfirm | Set Popconfirm common props | { className?: string, style?: React.CSSProperties, classNames?:[Popconfirm\["classNames"\]](/components/popconfirm#api), styles?: [Popconfirm\["styles"\]](/components/popconfirm#api) } | - | 5.23.0 |
|
||||
| transfer | Set Transfer common props | { className?: string, style?: React.CSSProperties, selectionsIcon?: React.ReactNode } | - | 5.7.0, `selectionsIcon`: 5.14.0 |
|
||||
| tree | Set Tree common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| treeSelect | Set TreeSelect common props | { classNames?:[TreeSelect\["classNames"\]](/components/tree-select#api), styles?: [TreeSelect\["styles"\]](/components/tree-select#api) } | - | 5.25.0 |
|
||||
| typography | Set Typography common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| upload | Set Upload common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| wave | Config wave effect | { disabled?: boolean, showEffect?: (node: HTMLElement, info: { className, token, component }) => void } | - | 5.8.0 |
|
||||
|
@ -115,11 +115,11 @@ const {
|
||||
| avatar | 设置 Avatar 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| badge | 设置 Badge 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [BadgeProps\["classNames"\]](/components/badge-cn#api), styles?: [BadgeProps\["styles"\]](/components/badge-cn#api) } | - | 5.7.0 |
|
||||
| breadcrumb | 设置 Breadcrumb 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| button | 设置 Button 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [ButtonProps\["classNames"\]](/components/button-cn#api), styles?: [ButtonProps\["styles"\]](/components/button-cn#api), autoInsertSpace?: boolean } | - | 5.6.0, `autoInsertSpace`: 5.17.0 |
|
||||
| button | 设置 Button 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [ButtonProps\["classNames"\]](/components/button-cn#api), styles?: [ButtonProps\["styles"\]](/components/button-cn#api), autoInsertSpace?: boolean, variant?: ButtonVariantType, color?: ButtonColorType } | - | 5.6.0, `autoInsertSpace`: 5.17.0, `variant` 和 `color`: 5.25.0 |
|
||||
| calendar | 设置 Calendar 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| card | 设置 Card 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CardProps\["classNames"\]](/components/card-cn#api), styles?: [CardProps\["styles"\]](/components/card-cn#api) } | - | 5.7.0, `classNames` 和 `styles`: 5.14.0 |
|
||||
| carousel | 设置 Carousel 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| cascader | 设置 Cascader 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| cascader | 设置 Cascader 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CascaderProps\["classNames"\]](/components/cascader-cn#semantic-dom), styles?: [CascaderProps\["styles"\]](/components/cascader-cn#semantic-dom) } | - | 5.7.0, `classNames` 和 `styles`: 5.25.0 |
|
||||
| checkbox | 设置 Checkbox 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| collapse | 设置 Collapse 组件的通用属性 | { className?: string, style?: React.CSSProperties, expandIcon?: (props) => ReactNode } | - | 5.7.0, `expandIcon`: 5.15.0 |
|
||||
| colorPicker | 设置 ColorPicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
@ -150,7 +150,7 @@ const {
|
||||
| result | 设置 Result 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| skeleton | 设置 Skeleton 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| segmented | 设置 Segmented 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| select | 设置 Select 组件的通用属性 | { className?: string, showSearch?: boolean, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| select | 设置 Select 组件的通用属性 | { className?: string, showSearch?: boolean, style?: React.CSSProperties, classNames?: [SelectProps\["classNames"\]](/components/select-cn#api), styles?: [SelectProps\["styles"\]](/components/select-cn#api) } | - | 5.7.0, `classNames` 和 `styles`: 5.25.0 |
|
||||
| slider | 设置 Slider 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [SliderProps\["classNames"\]](/components/slider-cn#api), styles?: [SliderProps\["styles"\]](/components/slider-cn#api) } | - | 5.7.0, `classNames` 和 `styles`: 5.23.0 |
|
||||
| switch | 设置 Switch 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| space | 设置 Space 的通用属性,参考 [Space](/components/space-cn) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: [SpaceProps\["classNames"\]](/components/space-cn#api), styles?: [SpaceProps\["styles"\]](/components/space-cn#api) } | - | 5.6.0 |
|
||||
@ -169,6 +169,7 @@ const {
|
||||
| popconfirm | 设置 Popconfirm 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[Popconfirm\["classNames"\]](/components/popconfirm-cn#api), styles?: [Popconfirm\["styles"\]](/components/popconfirm-cn#api) } | - | 5.23.0 |
|
||||
| transfer | 设置 Transfer 组件的通用属性 | { className?: string, style?: React.CSSProperties, selectionsIcon?: React.ReactNode } | - | 5.7.0, `selectionsIcon`: 5.14.0 |
|
||||
| tree | 设置 Tree 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| treeSelect | 设置 TreeSelect 组件的通用属性 | { classNames?:[TreeSelect\["classNames"\]](/components/tree-select-cn#api), styles?: [TreeSelect\["styles"\]](/components/tree-select-cn#api) } | - | 5.25.0 |
|
||||
| typography | 设置 Typography 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| upload | 设置 Upload 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| wave | 设置水波纹特效 | { disabled?: boolean, showEffect?: (node: HTMLElement, info: { className, token, component }) => void } | - | 5.8.0 |
|
||||
|
@ -20466,6 +20466,782 @@ exports[`renders components/date-picker/demo/disabled-date.tsx extend context co
|
||||
|
||||
exports[`renders components/date-picker/demo/disabled-date.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/date-picker/demo/external-panel.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small ant-dropdown-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span>
|
||||
2016-11-22
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up ant-dropdown-show-arrow ant-dropdown-placement-bottomLeft"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
>
|
||||
<div
|
||||
class="ant-dropdown-arrow"
|
||||
/>
|
||||
<ul
|
||||
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
|
||||
data-menu-list="true"
|
||||
role="menu"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
aria-describedby="test-id"
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||
data-menu-id="rc-menu-uuid-test-today"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Today
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-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"
|
||||
id="test-id"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<li
|
||||
aria-describedby="test-id"
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||
data-menu-id="rc-menu-uuid-test-tomorrow"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
Tomorrow
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-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"
|
||||
id="test-id"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<li
|
||||
aria-describedby="test-id"
|
||||
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
|
||||
data-menu-id="rc-menu-uuid-test-custom-date"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-dropdown-menu-title-content"
|
||||
>
|
||||
<div
|
||||
style="position: relative;"
|
||||
>
|
||||
<div>
|
||||
Customize
|
||||
</div>
|
||||
<div
|
||||
style="height: 0px; width: 0px; overflow: hidden; position: absolute; top: 0px; inset-inline-start: 0;"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-outlined"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
autocomplete="off"
|
||||
placeholder="Select date"
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
class="ant-picker-suffix"
|
||||
>
|
||||
<span
|
||||
aria-label="calendar"
|
||||
class="anticon anticon-calendar"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="calendar"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up ant-picker-dropdown-placement-bottomLeft"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box; z-index: 1150;"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-panel-container ant-picker-date-panel-container"
|
||||
style="margin-left: 0px; margin-right: auto;"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-panel-layout"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-picker-panel"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-date-panel"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-header"
|
||||
>
|
||||
<button
|
||||
aria-label="Last year (Control + left)"
|
||||
class="ant-picker-header-super-prev-btn"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-picker-super-prev-icon"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Previous month (PageUp)"
|
||||
class="ant-picker-header-prev-btn"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-picker-prev-icon"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
class="ant-picker-header-view"
|
||||
>
|
||||
<button
|
||||
aria-label="Choose a month"
|
||||
class="ant-picker-month-btn"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
Nov
|
||||
</button>
|
||||
<button
|
||||
aria-label="Choose a year"
|
||||
class="ant-picker-year-btn"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
2016
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Next month (PageDown)"
|
||||
class="ant-picker-header-next-btn"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-picker-next-icon"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Next year (Control + right)"
|
||||
class="ant-picker-header-super-next-btn"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-picker-super-next-icon"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-body"
|
||||
>
|
||||
<table
|
||||
class="ant-picker-content"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Su
|
||||
</th>
|
||||
<th>
|
||||
Mo
|
||||
</th>
|
||||
<th>
|
||||
Tu
|
||||
</th>
|
||||
<th>
|
||||
We
|
||||
</th>
|
||||
<th>
|
||||
Th
|
||||
</th>
|
||||
<th>
|
||||
Fr
|
||||
</th>
|
||||
<th>
|
||||
Sa
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-10-30"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
30
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-10-31"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
31
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-01"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-02"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-03"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-04"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
4
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-05"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
5
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-06"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
6
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-07"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
7
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-08"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
8
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-09"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
9
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-10"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
10
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-11"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
11
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-12"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
12
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-13"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
13
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-14"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
14
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-15"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
15
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-16"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
16
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-17"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
17
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-18"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
18
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-19"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
19
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-20"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
20
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-21"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
21
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view ant-picker-cell-today"
|
||||
title="2016-11-22"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
22
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-23"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
23
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-24"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
24
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-25"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
25
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-26"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
26
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-27"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
27
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-28"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
28
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-29"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
29
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2016-11-30"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
30
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-01"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-02"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-03"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-04"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
4
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-05"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
5
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-06"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
6
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-07"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
7
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-08"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
8
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-09"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
9
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2016-12-10"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
10
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-footer"
|
||||
>
|
||||
<ul
|
||||
class="ant-picker-ranges"
|
||||
>
|
||||
<li
|
||||
class="ant-picker-now"
|
||||
>
|
||||
<a
|
||||
aria-disabled="false"
|
||||
class="ant-picker-now-btn"
|
||||
>
|
||||
Today
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-dropdown-menu-inline-collapsed-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"
|
||||
id="test-id"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style="display: none;"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/date-picker/demo/external-panel.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/date-picker/demo/extra-footer.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
|
@ -1485,6 +1485,43 @@ exports[`renders components/date-picker/demo/disabled-date.tsx correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/date-picker/demo/external-panel.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small ant-dropdown-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span>
|
||||
2016-11-22
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/date-picker/demo/extra-footer.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
|
7
components/date-picker/demo/external-panel.md
Normal file
7
components/date-picker/demo/external-panel.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
自定义菜单,外置选择面板。
|
||||
|
||||
## en-US
|
||||
|
||||
Custom menu, external selection panel.
|
91
components/date-picker/demo/external-panel.tsx
Normal file
91
components/date-picker/demo/external-panel.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
import React from 'react';
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import { DatePicker, Dropdown, Space } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const App = () => {
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
const [panelVisible, setPanelVisible] = React.useState(false);
|
||||
|
||||
const [date, setDate] = React.useState(dayjs());
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
arrow
|
||||
open={visible}
|
||||
trigger={['click']}
|
||||
destroyOnHidden
|
||||
onOpenChange={(open) => {
|
||||
setVisible(open);
|
||||
|
||||
if (!open) {
|
||||
setPanelVisible(false);
|
||||
}
|
||||
}}
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: 'today',
|
||||
label: 'Today',
|
||||
onClick() {
|
||||
setDate(dayjs());
|
||||
setVisible(false);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'tomorrow',
|
||||
label: 'Tomorrow',
|
||||
onClick() {
|
||||
setDate(dayjs().add(1, 'day'));
|
||||
setVisible(false);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'custom-date',
|
||||
label: (
|
||||
<div
|
||||
style={{ position: 'relative' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setPanelVisible(true);
|
||||
}}
|
||||
>
|
||||
<div>Customize</div>
|
||||
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
style={{
|
||||
height: 0,
|
||||
width: 0,
|
||||
overflow: 'hidden',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
open={panelVisible}
|
||||
onChange={(date) => {
|
||||
setDate(date);
|
||||
setVisible(false);
|
||||
setPanelVisible(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<span>{date.format('YYYY-MM-DD')}</span>
|
||||
<DownOutlined />
|
||||
</Space>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -35,6 +35,7 @@ By clicking the input box, you can select a date from a popup calendar.
|
||||
<code src="./demo/size.tsx">Three Sizes</code>
|
||||
<code src="./demo/cell-render.tsx">Customized Cell Rendering</code>
|
||||
<code src="./demo/components.tsx" version="5.14.0">Customize Panel</code>
|
||||
<code src="./demo/external-panel.tsx">External use panel</code>
|
||||
<code src="./demo/buddhist-era.tsx" version="5.14.0">Buddhist Era</code>
|
||||
<code src="./demo/status.tsx">Status</code>
|
||||
<code src="./demo/variant.tsx" version="5.13.0">Variants</code>
|
||||
|
@ -36,6 +36,7 @@ demo:
|
||||
<code src="./demo/size.tsx">三种大小</code>
|
||||
<code src="./demo/cell-render.tsx">定制单元格</code>
|
||||
<code src="./demo/components.tsx" version="5.14.0">定制面板</code>
|
||||
<code src="./demo/external-panel.tsx">外部使用面板</code>
|
||||
<code src="./demo/buddhist-era.tsx" version="5.14.0">佛历格式</code>
|
||||
<code src="./demo/status.tsx">自定义状态</code>
|
||||
<code src="./demo/variant.tsx" version="5.13.0">形态变体</code>
|
||||
|
@ -203,6 +203,37 @@ Array [
|
||||
|
||||
exports[`renders components/divider/demo/plain.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/divider/demo/size.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-sm"
|
||||
role="separator"
|
||||
/>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-md"
|
||||
role="separator"
|
||||
/>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/divider/demo/size.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/divider/demo/variant.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<p>
|
||||
|
@ -195,6 +195,35 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/divider/demo/size.tsx correctly 1`] = `
|
||||
Array [
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-sm"
|
||||
role="separator"
|
||||
/>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-md"
|
||||
role="separator"
|
||||
/>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/divider/demo/variant.tsx correctly 1`] = `
|
||||
Array [
|
||||
<p>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { ConfigProvider } from 'antd';
|
||||
|
||||
import Divider from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
@ -40,4 +41,28 @@ describe('Divider', () => {
|
||||
borderStyle: 'dotted',
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply the componentSize of ConfigProvider', () => {
|
||||
const { container, rerender } = render(
|
||||
<ConfigProvider componentSize="middle">
|
||||
<Divider />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector<HTMLSpanElement>('.ant-divider-md')).toBeTruthy();
|
||||
|
||||
rerender(
|
||||
<ConfigProvider componentSize="small">
|
||||
<Divider />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
expect(container.querySelector<HTMLSpanElement>('.ant-divider-sm')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('support vertical size', () => {
|
||||
const { container, rerender } = render(<Divider type="vertical" size="middle" />);
|
||||
expect(container.querySelector<HTMLSpanElement>('.ant-divider-md')).toBeTruthy();
|
||||
|
||||
rerender(<Divider type="vertical" size="small" />);
|
||||
expect(container.querySelector<HTMLSpanElement>('.ant-divider-sm')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
7
components/divider/demo/size.md
Normal file
7
components/divider/demo/size.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
间距的大小。
|
||||
|
||||
## en-US
|
||||
|
||||
The size of the spacing.
|
28
components/divider/demo/size.tsx
Normal file
28
components/divider/demo/size.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Divider } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
|
||||
probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>
|
||||
<Divider size="small" />
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
|
||||
probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>
|
||||
<Divider size="middle" />
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
|
||||
probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>
|
||||
<Divider size="large" />
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
|
||||
probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
|
||||
export default App;
|
@ -21,6 +21,7 @@ group:
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/horizontal.tsx">Horizontal</code>
|
||||
<code src="./demo/with-text.tsx">Divider with title</code>
|
||||
<code src="./demo/size.tsx" version="5.25.0">Set the spacing size of the divider</code>
|
||||
<code src="./demo/plain.tsx">Text without heading style</code>
|
||||
<code src="./demo/vertical.tsx">Vertical</code>
|
||||
<code src="./demo/customize-style.tsx" debug>Style Customization</code>
|
||||
@ -41,6 +42,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| orientationMargin | The margin-left/right between the title and its closest border, while the `orientation` should not be `center`, If a numeric value of type `string` is provided without a unit, it is assumed to be in pixels (px) by default. | string \| number | - | |
|
||||
| plain | Divider text show as plain style | boolean | true | 4.2.0 |
|
||||
| style | The style object of container | CSSProperties | - | |
|
||||
| size | The size of divider. Only valid for horizontal layout | `small` \| `middle` \| `large` | - | 5.25.0 |
|
||||
| type | The direction type of divider | `horizontal` \| `vertical` | `horizontal` | |
|
||||
|
||||
## Design Token
|
||||
|
@ -3,6 +3,8 @@ import classNames from 'classnames';
|
||||
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import useSize from '../config-provider/hooks/useSize';
|
||||
import { SizeType } from '../config-provider/SizeContext';
|
||||
import useStyle from './style';
|
||||
|
||||
export interface DividerProps {
|
||||
@ -28,9 +30,12 @@ export interface DividerProps {
|
||||
*/
|
||||
variant?: 'dashed' | 'dotted' | 'solid';
|
||||
style?: React.CSSProperties;
|
||||
size?: SizeType;
|
||||
plain?: boolean;
|
||||
}
|
||||
|
||||
const sizeClassNameMap: Record<string, string> = { small: 'sm', middle: 'md' };
|
||||
|
||||
const Divider: React.FC<DividerProps> = (props) => {
|
||||
const {
|
||||
getPrefixCls,
|
||||
@ -51,12 +56,16 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
variant = 'solid',
|
||||
plain,
|
||||
style,
|
||||
size: customSize,
|
||||
...restProps
|
||||
} = props;
|
||||
const prefixCls = getPrefixCls('divider', customizePrefixCls);
|
||||
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
|
||||
|
||||
const sizeFullName = useSize(customSize);
|
||||
const sizeCls = sizeClassNameMap[sizeFullName];
|
||||
|
||||
const hasChildren = !!children;
|
||||
|
||||
const mergedOrientation = React.useMemo<'start' | 'end' | 'center'>(() => {
|
||||
@ -88,6 +97,7 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-no-default-orientation-margin-start`]: hasMarginStart,
|
||||
[`${prefixCls}-no-default-orientation-margin-end`]: hasMarginEnd,
|
||||
[`${prefixCls}-${sizeCls}`]: !!sizeCls,
|
||||
},
|
||||
className,
|
||||
rootClassName,
|
||||
|
@ -22,6 +22,7 @@ group:
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/horizontal.tsx">水平分割线</code>
|
||||
<code src="./demo/with-text.tsx">带文字的分割线</code>
|
||||
<code src="./demo/size.tsx" version="5.25.0">设置分割线的间距大小</code>
|
||||
<code src="./demo/plain.tsx">分割文字使用正文样式</code>
|
||||
<code src="./demo/vertical.tsx">垂直分割线</code>
|
||||
<code src="./demo/customize-style.tsx" debug>样式自定义</code>
|
||||
@ -42,6 +43,7 @@ group:
|
||||
| orientationMargin | 标题和最近 left/right 边框之间的距离,去除了分割线,同时 `orientation` 不能为 `center`。如果传入 `string` 类型的数字且不带单位,默认单位是 px | string \| number | - | |
|
||||
| plain | 文字是否显示为普通正文样式 | boolean | false | 4.2.0 |
|
||||
| style | 分割线样式对象 | CSSProperties | - | |
|
||||
| size | 间距大小,仅对水平布局有效 | `small` \| `middle` \| `large` | - | 5.25.0 |
|
||||
| type | 水平还是垂直类型 | `horizontal` \| `vertical` | `horizontal` | |
|
||||
|
||||
## 主题变量(Design Token)
|
||||
|
@ -40,13 +40,28 @@ interface DividerToken extends FullToken<'Divider'> {
|
||||
* @descEN Horizontal margin of divider with text
|
||||
*/
|
||||
dividerHorizontalWithTextGutterMargin: number | string;
|
||||
/**
|
||||
* @desc 水平分割线的外边距
|
||||
* @descEN Horizontal margin of divider
|
||||
*/
|
||||
dividerHorizontalGutterMargin: number | string;
|
||||
}
|
||||
|
||||
// ============================== Size ================================
|
||||
const genSizeDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject => {
|
||||
const { componentCls } = token;
|
||||
|
||||
return {
|
||||
[componentCls]: {
|
||||
'&-horizontal': {
|
||||
[`&${componentCls}`]: {
|
||||
'&-sm': {
|
||||
marginBlock: token.marginXS,
|
||||
},
|
||||
'&-md': {
|
||||
marginBlock: token.margin,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Shared ==============================
|
||||
const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject => {
|
||||
const {
|
||||
@ -82,7 +97,7 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
|
||||
clear: 'both',
|
||||
width: '100%',
|
||||
minWidth: '100%', // Fix https://github.com/ant-design/ant-design/issues/10914
|
||||
margin: `${unit(token.dividerHorizontalGutterMargin)} 0`,
|
||||
margin: `${unit(token.marginLG)} 0`,
|
||||
},
|
||||
|
||||
[`&-horizontal${componentCls}-with-text`]: {
|
||||
@ -223,10 +238,9 @@ export default genStyleHooks(
|
||||
(token) => {
|
||||
const dividerToken = mergeToken<DividerToken>(token, {
|
||||
dividerHorizontalWithTextGutterMargin: token.margin,
|
||||
dividerHorizontalGutterMargin: token.marginLG,
|
||||
sizePaddingEdgeHorizontal: 0,
|
||||
});
|
||||
return [genSharedDividerStyle(dividerToken)];
|
||||
return [genSharedDividerStyle(dividerToken), genSizeDividerStyle(dividerToken)];
|
||||
},
|
||||
prepareComponentToken,
|
||||
{
|
||||
|
@ -4,8 +4,8 @@ import type { DrawerProps as RCDrawerProps } from 'rc-drawer';
|
||||
|
||||
import useClosable, { pickClosable } from '../_util/hooks/useClosable';
|
||||
import type { ClosableType } from '../_util/hooks/useClosable';
|
||||
import Skeleton from '../skeleton';
|
||||
import { useComponentConfig } from '../config-provider/context';
|
||||
import Skeleton from '../skeleton';
|
||||
|
||||
export interface DrawerClassNames extends NonNullable<RCDrawerProps['classNames']> {
|
||||
header?: string;
|
||||
@ -74,7 +74,7 @@ const DrawerPanel: React.FC<DrawerPanelProps> = (props) => {
|
||||
|
||||
const customCloseIconRender = React.useCallback(
|
||||
(icon: React.ReactNode) => (
|
||||
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
|
||||
<button type="button" onClick={onClose} className={`${prefixCls}-close`}>
|
||||
{icon}
|
||||
</button>
|
||||
),
|
||||
|
@ -105,9 +105,9 @@ describe('Drawer', () => {
|
||||
expect(wrapper.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('destroyOnClose is true', () => {
|
||||
it('destroyOnHidden is true', () => {
|
||||
const { container: wrapper } = render(
|
||||
<Drawer destroyOnClose open={false} getContainer={false}>
|
||||
<Drawer destroyOnHidden open={false} getContainer={false}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
@ -118,7 +118,7 @@ describe('Drawer', () => {
|
||||
|
||||
it('className is test_drawer', () => {
|
||||
const { container: wrapper } = render(
|
||||
<Drawer destroyOnClose open rootClassName="test_drawer" getContainer={false}>
|
||||
<Drawer destroyOnHidden open rootClassName="test_drawer" getContainer={false}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
|
@ -56,11 +56,11 @@ describe('Drawer', () => {
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('dom should be removed after close when destroyOnClose is true', () => {
|
||||
const { container, rerender } = render(<DrawerTest destroyOnClose />);
|
||||
it('dom should be removed after close when destroyOnHidden is true', () => {
|
||||
const { container, rerender } = render(<DrawerTest destroyOnHidden />);
|
||||
expect(container.querySelector('.ant-drawer')).toBeTruthy();
|
||||
|
||||
rerender(<DrawerTest destroyOnClose open={false} />);
|
||||
rerender(<DrawerTest destroyOnHidden open={false} />);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
@ -68,7 +68,7 @@ describe('Drawer', () => {
|
||||
expect(container.querySelector('.ant-drawer')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('dom should be existed after close when destroyOnClose is false', () => {
|
||||
it('dom should be existed after close when destroyOnHidden is false', () => {
|
||||
const { container, rerender } = render(<DrawerTest />);
|
||||
expect(container.querySelector('.ant-drawer')).toBeTruthy();
|
||||
|
||||
|
@ -203,7 +203,7 @@ exports[`Drawer closable is false 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Drawer destroyOnClose is true 1`] = `null`;
|
||||
exports[`Drawer destroyOnHidden is true 1`] = `null`;
|
||||
|
||||
exports[`Drawer getContainer return undefined 1`] = `<div />`;
|
||||
|
||||
|
@ -39,7 +39,7 @@ Array [
|
||||
class="ant-drawer-header-title"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
aria-label="Close Button"
|
||||
class="ant-drawer-close"
|
||||
type="button"
|
||||
>
|
||||
|
@ -17,7 +17,12 @@ const App: React.FC = () => {
|
||||
<Button type="primary" onClick={showDrawer}>
|
||||
Open
|
||||
</Button>
|
||||
<Drawer title="Basic Drawer" onClose={onClose} open={open}>
|
||||
<Drawer
|
||||
title="Basic Drawer"
|
||||
closable={{ 'aria-label': 'Close Button' }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
<p>Some contents...</p>
|
||||
|
@ -22,7 +22,7 @@ const App: React.FC = () => {
|
||||
</Button>
|
||||
<Drawer
|
||||
closable
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
title={<p>Loading Drawer</p>}
|
||||
placement="right"
|
||||
open={open}
|
||||
|
@ -56,7 +56,8 @@ v5 uses `rootClassName` & `rootStyle` to configure the outermost element style,
|
||||
| className | Config Drawer Panel className. Use `rootClassName` if want to config top DOM style | string | - | |
|
||||
| classNames | Semantic structure className | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.10.0 |
|
||||
| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | ReactNode | <CloseOutlined /> | |
|
||||
| destroyOnClose | Whether to unmount child components on closing drawer or not | boolean | false | |
|
||||
| ~~destroyOnClose~~ | Whether to unmount child components on closing drawer or not | boolean | false | |
|
||||
| destroyOnHidden | Whether to unmount child components on closing drawer or not | boolean | false | 5.25.0 |
|
||||
| extra | Extra actions area at corner | ReactNode | - | 4.17.0 |
|
||||
| footer | The footer for Drawer | ReactNode | - | |
|
||||
| forceRender | Pre-render Drawer component forcibly | boolean | false | |
|
||||
|
@ -26,7 +26,7 @@ export interface PushState {
|
||||
|
||||
// Drawer diff props: 'open' | 'motion' | 'maskMotion' | 'wrapperClassName'
|
||||
export interface DrawerProps
|
||||
extends Omit<RcDrawerProps, 'maskStyle'>,
|
||||
extends Omit<RcDrawerProps, 'maskStyle' | 'destroyOnClose'>,
|
||||
Omit<DrawerPanelProps, 'prefixCls'> {
|
||||
size?: sizeType;
|
||||
|
||||
@ -41,6 +41,12 @@ export interface DrawerProps
|
||||
afterVisibleChange?: (open: boolean) => void;
|
||||
classNames?: DrawerClassNames;
|
||||
styles?: DrawerStyles;
|
||||
/** @deprecated Please use `destroyOnHidden` instead */
|
||||
destroyOnClose?: boolean;
|
||||
/**
|
||||
* @since 5.25.0
|
||||
*/
|
||||
destroyOnHidden?: boolean;
|
||||
}
|
||||
|
||||
const defaultPushState: PushState = { distance: 180 };
|
||||
@ -69,7 +75,8 @@ const Drawer: React.FC<DrawerProps> & {
|
||||
maskStyle,
|
||||
drawerStyle,
|
||||
contentWrapperStyle,
|
||||
|
||||
destroyOnClose,
|
||||
destroyOnHidden,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@ -116,6 +123,7 @@ const Drawer: React.FC<DrawerProps> & {
|
||||
['contentWrapperStyle', 'styles.wrapper'],
|
||||
['maskStyle', 'styles.mask'],
|
||||
['drawerStyle', 'styles.content'],
|
||||
['destroyInactivePanel', 'destroyOnHidden'],
|
||||
].forEach(([deprecatedName, newName]) => {
|
||||
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
|
||||
});
|
||||
@ -210,6 +218,8 @@ const Drawer: React.FC<DrawerProps> & {
|
||||
afterOpenChange={afterOpenChange ?? afterVisibleChange}
|
||||
panelRef={panelRef}
|
||||
zIndex={zIndex}
|
||||
// TODO: In the future, destroyOnClose in rc-drawer needs to be upgrade to destroyOnHidden
|
||||
destroyOnClose={destroyOnHidden ?? destroyOnClose}
|
||||
>
|
||||
<DrawerPanel prefixCls={prefixCls} {...rest} onClose={onClose} />
|
||||
</RcDrawer>
|
||||
|
@ -56,7 +56,8 @@ v5 使用 `rootClassName` 与 `rootStyle` 来配置最外层元素样式。原 v
|
||||
| className | Drawer 容器外层 className 设置,如果需要设置最外层,请使用 rootClassName | string | - | |
|
||||
| classNames | 语义化结构 className | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.10.0 |
|
||||
| closeIcon | 自定义关闭图标。5.7.0:设置为 `null` 或 `false` 时隐藏关闭按钮 | ReactNode | <CloseOutlined /> | |
|
||||
| destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false | |
|
||||
| ~~destroyOnClose~~ | 关闭时销毁 Drawer 里的子元素 | boolean | false | |
|
||||
| destroyOnHidden | 关闭时销毁 Drawer 里的子元素 | boolean | false | 5.25.0 |
|
||||
| extra | 抽屉右上角的操作区域 | ReactNode | - | 4.17.0 |
|
||||
| footer | 抽屉的页脚 | ReactNode | - | |
|
||||
| forceRender | 预渲染 Drawer 内元素 | boolean | false | |
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
@ -170,4 +171,18 @@ describe('DropdownButton', () => {
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-dropdown-menu-item-active')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('legacy destroyPopupOnHide with Dropdown.Button', () => {
|
||||
resetWarned();
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(
|
||||
<DropdownButton destroyPopupOnHide menu={{ items: [] }}>
|
||||
test
|
||||
</DropdownButton>,
|
||||
);
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Dropdown] `destroyPopupOnHide` is deprecated. Please use `destroyOnHidden` instead.',
|
||||
);
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user