mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 02:59:04 +08:00
merge master into Feature (#25262)
* feat: add successColor for Progress (#24655) * feat: add successColor for Progress * feat: update * fix: update test * remove snap * feat: add test case * refactor success * feat: adjust styyle * feat: add DevWarning * feat: Support rowSelection.dirty (#24718) * feat: Support rowSelection.dirty * rename to reserveKeys * preserveKeys will keep record also * to preserveSelectedRowKeys * feat: add ghost prop for collapse (#24734) * feat: add ghost prop for collapse * doc: version of collapse's ghost prop * refactor: make ghost collapse's less code to a nested style * chore: remove redundant codes in ghost collapse's less & doc * doc: add a background wrapper for ghost collapse demo * doc: dark-theme wrapper bg-color for ghost collapse demo * test: update snapshot of ghost collapse * doc: use softer bg-color on ghost collapse demo * doc: remove disabled panel in ghost collapse demo * feat: form instance support getFieldInstance (#24711) * support getFieldInstance * update doc * fix lint * move func * move into hooks * update ref logic * fix lint * rm only * fix docs * feat: dropdown support arrow (#23869) * feat: dropdown support arrow prop close #22758 * test: update snapshot * fix: fix dropdown cls names * test: update snapshot * test: update snapshot * doc: update demo * test: update demo snapshot * demo * fix: snapshot * chore: change the style of ghost collapse & demo modified (#24762) * refactor: reduce content padding in ghost collapse * doc: remove the wrapper outside ghost collapse Designer want the demo differs from other demos * refactor: remove redundant .less code in collapse * feat: cascader dropdown-render prop (#24812) * feat: cascader dropdown-render prop * fix: update Cascader dropdownRender type annotation * fix: set rc-cascader semver from ^ to ~ * docs: fix coding style in cascader/custom-dropdown * feat: 🆕 support Drawer closeIcon (#24842) * feat: 🆕 support Drawer closeIcon close #19283 close #19153 * add test case * update docs * feat: 🆕 Cascader expandIcon (#24865) * feat: cascader expandIcon * fix: snap * refactor: reduce CSS size (#24846) * refactor: reduce button css size * refactor: remove redundant button .less code * feat: add Table onChange an action param (#24697) * Working on tests * created TableAction type * changed TableActions to tuple * removed chinese documentation line * refactor TableActions * fix documentation * Moved action into extra param * minor doc change * feat: add closeIcon customize tag close (#24885) * feat: add closeIcon customize tag close * docs fix * update snap * fix: css name * update snapshot * snapshot * feat: add radio `optionType` api to set radio option type (#24809) * feat: radio component * docs: update md * fix: snap * test components * fix: use optionType * fix name * add warning * fix * feat: expand rate character (#24903) * feat: expand rate character * fix: demo * fix: snap * Update components/rate/index.zh-CN.md Co-authored-by: 偏右 <afc163@gmail.com> * fix Co-authored-by: 偏右 <afc163@gmail.com> * Refactor demo code box actions (#24887) * refactor: refine the styling of actions part of demo code-box * fix: lint style * refactor: move Result children to end (#24945) * feat: remove content max-width on dot-step (#24907) * feat: add Skeleton-Image (#24805) * feat: add Skeleton-Image * feat: add docs * fix: adjust skeleton * feat: adjust Image Component * feat: rebase * feat: adjust style * fix: lint * feat: remove size * feat: delete md * feat: fix style * ✨ feat: Mentions support autoSize (#24961) close #17746 * chore: replace textarea with rc-textarea (#24966) * feat: update pagination@2.3.0 support onChange called when pageSize change (#24964) * feat: update pagination@2.5.0 and add test case to relative component * fix: lint * delete * feat: add test case for pagination * adjust test case * feat: Implement centered prop in Tabs (#24958) * Implement centered in Tabs along with its tests and docs * Fix build error * Add Chinese translations and remove test case Co-authored-by: Ashkan Pourghasem <ashkan.pourghasem@gmail.com> * feat: Add modal style parameter (#24773) * add some paramters in default.less * Update components/style/themes/default.less Co-authored-by: Amumu <yoyo837@hotmail.com> * change parameter in compact.less Co-authored-by: Crystal Gao <jinggao@ebay.com> Co-authored-by: Amumu <yoyo837@hotmail.com> * feat: export Tabs addIcon (#25006) * feat: export Tabs addIcon * update snapshot * feat: showNow on timepicker and datetimepicker (#25032) * feat: update rc-picker@1.7.1 and fix icons of month and quarter picker in DatePicker Component (#25035) * feat: update rc-picker@1.7.1 * delete * add * feat: expand rate support props (#24993) * docs: 📝 Add Form.Item hidden in doc (#25108) close #25101 * fix: ⌨️ Improve Pagination accessibility issue (#25119) * ⌨️ Improve Pagination a11y by fixing a W3C error https://github.com/react-component/pagination/issues/280 * update snapshot * 🆙 rc-pagination to 2.4.1 * feat: support triggerSubMenuAction for <Menu /> (#25127) * feat(menu): add triggerSubMenuAction for Menu * feat(menu): test cases * chore: Adjust picker logic (#25135) * chore: update rc-picker 1.10.0 (#25174) * feat: table row check strictly (#24931) * feat: add checkStrictly on Table.rowSelection * fix: LGTM warnings * test: table rowSelection.checkStrictly * test: add cov [wip] * refactor: tree.rowSelection.checkStrictly [wip] * test: table.rowSelection.checkStrictly basic case * feat: support rowKey on checkStrictly table * feat: Table checkStrictly support getCheckboxProps * docs: Table checkStrictly * chore: typo * chore: remove useless comment * chore: update snapshot * chore: update snapshot * fix: fire selectAll on selection dropdown menu & changeRows incorrect in selectAll callback * docs: typo * chore * chore * fix: expand buttons of leaf rows in tree data are not hidden * feat: Table warning about rowKey index parameter * perf: only generate keyEntities when not checkStrictly * refactor: remove useless parseCheckedKeys * refactor: get derived selected & half selected keys from selectedRowKeys * chore: remove env condition stmt * chore: revert index usage & code formatting * chore: rerun ci * docs: table tree-data checkstrictly * test: update snapshots * refactor: use useMergedState hook * chore: rerun ci * chore: rerun ci 2 * chore: revert selection select all behavior * refactor: refactor code based on feature * chore: revert table code format * chore: revert table code format * fix: useMemo deps * fix: useMemo deps * fix: useMemo deps * feat: support preserve (#25186) * docs: add responsibly order for Col (#25139) * feat: add type * feat: add responsibly order cols * feat: add docs * feat: add test case * fix test Co-authored-by: 二货机器人 <smith3816@gmail.com> Co-authored-by: 偏右 <afc163@gmail.com> Co-authored-by: zoomdong <1344492820@qq.com> Co-authored-by: 07akioni <07akioni2@gmail.com> Co-authored-by: wendellhu <wendellhu95@gmail.com> Co-authored-by: xrkffgg <xrkffgg@gmail.com> Co-authored-by: Neto Braghetto <netow93@gmail.com> Co-authored-by: Kermit Xuan <kermitlx@outlook.com> Co-authored-by: Ashkan Pourghasem <64011067+ashkan-pm@users.noreply.github.com> Co-authored-by: Ashkan Pourghasem <ashkan.pourghasem@gmail.com> Co-authored-by: hicrystal <295247343@qq.com> Co-authored-by: Crystal Gao <jinggao@ebay.com> Co-authored-by: Amumu <yoyo837@hotmail.com> Co-authored-by: Li Ming <armyiljfe@gmail.com>
This commit is contained in:
parent
1ca1bb08a6
commit
523b74e3b6
@ -80,6 +80,46 @@ exports[`renders ./components/cascader/demo/change-on-select.md correctly 1`] =
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/custom-dropdown.md correctly 1`] = `
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="ant-cascader-picker-label"
|
||||
/>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input ant-cascader-input "
|
||||
placeholder="Please select"
|
||||
readonly=""
|
||||
tabindex="-1"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-cascader-picker-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
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>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/custom-render.md correctly 1`] = `
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
@ -551,7 +591,7 @@ exports[`renders ./components/cascader/demo/size.md correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`renders ./components/cascader/demo/suffix.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
tabindex="0"
|
||||
@ -588,10 +628,11 @@ exports[`renders ./components/cascader/demo/suffix.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
style="margin-top:1rem"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
@ -611,6 +652,84 @@ exports[`renders ./components/cascader/demo/suffix.md correctly 1`] = `
|
||||
>
|
||||
ab
|
||||
</span>
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="ant-cascader-picker-label"
|
||||
/>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input ant-cascader-input "
|
||||
placeholder="Please select"
|
||||
readonly=""
|
||||
tabindex="-1"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-cascader-picker-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</span>,
|
||||
<br />,
|
||||
<br />,
|
||||
<span
|
||||
class="ant-cascader-picker"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="ant-cascader-picker-label"
|
||||
/>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input ant-cascader-input "
|
||||
placeholder="Please select"
|
||||
readonly=""
|
||||
tabindex="-1"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-cascader-picker-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
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>,
|
||||
]
|
||||
`;
|
||||
|
72
components/cascader/demo/custom-dropdown.md
Normal file
72
components/cascader/demo/custom-dropdown.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
order: 12
|
||||
title:
|
||||
zh-CN: 扩展菜单
|
||||
en-US: Custom dropdown
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
使用 `dropdownRender` 对下拉菜单进行自由扩展。
|
||||
|
||||
## en-US
|
||||
|
||||
Customize the dropdown menu via `dropdownRender`.
|
||||
|
||||
```jsx
|
||||
import { Cascader, Divider } 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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function dropdownRender(menus) {
|
||||
return (
|
||||
<div>
|
||||
{menus}
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<div style={{ padding: 8 }}>The footer is not very short.</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Cascader
|
||||
options={options}
|
||||
dropdownRender={dropdownRender}
|
||||
placeholder="Please select"
|
||||
/>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
@ -2,17 +2,17 @@
|
||||
order: 11
|
||||
debug: true
|
||||
title:
|
||||
zh-CN: 后缀图标
|
||||
en-US: Suffix
|
||||
zh-CN: 自定义图标
|
||||
en-US: Custom Icons
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
省市区级联。
|
||||
通过 `suffixIcon` 自定义选择框后缀图标,通过 `expandIcon` 自定义次级菜单展开图标。
|
||||
|
||||
## en-US
|
||||
|
||||
Cascade selection box for selecting province/city/district.
|
||||
Use `suffixIcon` to customize the selection box suffix icon, and use `expandIcon` to customize the current item expand icon.
|
||||
|
||||
```jsx
|
||||
import { Cascader } from 'antd';
|
||||
@ -58,21 +58,28 @@ function onChange(value) {
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<Cascader
|
||||
suffixIcon={<SmileOutlined />}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
placeholder="Please select"
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Cascader suffixIcon="ab" options={options} onChange={onChange} placeholder="Please select" />
|
||||
<br />
|
||||
<br />
|
||||
<Cascader
|
||||
suffixIcon="ab"
|
||||
style={{ marginTop: '1rem' }}
|
||||
expandIcon={<SmileOutlined />}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
placeholder="Please select"
|
||||
/>
|
||||
</div>,
|
||||
<br />
|
||||
<br />
|
||||
<Cascader expandIcon="ab" options={options} onChange={onChange} placeholder="Please select" />
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -30,6 +30,7 @@ Cascade selection box.
|
||||
| disabled | whether disabled select | boolean | false | |
|
||||
| displayRender | render function of displaying selected options | `(label, selectedOptions) => ReactNode` | `label => label.join(' / ')` | |
|
||||
| expandTrigger | expand current item when click or hover, one of 'click' 'hover' | string | `click` | |
|
||||
| expandIcon | customize the current item expand icon | ReactNode | - | 4.4.0 |
|
||||
| fieldNames | custom field name for label and value and children | object | `{ label: 'label', value: 'value', children: 'children' }` | |
|
||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative.[example](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | Function(triggerNode) | () => document.body | |
|
||||
| loadData | To load option lazily, and it cannot work with `showSearch` | `(selectedOptions) => void` | - | |
|
||||
@ -44,6 +45,7 @@ Cascade selection box.
|
||||
| style | additional style | CSSProperties | - | |
|
||||
| suffixIcon | The custom suffix icon | ReactNode | - | |
|
||||
| value | selected value | string\[] \| number\[] | - | |
|
||||
| dropdownRender | Customize dropdown content | `(menus: ReactNode) => ReactNode` | - | 4.4.0 |
|
||||
| onChange | callback when finishing cascader select | `(value, selectedOptions) => void` | - | |
|
||||
| onPopupVisibleChange | callback when popup shown or hidden | `(value) => void` | - | |
|
||||
|
||||
|
@ -97,6 +97,7 @@ export interface CascaderProps {
|
||||
loadData?: (selectedOptions?: CascaderOptionType[]) => void;
|
||||
/** 次级菜单的展开方式,可选 'click' 和 'hover' */
|
||||
expandTrigger?: CascaderExpandTrigger;
|
||||
expandIcon?: React.ReactNode;
|
||||
/** 当此项为 true 时,点选每级菜单选项值都会发生变化 */
|
||||
changeOnSelect?: boolean;
|
||||
/** 浮层可见变化时回调 */
|
||||
@ -108,6 +109,7 @@ export interface CascaderProps {
|
||||
/** use this after antd@3.7.0 */
|
||||
fieldNames?: FieldNamesType;
|
||||
suffixIcon?: React.ReactNode;
|
||||
dropdownRender?: (menus: React.ReactNode) => React.ReactNode
|
||||
}
|
||||
|
||||
export interface CascaderState {
|
||||
@ -458,11 +460,14 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
allowClear,
|
||||
showSearch = false,
|
||||
suffixIcon,
|
||||
expandIcon,
|
||||
notFoundContent,
|
||||
popupClassName,
|
||||
bordered,
|
||||
dropdownRender,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const mergedSize = customizeSize || size;
|
||||
|
||||
const { value, inputFocused } = state;
|
||||
@ -592,9 +597,11 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
</span>
|
||||
);
|
||||
|
||||
let expandIcon = <RightOutlined />;
|
||||
if (isRtlLayout) {
|
||||
expandIcon = <LeftOutlined />;
|
||||
let expandIconNode;
|
||||
if (expandIcon) {
|
||||
expandIconNode = expandIcon;
|
||||
} else {
|
||||
expandIconNode = isRtlLayout ? <LeftOutlined /> : <RightOutlined />;
|
||||
}
|
||||
|
||||
const loadingIcon = (
|
||||
@ -621,10 +628,11 @@ class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
onPopupVisibleChange={this.handlePopupVisibleChange}
|
||||
onChange={this.handleChange}
|
||||
dropdownMenuColumnStyle={dropdownMenuColumnStyle}
|
||||
expandIcon={expandIcon}
|
||||
expandIcon={expandIconNode}
|
||||
loadingIcon={loadingIcon}
|
||||
popupClassName={rcCascaderPopupClassName}
|
||||
popupPlacement={this.getPopupPlacement(direction)}
|
||||
dropdownRender={dropdownRender}
|
||||
>
|
||||
{input}
|
||||
</RcCascader>
|
||||
|
@ -31,6 +31,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
|
||||
| disabled | 禁用 | boolean | false | |
|
||||
| displayRender | 选择后展示的渲染函数 | `(label, selectedOptions) => ReactNode` | `label => label.join(' / ')` | |
|
||||
| expandTrigger | 次级菜单的展开方式,可选 'click' 和 'hover' | string | `click` | |
|
||||
| expandIcon | 自定义次级菜单展开图标 | ReactNode | - | 4.4.0 |
|
||||
| fieldNames | 自定义 options 中 label name children 的字段 | object | `{ label: 'label', value: 'value', children: 'children' }` | |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | Function(triggerNode) | () => document.body | |
|
||||
| loadData | 用于动态加载选项,无法与 `showSearch` 一起使用 | `(selectedOptions) => void` | - | |
|
||||
@ -45,6 +46,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
|
||||
| style | 自定义样式 | CSSProperties | - | |
|
||||
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
|
||||
| value | 指定选中项 | string\[] \| number\[] | - | |
|
||||
| dropdownRender | 自定义下拉框内容 | `(menus: ReactNode) => ReactNode` | - | 4.4.0 |
|
||||
| onChange | 选择完成后的回调 | `(value, selectedOptions) => void` | - | |
|
||||
| onPopupVisibleChange | 显示/隐藏浮层的回调 | `(value) => void` | - | |
|
||||
|
||||
|
@ -23,6 +23,7 @@ export interface CollapseProps {
|
||||
prefixCls?: string;
|
||||
expandIcon?: (panelProps: PanelProps) => React.ReactNode;
|
||||
expandIconPosition?: ExpandIconPosition;
|
||||
ghost?: boolean;
|
||||
}
|
||||
|
||||
interface PanelProps {
|
||||
@ -42,7 +43,7 @@ interface CollapseInterface extends React.FC<CollapseProps> {
|
||||
|
||||
const Collapse: CollapseInterface = props => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const { prefixCls: customizePrefixCls, className = '', bordered } = props;
|
||||
const { prefixCls: customizePrefixCls, className = '', bordered, ghost } = props;
|
||||
const prefixCls = getPrefixCls('collapse', customizePrefixCls);
|
||||
|
||||
const getIconPosition = () => {
|
||||
@ -72,6 +73,7 @@ const Collapse: CollapseInterface = props => {
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
[`${prefixCls}-icon-position-${iconPosition}`]: true,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-ghost`]: !!ghost,
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
@ -718,6 +718,125 @@ exports[`renders ./components/collapse/demo/extra.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/collapse/demo/ghost.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-collapse ant-collapse-icon-position-left ant-collapse-ghost"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-item ant-collapse-item-active"
|
||||
>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="right"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
style="-ms-transform:rotate(90deg);transform:rotate(90deg)"
|
||||
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>
|
||||
This is panel header 1
|
||||
</div>
|
||||
<div
|
||||
class="ant-collapse-content ant-collapse-content-active"
|
||||
>
|
||||
<div
|
||||
class="ant-collapse-content-box"
|
||||
>
|
||||
<p>
|
||||
|
||||
A dog is a type of domesticated animal.
|
||||
Known for its loyalty and faithfulness,
|
||||
it can be found as a welcome guest in many households across the world.
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-collapse-item"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
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>
|
||||
This is panel header 2
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-collapse-item"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
class="ant-collapse-header"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
class="anticon anticon-right ant-collapse-arrow"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
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>
|
||||
This is panel header 3
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/collapse/demo/mix.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-collapse ant-collapse-icon-position-left"
|
||||
|
41
components/collapse/demo/ghost.md
Normal file
41
components/collapse/demo/ghost.md
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
order: 6
|
||||
title:
|
||||
zh-CN: 幽灵折叠面板
|
||||
en-US: Ghost Collapse
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
将折叠面板的背景变成透明。
|
||||
|
||||
## en-US
|
||||
|
||||
Making collapse's background to transparent.
|
||||
|
||||
```jsx
|
||||
import { Collapse } from 'antd';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
|
||||
const text = `
|
||||
A dog is a type of domesticated animal.
|
||||
Known for its loyalty and faithfulness,
|
||||
it can be found as a welcome guest in many households across the world.
|
||||
`;
|
||||
|
||||
ReactDOM.render(
|
||||
<Collapse defaultActiveKey={['1']} ghost>
|
||||
<Panel header="This is panel header 1" key="1">
|
||||
<p>{text}</p>
|
||||
</Panel>
|
||||
<Panel header="This is panel header 2" key="2">
|
||||
<p>{text}</p>
|
||||
</Panel>
|
||||
<Panel header="This is panel header 3" key="3">
|
||||
<p>{text}</p>
|
||||
</Panel>
|
||||
</Collapse>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
@ -27,6 +27,7 @@ A content area which can be collapsed and expanded.
|
||||
| expandIcon | allow to customize collapse icon | (panelProps) => ReactNode | - | |
|
||||
| expandIconPosition | Set expand icon position | `left` \| `right` | - | |
|
||||
| destroyInactivePanel | Destroy Inactive Panel | boolean | false | |
|
||||
| ghost | make the collapse borderless and its background transparent | boolean | false | 4.4.0 |
|
||||
|
||||
### Collapse.Panel
|
||||
|
||||
|
@ -28,6 +28,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/IxH16B9RD/Collapse.svg
|
||||
| expandIcon | 自定义切换图标 | (panelProps) => ReactNode | - | |
|
||||
| expandIconPosition | 设置图标位置 | `left` \| `right` | - | |
|
||||
| destroyInactivePanel | 销毁折叠隐藏的面板 | boolean | false | |
|
||||
| ghost | 使折叠面板透明且无边框 | boolean | false | 4.4.0 |
|
||||
|
||||
### Collapse.Panel
|
||||
|
||||
|
@ -125,6 +125,22 @@
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
&-ghost {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
> .@{collapse-prefix-cls}-item {
|
||||
border-bottom: 0;
|
||||
> .@{collapse-prefix-cls}-content {
|
||||
background-color: transparent;
|
||||
border-top: 0;
|
||||
> .@{collapse-prefix-cls}-content-box {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& &-item-disabled > &-header {
|
||||
&,
|
||||
& > .arrow {
|
||||
|
@ -16901,9 +16901,11 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
|
||||
class="config-pagination-prev config-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -16925,7 +16927,7 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-item config-pagination-item-0 config-pagination-disabled config-pagination-item-disabled"
|
||||
@ -16941,9 +16943,11 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
|
||||
class="config-pagination-next config-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -16965,7 +16969,7 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-options"
|
||||
@ -17048,9 +17052,11 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
|
||||
class="config-pagination-prev config-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -17072,7 +17078,7 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-item config-pagination-item-0 config-pagination-disabled config-pagination-item-disabled"
|
||||
@ -17088,9 +17094,11 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
|
||||
class="config-pagination-next config-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -17112,7 +17120,7 @@ exports[`ConfigProvider components Pagination configProvider 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-options"
|
||||
@ -17200,9 +17208,11 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
|
||||
class="config-pagination-prev config-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -17224,7 +17234,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-item config-pagination-item-0 config-pagination-disabled config-pagination-item-disabled"
|
||||
@ -17240,9 +17250,11 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
|
||||
class="config-pagination-next config-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -17264,7 +17276,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-options"
|
||||
@ -17347,9 +17359,11 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
|
||||
class="config-pagination-prev config-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -17371,7 +17385,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-item config-pagination-item-0 config-pagination-disabled config-pagination-item-disabled"
|
||||
@ -17387,9 +17401,11 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
|
||||
class="config-pagination-next config-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -17411,7 +17427,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize large
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-options"
|
||||
@ -17499,9 +17515,11 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
|
||||
class="config-pagination-prev config-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -17523,7 +17541,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-item config-pagination-item-0 config-pagination-disabled config-pagination-item-disabled"
|
||||
@ -17539,9 +17557,11 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
|
||||
class="config-pagination-next config-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -17563,7 +17583,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-options"
|
||||
@ -17646,9 +17666,11 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
|
||||
class="config-pagination-prev config-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -17670,7 +17692,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-item config-pagination-item-0 config-pagination-disabled config-pagination-item-disabled"
|
||||
@ -17686,9 +17708,11 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
|
||||
class="config-pagination-next config-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="config-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -17710,7 +17734,7 @@ exports[`ConfigProvider components Pagination configProvider componentSize middl
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="config-pagination-options"
|
||||
@ -17798,9 +17822,11 @@ exports[`ConfigProvider components Pagination configProvider virtual and dropdow
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -17822,7 +17848,7 @@ exports[`ConfigProvider components Pagination configProvider virtual and dropdow
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-0 ant-pagination-disabled ant-pagination-item-disabled"
|
||||
@ -17838,9 +17864,11 @@ exports[`ConfigProvider components Pagination configProvider virtual and dropdow
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -17862,7 +17890,7 @@ exports[`ConfigProvider components Pagination configProvider virtual and dropdow
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -17945,9 +17973,11 @@ exports[`ConfigProvider components Pagination configProvider virtual and dropdow
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -17969,7 +17999,7 @@ exports[`ConfigProvider components Pagination configProvider virtual and dropdow
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-0 ant-pagination-disabled ant-pagination-item-disabled"
|
||||
@ -17985,9 +18015,11 @@ exports[`ConfigProvider components Pagination configProvider virtual and dropdow
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -18009,7 +18041,7 @@ exports[`ConfigProvider components Pagination configProvider virtual and dropdow
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -18097,9 +18129,11 @@ exports[`ConfigProvider components Pagination normal 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -18121,7 +18155,7 @@ exports[`ConfigProvider components Pagination normal 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-0 ant-pagination-disabled ant-pagination-item-disabled"
|
||||
@ -18137,9 +18171,11 @@ exports[`ConfigProvider components Pagination normal 1`] = `
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -18161,7 +18197,7 @@ exports[`ConfigProvider components Pagination normal 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -18244,9 +18280,11 @@ exports[`ConfigProvider components Pagination normal 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -18268,7 +18306,7 @@ exports[`ConfigProvider components Pagination normal 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-0 ant-pagination-disabled ant-pagination-item-disabled"
|
||||
@ -18284,9 +18322,11 @@ exports[`ConfigProvider components Pagination normal 1`] = `
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -18308,7 +18348,7 @@ exports[`ConfigProvider components Pagination normal 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -18396,9 +18436,11 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
|
||||
class="prefix-Pagination-prev prefix-Pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="prefix-Pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -18420,7 +18462,7 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="prefix-Pagination-item prefix-Pagination-item-0 prefix-Pagination-disabled prefix-Pagination-item-disabled"
|
||||
@ -18436,9 +18478,11 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
|
||||
class="prefix-Pagination-next prefix-Pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="prefix-Pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -18460,7 +18504,7 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="prefix-Pagination-options"
|
||||
@ -18543,9 +18587,11 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
|
||||
class="prefix-Pagination-prev prefix-Pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="prefix-Pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -18567,7 +18613,7 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="prefix-Pagination-item prefix-Pagination-item-0 prefix-Pagination-disabled prefix-Pagination-item-disabled"
|
||||
@ -18583,9 +18629,11 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
|
||||
class="prefix-Pagination-next prefix-Pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="prefix-Pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -18607,7 +18655,7 @@ exports[`ConfigProvider components Pagination prefixCls 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="prefix-Pagination-options"
|
||||
|
@ -24,7 +24,9 @@ exports[`MonthPicker and WeekPicker render MonthPicker 1`] = `
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
«
|
||||
<span
|
||||
class="ant-picker-super-prev-icon"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
class="ant-picker-header-view"
|
||||
@ -41,7 +43,9 @@ exports[`MonthPicker and WeekPicker render MonthPicker 1`] = `
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
»
|
||||
<span
|
||||
class="ant-picker-super-next-icon"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
|
@ -92,6 +92,7 @@ The following APIs are shared by DatePicker, YearPicker, MonthPicker, RangePicke
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(date: moment, dateString: string) | - | |
|
||||
| onOk | callback when click ok button | function() | - | |
|
||||
| onPanelChange | Callback function for panel changing | function(value, mode) | - | |
|
||||
| showNow | Whether to show 'Now' button on panel when `showTime` is set | boolean | - | 4.4.0 |
|
||||
|
||||
### YearPicker
|
||||
|
||||
|
@ -94,6 +94,7 @@ import 'moment/locale/zh-cn';
|
||||
| onChange | 时间发生变化的回调 | function(date: moment, dateString: string) | - | |
|
||||
| onOk | 点击确定按钮的回调 | function() | - | |
|
||||
| onPanelChange | 日期面板变化时的回调 | function(value, mode) | - | |
|
||||
| showNow | 当设定了 `showTime` 的时候,面板是否显示“此刻”按钮 | boolean | - | 4.4.0 |
|
||||
|
||||
### YearPicker
|
||||
|
||||
|
@ -125,4 +125,13 @@ describe('Drawer', () => {
|
||||
);
|
||||
expect(wrapper2.find('button.forceRender').length).toBe(1);
|
||||
});
|
||||
|
||||
it('support closeIcon', () => {
|
||||
const wrapper = render(
|
||||
<Drawer visible closable closeIcon={<span>close</span>} width={400} getContainer={false}>
|
||||
Here is content of Drawer
|
||||
</Drawer>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -82,7 +82,7 @@ describe('Drawer', () => {
|
||||
expect(wrapper.instance().state.visible).toBe(true);
|
||||
});
|
||||
|
||||
it('destroyOnClose is true onClose', () => {
|
||||
it('dom should be removed after close when destroyOnClose is true', () => {
|
||||
const wrapper = mount(<DrawerEventTester destroyOnClose />);
|
||||
wrapper.find('button.ant-btn').simulate('click');
|
||||
expect(wrapper.find('.ant-drawer-wrapper-body').exists()).toBe(true);
|
||||
@ -94,6 +94,18 @@ describe('Drawer', () => {
|
||||
expect(wrapper.find('.ant-drawer-wrapper-body').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('dom should be existed after close when destroyOnClose is false', () => {
|
||||
const wrapper = mount(<DrawerEventTester />);
|
||||
wrapper.find('button.ant-btn').simulate('click');
|
||||
expect(wrapper.find('.ant-drawer-wrapper-body').exists()).toBe(true);
|
||||
|
||||
wrapper.setState({
|
||||
visible: false,
|
||||
});
|
||||
wrapper.find('.ant-drawer-wrapper-body').simulate('transitionend');
|
||||
expect(wrapper.find('.ant-drawer-wrapper-body').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('no mask and no closable', () => {
|
||||
const wrapper = mount(<DrawerEventTester destroyOnClose />);
|
||||
|
||||
|
@ -558,3 +558,49 @@ exports[`Drawer style/drawerStyle/headerStyle/bodyStyle should work 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Drawer support closeIcon 1`] = `
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-drawer ant-drawer-right"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-mask"
|
||||
/>
|
||||
<div
|
||||
class="ant-drawer-content-wrapper"
|
||||
style="transform:translateX(100%);-ms-transform:translateX(100%);width:400px"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-content"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-wrapper-body"
|
||||
>
|
||||
<div
|
||||
class="ant-drawer-header-no-title"
|
||||
>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="ant-drawer-close"
|
||||
style="--scroll-bar:0px"
|
||||
>
|
||||
<span>
|
||||
close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-drawer-body"
|
||||
>
|
||||
Here is content of Drawer
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -21,6 +21,7 @@ A Drawer is a panel that is typically overlaid on top of a page and slides in fr
|
||||
| Props | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| closable | Whether a close (x) button is visible on top right of the Drawer dialog or not. | boolean | true |
|
||||
| closeIcon | custom close icon | ReactNode | `<CloseOutlined />` |
|
||||
| destroyOnClose | Whether to unmount child components on closing drawer or not. | boolean | false |
|
||||
| forceRender | Prerender Drawer component forcely | boolean | false |
|
||||
| getContainer | Return the mounted node for Drawer. | HTMLElement \| `() => HTMLElement` \| Selectors \| false | 'body' |
|
||||
|
@ -21,6 +21,7 @@ const PlacementTypes = tuple('top', 'right', 'bottom', 'left');
|
||||
type placementType = typeof PlacementTypes[number];
|
||||
export interface DrawerProps {
|
||||
closable?: boolean;
|
||||
closeIcon?: React.ReactNode;
|
||||
destroyOnClose?: boolean;
|
||||
forceRender?: boolean;
|
||||
getContainer?: string | HTMLElement | getContainerFunc | false;
|
||||
@ -195,7 +196,7 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
|
||||
}
|
||||
|
||||
renderCloseIcon() {
|
||||
const { closable, prefixCls, onClose } = this.props;
|
||||
const { closable, closeIcon = <CloseOutlined />, prefixCls, onClose } = this.props;
|
||||
return (
|
||||
closable && (
|
||||
// eslint-disable-next-line react/button-has-type
|
||||
@ -209,7 +210,7 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
|
||||
} as any
|
||||
}
|
||||
>
|
||||
<CloseOutlined />
|
||||
{closeIcon}
|
||||
</button>
|
||||
)
|
||||
);
|
||||
@ -283,6 +284,7 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
|
||||
'zIndex',
|
||||
'style',
|
||||
'closable',
|
||||
'closeIcon',
|
||||
'destroyOnClose',
|
||||
'drawerStyle',
|
||||
'headerStyle',
|
||||
|
@ -20,6 +20,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/7z8NJQhFb/Drawer.svg
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| closable | 是否显示右上角的关闭按钮 | boolean | true |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | `<CloseOutlined />` |
|
||||
| destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false |
|
||||
| forceRender | 预渲染 Drawer 内元素 | boolean | false |
|
||||
| getContainer | 指定 Drawer 挂载的 HTML 节点, false 为挂载在当前 dom | HTMLElement \| `() => HTMLElement` \| Selectors \| false | 'body' |
|
||||
|
@ -1,5 +1,59 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/dropdown/demo/arrow.md correctly 1`] = `
|
||||
Array [
|
||||
<button
|
||||
class="ant-btn ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomLeft
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomCenter
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
bottomRight
|
||||
</span>
|
||||
</button>,
|
||||
<br />,
|
||||
<button
|
||||
class="ant-btn ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topLeft
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topCenter
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
class="ant-btn ant-dropdown-trigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
topRight
|
||||
</span>
|
||||
</button>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/dropdown/demo/basic.md correctly 1`] = `
|
||||
<a
|
||||
class="ant-dropdown-link ant-dropdown-trigger"
|
||||
|
75
components/dropdown/demo/arrow.md
Normal file
75
components/dropdown/demo/arrow.md
Normal file
@ -0,0 +1,75 @@
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 箭头
|
||||
en-US: Arrow
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以展示一个箭头。
|
||||
|
||||
## en-US
|
||||
|
||||
You could display an arrow.
|
||||
|
||||
```jsx
|
||||
import { Menu, Dropdown, Button } from 'antd';
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item>
|
||||
<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
|
||||
1st menu item
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">
|
||||
2nd menu item
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">
|
||||
3rd menu item
|
||||
</a>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<Dropdown overlay={menu} placement="bottomLeft" arrow>
|
||||
<Button>bottomLeft</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="bottomCenter" arrow>
|
||||
<Button>bottomCenter</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="bottomRight" arrow>
|
||||
<Button>bottomRight</Button>
|
||||
</Dropdown>
|
||||
<br />
|
||||
<Dropdown overlay={menu} placement="topLeft" arrow>
|
||||
<Button>topLeft</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="topCenter" arrow>
|
||||
<Button>topCenter</Button>
|
||||
</Dropdown>
|
||||
<Dropdown overlay={menu} placement="topRight" arrow>
|
||||
<Button>topRight</Button>
|
||||
</Dropdown>
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
```css
|
||||
#components-dropdown-demo-arrow .ant-btn {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.ant-row-rtl #components-dropdown-demo-arrow .ant-btn {
|
||||
margin-right: 0;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
```
|
@ -35,6 +35,7 @@ type Align = {
|
||||
};
|
||||
|
||||
export interface DropDownProps {
|
||||
arrow?: boolean;
|
||||
trigger?: ('click' | 'hover' | 'contextMenu')[];
|
||||
overlay: React.ReactElement | OverlayFunc;
|
||||
onVisibleChange?: (visible: boolean) => void;
|
||||
@ -130,6 +131,7 @@ const Dropdown: DropdownInterface = props => {
|
||||
};
|
||||
|
||||
const {
|
||||
arrow,
|
||||
prefixCls: customizePrefixCls,
|
||||
children,
|
||||
trigger,
|
||||
@ -160,6 +162,7 @@ const Dropdown: DropdownInterface = props => {
|
||||
|
||||
return (
|
||||
<RcDropdown
|
||||
arrow={arrow}
|
||||
alignPoint={alignPoint}
|
||||
{...props}
|
||||
overlayClassName={overlayClassNameCustomized}
|
||||
|
@ -17,7 +17,8 @@ When there are more than a few options to choose from, you can wrap them in a `D
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| disabled | Whether the dropdown menu is disabled | boolean | false | |
|
||||
| arrow | Whether the dropdown arrow should be visible | boolean | false | |
|
||||
| disabled | Whether the dropdown menu is disabled | boolean | - | |
|
||||
| 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). | Function(triggerNode) | `() => document.body` | |
|
||||
| overlay | The dropdown menu | [Menu](/components/menu) \| () => Menu | - | |
|
||||
| overlayClassName | Class name of the dropdown root element | string | - | |
|
||||
|
@ -21,7 +21,8 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| disabled | 菜单是否禁用 | boolean | false | |
|
||||
| arrow | 下拉框箭头是否显示 | boolean | false | |
|
||||
| disabled | 菜单是否禁用 | boolean | - | |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | Function(triggerNode) | `() => document.body` | |
|
||||
| overlay | 菜单 | [Menu](/components/menu) \| () => Menu | - | |
|
||||
| overlayClassName | 下拉根元素的类名称 | string | - | |
|
||||
|
@ -47,6 +47,76 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Offset the popover to account for the dropdown arrow
|
||||
&-show-arrow&-placement-topCenter,
|
||||
&-show-arrow&-placement-topLeft,
|
||||
&-show-arrow&-placement-topRight {
|
||||
padding-bottom: @popover-distance;
|
||||
}
|
||||
|
||||
&-show-arrow&-placement-bottomCenter,
|
||||
&-show-arrow&-placement-bottomLeft,
|
||||
&-show-arrow&-placement-bottomRight {
|
||||
padding-top: @popover-distance;
|
||||
}
|
||||
|
||||
// Arrows
|
||||
// .popover-arrow is outer, .popover-arrow:after is inner
|
||||
|
||||
&-arrow {
|
||||
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;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
&-placement-topCenter > &-arrow,
|
||||
&-placement-topLeft > &-arrow,
|
||||
&-placement-topRight > &-arrow {
|
||||
bottom: @popover-distance - @popover-arrow-width + 2.2px;
|
||||
border-top-color: transparent;
|
||||
border-right-color: @popover-bg;
|
||||
border-bottom-color: @popover-bg;
|
||||
border-left-color: transparent;
|
||||
box-shadow: 3px 3px 7px fade(@black, 7%);
|
||||
}
|
||||
&-placement-topCenter > &-arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
}
|
||||
&-placement-topLeft > &-arrow {
|
||||
left: 16px;
|
||||
}
|
||||
&-placement-topRight > &-arrow {
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
&-placement-bottomCenter > &-arrow,
|
||||
&-placement-bottomLeft > &-arrow,
|
||||
&-placement-bottomRight > &-arrow {
|
||||
top: @popover-distance - @popover-arrow-width + 2px;
|
||||
border-top-color: @popover-bg;
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: @popover-bg;
|
||||
box-shadow: -2px -2px 5px fade(@black, 6%);
|
||||
}
|
||||
&-placement-bottomCenter > &-arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
}
|
||||
&-placement-bottomLeft > &-arrow {
|
||||
left: 16px;
|
||||
}
|
||||
&-placement-bottomRight > &-arrow {
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
&-menu {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import omit from 'omit.js';
|
||||
import classNames from 'classnames';
|
||||
import FieldForm, { List } from 'rc-field-form';
|
||||
import { FormProps as RcFormProps } from 'rc-field-form/lib/Form';
|
||||
@ -8,7 +7,7 @@ import { ColProps } from '../grid/col';
|
||||
import { ConfigContext, ConfigConsumerProps } from '../config-provider';
|
||||
import { FormContext } from './context';
|
||||
import { FormLabelAlign } from './interface';
|
||||
import { useForm, FormInstance } from './util';
|
||||
import useForm, { FormInstance } from './hooks/useForm';
|
||||
import SizeContext, { SizeType, SizeContextProvider } from '../config-provider/SizeContext';
|
||||
|
||||
export type FormLayout = 'horizontal' | 'inline' | 'vertical';
|
||||
@ -31,21 +30,24 @@ const InternalForm: React.ForwardRefRenderFunction<unknown, FormProps> = (props,
|
||||
const contextSize = React.useContext(SizeContext);
|
||||
const { getPrefixCls, direction }: ConfigConsumerProps = React.useContext(ConfigContext);
|
||||
|
||||
const { name } = props;
|
||||
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className = '',
|
||||
size = contextSize,
|
||||
form,
|
||||
colon,
|
||||
name,
|
||||
labelAlign,
|
||||
labelCol,
|
||||
wrapperCol,
|
||||
prefixCls: customizePrefixCls,
|
||||
hideRequiredMark,
|
||||
className = '',
|
||||
layout = 'horizontal',
|
||||
size = contextSize,
|
||||
scrollToFirstError,
|
||||
onFinishFailed,
|
||||
...restFormProps
|
||||
} = props;
|
||||
|
||||
const prefixCls = getPrefixCls('form', customizePrefixCls);
|
||||
|
||||
const formClassName = classNames(
|
||||
@ -59,20 +61,9 @@ const InternalForm: React.ForwardRefRenderFunction<unknown, FormProps> = (props,
|
||||
className,
|
||||
);
|
||||
|
||||
const formProps = omit(props, [
|
||||
'prefixCls',
|
||||
'className',
|
||||
'layout',
|
||||
'hideRequiredMark',
|
||||
'wrapperCol',
|
||||
'labelAlign',
|
||||
'labelCol',
|
||||
'colon',
|
||||
'scrollToFirstError',
|
||||
]);
|
||||
|
||||
const [wrapForm] = useForm(form);
|
||||
wrapForm.__INTERNAL__.name = name;
|
||||
const { __INTERNAL__ } = wrapForm;
|
||||
__INTERNAL__.name = name;
|
||||
|
||||
const formContextValue = React.useMemo(
|
||||
() => ({
|
||||
@ -82,6 +73,7 @@ const InternalForm: React.ForwardRefRenderFunction<unknown, FormProps> = (props,
|
||||
wrapperCol,
|
||||
vertical: layout === 'vertical',
|
||||
colon,
|
||||
itemRef: __INTERNAL__.itemRef,
|
||||
}),
|
||||
[name, labelAlign, labelCol, wrapperCol, layout, colon],
|
||||
);
|
||||
@ -100,12 +92,10 @@ const InternalForm: React.ForwardRefRenderFunction<unknown, FormProps> = (props,
|
||||
|
||||
return (
|
||||
<SizeContextProvider size={size}>
|
||||
<FormContext.Provider
|
||||
value={formContextValue}
|
||||
>
|
||||
<FormContext.Provider value={formContextValue}>
|
||||
<FieldForm
|
||||
id={name}
|
||||
{...formProps}
|
||||
{...restFormProps}
|
||||
onFinishFailed={onInternalFinishFailed}
|
||||
form={wrapForm}
|
||||
className={formClassName}
|
||||
|
@ -5,6 +5,7 @@ import { Field, FormInstance } from 'rc-field-form';
|
||||
import { FieldProps } from 'rc-field-form/lib/Field';
|
||||
import FieldContext from 'rc-field-form/lib/FieldContext';
|
||||
import { Meta, NamePath } from 'rc-field-form/lib/interface';
|
||||
import { supportRef } from 'rc-util/lib/ref';
|
||||
import omit from 'omit.js';
|
||||
import Row from '../grid/row';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
@ -13,8 +14,10 @@ import devWarning from '../_util/devWarning';
|
||||
import FormItemLabel, { FormItemLabelProps } from './FormItemLabel';
|
||||
import FormItemInput, { FormItemInputProps } from './FormItemInput';
|
||||
import { FormContext, FormItemContext } from './context';
|
||||
import { toArray, getFieldId, useFrameState } from './util';
|
||||
import { toArray, getFieldId } from './util';
|
||||
import { cloneElement, isValidElement } from '../_util/reactNode';
|
||||
import useFrameState from './hooks/useFrameState';
|
||||
import useItemRef from './hooks/useItemRef';
|
||||
|
||||
const ValidateStatuses = tuple('success', 'warning', 'error', 'validating', '');
|
||||
export type ValidateStatus = typeof ValidateStatuses[number];
|
||||
@ -46,6 +49,7 @@ export interface FormItemProps extends FormItemLabelProps, FormItemInputProps, R
|
||||
hasFeedback?: boolean;
|
||||
validateStatus?: ValidateStatus;
|
||||
required?: boolean;
|
||||
hidden?: boolean;
|
||||
|
||||
/** Auto passed by List render props. User should not use this. */
|
||||
fieldKey?: React.Key | React.Key[];
|
||||
@ -77,11 +81,12 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
||||
label,
|
||||
trigger = 'onChange',
|
||||
validateTrigger,
|
||||
hidden,
|
||||
...restProps
|
||||
} = props;
|
||||
const destroyRef = React.useRef(false);
|
||||
const { getPrefixCls } = React.useContext(ConfigContext);
|
||||
const formContext = React.useContext(FormContext);
|
||||
const { name: formName } = React.useContext(FormContext);
|
||||
const { updateItemErrors } = React.useContext(FormItemContext);
|
||||
const [domErrorVisible, innerSetDomErrorVisible] = React.useState(!!help);
|
||||
const prevValidateStatusRef = React.useRef<ValidateStatus | undefined>(validateStatus);
|
||||
@ -97,7 +102,6 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
const { name: formName } = formContext;
|
||||
const hasName = hasValidName(name);
|
||||
|
||||
// Cache Field NamePath
|
||||
@ -126,6 +130,9 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
||||
}
|
||||
};
|
||||
|
||||
// ===================== Children Ref =====================
|
||||
const getItemRef = useItemRef();
|
||||
|
||||
function renderLayout(
|
||||
baseChildren: React.ReactNode,
|
||||
fieldId?: string,
|
||||
@ -179,6 +186,7 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
||||
[`${prefixCls}-item-has-error-leave`]:
|
||||
!help && domErrorVisible && prevValidateStatusRef.current === 'error',
|
||||
[`${prefixCls}-item-is-validating`]: mergedValidateStatus === 'validating',
|
||||
[`${prefixCls}-hidden`]: hidden,
|
||||
};
|
||||
|
||||
// ======================= Children =======================
|
||||
@ -323,6 +331,10 @@ function FormItem(props: FormItemProps): React.ReactElement {
|
||||
childProps.id = fieldId;
|
||||
}
|
||||
|
||||
if (supportRef(children)) {
|
||||
childProps.ref = getItemRef(mergedName, children);
|
||||
}
|
||||
|
||||
// We should keep user origin event handler
|
||||
const triggers = new Set<string>([
|
||||
...toArray(trigger),
|
||||
|
@ -10,7 +10,7 @@ import CSSMotion from 'rc-animate/lib/CSSMotion';
|
||||
import Col, { ColProps } from '../grid/col';
|
||||
import { ValidateStatus } from './FormItem';
|
||||
import { FormContext } from './context';
|
||||
import { useCacheErrors } from './util';
|
||||
import useCacheErrors from './hooks/useCacheErrors';
|
||||
|
||||
interface FormItemInputMiscProps {
|
||||
prefixCls: string;
|
||||
|
@ -2196,6 +2196,84 @@ exports[`renders ./components/form/demo/normal-login.md correctly 1`] = `
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/form/demo/ref-item.md correctly 1`] = `
|
||||
<form
|
||||
class="ant-form ant-form-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="test"
|
||||
title="test"
|
||||
>
|
||||
test
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
id="test"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
id="list_0"
|
||||
type="text"
|
||||
value="light"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="ant-btn ant-btn-button"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Focus Form.Item
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="ant-btn"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Focus Form.List
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/form/demo/register.md correctly 1`] = `
|
||||
<form
|
||||
class="ant-form ant-form-horizontal"
|
||||
|
@ -1,5 +1,34 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Form Form item hidden 1`] = `
|
||||
<form
|
||||
class="ant-form ant-form-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item ant-form-hidden"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
class="ant-input"
|
||||
id="light"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`Form Form.Item should support data-*、aria-* and custom attribute 1`] = `
|
||||
<form
|
||||
class="ant-form ant-form-horizontal"
|
||||
|
@ -597,4 +597,15 @@ describe('Form', () => {
|
||||
|
||||
expect(wrapper.find('input').prop('onBlur')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Form item hidden', () => {
|
||||
const wrapper = mount(
|
||||
<Form>
|
||||
<Form.Item name="light" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
expect(wrapper).toMatchRenderedSnapshot();
|
||||
});
|
||||
});
|
||||
|
91
components/form/__tests__/ref.test.tsx
Normal file
91
components/form/__tests__/ref.test.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
/* eslint-disable react/jsx-key */
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Form from '..';
|
||||
import Input from '../../input';
|
||||
import Button from '../../button';
|
||||
|
||||
describe('Form.Ref', () => {
|
||||
const Test = ({
|
||||
onRef,
|
||||
show,
|
||||
}: {
|
||||
onRef: (node: React.ReactElement, originRef: React.RefObject<any>) => void;
|
||||
show?: boolean;
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const removeRef = React.useRef<any>();
|
||||
const testRef = React.useRef<any>();
|
||||
const listRef = React.useRef<any>();
|
||||
|
||||
return (
|
||||
<Form form={form} initialValues={{ list: ['light'] }}>
|
||||
{show && (
|
||||
<Form.Item name="remove" label="remove">
|
||||
<Input ref={removeRef} />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item name="test" label="test">
|
||||
<Input ref={testRef} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List name="list">
|
||||
{fields =>
|
||||
fields.map(field => (
|
||||
<Form.Item {...field}>
|
||||
<Input ref={listRef} />
|
||||
</Form.Item>
|
||||
))
|
||||
}
|
||||
</Form.List>
|
||||
|
||||
<Button
|
||||
className="ref-item"
|
||||
onClick={() => {
|
||||
onRef(form.getFieldInstance('test'), testRef.current);
|
||||
}}
|
||||
>
|
||||
Form.Item
|
||||
</Button>
|
||||
<Button
|
||||
className="ref-list"
|
||||
onClick={() => {
|
||||
onRef(form.getFieldInstance(['list', 0]), listRef.current);
|
||||
}}
|
||||
>
|
||||
Form.List
|
||||
</Button>
|
||||
<Button
|
||||
className="ref-remove"
|
||||
onClick={() => {
|
||||
onRef(form.getFieldInstance('remove'), removeRef.current);
|
||||
}}
|
||||
>
|
||||
Removed
|
||||
</Button>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
it('should ref work', () => {
|
||||
const onRef = jest.fn();
|
||||
const wrapper = mount(<Test onRef={onRef} show />);
|
||||
|
||||
wrapper.find('.ref-item').last().simulate('click');
|
||||
expect(onRef).toHaveBeenCalled();
|
||||
expect(onRef.mock.calls[0][0]).toBe(onRef.mock.calls[0][1]);
|
||||
|
||||
onRef.mockReset();
|
||||
wrapper.find('.ref-list').last().simulate('click');
|
||||
expect(onRef).toHaveBeenCalled();
|
||||
expect(onRef.mock.calls[0][0]).toBe(onRef.mock.calls[0][1]);
|
||||
|
||||
onRef.mockReset();
|
||||
wrapper.setProps({ show: false });
|
||||
wrapper.update();
|
||||
wrapper.find('.ref-remove').last().simulate('click');
|
||||
expect(onRef).toHaveBeenCalledWith(undefined, null);
|
||||
});
|
||||
});
|
@ -16,11 +16,13 @@ export interface FormContextProps {
|
||||
labelAlign?: FormLabelAlign;
|
||||
labelCol?: ColProps;
|
||||
wrapperCol?: ColProps;
|
||||
itemRef: (name: (string | number)[]) => (node: React.ReactElement) => void;
|
||||
}
|
||||
|
||||
export const FormContext = React.createContext<FormContextProps>({
|
||||
labelAlign: 'right',
|
||||
vertical: false,
|
||||
itemRef: (() => {}) as any,
|
||||
});
|
||||
|
||||
/**
|
||||
|
61
components/form/demo/ref-item.md
Normal file
61
components/form/demo/ref-item.md
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
order: 999999
|
||||
title:
|
||||
zh-CN: 引用字段
|
||||
en-US: Ref item
|
||||
debug: true
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
请优先使用 `ref`!
|
||||
|
||||
## en-US
|
||||
|
||||
Use `ref` first!
|
||||
|
||||
```jsx
|
||||
import React from 'react';
|
||||
import { Button, Form, Input } from 'antd';
|
||||
|
||||
const Demo = () => {
|
||||
const [form] = Form.useForm();
|
||||
const ref = React.useRef();
|
||||
|
||||
return (
|
||||
<Form form={form} initialValues={{ list: ['light'] }}>
|
||||
<Form.Item name="test" label="test">
|
||||
<Input ref={ref} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List name="list">
|
||||
{fields =>
|
||||
fields.map(field => (
|
||||
<Form.Item key={field.key} {...field}>
|
||||
<Input ref={ref} />
|
||||
</Form.Item>
|
||||
))
|
||||
}
|
||||
</Form.List>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
form.getFieldInstance('test').focus();
|
||||
}}
|
||||
>
|
||||
Focus Form.Item
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
form.getFieldInstance(['list', 0]).focus();
|
||||
}}
|
||||
>
|
||||
Focus Form.List
|
||||
</Button>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
48
components/form/hooks/useCacheErrors.ts
Normal file
48
components/form/hooks/useCacheErrors.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* Always debounce error to avoid [error -> null -> error] blink
|
||||
*/
|
||||
export default function useCacheErrors(
|
||||
errors: React.ReactNode[],
|
||||
changeTrigger: (visible: boolean) => void,
|
||||
directly: boolean,
|
||||
): [boolean, React.ReactNode[]] {
|
||||
const cacheRef = React.useRef({
|
||||
errors,
|
||||
visible: !!errors.length,
|
||||
});
|
||||
|
||||
const [, forceUpdate] = React.useState({});
|
||||
|
||||
const update = () => {
|
||||
const prevVisible = cacheRef.current.visible;
|
||||
const newVisible = !!errors.length;
|
||||
|
||||
const prevErrors = cacheRef.current.errors;
|
||||
cacheRef.current.errors = errors;
|
||||
cacheRef.current.visible = newVisible;
|
||||
|
||||
if (prevVisible !== newVisible) {
|
||||
changeTrigger(newVisible);
|
||||
} else if (
|
||||
prevErrors.length !== errors.length ||
|
||||
prevErrors.some((prevErr, index) => prevErr !== errors[index])
|
||||
) {
|
||||
forceUpdate({});
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!directly) {
|
||||
const timeout = setTimeout(update, 10);
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
}, [errors]);
|
||||
|
||||
if (directly) {
|
||||
update();
|
||||
}
|
||||
|
||||
return [cacheRef.current.visible, cacheRef.current.errors];
|
||||
}
|
64
components/form/hooks/useForm.ts
Normal file
64
components/form/hooks/useForm.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { useRef, useMemo } from 'react';
|
||||
import { useForm as useRcForm, FormInstance as RcFormInstance } from 'rc-field-form';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { ScrollOptions, NamePath, InternalNamePath } from '../interface';
|
||||
import { toArray, getFieldId } from '../util';
|
||||
|
||||
export interface FormInstance extends RcFormInstance {
|
||||
scrollToField: (name: NamePath, options?: ScrollOptions) => void;
|
||||
/** This is an internal usage. Do not use in your prod */
|
||||
__INTERNAL__: {
|
||||
/** No! Do not use this in your code! */
|
||||
name?: string;
|
||||
/** No! Do not use this in your code! */
|
||||
itemRef: (name: InternalNamePath) => (node: React.ReactElement) => void;
|
||||
};
|
||||
getFieldInstance: (name: NamePath) => any;
|
||||
}
|
||||
|
||||
function toNamePathStr(name: NamePath) {
|
||||
const namePath = toArray(name);
|
||||
return namePath.join('_');
|
||||
}
|
||||
|
||||
export default function useForm(form?: FormInstance): [FormInstance] {
|
||||
const [rcForm] = useRcForm();
|
||||
const itemsRef = useRef<Record<string, React.ReactElement>>({});
|
||||
|
||||
const wrapForm: FormInstance = useMemo(
|
||||
() =>
|
||||
form || {
|
||||
...rcForm,
|
||||
__INTERNAL__: {
|
||||
itemRef: (name: InternalNamePath) => (node: React.ReactElement) => {
|
||||
const namePathStr = toNamePathStr(name);
|
||||
if (node) {
|
||||
itemsRef.current[namePathStr] = node;
|
||||
} else {
|
||||
delete itemsRef.current[namePathStr];
|
||||
}
|
||||
},
|
||||
},
|
||||
scrollToField: (name: string, options: ScrollOptions = {}) => {
|
||||
const namePath = toArray(name);
|
||||
const fieldId = getFieldId(namePath, wrapForm.__INTERNAL__.name);
|
||||
const node: HTMLElement | null = fieldId ? document.getElementById(fieldId) : null;
|
||||
|
||||
if (node) {
|
||||
scrollIntoView(node, {
|
||||
scrollMode: 'if-needed',
|
||||
block: 'nearest',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
},
|
||||
getFieldInstance: (name: string) => {
|
||||
const namePathStr = toNamePathStr(name);
|
||||
return itemsRef.current[namePathStr];
|
||||
},
|
||||
},
|
||||
[form, rcForm],
|
||||
);
|
||||
|
||||
return [wrapForm];
|
||||
}
|
48
components/form/hooks/useFrameState.ts
Normal file
48
components/form/hooks/useFrameState.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import * as React from 'react';
|
||||
import { useRef } from 'react';
|
||||
import raf from 'raf';
|
||||
|
||||
type Updater<ValueType> = (prev?: ValueType) => ValueType;
|
||||
|
||||
export default function useFrameState<ValueType>(
|
||||
defaultValue: ValueType,
|
||||
): [ValueType, (updater: Updater<ValueType>) => void] {
|
||||
const [value, setValue] = React.useState(defaultValue);
|
||||
const frameRef = useRef<number | null>(null);
|
||||
const batchRef = useRef<Updater<ValueType>[]>([]);
|
||||
const destroyRef = useRef(false);
|
||||
|
||||
React.useEffect(
|
||||
() => () => {
|
||||
destroyRef.current = true;
|
||||
raf.cancel(frameRef.current!);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
function setFrameValue(updater: Updater<ValueType>) {
|
||||
if (destroyRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frameRef.current === null) {
|
||||
batchRef.current = [];
|
||||
frameRef.current = raf(() => {
|
||||
frameRef.current = null;
|
||||
setValue(prevValue => {
|
||||
let current = prevValue;
|
||||
|
||||
batchRef.current.forEach(func => {
|
||||
current = func(current);
|
||||
});
|
||||
|
||||
return current;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
batchRef.current.push(updater);
|
||||
}
|
||||
|
||||
return [value, setFrameValue];
|
||||
}
|
28
components/form/hooks/useItemRef.ts
Normal file
28
components/form/hooks/useItemRef.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import * as React from 'react';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import { FormContext } from '../context';
|
||||
import { InternalNamePath } from '../interface';
|
||||
|
||||
export default function useItemRef() {
|
||||
const { itemRef } = React.useContext(FormContext);
|
||||
const cacheRef = React.useRef<{
|
||||
name?: string;
|
||||
originRef?: React.Ref<any>;
|
||||
ref?: React.Ref<any>;
|
||||
}>({});
|
||||
|
||||
function getRef(name: InternalNamePath, children: any) {
|
||||
const childrenRef: React.Ref<React.ReactElement> =
|
||||
children && typeof children === 'object' && children.ref;
|
||||
const nameStr = name.join('_');
|
||||
if (cacheRef.current.name !== nameStr || cacheRef.current.originRef !== childrenRef) {
|
||||
cacheRef.current.name = nameStr;
|
||||
cacheRef.current.originRef = childrenRef;
|
||||
cacheRef.current.ref = composeRef(itemRef(name), childrenRef);
|
||||
}
|
||||
|
||||
return cacheRef.current.ref;
|
||||
}
|
||||
|
||||
return getRef;
|
||||
}
|
@ -29,6 +29,7 @@ High performance Form component with data scope management. Including data colle
|
||||
| labelCol | label layout, like `<Col>` component. Set `span` `offset` value like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` | [object](/components/grid/#Col) | - | |
|
||||
| layout | Form layout | `horizontal` \| `vertical` \| `inline` | `horizontal` | |
|
||||
| name | Form name. Will be the prefix of Field `id` | string | - | |
|
||||
| preserve | Keep field value even when field removed | boolean | true | 4.4.0 |
|
||||
| scrollToFirstError | Auto scroll to first failed field when submit | boolean | false | |
|
||||
| size | Set field component size (antd components only) | `small` \| `middle` \| `large` | - | |
|
||||
| validateMessages | Validation prompt template, description [see below](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - | |
|
||||
@ -86,6 +87,7 @@ Form field component for data bidirectional binding, validation, layout, and so
|
||||
| labelCol | The layout of label. You can set `span` `offset` to something like `{span: 3, offset: 12}` or `sm: {span: 3, offset: 12}` same as with `<Col>`. You can set `labelCol` on Form. If both exists, use Item first | [object](/components/grid/#Col) | - | |
|
||||
| name | Field name, support array | [NamePath](#NamePath) | - | |
|
||||
| normalize | Normalize value from component value before passing to Form instance | (value, prevValue, prevValues) => any | - | |
|
||||
| preserve | Keep field value even when field removed | boolean | true | 4.4.0 |
|
||||
| required | Display required style. It will be generated by the validation rule | boolean | false | |
|
||||
| rules | Rules for field validation. Click [here](#components-form-demo-basic) to see an example | [Rule](#Rule)[] | - | |
|
||||
| shouldUpdate | Custom field update logic. See [below](#shouldUpdate) | boolean \| (prevValue, curValue) => boolean | false | |
|
||||
@ -95,6 +97,7 @@ Form field component for data bidirectional binding, validation, layout, and so
|
||||
| validateTrigger | When to validate the value of children node | string \| string[] | `onChange` | |
|
||||
| valuePropName | Props of children node, for example, the prop of Switch is 'checked'. This prop is an encapsulation of `getValueProps`, which will be invalid after customizing `getValueProps` | string | `value` | |
|
||||
| wrapperCol | The layout for input controls, same as `labelCol`. You can set `wrapperCol` on Form. If both exists, use Item first | [object](/components/grid/#Col) | - | |
|
||||
| hidden | whether to hide Form.Item (still collect and validate value) | boolean | false | |
|
||||
|
||||
After wrapped by `Form.Item` with `name` property, `value`(or other property defined by `valuePropName`) `onChange`(or other property defined by `trigger`) props will be added to form controls, the flow of form data will be handled by Form which will cause:
|
||||
|
||||
@ -185,21 +188,22 @@ Provide linkage between forms. If a sub form with `name` prop update, it will au
|
||||
|
||||
### FormInstance
|
||||
|
||||
| Name | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| getFieldValue | Get the value by the field name | (name: [NamePath](#NamePath)) => any |
|
||||
| getFieldsValue | Get values by a set of field names. Return according to the corresponding structure | (nameList?: [NamePath](#NamePath)[], filterFunc?: (meta: { touched: boolean, validating: boolean }) => boolean) => any |
|
||||
| getFieldError | Get the error messages by the field name | (name: [NamePath](#NamePath)) => string[] |
|
||||
| getFieldsError | Get the error messages by the fields name. Return as an array | (nameList?: [NamePath](#NamePath)[]) => FieldError[] |
|
||||
| isFieldTouched | Check if a field has been operated | (name: [NamePath](#NamePath)) => boolean |
|
||||
| isFieldsTouched | Check if fields have been operated. Check if all fields is touched when `allTouched` is `true` | (nameList?: [NamePath](#NamePath)[], allTouched?: boolean) => boolean |
|
||||
| isFieldValidating | Check fields if is in validating | (name: [NamePath](#NamePath)) => boolean |
|
||||
| resetFields | Reset fields to `initialValues` | (fields?: [NamePath](#NamePath)[]) => void |
|
||||
| scrollToField | Scroll to field position | (name: [NamePath](#NamePath), options: [[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void |
|
||||
| setFields | Set fields status | (fields: [FieldData](#FieldData)[]) => void |
|
||||
| setFieldsValue | Set fields value | (values) => void |
|
||||
| submit | Submit the form. It's same as click `submit` button | () => void |
|
||||
| validateFields | Validate fields | (nameList?: [NamePath](#NamePath)[]) => Promise |
|
||||
| Name | Description | Type | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| getFieldInstance | Get field instance | (name: [NamePath](#NamePath)) => any | 4.4.0 |
|
||||
| getFieldValue | Get the value by the field name | (name: [NamePath](#NamePath)) => any | |
|
||||
| getFieldsValue | Get values by a set of field names. Return according to the corresponding structure | (nameList?: [NamePath](#NamePath)[], filterFunc?: (meta: { touched: boolean, validating: boolean }) => boolean) => any | |
|
||||
| getFieldError | Get the error messages by the field name | (name: [NamePath](#NamePath)) => string[] | |
|
||||
| getFieldsError | Get the error messages by the fields name. Return as an array | (nameList?: [NamePath](#NamePath)[]) => FieldError[] | |
|
||||
| isFieldTouched | Check if a field has been operated | (name: [NamePath](#NamePath)) => boolean | |
|
||||
| isFieldsTouched | Check if fields have been operated. Check if all fields is touched when `allTouched` is `true` | (nameList?: [NamePath](#NamePath)[], allTouched?: boolean) => boolean | |
|
||||
| isFieldValidating | Check fields if is in validating | (name: [NamePath](#NamePath)) => boolean | |
|
||||
| resetFields | Reset fields to `initialValues` | (fields?: [NamePath](#NamePath)[]) => void | |
|
||||
| scrollToField | Scroll to field position | (name: [NamePath](#NamePath), options: [[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void | |
|
||||
| setFields | Set fields status | (fields: [FieldData](#FieldData)[]) => void | |
|
||||
| setFieldsValue | Set fields value | (values) => void | |
|
||||
| submit | Submit the form. It's same as click `submit` button | () => void | |
|
||||
| validateFields | Validate fields | (nameList?: [NamePath](#NamePath)[]) => Promise | |
|
||||
|
||||
#### validateFields return sample
|
||||
|
||||
|
@ -30,6 +30,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/ORmcdeaoO/Form.svg
|
||||
| labelCol | label 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}` 或 `sm: {span: 3, offset: 12}` | [object](/components/grid/#Col) | - | |
|
||||
| layout | 表单布局 | `horizontal` \| `vertical` \| `inline` | `horizontal` | |
|
||||
| name | 表单名称,会作为表单字段 `id` 前缀使用 | string | - | |
|
||||
| preserve | 当字段被删除时保留字段值 | boolean | true | 4.4.0 |
|
||||
| scrollToFirstError | 提交失败自动滚动到第一个错误字段 | boolean | false | |
|
||||
| size | 设置字段组件的尺寸(仅限 antd 组件) | `small` \| `middle` \| `large` | - | |
|
||||
| validateMessages | 验证提示模板,说明[见下](#validateMessages) | [ValidateMessages](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) | - | |
|
||||
@ -86,6 +87,7 @@ const validateMessages = {
|
||||
| labelAlign | 标签文本对齐方式 | `left` \| `right` | `right` | |
|
||||
| labelCol | `label` 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}` 或 `sm: {span: 3, offset: 12}`。你可以通过 Form 的 `labelCol` 进行统一设置。当和 Form 同时设置时,以 Item 为准 | [object](/components/grid/#Col) | - | |
|
||||
| name | 字段名,支持数组 | [NamePath](#NamePath) | - | |
|
||||
| preserve | 当字段被删除时保留字段值 | boolean | true | 4.4.0 |
|
||||
| normalize | 组件获取值后进行转换,再放入 Form 中 | (value, prevValue, prevValues) => any | - | |
|
||||
| required | 必填样式设置。如不设置,则会根据校验规则自动生成 | boolean | false | |
|
||||
| rules | 校验规则,设置字段的校验逻辑。点击[此处](#components-form-demo-basic)查看示例 | [Rule](#Rule)[] | - | |
|
||||
@ -96,6 +98,7 @@ const validateMessages = {
|
||||
| validateTrigger | 设置字段校验的时机 | string \| string[] | `onChange` | |
|
||||
| valuePropName | 子节点的值的属性,如 Switch 的是 'checked'。该属性为 `getValueProps` 的封装,自定义 `getValueProps` 后会失效 | string | `value` | |
|
||||
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 `labelCol`。你可以通过 Form 的 `wrapperCol` 进行统一设置。当和 Form 同时设置时,以 Item 为准。 | [object](/components/grid/#Col) | - | |
|
||||
| hidden | 是否隐藏字段(依然会收集和校验字段) | boolean | false | |
|
||||
|
||||
被设置了 `name` 属性的 `Form.Item` 包装的控件,表单控件会自动添加 `value`(或 `valuePropName` 指定的其他属性) `onChange`(或 `trigger` 指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
|
||||
|
||||
@ -186,21 +189,22 @@ Form 通过增量更新方式,只更新被修改的字段相关组件以达到
|
||||
|
||||
### FormInstance
|
||||
|
||||
| 名称 | 说明 | 类型 |
|
||||
| --- | --- | --- |
|
||||
| getFieldValue | 获取对应字段名的值 | (name: [NamePath](#NamePath)) => any |
|
||||
| getFieldsValue | 获取一组字段名对应的值,会按照对应结构返回 | (nameList?: [NamePath](#NamePath)[], filterFunc?: (meta: { touched: boolean, validating: boolean }) => boolean) => any |
|
||||
| getFieldError | 获取对应字段名的错误信息 | (name: [NamePath](#NamePath)) => string[] |
|
||||
| getFieldsError | 获取一组字段名对应的错误信息,返回为数组形式 | (nameList?: [NamePath](#NamePath)[]) => FieldError[] |
|
||||
| isFieldTouched | 检查对应字段是否被用户操作过 | (name: [NamePath](#NamePath)) => boolean |
|
||||
| isFieldsTouched | 检查一组字段是否被用户操作过,`allTouched` 为 `true` 时检查是否所有字段都被操作过 | (nameList?: [NamePath](#NamePath)[], allTouched?: boolean) => boolean |
|
||||
| isFieldValidating | 检查一组字段是否正在校验 | (name: [NamePath](#NamePath)) => boolean |
|
||||
| resetFields | 重置一组字段到 `initialValues` | (fields?: [NamePath](#NamePath)[]) => void |
|
||||
| scrollToField | 滚动到对应字段位置 | (name: [NamePath](#NamePath), options: [[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void |
|
||||
| setFields | 设置一组字段状态 | (fields: [FieldData](#FieldData)[]) => void |
|
||||
| setFieldsValue | 设置表单的值 | (values) => void |
|
||||
| submit | 提交表单,与点击 `submit` 按钮效果相同 | () => void |
|
||||
| validateFields | 触发表单验证 | (nameList?: [NamePath](#NamePath)[]) => Promise |
|
||||
| 名称 | 说明 | 类型 | 版本 |
|
||||
| --- | --- | --- | --- |
|
||||
| getFieldInstance | 获取对应字段示例 | (name: [NamePath](#NamePath)) => any | 4.4.0 |
|
||||
| getFieldValue | 获取对应字段名的值 | (name: [NamePath](#NamePath)) => any | |
|
||||
| getFieldsValue | 获取一组字段名对应的值,会按照对应结构返回 | (nameList?: [NamePath](#NamePath)[], filterFunc?: (meta: { touched: boolean, validating: boolean }) => boolean) => any | |
|
||||
| getFieldError | 获取对应字段名的错误信息 | (name: [NamePath](#NamePath)) => string[] | |
|
||||
| getFieldsError | 获取一组字段名对应的错误信息,返回为数组形式 | (nameList?: [NamePath](#NamePath)[]) => FieldError[] | |
|
||||
| isFieldTouched | 检查对应字段是否被用户操作过 | (name: [NamePath](#NamePath)) => boolean | |
|
||||
| isFieldsTouched | 检查一组字段是否被用户操作过,`allTouched` 为 `true` 时检查是否所有字段都被操作过 | (nameList?: [NamePath](#NamePath)[], allTouched?: boolean) => boolean | |
|
||||
| isFieldValidating | 检查一组字段是否正在校验 | (name: [NamePath](#NamePath)) => boolean | |
|
||||
| resetFields | 重置一组字段到 `initialValues` | (fields?: [NamePath](#NamePath)[]) => void | |
|
||||
| scrollToField | 滚动到对应字段位置 | (name: [NamePath](#NamePath), options: [[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void | |
|
||||
| setFields | 设置一组字段状态 | (fields: [FieldData](#FieldData)[]) => void | |
|
||||
| setFieldsValue | 设置表单的值 | (values) => void | |
|
||||
| submit | 提交表单,与点击 `submit` 按钮效果相同 | () => void | |
|
||||
| validateFields | 触发表单验证 | (nameList?: [NamePath](#NamePath)[]) => Promise | |
|
||||
|
||||
#### validateFields 返回示例
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
export { Options as ScrollOptions } from 'scroll-into-view-if-needed';
|
||||
export type FormLabelAlign = 'left' | 'right';
|
||||
export { Store, StoreValue } from 'rc-field-form/lib/interface';
|
||||
export { Store, StoreValue, NamePath, InternalNamePath } from 'rc-field-form/lib/interface';
|
||||
|
@ -66,6 +66,10 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// = Label =
|
||||
// ==============================================================
|
||||
|
@ -1,61 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import raf from 'raf';
|
||||
import { useForm as useRcForm, FormInstance as RcFormInstance } from 'rc-field-form';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { ScrollOptions } from './interface';
|
||||
|
||||
type InternalNamePath = (string | number)[];
|
||||
|
||||
/**
|
||||
* Always debounce error to avoid [error -> null -> error] blink
|
||||
*/
|
||||
export function useCacheErrors(
|
||||
errors: React.ReactNode[],
|
||||
changeTrigger: (visible: boolean) => void,
|
||||
directly: boolean,
|
||||
): [boolean, React.ReactNode[]] {
|
||||
const cacheRef = React.useRef({
|
||||
errors,
|
||||
visible: !!errors.length,
|
||||
});
|
||||
|
||||
const [, forceUpdate] = React.useState({});
|
||||
|
||||
const update = (newErrors: React.ReactNode[]) => {
|
||||
const prevVisible = cacheRef.current.visible;
|
||||
const newVisible = !!newErrors.length;
|
||||
|
||||
const prevErrors = cacheRef.current.errors;
|
||||
cacheRef.current.errors = newErrors;
|
||||
cacheRef.current.visible = newVisible;
|
||||
|
||||
if (prevVisible !== newVisible) {
|
||||
changeTrigger(newVisible);
|
||||
} else if (
|
||||
prevErrors.length !== newErrors.length ||
|
||||
prevErrors.some((prevErr, index) => prevErr !== newErrors[index])
|
||||
) {
|
||||
forceUpdate({});
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!directly) {
|
||||
const timeout = setTimeout(() => {
|
||||
update(errors);
|
||||
}, 10);
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
}, [errors]);
|
||||
|
||||
if (directly) {
|
||||
update(errors);
|
||||
}
|
||||
|
||||
return [cacheRef.current.visible, cacheRef.current.errors];
|
||||
}
|
||||
import { InternalNamePath } from './interface';
|
||||
|
||||
export function toArray<T>(candidate?: T | T[] | false): T[] {
|
||||
if (candidate === undefined || candidate === false) return [];
|
||||
@ -69,83 +12,3 @@ export function getFieldId(namePath: InternalNamePath, formName?: string): strin
|
||||
const mergedId = namePath.join('_');
|
||||
return formName ? `${formName}_${mergedId}` : mergedId;
|
||||
}
|
||||
|
||||
export interface FormInstance extends RcFormInstance {
|
||||
scrollToField: (name: string | number | InternalNamePath, options?: ScrollOptions) => void;
|
||||
__INTERNAL__: {
|
||||
name?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function useForm(form?: FormInstance): [FormInstance] {
|
||||
const [rcForm] = useRcForm();
|
||||
|
||||
const wrapForm: FormInstance = React.useMemo(
|
||||
() =>
|
||||
form || {
|
||||
...rcForm,
|
||||
__INTERNAL__: {},
|
||||
scrollToField: (name: string, options: ScrollOptions = {}) => {
|
||||
const namePath = toArray(name);
|
||||
const fieldId = getFieldId(namePath, wrapForm.__INTERNAL__.name);
|
||||
const node: HTMLElement | null = fieldId ? document.getElementById(fieldId) : null;
|
||||
|
||||
if (node) {
|
||||
scrollIntoView(node, {
|
||||
scrollMode: 'if-needed',
|
||||
block: 'nearest',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
[form, rcForm],
|
||||
);
|
||||
|
||||
return [wrapForm];
|
||||
}
|
||||
|
||||
type Updater<ValueType> = (prev?: ValueType) => ValueType;
|
||||
|
||||
export function useFrameState<ValueType>(
|
||||
defaultValue: ValueType,
|
||||
): [ValueType, (updater: Updater<ValueType>) => void] {
|
||||
const [value, setValue] = React.useState(defaultValue);
|
||||
const frameRef = React.useRef<number | null>(null);
|
||||
const batchRef = React.useRef<Updater<ValueType>[]>([]);
|
||||
const destroyRef = React.useRef(false);
|
||||
|
||||
React.useEffect(
|
||||
() => () => {
|
||||
destroyRef.current = true;
|
||||
raf.cancel(frameRef.current!);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
function setFrameValue(updater: Updater<ValueType>) {
|
||||
if (destroyRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frameRef.current === null) {
|
||||
batchRef.current = [];
|
||||
frameRef.current = raf(() => {
|
||||
frameRef.current = null;
|
||||
setValue(prevValue => {
|
||||
let current = prevValue;
|
||||
|
||||
batchRef.current.forEach(func => {
|
||||
current = func(current);
|
||||
});
|
||||
|
||||
return current;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
batchRef.current.push(updater);
|
||||
}
|
||||
|
||||
return [value, setFrameValue];
|
||||
}
|
||||
|
@ -410,9 +410,21 @@ Array [
|
||||
`;
|
||||
|
||||
exports[`renders ./components/grid/demo/flex-order.md correctly 1`] = `
|
||||
<div
|
||||
Array [
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
|
||||
role="separator"
|
||||
style="color:#333;font-weight:normal"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Normal
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6 ant-col-order-4"
|
||||
>
|
||||
@ -433,7 +445,43 @@ exports[`renders ./components/grid/demo/flex-order.md correctly 1`] = `
|
||||
>
|
||||
4 col-order-1
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-left"
|
||||
role="separator"
|
||||
style="color:#333;font-weight:normal"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Responsive
|
||||
</span>
|
||||
</div>,
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6 ant-col-xs-order-1 ant-col-sm-order-2 ant-col-md-order-3 ant-col-lg-order-4"
|
||||
>
|
||||
1 col-order-esponsive
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-6 ant-col-xs-order-2 ant-col-sm-order-1 ant-col-md-order-4 ant-col-lg-order-3"
|
||||
>
|
||||
2 col-order-esponsive
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-6 ant-col-xs-order-3 ant-col-sm-order-4 ant-col-md-order-2 ant-col-lg-order-1"
|
||||
>
|
||||
3 col-order-esponsive
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-6 ant-col-xs-order-4 ant-col-sm-order-3 ant-col-md-order-1 ant-col-lg-order-2"
|
||||
>
|
||||
4 col-order-esponsive
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/grid/demo/flex-stretch.md correctly 1`] = `
|
||||
|
@ -14,9 +14,13 @@ title:
|
||||
To change the element sort by order.
|
||||
|
||||
```jsx
|
||||
import { Row, Col } from 'antd';
|
||||
import { Row, Col, Divider } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<Divider orientation="left" style={{ color: '#333', fontWeight: 'normal' }}>
|
||||
Normal
|
||||
</Divider>
|
||||
<Row>
|
||||
<Col span={6} order={4}>
|
||||
1 col-order-4
|
||||
@ -30,7 +34,25 @@ ReactDOM.render(
|
||||
<Col span={6} order={1}>
|
||||
4 col-order-1
|
||||
</Col>
|
||||
</Row>,
|
||||
</Row>
|
||||
<Divider orientation="left" style={{ color: '#333', fontWeight: 'normal' }}>
|
||||
Responsive
|
||||
</Divider>
|
||||
<Row>
|
||||
<Col span={6} xs={{ order: 1 }} sm={{ order: 2 }} md={{ order: 3 }} lg={{ order: 4 }}>
|
||||
1 col-order-esponsive
|
||||
</Col>
|
||||
<Col span={6} xs={{ order: 2 }} sm={{ order: 1 }} md={{ order: 4 }} lg={{ order: 3 }}>
|
||||
2 col-order-esponsive
|
||||
</Col>
|
||||
<Col span={6} xs={{ order: 3 }} sm={{ order: 4 }} md={{ order: 2 }} lg={{ order: 1 }}>
|
||||
3 col-order-esponsive
|
||||
</Col>
|
||||
<Col span={6} xs={{ order: 4 }} sm={{ order: 3 }} md={{ order: 1 }} lg={{ order: 2 }}>
|
||||
4 col-order-esponsive
|
||||
</Col>
|
||||
</Row>
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -1,158 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import ResizeObserver from 'rc-resize-observer';
|
||||
import omit from 'omit.js';
|
||||
import classNames from 'classnames';
|
||||
import calculateNodeHeight from './calculateNodeHeight';
|
||||
import raf from '../_util/raf';
|
||||
import { TextAreaProps } from './TextArea';
|
||||
|
||||
const RESIZE_STATUS_NONE = 0;
|
||||
const RESIZE_STATUS_RESIZING = 1;
|
||||
const RESIZE_STATUS_RESIZED = 2;
|
||||
|
||||
export interface AutoSizeType {
|
||||
minRows?: number;
|
||||
maxRows?: number;
|
||||
}
|
||||
|
||||
export interface TextAreaState {
|
||||
textareaStyles?: React.CSSProperties;
|
||||
/** We need add process style to disable scroll first and then add back to avoid unexpected scrollbar */
|
||||
resizeStatus?:
|
||||
| typeof RESIZE_STATUS_NONE
|
||||
| typeof RESIZE_STATUS_RESIZING
|
||||
| typeof RESIZE_STATUS_RESIZED;
|
||||
}
|
||||
|
||||
class ResizableTextArea extends React.Component<TextAreaProps, TextAreaState> {
|
||||
nextFrameActionId: number;
|
||||
|
||||
resizeFrameId: number;
|
||||
|
||||
constructor(props: TextAreaProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
textareaStyles: {},
|
||||
resizeStatus: RESIZE_STATUS_NONE,
|
||||
};
|
||||
}
|
||||
|
||||
textArea: HTMLTextAreaElement;
|
||||
|
||||
saveTextArea = (textArea: HTMLTextAreaElement) => {
|
||||
this.textArea = textArea;
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.resizeTextarea();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: TextAreaProps) {
|
||||
// Re-render with the new content then recalculate the height as required.
|
||||
if (prevProps.value !== this.props.value) {
|
||||
this.resizeTextarea();
|
||||
}
|
||||
}
|
||||
|
||||
handleResize = (size: { width: number; height: number }) => {
|
||||
const { resizeStatus } = this.state;
|
||||
const { autoSize, onResize } = this.props;
|
||||
|
||||
if (resizeStatus !== RESIZE_STATUS_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof onResize === 'function') {
|
||||
onResize(size);
|
||||
}
|
||||
if (autoSize) {
|
||||
this.resizeOnNextFrame();
|
||||
}
|
||||
};
|
||||
|
||||
resizeOnNextFrame = () => {
|
||||
raf.cancel(this.nextFrameActionId);
|
||||
this.nextFrameActionId = raf(this.resizeTextarea);
|
||||
};
|
||||
|
||||
resizeTextarea = () => {
|
||||
const { autoSize } = this.props;
|
||||
if (!autoSize || !this.textArea) {
|
||||
return;
|
||||
}
|
||||
const { minRows, maxRows } = autoSize as AutoSizeType;
|
||||
const textareaStyles = calculateNodeHeight(this.textArea, false, minRows, maxRows);
|
||||
this.setState({ textareaStyles, resizeStatus: RESIZE_STATUS_RESIZING }, () => {
|
||||
raf.cancel(this.resizeFrameId);
|
||||
this.resizeFrameId = raf(() => {
|
||||
this.setState({ resizeStatus: RESIZE_STATUS_RESIZED }, () => {
|
||||
this.resizeFrameId = raf(() => {
|
||||
this.setState({ resizeStatus: RESIZE_STATUS_NONE });
|
||||
this.fixFirefoxAutoScroll();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
raf.cancel(this.nextFrameActionId);
|
||||
raf.cancel(this.resizeFrameId);
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/21870
|
||||
fixFirefoxAutoScroll() {
|
||||
try {
|
||||
if (document.activeElement === this.textArea) {
|
||||
const currentStart = this.textArea.selectionStart;
|
||||
const currentEnd = this.textArea.selectionEnd;
|
||||
this.textArea.setSelectionRange(currentStart, currentEnd);
|
||||
}
|
||||
} catch (e) {
|
||||
// Fix error in Chrome:
|
||||
// Failed to read the 'selectionStart' property from 'HTMLInputElement'
|
||||
// http://stackoverflow.com/q/21177489/3040605
|
||||
}
|
||||
}
|
||||
|
||||
renderTextArea = () => {
|
||||
const { prefixCls, autoSize, onResize, className, disabled } = this.props;
|
||||
const { textareaStyles, resizeStatus } = this.state;
|
||||
const otherProps = omit(this.props, [
|
||||
'prefixCls',
|
||||
'onPressEnter',
|
||||
'autoSize',
|
||||
'defaultValue',
|
||||
'allowClear',
|
||||
'onResize',
|
||||
]);
|
||||
const cls = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
});
|
||||
// Fix https://github.com/ant-design/ant-design/issues/6776
|
||||
// Make sure it could be reset when using form.getFieldDecorator
|
||||
if ('value' in otherProps) {
|
||||
otherProps.value = otherProps.value || '';
|
||||
}
|
||||
const style = {
|
||||
...this.props.style,
|
||||
...textareaStyles,
|
||||
...(resizeStatus === RESIZE_STATUS_RESIZING
|
||||
? // React will warning when mix `overflow` & `overflowY`.
|
||||
// We need to define this separately.
|
||||
{ overflowX: 'hidden', overflowY: 'hidden' }
|
||||
: null),
|
||||
};
|
||||
return (
|
||||
<ResizeObserver onResize={this.handleResize} disabled={!(autoSize || onResize)}>
|
||||
<textarea {...otherProps} className={cls} style={style} ref={this.saveTextArea} />
|
||||
</ResizeObserver>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return this.renderTextArea();
|
||||
}
|
||||
}
|
||||
|
||||
export default ResizableTextArea;
|
@ -1,17 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import RcTextArea, { TextAreaProps as RcTextAreaProps, ResizableTextArea } from 'rc-textarea';
|
||||
import omit from 'omit.js';
|
||||
import ClearableLabeledInput from './ClearableLabeledInput';
|
||||
import ResizableTextArea, { AutoSizeType } from './ResizableTextArea';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { fixControlledValue, resolveOnChange } from './Input';
|
||||
|
||||
export type HTMLTextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
||||
|
||||
export interface TextAreaProps extends HTMLTextareaProps {
|
||||
prefixCls?: string;
|
||||
autoSize?: boolean | AutoSizeType;
|
||||
onPressEnter?: React.KeyboardEventHandler<HTMLTextAreaElement>;
|
||||
export interface TextAreaProps extends RcTextAreaProps {
|
||||
allowClear?: boolean;
|
||||
onResize?: (size: { width: number; height: number }) => void;
|
||||
}
|
||||
|
||||
export interface TextAreaState {
|
||||
@ -54,8 +49,8 @@ class TextArea extends React.Component<TextAreaProps, TextAreaState> {
|
||||
this.resizableTextArea.textArea.blur();
|
||||
}
|
||||
|
||||
saveTextArea = (resizableTextArea: ResizableTextArea) => {
|
||||
this.resizableTextArea = resizableTextArea;
|
||||
saveTextArea = (textarea: RcTextArea) => {
|
||||
this.resizableTextArea = textarea?.resizableTextArea;
|
||||
};
|
||||
|
||||
saveClearableInput = (clearableInput: ClearableLabeledInput) => {
|
||||
@ -63,25 +58,12 @@ class TextArea extends React.Component<TextAreaProps, TextAreaState> {
|
||||
};
|
||||
|
||||
handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
this.setValue(e.target.value, () => {
|
||||
this.resizableTextArea.resizeTextarea();
|
||||
});
|
||||
this.setValue(e.target.value);
|
||||
resolveOnChange(this.resizableTextArea.textArea, e, this.props.onChange);
|
||||
};
|
||||
|
||||
handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
const { onPressEnter, onKeyDown } = this.props;
|
||||
if (e.keyCode === 13 && onPressEnter) {
|
||||
onPressEnter(e);
|
||||
}
|
||||
if (onKeyDown) {
|
||||
onKeyDown(e);
|
||||
}
|
||||
};
|
||||
|
||||
handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
this.setValue('', () => {
|
||||
this.resizableTextArea.renderTextArea();
|
||||
this.focus();
|
||||
});
|
||||
resolveOnChange(this.resizableTextArea.textArea, e, this.props.onChange);
|
||||
@ -89,10 +71,9 @@ class TextArea extends React.Component<TextAreaProps, TextAreaState> {
|
||||
|
||||
renderTextArea = (prefixCls: string) => {
|
||||
return (
|
||||
<ResizableTextArea
|
||||
{...this.props}
|
||||
<RcTextArea
|
||||
{...omit(this.props, ['allowClear'])}
|
||||
prefixCls={prefixCls}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onChange={this.handleChange}
|
||||
ref={this.saveTextArea}
|
||||
/>
|
||||
|
@ -252,6 +252,7 @@ Array [
|
||||
style="width:100px"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
</div>,
|
||||
|
@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import RcTextArea from 'rc-textarea';
|
||||
import Input from '..';
|
||||
import focusTest from '../../../tests/shared/focusTest';
|
||||
import calculateNodeHeight, { calculateNodeStyling } from '../calculateNodeHeight';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
const { TextArea } = Input;
|
||||
@ -78,56 +77,14 @@ describe('TextArea', () => {
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('calculateNodeStyling works correctly', () => {
|
||||
const wrapper = document.createElement('textarea');
|
||||
wrapper.id = 'test';
|
||||
wrapper.wrap = 'wrap';
|
||||
calculateNodeStyling(wrapper, true);
|
||||
const value = calculateNodeStyling(wrapper, true);
|
||||
expect(value).toEqual({
|
||||
borderSize: 2,
|
||||
boxSizing: 'border-box',
|
||||
paddingSize: 4,
|
||||
sizingStyle:
|
||||
'letter-spacing:normal;line-height:normal;padding-top:2px;padding-bottom:2px;font-family:-webkit-small-control;font-weight:;font-size:;font-variant:;text-rendering:auto;text-transform:none;width:;text-indent:0;padding-left:2px;padding-right:2px;border-width:1px;box-sizing:border-box',
|
||||
});
|
||||
});
|
||||
|
||||
it('boxSizing === "border-box"', () => {
|
||||
const wrapper = document.createElement('textarea');
|
||||
wrapper.style.boxSizing = 'border-box';
|
||||
const { height } = calculateNodeHeight(wrapper);
|
||||
expect(height).toBe(2);
|
||||
});
|
||||
|
||||
it('boxSizing === "content-box"', () => {
|
||||
const wrapper = document.createElement('textarea');
|
||||
wrapper.style.boxSizing = 'content-box';
|
||||
const { height } = calculateNodeHeight(wrapper);
|
||||
expect(height).toBe(-4);
|
||||
});
|
||||
|
||||
it('minRows or maxRows is not null', () => {
|
||||
const wrapper = document.createElement('textarea');
|
||||
expect(calculateNodeHeight(wrapper, 1, 1)).toEqual({
|
||||
height: 2,
|
||||
maxHeight: 9007199254740991,
|
||||
minHeight: 2,
|
||||
overflowY: undefined,
|
||||
});
|
||||
wrapper.style.boxSizing = 'content-box';
|
||||
expect(calculateNodeHeight(wrapper, 1, 1)).toEqual({
|
||||
height: -4,
|
||||
maxHeight: 9007199254740991,
|
||||
minHeight: -4,
|
||||
overflowY: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('when prop value not in this.props, resizeTextarea should be called', () => {
|
||||
it('when prop value not in this.props, resizeTextarea should be called', async () => {
|
||||
const wrapper = mount(<TextArea aria-label="textarea" />);
|
||||
const resizeTextarea = jest.spyOn(wrapper.instance().resizableTextArea, 'resizeTextarea');
|
||||
wrapper.find('textarea').simulate('change', 'test');
|
||||
wrapper.find('textarea').simulate('change', {
|
||||
target: {
|
||||
value: 'test',
|
||||
},
|
||||
});
|
||||
expect(resizeTextarea).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -137,7 +94,7 @@ describe('TextArea', () => {
|
||||
const wrapper = mount(
|
||||
<TextArea onPressEnter={onPressEnter} onKeyDown={onKeyDown} aria-label="textarea" />,
|
||||
);
|
||||
wrapper.instance().handleKeyDown({ keyCode: 13 });
|
||||
wrapper.find(RcTextArea).instance().handleKeyDown({ keyCode: 13 });
|
||||
expect(onPressEnter).toHaveBeenCalled();
|
||||
expect(onKeyDown).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -1,156 +0,0 @@
|
||||
// Thanks to https://github.com/andreypopp/react-textarea-autosize/
|
||||
|
||||
/**
|
||||
* calculateNodeHeight(uiTextNode, useCache = false)
|
||||
*/
|
||||
|
||||
const HIDDEN_TEXTAREA_STYLE = `
|
||||
min-height:0 !important;
|
||||
max-height:none !important;
|
||||
height:0 !important;
|
||||
visibility:hidden !important;
|
||||
overflow:hidden !important;
|
||||
position:absolute !important;
|
||||
z-index:-1000 !important;
|
||||
top:0 !important;
|
||||
right:0 !important
|
||||
`;
|
||||
|
||||
const SIZING_STYLE = [
|
||||
'letter-spacing',
|
||||
'line-height',
|
||||
'padding-top',
|
||||
'padding-bottom',
|
||||
'font-family',
|
||||
'font-weight',
|
||||
'font-size',
|
||||
'font-variant',
|
||||
'text-rendering',
|
||||
'text-transform',
|
||||
'width',
|
||||
'text-indent',
|
||||
'padding-left',
|
||||
'padding-right',
|
||||
'border-width',
|
||||
'box-sizing',
|
||||
];
|
||||
|
||||
export interface NodeType {
|
||||
sizingStyle: string;
|
||||
paddingSize: number;
|
||||
borderSize: number;
|
||||
boxSizing: string;
|
||||
}
|
||||
|
||||
const computedStyleCache: { [key: string]: NodeType } = {};
|
||||
let hiddenTextarea: HTMLTextAreaElement;
|
||||
|
||||
export function calculateNodeStyling(node: HTMLElement, useCache = false) {
|
||||
const nodeRef = (node.getAttribute('id') ||
|
||||
node.getAttribute('data-reactid') ||
|
||||
node.getAttribute('name')) as string;
|
||||
|
||||
if (useCache && computedStyleCache[nodeRef]) {
|
||||
return computedStyleCache[nodeRef];
|
||||
}
|
||||
|
||||
const style = window.getComputedStyle(node);
|
||||
|
||||
const boxSizing =
|
||||
style.getPropertyValue('box-sizing') ||
|
||||
style.getPropertyValue('-moz-box-sizing') ||
|
||||
style.getPropertyValue('-webkit-box-sizing');
|
||||
|
||||
const paddingSize =
|
||||
parseFloat(style.getPropertyValue('padding-bottom')) +
|
||||
parseFloat(style.getPropertyValue('padding-top'));
|
||||
|
||||
const borderSize =
|
||||
parseFloat(style.getPropertyValue('border-bottom-width')) +
|
||||
parseFloat(style.getPropertyValue('border-top-width'));
|
||||
|
||||
const sizingStyle = SIZING_STYLE.map(name => `${name}:${style.getPropertyValue(name)}`).join(';');
|
||||
|
||||
const nodeInfo: NodeType = {
|
||||
sizingStyle,
|
||||
paddingSize,
|
||||
borderSize,
|
||||
boxSizing,
|
||||
};
|
||||
|
||||
if (useCache && nodeRef) {
|
||||
computedStyleCache[nodeRef] = nodeInfo;
|
||||
}
|
||||
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
export default function calculateNodeHeight(
|
||||
uiTextNode: HTMLTextAreaElement,
|
||||
useCache = false,
|
||||
minRows: number | null = null,
|
||||
maxRows: number | null = null,
|
||||
) {
|
||||
if (!hiddenTextarea) {
|
||||
hiddenTextarea = document.createElement('textarea');
|
||||
hiddenTextarea.setAttribute('tab-index', '-1');
|
||||
hiddenTextarea.setAttribute('aria-hidden', 'true');
|
||||
document.body.appendChild(hiddenTextarea);
|
||||
}
|
||||
|
||||
// Fix wrap="off" issue
|
||||
// https://github.com/ant-design/ant-design/issues/6577
|
||||
if (uiTextNode.getAttribute('wrap')) {
|
||||
hiddenTextarea.setAttribute('wrap', uiTextNode.getAttribute('wrap') as string);
|
||||
} else {
|
||||
hiddenTextarea.removeAttribute('wrap');
|
||||
}
|
||||
|
||||
// Copy all CSS properties that have an impact on the height of the content in
|
||||
// the textbox
|
||||
const { paddingSize, borderSize, boxSizing, sizingStyle } = calculateNodeStyling(
|
||||
uiTextNode,
|
||||
useCache,
|
||||
);
|
||||
|
||||
// Need to have the overflow attribute to hide the scrollbar otherwise
|
||||
// text-lines will not calculated properly as the shadow will technically be
|
||||
// narrower for content
|
||||
hiddenTextarea.setAttribute('style', `${sizingStyle};${HIDDEN_TEXTAREA_STYLE}`);
|
||||
hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || '';
|
||||
|
||||
let minHeight = Number.MIN_SAFE_INTEGER;
|
||||
let maxHeight = Number.MAX_SAFE_INTEGER;
|
||||
let height = hiddenTextarea.scrollHeight;
|
||||
let overflowY: any;
|
||||
|
||||
if (boxSizing === 'border-box') {
|
||||
// border-box: add border, since height = content + padding + border
|
||||
height += borderSize;
|
||||
} else if (boxSizing === 'content-box') {
|
||||
// remove padding, since height = content
|
||||
height -= paddingSize;
|
||||
}
|
||||
|
||||
if (minRows !== null || maxRows !== null) {
|
||||
// measure height of a textarea with a single row
|
||||
hiddenTextarea.value = ' ';
|
||||
const singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
|
||||
if (minRows !== null) {
|
||||
minHeight = singleRowHeight * minRows;
|
||||
if (boxSizing === 'border-box') {
|
||||
minHeight = minHeight + paddingSize + borderSize;
|
||||
}
|
||||
height = Math.max(minHeight, height);
|
||||
}
|
||||
if (maxRows !== null) {
|
||||
maxHeight = singleRowHeight * maxRows;
|
||||
if (boxSizing === 'border-box') {
|
||||
maxHeight = maxHeight + paddingSize + borderSize;
|
||||
}
|
||||
overflowY = height > maxHeight ? '' : 'hidden';
|
||||
height = Math.min(maxHeight, height);
|
||||
}
|
||||
}
|
||||
return { height, minHeight, maxHeight, overflowY };
|
||||
}
|
@ -2142,9 +2142,11 @@ exports[`renders ./components/list/demo/vertical.md correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -2166,7 +2168,7 @@ exports[`renders ./components/list/demo/vertical.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -2267,8 +2269,10 @@ exports[`renders ./components/list/demo/vertical.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -2290,7 +2294,7 @@ exports[`renders ./components/list/demo/vertical.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -38,9 +38,11 @@ exports[`List.pagination renders pagination correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -62,7 +64,7 @@ exports[`List.pagination renders pagination correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -88,8 +90,10 @@ exports[`List.pagination renders pagination correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -111,7 +115,7 @@ exports[`List.pagination renders pagination correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -128,9 +132,11 @@ exports[`List.pagination should change page size work 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -152,7 +158,7 @@ exports[`List.pagination should change page size work 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -168,9 +174,11 @@ exports[`List.pagination should change page size work 1`] = `
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -192,7 +200,7 @@ exports[`List.pagination should change page size work 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -270,9 +278,11 @@ exports[`List.pagination should change page size work 2`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -294,7 +304,7 @@ exports[`List.pagination should change page size work 2`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -310,9 +320,11 @@ exports[`List.pagination should change page size work 2`] = `
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -334,7 +346,7 @@ exports[`List.pagination should change page size work 2`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -536,8 +548,10 @@ exports[`List.pagination should default work 1`] = `
|
||||
tabindex="0"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -559,7 +573,7 @@ exports[`List.pagination should default work 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1"
|
||||
@ -584,9 +598,11 @@ exports[`List.pagination should default work 1`] = `
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -608,7 +624,7 @@ exports[`List.pagination should default work 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
|
@ -136,6 +136,7 @@ describe('List.pagination', () => {
|
||||
expect(wrapper.find('Pagination').first().render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/24913
|
||||
// https://github.com/ant-design/ant-design/issues/24501
|
||||
it('should onChange called when pageSize change', () => {
|
||||
const handlePaginationChange = jest.fn();
|
||||
|
@ -105,11 +105,6 @@ function List<T>({
|
||||
return (page: number, pageSize: number) => {
|
||||
setPaginationCurrent(page);
|
||||
setPaginationSize(pageSize);
|
||||
if (eventName === 'onShowSizeChange') {
|
||||
if (pagination) {
|
||||
pagination?.onChange?.(page, pageSize);
|
||||
}
|
||||
}
|
||||
if (pagination && (pagination as any)[eventName]) {
|
||||
(pagination as any)[eventName](page, pageSize);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,19 @@ exports[`renders ./components/mentions/demo/async.md correctly 1`] = `
|
||||
style="width:100%"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/mentions/demo/autoSize.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-mentions"
|
||||
style="width:100%"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
</div>
|
||||
@ -17,6 +30,7 @@ exports[`renders ./components/mentions/demo/basic.md correctly 1`] = `
|
||||
style="width:100%"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
>
|
||||
@afc163
|
||||
@ -55,6 +69,7 @@ exports[`renders ./components/mentions/demo/form.md correctly 1`] = `
|
||||
class="ant-mentions"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
id="coders"
|
||||
rows="1"
|
||||
/>
|
||||
@ -90,6 +105,7 @@ exports[`renders ./components/mentions/demo/form.md correctly 1`] = `
|
||||
class="ant-mentions"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
id="bio"
|
||||
placeholder="You can use @ to ref user here"
|
||||
rows="3"
|
||||
@ -141,6 +157,7 @@ exports[`renders ./components/mentions/demo/placement.md correctly 1`] = `
|
||||
style="width:100%"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
</div>
|
||||
@ -152,6 +169,7 @@ exports[`renders ./components/mentions/demo/prefix.md correctly 1`] = `
|
||||
style="width:100%"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
placeholder="input @ to mention people, # to mention tag"
|
||||
rows="1"
|
||||
/>
|
||||
@ -168,6 +186,7 @@ exports[`renders ./components/mentions/demo/readonly.md correctly 1`] = `
|
||||
style="width:100%"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea rc-textarea-disabled"
|
||||
disabled=""
|
||||
placeholder="this is disabled Mentions"
|
||||
rows="1"
|
||||
@ -179,6 +198,7 @@ exports[`renders ./components/mentions/demo/readonly.md correctly 1`] = `
|
||||
style="width:100%"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
placeholder="this is readOnly Mentions"
|
||||
readonly=""
|
||||
rows="1"
|
||||
|
@ -5,6 +5,7 @@ exports[`Mentions rtl render component should be rendered correctly in RTL direc
|
||||
class="ant-mentions ant-mentions-rtl"
|
||||
>
|
||||
<textarea
|
||||
class="rc-textarea"
|
||||
rows="1"
|
||||
/>
|
||||
</div>
|
||||
|
29
components/mentions/demo/autoSize.md
Normal file
29
components/mentions/demo/autoSize.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
order: 6
|
||||
title:
|
||||
zh-CN: 自动大小
|
||||
en-US: autoSize
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
自适应内容高度。
|
||||
|
||||
## en-US
|
||||
|
||||
Height autoSize.
|
||||
|
||||
```jsx
|
||||
import { Mentions } from 'antd';
|
||||
|
||||
const { Option } = Mentions;
|
||||
|
||||
ReactDOM.render(
|
||||
<Mentions autoSize style={{ width: '100%' }}>
|
||||
<Option value="afc163">afc163</Option>
|
||||
<Option value="zombieJ">zombieJ</Option>
|
||||
<Option value="yesmeck">yesmeck</Option>
|
||||
</Mentions>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
@ -7,7 +7,7 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
基本使用
|
||||
基本使用。
|
||||
|
||||
## en-US
|
||||
|
||||
|
@ -38,6 +38,8 @@ When need to mention someone or something.
|
||||
| onFocus | Trigger when mentions get focus | () => void | |
|
||||
| onBlur | Trigger when mentions lose focus | () => void | |
|
||||
| getPopupContainer | Set the mount HTML node for suggestions | () => HTMLElement | |
|
||||
| autoSize | Textarea height autosize feature, can be set to `true\|false` or an object `{ minRows: 2, maxRows: 6 }` | boolean \| object | false |
|
||||
| onResize | The callback function that is triggered when textarea resize | function({ width, height }) | |
|
||||
|
||||
### Mention methods
|
||||
|
||||
|
@ -39,6 +39,8 @@ cover: https://gw.alipayobjects.com/zos/alicdn/jPE-itMFM/Mentions.svg
|
||||
| onFocus | 获得焦点时触发 | () => void | |
|
||||
| onBlur | 失去焦点时触发 | () => void | |
|
||||
| getPopupContainer | 指定建议框挂载的 HTML 节点 | () => HTMLElement | |
|
||||
| autoSize | 自适应内容高度,可设置为 `true|false` 或对象:`{ minRows: 2, maxRows: 6 }`。 | boolean\|object | false |
|
||||
| onResize | resize 回调 | function({ width, height }) | |
|
||||
|
||||
### Mentions 方法
|
||||
|
||||
|
@ -332,7 +332,7 @@ describe('Menu', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('vertical', () => {
|
||||
it('vertical with hover(default)', () => {
|
||||
const wrapper = mount(
|
||||
<Menu mode="vertical">
|
||||
<SubMenu key="1" title="submenu1">
|
||||
@ -354,7 +354,29 @@ describe('Menu', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('horizontal', () => {
|
||||
it('vertical with click', () => {
|
||||
const wrapper = mount(
|
||||
<Menu mode="vertical" triggerSubMenuAction="click">
|
||||
<SubMenu key="1" title="submenu1">
|
||||
<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>,
|
||||
);
|
||||
expect(wrapper.find('.ant-menu-sub').length).toBe(0);
|
||||
toggleMenu(wrapper, 0, 'click');
|
||||
expect(wrapper.find('.ant-menu-sub').hostNodes().length).toBe(1);
|
||||
expect(wrapper.find('.ant-menu-sub').hostNodes().at(0).hasClass('ant-menu-hidden')).not.toBe(
|
||||
true,
|
||||
);
|
||||
toggleMenu(wrapper, 0, 'click');
|
||||
expect(wrapper.find('.ant-menu-sub').hostNodes().at(0).hasClass('ant-menu-hidden')).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('horizontal with hover(default)', () => {
|
||||
jest.useFakeTimers();
|
||||
const wrapper = mount(
|
||||
<Menu mode="horizontal">
|
||||
@ -376,6 +398,29 @@ describe('Menu', () => {
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('horizontal with click', () => {
|
||||
jest.useFakeTimers();
|
||||
const wrapper = mount(
|
||||
<Menu mode="horizontal" triggerSubMenuAction="click">
|
||||
<SubMenu key="1" title="submenu1">
|
||||
<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>,
|
||||
);
|
||||
expect(wrapper.find('.ant-menu-sub').length).toBe(0);
|
||||
toggleMenu(wrapper, 0, 'click');
|
||||
expect(wrapper.find('.ant-menu-sub').hostNodes().length).toBe(1);
|
||||
expect(wrapper.find('.ant-menu-sub').hostNodes().at(0).hasClass('ant-menu-hidden')).not.toBe(
|
||||
true,
|
||||
);
|
||||
toggleMenu(wrapper, 0, 'click');
|
||||
expect(wrapper.find('.ant-menu-sub').hostNodes().at(0).hasClass('ant-menu-hidden')).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('inline title', () => {
|
||||
|
@ -45,6 +45,7 @@ More layouts with navigation: [Layout](/components/layout).
|
||||
| theme | Color theme of the menu | `light` \| `dark` | `light` | |
|
||||
| onClick | Called when a menu item is clicked | function({ item, key, keyPath, domEvent }) | - | |
|
||||
| onDeselect | Called when a menu item is deselected (multiple mode only) | function({ item, key, keyPath, selectedKeys, domEvent }) | - | |
|
||||
| triggerSubMenuAction | Which action can trigger submenu open/close | `hover` \| `click` | `hover` | |
|
||||
| onOpenChange | Called when sub-menus are opened or closed | function(openKeys: string\[]) | noop | |
|
||||
| onSelect | Called when a menu item is selected | function({ item, key, keyPath, selectedKeys, domEvent }) | none | |
|
||||
| overflowedIndicator | Customized icon when menu is collapsed | ReactNode | - | |
|
||||
|
@ -42,6 +42,7 @@ export interface MenuProps {
|
||||
onOpenChange?: (openKeys: string[]) => void;
|
||||
onSelect?: (param: SelectParam) => void;
|
||||
onDeselect?: (param: SelectParam) => void;
|
||||
triggerSubMenuAction?: 'hover' | 'click';
|
||||
onClick?: (param: ClickParam) => void;
|
||||
style?: React.CSSProperties;
|
||||
openAnimation?: string;
|
||||
|
@ -46,6 +46,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3XZcjGpvK/Menu.svg
|
||||
| theme | 主题颜色 | `light` \| `dark` | `light` | |
|
||||
| onClick | 点击 MenuItem 调用此函数 | function({ item, key, keyPath, domEvent }) | - | |
|
||||
| onDeselect | 取消选中时调用,仅在 multiple 生效 | function({ item, key, keyPath, selectedKeys, domEvent }) | - | |
|
||||
| triggerSubMenuAction | SubMenu 展开/关闭的触发行为 | `hover` \| `click` | `hover` | |
|
||||
| onOpenChange | SubMenu 展开/关闭的回调 | function(openKeys: string\[]) | noop | |
|
||||
| onSelect | 被选中时调用 | function({ item, key, keyPath, selectedKeys, domEvent }) | - | |
|
||||
| overflowedIndicator | 自定义 Menu 折叠时的图标 | ReactNode | - | |
|
||||
|
@ -20,7 +20,7 @@ When requiring users to interact with the application, but without jumping to a
|
||||
| cancelText | Text of the Cancel button | string\|ReactNode | `Cancel` |
|
||||
| centered | Centered Modal | boolean | false |
|
||||
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | true |
|
||||
| closeIcon | custom close icon | ReactNode | - |
|
||||
| closeIcon | custom close icon | ReactNode | `<CloseOutlined />` |
|
||||
| confirmLoading | Whether to apply loading visual effect for OK button or not | boolean | false |
|
||||
| destroyOnClose | Whether to unmount child components on onClose | boolean | false |
|
||||
| footer | Footer content, set as `footer={null}` when you don't need default buttons | string\|ReactNode | OK and Cancel buttons |
|
||||
|
@ -23,7 +23,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3StSdUlSH/Modal.svg
|
||||
| cancelText | 取消按钮文字 | string\|ReactNode | 取消 |
|
||||
| centered | 垂直居中展示 Modal | boolean | false |
|
||||
| closable | 是否显示右上角的关闭按钮 | boolean | true |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | - |
|
||||
| closeIcon | 自定义关闭图标 | ReactNode | `<CloseOutlined />` |
|
||||
| confirmLoading | 确定按钮 loading | boolean | false |
|
||||
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false |
|
||||
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer={null}` | string\|ReactNode | 确定取消按钮 |
|
||||
|
@ -27,8 +27,8 @@
|
||||
margin: 0;
|
||||
color: @modal-heading-color;
|
||||
font-weight: 500;
|
||||
font-size: @font-size-lg;
|
||||
line-height: 22px;
|
||||
font-size: @modal-header-title-font-size;
|
||||
line-height: @modal-header-title-line-height;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@
|
||||
padding: @modal-header-padding;
|
||||
color: @text-color;
|
||||
background: @modal-header-bg;
|
||||
border-bottom: @border-width-base @border-style-base @modal-header-border-color-split;
|
||||
border-bottom: @modal-header-border-width @modal-header-border-style @modal-header-border-color-split;
|
||||
border-radius: @border-radius-base @border-radius-base 0 0;
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@
|
||||
padding: @modal-footer-padding-vertical @modal-footer-padding-horizontal;
|
||||
text-align: right;
|
||||
background: @modal-footer-bg;
|
||||
border-top: @border-width-base @border-style-base @modal-footer-border-color-split;
|
||||
border-top: @modal-footer-border-width @modal-footer-border-style @modal-footer-border-color-split;
|
||||
border-radius: 0 0 @border-radius-base @border-radius-base;
|
||||
|
||||
button + button {
|
||||
|
@ -67,22 +67,23 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
const prefixCls = getPrefixCls('pagination', customizePrefixCls);
|
||||
|
||||
const getIconsProps = () => {
|
||||
const ellipsis = <span className={`${prefixCls}-item-ellipsis`}>•••</span>;
|
||||
let prevIcon = (
|
||||
<a className={`${prefixCls}-item-link`}>
|
||||
<button className={`${prefixCls}-item-link`} type="button" tabIndex={-1}>
|
||||
<LeftOutlined />
|
||||
</a>
|
||||
</button>
|
||||
);
|
||||
let nextIcon = (
|
||||
<a className={`${prefixCls}-item-link`}>
|
||||
<button className={`${prefixCls}-item-link`} type="button" tabIndex={-1}>
|
||||
<RightOutlined />
|
||||
</a>
|
||||
</button>
|
||||
);
|
||||
let jumpPrevIcon = (
|
||||
<a className={`${prefixCls}-item-link`}>
|
||||
{/* You can use transition effects in the container :) */}
|
||||
<div className={`${prefixCls}-item-container`}>
|
||||
<DoubleLeftOutlined className={`${prefixCls}-item-link-icon`} />
|
||||
<span className={`${prefixCls}-item-ellipsis`}>•••</span>
|
||||
{ellipsis}
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
@ -91,21 +92,14 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
{/* You can use transition effects in the container :) */}
|
||||
<div className={`${prefixCls}-item-container`}>
|
||||
<DoubleRightOutlined className={`${prefixCls}-item-link-icon`} />
|
||||
<span className={`${prefixCls}-item-ellipsis`}>•••</span>
|
||||
{ellipsis}
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
|
||||
// change arrows direction in right-to-left direction
|
||||
if (direction === 'rtl') {
|
||||
let temp: any;
|
||||
temp = prevIcon;
|
||||
prevIcon = nextIcon;
|
||||
nextIcon = temp;
|
||||
|
||||
temp = jumpPrevIcon;
|
||||
jumpPrevIcon = jumpNextIcon;
|
||||
jumpNextIcon = temp;
|
||||
[prevIcon, nextIcon] = [nextIcon, prevIcon];
|
||||
[jumpPrevIcon, jumpNextIcon] = [jumpNextIcon, jumpPrevIcon];
|
||||
}
|
||||
return {
|
||||
prevIcon,
|
||||
|
@ -16,9 +16,11 @@ exports[`renders ./components/pagination/demo/all.md correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -40,7 +42,7 @@ exports[`renders ./components/pagination/demo/all.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -141,8 +143,10 @@ exports[`renders ./components/pagination/demo/all.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -164,7 +168,7 @@ exports[`renders ./components/pagination/demo/all.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -251,9 +255,11 @@ exports[`renders ./components/pagination/demo/basic.md correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -275,7 +281,7 @@ exports[`renders ./components/pagination/demo/basic.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -328,8 +334,10 @@ exports[`renders ./components/pagination/demo/basic.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -351,7 +359,7 @@ exports[`renders ./components/pagination/demo/basic.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
@ -368,8 +376,10 @@ exports[`renders ./components/pagination/demo/changer.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -391,7 +401,7 @@ exports[`renders ./components/pagination/demo/changer.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1"
|
||||
@ -492,8 +502,10 @@ exports[`renders ./components/pagination/demo/changer.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -515,7 +527,7 @@ exports[`renders ./components/pagination/demo/changer.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -591,8 +603,10 @@ exports[`renders ./components/pagination/demo/changer.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -614,7 +628,7 @@ exports[`renders ./components/pagination/demo/changer.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1"
|
||||
@ -715,8 +729,10 @@ exports[`renders ./components/pagination/demo/changer.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -738,7 +754,7 @@ exports[`renders ./components/pagination/demo/changer.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -819,8 +835,10 @@ exports[`renders ./components/pagination/demo/controlled.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -842,7 +860,7 @@ exports[`renders ./components/pagination/demo/controlled.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1"
|
||||
@ -895,8 +913,10 @@ exports[`renders ./components/pagination/demo/controlled.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -918,7 +938,7 @@ exports[`renders ./components/pagination/demo/controlled.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
@ -1119,8 +1139,10 @@ exports[`renders ./components/pagination/demo/jump.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -1142,7 +1164,7 @@ exports[`renders ./components/pagination/demo/jump.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1"
|
||||
@ -1243,8 +1265,10 @@ exports[`renders ./components/pagination/demo/jump.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -1266,7 +1290,7 @@ exports[`renders ./components/pagination/demo/jump.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -1351,8 +1375,10 @@ exports[`renders ./components/pagination/demo/jump.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -1374,7 +1400,7 @@ exports[`renders ./components/pagination/demo/jump.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1"
|
||||
@ -1475,8 +1501,10 @@ exports[`renders ./components/pagination/demo/jump.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -1498,7 +1526,7 @@ exports[`renders ./components/pagination/demo/jump.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -1589,9 +1617,11 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -1613,7 +1643,7 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -1666,8 +1696,10 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -1689,7 +1721,7 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
@ -1701,9 +1733,11 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -1725,7 +1759,7 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -1778,8 +1812,10 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -1801,7 +1837,7 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -1889,9 +1925,11 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -1913,7 +1951,7 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -1966,8 +2004,10 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -1989,7 +2029,7 @@ exports[`renders ./components/pagination/demo/mini.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -2006,8 +2046,10 @@ exports[`renders ./components/pagination/demo/more.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -2029,7 +2071,7 @@ exports[`renders ./components/pagination/demo/more.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1"
|
||||
@ -2178,8 +2220,10 @@ exports[`renders ./components/pagination/demo/more.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -2201,7 +2245,7 @@ exports[`renders ./components/pagination/demo/more.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -2278,8 +2322,10 @@ exports[`renders ./components/pagination/demo/simple.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -2301,7 +2347,7 @@ exports[`renders ./components/pagination/demo/simple.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-simple-pager"
|
||||
@ -2325,8 +2371,10 @@ exports[`renders ./components/pagination/demo/simple.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -2348,7 +2396,7 @@ exports[`renders ./components/pagination/demo/simple.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
@ -2369,9 +2417,11 @@ exports[`renders ./components/pagination/demo/total.md correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -2393,7 +2443,7 @@ exports[`renders ./components/pagination/demo/total.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -2446,8 +2496,10 @@ exports[`renders ./components/pagination/demo/total.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -2469,7 +2521,7 @@ exports[`renders ./components/pagination/demo/total.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
@ -2549,9 +2601,11 @@ exports[`renders ./components/pagination/demo/total.md correctly 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -2573,7 +2627,7 @@ exports[`renders ./components/pagination/demo/total.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -2626,8 +2680,10 @@ exports[`renders ./components/pagination/demo/total.md correctly 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -2649,7 +2705,7 @@ exports[`renders ./components/pagination/demo/total.md correctly 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-options"
|
||||
|
@ -10,9 +10,11 @@ exports[`Pagination rtl render component should be rendered correctly in RTL dir
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -34,7 +36,7 @@ exports[`Pagination rtl render component should be rendered correctly in RTL dir
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-0 ant-pagination-disabled ant-pagination-item-disabled"
|
||||
@ -50,9 +52,11 @@ exports[`Pagination rtl render component should be rendered correctly in RTL dir
|
||||
class="ant-pagination-next ant-pagination-disabled"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -74,7 +78,7 @@ exports[`Pagination rtl render component should be rendered correctly in RTL dir
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
@ -89,9 +93,11 @@ exports[`Pagination should be rendered correctly in RTL 1`] = `
|
||||
class="ant-pagination-prev ant-pagination-disabled"
|
||||
title="Previous Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="right"
|
||||
@ -113,7 +119,7 @@ exports[`Pagination should be rendered correctly in RTL 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
|
||||
@ -166,8 +172,10 @@ exports[`Pagination should be rendered correctly in RTL 1`] = `
|
||||
tabindex="0"
|
||||
title="Next Page"
|
||||
>
|
||||
<a
|
||||
<button
|
||||
class="ant-pagination-item-link"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="left"
|
||||
@ -189,7 +197,7 @@ exports[`Pagination should be rendered correctly in RTL 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
|
@ -29,21 +29,30 @@ describe('Pagination', () => {
|
||||
return originalElement;
|
||||
}
|
||||
const wrapper = mount(<Pagination defaultCurrent={1} total={50} itemRender={itemRender} />);
|
||||
expect(
|
||||
wrapper
|
||||
.find('button')
|
||||
.at(0)
|
||||
.props().disabled,
|
||||
).toBe(true);
|
||||
expect(wrapper.find('button').at(0).props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should autometically be small when size is not specified', async () => {
|
||||
const wrapper = mount(<Pagination responsive />);
|
||||
expect(
|
||||
wrapper
|
||||
.find('ul')
|
||||
.at(0)
|
||||
.hasClass('mini'),
|
||||
).toBe(true);
|
||||
expect(wrapper.find('ul').at(0).hasClass('mini')).toBe(true);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/24913
|
||||
// https://github.com/ant-design/ant-design/issues/24501
|
||||
it('should onChange called when pageSize change', () => {
|
||||
const onChange = jest.fn();
|
||||
const onShowSizeChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Pagination
|
||||
defaultCurrent={1}
|
||||
total={500}
|
||||
onChange={onChange}
|
||||
onShowSizeChange={onShowSizeChange}
|
||||
/>,
|
||||
);
|
||||
wrapper.find('.ant-select-selector').simulate('mousedown');
|
||||
expect(wrapper.find('.ant-select-item-option').length).toBe(4);
|
||||
wrapper.find('.ant-select-item-option').at(1).simulate('click');
|
||||
expect(onChange).toHaveBeenCalledWith(1, 20);
|
||||
});
|
||||
});
|
||||
|
@ -165,17 +165,18 @@
|
||||
&-next {
|
||||
outline: 0;
|
||||
|
||||
a {
|
||||
button {
|
||||
color: @text-color;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
&:hover a {
|
||||
&:hover button {
|
||||
border-color: @primary-5;
|
||||
}
|
||||
|
||||
.@{pagination-prefix-cls}-item-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
@ -198,7 +199,6 @@
|
||||
&:hover,
|
||||
&:focus {
|
||||
cursor: not-allowed;
|
||||
a,
|
||||
.@{pagination-prefix-cls}-item-link {
|
||||
color: @disabled-color;
|
||||
border-color: @border-color-base;
|
||||
@ -372,29 +372,20 @@
|
||||
}
|
||||
|
||||
.@{pagination-prefix-cls}-item-link {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: @text-color-secondary;
|
||||
color: @disabled-color;
|
||||
background: @disabled-bg;
|
||||
border-color: @border-color-base;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.@{pagination-prefix-cls}-jump-prev,
|
||||
.@{pagination-prefix-cls}-jump-next {
|
||||
&:focus,
|
||||
&:hover {
|
||||
.@{pagination-prefix-cls}-item-link-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.@{pagination-prefix-cls}-item-ellipsis {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: @screen-lg) {
|
||||
|
@ -10,8 +10,11 @@ interface CircleProps extends ProgressProps {
|
||||
progressStatus: string;
|
||||
}
|
||||
|
||||
function getPercentage({ percent, successPercent }: CircleProps) {
|
||||
function getPercentage({ percent, success, successPercent }: CircleProps) {
|
||||
const ptg = validProgress(percent);
|
||||
if (success && 'progress' in success) {
|
||||
successPercent = success.progress;
|
||||
}
|
||||
if (!successPercent) {
|
||||
return ptg;
|
||||
}
|
||||
@ -20,8 +23,11 @@ function getPercentage({ percent, successPercent }: CircleProps) {
|
||||
return [successPercent, validProgress(ptg - successPtg)];
|
||||
}
|
||||
|
||||
function getStrokeColor({ successPercent, strokeColor }: CircleProps) {
|
||||
function getStrokeColor({ success, strokeColor, successPercent }: CircleProps) {
|
||||
const color = strokeColor || null;
|
||||
if (success && 'progress' in success) {
|
||||
successPercent = success.progress;
|
||||
}
|
||||
if (!successPercent) {
|
||||
return color;
|
||||
}
|
||||
|
@ -61,14 +61,15 @@ const Line: React.FC<LineProps> = props => {
|
||||
const {
|
||||
prefixCls,
|
||||
percent,
|
||||
successPercent,
|
||||
strokeWidth,
|
||||
size,
|
||||
strokeColor,
|
||||
strokeLinecap,
|
||||
children,
|
||||
trailColor,
|
||||
success,
|
||||
} = props;
|
||||
|
||||
let backgroundProps;
|
||||
if (strokeColor && typeof strokeColor !== 'string') {
|
||||
backgroundProps = handleGradient(strokeColor);
|
||||
@ -77,23 +78,45 @@ const Line: React.FC<LineProps> = props => {
|
||||
background: strokeColor,
|
||||
};
|
||||
}
|
||||
|
||||
let trailStyle;
|
||||
if (trailColor && typeof trailColor === 'string') {
|
||||
trailStyle = {
|
||||
backgroundColor: trailColor,
|
||||
};
|
||||
}
|
||||
|
||||
let successColor;
|
||||
if (success && 'strokeColor' in success) {
|
||||
successColor = success.strokeColor;
|
||||
}
|
||||
|
||||
let successStyle;
|
||||
if (successColor && typeof successColor === 'string') {
|
||||
successStyle = {
|
||||
backgroundColor: successColor,
|
||||
};
|
||||
}
|
||||
const percentStyle = {
|
||||
width: `${validProgress(percent)}%`,
|
||||
height: strokeWidth || (size === 'small' ? 6 : 8),
|
||||
borderRadius: strokeLinecap === 'square' ? 0 : '',
|
||||
...backgroundProps,
|
||||
};
|
||||
const successPercentStyle = {
|
||||
|
||||
let { successPercent } = props;
|
||||
if (success && 'progress' in success) {
|
||||
successPercent = success.progress;
|
||||
}
|
||||
|
||||
let successPercentStyle = {
|
||||
width: `${validProgress(successPercent)}%`,
|
||||
height: strokeWidth || (size === 'small' ? 6 : 8),
|
||||
borderRadius: strokeLinecap === 'square' ? 0 : '',
|
||||
};
|
||||
if (successStyle) {
|
||||
successPercentStyle = { ...successPercentStyle, ...successStyle };
|
||||
}
|
||||
const successSegment =
|
||||
successPercent !== undefined ? (
|
||||
<div className={`${prefixCls}-success-bg`} style={successPercentStyle} />
|
||||
|
@ -500,6 +500,35 @@ exports[`Progress render strokeColor 3`] = `
|
||||
</Progress>
|
||||
`;
|
||||
|
||||
exports[`Progress render successColor progress 1`] = `
|
||||
<div
|
||||
class="ant-progress ant-progress-line ant-progress-status-normal ant-progress-show-info ant-progress-default"
|
||||
>
|
||||
<div
|
||||
class="ant-progress-outer"
|
||||
>
|
||||
<div
|
||||
class="ant-progress-inner"
|
||||
>
|
||||
<div
|
||||
class="ant-progress-bg"
|
||||
style="width: 60%; height: 8px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-progress-success-bg"
|
||||
style="width: 30%; height: 8px; background-color: rgb(255, 255, 255);"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="ant-progress-text"
|
||||
title="60%"
|
||||
>
|
||||
60%
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Progress render trailColor progress 1`] = `
|
||||
<div
|
||||
class="ant-progress ant-progress-line ant-progress-status-normal ant-progress-show-info ant-progress-default"
|
||||
|
@ -10,13 +10,13 @@ describe('Progress', () => {
|
||||
rtlTest(Progress);
|
||||
|
||||
it('successPercent should decide the progress status when it exists', () => {
|
||||
const wrapper = mount(<Progress percent={100} successPercent={50} />);
|
||||
const wrapper = mount(<Progress percent={100} success={{ progress: 50 }} />);
|
||||
expect(wrapper.find('.ant-progress-status-success')).toHaveLength(0);
|
||||
|
||||
wrapper.setProps({ percent: 50, successPercent: 100 });
|
||||
wrapper.setProps({ percent: 50, success: { progress: 100 } });
|
||||
expect(wrapper.find('.ant-progress-status-success')).toHaveLength(1);
|
||||
|
||||
wrapper.setProps({ percent: 100, successPercent: 0 });
|
||||
wrapper.setProps({ percent: 100, success: { progress: 0 } });
|
||||
expect(wrapper.find('.ant-progress-status-success')).toHaveLength(0);
|
||||
});
|
||||
|
||||
@ -36,7 +36,7 @@ describe('Progress', () => {
|
||||
});
|
||||
|
||||
it('render negative successPercent', () => {
|
||||
const wrapper = mount(<Progress percent={50} successPercent={-20} />);
|
||||
const wrapper = mount(<Progress percent={50} success={{ progress: -20 }} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@ -44,7 +44,7 @@ describe('Progress', () => {
|
||||
const wrapper = mount(
|
||||
<Progress
|
||||
percent={50}
|
||||
successPercent={10}
|
||||
success={{ progress: 10 }}
|
||||
format={(percent, successPercent) => `${percent} ${successPercent}`}
|
||||
/>,
|
||||
);
|
||||
@ -81,6 +81,13 @@ describe('Progress', () => {
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render successColor progress', () => {
|
||||
const wrapper = mount(
|
||||
<Progress percent={60} success={{ progress: 30, strokeColor: '#ffffff' }} />,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render dashboard zero gapDegree', () => {
|
||||
const wrapper = mount(<Progress type="dashboard" gapDegree={0} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
|
@ -19,15 +19,15 @@ import { Tooltip, Progress } from 'antd';
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<Tooltip title="3 done / 3 in progress / 4 to do">
|
||||
<Progress percent={60} successPercent={30} />
|
||||
<Progress percent={60} success={{ progress: 30 }} />
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="3 done / 3 in progress / 4 to do">
|
||||
<Progress percent={60} successPercent={30} type="circle" />
|
||||
<Progress percent={60} success={{ progress: 30 }} type="circle" />
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="3 done / 3 in progress / 4 to do">
|
||||
<Progress percent={60} successPercent={30} type="dashboard" />
|
||||
<Progress percent={60} success={{ progress: 30 }} type="dashboard" />
|
||||
</Tooltip>
|
||||
</>,
|
||||
mountNode,
|
||||
|
@ -27,8 +27,8 @@ Properties that shared by all types.
|
||||
| status | to set the status of the Progress, options: `success` `exception` `normal` `active`(line only) | string | - |
|
||||
| strokeLinecap | to set the style of the progress linecap | `round` \| `square` | `round` |
|
||||
| strokeColor | color of progress bar | string | - |
|
||||
| successPercent | segmented success percent | number | 0 |
|
||||
| trailColor | color of unfilled part | string | - |
|
||||
| success | configs of successfully progress bar | { progress: number, strokeColor: string } | - |
|
||||
|
||||
### `type="line"`
|
||||
|
||||
|
@ -28,8 +28,8 @@ cover: https://gw.alipayobjects.com/zos/alicdn/xqsDu4ZyR/Progress.svg
|
||||
| status | 状态,可选:`success` `exception` `normal` `active`(仅限 line) | string | - |
|
||||
| strokeLinecap | - | `round` \| `square` | `round` |
|
||||
| strokeColor | 进度条的色彩 | string | - |
|
||||
| successPercent | 已完成的分段百分比 | number | 0 |
|
||||
| trailColor | 未完成的分段的颜色 | string | - |
|
||||
| success | 成功进度条相关配置 | { progress: number, strokeColor: string } | - |
|
||||
|
||||
### `type="line"`
|
||||
|
||||
|
@ -8,6 +8,7 @@ import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { tuple } from '../_util/type';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import Line from './Line';
|
||||
import Circle from './Circle';
|
||||
import Steps from './Steps';
|
||||
@ -20,12 +21,17 @@ export type ProgressSize = 'default' | 'small';
|
||||
export type StringGradients = { [percentage: string]: string };
|
||||
type FromToGradients = { from: string; to: string };
|
||||
export type ProgressGradient = { direction?: string } & (StringGradients | FromToGradients);
|
||||
|
||||
export interface SuccessProps {
|
||||
progress?: number;
|
||||
strokeColor?: string;
|
||||
}
|
||||
|
||||
export interface ProgressProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
type?: ProgressType;
|
||||
percent?: number;
|
||||
successPercent?: number;
|
||||
format?: (percent?: number, successPercent?: number) => React.ReactNode;
|
||||
status?: typeof ProgressStatuses[number];
|
||||
showInfo?: boolean;
|
||||
@ -34,11 +40,14 @@ export interface ProgressProps {
|
||||
strokeColor?: string | ProgressGradient;
|
||||
trailColor?: string;
|
||||
width?: number;
|
||||
success?: SuccessProps;
|
||||
style?: React.CSSProperties;
|
||||
gapDegree?: number;
|
||||
gapPosition?: 'top' | 'bottom' | 'left' | 'right';
|
||||
size?: ProgressSize;
|
||||
steps?: number;
|
||||
/** @deprecated Use `success` instead */
|
||||
successPercent?: number;
|
||||
}
|
||||
|
||||
export default class Progress extends React.Component<ProgressProps> {
|
||||
@ -54,7 +63,11 @@ export default class Progress extends React.Component<ProgressProps> {
|
||||
};
|
||||
|
||||
getPercentNumber() {
|
||||
const { successPercent, percent = 0 } = this.props;
|
||||
const { percent = 0, success } = this.props;
|
||||
let { successPercent } = this.props;
|
||||
if (success && 'progress' in success) {
|
||||
successPercent = success.progress;
|
||||
}
|
||||
return parseInt(
|
||||
successPercent !== undefined ? successPercent.toString() : percent.toString(),
|
||||
10,
|
||||
@ -70,7 +83,11 @@ export default class Progress extends React.Component<ProgressProps> {
|
||||
}
|
||||
|
||||
renderProcessInfo(prefixCls: string, progressStatus: typeof ProgressStatuses[number]) {
|
||||
const { showInfo, format, type, percent, successPercent } = this.props;
|
||||
const { showInfo, format, type, percent, success } = this.props;
|
||||
let { successPercent } = this.props;
|
||||
if (success && 'progress' in success) {
|
||||
successPercent = success.progress;
|
||||
}
|
||||
if (!showInfo) return null;
|
||||
|
||||
let text;
|
||||
@ -105,6 +122,13 @@ export default class Progress extends React.Component<ProgressProps> {
|
||||
const prefixCls = getPrefixCls('progress', customizePrefixCls);
|
||||
const progressStatus = this.getProgressStatus();
|
||||
const progressInfo = this.renderProcessInfo(prefixCls, progressStatus);
|
||||
|
||||
devWarning(
|
||||
'successPercent' in props,
|
||||
'Progress',
|
||||
'`successPercent` is deprecated. Please use `success` instead.',
|
||||
);
|
||||
|
||||
let progress;
|
||||
// Render progress shape
|
||||
if (type === 'line') {
|
||||
@ -148,7 +172,6 @@ export default class Progress extends React.Component<ProgressProps> {
|
||||
'status',
|
||||
'format',
|
||||
'trailColor',
|
||||
'successPercent',
|
||||
'strokeWidth',
|
||||
'width',
|
||||
'gapDegree',
|
||||
@ -157,6 +180,8 @@ export default class Progress extends React.Component<ProgressProps> {
|
||||
'strokeLinecap',
|
||||
'percent',
|
||||
'steps',
|
||||
'success',
|
||||
'successPercent',
|
||||
])}
|
||||
className={classString}
|
||||
>
|
||||
|
@ -1110,6 +1110,7 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
@ -1153,13 +1154,14 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
class="ant-radio-wrapper ant-radio-wrapper-disabled"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
class="ant-radio ant-radio-disabled"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
disabled=""
|
||||
type="radio"
|
||||
value="Orange"
|
||||
/>
|
||||
@ -1172,23 +1174,25 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-outline"
|
||||
>
|
||||
<label
|
||||
class="ant-radio-wrapper ant-radio-wrapper-checked"
|
||||
class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-radio ant-radio-checked"
|
||||
class="ant-radio-button ant-radio-button-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-radio-input"
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="Apple"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
@ -1196,18 +1200,18 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="Pear"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
@ -1215,18 +1219,83 @@ Array [
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-wrapper"
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio"
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-input"
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="Orange"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-inner"
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
Orange
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-radio-group ant-radio-group-solid"
|
||||
>
|
||||
<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="Apple"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
Apple
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
type="radio"
|
||||
value="Pear"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
Pear
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="ant-radio-button-wrapper ant-radio-button-wrapper-disabled"
|
||||
>
|
||||
<span
|
||||
class="ant-radio-button ant-radio-button-disabled"
|
||||
>
|
||||
<input
|
||||
class="ant-radio-button-input"
|
||||
disabled=""
|
||||
type="radio"
|
||||
value="Orange"
|
||||
/>
|
||||
<span
|
||||
class="ant-radio-button-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
|
@ -7,11 +7,11 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
通过配置 `options` 参数来渲染单选框。
|
||||
通过配置 `options` 参数来渲染单选框。也可通过 `optionType` 参数来设置 Radio 类型。
|
||||
|
||||
## en-US
|
||||
|
||||
Render radios by configuring `options`.
|
||||
Render radios by configuring `options`. Radio type can also be set through the `optionType` parameter.
|
||||
|
||||
```jsx
|
||||
import { Radio } from 'antd';
|
||||
@ -25,7 +25,7 @@ const options = [
|
||||
const optionsWithDisabled = [
|
||||
{ label: 'Apple', value: 'Apple' },
|
||||
{ label: 'Pear', value: 'Pear' },
|
||||
{ label: 'Orange', value: 'Orange', disabled: false },
|
||||
{ label: 'Orange', value: 'Orange', disabled: true },
|
||||
];
|
||||
|
||||
class App extends React.Component {
|
||||
@ -33,6 +33,7 @@ class App extends React.Component {
|
||||
value1: 'Apple',
|
||||
value2: 'Apple',
|
||||
value3: 'Apple',
|
||||
value4: 'Apple',
|
||||
};
|
||||
|
||||
onChange1 = e => {
|
||||
@ -56,16 +57,36 @@ class App extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
onChange4 = e => {
|
||||
console.log('radio4 checked', e.target.value);
|
||||
this.setState({
|
||||
value4: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value1, value2, value3 } = this.state;
|
||||
const { value1, value2, value3, value4 } = this.state;
|
||||
return (
|
||||
<>
|
||||
<Radio.Group options={plainOptions} onChange={this.onChange1} value={value1} />
|
||||
<Radio.Group options={options} onChange={this.onChange2} value={value2} />
|
||||
<br />
|
||||
<Radio.Group options={optionsWithDisabled} onChange={this.onChange2} value={value2} />
|
||||
<br />
|
||||
<br />
|
||||
<Radio.Group
|
||||
options={optionsWithDisabled}
|
||||
options={options}
|
||||
onChange={this.onChange3}
|
||||
value={value3}
|
||||
optionType="button"
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Radio.Group
|
||||
options={optionsWithDisabled}
|
||||
onChange={this.onChange4}
|
||||
value={value4}
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -43,6 +43,7 @@ const RadioGroup: React.FC<RadioGroupProps> = props => {
|
||||
prefixCls: customizePrefixCls,
|
||||
className = '',
|
||||
options,
|
||||
optionType,
|
||||
buttonStyle,
|
||||
disabled,
|
||||
children,
|
||||
@ -57,13 +58,14 @@ const RadioGroup: React.FC<RadioGroupProps> = props => {
|
||||
let childrenToRender = children;
|
||||
// 如果存在 options, 优先使用
|
||||
if (options && options.length > 0) {
|
||||
const optionsPrefixCls = optionType === 'button' ? `${prefixCls}-button` : prefixCls;
|
||||
childrenToRender = options.map(option => {
|
||||
if (typeof option === 'string') {
|
||||
// 此处类型自动推导为 string
|
||||
return (
|
||||
<Radio
|
||||
key={option}
|
||||
prefixCls={prefixCls}
|
||||
prefixCls={optionsPrefixCls}
|
||||
disabled={disabled}
|
||||
value={option}
|
||||
checked={value === option}
|
||||
@ -76,7 +78,7 @@ const RadioGroup: React.FC<RadioGroupProps> = props => {
|
||||
return (
|
||||
<Radio
|
||||
key={`radio-group-value-options-${option.value}`}
|
||||
prefixCls={prefixCls}
|
||||
prefixCls={optionsPrefixCls}
|
||||
disabled={option.disabled || disabled}
|
||||
value={option.value}
|
||||
checked={value === option.value}
|
||||
|
@ -28,16 +28,17 @@ Radio.
|
||||
|
||||
Radio group can wrap a group of `Radio`。
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| defaultValue | Default selected value | any | |
|
||||
| disabled | Disable all radio buttons | boolean | false |
|
||||
| name | The `name` property of all `input[type="radio"]` children | string | |
|
||||
| options | set children optional | string\[] \| Array<{ label: string value: string disabled?: boolean }> | |
|
||||
| size | size for radio button style | `large` \| `middle` \| `small` | |
|
||||
| value | Used for setting the currently selected value. | any | |
|
||||
| onChange | The callback function that is triggered when the state changes. | Function(e:Event) | |
|
||||
| buttonStyle | style type of radio button | `outline` \| `solid` | `outline` |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| defaultValue | Default selected value | any | | |
|
||||
| disabled | Disable all radio buttons | boolean | false | |
|
||||
| name | The `name` property of all `input[type="radio"]` children | string | | |
|
||||
| options | set children optional | string\[] \| Array<{ label: string value: string disabled?: boolean }> | | |
|
||||
| size | size for radio button style | `large` \| `middle` \| `small` | | |
|
||||
| value | Used for setting the currently selected value. | any | | |
|
||||
| onChange | The callback function that is triggered when the state changes. | Function(e:Event) | | |
|
||||
| optionType | set Radio optionType | `default` \| `button` | `default` | 4.4.0 |
|
||||
| buttonStyle | style type of radio button | `outline` \| `solid` | `outline` | |
|
||||
|
||||
## Methods
|
||||
|
||||
|
@ -29,16 +29,17 @@ cover: https://gw.alipayobjects.com/zos/alicdn/8cYb5seNB/Radio.svg
|
||||
|
||||
单选框组合,用于包裹一组 `Radio`。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| defaultValue | 默认选中的值 | any | |
|
||||
| disabled | 禁选所有子单选器 | boolean | false |
|
||||
| name | RadioGroup 下所有 `input[type="radio"]` 的 `name` 属性 | string | |
|
||||
| options | 以配置形式设置子元素 | string\[] \| Array<{ label: string value: string disabled?: boolean }> | |
|
||||
| size | 大小,只对按钮样式生效 | `large` \| `middle` \| `small` | - |
|
||||
| value | 用于设置当前选中的值 | any | |
|
||||
| onChange | 选项变化时的回调函数 | Function(e:Event) | |
|
||||
| buttonStyle | RadioButton 的风格样式,目前有描边和填色两种风格 | `outline` \| `solid` | `outline` |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| defaultValue | 默认选中的值 | any | | |
|
||||
| disabled | 禁选所有子单选器 | boolean | false | | |
|
||||
| name | RadioGroup 下所有 `input[type="radio"]` 的 `name` 属性 | string | | |
|
||||
| options | 以配置形式设置子元素 | string\[] \| Array<{ label: string value: string disabled?: boolean }> | | |
|
||||
| size | 大小,只对按钮样式生效 | `large` \| `middle` \| `small` | - | |
|
||||
| value | 用于设置当前选中的值 | any | | |
|
||||
| onChange | 选项变化时的回调函数 | Function(e:Event) | | |
|
||||
| optionType | 用于设置 Radio `options` 类型 | `default` \| `button` | `default` | 4.4.0 |
|
||||
| buttonStyle | RadioButton 的风格样式,目前有描边和填色两种风格 | `outline` \| `solid` | `outline` | |
|
||||
|
||||
## 方法
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { AbstractCheckboxProps } from '../checkbox/Checkbox';
|
||||
import { SizeType } from '../config-provider/SizeContext';
|
||||
|
||||
export type RadioGroupButtonStyle = 'outline' | 'solid';
|
||||
export type RadioGroupOptionType = 'default' | 'button';
|
||||
|
||||
export interface RadioGroupProps extends AbstractCheckboxGroupProps {
|
||||
defaultValue?: any;
|
||||
@ -15,6 +16,7 @@ export interface RadioGroupProps extends AbstractCheckboxGroupProps {
|
||||
name?: string;
|
||||
children?: React.ReactNode;
|
||||
id?: string;
|
||||
optionType?: RadioGroupOptionType;
|
||||
buttonStyle?: RadioGroupButtonStyle;
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { RadioProps, RadioChangeEvent } from './interface';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import RadioGroupContext from './context';
|
||||
import { composeRef } from '../_util/ref';
|
||||
import devWarning from '../_util/devWarning';
|
||||
|
||||
const InternalRadio: React.ForwardRefRenderFunction<unknown, RadioProps> = (props, ref) => {
|
||||
const context = React.useContext(RadioGroupContext);
|
||||
@ -12,6 +13,10 @@ const InternalRadio: React.ForwardRefRenderFunction<unknown, RadioProps> = (prop
|
||||
const innerRef = React.useRef<HTMLElement>();
|
||||
const mergedRef = composeRef(ref, innerRef);
|
||||
|
||||
React.useEffect(() => {
|
||||
devWarning(!('optionType' in props), 'Radio', '`optionType` is only support in Radio.Group.');
|
||||
}, []);
|
||||
|
||||
const onChange = (e: RadioChangeEvent) => {
|
||||
if (props.onChange) {
|
||||
props.onChange(e);
|
||||
|
@ -310,7 +310,7 @@ exports[`renders ./components/rate/demo/basic.md correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`renders ./components/rate/demo/character.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<ul
|
||||
class="ant-rate"
|
||||
role="radiogroup"
|
||||
@ -616,8 +616,8 @@ exports[`renders ./components/rate/demo/character.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<br />
|
||||
</ul>,
|
||||
<br />,
|
||||
<ul
|
||||
class="ant-rate"
|
||||
role="radiogroup"
|
||||
@ -734,8 +734,8 @@ exports[`renders ./components/rate/demo/character.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<br />
|
||||
</ul>,
|
||||
<br />,
|
||||
<ul
|
||||
class="ant-rate"
|
||||
role="radiogroup"
|
||||
@ -851,12 +851,440 @@ exports[`renders ./components/rate/demo/character.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ul>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/rate/demo/character-function.md correctly 1`] = `
|
||||
Array [
|
||||
<ul
|
||||
class="ant-rate"
|
||||
role="radiogroup"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-full"
|
||||
>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-posinset="1"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-full"
|
||||
>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-posinset="2"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="3"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="4"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
4
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
4
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="5"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
5
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
5
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>,
|
||||
<br />,
|
||||
<ul
|
||||
class="ant-rate"
|
||||
role="radiogroup"
|
||||
tabindex="0"
|
||||
>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-full"
|
||||
>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-posinset="1"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="frown"
|
||||
class="anticon anticon-frown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="frown"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM512 533c-85.5 0-155.6 67.3-160 151.6a8 8 0 008 8.4h48.1c4.2 0 7.8-3.2 8.1-7.4C420 636.1 461.5 597 512 597s92.1 39.1 95.8 88.6c.3 4.2 3.9 7.4 8.1 7.4H664a8 8 0 008-8.4C667.6 600.3 597.5 533 512 533z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="frown"
|
||||
class="anticon anticon-frown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="frown"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM512 533c-85.5 0-155.6 67.3-160 151.6a8 8 0 008 8.4h48.1c4.2 0 7.8-3.2 8.1-7.4C420 636.1 461.5 597 512 597s92.1 39.1 95.8 88.6c.3 4.2 3.9 7.4 8.1 7.4H664a8 8 0 008-8.4C667.6 600.3 597.5 533 512 533z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-full"
|
||||
>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-posinset="2"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="frown"
|
||||
class="anticon anticon-frown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="frown"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM512 533c-85.5 0-155.6 67.3-160 151.6a8 8 0 008 8.4h48.1c4.2 0 7.8-3.2 8.1-7.4C420 636.1 461.5 597 512 597s92.1 39.1 95.8 88.6c.3 4.2 3.9 7.4 8.1 7.4H664a8 8 0 008-8.4C667.6 600.3 597.5 533 512 533z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="frown"
|
||||
class="anticon anticon-frown"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="frown"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM512 533c-85.5 0-155.6 67.3-160 151.6a8 8 0 008 8.4h48.1c4.2 0 7.8-3.2 8.1-7.4C420 636.1 461.5 597 512 597s92.1 39.1 95.8 88.6c.3 4.2 3.9 7.4 8.1 7.4H664a8 8 0 008-8.4C667.6 600.3 597.5 533 512 533z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-full"
|
||||
>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-posinset="3"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="meh"
|
||||
class="anticon anticon-meh"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="meh"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 565H360c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="meh"
|
||||
class="anticon anticon-meh"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="meh"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 565H360c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="4"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
class="anticon anticon-smile"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="smile"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
class="anticon anticon-smile"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="smile"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="ant-rate-star ant-rate-star-zero"
|
||||
>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-posinset="5"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
class="anticon anticon-smile"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="smile"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-rate-star-second"
|
||||
>
|
||||
<span
|
||||
aria-label="smile"
|
||||
class="anticon anticon-smile"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="smile"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/rate/demo/clear.md correctly 1`] = `
|
||||
<div>
|
||||
Array [
|
||||
<ul
|
||||
class="ant-rate"
|
||||
role="radiogroup"
|
||||
@ -1162,13 +1590,13 @@ exports[`renders ./components/rate/demo/clear.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>,
|
||||
<span
|
||||
class="ant-rate-text"
|
||||
>
|
||||
allowClear: true
|
||||
</span>
|
||||
<br />
|
||||
</span>,
|
||||
<br />,
|
||||
<ul
|
||||
class="ant-rate"
|
||||
role="radiogroup"
|
||||
@ -1474,13 +1902,13 @@ exports[`renders ./components/rate/demo/clear.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>,
|
||||
<span
|
||||
class="ant-rate-text"
|
||||
>
|
||||
allowClear: false
|
||||
</span>
|
||||
</div>
|
||||
</span>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/rate/demo/disabled.md correctly 1`] = `
|
||||
@ -1497,7 +1925,7 @@ exports[`renders ./components/rate/demo/disabled.md correctly 1`] = `
|
||||
aria-posinset="1"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
@ -1557,7 +1985,7 @@ exports[`renders ./components/rate/demo/disabled.md correctly 1`] = `
|
||||
aria-posinset="2"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
@ -1617,7 +2045,7 @@ exports[`renders ./components/rate/demo/disabled.md correctly 1`] = `
|
||||
aria-posinset="3"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
@ -1677,7 +2105,7 @@ exports[`renders ./components/rate/demo/disabled.md correctly 1`] = `
|
||||
aria-posinset="4"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
@ -1737,7 +2165,7 @@ exports[`renders ./components/rate/demo/disabled.md correctly 1`] = `
|
||||
aria-posinset="5"
|
||||
aria-setsize="5"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="ant-rate-star-first"
|
||||
|
46
components/rate/demo/character-function.md
Normal file
46
components/rate/demo/character-function.md
Normal file
@ -0,0 +1,46 @@
|
||||
---
|
||||
order: 6
|
||||
title:
|
||||
zh-CN: 自定义字符
|
||||
en-US: Customize character
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可以使用 `(RateProps) => ReactNode` 的方式自定义每一个字符。
|
||||
|
||||
## en-US
|
||||
|
||||
Can customize each character using `(RateProps) => ReactNode`.
|
||||
|
||||
```jsx
|
||||
import { Rate } from 'antd';
|
||||
import { FrownOutlined, MehOutlined, SmileOutlined } from '@ant-design/icons';
|
||||
|
||||
const customIcons = {
|
||||
1: <FrownOutlined />,
|
||||
2: <FrownOutlined />,
|
||||
3: <MehOutlined />,
|
||||
4: <SmileOutlined />,
|
||||
5: <SmileOutlined />,
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<Rate
|
||||
defaultValue={2}
|
||||
character={({ index }) => {
|
||||
return index + 1;
|
||||
}}
|
||||
/>
|
||||
<br />
|
||||
<Rate
|
||||
defaultValue={3}
|
||||
character={({ index }) => {
|
||||
return customIcons[index + 1];
|
||||
}}
|
||||
/>
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
@ -18,13 +18,13 @@ import { Rate } from 'antd';
|
||||
import { HeartOutlined } from '@ant-design/icons';
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<Rate character={<HeartOutlined />} allowHalf />
|
||||
<br />
|
||||
<Rate character="A" allowHalf style={{ fontSize: 36 }} />
|
||||
<br />
|
||||
<Rate character="好" allowHalf />
|
||||
</div>,
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -17,13 +17,13 @@ Support set allow to clear star when click again.
|
||||
import { Rate } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<>
|
||||
<Rate defaultValue={3} />
|
||||
<span className="ant-rate-text">allowClear: true</span>
|
||||
<br />
|
||||
<Rate allowClear={false} defaultValue={3} />
|
||||
<span className="ant-rate-text">allowClear: false</span>
|
||||
</div>,
|
||||
</>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
@ -14,24 +14,24 @@ Rate component.
|
||||
|
||||
## API
|
||||
|
||||
| Property | Description | type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| allowClear | whether to allow clear when click again | boolean | true |
|
||||
| allowHalf | whether to allow semi selection | boolean | false |
|
||||
| autoFocus | get focus when component mounted | boolean | false |
|
||||
| character | custom character of rate | ReactNode | [`<StarFilled />`](/components/icon/) |
|
||||
| className | custom class name of rate | string | |
|
||||
| count | star count | number | 5 |
|
||||
| defaultValue | default value | number | 0 |
|
||||
| disabled | read only, unable to interact | boolean | false |
|
||||
| style | custom style object of rate | CSSProperties | |
|
||||
| tooltips | Customize tooltip by each character | string\[] | |
|
||||
| value | current value | number | |
|
||||
| onBlur | callback when component lose focus | Function() | |
|
||||
| onChange | callback when select value | Function(value: number) | |
|
||||
| onFocus | callback when component get focus | Function() | |
|
||||
| onHoverChange | callback when hover item | Function(value: number) | |
|
||||
| onKeyDown | callback when keydown on component | Function(event) | |
|
||||
| Property | Description | type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| allowClear | whether to allow clear when click again | boolean | true | |
|
||||
| allowHalf | whether to allow semi selection | boolean | false | |
|
||||
| autoFocus | get focus when component mounted | boolean | false | |
|
||||
| character | custom character of rate | ReactNode \| (RateProps) => ReactNode | [`<StarFilled />`](/components/icon/) | Function(): 4.4.0 |
|
||||
| className | custom class name of rate | string | | |
|
||||
| count | star count | number | 5 | |
|
||||
| defaultValue | default value | number | 0 | |
|
||||
| disabled | read only, unable to interact | boolean | false | |
|
||||
| style | custom style object of rate | CSSProperties | | |
|
||||
| tooltips | Customize tooltip by each character | string\[] | | |
|
||||
| value | current value | number | | |
|
||||
| onBlur | callback when component lose focus | Function() | | |
|
||||
| onChange | callback when select value | Function(value: number) | | |
|
||||
| onFocus | callback when component get focus | Function() | | |
|
||||
| onHoverChange | callback when hover item | Function(value: number) | | |
|
||||
| onKeyDown | callback when keydown on component | Function(event) | | |
|
||||
|
||||
## Methods
|
||||
|
||||
|
@ -15,24 +15,24 @@ cover: https://gw.alipayobjects.com/zos/alicdn/R5uiIWmxe/Rate.svg
|
||||
|
||||
## API
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| allowClear | 是否允许再次点击后清除 | boolean | true |
|
||||
| allowHalf | 是否允许半选 | boolean | false |
|
||||
| autoFocus | 自动获取焦点 | boolean | false |
|
||||
| character | 自定义字符 | ReactNode | [`<StarFilled />`](/components/icon/) |
|
||||
| className | 自定义样式类名 | string | |
|
||||
| count | star 总数 | number | 5 |
|
||||
| defaultValue | 默认值 | number | 0 |
|
||||
| disabled | 只读,无法进行交互 | boolean | false |
|
||||
| style | 自定义样式对象 | CSSProperties | |
|
||||
| tooltips | 自定义每项的提示信息 | string\[] | |
|
||||
| value | 当前数,受控值 | number | |
|
||||
| onBlur | 失去焦点时的回调 | Function() | |
|
||||
| onChange | 选择时的回调 | Function(value: number) | |
|
||||
| onFocus | 获取焦点时的回调 | Function() | |
|
||||
| onHoverChange | 鼠标经过时数值变化的回调 | Function(value: number) | |
|
||||
| onKeyDown | 按键回调 | Function(event) | |
|
||||
| 属性 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| allowClear | 是否允许再次点击后清除 | boolean | true | |
|
||||
| allowHalf | 是否允许半选 | boolean | false | |
|
||||
| autoFocus | 自动获取焦点 | boolean | false | |
|
||||
| character | 自定义字符 | ReactNode \| (RateProps) => ReactNode | [`<StarFilled />`](/components/icon/) | Function(): 4.4.0 |
|
||||
| className | 自定义样式类名 | string | | |
|
||||
| count | star 总数 | number | 5 | |
|
||||
| defaultValue | 默认值 | number | 0 | |
|
||||
| disabled | 只读,无法进行交互 | boolean | false | |
|
||||
| style | 自定义样式对象 | CSSProperties | | |
|
||||
| tooltips | 自定义每项的提示信息 | string\[] | | |
|
||||
| value | 当前数,受控值 | number | | |
|
||||
| onBlur | 失去焦点时的回调 | Function() | | |
|
||||
| onChange | 选择时的回调 | Function(value: number) | | |
|
||||
| onFocus | 获取焦点时的回调 | Function() | | |
|
||||
| onHoverChange | 鼠标经过时数值变化的回调 | Function(value: number) | | |
|
||||
| onKeyDown | 按键回调 | Function(event) | | |
|
||||
|
||||
## 方法
|
||||
|
||||
|
@ -1108,6 +1108,26 @@ exports[`renders ./components/result/demo/error.md correctly 1`] = `
|
||||
>
|
||||
Please check and modify the following information before resubmitting.
|
||||
</div>
|
||||
<div
|
||||
class="ant-result-extra"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Go Console
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="ant-btn"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Buy Again
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-result-content"
|
||||
>
|
||||
@ -1190,26 +1210,6 @@ exports[`renders ./components/result/demo/error.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-result-extra"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Go Console
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="ant-btn"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Buy Again
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -104,8 +104,8 @@ const Result: ResultType = props => (
|
||||
{renderIcon(prefixCls, props)}
|
||||
<div className={`${prefixCls}-title`}>{title}</div>
|
||||
{subTitle && <div className={`${prefixCls}-subtitle`}>{subTitle}</div>}
|
||||
{children && <div className={`${prefixCls}-content`}>{children}</div>}
|
||||
{renderExtra(prefixCls, props)}
|
||||
{children && <div className={`${prefixCls}-content`}>{children}</div>}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
|
@ -741,7 +741,7 @@ exports[`renders ./components/select/demo/custom-tag-render.md correctly 1`] = `
|
||||
gold
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
class="anticon anticon-close ant-tag-close-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
@ -770,7 +770,7 @@ exports[`renders ./components/select/demo/custom-tag-render.md correctly 1`] = `
|
||||
cyan
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
class="anticon anticon-close ant-tag-close-icon"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user