mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 06:03:38 +08:00
chore: merge feature into master
This commit is contained in:
commit
34a63e6892
@ -1,5 +1,6 @@
|
||||
import { CSSMotionProps, MotionEventHandler, MotionEndEventHandler } from 'rc-motion';
|
||||
import { MotionEvent } from 'rc-motion/lib/interface';
|
||||
import { tuple } from './type';
|
||||
|
||||
// ================== Collapse Motion ==================
|
||||
const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 });
|
||||
@ -25,11 +26,21 @@ const collapseMotion: CSSMotionProps = {
|
||||
motionDeadline: 500,
|
||||
};
|
||||
|
||||
const SelectPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
|
||||
export type SelectCommonPlacement = typeof SelectPlacements[number];
|
||||
|
||||
const getTransitionDirection = (placement: SelectCommonPlacement | undefined) => {
|
||||
if (placement !== undefined && (placement === 'topLeft' || placement === 'topRight')) {
|
||||
return `slide-down`;
|
||||
}
|
||||
return `slide-up`;
|
||||
};
|
||||
|
||||
const getTransitionName = (rootPrefixCls: string, motion: string, transitionName?: string) => {
|
||||
if (transitionName !== undefined) {
|
||||
return transitionName;
|
||||
}
|
||||
return `${rootPrefixCls}-${motion}`;
|
||||
};
|
||||
export { getTransitionName };
|
||||
export { getTransitionName, getTransitionDirection };
|
||||
export default collapseMotion;
|
||||
|
@ -42,6 +42,7 @@ export default function getPlacements(config: PlacementsConfig) {
|
||||
horizontalArrowShift = 16,
|
||||
verticalArrowShift = 8,
|
||||
autoAdjustOverflow,
|
||||
arrowPointAtCenter,
|
||||
} = config;
|
||||
const placementMap: BuildInPlacements = {
|
||||
left: {
|
||||
@ -94,7 +95,7 @@ export default function getPlacements(config: PlacementsConfig) {
|
||||
},
|
||||
};
|
||||
Object.keys(placementMap).forEach(key => {
|
||||
placementMap[key] = config.arrowPointAtCenter
|
||||
placementMap[key] = arrowPointAtCenter
|
||||
? {
|
||||
...placementMap[key],
|
||||
overflow: getOverflowOptions(autoAdjustOverflow),
|
44
components/_util/statusUtils.tsx
Normal file
44
components/_util/statusUtils.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
||||
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import classNames from 'classnames';
|
||||
import { ValidateStatus } from '../form/FormItem';
|
||||
import { tuple } from './type';
|
||||
|
||||
const InputStatuses = tuple('warning', 'error', '');
|
||||
export type InputStatus = typeof InputStatuses[number];
|
||||
|
||||
const iconMap = {
|
||||
success: CheckCircleFilled,
|
||||
warning: ExclamationCircleFilled,
|
||||
error: CloseCircleFilled,
|
||||
validating: LoadingOutlined,
|
||||
};
|
||||
|
||||
export const getFeedbackIcon = (prefixCls: string, status?: ValidateStatus) => {
|
||||
const IconNode = status && iconMap[status];
|
||||
return IconNode ? (
|
||||
<span className={`${prefixCls}-feedback-icon`}>
|
||||
<IconNode />
|
||||
</span>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export function getStatusClassNames(
|
||||
prefixCls: string,
|
||||
status?: ValidateStatus,
|
||||
hasFeedback?: boolean,
|
||||
) {
|
||||
return classNames({
|
||||
[`${prefixCls}-status-success`]: status === 'success',
|
||||
[`${prefixCls}-status-warning`]: status === 'warning',
|
||||
[`${prefixCls}-status-error`]: status === 'error',
|
||||
[`${prefixCls}-status-validating`]: status === 'validating',
|
||||
[`${prefixCls}-has-feedback`]: hasFeedback,
|
||||
});
|
||||
}
|
||||
|
||||
export const getMergedStatus = (contextStatus?: ValidateStatus, customStatus?: InputStatus) =>
|
||||
customStatus || contextStatus;
|
@ -1876,6 +1876,107 @@ exports[`renders ./components/auto-complete/demo/options.md extend context corre
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/status.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-status-error ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="undefined_list_0"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-select-dropdown-empty"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-item-empty"
|
||||
id="undefined_list"
|
||||
role="listbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-status-warning ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="undefined_list_0"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-select-dropdown-empty"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-item-empty"
|
||||
id="undefined_list"
|
||||
role="listbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/uncertain-category.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
|
@ -1033,6 +1033,79 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-status-error ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="undefined_list_0"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-select-status-warning ant-select-auto-complete ant-select-single ant-select-show-search"
|
||||
style="width:200px"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="undefined_list_0"
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
role="combobox"
|
||||
type="search"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
|
||||
|
42
components/auto-complete/demo/status.md
Normal file
42
components/auto-complete/demo/status.md
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
order: 19
|
||||
version: 4.19.0
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 AutoComplete 添加状态,可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to AutoComplete with `status`, which could be `error` or `warning`.
|
||||
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
import { AutoComplete, Space } from 'antd';
|
||||
|
||||
const mockVal = (str: string, repeat: number = 1) => ({
|
||||
value: str.repeat(repeat),
|
||||
});
|
||||
const ValidateInputs: React.FC = () => {
|
||||
const [options, setOptions] = useState<{ value: string }[]>([]);
|
||||
|
||||
const onSearch = (searchText: string) => {
|
||||
setOptions(
|
||||
!searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)],
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<AutoComplete options={options} onSearch={onSearch} status="error" style={{ width: 200 }} />
|
||||
<AutoComplete options={options} onSearch={onSearch} status="warning" style={{ width: 200 }} />
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<ValidateInputs />, mountNode);
|
||||
```
|
@ -38,6 +38,7 @@ The differences with Select are:
|
||||
| open | Controlled open state of dropdown | boolean | - | |
|
||||
| options | Select options. Will get better perf than jsx definition | { label, value }\[] | - | |
|
||||
| placeholder | The placeholder of input | string | - | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| value | Selected option | string | - | |
|
||||
| onBlur | Called when leaving the component | function() | - | |
|
||||
| onChange | Called when select an option or input value change, or value of input is changed | function(value) | - | |
|
||||
@ -48,10 +49,10 @@ The differences with Select are:
|
||||
|
||||
## Methods
|
||||
|
||||
| Name | Description | Version |
|
||||
| --- | --- | --- |
|
||||
| blur() | Remove focus | |
|
||||
| focus() | Get focus | |
|
||||
| Name | Description | Version |
|
||||
| ------- | ------------ | ------- |
|
||||
| blur() | Remove focus | |
|
||||
| focus() | Get focus | |
|
||||
|
||||
## FAQ
|
||||
|
||||
|
@ -20,6 +20,7 @@ import Select, {
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import { isValidElement } from '../_util/reactNode';
|
||||
import { InputStatus } from '../_util/statusUtils';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@ -37,6 +38,7 @@ export interface AutoCompleteProps<
|
||||
'inputIcon' | 'loading' | 'mode' | 'optionLabelProp' | 'labelInValue'
|
||||
> {
|
||||
dataSource?: DataSourceItemType[];
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
function isSelectOptionOrSelectOptGroup(child: any): Boolean {
|
||||
|
@ -40,6 +40,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/qtJm4yt45/AutoComplete.svg
|
||||
| open | 是否展开下拉菜单 | boolean | - | |
|
||||
| options | 数据化配置选项内容,相比 jsx 定义会获得更好的渲染性能 | { label, value }\[] | - | |
|
||||
| placeholder | 输入框提示 | string | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| value | 指定当前选中的条目 | string | - | |
|
||||
| onBlur | 失去焦点时的回调 | function() | - | |
|
||||
| onChange | 选中 option,或 input 的 value 变化时,调用此函数 | function(value) | - | |
|
||||
@ -50,10 +51,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/qtJm4yt45/AutoComplete.svg
|
||||
|
||||
## 方法
|
||||
|
||||
| 名称 | 描述 | 版本 |
|
||||
| --- | --- | --- |
|
||||
| blur() | 移除焦点 | |
|
||||
| focus() | 获取焦点 | |
|
||||
| 名称 | 描述 | 版本 |
|
||||
| ------- | -------- | ---- |
|
||||
| blur() | 移除焦点 | |
|
||||
| focus() | 获取焦点 | |
|
||||
|
||||
## FAQ
|
||||
|
||||
|
@ -30,7 +30,7 @@ const BreadcrumbItem: BreadcrumbItemInterface = ({
|
||||
const renderBreadcrumbNode = (breadcrumbItem: React.ReactNode) => {
|
||||
if (overlay) {
|
||||
return (
|
||||
<DropDown overlay={overlay} placement="bottomCenter" {...dropdownProps}>
|
||||
<DropDown overlay={overlay} placement="bottom" {...dropdownProps}>
|
||||
<span className={`${prefixCls}-overlay-link`}>
|
||||
{breadcrumbItem}
|
||||
<DownOutlined />
|
||||
@ -62,9 +62,7 @@ const BreadcrumbItem: BreadcrumbItemInterface = ({
|
||||
return (
|
||||
<span>
|
||||
{link}
|
||||
{separator && (
|
||||
<span className={`${prefixCls}-separator`}>{separator}</span>
|
||||
)}
|
||||
{separator && <span className={`${prefixCls}-separator`}>{separator}</span>}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -1674,6 +1674,241 @@ exports[`renders ./components/cascader/demo/multiple.md extend context correctly
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/placement.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button ant-radio-button-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Please select
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-cascader-dropdown"
|
||||
style="opacity:0;pointer-events:none;min-width:auto"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-cascader-menus"
|
||||
>
|
||||
<ul
|
||||
class="ant-cascader-menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-expand"
|
||||
data-path-key="zhejiang"
|
||||
role="menuitemcheckbox"
|
||||
title="Zhejiang"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
Zhejiang
|
||||
</div>
|
||||
<div
|
||||
class="ant-cascader-menu-item-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-expand"
|
||||
data-path-key="jiangsu"
|
||||
role="menuitemcheckbox"
|
||||
title="Jiangsu"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
Jiangsu
|
||||
</div>
|
||||
<div
|
||||
class="ant-cascader-menu-item-expand-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
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>
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/search.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search"
|
||||
@ -2267,6 +2502,276 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/status.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-status-error ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Error
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-cascader-menus ant-cascader-menu-empty"
|
||||
>
|
||||
<ul
|
||||
class="ant-cascader-menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-disabled"
|
||||
data-path-key="__EMPTY__"
|
||||
role="menuitemcheckbox"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
class="ant-empty-img-simple"
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
class="ant-empty-img-simple-ellipse"
|
||||
cx="32"
|
||||
cy="33"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
class="ant-empty-img-simple-g"
|
||||
fill-rule="nonzero"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
class="ant-empty-img-simple-path"
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No Data
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-status-warning ant-select-multiple ant-select-allow-clear"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-overflow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix"
|
||||
style="opacity:1"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-search"
|
||||
style="width:0"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-selection-search-mirror"
|
||||
>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Warning multiple
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-cascader-menus ant-cascader-menu-empty"
|
||||
>
|
||||
<ul
|
||||
class="ant-cascader-menu"
|
||||
role="menu"
|
||||
>
|
||||
<li
|
||||
aria-checked="false"
|
||||
class="ant-cascader-menu-item ant-cascader-menu-item-disabled"
|
||||
data-path-key="__EMPTY__"
|
||||
role="menuitemcheckbox"
|
||||
>
|
||||
<div
|
||||
class="ant-cascader-menu-item-content"
|
||||
>
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
class="ant-empty-img-simple"
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
class="ant-empty-img-simple-ellipse"
|
||||
cx="32"
|
||||
cy="33"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
class="ant-empty-img-simple-g"
|
||||
fill-rule="nonzero"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
class="ant-empty-img-simple-path"
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No Data
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/suffix.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
|
@ -658,6 +658,151 @@ exports[`renders ./components/cascader/demo/multiple.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/placement.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button ant-radio-button-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Please select
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
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>
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/search.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-single ant-select-allow-clear ant-select-show-arrow ant-select-show-search"
|
||||
@ -891,6 +1036,126 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-status-error ant-select-single ant-select-allow-clear ant-select-show-arrow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Error
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-select ant-cascader ant-select-status-warning ant-select-multiple ant-select-allow-clear"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-overflow"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix"
|
||||
style="opacity:1"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selection-search"
|
||||
style="width:0"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="undefined_list"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="undefined_list"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-selection-search-mirror"
|
||||
>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
>
|
||||
Warning multiple
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/suffix.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
|
@ -368,6 +368,23 @@ describe('Cascader', () => {
|
||||
expect(wrapper.find('.ant-select-selection-placeholder').text()).toEqual(customPlaceholder);
|
||||
});
|
||||
|
||||
it('placement work correctly', () => {
|
||||
const customerOptions = [
|
||||
{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [
|
||||
{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const wrapper = mount(<Cascader options={customerOptions} placement="topRight" />);
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('topRight');
|
||||
});
|
||||
|
||||
it('popup correctly with defaultValue RTL', () => {
|
||||
const wrapper = mount(
|
||||
<ConfigProvider direction="rtl">
|
||||
@ -484,10 +501,12 @@ describe('Cascader', () => {
|
||||
describe('legacy props', () => {
|
||||
it('popupClassName', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const wrapper = mount(<Cascader open popupPlacement="topRight" popupClassName="mock-cls" />);
|
||||
const wrapper = mount(
|
||||
<Cascader open popupPlacement="bottomLeft" popupClassName="mock-cls" />,
|
||||
);
|
||||
|
||||
expect(wrapper.exists('.mock-cls')).toBeTruthy();
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('topRight');
|
||||
expect(wrapper.find('Trigger').prop('popupPlacement')).toEqual('bottomLeft');
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Cascader] `popupClassName` is deprecated. Please use `dropdownClassName` instead.',
|
||||
|
77
components/cascader/demo/placement.md
Normal file
77
components/cascader/demo/placement.md
Normal file
@ -0,0 +1,77 @@
|
||||
---
|
||||
order: 13
|
||||
title:
|
||||
zh-CN: 弹出位置
|
||||
en-US: Placement
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以通过 `placement` 手动指定弹出的位置。
|
||||
|
||||
## en-US
|
||||
|
||||
You can manually specify the position of the popup via `placement`.
|
||||
|
||||
```jsx
|
||||
import { Cascader, Radio } from 'antd';
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [
|
||||
{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [
|
||||
{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [
|
||||
{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [
|
||||
{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const SetPlacementDemo = () => {
|
||||
const [placement, SetPlacement] = React.useState('topLeft');
|
||||
|
||||
const placementChange = e => {
|
||||
SetPlacement(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Radio.Group value={placement} onChange={placementChange}>
|
||||
<Radio.Button value="topLeft">topLeft</Radio.Button>
|
||||
<Radio.Button value="topRight">topRight</Radio.Button>
|
||||
<Radio.Button value="bottomLeft">bottomLeft</Radio.Button>
|
||||
<Radio.Button value="bottomRight">bottomRight</Radio.Button>
|
||||
</Radio.Group>
|
||||
<br />
|
||||
<br />
|
||||
<Cascader options={options} placeholder="Please select" placement={placement} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<SetPlacementDemo />, mountNode);
|
||||
```
|
28
components/cascader/demo/status.md
Normal file
28
components/cascader/demo/status.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
order: 16
|
||||
version: 4.19.0
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 Cascader 添加状态,可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to Cascader with `status`, which could be `error` or `warning`.
|
||||
|
||||
```tsx
|
||||
import { Cascader, Space } from 'antd';
|
||||
|
||||
const Validation: React.FC = () => (
|
||||
<Space direction="vertical">
|
||||
<Cascader status="error" placeholder="Error" />
|
||||
<Cascader status="warning" multiple placeholder="Warning multiple" />
|
||||
</Space>
|
||||
);
|
||||
|
||||
ReactDOM.render(<Validation />, mountNode);
|
||||
```
|
@ -42,9 +42,10 @@ Cascade selection box.
|
||||
| open | Set visible of cascader popup | boolean | - | 4.17.0 |
|
||||
| options | The data options of cascade | [Option](#Option)\[] | - | |
|
||||
| placeholder | The input placeholder | string | `Please select` | |
|
||||
| placement | Use preset popup align config from builtinPlacements:`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | 4.17.0 |
|
||||
| placement | Use preset popup align config from builtinPlacements | `bottomLeft` `bottomRight` `topLeft` `topRight` | `bottomLeft` | 4.17.0 |
|
||||
| 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 | - | |
|
||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||
| tagRender | Customize tag render when `multiple` | (props) => ReactNode | - | 4.17.0 |
|
||||
|
@ -13,12 +13,15 @@ import omit from 'rc-util/lib/omit';
|
||||
import RightOutlined from '@ant-design/icons/RightOutlined';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import { useContext } from 'react';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import SizeContext from '../config-provider/SizeContext';
|
||||
import getIcons from '../select/utils/iconUtil';
|
||||
import { getTransitionName } from '../_util/motion';
|
||||
import { getTransitionName, getTransitionDirection, SelectCommonPlacement } from '../_util/motion';
|
||||
import { FormItemStatusContext } from '../form/context';
|
||||
import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils';
|
||||
|
||||
// Align the design since we use `rc-select` in root. This help:
|
||||
// - List search content will show all content
|
||||
@ -94,10 +97,11 @@ export type CascaderProps<DataNodeType> = UnionCascaderProps & {
|
||||
multiple?: boolean;
|
||||
size?: SizeType;
|
||||
bordered?: boolean;
|
||||
|
||||
placement?: SelectCommonPlacement;
|
||||
suffixIcon?: React.ReactNode;
|
||||
options?: DataNodeType[];
|
||||
}
|
||||
status?: InputStatus;
|
||||
};
|
||||
|
||||
export interface CascaderRef {
|
||||
focus: () => void;
|
||||
@ -116,11 +120,14 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
popupClassName,
|
||||
dropdownClassName,
|
||||
expandIcon,
|
||||
placement,
|
||||
showSearch,
|
||||
allowClear = true,
|
||||
notFoundContent,
|
||||
direction,
|
||||
getPopupContainer,
|
||||
status: customStatus,
|
||||
showArrow,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@ -133,11 +140,15 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
direction: rootDirection,
|
||||
// virtual,
|
||||
// dropdownMatchSelectWidth,
|
||||
} = React.useContext(ConfigContext);
|
||||
} = useContext(ConfigContext);
|
||||
|
||||
const mergedDirection = direction || rootDirection;
|
||||
const isRtl = mergedDirection === 'rtl';
|
||||
|
||||
// =================== Status =====================
|
||||
const { status: contextStatus, hasFeedback } = useContext(FormItemStatusContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
// =================== Warning =====================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
devWarning(
|
||||
@ -213,12 +224,26 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
);
|
||||
|
||||
// ===================== Icons =====================
|
||||
const mergedShowArrow = showArrow !== undefined ? showArrow : props.loading || !multiple;
|
||||
const { suffixIcon, removeIcon, clearIcon } = getIcons({
|
||||
...props,
|
||||
status: mergedStatus,
|
||||
hasFeedback,
|
||||
showArrow: mergedShowArrow,
|
||||
multiple,
|
||||
prefixCls,
|
||||
});
|
||||
|
||||
// ===================== Placement =====================
|
||||
const getPlacement = () => {
|
||||
if (placement !== undefined) {
|
||||
return placement;
|
||||
}
|
||||
return direction === 'rtl'
|
||||
? ('bottomRight' as SelectCommonPlacement)
|
||||
: ('bottomLeft' as SelectCommonPlacement);
|
||||
};
|
||||
|
||||
// ==================== Render =====================
|
||||
return (
|
||||
<RcCascader
|
||||
@ -231,10 +256,12 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
[`${prefixCls}-rtl`]: isRtl,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(prefixCls, mergedStatus, hasFeedback),
|
||||
className,
|
||||
)}
|
||||
{...(restProps as any)}
|
||||
direction={mergedDirection}
|
||||
placement={getPlacement()}
|
||||
notFoundContent={mergedNotFoundContent}
|
||||
allowClear={allowClear}
|
||||
showSearch={mergedShowSearch}
|
||||
@ -247,9 +274,14 @@ const Cascader = React.forwardRef((props: CascaderProps<any>, ref: React.Ref<Cas
|
||||
dropdownClassName={mergedDropdownClassName}
|
||||
dropdownPrefixCls={customizePrefixCls || cascaderPrefixCls}
|
||||
choiceTransitionName={getTransitionName(rootPrefixCls, '', choiceTransitionName)}
|
||||
transitionName={getTransitionName(rootPrefixCls, 'slide-up', transitionName)}
|
||||
transitionName={getTransitionName(
|
||||
rootPrefixCls,
|
||||
getTransitionDirection(placement),
|
||||
transitionName,
|
||||
)}
|
||||
getPopupContainer={getPopupContainer || getContextPopupContainer}
|
||||
ref={ref}
|
||||
showArrow={hasFeedback || showArrow}
|
||||
/>
|
||||
);
|
||||
}) as (<OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType>(
|
||||
|
@ -43,9 +43,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
|
||||
| open | 控制浮层显隐 | boolean | - | 4.17.0 |
|
||||
| options | 可选项数据源 | [Option](#Option)\[] | - | |
|
||||
| placeholder | 输入框占位文本 | string | `请选择` | |
|
||||
| placement | 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | 4.17.0 |
|
||||
| placement | 浮层预设位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | `bottomLeft` | 4.17.0 |
|
||||
| showSearch | 在选择框中显示搜索框 | boolean \| [Object](#showSearch) | false | |
|
||||
| size | 输入框大小 | `large` \| `middle` \| `small` | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | 自定义样式 | CSSProperties | - | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||
| tagRender | 自定义 tag 内容,多选时生效 | (props) => ReactNode | - | 4.17.0 |
|
||||
|
@ -4,3 +4,5 @@ import './index.less';
|
||||
// style dependencies
|
||||
import '../../empty/style';
|
||||
import '../../select/style';
|
||||
|
||||
// deps-lint-skip: form
|
||||
|
@ -13367,7 +13367,7 @@ exports[`ConfigProvider components Form configProvider 1`] = `
|
||||
class="config-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="config-input"
|
||||
class="config-input config-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13405,7 +13405,7 @@ exports[`ConfigProvider components Form configProvider componentSize large 1`] =
|
||||
class="config-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="config-input config-input-lg"
|
||||
class="config-input config-input-lg config-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13443,7 +13443,7 @@ exports[`ConfigProvider components Form configProvider componentSize middle 1`]
|
||||
class="config-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="config-input"
|
||||
class="config-input config-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13481,7 +13481,7 @@ exports[`ConfigProvider components Form configProvider virtual and dropdownMatch
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
class="ant-input ant-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13519,7 +13519,7 @@ exports[`ConfigProvider components Form normal 1`] = `
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
class="ant-input ant-input-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -13557,7 +13557,7 @@ exports[`ConfigProvider components Form prefixCls 1`] = `
|
||||
class="prefix-Form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="prefix-Form"
|
||||
class="prefix-Form prefix-Form-status-error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
|
@ -199,4 +199,36 @@ describe('DatePicker', () => {
|
||||
expect(year).toBe(startDate.format('YYYY'));
|
||||
expect(wrapper.find('.ant-picker-time-panel').length).toBe(1);
|
||||
});
|
||||
|
||||
it('placement api work correctly ', () => {
|
||||
const popupAlignDefault = (points = ['tl', 'bl'], offset = [0, 4]) => ({
|
||||
points,
|
||||
offset,
|
||||
overflow: {
|
||||
adjustX: 1,
|
||||
adjustY: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(
|
||||
<DatePicker.RangePicker defaultValue={moment()} placement="bottomLeft" />,
|
||||
);
|
||||
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(popupAlignDefault(['tl', 'bl']));
|
||||
wrapper.setProps({
|
||||
placement: 'bottomRight',
|
||||
});
|
||||
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(popupAlignDefault(['tr', 'br']));
|
||||
wrapper.setProps({
|
||||
placement: 'topLeft',
|
||||
});
|
||||
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(
|
||||
popupAlignDefault(['bl', 'tl'], [0, -4]),
|
||||
);
|
||||
wrapper.setProps({
|
||||
placement: 'topRight',
|
||||
});
|
||||
expect(wrapper.find('Trigger').prop('popupAlign')).toEqual(
|
||||
popupAlignDefault(['br', 'tr'], [0, -4]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -5,6 +5,7 @@ import DatePicker from '..';
|
||||
import { setMockDate, resetMockDate } from '../../../tests/utils';
|
||||
import { openPicker, selectCell, closePicker } from './utils';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import enUS from '../locale/en_US';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
@ -96,4 +97,10 @@ describe('RangePicker', () => {
|
||||
expect(wrapper.find('input').first().props().placeholder).toEqual('Start date');
|
||||
expect(wrapper.find('input').last().props().placeholder).toEqual('End date');
|
||||
});
|
||||
|
||||
it('RangePicker picker quarter placeholder', () => {
|
||||
const wrapper = mount(<RangePicker picker="quarter" locale={enUS} />);
|
||||
expect(wrapper.find('input').at(0).props().placeholder).toEqual('Start quarter');
|
||||
expect(wrapper.find('input').at(1).props().placeholder).toEqual('End quarter');
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2349,6 +2349,216 @@ exports[`renders ./components/date-picker/demo/mode.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/date-picker/demo/placement.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button ant-radio-button-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="topRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomLeft"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="bottomRight"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-picker"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Select date"
|
||||
readonly=""
|
||||
size="12"
|
||||
title=""
|
||||
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>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input ant-picker-input-active"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Start date"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-range-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="to"
|
||||
class="ant-picker-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="swap-right"
|
||||
class="anticon anticon-swap-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="swap-right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="End date"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-active-bar"
|
||||
style="left:0;width:0;position:absolute"
|
||||
/>
|
||||
<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>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/date-picker/demo/presetted-ranges.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
@ -2875,6 +3085,92 @@ exports[`renders ./components/date-picker/demo/range-picker.md correctly 1`] = `
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:12px"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-range"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input ant-picker-input-active"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Start quarter"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-range-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="to"
|
||||
class="ant-picker-separator"
|
||||
>
|
||||
<span
|
||||
aria-label="swap-right"
|
||||
class="anticon anticon-swap-right"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="swap-right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M873.1 596.2l-164-208A32 32 0 00684 376h-64.8c-6.7 0-10.4 7.7-6.3 13l144.3 183H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h695.9c26.8 0 41.7-30.8 25.2-51.8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="End quarter"
|
||||
readonly=""
|
||||
size="12"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-active-bar"
|
||||
style="left:0;width:0;position:absolute"
|
||||
/>
|
||||
<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-space-item"
|
||||
>
|
||||
@ -3432,6 +3728,103 @@ exports[`renders ./components/date-picker/demo/start-end.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/date-picker/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-status-error"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Select date"
|
||||
readonly=""
|
||||
size="12"
|
||||
title=""
|
||||
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>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-picker ant-picker-status-warning"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-input"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Select date"
|
||||
readonly=""
|
||||
size="12"
|
||||
title=""
|
||||
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>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
|
47
components/date-picker/demo/placement.md
Normal file
47
components/date-picker/demo/placement.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
order: 28
|
||||
title:
|
||||
zh-CN: 基本
|
||||
en-US: Basic
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以通过 `placement` 手动指定弹出的位置。
|
||||
|
||||
## en-US
|
||||
|
||||
You can manually specify the position of the popup via `placement`.
|
||||
|
||||
```jsx
|
||||
import { DatePicker, Space, Radio } from 'antd';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const SetPlacementDemo = () => {
|
||||
const [placement, SetPlacement] = React.useState('topLeft');
|
||||
|
||||
const placementChange = e => {
|
||||
SetPlacement(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Radio.Group value={placement} onChange={placementChange}>
|
||||
<Radio.Button value="topLeft">topLeft</Radio.Button>
|
||||
<Radio.Button value="topRight">topRight</Radio.Button>
|
||||
<Radio.Button value="bottomLeft">bottomLeft</Radio.Button>
|
||||
<Radio.Button value="bottomRight">bottomRight</Radio.Button>
|
||||
</Radio.Group>
|
||||
<br />
|
||||
<br />
|
||||
<DatePicker placement={placement} />
|
||||
<br />
|
||||
<br />
|
||||
<RangePicker placement={placement} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<SetPlacementDemo />, mountNode);
|
||||
```
|
@ -24,6 +24,7 @@ ReactDOM.render(
|
||||
<RangePicker showTime />
|
||||
<RangePicker picker="week" />
|
||||
<RangePicker picker="month" />
|
||||
<RangePicker picker="quarter" />
|
||||
<RangePicker picker="year" />
|
||||
</Space>,
|
||||
mountNode,
|
||||
|
28
components/date-picker/demo/status.md
Normal file
28
components/date-picker/demo/status.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
order: 19
|
||||
version: 4.19.0
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 DatePicker 添加状态,可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to DatePicker with `status`, which could be `error` or `warning`.
|
||||
|
||||
```tsx
|
||||
import { DatePicker, Space } from 'antd';
|
||||
|
||||
const Status: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<DatePicker status="error" style={{ width: '100%' }} />
|
||||
<DatePicker status="warning" style={{ width: '100%' }} />
|
||||
</Space>
|
||||
);
|
||||
|
||||
ReactDOM.render(<Status />, mountNode);
|
||||
```
|
@ -10,7 +10,7 @@ import enUS from '../locale/en_US';
|
||||
import { ConfigContext, ConfigConsumerProps } from '../../config-provider';
|
||||
import SizeContext from '../../config-provider/SizeContext';
|
||||
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
|
||||
import { getRangePlaceholder } from '../util';
|
||||
import { getRangePlaceholder, transPlacement2DropdownAlign } from '../util';
|
||||
import { RangePickerProps, PickerLocale, getTimeProps, Components } from '.';
|
||||
import { PickerComponentClass } from './interface';
|
||||
|
||||
@ -43,6 +43,7 @@ export default function generateRangePicker<DateType>(
|
||||
prefixCls: customizePrefixCls,
|
||||
getPopupContainer: customGetPopupContainer,
|
||||
className,
|
||||
placement,
|
||||
size: customizeSize,
|
||||
bordered = true,
|
||||
placeholder,
|
||||
@ -73,6 +74,7 @@ export default function generateRangePicker<DateType>(
|
||||
</span>
|
||||
}
|
||||
ref={this.pickerRef}
|
||||
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
|
||||
placeholder={getRangePlaceholder(picker, locale, placeholder)}
|
||||
suffixIcon={picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
|
@ -7,7 +7,7 @@ import RCPicker from 'rc-picker';
|
||||
import { PickerMode } from 'rc-picker/lib/interface';
|
||||
import { GenerateConfig } from 'rc-picker/lib/generate/index';
|
||||
import enUS from '../locale/en_US';
|
||||
import { getPlaceholder } from '../util';
|
||||
import { getPlaceholder, transPlacement2DropdownAlign } from '../util';
|
||||
import devWarning from '../../_util/devWarning';
|
||||
import { ConfigContext, ConfigConsumerProps } from '../../config-provider';
|
||||
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
|
||||
@ -21,9 +21,18 @@ import {
|
||||
Components,
|
||||
} from '.';
|
||||
import { PickerComponentClass } from './interface';
|
||||
import { FormItemStatusContext } from '../../form/context';
|
||||
import {
|
||||
getFeedbackIcon,
|
||||
getMergedStatus,
|
||||
getStatusClassNames,
|
||||
InputStatus,
|
||||
} from '../../_util/statusUtils';
|
||||
|
||||
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
|
||||
type DatePickerProps = PickerProps<DateType>;
|
||||
type DatePickerProps = PickerProps<DateType> & {
|
||||
status?: InputStatus;
|
||||
};
|
||||
|
||||
function getPicker<InnerPickerProps extends DatePickerProps>(
|
||||
picker?: PickerMode,
|
||||
@ -59,6 +68,23 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
}
|
||||
};
|
||||
|
||||
renderFeedback = (prefixCls: string) => (
|
||||
<FormItemStatusContext.Consumer>
|
||||
{({ hasFeedback, status: contextStatus }) => {
|
||||
const { status: customStatus } = this.props;
|
||||
const status = getMergedStatus(contextStatus, customStatus);
|
||||
return hasFeedback && getFeedbackIcon(prefixCls, status);
|
||||
}}
|
||||
</FormItemStatusContext.Consumer>
|
||||
);
|
||||
|
||||
renderSuffix = (prefixCls: string, mergedPicker?: PickerMode) => (
|
||||
<>
|
||||
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
|
||||
{this.renderFeedback(prefixCls)}
|
||||
</>
|
||||
);
|
||||
|
||||
renderPicker = (contextLocale: PickerLocale) => {
|
||||
const locale = { ...contextLocale, ...this.props.locale };
|
||||
const { getPrefixCls, direction, getPopupContainer } = this.context;
|
||||
@ -68,7 +94,9 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
className,
|
||||
size: customizeSize,
|
||||
bordered = true,
|
||||
placement,
|
||||
placeholder,
|
||||
status: customStatus,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const { format, showTime } = this.props as any;
|
||||
@ -99,36 +127,44 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
|
||||
const mergedSize = customizeSize || size;
|
||||
|
||||
return (
|
||||
<RCPicker<DateType>
|
||||
ref={this.pickerRef}
|
||||
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
|
||||
suffixIcon={
|
||||
mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />
|
||||
}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
|
||||
nextIcon={<span className={`${prefixCls}-next-icon`} />}
|
||||
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
|
||||
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
|
||||
allowClear
|
||||
transitionName={`${rootPrefixCls}-slide-up`}
|
||||
{...additionalProps}
|
||||
{...restProps}
|
||||
{...additionalOverrideProps}
|
||||
locale={locale!.lang}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-${mergedSize}`]: mergedSize,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
className,
|
||||
<FormItemStatusContext.Consumer>
|
||||
{({ hasFeedback, status: contextStatus }) => (
|
||||
<RCPicker<DateType>
|
||||
ref={this.pickerRef}
|
||||
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
|
||||
suffixIcon={this.renderSuffix(prefixCls, mergedPicker)}
|
||||
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
|
||||
clearIcon={<CloseCircleFilled />}
|
||||
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
|
||||
nextIcon={<span className={`${prefixCls}-next-icon`} />}
|
||||
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
|
||||
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
|
||||
allowClear
|
||||
transitionName={`${rootPrefixCls}-slide-up`}
|
||||
{...additionalProps}
|
||||
{...restProps}
|
||||
{...additionalOverrideProps}
|
||||
locale={locale!.lang}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-${mergedSize}`]: mergedSize,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(
|
||||
prefixCls,
|
||||
getMergedStatus(contextStatus, customStatus),
|
||||
hasFeedback,
|
||||
),
|
||||
className,
|
||||
)}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
|
||||
generateConfig={generateConfig}
|
||||
components={Components}
|
||||
direction={direction}
|
||||
/>
|
||||
)}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
|
||||
generateConfig={generateConfig}
|
||||
components={Components}
|
||||
direction={direction}
|
||||
/>
|
||||
</FormItemStatusContext.Consumer>
|
||||
);
|
||||
}}
|
||||
</SizeContext.Consumer>
|
||||
|
@ -17,6 +17,7 @@ import PickerTag from '../PickerTag';
|
||||
import { TimePickerLocale } from '../../time-picker';
|
||||
import generateSinglePicker from './generateSinglePicker';
|
||||
import generateRangePicker from './generateRangePicker';
|
||||
import { tuple } from '../../_util/type';
|
||||
|
||||
export const Components = { button: PickerButton, rangeItem: PickerTag };
|
||||
|
||||
@ -27,13 +28,18 @@ function toArray<T>(list: T | T[]): T[] {
|
||||
return Array.isArray(list) ? list : [list];
|
||||
}
|
||||
|
||||
export function getTimeProps<DateType>(
|
||||
props: { format?: string; picker?: PickerMode } & SharedTimeProps<DateType>,
|
||||
export function getTimeProps<DateType, DisabledTime>(
|
||||
props: { format?: string; picker?: PickerMode } & Omit<
|
||||
SharedTimeProps<DateType>,
|
||||
'disabledTime'
|
||||
> & {
|
||||
disabledTime?: DisabledTime;
|
||||
},
|
||||
) {
|
||||
const { format, picker, showHour, showMinute, showSecond, use12Hours } = props;
|
||||
|
||||
const firstFormat = toArray(format)[0];
|
||||
const showTimeObj: SharedTimeProps<DateType> = { ...props };
|
||||
const showTimeObj = { ...props };
|
||||
|
||||
if (firstFormat && typeof firstFormat === 'string') {
|
||||
if (!firstFormat.includes('s') && showSecond === undefined) {
|
||||
@ -64,16 +70,16 @@ export function getTimeProps<DateType>(
|
||||
showTime: showTimeObj,
|
||||
};
|
||||
}
|
||||
const DataPickerPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
|
||||
type DataPickerPlacement = typeof DataPickerPlacements[number];
|
||||
|
||||
type InjectDefaultProps<Props> = Omit<
|
||||
Props,
|
||||
| 'locale'
|
||||
| 'generateConfig'
|
||||
| 'hideHeader'
|
||||
| 'components'
|
||||
'locale' | 'generateConfig' | 'hideHeader' | 'components'
|
||||
> & {
|
||||
locale?: PickerLocale;
|
||||
size?: SizeType;
|
||||
placement?: DataPickerPlacement;
|
||||
bordered?: boolean;
|
||||
};
|
||||
|
||||
@ -96,6 +102,7 @@ export type AdditionalPickerLocaleLangProps = {
|
||||
monthPlaceholder?: string;
|
||||
weekPlaceholder?: string;
|
||||
rangeYearPlaceholder?: [string, string];
|
||||
rangeQuarterPlaceholder?: [string, string];
|
||||
rangeMonthPlaceholder?: [string, string];
|
||||
rangeWeekPlaceholder?: [string, string];
|
||||
rangePlaceholder?: [string, string];
|
||||
|
@ -69,9 +69,11 @@ The following APIs are shared by DatePicker, RangePicker.
|
||||
| panelRender | Customize panel render | (panelNode) => ReactNode | - | 4.5.0 |
|
||||
| picker | Set picker type | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
|
||||
| placeholder | The placeholder of date input | string \| \[string,string] | - | |
|
||||
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
|
||||
| popupStyle | To customize the style of the popup calendar | CSSProperties | {} | |
|
||||
| prevIcon | The custom prev icon | ReactNode | - | 4.17.0 |
|
||||
| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \| `middle` \| `small` | - | |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | To customize the style of the input box | CSSProperties | {} | |
|
||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||
| superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 |
|
||||
|
@ -70,9 +70,11 @@ import locale from 'antd/lib/locale/zh_CN';
|
||||
| panelRender | 自定义渲染面板 | (panelNode) => ReactNode | - | 4.5.0 |
|
||||
| picker | 设置选择器类型 | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
|
||||
| placeholder | 输入框提示文字 | string \| \[string, string] | - | |
|
||||
| placement | 选择框弹出的位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
|
||||
| popupStyle | 额外的弹出日历样式 | CSSProperties | {} | |
|
||||
| prevIcon | 自定义上一个图标 | ReactNode | - | 4.17.0 |
|
||||
| size | 输入框大小,`large` 高度为 40px,`small` 为 24px,默认是 32px | `large` \| `middle` \| `small` | - | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| style | 自定义输入框样式 | CSSProperties | {} | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||
| superNextIcon | 自定义 `<<` 切换图标 | ReactNode | - | 4.17.0 |
|
||||
|
@ -12,6 +12,7 @@ const locale: PickerLocale = {
|
||||
weekPlaceholder: 'Select week',
|
||||
rangePlaceholder: ['Start date', 'End date'],
|
||||
rangeYearPlaceholder: ['Start year', 'End year'],
|
||||
rangeQuarterPlaceholder: ['Start quarter', 'End quarter'],
|
||||
rangeMonthPlaceholder: ['Start month', 'End month'],
|
||||
rangeWeekPlaceholder: ['Start week', 'End week'],
|
||||
...CalendarLocale,
|
||||
|
@ -12,6 +12,7 @@ const locale: PickerLocale = {
|
||||
weekPlaceholder: 'Select week',
|
||||
rangePlaceholder: ['Start date', 'End date'],
|
||||
rangeYearPlaceholder: ['Start year', 'End year'],
|
||||
rangeQuarterPlaceholder: ['Start quarter', 'End quarter'],
|
||||
rangeMonthPlaceholder: ['Start month', 'End month'],
|
||||
rangeWeekPlaceholder: ['Start week', 'End week'],
|
||||
...CalendarLocale,
|
||||
|
@ -13,6 +13,7 @@ const locale: PickerLocale = {
|
||||
rangePlaceholder: ['开始日期', '结束日期'],
|
||||
rangeYearPlaceholder: ['开始年份', '结束年份'],
|
||||
rangeMonthPlaceholder: ['开始月份', '结束月份'],
|
||||
rangeQuarterPlaceholder: ['开始季度', '结束季度'],
|
||||
rangeWeekPlaceholder: ['开始周', '结束周'],
|
||||
...CalendarLocale,
|
||||
},
|
||||
|
@ -13,6 +13,7 @@ const locale: PickerLocale = {
|
||||
rangePlaceholder: ['開始日期', '結束日期'],
|
||||
rangeYearPlaceholder: ['開始年份', '結束年份'],
|
||||
rangeMonthPlaceholder: ['開始月份', '結束月份'],
|
||||
rangeQuarterPlaceholder: ['開始季度', '結束季度'],
|
||||
rangeWeekPlaceholder: ['開始周', '結束周'],
|
||||
...CalendarLocale,
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../input/style/mixin';
|
||||
@import './status';
|
||||
|
||||
@picker-prefix-cls: ~'@{ant-prefix}-picker';
|
||||
|
||||
@ -13,7 +14,7 @@
|
||||
}
|
||||
|
||||
.@{picker-prefix-cls} {
|
||||
@arrow-size: 10px;
|
||||
@arrow-size: @popover-arrow-width;
|
||||
|
||||
.reset-component();
|
||||
.picker-padding(@input-height-base, @font-size-base, @input-padding-horizontal-base);
|
||||
@ -106,6 +107,8 @@
|
||||
}
|
||||
|
||||
&-suffix {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-self: center;
|
||||
margin-left: (@padding-xs / 2);
|
||||
color: @disabled-color;
|
||||
@ -114,6 +117,10 @@
|
||||
|
||||
> * {
|
||||
vertical-align: top;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,17 +228,17 @@
|
||||
|
||||
&-placement-bottomLeft {
|
||||
.@{picker-prefix-cls}-range-arrow {
|
||||
top: (@arrow-size / 2) - (@arrow-size / 3);
|
||||
top: (@arrow-size / 2) - (@arrow-size / 3) + 0.7px;
|
||||
display: block;
|
||||
transform: rotate(-45deg);
|
||||
transform: rotate(-135deg) translateY(1px);
|
||||
}
|
||||
}
|
||||
|
||||
&-placement-topLeft {
|
||||
.@{picker-prefix-cls}-range-arrow {
|
||||
bottom: (@arrow-size / 2) - (@arrow-size / 3);
|
||||
bottom: (@arrow-size / 2) - (@arrow-size / 3) + 0.7px;
|
||||
display: block;
|
||||
transform: rotate(135deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,19 +318,14 @@
|
||||
width: @arrow-size;
|
||||
height: @arrow-size;
|
||||
margin-left: @input-padding-horizontal-base * 1.5;
|
||||
box-shadow: 2px -2px 6px fade(@black, 6%);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
transparent 40%,
|
||||
@calendar-bg 40%
|
||||
); // Use linear-gradient to prevent arrow from covering text
|
||||
box-shadow: 2px 2px 6px -2px fade(@black, 10%); // use spread radius to hide shadow over popover
|
||||
transition: left @animation-duration-slow ease-out;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: @border-width-base;
|
||||
right: @border-width-base;
|
||||
width: @arrow-size;
|
||||
height: @arrow-size;
|
||||
border: (@arrow-size / 2) solid @border-color-split;
|
||||
border-color: @calendar-bg @calendar-bg transparent transparent;
|
||||
content: '';
|
||||
}
|
||||
.roundedArrow(@arrow-size, 5px, @calendar-bg);
|
||||
}
|
||||
|
||||
&-panel-container {
|
||||
|
@ -3,3 +3,5 @@ import './index.less';
|
||||
// style dependencies
|
||||
import '../../tag/style';
|
||||
import '../../button/style';
|
||||
|
||||
// deps-lint-skip: form
|
||||
|
52
components/date-picker/style/status.less
Normal file
52
components/date-picker/style/status.less
Normal file
@ -0,0 +1,52 @@
|
||||
@import '../../input/style/mixin';
|
||||
|
||||
@picker-prefix-cls: ~'@{ant-prefix}-picker';
|
||||
|
||||
.picker-status-color(
|
||||
@text-color: @input-color;
|
||||
@border-color: @input-border-color;
|
||||
@background-color: @input-bg;
|
||||
@hoverBorderColor: @primary-color-hover;
|
||||
@outlineColor: @primary-color-outline;
|
||||
) {
|
||||
&.@{picker-prefix-cls} {
|
||||
&,
|
||||
&:not([disabled]):hover {
|
||||
background-color: @background-color;
|
||||
border-color: @border-color;
|
||||
}
|
||||
|
||||
&-focused,
|
||||
&:focus {
|
||||
.active(@text-color, @hoverBorderColor, @outlineColor);
|
||||
}
|
||||
}
|
||||
|
||||
.@{picker-prefix-cls}-feedback-icon {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.@{picker-prefix-cls} {
|
||||
&-status-error {
|
||||
.picker-status-color(@error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.picker-status-color(@warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
}
|
||||
|
||||
&-status-validating {
|
||||
.@{picker-prefix-cls}-feedback-icon {
|
||||
display: inline-block;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-status-success {
|
||||
.@{picker-prefix-cls}-feedback-icon {
|
||||
color: @success-color;
|
||||
animation-name: diffZoomIn1 !important;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
import { PickerMode } from 'rc-picker/lib/interface';
|
||||
import { DirectionType } from '../config-provider';
|
||||
import { SelectCommonPlacement } from '../_util/motion';
|
||||
import { PickerLocale } from './generatePicker';
|
||||
|
||||
export function getPlaceholder(
|
||||
@ -40,6 +42,9 @@ export function getRangePlaceholder(
|
||||
if (picker === 'year' && locale.lang.yearPlaceholder) {
|
||||
return locale.lang.rangeYearPlaceholder;
|
||||
}
|
||||
if (picker === 'quarter' && locale.lang.quarterPlaceholder) {
|
||||
return locale.lang.rangeQuarterPlaceholder;
|
||||
}
|
||||
if (picker === 'month' && locale.lang.monthPlaceholder) {
|
||||
return locale.lang.rangeMonthPlaceholder;
|
||||
}
|
||||
@ -51,3 +56,56 @@ export function getRangePlaceholder(
|
||||
}
|
||||
return locale.lang.rangePlaceholder;
|
||||
}
|
||||
|
||||
export function transPlacement2DropdownAlign(
|
||||
direction: DirectionType,
|
||||
placement?: SelectCommonPlacement,
|
||||
) {
|
||||
const overflow = {
|
||||
adjustX: 1,
|
||||
adjustY: 1,
|
||||
};
|
||||
switch (placement) {
|
||||
case 'bottomLeft': {
|
||||
return {
|
||||
points: ['tl', 'bl'],
|
||||
offset: [0, 4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
case 'bottomRight': {
|
||||
return {
|
||||
points: ['tr', 'br'],
|
||||
offset: [0, 4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
case 'topLeft': {
|
||||
return {
|
||||
points: ['bl', 'tl'],
|
||||
offset: [0, -4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
case 'topRight': {
|
||||
return {
|
||||
points: ['br', 'tr'],
|
||||
offset: [0, -4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return direction === 'rtl'
|
||||
? {
|
||||
points: ['tr', 'br'],
|
||||
offset: [0, 4],
|
||||
overflow,
|
||||
}
|
||||
: {
|
||||
points: ['tl', 'bl'],
|
||||
offset: [0, 4],
|
||||
overflow,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ Array [
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomCenter
|
||||
bottom
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
@ -40,7 +40,61 @@ Array [
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topCenter
|
||||
top
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</button>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/dropdown/demo/arrow-center.md correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottom
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</button>,
|
||||
<br />,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-btn-default ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
top
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
@ -658,7 +712,7 @@ exports[`renders ./components/dropdown/demo/placement.md correctly 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomCenter
|
||||
bottom
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@ -706,7 +760,7 @@ exports[`renders ./components/dropdown/demo/placement.md correctly 1`] = `
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topCenter
|
||||
top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -59,4 +59,24 @@ describe('Dropdown', () => {
|
||||
await sleep(500);
|
||||
expect(wrapper.find(Dropdown).find('#customExpandIcon').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn if use topCenter or bottomCenter', () => {
|
||||
const error = jest.spyOn(console, 'error');
|
||||
mount(
|
||||
<div>
|
||||
<Dropdown overlay="123" placement="bottomCenter">
|
||||
<button type="button">bottomCenter</button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay="123" placement="topCenter">
|
||||
<button type="button">topCenter</button>
|
||||
</Dropdown>
|
||||
</div>,
|
||||
);
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("[antd: Dropdown] You are using 'bottomCenter'"),
|
||||
);
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("[antd: Dropdown] You are using 'topCenter'"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
75
components/dropdown/demo/arrow-center.md
Normal file
75
components/dropdown/demo/arrow-center.md
Normal file
@ -0,0 +1,75 @@
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 箭头指向
|
||||
en-US: Arrow pointing at the center
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
设置 `arrow` 为 `{ pointAtCenter: true }` 后,箭头将指向目标元素的中心。
|
||||
|
||||
## en-US
|
||||
|
||||
By specifying `arrow` prop with `{ pointAtCenter: true }`, the arrow will point to the center of the target element.
|
||||
|
||||
```jsx
|
||||
import { Menu, Dropdown, Button } from 'antd';
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
|
||||
1st menu item
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
|
||||
2nd menu item
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
|
||||
3rd menu item
|
||||
</a>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<Dropdown overlay={menu} placement="bottomLeft" arrow={{ pointAtCenter: true }}>
|
||||
<Button>bottomLeft</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="bottom" arrow={{ pointAtCenter: true }}>
|
||||
<Button>bottom</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="bottomRight" arrow={{ pointAtCenter: true }}>
|
||||
<Button>bottomRight</Button>
|
||||
</Dropdown>
|
||||
<br />
|
||||
<Dropdown overlay={menu} placement="topLeft" arrow={{ pointAtCenter: true }}>
|
||||
<Button>topLeft</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="top" arrow={{ pointAtCenter: true }}>
|
||||
<Button>top</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="topRight" arrow={{ pointAtCenter: true }}>
|
||||
<Button>topRight</Button>
|
||||
</Dropdown>
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
```css
|
||||
#components-dropdown-demo-arrow-center .ant-btn {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.ant-row-rtl #components-dropdown-demo-arrow-center .ant-btn {
|
||||
margin-right: 0;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
```
|
@ -41,8 +41,8 @@ ReactDOM.render(
|
||||
<Dropdown overlay={menu} placement="bottomLeft" arrow>
|
||||
<Button>bottomLeft</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="bottomCenter" arrow>
|
||||
<Button>bottomCenter</Button>
|
||||
<Dropdown overlay={menu} placement="bottom" arrow>
|
||||
<Button>bottom</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="bottomRight" arrow>
|
||||
<Button>bottomRight</Button>
|
||||
@ -51,8 +51,8 @@ ReactDOM.render(
|
||||
<Dropdown overlay={menu} placement="topLeft" arrow>
|
||||
<Button>topLeft</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="topCenter" arrow>
|
||||
<Button>topCenter</Button>
|
||||
<Dropdown overlay={menu} placement="top" arrow>
|
||||
<Button>top</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="topRight" arrow>
|
||||
<Button>topRight</Button>
|
||||
|
@ -46,7 +46,7 @@ ReactDOM.render(
|
||||
<Dropdown.Button onClick={handleButtonClick} overlay={menu}>
|
||||
Dropdown
|
||||
</Dropdown.Button>
|
||||
<Dropdown.Button overlay={menu} placement="bottomCenter" icon={<UserOutlined />}>
|
||||
<Dropdown.Button overlay={menu} placement="bottom" icon={<UserOutlined />}>
|
||||
Dropdown
|
||||
</Dropdown.Button>
|
||||
<Dropdown.Button onClick={handleButtonClick} overlay={menu} disabled>
|
||||
|
@ -42,8 +42,8 @@ ReactDOM.render(
|
||||
<Dropdown overlay={menu} placement="bottomLeft">
|
||||
<Button>bottomLeft</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="bottomCenter">
|
||||
<Button>bottomCenter</Button>
|
||||
<Dropdown overlay={menu} placement="bottom">
|
||||
<Button>bottom</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="bottomRight">
|
||||
<Button>bottomRight</Button>
|
||||
@ -53,8 +53,8 @@ ReactDOM.render(
|
||||
<Dropdown overlay={menu} placement="topLeft">
|
||||
<Button>topLeft</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="topCenter">
|
||||
<Button>topCenter</Button>
|
||||
<Dropdown overlay={menu} placement="top">
|
||||
<Button>top</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="topRight">
|
||||
<Button>topRight</Button>
|
||||
|
@ -7,6 +7,7 @@ import { ConfigContext } from '../config-provider';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import { tuple } from '../_util/type';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import getPlacements from '../_util/placements';
|
||||
|
||||
const Placements = tuple(
|
||||
'topLeft',
|
||||
@ -15,6 +16,8 @@ const Placements = tuple(
|
||||
'bottomLeft',
|
||||
'bottomCenter',
|
||||
'bottomRight',
|
||||
'top',
|
||||
'bottom',
|
||||
);
|
||||
|
||||
type Placement = typeof Placements[number];
|
||||
@ -34,8 +37,12 @@ type Align = {
|
||||
useCssTransform?: boolean;
|
||||
};
|
||||
|
||||
export type DropdownArrowOptions = {
|
||||
pointAtCenter?: boolean;
|
||||
};
|
||||
|
||||
export interface DropDownProps {
|
||||
arrow?: boolean;
|
||||
arrow?: boolean | DropdownArrowOptions;
|
||||
trigger?: ('click' | 'hover' | 'contextMenu')[];
|
||||
overlay: React.ReactElement | OverlayFunc;
|
||||
onVisibleChange?: (visible: boolean) => void;
|
||||
@ -129,10 +136,21 @@ const Dropdown: DropdownInterface = props => {
|
||||
|
||||
const getPlacement = () => {
|
||||
const { placement } = props;
|
||||
if (placement !== undefined) {
|
||||
return placement;
|
||||
if (!placement) {
|
||||
return direction === 'rtl' ? ('bottomRight' as Placement) : ('bottomLeft' as Placement);
|
||||
}
|
||||
return direction === 'rtl' ? ('bottomRight' as Placement) : ('bottomLeft' as Placement);
|
||||
|
||||
if (placement.includes('Center')) {
|
||||
const newPlacement = placement.slice(0, placement.indexOf('Center'));
|
||||
devWarning(
|
||||
!placement.includes('Center'),
|
||||
'Dropdown',
|
||||
`You are using '${placement}' placement in Dropdown, which is deprecated. Try to use '${newPlacement}' instead.`,
|
||||
);
|
||||
return newPlacement;
|
||||
}
|
||||
|
||||
return placement;
|
||||
};
|
||||
|
||||
const {
|
||||
@ -169,11 +187,16 @@ const Dropdown: DropdownInterface = props => {
|
||||
alignPoint = true;
|
||||
}
|
||||
|
||||
const builtinPlacements = getPlacements({
|
||||
arrowPointAtCenter: typeof arrow === 'object' && arrow.pointAtCenter,
|
||||
});
|
||||
|
||||
return (
|
||||
<RcDropdown
|
||||
arrow={arrow}
|
||||
alignPoint={alignPoint}
|
||||
{...props}
|
||||
builtinPlacements={builtinPlacements}
|
||||
arrow={!!arrow}
|
||||
overlayClassName={overlayClassNameCustomized}
|
||||
prefixCls={prefixCls}
|
||||
getPopupContainer={getPopupContainer || getContextPopupContainer}
|
||||
|
@ -17,14 +17,14 @@ When there are more than a few options to choose from, you can wrap them in a `D
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| arrow | Whether the dropdown arrow should be visible | boolean | false | |
|
||||
| arrow | Whether the dropdown arrow should be visible | boolean \| { pointAtCenter: boolean } | false | |
|
||||
| disabled | Whether the dropdown menu is disabled | boolean | - | |
|
||||
| destroyPopupOnHide | Whether destroy dropdown when hidden | boolean | false | |
|
||||
| getPopupContainer | To set the container of the dropdown menu. The default is to create a div element in body, but you can reset it to the scrolling area and make a relative reposition. [Example on CodePen](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
|
||||
| overlay | The dropdown menu | [Menu](/components/menu) \| () => Menu | - | |
|
||||
| overlayClassName | The class name of the dropdown root element | string | - | |
|
||||
| overlayStyle | The style of the dropdown root element | CSSProperties | - | |
|
||||
| placement | Placement of popup menu: `bottomLeft`, `bottomCenter`, `bottomRight`, `topLeft`, `topCenter` or `topRight` | string | `bottomLeft` | |
|
||||
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| trigger | The trigger mode which executes the dropdown action. Note that hover can't be used on touchscreens | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| visible | Whether the dropdown menu is currently visible | boolean | - | |
|
||||
| onVisibleChange | Called when the visible state is changed. Not trigger when hidden by click item | (visible: boolean) => void | - | |
|
||||
@ -44,7 +44,7 @@ You should use [Menu](/components/menu/) as `overlay`. The menu items and divide
|
||||
| disabled | Whether the dropdown menu is disabled | boolean | - | |
|
||||
| icon | Icon (appears on the right) | ReactNode | - | |
|
||||
| overlay | The dropdown menu | [Menu](/components/menu) | - | |
|
||||
| placement | Placement of popup menu: `bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | string | `bottomLeft` | |
|
||||
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| size | Size of the button, the same as [Button](/components/button/#API) | string | `default` | |
|
||||
| trigger | The trigger mode which executes the dropdown action | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| type | Type of the button, the same as [Button](/components/button/#API) | string | `default` | |
|
||||
|
@ -21,14 +21,14 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| arrow | 下拉框箭头是否显示 | boolean | false | |
|
||||
| arrow | 下拉框箭头是否显示 | boolean \| { pointAtCenter: boolean } | false | |
|
||||
| disabled | 菜单是否禁用 | boolean | - | |
|
||||
| destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
|
||||
| overlay | 菜单 | [Menu](/components/menu) \| () => Menu | - | |
|
||||
| overlayClassName | 下拉根元素的类名称 | string | - | |
|
||||
| overlayStyle | 下拉根元素的样式 | CSSProperties | - | |
|
||||
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | string | `bottomLeft` | |
|
||||
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| trigger | 触发下拉的行为, 移动端不支持 hover | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| visible | 菜单是否显示 | boolean | - | |
|
||||
| onVisibleChange | 菜单显示状态改变时调用,参数为 `visible`。点击菜单按钮导致的消失不会触发 | (visible: boolean) => void | - | |
|
||||
@ -48,7 +48,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
|
||||
| disabled | 菜单是否禁用 | boolean | - | |
|
||||
| icon | 右侧的 icon | ReactNode | - | |
|
||||
| overlay | 菜单 | [Menu](/components/menu/) | - | |
|
||||
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | string | `bottomLeft` | |
|
||||
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| size | 按钮大小,和 [Button](/components/button/#API) 一致 | string | `default` | |
|
||||
| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| type | 按钮类型,和 [Button](/components/button/#API) 一致 | string | `default` | |
|
||||
|
@ -49,14 +49,14 @@
|
||||
}
|
||||
|
||||
// Offset the popover to account for the dropdown arrow
|
||||
&-show-arrow&-placement-topCenter,
|
||||
&-show-arrow&-placement-topLeft,
|
||||
&-show-arrow&-placement-top,
|
||||
&-show-arrow&-placement-topRight {
|
||||
padding-bottom: @popover-distance;
|
||||
}
|
||||
|
||||
&-show-arrow&-placement-bottomCenter,
|
||||
&-show-arrow&-placement-bottomLeft,
|
||||
&-show-arrow&-placement-bottom,
|
||||
&-show-arrow&-placement-bottomRight {
|
||||
padding-top: @popover-distance;
|
||||
}
|
||||
@ -68,23 +68,25 @@
|
||||
position: absolute;
|
||||
z-index: 1; // lift it up so the menu wouldn't cask shadow on it
|
||||
display: block;
|
||||
width: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
|
||||
height: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
|
||||
background: transparent;
|
||||
border-style: solid;
|
||||
border-width: (sqrt(@popover-arrow-width * @popover-arrow-width * 2) / 2);
|
||||
width: @popover-arrow-width;
|
||||
height: @popover-arrow-width;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
transparent 40%,
|
||||
@popover-bg 40%
|
||||
); // Use linear-gradient to prevent arrow from covering text
|
||||
.roundedArrow(@popover-arrow-width, 5px, @popover-bg);
|
||||
}
|
||||
|
||||
&-placement-top > &-arrow,
|
||||
&-placement-topLeft > &-arrow,
|
||||
&-placement-topRight > &-arrow {
|
||||
bottom: @popover-arrow-width * sqrt((1 / 2)) + 2px;
|
||||
box-shadow: 3px 3px 7px -3px fade(@black, 10%);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
&-placement-topCenter > &-arrow,
|
||||
&-placement-topLeft > &-arrow,
|
||||
&-placement-topRight > &-arrow {
|
||||
bottom: @popover-distance - @popover-arrow-width + 2.2px;
|
||||
border-color: transparent @popover-bg @popover-bg transparent;
|
||||
box-shadow: 3px 3px 7px fade(@black, 7%);
|
||||
}
|
||||
|
||||
&-placement-topCenter > &-arrow {
|
||||
&-placement-top > &-arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
}
|
||||
@ -97,17 +99,17 @@
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
&-placement-bottomCenter > &-arrow,
|
||||
&-placement-bottom > &-arrow,
|
||||
&-placement-bottomLeft > &-arrow,
|
||||
&-placement-bottomRight > &-arrow {
|
||||
top: @popover-distance - @popover-arrow-width + 2px;
|
||||
border-color: @popover-bg transparent transparent @popover-bg;
|
||||
box-shadow: -2px -2px 5px fade(@black, 6%);
|
||||
top: (@popover-arrow-width + 2px) * sqrt((1 / 2));
|
||||
box-shadow: 2px 2px 5px -2px fade(@black, 10%);
|
||||
transform: rotate(-135deg) translateY(-0.5px);
|
||||
}
|
||||
|
||||
&-placement-bottomCenter > &-arrow {
|
||||
&-placement-bottom > &-arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
transform: translateX(-50%) rotate(-135deg) translateY(-0.5px);
|
||||
}
|
||||
|
||||
&-placement-bottomLeft > &-arrow {
|
||||
@ -300,8 +302,8 @@
|
||||
|
||||
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomLeft,
|
||||
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomLeft,
|
||||
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomCenter,
|
||||
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomCenter,
|
||||
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottom,
|
||||
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottom,
|
||||
&.@{ant-prefix}-slide-down-enter.@{ant-prefix}-slide-down-enter-active&-placement-bottomRight,
|
||||
&.@{ant-prefix}-slide-down-appear.@{ant-prefix}-slide-down-appear-active&-placement-bottomRight {
|
||||
animation-name: antSlideUpIn;
|
||||
@ -309,21 +311,21 @@
|
||||
|
||||
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topLeft,
|
||||
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topLeft,
|
||||
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topCenter,
|
||||
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topCenter,
|
||||
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-top,
|
||||
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-top,
|
||||
&.@{ant-prefix}-slide-up-enter.@{ant-prefix}-slide-up-enter-active&-placement-topRight,
|
||||
&.@{ant-prefix}-slide-up-appear.@{ant-prefix}-slide-up-appear-active&-placement-topRight {
|
||||
animation-name: antSlideDownIn;
|
||||
}
|
||||
|
||||
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomLeft,
|
||||
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomCenter,
|
||||
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottom,
|
||||
&.@{ant-prefix}-slide-down-leave.@{ant-prefix}-slide-down-leave-active&-placement-bottomRight {
|
||||
animation-name: antSlideUpOut;
|
||||
}
|
||||
|
||||
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topLeft,
|
||||
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topCenter,
|
||||
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-top,
|
||||
&.@{ant-prefix}-slide-up-leave.@{ant-prefix}-slide-up-leave-active&-placement-topRight {
|
||||
animation-name: antSlideDownOut;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Field, FormInstance, FieldContext, ListContext } from 'rc-field-form';
|
||||
import { FieldProps } from 'rc-field-form/lib/Field';
|
||||
@ -12,7 +12,12 @@ import { tuple } from '../_util/type';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import FormItemLabel, { FormItemLabelProps, LabelTooltipType } from './FormItemLabel';
|
||||
import FormItemInput, { FormItemInputProps } from './FormItemInput';
|
||||
import { FormContext, NoStyleItemContext } from './context';
|
||||
import {
|
||||
FormContext,
|
||||
FormItemStatusContext,
|
||||
NoStyleItemContext,
|
||||
FormItemStatusContextProps,
|
||||
} from './context';
|
||||
import { toArray, getFieldId } from './util';
|
||||
import { cloneElement, isValidElement } from '../_util/reactNode';
|
||||
import useFrameState from './hooks/useFrameState';
|
||||
@ -199,6 +204,28 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
// ===================== Children Ref =====================
|
||||
const getItemRef = useItemRef();
|
||||
|
||||
// ======================== Status ========================
|
||||
let mergedValidateStatus: ValidateStatus = '';
|
||||
if (validateStatus !== undefined) {
|
||||
mergedValidateStatus = validateStatus;
|
||||
} else if (meta?.validating) {
|
||||
mergedValidateStatus = 'validating';
|
||||
} else if (debounceErrors.length) {
|
||||
mergedValidateStatus = 'error';
|
||||
} else if (debounceWarnings.length) {
|
||||
mergedValidateStatus = 'warning';
|
||||
} else if (meta?.touched) {
|
||||
mergedValidateStatus = 'success';
|
||||
}
|
||||
|
||||
const formItemStatusContext = useMemo<FormItemStatusContextProps>(
|
||||
() => ({
|
||||
status: mergedValidateStatus,
|
||||
hasFeedback,
|
||||
}),
|
||||
[mergedValidateStatus, hasFeedback],
|
||||
);
|
||||
|
||||
// ======================== Render ========================
|
||||
function renderLayout(
|
||||
baseChildren: React.ReactNode,
|
||||
@ -208,19 +235,6 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
if (noStyle && !hidden) {
|
||||
return baseChildren;
|
||||
}
|
||||
// ======================== Status ========================
|
||||
let mergedValidateStatus: ValidateStatus = '';
|
||||
if (validateStatus !== undefined) {
|
||||
mergedValidateStatus = validateStatus;
|
||||
} else if (meta?.validating) {
|
||||
mergedValidateStatus = 'validating';
|
||||
} else if (debounceErrors.length) {
|
||||
mergedValidateStatus = 'error';
|
||||
} else if (debounceWarnings.length) {
|
||||
mergedValidateStatus = 'warning';
|
||||
} else if (meta?.touched) {
|
||||
mergedValidateStatus = 'success';
|
||||
}
|
||||
|
||||
const itemClassName = {
|
||||
[`${prefixCls}-item`]: true,
|
||||
@ -281,11 +295,12 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
warnings={debounceWarnings}
|
||||
prefixCls={prefixCls}
|
||||
status={mergedValidateStatus}
|
||||
validateStatus={mergedValidateStatus}
|
||||
help={help}
|
||||
>
|
||||
<NoStyleItemContext.Provider value={onSubItemMetaChange}>
|
||||
{baseChildren}
|
||||
<FormItemStatusContext.Provider value={formItemStatusContext}>
|
||||
{baseChildren}
|
||||
</FormItemStatusContext.Provider>
|
||||
</NoStyleItemContext.Provider>
|
||||
</FormItemInput>
|
||||
</Row>
|
||||
|
@ -1,10 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
|
||||
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
|
||||
import Col, { ColProps } from '../grid/col';
|
||||
import { ValidateStatus } from './FormItem';
|
||||
import { FormContext, FormItemPrefixContext } from './context';
|
||||
@ -15,8 +10,6 @@ interface FormItemInputMiscProps {
|
||||
children: React.ReactNode;
|
||||
errors: React.ReactNode[];
|
||||
warnings: React.ReactNode[];
|
||||
hasFeedback?: boolean;
|
||||
validateStatus?: ValidateStatus;
|
||||
/** @private Internal Usage, do not use in any of your production. */
|
||||
_internalItemRender?: {
|
||||
mark: string;
|
||||
@ -38,13 +31,6 @@ export interface FormItemInputProps {
|
||||
help?: React.ReactNode;
|
||||
}
|
||||
|
||||
const iconMap: { [key: string]: any } = {
|
||||
success: CheckCircleFilled,
|
||||
warning: ExclamationCircleFilled,
|
||||
error: CloseCircleFilled,
|
||||
validating: LoadingOutlined,
|
||||
};
|
||||
|
||||
const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = props => {
|
||||
const {
|
||||
prefixCls,
|
||||
@ -53,9 +39,7 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
|
||||
children,
|
||||
errors,
|
||||
warnings,
|
||||
hasFeedback,
|
||||
_internalItemRender: formItemRender,
|
||||
validateStatus,
|
||||
extra,
|
||||
help,
|
||||
} = props;
|
||||
@ -67,15 +51,6 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
|
||||
|
||||
const className = classNames(`${baseClassName}-control`, mergedWrapperCol.className);
|
||||
|
||||
// Should provides additional icon if `hasFeedback`
|
||||
const IconNode = validateStatus && iconMap[validateStatus];
|
||||
const icon =
|
||||
hasFeedback && IconNode ? (
|
||||
<span className={`${baseClassName}-children-icon`}>
|
||||
<IconNode />
|
||||
</span>
|
||||
) : null;
|
||||
|
||||
// Pass to sub FormItem should not with col info
|
||||
const subFormContext = React.useMemo(() => ({ ...formContext }), [formContext]);
|
||||
delete subFormContext.labelCol;
|
||||
@ -84,7 +59,6 @@ const FormItemInput: React.FC<FormItemInputProps & FormItemInputMiscProps> = pro
|
||||
const inputDom = (
|
||||
<div className={`${baseClassName}-control-input`}>
|
||||
<div className={`${baseClassName}-control-input-content`}>{children}</div>
|
||||
{icon}
|
||||
</div>
|
||||
);
|
||||
const formItemContext = React.useMemo(() => ({ prefixCls, status }), [prefixCls, status]);
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -50,3 +50,10 @@ export interface FormItemPrefixContextProps {
|
||||
export const FormItemPrefixContext = React.createContext<FormItemPrefixContextProps>({
|
||||
prefixCls: '',
|
||||
});
|
||||
|
||||
export interface FormItemStatusContextProps {
|
||||
status?: ValidateStatus;
|
||||
hasFeedback?: boolean;
|
||||
}
|
||||
|
||||
export const FormItemStatusContext = React.createContext<FormItemStatusContextProps>({});
|
||||
|
@ -23,7 +23,17 @@ We provide properties like `validateStatus` `help` `hasFeedback` to customize yo
|
||||
|
||||
```tsx
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
import { Form, Input, DatePicker, TimePicker, Select, Cascader, InputNumber, Mentions } from 'antd';
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
DatePicker,
|
||||
TimePicker,
|
||||
Select,
|
||||
Cascader,
|
||||
InputNumber,
|
||||
Mentions,
|
||||
TreeSelect,
|
||||
} from 'antd';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@ -87,7 +97,7 @@ ReactDOM.render(
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Error" hasFeedback validateStatus="error">
|
||||
<Select allowClear>
|
||||
<Select placeholder="I'm Select" allowClear>
|
||||
<Option value="1">Option 1</Option>
|
||||
<Option value="2">Option 2</Option>
|
||||
<Option value="3">Option 3</Option>
|
||||
@ -97,10 +107,18 @@ ReactDOM.render(
|
||||
<Form.Item
|
||||
label="Validating"
|
||||
hasFeedback
|
||||
validateStatus="validating"
|
||||
help="The information is being validated..."
|
||||
validateStatus="error"
|
||||
help="Something breaks the rule."
|
||||
>
|
||||
<Cascader options={[{ value: 'xx', label: 'xx' }]} allowClear />
|
||||
<Cascader placeholder="I'm Cascader" options={[{ value: 'xx', label: 'xx' }]} allowClear />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Warning" hasFeedback validateStatus="warning" help="Need to be checked">
|
||||
<TreeSelect
|
||||
placeholder="I'm TreeSelect"
|
||||
treeData={[{ value: 'xx', label: 'xx' }]}
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="inline" style={{ marginBottom: 0 }}>
|
||||
@ -137,9 +155,13 @@ ReactDOM.render(
|
||||
<Input.Password allowClear placeholder="with input password and allowClear" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Fail" validateStatus="error">
|
||||
<Form.Item label="Fail" validateStatus="error" hasFeedback>
|
||||
<Mentions />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Fail" validateStatus="error" hasFeedback help="Should have something">
|
||||
<Input.TextArea allowClear showCount />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
mountNode,
|
||||
);
|
||||
|
@ -19,9 +19,10 @@ import { Form, InputNumber } from 'antd';
|
||||
|
||||
type ValidateStatus = Parameters<typeof Form.Item>[0]['validateStatus'];
|
||||
|
||||
function validatePrimeNumber(
|
||||
number: number,
|
||||
): { validateStatus: ValidateStatus; errorMsg: string | null } {
|
||||
function validatePrimeNumber(number: number): {
|
||||
validateStatus: ValidateStatus;
|
||||
errorMsg: string | null;
|
||||
} {
|
||||
if (number === 11) {
|
||||
return {
|
||||
validateStatus: 'success',
|
||||
|
@ -10,40 +10,10 @@
|
||||
.@{ant-prefix}-form-item-split {
|
||||
color: @text-color;
|
||||
}
|
||||
// 输入框的不同校验状态
|
||||
:not(.@{ant-prefix}-input-disabled):not(.@{ant-prefix}-input-borderless).@{ant-prefix}-input,
|
||||
:not(.@{ant-prefix}-input-affix-wrapper-disabled):not(.@{ant-prefix}-input-affix-wrapper-borderless).@{ant-prefix}-input-affix-wrapper,
|
||||
:not(.@{ant-prefix}-input-number-affix-wrapper-disabled):not(.@{ant-prefix}-input-number-affix-wrapper-borderless).@{ant-prefix}-input-number-affix-wrapper {
|
||||
&,
|
||||
&:hover {
|
||||
background-color: @background-color;
|
||||
border-color: @border-color;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&-focused {
|
||||
.active(@border-color, @hoverBorderColor, @outlineColor);
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-calendar-picker-open .@{ant-prefix}-calendar-picker-input {
|
||||
.active(@border-color, @hoverBorderColor, @outlineColor);
|
||||
}
|
||||
|
||||
.@{ant-prefix}-input-prefix,
|
||||
.@{ant-prefix}-input-number-prefix {
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
.@{ant-prefix}-input-group-addon,
|
||||
.@{ant-prefix}-input-number-group-addon {
|
||||
color: @text-color;
|
||||
border-color: @border-color;
|
||||
}
|
||||
|
||||
.has-feedback {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset form styles
|
||||
|
@ -24,287 +24,19 @@
|
||||
}
|
||||
|
||||
&-has-feedback {
|
||||
// ========================= Input =========================
|
||||
.@{ant-prefix}-input {
|
||||
padding-right: 24px;
|
||||
}
|
||||
// https://github.com/ant-design/ant-design/issues/19884
|
||||
.@{ant-prefix}-input-affix-wrapper {
|
||||
.@{ant-prefix}-input-suffix {
|
||||
padding-right: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix issue: https://github.com/ant-design/ant-design/issues/7854
|
||||
.@{ant-prefix}-input-search:not(.@{ant-prefix}-input-search-enter-button) {
|
||||
.@{ant-prefix}-input-suffix {
|
||||
right: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== Switch =========================
|
||||
.@{ant-prefix}-switch {
|
||||
margin: 2px 0 4px;
|
||||
}
|
||||
|
||||
// ======================== Select =========================
|
||||
// Fix overlapping between feedback icon and <Select>'s arrow.
|
||||
// https://github.com/ant-design/ant-design/issues/4431
|
||||
> .@{ant-prefix}-select .@{ant-prefix}-select-arrow,
|
||||
> .@{ant-prefix}-select .@{ant-prefix}-select-clear,
|
||||
:not(.@{ant-prefix}-input-group-addon) > .@{ant-prefix}-select .@{ant-prefix}-select-arrow,
|
||||
:not(.@{ant-prefix}-input-group-addon) > .@{ant-prefix}-select .@{ant-prefix}-select-clear,
|
||||
:not(.@{ant-prefix}-input-number-group-addon)
|
||||
> .@{ant-prefix}-select
|
||||
.@{ant-prefix}-select-arrow,
|
||||
:not(.@{ant-prefix}-input-number-group-addon)
|
||||
> .@{ant-prefix}-select
|
||||
.@{ant-prefix}-select-clear {
|
||||
right: 32px;
|
||||
}
|
||||
> .@{ant-prefix}-select .@{ant-prefix}-select-selection-selected-value,
|
||||
:not(.@{ant-prefix}-input-group-addon)
|
||||
> .@{ant-prefix}-select
|
||||
.@{ant-prefix}-select-selection-selected-value,
|
||||
:not(.@{ant-prefix}-input-number-group-addon)
|
||||
> .@{ant-prefix}-select
|
||||
.@{ant-prefix}-select-selection-selected-value {
|
||||
padding-right: 42px;
|
||||
}
|
||||
|
||||
// ======================= Cascader ========================
|
||||
.@{ant-prefix}-cascader-picker {
|
||||
&-arrow {
|
||||
margin-right: 19px;
|
||||
}
|
||||
|
||||
&-clear {
|
||||
right: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== Picker =========================
|
||||
// Fix issue: https://github.com/ant-design/ant-design/issues/4783
|
||||
.@{ant-prefix}-picker {
|
||||
padding-right: @input-padding-horizontal-base + @font-size-base * 1.3;
|
||||
|
||||
&-large {
|
||||
padding-right: @input-padding-horizontal-lg + @font-size-base * 1.3;
|
||||
}
|
||||
|
||||
&-small {
|
||||
padding-right: @input-padding-horizontal-sm + @font-size-base * 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== Status Group ======================
|
||||
&.@{form-item-prefix-cls} {
|
||||
&-has-success,
|
||||
&-has-warning,
|
||||
&-has-error,
|
||||
&-is-validating {
|
||||
// ====================== Icon ======================
|
||||
.@{form-item-prefix-cls}-children-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
width: @input-height-base;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
font-size: @font-size-base;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
visibility: visible;
|
||||
animation: zoomIn 0.3s @ease-out-back;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== Success ========================
|
||||
&-has-success {
|
||||
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
|
||||
color: @success-color;
|
||||
animation-name: diffZoomIn1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== Warning ========================
|
||||
&-has-warning {
|
||||
.form-control-validation(@warning-color; @warning-color; @form-warning-input-bg; @warning-color-hover; @warning-color-outline);
|
||||
|
||||
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
|
||||
color: @warning-color;
|
||||
animation-name: diffZoomIn3 !important;
|
||||
}
|
||||
|
||||
// Select
|
||||
.@{ant-prefix}-select:not(.@{ant-prefix}-select-disabled):not(.@{ant-prefix}-select-customize-input) {
|
||||
.@{ant-prefix}-select-selector {
|
||||
background-color: @form-warning-input-bg;
|
||||
border-color: @warning-color !important;
|
||||
}
|
||||
&.@{ant-prefix}-select-open .@{ant-prefix}-select-selector,
|
||||
&.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector {
|
||||
.active(@warning-color, @warning-color-hover, @warning-color-outline);
|
||||
}
|
||||
}
|
||||
|
||||
// InputNumber, TimePicker
|
||||
.@{ant-prefix}-input-number,
|
||||
.@{ant-prefix}-picker {
|
||||
background-color: @form-warning-input-bg;
|
||||
border-color: @warning-color;
|
||||
|
||||
&-focused,
|
||||
&:focus {
|
||||
.active(@warning-color, @warning-color-hover, @warning-color-outline);
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
background-color: @form-warning-input-bg;
|
||||
border-color: @warning-color;
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input {
|
||||
.active(@warning-color, @warning-color-hover, @warning-color-outline);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================= Error =========================
|
||||
&-has-error {
|
||||
.form-control-validation(@error-color; @error-color; @form-error-input-bg; @error-color-hover; @error-color-outline);
|
||||
|
||||
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
|
||||
color: @error-color;
|
||||
animation-name: diffZoomIn2 !important;
|
||||
}
|
||||
|
||||
// Select
|
||||
.@{ant-prefix}-select:not(.@{ant-prefix}-select-disabled):not(.@{ant-prefix}-select-customize-input) {
|
||||
.@{ant-prefix}-select-selector {
|
||||
background-color: @form-error-input-bg;
|
||||
border-color: @error-color !important;
|
||||
}
|
||||
&.@{ant-prefix}-select-open .@{ant-prefix}-select-selector,
|
||||
&.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector {
|
||||
.active(@error-color, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
}
|
||||
|
||||
// fixes https://github.com/ant-design/ant-design/issues/20482
|
||||
.@{ant-prefix}-input-group-addon,
|
||||
.@{ant-prefix}-input-number-group-addon {
|
||||
.@{ant-prefix}-select {
|
||||
&.@{ant-prefix}-select-single:not(.@{ant-prefix}-select-customize-input)
|
||||
.@{ant-prefix}-select-selector {
|
||||
background-color: inherit;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-select.@{ant-prefix}-select-auto-complete {
|
||||
.@{ant-prefix}-input:focus {
|
||||
border-color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
// InputNumber, TimePicker
|
||||
.@{ant-prefix}-input-number,
|
||||
.@{ant-prefix}-picker {
|
||||
background-color: @form-error-input-bg;
|
||||
border-color: @error-color;
|
||||
|
||||
&-focused,
|
||||
&:focus {
|
||||
.active(@error-color, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
background-color: @form-error-input-bg;
|
||||
border-color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-mention-wrapper {
|
||||
.@{ant-prefix}-mention-editor {
|
||||
&,
|
||||
&:not([disabled]):hover {
|
||||
background-color: @form-error-input-bg;
|
||||
border-color: @error-color;
|
||||
}
|
||||
}
|
||||
&.@{ant-prefix}-mention-active:not([disabled]) .@{ant-prefix}-mention-editor,
|
||||
.@{ant-prefix}-mention-editor:not([disabled]):focus {
|
||||
.active(@error-color, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
}
|
||||
|
||||
// Cascader
|
||||
.@{ant-prefix}-cascader-picker {
|
||||
&:hover
|
||||
.@{ant-prefix}-cascader-picker-label:hover
|
||||
+ .@{ant-prefix}-cascader-input.@{ant-prefix}-input {
|
||||
border-color: @error-color;
|
||||
}
|
||||
|
||||
&:focus .@{ant-prefix}-cascader-input {
|
||||
background-color: @form-error-input-bg;
|
||||
.active(@error-color, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer
|
||||
.@{ant-prefix}-transfer {
|
||||
&-list {
|
||||
border-color: @error-color;
|
||||
|
||||
&-search:not([disabled]) {
|
||||
border-color: @input-border-color;
|
||||
|
||||
&:hover {
|
||||
.hover();
|
||||
}
|
||||
|
||||
&:focus {
|
||||
.active();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Radio.Group
|
||||
.@{ant-prefix}-radio-button-wrapper {
|
||||
border-color: @error-color !important;
|
||||
|
||||
&:not(:first-child) {
|
||||
&::before {
|
||||
background-color: @error-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mentions
|
||||
.@{ant-prefix}-mentions {
|
||||
border-color: @error-color !important;
|
||||
|
||||
&-focused,
|
||||
&:focus {
|
||||
.active(@error-color, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== Validating =======================
|
||||
&-is-validating {
|
||||
&.@{form-item-prefix-cls}-has-feedback .@{form-item-prefix-cls}-children-icon {
|
||||
display: inline-block;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1008,6 +1008,90 @@ exports[`renders ./components/input-number/demo/borderless.md extend context cor
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/controls.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-handler-up-inner"
|
||||
>
|
||||
<span
|
||||
aria-label="arrow-up"
|
||||
class="anticon anticon-arrow-up"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="arrow-up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M868 545.5L536.1 163a31.96 31.96 0 00-48.3 0L156 545.5a7.97 7.97 0 006 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-handler-down-inner"
|
||||
>
|
||||
<span
|
||||
aria-label="arrow-down"
|
||||
class="anticon anticon-arrow-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="arrow-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861a31.96 31.96 0 0048.3 0L868 478.5c4.5-5.2.8-13.2-6-13.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/digit.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-input-number"
|
||||
@ -2097,3 +2181,386 @@ exports[`renders ./components/input-number/demo/size.md extend context correctly
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/status.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-status-error"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-status-warning"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-affix-wrapper ant-input-number-affix-wrapper-status-error"
|
||||
style="width:100%"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-prefix"
|
||||
>
|
||||
<span
|
||||
aria-label="clock-circle"
|
||||
class="anticon anticon-clock-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="clock-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-status-error"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-affix-wrapper ant-input-number-affix-wrapper-status-warning"
|
||||
style="width:100%"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-prefix"
|
||||
>
|
||||
<span
|
||||
aria-label="clock-circle"
|
||||
class="anticon anticon-clock-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="clock-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-status-warning"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -735,6 +735,90 @@ exports[`renders ./components/input-number/demo/borderless.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/controls.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-handler-up-inner"
|
||||
>
|
||||
<span
|
||||
aria-label="arrow-up"
|
||||
class="anticon anticon-arrow-up"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="arrow-up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M868 545.5L536.1 163a31.96 31.96 0 00-48.3 0L156 545.5a7.97 7.97 0 006 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-handler-down-inner"
|
||||
>
|
||||
<span
|
||||
aria-label="arrow-down"
|
||||
class="anticon anticon-arrow-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="arrow-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861a31.96 31.96 0 0048.3 0L868 478.5c4.5-5.2.8-13.2-6-13.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/digit.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-input-number"
|
||||
@ -1824,3 +1908,386 @@ exports[`renders ./components/input-number/demo/size.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input-number/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-status-error"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-status-warning"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-affix-wrapper ant-input-number-affix-wrapper-status-error"
|
||||
style="width:100%"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-prefix"
|
||||
>
|
||||
<span
|
||||
aria-label="clock-circle"
|
||||
class="anticon anticon-clock-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="clock-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-status-error"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-affix-wrapper ant-input-number-affix-wrapper-status-warning"
|
||||
style="width:100%"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-prefix"
|
||||
>
|
||||
<span
|
||||
aria-label="clock-circle"
|
||||
class="anticon anticon-clock-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="clock-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="ant-input-number ant-input-number-status-warning"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,5 +1,183 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`InputNumber renders correctly when controls has custom upIcon and downIcon 1`] = `
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-handler-up-inner"
|
||||
>
|
||||
<span
|
||||
aria-label="arrow-up"
|
||||
class="anticon anticon-arrow-up"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="arrow-up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M868 545.5L536.1 163a31.96 31.96 0 00-48.3 0L156 545.5a7.97 7.97 0 006 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
class="ant-input-number-handler-down-inner"
|
||||
>
|
||||
<span
|
||||
aria-label="arrow-down"
|
||||
class="anticon anticon-arrow-down"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="arrow-down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861a31.96 31.96 0 0048.3 0L868 478.5c4.5-5.2.8-13.2-6-13.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`InputNumber renders correctly when controls is {} 1`] = `
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
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>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`InputNumber renders correctly when controls is boolean 1`] = `
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`InputNumber rtl render component should be rendered correctly in RTL direction 1`] = `
|
||||
<div
|
||||
class="ant-input-number ant-input-number-rtl"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
|
||||
import InputNumber from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
@ -28,4 +29,41 @@ describe('InputNumber', () => {
|
||||
expect(onStep).toBeCalledTimes(2);
|
||||
expect(onStep).toHaveBeenLastCalledWith(1, { offset: 1, type: 'down' });
|
||||
});
|
||||
|
||||
it('renders correctly when controls is boolean', () => {
|
||||
expect(mount(<InputNumber controls={false} />).render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders correctly when controls is {}', () => {
|
||||
expect(mount(<InputNumber controls={{}} />).render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders correctly when controls has custom upIcon and downIcon', () => {
|
||||
const wrapper = mount(
|
||||
<InputNumber
|
||||
controls={{
|
||||
upIcon: <ArrowUpOutlined />,
|
||||
downIcon: <ArrowDownOutlined />,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support className', () => {
|
||||
const wrapper = mount(
|
||||
<InputNumber
|
||||
controls={{
|
||||
upIcon: <ArrowUpOutlined className="my-class-name" />,
|
||||
downIcon: <ArrowDownOutlined className="my-class-name" />,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.find('.anticon-arrow-up').getDOMNode().className.includes('my-class-name')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
wrapper.find('.anticon-arrow-down').getDOMNode().className.includes('my-class-name'),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
25
components/input-number/demo/controls.md
Normal file
25
components/input-number/demo/controls.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
order: 99
|
||||
debug: true
|
||||
title:
|
||||
zh-CN: 图标按钮
|
||||
en-US: Icon
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以扩展 `controls` 属性用以设置自定义图标。
|
||||
|
||||
## en-US
|
||||
|
||||
When you need to use a custom `Icon`, you can set the `Icon` component as the property value of `upIcon` and `downIcon`.
|
||||
|
||||
```jsx
|
||||
import { InputNumber } from 'antd';
|
||||
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
|
||||
|
||||
ReactDOM.render(
|
||||
<InputNumber controls={{ upIcon: <ArrowUpOutlined />, downIcon: <ArrowDownOutlined /> }} />,
|
||||
mountNode,
|
||||
);
|
||||
```
|
31
components/input-number/demo/status.md
Normal file
31
components/input-number/demo/status.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
order: 19
|
||||
version: 4.19.0
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 InputNumber 添加状态,可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to InputNumber with `status`, which could be `error` or `warning`.
|
||||
|
||||
```tsx
|
||||
import { InputNumber, Space } from 'antd';
|
||||
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
|
||||
|
||||
const ValidateInputs: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<InputNumber status="error" style={{ width: '100%' }} />
|
||||
<InputNumber status="warning" style={{ width: '100%' }} />
|
||||
<InputNumber status="error" style={{ width: '100%' }} prefix={<ClockCircleOutlined />} />
|
||||
<InputNumber status="warning" style={{ width: '100%' }} prefix={<ClockCircleOutlined />} />
|
||||
</Space>
|
||||
);
|
||||
|
||||
ReactDOM.render(<ValidateInputs />, mountNode);
|
||||
```
|
@ -19,7 +19,7 @@ When a numeric value needs to be provided.
|
||||
| addonBefore | The label text displayed before (on the left side of) the input field | ReactNode | - | |
|
||||
| autoFocus | If get focus when component mounted | boolean | false | - |
|
||||
| bordered | Whether has border style | boolean | true | 4.12.0 |
|
||||
| controls | Whether to show `+-` controls | boolean | true | 4.17.0 |
|
||||
| controls | Whether to show `+-` controls, or set custom arrows icon | boolean \| { upIcon?: React.ReactNode; downIcon?: React.ReactNode; } | - | 4.19.0 |
|
||||
| decimalSeparator | Decimal separator | string | - | - |
|
||||
| defaultValue | The initial value | number | - | - |
|
||||
| disabled | If disable the input | boolean | false | - |
|
||||
@ -30,6 +30,7 @@ When a numeric value needs to be provided.
|
||||
| parser | Specifies the value extracted from formatter | function(string): number | - | - |
|
||||
| precision | The precision of input value. Will use `formatter` when config of `formatter` | number | - | - |
|
||||
| readOnly | If readonly the input | boolean | false | - |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| prefix | The prefix icon for the Input | ReactNode | - | 4.17.0 |
|
||||
| size | The height of input box | `large` \| `middle` \| `small` | - | - |
|
||||
| step | The number to which the current value is increased or decreased. It can be an integer or decimal | number \| string | 1 | - |
|
||||
|
@ -1,23 +1,32 @@
|
||||
import * as React from 'react';
|
||||
import DownOutlined from '@ant-design/icons/DownOutlined';
|
||||
import UpOutlined from '@ant-design/icons/UpOutlined';
|
||||
import classNames from 'classnames';
|
||||
import RcInputNumber, { InputNumberProps as RcInputNumberProps } from 'rc-input-number';
|
||||
import UpOutlined from '@ant-design/icons/UpOutlined';
|
||||
import DownOutlined from '@ant-design/icons/DownOutlined';
|
||||
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import SizeContext, { SizeType } from '../config-provider/SizeContext';
|
||||
import { FormItemStatusContext } from '../form/context';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import {
|
||||
getFeedbackIcon,
|
||||
getStatusClassNames,
|
||||
InputStatus,
|
||||
getMergedStatus,
|
||||
} from '../_util/statusUtils';
|
||||
|
||||
type ValueType = string | number;
|
||||
|
||||
export interface InputNumberProps<T extends ValueType = ValueType>
|
||||
extends Omit<RcInputNumberProps<T>, 'prefix' | 'size'> {
|
||||
extends Omit<RcInputNumberProps<T>, 'prefix' | 'size' | 'controls'> {
|
||||
prefixCls?: string;
|
||||
addonBefore?: React.ReactNode;
|
||||
addonAfter?: React.ReactNode;
|
||||
prefix?: React.ReactNode;
|
||||
size?: SizeType;
|
||||
bordered?: boolean;
|
||||
status?: InputStatus;
|
||||
controls?: boolean | { upIcon?: React.ReactNode; downIcon?: React.ReactNode };
|
||||
}
|
||||
|
||||
const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props, ref) => {
|
||||
@ -37,12 +46,33 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
prefix,
|
||||
bordered = true,
|
||||
readOnly,
|
||||
status: customStatus,
|
||||
controls,
|
||||
...others
|
||||
} = props;
|
||||
|
||||
const prefixCls = getPrefixCls('input-number', customizePrefixCls);
|
||||
const upIcon = <UpOutlined className={`${prefixCls}-handler-up-inner`} />;
|
||||
const downIcon = <DownOutlined className={`${prefixCls}-handler-down-inner`} />;
|
||||
let upIcon = <UpOutlined className={`${prefixCls}-handler-up-inner`} />;
|
||||
let downIcon = <DownOutlined className={`${prefixCls}-handler-down-inner`} />;
|
||||
const controlsTemp = typeof controls === 'boolean' ? controls : undefined;
|
||||
|
||||
if (typeof controls === 'object') {
|
||||
upIcon =
|
||||
typeof controls.upIcon === 'undefined' ? (
|
||||
upIcon
|
||||
) : (
|
||||
<span className={`${prefixCls}-handler-up-inner`}>{controls.upIcon}</span>
|
||||
);
|
||||
downIcon =
|
||||
typeof controls.downIcon === 'undefined' ? (
|
||||
downIcon
|
||||
) : (
|
||||
<span className={`${prefixCls}-handler-down-inner`}>{controls.downIcon}</span>
|
||||
);
|
||||
}
|
||||
|
||||
const { hasFeedback, status: contextStatus } = useContext(FormItemStatusContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
const mergeSize = customizeSize || size;
|
||||
const inputNumberClass = classNames(
|
||||
@ -53,6 +83,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
[`${prefixCls}-readonly`]: readOnly,
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
className,
|
||||
);
|
||||
|
||||
@ -64,29 +95,35 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
downHandler={downIcon}
|
||||
prefixCls={prefixCls}
|
||||
readOnly={readOnly}
|
||||
controls={controlsTemp}
|
||||
{...others}
|
||||
/>
|
||||
);
|
||||
|
||||
if (prefix != null) {
|
||||
const affixWrapperCls = classNames(`${prefixCls}-affix-wrapper`, {
|
||||
[`${prefixCls}-affix-wrapper-focused`]: focused,
|
||||
[`${prefixCls}-affix-wrapper-disabled`]: props.disabled,
|
||||
[`${prefixCls}-affix-wrapper-sm`]: size === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: size === 'large',
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
// className will go to addon wrapper
|
||||
[`${className}`]: !(addonBefore || addonAfter) && className,
|
||||
});
|
||||
if (prefix != null || hasFeedback) {
|
||||
const affixWrapperCls = classNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-focused`]: focused,
|
||||
[`${prefixCls}-affix-wrapper-disabled`]: props.disabled,
|
||||
[`${prefixCls}-affix-wrapper-sm`]: size === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: size === 'large',
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
// className will go to addon wrapper
|
||||
[`${className}`]: !(addonBefore || addonAfter) && className,
|
||||
},
|
||||
);
|
||||
|
||||
element = (
|
||||
<div
|
||||
className={affixWrapperCls}
|
||||
style={props.style}
|
||||
onMouseUp={() => inputRef.current!.focus()}
|
||||
>
|
||||
<span className={`${prefixCls}-prefix`}>{prefix}</span>
|
||||
{prefix && <span className={`${prefixCls}-prefix`}>{prefix}</span>}
|
||||
{cloneElement(element, {
|
||||
style: null,
|
||||
value: props.value,
|
||||
@ -99,6 +136,9 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
props.onBlur?.(event);
|
||||
},
|
||||
})}
|
||||
{hasFeedback && (
|
||||
<span className={`${prefixCls}-suffix`}>{getFeedbackIcon(prefixCls, mergedStatus)}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -122,6 +162,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
[`${prefixCls}-group-wrapper-lg`]: size === 'large',
|
||||
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
|
||||
},
|
||||
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
|
||||
className,
|
||||
);
|
||||
element = (
|
||||
|
@ -22,7 +22,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/XOS8qZ0kU/InputNumber.svg
|
||||
| addonBefore | 带标签的 input,设置前置标签 | ReactNode | - | 4.17.0 |
|
||||
| autoFocus | 自动获取焦点 | boolean | false | - |
|
||||
| bordered | 是否有边框 | boolean | true | 4.12.0 |
|
||||
| controls | 是否显示增减按钮 | boolean | true | 4.17.0 |
|
||||
| controls | 是否显示增减按钮,也可设置自定义箭头图标 | boolean \| { upIcon?: React.ReactNode; downIcon?: React.ReactNode; } | - | 4.19.0 |
|
||||
| decimalSeparator | 小数点 | string | - | - |
|
||||
| defaultValue | 初始值 | number | - | - |
|
||||
| disabled | 禁用 | boolean | false | - |
|
||||
@ -33,6 +33,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/XOS8qZ0kU/InputNumber.svg
|
||||
| parser | 指定从 `formatter` 里转换回数字的方式,和 `formatter` 搭配使用 | function(string): number | - | - |
|
||||
| precision | 数值精度,配置 `formatter` 时会以 `formatter` 为准 | number | - | - |
|
||||
| readOnly | 只读 | boolean | false | - |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| prefix | 带有前缀图标的 input | ReactNode | - | 4.17.0 |
|
||||
| size | 输入框大小 | `large` \| `middle` \| `small` | - | - |
|
||||
| step | 每次改变步数,可以为小数 | number \| string | 1 | - |
|
||||
|
@ -8,7 +8,7 @@
|
||||
&-affix-wrapper {
|
||||
.input();
|
||||
// or number handler will cover form status
|
||||
position: static;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: 90px;
|
||||
padding: 0;
|
||||
@ -49,14 +49,33 @@
|
||||
visibility: hidden;
|
||||
content: '\a0';
|
||||
}
|
||||
|
||||
.@{ant-prefix}-input-number-handler-wrap {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
&-prefix {
|
||||
&-prefix,
|
||||
&-suffix {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-prefix {
|
||||
margin-inline-end: @input-affix-margin;
|
||||
}
|
||||
|
||||
&-suffix {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
margin-right: @input-padding-horizontal-base;
|
||||
margin-left: @input-affix-margin;
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-input-number-group-wrapper .@{ant-prefix}-input-number-affix-wrapper {
|
||||
|
@ -2,6 +2,7 @@
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../input/style/mixin';
|
||||
@import './affix';
|
||||
@import './status';
|
||||
|
||||
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
|
||||
@form-item-prefix-cls: ~'@{ant-prefix}-form-item';
|
||||
|
@ -1,2 +1,4 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// deps-lint-skip: form
|
||||
|
45
components/input-number/style/status.less
Normal file
45
components/input-number/style/status.less
Normal file
@ -0,0 +1,45 @@
|
||||
@import '../../input/style/mixin';
|
||||
|
||||
@input-number-prefix-cls: ~'@{ant-prefix}-input-number';
|
||||
|
||||
@input-number-wrapper-cls: @input-number-prefix-cls, ~'@{input-number-prefix-cls}-affix-wrapper';
|
||||
|
||||
each(@input-number-wrapper-cls, {
|
||||
.@{value} {
|
||||
&-status-error {
|
||||
.status-color(@value, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
.status-color-common(@input-number-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline)
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.status-color(@value, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
.status-color-common(@input-number-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
.@{input-number-prefix-cls}-affix-wrapper {
|
||||
&-status-validating {
|
||||
.@{input-number-prefix-cls}-feedback-icon {
|
||||
display: inline-block;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-status-success {
|
||||
.@{input-number-prefix-cls}-feedback-icon {
|
||||
color: @success-color;
|
||||
animation-name: diffZoomIn1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{input-number-prefix-cls}-group-wrapper {
|
||||
&-status-error {
|
||||
.group-status-color(@input-number-prefix-cls, @error-color, @error-color);
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.group-status-color(@input-number-prefix-cls, @warning-color, @warning-color);
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import { tuple } from '../_util/type';
|
||||
import type { InputProps } from './Input';
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import { DirectionType } from '../config-provider';
|
||||
import { SizeType } from '../config-provider/SizeContext';
|
||||
import { FormItemStatusContext, FormItemStatusContextProps } from '../form/context';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import { getMergedStatus, getStatusClassNames, InputStatus } from '../_util/statusUtils';
|
||||
import { tuple } from '../_util/type';
|
||||
import type { InputProps } from './Input';
|
||||
import { getInputClassName, hasPrefixSuffix } from './utils';
|
||||
|
||||
const ClearableInputType = tuple('text', 'input');
|
||||
@ -40,6 +42,7 @@ export interface ClearableInputProps extends BasicProps {
|
||||
addonBefore?: React.ReactNode;
|
||||
addonAfter?: React.ReactNode;
|
||||
triggerFocus?: () => void;
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
@ -91,7 +94,11 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
return null;
|
||||
}
|
||||
|
||||
renderLabeledIcon(prefixCls: string, element: React.ReactElement) {
|
||||
renderLabeledIcon(
|
||||
prefixCls: string,
|
||||
element: React.ReactElement,
|
||||
statusContext: FormItemStatusContextProps,
|
||||
) {
|
||||
const {
|
||||
focused,
|
||||
value,
|
||||
@ -106,7 +113,11 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
readOnly,
|
||||
bordered,
|
||||
hidden,
|
||||
status: customStatus,
|
||||
} = this.props;
|
||||
|
||||
const { status: contextStatus, hasFeedback } = statusContext;
|
||||
|
||||
if (!hasPrefixSuffix(this.props)) {
|
||||
return cloneElement(element, {
|
||||
value,
|
||||
@ -116,18 +127,26 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
const suffixNode = this.renderSuffix(prefixCls);
|
||||
const prefixNode = prefix ? <span className={`${prefixCls}-prefix`}>{prefix}</span> : null;
|
||||
|
||||
const affixWrapperCls = classNames(`${prefixCls}-affix-wrapper`, {
|
||||
[`${prefixCls}-affix-wrapper-focused`]: focused,
|
||||
[`${prefixCls}-affix-wrapper-disabled`]: disabled,
|
||||
[`${prefixCls}-affix-wrapper-sm`]: size === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: size === 'large',
|
||||
[`${prefixCls}-affix-wrapper-input-with-clear-btn`]: suffix && allowClear && value,
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
// className will go to addon wrapper
|
||||
[`${className}`]: !hasAddon(this.props) && className,
|
||||
});
|
||||
const affixWrapperCls = classNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-focused`]: focused,
|
||||
[`${prefixCls}-affix-wrapper-disabled`]: disabled,
|
||||
[`${prefixCls}-affix-wrapper-sm`]: size === 'small',
|
||||
[`${prefixCls}-affix-wrapper-lg`]: size === 'large',
|
||||
[`${prefixCls}-affix-wrapper-input-with-clear-btn`]: suffix && allowClear && value,
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-readonly`]: readOnly,
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
// className will go to addon wrapper
|
||||
[`${className}`]: !hasAddon(this.props) && className,
|
||||
},
|
||||
getStatusClassNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
getMergedStatus(contextStatus, customStatus),
|
||||
hasFeedback,
|
||||
),
|
||||
);
|
||||
return (
|
||||
<span
|
||||
ref={this.containerRef}
|
||||
@ -147,8 +166,22 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
);
|
||||
}
|
||||
|
||||
renderInputWithLabel(prefixCls: string, labeledElement: React.ReactElement) {
|
||||
const { addonBefore, addonAfter, style, size, className, direction, hidden } = this.props;
|
||||
renderInputWithLabel(
|
||||
prefixCls: string,
|
||||
labeledElement: React.ReactElement,
|
||||
statusContext: FormItemStatusContextProps,
|
||||
) {
|
||||
const {
|
||||
addonBefore,
|
||||
addonAfter,
|
||||
style,
|
||||
size,
|
||||
className,
|
||||
direction,
|
||||
hidden,
|
||||
status: customStatus,
|
||||
} = this.props;
|
||||
const { status: contextStatus, hasFeedback } = statusContext;
|
||||
// Not wrap when there is not addons
|
||||
if (!hasAddon(this.props)) {
|
||||
return labeledElement;
|
||||
@ -172,6 +205,11 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
[`${prefixCls}-group-wrapper-lg`]: size === 'large',
|
||||
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
|
||||
},
|
||||
getStatusClassNames(
|
||||
`${prefixCls}-group-wrapper`,
|
||||
getMergedStatus(contextStatus, customStatus),
|
||||
hasFeedback,
|
||||
),
|
||||
className,
|
||||
);
|
||||
|
||||
@ -188,8 +226,24 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
);
|
||||
}
|
||||
|
||||
renderTextAreaWithClearIcon(prefixCls: string, element: React.ReactElement) {
|
||||
const { value, allowClear, className, style, direction, bordered, hidden } = this.props;
|
||||
renderTextAreaWithClearIcon(
|
||||
prefixCls: string,
|
||||
element: React.ReactElement,
|
||||
statusContext: FormItemStatusContextProps,
|
||||
) {
|
||||
const {
|
||||
value,
|
||||
allowClear,
|
||||
className,
|
||||
style,
|
||||
direction,
|
||||
bordered,
|
||||
hidden,
|
||||
status: customStatus,
|
||||
} = this.props;
|
||||
|
||||
const { status: contextStatus, hasFeedback } = statusContext;
|
||||
|
||||
if (!allowClear) {
|
||||
return cloneElement(element, {
|
||||
value,
|
||||
@ -198,6 +252,11 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
const affixWrapperCls = classNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
`${prefixCls}-affix-wrapper-textarea-with-clear-btn`,
|
||||
getStatusClassNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
getMergedStatus(contextStatus, customStatus),
|
||||
hasFeedback,
|
||||
),
|
||||
{
|
||||
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
|
||||
@ -217,11 +276,21 @@ class ClearableLabeledInput extends React.Component<ClearableInputProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { prefixCls, inputType, element } = this.props;
|
||||
if (inputType === ClearableInputType[0]) {
|
||||
return this.renderTextAreaWithClearIcon(prefixCls, element);
|
||||
}
|
||||
return this.renderInputWithLabel(prefixCls, this.renderLabeledIcon(prefixCls, element));
|
||||
return (
|
||||
<FormItemStatusContext.Consumer>
|
||||
{statusContext => {
|
||||
const { prefixCls, inputType, element } = this.props;
|
||||
if (inputType === ClearableInputType[0]) {
|
||||
return this.renderTextAreaWithClearIcon(prefixCls, element, statusContext);
|
||||
}
|
||||
return this.renderInputWithLabel(
|
||||
prefixCls,
|
||||
this.renderLabeledIcon(prefixCls, element, statusContext),
|
||||
statusContext,
|
||||
);
|
||||
}}
|
||||
</FormItemStatusContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import { ValidateStatus } from '../form/FormItem';
|
||||
import type Group from './Group';
|
||||
import type Search from './Search';
|
||||
import type TextArea from './TextArea';
|
||||
@ -11,6 +12,8 @@ import { ConfigConsumer, ConfigConsumerProps, DirectionType } from '../config-pr
|
||||
import SizeContext, { SizeType } from '../config-provider/SizeContext';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import { getInputClassName, hasPrefixSuffix } from './utils';
|
||||
import { FormItemStatusContext } from '../form/context';
|
||||
import { getFeedbackIcon, InputStatus, getMergedStatus } from '../_util/statusUtils';
|
||||
|
||||
export interface InputFocusOptions extends FocusOptions {
|
||||
cursor?: 'start' | 'end' | 'all';
|
||||
@ -59,6 +62,7 @@ export interface InputProps
|
||||
showCount?: boolean | ShowCountProps;
|
||||
bordered?: boolean;
|
||||
htmlSize?: number;
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
export function fixControlledValue<T>(value: T) {
|
||||
@ -280,6 +284,7 @@ class Input extends React.Component<InputProps, InputState> {
|
||||
prefixCls: string,
|
||||
size: SizeType | undefined,
|
||||
bordered: boolean,
|
||||
status?: ValidateStatus,
|
||||
input: ConfigConsumerProps['input'] = {},
|
||||
) => {
|
||||
const {
|
||||
@ -307,7 +312,9 @@ class Input extends React.Component<InputProps, InputState> {
|
||||
'bordered',
|
||||
'htmlSize',
|
||||
'showCount',
|
||||
'status',
|
||||
]);
|
||||
|
||||
return (
|
||||
<input
|
||||
autoComplete={input.autoComplete}
|
||||
@ -317,7 +324,14 @@ class Input extends React.Component<InputProps, InputState> {
|
||||
onBlur={this.onBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
className={classNames(
|
||||
getInputClassName(prefixCls, bordered, customizeSize || size, disabled, this.direction),
|
||||
getInputClassName(
|
||||
prefixCls,
|
||||
bordered,
|
||||
customizeSize || size,
|
||||
disabled,
|
||||
this.direction,
|
||||
status,
|
||||
),
|
||||
{
|
||||
[className!]: className && !addonBefore && !addonAfter,
|
||||
},
|
||||
@ -369,49 +383,66 @@ class Input extends React.Component<InputProps, InputState> {
|
||||
dataCount = `${valueLength}${hasMaxLength ? ` / ${maxLength}` : ''}`;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{!!showCount && (
|
||||
<span
|
||||
className={classNames(`${prefixCls}-show-count-suffix`, {
|
||||
[`${prefixCls}-show-count-has-suffix`]: !!suffix,
|
||||
})}
|
||||
>
|
||||
{dataCount}
|
||||
</span>
|
||||
)}
|
||||
{suffix}
|
||||
</>
|
||||
!!showCount && (
|
||||
<span
|
||||
className={classNames(`${prefixCls}-show-count-suffix`, {
|
||||
[`${prefixCls}-show-count-has-suffix`]: !!suffix,
|
||||
})}
|
||||
>
|
||||
{dataCount}
|
||||
</span>
|
||||
)
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
renderSuffix = (prefixCls: string, hasFeedback?: boolean, status?: ValidateStatus) => {
|
||||
const { suffix, showCount } = this.props;
|
||||
|
||||
return (
|
||||
(showCount || suffix || hasFeedback) && (
|
||||
<>
|
||||
{this.renderShowCountSuffix(prefixCls)}
|
||||
{suffix}
|
||||
{hasFeedback && getFeedbackIcon(prefixCls, status)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
renderComponent = ({ getPrefixCls, direction, input }: ConfigConsumerProps) => {
|
||||
const { value, focused } = this.state;
|
||||
const { prefixCls: customizePrefixCls, bordered = true } = this.props;
|
||||
const { prefixCls: customizePrefixCls, bordered = true, status: customStatus } = this.props;
|
||||
const prefixCls = getPrefixCls('input', customizePrefixCls);
|
||||
this.direction = direction;
|
||||
|
||||
const showCountSuffix = this.renderShowCountSuffix(prefixCls);
|
||||
|
||||
return (
|
||||
<SizeContext.Consumer>
|
||||
{size => (
|
||||
<ClearableLabeledInput
|
||||
size={size}
|
||||
{...this.props}
|
||||
prefixCls={prefixCls}
|
||||
inputType="input"
|
||||
value={fixControlledValue(value)}
|
||||
element={this.renderInput(prefixCls, size, bordered, input)}
|
||||
handleReset={this.handleReset}
|
||||
ref={this.saveClearableInput}
|
||||
direction={direction}
|
||||
focused={focused}
|
||||
triggerFocus={this.focus}
|
||||
bordered={bordered}
|
||||
suffix={showCountSuffix}
|
||||
/>
|
||||
<FormItemStatusContext.Consumer>
|
||||
{({ status: contextStatus, hasFeedback }) => {
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
return (
|
||||
<ClearableLabeledInput
|
||||
size={size}
|
||||
{...this.props}
|
||||
prefixCls={prefixCls}
|
||||
inputType="input"
|
||||
value={fixControlledValue(value)}
|
||||
element={this.renderInput(prefixCls, size, bordered, mergedStatus, input)}
|
||||
handleReset={this.handleReset}
|
||||
ref={this.saveClearableInput}
|
||||
direction={direction}
|
||||
focused={focused}
|
||||
triggerFocus={this.focus}
|
||||
bordered={bordered}
|
||||
suffix={this.renderSuffix(prefixCls, hasFeedback, mergedStatus)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</FormItemStatusContext.Consumer>
|
||||
)}
|
||||
</SizeContext.Consumer>
|
||||
);
|
||||
|
@ -1,13 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import RcTextArea, { TextAreaProps as RcTextAreaProps } from 'rc-textarea';
|
||||
import ResizableTextArea from 'rc-textarea/lib/ResizableTextArea';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import classNames from 'classnames';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
import ClearableLabeledInput from './ClearableLabeledInput';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { fixControlledValue, resolveOnChange, triggerFocus, InputFocusOptions } from './Input';
|
||||
import SizeContext, { SizeType } from '../config-provider/SizeContext';
|
||||
import { FormItemStatusContext } from '../form/context';
|
||||
import {
|
||||
getFeedbackIcon,
|
||||
getStatusClassNames,
|
||||
InputStatus,
|
||||
getMergedStatus,
|
||||
} from '../_util/statusUtils';
|
||||
import ClearableLabeledInput from './ClearableLabeledInput';
|
||||
import { fixControlledValue, InputFocusOptions, resolveOnChange, triggerFocus } from './Input';
|
||||
|
||||
interface ShowCountProps {
|
||||
formatter: (args: { count: number; maxLength?: number }) => string;
|
||||
@ -42,6 +49,7 @@ export interface TextAreaProps extends RcTextAreaProps {
|
||||
bordered?: boolean;
|
||||
showCount?: boolean | ShowCountProps;
|
||||
size?: SizeType;
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
export interface TextAreaRef {
|
||||
@ -63,6 +71,7 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
onCompositionStart,
|
||||
onCompositionEnd,
|
||||
onChange,
|
||||
status: customStatus,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@ -70,6 +79,9 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const size = React.useContext(SizeContext);
|
||||
|
||||
const { status: contextStatus, hasFeedback } = React.useContext(FormItemStatusContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
const innerRef = React.useRef<RcTextArea>(null);
|
||||
const clearableInputRef = React.useRef<ClearableLabeledInput>(null);
|
||||
|
||||
@ -161,12 +173,15 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
const textArea = (
|
||||
<RcTextArea
|
||||
{...omit(props, ['allowClear'])}
|
||||
className={classNames({
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
[className!]: className && !showCount,
|
||||
[`${prefixCls}-sm`]: size === 'small' || customizeSize === 'small',
|
||||
[`${prefixCls}-lg`]: size === 'large' || customizeSize === 'large',
|
||||
})}
|
||||
className={classNames(
|
||||
{
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
[className!]: className && !showCount,
|
||||
[`${prefixCls}-sm`]: size === 'small' || customizeSize === 'small',
|
||||
[`${prefixCls}-lg`]: size === 'large' || customizeSize === 'large',
|
||||
},
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
)}
|
||||
style={showCount ? undefined : style}
|
||||
prefixCls={prefixCls}
|
||||
onCompositionStart={onInternalCompositionStart}
|
||||
@ -195,12 +210,13 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
handleReset={handleReset}
|
||||
ref={clearableInputRef}
|
||||
bordered={bordered}
|
||||
status={customStatus}
|
||||
style={showCount ? undefined : style}
|
||||
/>
|
||||
);
|
||||
|
||||
// Only show text area wrapper when needed
|
||||
if (showCount) {
|
||||
if (showCount || hasFeedback) {
|
||||
const valueLength = [...val].length;
|
||||
|
||||
let dataCount = '';
|
||||
@ -217,14 +233,16 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
|
||||
`${prefixCls}-textarea`,
|
||||
{
|
||||
[`${prefixCls}-textarea-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-textarea-show-count`]: showCount,
|
||||
},
|
||||
`${prefixCls}-textarea-show-count`,
|
||||
getStatusClassNames(`${prefixCls}-textarea`, mergedStatus, hasFeedback),
|
||||
className,
|
||||
)}
|
||||
style={style}
|
||||
data-count={dataCount}
|
||||
>
|
||||
{textareaNode}
|
||||
{hasFeedback && getFeedbackIcon(prefixCls, mergedStatus)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -9529,6 +9529,117 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/status.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<input
|
||||
class="ant-input ant-input-status-error"
|
||||
placeholder="Error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<input
|
||||
class="ant-input ant-input-status-warning"
|
||||
placeholder="Warning"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-status-error"
|
||||
>
|
||||
<span
|
||||
class="ant-input-prefix"
|
||||
>
|
||||
<span
|
||||
aria-label="clock-circle"
|
||||
class="anticon anticon-clock-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="clock-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
class="ant-input"
|
||||
placeholder="Error with prefix"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-status-warning"
|
||||
>
|
||||
<span
|
||||
class="ant-input-prefix"
|
||||
>
|
||||
<span
|
||||
aria-label="clock-circle"
|
||||
class="anticon anticon-clock-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="clock-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
class="ant-input"
|
||||
placeholder="Warning with prefix"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/textarea.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<textarea
|
||||
|
@ -3303,6 +3303,117 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<input
|
||||
class="ant-input ant-input-status-error"
|
||||
placeholder="Error"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<input
|
||||
class="ant-input ant-input-status-warning"
|
||||
placeholder="Warning"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-status-error"
|
||||
>
|
||||
<span
|
||||
class="ant-input-prefix"
|
||||
>
|
||||
<span
|
||||
aria-label="clock-circle"
|
||||
class="anticon anticon-clock-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="clock-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
class="ant-input"
|
||||
placeholder="Error with prefix"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-status-warning"
|
||||
>
|
||||
<span
|
||||
class="ant-input-prefix"
|
||||
>
|
||||
<span
|
||||
aria-label="clock-circle"
|
||||
class="anticon anticon-clock-circle"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="clock-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||
/>
|
||||
<path
|
||||
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
class="ant-input"
|
||||
placeholder="Warning with prefix"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/input/demo/textarea.md correctly 1`] = `
|
||||
Array [
|
||||
<textarea
|
||||
|
31
components/input/demo/status.md
Normal file
31
components/input/demo/status.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
order: 19
|
||||
version: 4.19.0
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 Input 添加状态,可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to Input with `status`, which could be `error` or `warning`.
|
||||
|
||||
```tsx
|
||||
import { Input, Space } from 'antd';
|
||||
import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined';
|
||||
|
||||
const ValidateInputs: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Input status="error" placeholder="Error" />
|
||||
<Input status="warning" placeholder="Warning" />
|
||||
<Input status="error" prefix={<ClockCircleOutlined />} placeholder="Error with prefix" />
|
||||
<Input status="warning" prefix={<ClockCircleOutlined />} placeholder="Warning with prefix" />
|
||||
</Space>
|
||||
);
|
||||
|
||||
ReactDOM.render(<ValidateInputs />, mountNode);
|
||||
```
|
@ -27,6 +27,7 @@ A basic widget for getting the user input is a text field. Keyboard and mouse ca
|
||||
| id | The ID for input | string | - | |
|
||||
| maxLength | The max length | number | - | |
|
||||
| showCount | Whether show text count | boolean \| { formatter: ({ count: number, maxLength?: number }) => ReactNode } | false | 4.18.0 |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| prefix | The prefix icon for the Input | ReactNode | - | |
|
||||
| size | The size of the input box. Note: in the context of a form, the `large` size is used | `large` \| `middle` \| `small` | - | |
|
||||
| suffix | The suffix icon for the Input | ReactNode | - | |
|
||||
|
@ -28,6 +28,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/xS9YEJhfe/Input.svg
|
||||
| id | 输入框的 id | string | - | |
|
||||
| maxLength | 最大长度 | number | - | |
|
||||
| showCount | 是否展示字数 | boolean \| { formatter: ({ count: number, maxLength?: number }) => ReactNode } | false | 4.18.0 |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| prefix | 带有前缀图标的 input | ReactNode | - | |
|
||||
| size | 控件大小。注:标准表单内的输入框大小限制为 `large` | `large` \| `middle` \| `small` | - | |
|
||||
| suffix | 带有后缀图标的 input | ReactNode | - | |
|
||||
|
@ -50,6 +50,10 @@
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-show-count-suffix {
|
||||
|
@ -3,6 +3,7 @@
|
||||
@import './mixin';
|
||||
@import './affix';
|
||||
@import './allow-clear';
|
||||
@import './status';
|
||||
|
||||
@input-prefix-cls: ~'@{ant-prefix}-input';
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// deps-lint-skip: form
|
||||
// style dependencies
|
||||
import '../../button/style';
|
||||
|
@ -422,3 +422,53 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-color(
|
||||
@prefix-cls: @input-prefix-cls;
|
||||
@text-color: @input-color;
|
||||
@border-color: @input-border-color;
|
||||
@background-color: @input-bg;
|
||||
@hoverBorderColor: @primary-color-hover;
|
||||
@outlineColor: @primary-color-outline;
|
||||
) {
|
||||
&:not(.@{prefix-cls}-disabled):not(.@{prefix-cls}-borderless).@{prefix-cls} {
|
||||
&,
|
||||
&:hover {
|
||||
background: @background-color;
|
||||
border-color: @border-color;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&-focused {
|
||||
.active(@text-color, @hoverBorderColor, @outlineColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-color-common(
|
||||
@prefix-cls: @input-prefix-cls;
|
||||
@text-color: @input-color;
|
||||
@border-color: @input-border-color;
|
||||
@background-color: @input-bg;
|
||||
@hoverBorderColor: @primary-color-hover;
|
||||
@outlineColor: @primary-color-outline;
|
||||
) {
|
||||
.@{prefix-cls}-feedback-icon {
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
.@{prefix-cls}-prefix {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.group-status-color(
|
||||
@prefix-cls: @input-prefix-cls;
|
||||
@text-color: @input-color;
|
||||
@border-color: @input-border-color;
|
||||
) {
|
||||
.@{prefix-cls}-group-addon {
|
||||
color: @text-color;
|
||||
border-color: @border-color;
|
||||
}
|
||||
}
|
||||
|
82
components/input/style/status.less
Normal file
82
components/input/style/status.less
Normal file
@ -0,0 +1,82 @@
|
||||
@import './mixin';
|
||||
|
||||
@input-prefix-cls: ~'@{ant-prefix}-input';
|
||||
|
||||
@input-wrapper-cls: @input-prefix-cls, ~'@{input-prefix-cls}-affix-wrapper';
|
||||
|
||||
each(@input-wrapper-cls, {
|
||||
.@{value} {
|
||||
&-status-error {
|
||||
.status-color(@value, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
.status-color-common(@input-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.status-color(@value, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
.status-color-common(@input-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
.@{input-prefix-cls}-textarea,
|
||||
.@{input-prefix-cls}-affix-wrapper {
|
||||
&-status-validating {
|
||||
.@{input-prefix-cls}-feedback-icon {
|
||||
display: inline-block;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-status-success {
|
||||
.@{input-prefix-cls}-feedback-icon {
|
||||
color: @success-color;
|
||||
animation-name: diffZoomIn1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{input-prefix-cls}-textarea {
|
||||
&-status-error {
|
||||
.@{input-prefix-cls}-feedback-icon {
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.@{input-prefix-cls}-feedback-icon {
|
||||
color: @warning-color;
|
||||
}
|
||||
}
|
||||
|
||||
.@{input-prefix-cls}-feedback-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: @input-padding-horizontal-base;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&-status-error,
|
||||
&-status-warning,
|
||||
&-status-success,
|
||||
&-status-validating {
|
||||
&.@{input-prefix-cls}-textarea-has-feedback {
|
||||
.@{input-prefix-cls} {
|
||||
padding-right: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{input-prefix-cls}-group-wrapper {
|
||||
&-status-error {
|
||||
.group-status-color(@input-prefix-cls, @error-color, @error-color);
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.group-status-color(@input-prefix-cls, @warning-color, @warning-color);
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
import classNames from 'classnames';
|
||||
import { ValidateStatus } from '../form/FormItem';
|
||||
import type { DirectionType } from '../config-provider';
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import type { ClearableInputProps } from './ClearableLabeledInput';
|
||||
import type { InputProps } from './Input';
|
||||
import { getStatusClassNames } from '../_util/statusUtils';
|
||||
|
||||
export function getInputClassName(
|
||||
prefixCls: string,
|
||||
@ -10,14 +12,20 @@ export function getInputClassName(
|
||||
size?: SizeType,
|
||||
disabled?: boolean,
|
||||
direction?: DirectionType,
|
||||
status?: ValidateStatus,
|
||||
hasFeedback?: boolean,
|
||||
) {
|
||||
return classNames(prefixCls, {
|
||||
[`${prefixCls}-sm`]: size === 'small',
|
||||
[`${prefixCls}-lg`]: size === 'large',
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
});
|
||||
return classNames(
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}-sm`]: size === 'small',
|
||||
[`${prefixCls}-lg`]: size === 'large',
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
getStatusClassNames(prefixCls, status, hasFeedback),
|
||||
);
|
||||
}
|
||||
|
||||
export function hasPrefixSuffix(props: InputProps | ClearableInputProps) {
|
||||
|
@ -206,3 +206,39 @@ Array [
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mentions/demo/status.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-error"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-warning"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -206,3 +206,39 @@ Array [
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mentions/demo/status.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-error"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-mentions ant-mentions-status-warning"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
51
components/mentions/demo/status.md
Normal file
51
components/mentions/demo/status.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
order: 8
|
||||
title:
|
||||
zh-CN: 自定义状态
|
||||
en-US: Status
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `status` 为 Mentions 添加状态。可选 `error` 或者 `warning`。
|
||||
|
||||
## en-US
|
||||
|
||||
Add status to Mentions with `status`, which could be `error` or `warning`。
|
||||
|
||||
```jsx
|
||||
import { Mentions, Space } from 'antd';
|
||||
|
||||
const { Option } = Mentions;
|
||||
|
||||
function onChange(value) {
|
||||
console.log('Change:', value);
|
||||
}
|
||||
|
||||
function onSelect(option) {
|
||||
console.log('select', option);
|
||||
}
|
||||
|
||||
const MentionsStatuses = () => {
|
||||
const options = (
|
||||
<>
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<Mentions onChange={onChange} onSelect={onSelect} defaultValue="@afc163" status="error">
|
||||
{options}
|
||||
</Mentions>
|
||||
<Mentions onChange={onChange} onSelect={onSelect} defaultValue="@afc163" status="warning">
|
||||
{options}
|
||||
</Mentions>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<MentionsStatuses />, mountNode);
|
||||
```
|
@ -21,32 +21,33 @@ When you need to mention someone or something.
|
||||
|
||||
### Mention
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| autoFocus | Auto get focus when component mounted | boolean | false |
|
||||
| autoSize | Textarea height autosize feature, can be set to true \| false or an object { minRows: 2, maxRows: 6 } | boolean \| object | false |
|
||||
| defaultValue | Default value | string | - |
|
||||
| filterOption | Customize filter option logic | false \| (input: string, option: OptionProps) => boolean | - |
|
||||
| getPopupContainer | Set the mount HTML node for suggestions | () => HTMLElement | - |
|
||||
| notFoundContent | Set mentions content when not match | ReactNode | `Not Found` |
|
||||
| placement | Set popup placement | `top` \| `bottom` | `bottom` |
|
||||
| prefix | Set trigger prefix keyword | string \| string\[] | `@` |
|
||||
| split | Set split string before and after selected mention | string | ` ` |
|
||||
| validateSearch | Customize trigger search logic | (text: string, props: MentionsProps) => void | - |
|
||||
| value | Set value of mentions | string | - |
|
||||
| onBlur | Trigger when mentions lose focus | () => void | - |
|
||||
| onChange | Trigger when value changed | (text: string) => void | - |
|
||||
| onFocus | Trigger when mentions get focus | () => void | - |
|
||||
| onResize | The callback function that is triggered when textarea resize | function({ width, height }) | - |
|
||||
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - |
|
||||
| onSelect | Trigger when user select the option | (option: OptionProps, prefix: string) => void | - |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoFocus | Auto get focus when component mounted | boolean | false | |
|
||||
| autoSize | Textarea height autosize feature, can be set to true \| false or an object { minRows: 2, maxRows: 6 } | boolean \| object | false | |
|
||||
| defaultValue | Default value | string | - | |
|
||||
| filterOption | Customize filter option logic | false \| (input: string, option: OptionProps) => boolean | - | |
|
||||
| getPopupContainer | Set the mount HTML node for suggestions | () => HTMLElement | - | |
|
||||
| notFoundContent | Set mentions content when not match | ReactNode | `Not Found` | |
|
||||
| placement | Set popup placement | `top` \| `bottom` | `bottom` | |
|
||||
| prefix | Set trigger prefix keyword | string \| string\[] | `@` | |
|
||||
| split | Set split string before and after selected mention | string | ` ` | |
|
||||
| status | Set validation status | 'error' \| 'warning' \| 'success' \| 'validating' | - | 4.19.0 |
|
||||
| validateSearch | Customize trigger search logic | (text: string, props: MentionsProps) => void | - | |
|
||||
| value | Set value of mentions | string | - | |
|
||||
| onBlur | Trigger when mentions lose focus | () => void | - | |
|
||||
| onChange | Trigger when value changed | (text: string) => void | - | |
|
||||
| onFocus | Trigger when mentions get focus | () => void | - | |
|
||||
| onResize | The callback function that is triggered when textarea resize | function({ width, height }) | - | |
|
||||
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - | |
|
||||
| onSelect | Trigger when user select the option | (option: OptionProps, prefix: string) => void | - | |
|
||||
|
||||
### Mention methods
|
||||
|
||||
| Name | Description |
|
||||
| --- | --- |
|
||||
| blur() | Remove focus |
|
||||
| focus() | Get focus |
|
||||
| Name | Description |
|
||||
| ------- | ------------ |
|
||||
| blur() | Remove focus |
|
||||
| focus() | Get focus |
|
||||
|
||||
### Option
|
||||
|
||||
|
@ -5,6 +5,13 @@ import { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import Spin from '../spin';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { FormItemStatusContext } from '../form/context';
|
||||
import {
|
||||
getFeedbackIcon,
|
||||
getMergedStatus,
|
||||
getStatusClassNames,
|
||||
InputStatus,
|
||||
} from '../_util/statusUtils';
|
||||
|
||||
export const { Option } = RcMentions;
|
||||
|
||||
@ -22,6 +29,7 @@ export interface OptionProps {
|
||||
|
||||
export interface MentionProps extends RcMentionsProps {
|
||||
loading?: boolean;
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
export interface MentionState {
|
||||
@ -53,6 +61,7 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
filterOption,
|
||||
children,
|
||||
notFoundContent,
|
||||
status: customStatus,
|
||||
...restProps
|
||||
},
|
||||
ref,
|
||||
@ -61,6 +70,8 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
const innerRef = React.useRef<HTMLElement>();
|
||||
const mergedRef = composeRef(ref, innerRef);
|
||||
const { getPrefixCls, renderEmpty, direction } = React.useContext(ConfigContext);
|
||||
const { status: contextStatus, hasFeedback } = React.useContext(FormItemStatusContext);
|
||||
const mergedStatus = getMergedStatus(contextStatus, customStatus);
|
||||
|
||||
const onFocus: React.FocusEventHandler<HTMLTextAreaElement> = (...args) => {
|
||||
if (restProps.onFocus) {
|
||||
@ -112,10 +123,11 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
[`${prefixCls}-focused`]: focused,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
getStatusClassNames(prefixCls, mergedStatus),
|
||||
!hasFeedback && className,
|
||||
);
|
||||
|
||||
return (
|
||||
const mentions = (
|
||||
<RcMentions
|
||||
prefixCls={prefixCls}
|
||||
notFoundContent={getNotFoundContent()}
|
||||
@ -131,6 +143,23 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
{getOptions()}
|
||||
</RcMentions>
|
||||
);
|
||||
|
||||
if (hasFeedback) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
`${prefixCls}-affix-wrapper`,
|
||||
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{mentions}
|
||||
{getFeedbackIcon(prefixCls, mergedStatus)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return mentions;
|
||||
};
|
||||
|
||||
const Mentions = React.forwardRef<unknown, MentionProps>(InternalMentions) as CompoundedComponent;
|
||||
|
@ -22,36 +22,37 @@ cover: https://gw.alipayobjects.com/zos/alicdn/jPE-itMFM/Mentions.svg
|
||||
|
||||
### Mentions
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| autoFocus | 自动获得焦点 | boolean | false |
|
||||
| autoSize | 自适应内容高度,可设置为 true \| false 或对象:{ minRows: 2, maxRows: 6 } | boolean \| object | false |
|
||||
| defaultValue | 默认值 | string | - |
|
||||
| filterOption | 自定义过滤逻辑 | false \| (input: string, option: OptionProps) => boolean | - |
|
||||
| getPopupContainer | 指定建议框挂载的 HTML 节点 | () => HTMLElement | - |
|
||||
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | `Not Found` |
|
||||
| placement | 弹出层展示位置 | `top` \| `bottom` | `bottom` |
|
||||
| prefix | 设置触发关键字 | string \| string\[] | `@` |
|
||||
| split | 设置选中项前后分隔符 | string | ` ` |
|
||||
| validateSearch | 自定义触发验证逻辑 | (text: string, props: MentionsProps) => void | - |
|
||||
| value | 设置值 | string | - |
|
||||
| onBlur | 失去焦点时触发 | () => void | - |
|
||||
| onChange | 值改变时触发 | (text: string) => void | - |
|
||||
| onFocus | 获得焦点时触发 | () => void | - |
|
||||
| onResize | resize 回调 | function({ width, height }) | - |
|
||||
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - |
|
||||
| onSelect | 选择选项时触发 | (option: OptionProps, prefix: string) => void | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoFocus | 自动获得焦点 | boolean | false | |
|
||||
| autoSize | 自适应内容高度,可设置为 true \| false 或对象:{ minRows: 2, maxRows: 6 } | boolean \| object | false | |
|
||||
| defaultValue | 默认值 | string | - | |
|
||||
| filterOption | 自定义过滤逻辑 | false \| (input: string, option: OptionProps) => boolean | - | |
|
||||
| getPopupContainer | 指定建议框挂载的 HTML 节点 | () => HTMLElement | - | |
|
||||
| notFoundContent | 当下拉列表为空时显示的内容 | ReactNode | `Not Found` | |
|
||||
| placement | 弹出层展示位置 | `top` \| `bottom` | `bottom` | |
|
||||
| prefix | 设置触发关键字 | string \| string\[] | `@` | |
|
||||
| split | 设置选中项前后分隔符 | string | ` ` | |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| validateSearch | 自定义触发验证逻辑 | (text: string, props: MentionsProps) => void | - | |
|
||||
| value | 设置值 | string | - | |
|
||||
| onBlur | 失去焦点时触发 | () => void | - | |
|
||||
| onChange | 值改变时触发 | (text: string) => void | - | |
|
||||
| onFocus | 获得焦点时触发 | () => void | - | |
|
||||
| onResize | resize 回调 | function({ width, height }) | - | |
|
||||
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - | |
|
||||
| onSelect | 选择选项时触发 | (option: OptionProps, prefix: string) => void | - | |
|
||||
|
||||
### Mentions 方法
|
||||
|
||||
| 名称 | 描述 |
|
||||
| --- | --- |
|
||||
| blur() | 移除焦点 |
|
||||
| 名称 | 描述 |
|
||||
| ------- | -------- |
|
||||
| blur() | 移除焦点 |
|
||||
| focus() | 获取焦点 |
|
||||
|
||||
### Option
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| children | 选项内容 | ReactNode | - |
|
||||
| value | 选择时填充的值 | string | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| -------- | -------------- | --------- | ------ |
|
||||
| children | 选项内容 | ReactNode | - |
|
||||
| value | 选择时填充的值 | string | - |
|
||||
|
@ -1,6 +1,7 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../input/style/mixin';
|
||||
@import './status';
|
||||
|
||||
@mention-prefix-cls: ~'@{ant-prefix}-mentions';
|
||||
|
||||
|
@ -3,3 +3,5 @@ import './index.less';
|
||||
// style dependencies
|
||||
import '../../empty/style';
|
||||
import '../../spin/style';
|
||||
|
||||
// deps-lint-skip: form
|
||||
|
43
components/mentions/style/status.less
Normal file
43
components/mentions/style/status.less
Normal file
@ -0,0 +1,43 @@
|
||||
@import '../../input/style/mixin';
|
||||
|
||||
@mention-prefix-cls: ~'@{ant-prefix}-mentions';
|
||||
@input-prefix-cls: ~'@{ant-prefix}-input';
|
||||
|
||||
.@{mention-prefix-cls} {
|
||||
&-status-error {
|
||||
.status-color(@mention-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
.status-color-common(@input-prefix-cls, @error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
|
||||
}
|
||||
|
||||
&-status-warning {
|
||||
.status-color(@mention-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
.status-color-common(@input-prefix-cls, @warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
|
||||
}
|
||||
|
||||
&-affix-wrapper {
|
||||
position: relative;
|
||||
|
||||
.@{mention-prefix-cls}-feedback-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: @input-padding-horizontal-base;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&-status-error {
|
||||
.@{mention-prefix-cls}-feedback-icon {
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-has-warning {
|
||||
.@{mention-prefix-cls}-feedback-icon {
|
||||
color: @warning-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { SubMenu as RcSubMenu, useFullPath } from 'rc-menu';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import MenuContext from './MenuContext';
|
||||
import MenuContext, { MenuTheme } from './MenuContext';
|
||||
import { isValidElement, cloneElement } from '../_util/reactNode';
|
||||
|
||||
interface TitleEventEntity {
|
||||
@ -23,10 +23,11 @@ export interface SubMenuProps {
|
||||
popupOffset?: [number, number];
|
||||
popupClassName?: string;
|
||||
children?: React.ReactNode;
|
||||
theme?: MenuTheme;
|
||||
}
|
||||
|
||||
function SubMenu(props: SubMenuProps) {
|
||||
const { popupClassName, icon, title } = props;
|
||||
const { popupClassName, icon, title, theme } = props;
|
||||
const context = React.useContext(MenuContext);
|
||||
const { prefixCls, inlineCollapsed, antdMenuTheme } = context;
|
||||
|
||||
@ -71,7 +72,11 @@ function SubMenu(props: SubMenuProps) {
|
||||
<RcSubMenu
|
||||
{...omit(props, ['icon'])}
|
||||
title={titleNode}
|
||||
popupClassName={classNames(prefixCls, `${prefixCls}-${antdMenuTheme}`, popupClassName)}
|
||||
popupClassName={classNames(
|
||||
prefixCls,
|
||||
`${prefixCls}-${theme || antdMenuTheme}`,
|
||||
popupClassName,
|
||||
)}
|
||||
/>
|
||||
</MenuContext.Provider>
|
||||
);
|
||||
|
@ -3801,6 +3801,369 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/menu/demo/submenu-theme.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
aria-checked="false"
|
||||
class="ant-switch"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-switch-handle"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner"
|
||||
>
|
||||
Light
|
||||
</span>
|
||||
</button>,
|
||||
<br />,
|
||||
<br />,
|
||||
<ul
|
||||
class="ant-menu ant-menu-root ant-menu-vertical ant-menu-dark"
|
||||
data-menu-list="true"
|
||||
role="menu"
|
||||
style="width:256px"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-menu-submenu ant-menu-submenu-vertical ant-menu-submenu-open"
|
||||
role="none"
|
||||
theme="light"
|
||||
>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
aria-haspopup="true"
|
||||
class="ant-menu-submenu-title"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
aria-label="mail"
|
||||
class="anticon anticon-mail ant-menu-item-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="mail"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32zm-40 110.8V792H136V270.8l-27.6-21.5 39.3-50.5 42.8 33.3h643.1l42.8-33.3 39.3 50.5-27.7 21.5zM833.6 232L512 482 190.4 232l-42.8-33.3-39.3 50.5 27.6 21.5 341.6 265.6a55.99 55.99 0 0068.7 0L888 270.8l27.6-21.5-39.3-50.5-42.7 33.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Navigation One
|
||||
</span>
|
||||
<i
|
||||
class="ant-menu-submenu-arrow"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-menu-submenu ant-menu-submenu-popup ant-menu ant-menu-light"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<ul
|
||||
class="ant-menu ant-menu-sub ant-menu-vertical"
|
||||
data-menu-list="true"
|
||||
>
|
||||
<li
|
||||
class="ant-menu-item ant-menu-item-selected ant-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Option 1
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<li
|
||||
class="ant-menu-item ant-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Option 2
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<li
|
||||
class="ant-menu-item ant-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Option 3
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-menu-item ant-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Option 5
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<li
|
||||
class="ant-menu-item ant-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Option 6
|
||||
</span>
|
||||
</li>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ul>,
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style="display:none"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="ant-tooltip ant-menu-inline-collapsed-tooltip"
|
||||
style="opacity:0;pointer-events:none"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-content"
|
||||
>
|
||||
<div
|
||||
class="ant-tooltip-arrow"
|
||||
>
|
||||
<span
|
||||
class="ant-tooltip-arrow-content"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/menu/demo/switch-mode.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
|
@ -998,6 +998,103 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/menu/demo/submenu-theme.md correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
aria-checked="false"
|
||||
class="ant-switch"
|
||||
role="switch"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-switch-handle"
|
||||
/>
|
||||
<span
|
||||
class="ant-switch-inner"
|
||||
>
|
||||
Light
|
||||
</span>
|
||||
</button>,
|
||||
<br />,
|
||||
<br />,
|
||||
<ul
|
||||
class="ant-menu ant-menu-root ant-menu-vertical ant-menu-dark"
|
||||
data-menu-list="true"
|
||||
role="menu"
|
||||
style="width:256px"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-menu-submenu ant-menu-submenu-vertical ant-menu-submenu-open"
|
||||
role="none"
|
||||
theme="light"
|
||||
>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
aria-haspopup="true"
|
||||
class="ant-menu-submenu-title"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
aria-label="mail"
|
||||
class="anticon anticon-mail ant-menu-item-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="mail"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M928 160H96c-17.7 0-32 14.3-32 32v640c0 17.7 14.3 32 32 32h832c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32zm-40 110.8V792H136V270.8l-27.6-21.5 39.3-50.5 42.8 33.3h643.1l42.8-33.3 39.3 50.5-27.7 21.5zM833.6 232L512 482 190.4 232l-42.8-33.3-39.3 50.5 27.6 21.5 341.6 265.6a55.99 55.99 0 0068.7 0L888 270.8l27.6-21.5-39.3-50.5-42.7 33.2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Navigation One
|
||||
</span>
|
||||
<i
|
||||
class="ant-menu-submenu-arrow"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-menu-item ant-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Option 5
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
class="ant-menu-item ant-menu-item-only-child"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="ant-menu-title-content"
|
||||
>
|
||||
Option 6
|
||||
</span>
|
||||
</li>
|
||||
</ul>,
|
||||
<div
|
||||
aria-hidden="true"
|
||||
style="display:none"
|
||||
/>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/menu/demo/switch-mode.md correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
|
@ -287,6 +287,32 @@ describe('Menu', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('allows the overriding of theme at the popup submenu level', () => {
|
||||
const menuModesWithPopupSubMenu = ['horizontal', 'vertical'];
|
||||
|
||||
menuModesWithPopupSubMenu.forEach(menuMode => {
|
||||
it(`when menu is mode ${menuMode}`, () => {
|
||||
const wrapper = mount(
|
||||
<Menu mode={menuMode} openKeys={['1']} theme="dark">
|
||||
<SubMenu key="1" title="submenu1" theme="light">
|
||||
<Menu.Item key="submenu1">Option 1</Menu.Item>
|
||||
<Menu.Item key="submenu2">Option 2</Menu.Item>
|
||||
</SubMenu>
|
||||
<Menu.Item key="2">menu2</Menu.Item>
|
||||
</Menu>,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
expect(wrapper.find('ul.ant-menu-root').hasClass('ant-menu-dark')).toBeTruthy();
|
||||
expect(wrapper.find('div.ant-menu-submenu-popup').hasClass('ant-menu-light')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/pulls/4677
|
||||
// https://github.com/ant-design/ant-design/issues/4692
|
||||
// TypeError: Cannot read property 'indexOf' of undefined
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user