mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 14:13:37 +08:00
Merge pull request #27644 from ant-design/master
chore: merge master into feature
This commit is contained in:
commit
0e6e185da2
@ -15,6 +15,25 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.8.1
|
||||
|
||||
`2020-11-08`
|
||||
|
||||
- 🛠 Refactor TreeSelect with React hooks. [#27593](https://github.com/ant-design/ant-design/pull/27593)
|
||||
- 🛠 Refactor Layout with React hooks. [#27595](https://github.com/ant-design/ant-design/pull/27595)
|
||||
- 🐞 Fix Select abnormal outline style in Form validation. [#27607](https://github.com/ant-design/ant-design/pull/27607)
|
||||
- 🐞 Fix Pagination extra `margin-right` when enable size changer. [#27610](https://github.com/ant-design/ant-design/pull/27610)
|
||||
- 🐞 Fix Input.Search `enterButton={null}` throw error. [#27591](https://github.com/ant-design/ant-design/pull/27591) [@davidebianchi](https://github.com/davidebianchi)
|
||||
- 🐞 fix Avatar.Group `size` not working. [#27531](https://github.com/ant-design/ant-design/pull/27531)
|
||||
- 🐞 Fix vertical Tabs long title cause tab width changes. [#27569](https://github.com/ant-design/ant-design/pull/27569)
|
||||
- 🐞 Fix Table filters didn't display in `column.children`. [#27435](https://github.com/ant-design/ant-design/pull/27435) [@JhonXY](https://github.com/JhonXY)
|
||||
- 💄 Fix Steps style that inside another Steps. [#27514](https://github.com/ant-design/ant-design/pull/27514)
|
||||
- TypeScript
|
||||
- 🤖 Fix Select `ref` TS error. [#27482](https://github.com/ant-design/ant-design/pull/27482)
|
||||
- 🤖 Fix Avatar `src` type. [#27524](https://github.com/ant-design/ant-design/pull/27524) [@n0ruSh](https://github.com/n0ruSh)
|
||||
- RTL
|
||||
- 💄 Fix Progress.Line `strokeColor` direction error in RTL mode. [#27515](https://github.com/ant-design/ant-design/pull/27515)
|
||||
|
||||
## 4.8.0
|
||||
|
||||
`2020-11-02`
|
||||
@ -1506,7 +1525,7 @@ Ant Design 4.0-rc released! Here is the release [document](https://github.com/an
|
||||
|
||||
- 🌟 antd package size optimization, js gzipped dropped from 532.75KB to 289.89 KB. [#20356](https://github.com/ant-design/ant-design/pull/20356)
|
||||
- 💄 New dark theme support. [#20281](https://github.com/ant-design/ant-design/pull/20281)
|
||||
- 🌟 ConfigProvider supports `direction` internationalization setting`rtl`. [#19380](https://github.com/ant-design/ant-design/pull/19380)
|
||||
- 🌟 ConfigProvider supports `direction` internationalization setting `rtl`. [#19380](https://github.com/ant-design/ant-design/pull/19380)
|
||||
- 🌟 New Form component. [#17327](https://github.com/ant-design/ant-design/pull/17327)
|
||||
- 🌟 Form comes with data binding function.
|
||||
- 🌟 Field changes only affect the rendering of related field components and not the entire Form.
|
||||
@ -1533,7 +1552,7 @@ Ant Design 4.0-rc released! Here is the release [document](https://github.com/an
|
||||
- 💄 Optimized `expand` animation effect.
|
||||
- 🌟 New DatePicker, TimePicker and Calendar components. [#20023](https://github.com/ant-design/ant-design/pull/20023)
|
||||
- 🌟 Support custom date library.
|
||||
- 🌟 Added `picker` support for setting selectors (no longer need to simulate selectors via controlled`mode`).
|
||||
- 🌟 Added `picker` support for setting selectors (no longer need to simulate selectors via controlled `mode`).
|
||||
- 🌟 Full range selector support: time, date, week, month, year.
|
||||
- 🌟 Range selector can now select start and end times individually.
|
||||
- 🌟 The range selector can be set to `disabled` separately for the start and end time.
|
||||
@ -1551,7 +1570,7 @@ Ant Design 4.0-rc released! Here is the release [document](https://github.com/an
|
||||
- 🌟 Uncontrolled mode when `value` is `undefined` now.
|
||||
- 🌟 TreeSelect uses virtual scrolling and optimizes keyboard support. [#19040](https://github.com/ant-design/ant-design/pull/19040)
|
||||
- 🌟 Uncontrolled mode when `value` is `undefined` now.
|
||||
- 🌟 Button adds `default` and`link` styles for `danger`. [#19837](https://github.com/ant-design/ant-design/pull/19837)
|
||||
- 🌟 Button adds `default` and `link` styles for `danger`. [#19837](https://github.com/ant-design/ant-design/pull/19837)
|
||||
- 🌟 Form and ConfigProvider support `size` setting to include component size. [#20570](https://github.com/ant-design/ant-design/pull/20570)
|
||||
- 🌟 Typography adds `suffix` attribute. [#20224](https://github.com/ant-design/ant-design/pull/20224)
|
||||
- 🌟 Progress adds `steps` subcomponent. [#19613](https://github.com/ant-design/ant-design/pull/19613)
|
||||
@ -1560,7 +1579,7 @@ Ant Design 4.0-rc released! Here is the release [document](https://github.com/an
|
||||
- 🌟 Upload supports iconRender to customize icons. [#20034](https://github.com/ant-design/ant-design/pull/20034) [@qq645381995](https://github.com/qq645381995)
|
||||
- 🌟 Tag component preset status color. [#19399](https://github.com/ant-design/ant-design/pull/19399)
|
||||
- 🌟 Grid uses `flex` layout. [#16635](https://github.com/ant-design/ant-design/pull/16635)
|
||||
- 🐞 Fix the display error of Carousel component `dotposition` as`left | right`. [#20645](https://github.com/ant-design/ant-design/pull/20645) [@xrkffgg](https://github.com/xrkffgg)
|
||||
- 🐞 Fix the display error of Carousel component `dotposition` as `left | right`. [#20645](https://github.com/ant-design/ant-design/pull/20645) [@xrkffgg](https://github.com/xrkffgg)
|
||||
- 🐞 Fix Alert style text overflow. [#20318](https://github.com/ant-design/ant-design/pull/20318)
|
||||
- 🙅 Removed warning messages for deprecated APIs. [#17510](https://github.com/ant-design/ant-design/pull/17510)
|
||||
- 🙅 Added warning for Avatar, Button, Modal.method and Result components using v3 strings as icons. [#20226](https://github.com/ant-design/ant-design/pull/20226)
|
||||
|
@ -15,6 +15,25 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.8.1
|
||||
|
||||
`2020-11-08`
|
||||
|
||||
- 🛠 使用 React hooks 重构 TreeSelect。[#27593](https://github.com/ant-design/ant-design/pull/27593)
|
||||
- 🛠 使用 React hooks 重构 Layout。[#27595](https://github.com/ant-design/ant-design/pull/27595)
|
||||
- 🐞 再次修复 Select 组件在 Form 中触发校验时外边框样式异常的问题。[#27607](https://github.com/ant-design/ant-design/pull/27607)
|
||||
- 🐞 修复 Pagination 开启页码切换器时右侧多余的 `margin`。[#27610](https://github.com/ant-design/ant-design/pull/27610)
|
||||
- 🐞 修复 Input.Search `enterButton={null}` 报错的问题。[#27591](https://github.com/ant-design/ant-design/pull/27591) [@davidebianchi](https://github.com/davidebianchi)
|
||||
- 🐞 修复 Avatar.Group `size` 不生效的问题。[#27531](https://github.com/ant-design/ant-design/pull/27531)
|
||||
- 🐞 修复垂直 Tabs 标题文字很长时导致页签宽度跳动的问题。[#27569](https://github.com/ant-design/ant-design/pull/27569)
|
||||
- 🐞 修复 Table `column.children` 内的筛选功能不展示的问题。[#27435](https://github.com/ant-design/ant-design/pull/27435) [@JhonXY](https://github.com/JhonXY)
|
||||
- 💄 修复 Steps 内嵌 Steps 时的样式。[#27514](https://github.com/ant-design/ant-design/pull/27514)
|
||||
- TypeScript
|
||||
- 🤖 修复 Select `ref` 属性 TS 错误。[#27482](https://github.com/ant-design/ant-design/pull/27482)
|
||||
- 🤖 修复 Avatar `src` 类型。[#27524](https://github.com/ant-design/ant-design/pull/27524) [@n0ruSh](https://github.com/n0ruSh)
|
||||
- RTL
|
||||
- 💄 修复 Progress.Line `strokeColor` 在 RTL 模式下方向错误。[#27515](https://github.com/ant-design/ant-design/pull/27515)
|
||||
|
||||
## 4.8.0
|
||||
|
||||
`2020-11-02`
|
||||
@ -35,7 +54,7 @@ timeline: true
|
||||
- 🆕 `modal.update()` 支持函数式更新。[#27163](https://github.com/ant-design/ant-design/pull/27163) [@Mongkii](https://github.com/Mongkii)
|
||||
- 🆕 Modal method 增加 `bodyStyle` 属性。[#27292](https://github.com/ant-design/ant-design/pull/27292)
|
||||
- 🐞 Fix Modal missing `modalRender` prop。[#27272](https://github.com/ant-design/ant-design/pull/27272) [@jieny](https://github.com/jieny)
|
||||
- 🐞 `Modal.config` 中设置的 `rootPrefixCls` 可以对`title`和`content`下使用的 antd 组件生效。[#27376](https://github.com/ant-design/ant-design/pull/27376) [@Chersquwn](https://github.com/Chersquwn)
|
||||
- 🐞 `Modal.config` 中设置的 `rootPrefixCls` 可以对 `title` 和 `content` 下使用的 antd 组件生效。[#27376](https://github.com/ant-design/ant-design/pull/27376) [@Chersquwn](https://github.com/Chersquwn)
|
||||
- Input
|
||||
- 🆕 Input.Textarea 支持 `size` 属性。[#27110](https://github.com/ant-design/ant-design/pull/27110)
|
||||
- 🐞 修复 Input `allowClear` 时 `className` 丢失的问题。[#27462](https://github.com/ant-design/ant-design/pull/27462)
|
||||
@ -297,7 +316,7 @@ timeline: true
|
||||
- Badge
|
||||
- 💄 新增 `@badge-color` less 变量。[#26159](https://github.com/ant-design/ant-design/pull/26159)
|
||||
- 🆕 Badge 新增 `size` 属性用于设置大小。[#25851](https://github.com/ant-design/ant-design/pull/25851) [@moonrailgun](https://github.com/moonrailgun)
|
||||
- 🆕 Tabs 可自定义`tabBarExtraContent` 的渲染位置。[#25138](https://github.com/ant-design/ant-design/pull/25138) [@jesse3mh9a](https://github.com/jesse3mh9a)
|
||||
- 🆕 Tabs 可自定义 `tabBarExtraContent` 的渲染位置。[#25138](https://github.com/ant-design/ant-design/pull/25138) [@jesse3mh9a](https://github.com/jesse3mh9a)
|
||||
- 💄 优化 Descriptions 在内容比较多时的显示效果。[#25903](https://github.com/ant-design/ant-design/pull/25903)
|
||||
- 🆕 message 支持通过 `message.desctroy(key)` 销毁。[#26052](https://github.com/ant-design/ant-design/pull/26052) [@lihqi](https://github.com/lihqi)
|
||||
- 💄 调整 InputNumber 操作栏在 `readOnly` 时为隐藏。[#25998](https://github.com/ant-design/ant-design/pull/25998)
|
||||
|
@ -7,7 +7,7 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
`block`属性将使按钮适合其父宽度。
|
||||
`block` 属性将使按钮适合其父宽度。
|
||||
|
||||
## en-US
|
||||
|
||||
|
@ -23,29 +23,28 @@ exports[`renders ./components/checkbox/demo/basic.md correctly 1`] = `
|
||||
|
||||
exports[`renders ./components/checkbox/demo/check-all.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="site-checkbox-all-wrapper"
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
<span
|
||||
class="ant-checkbox ant-checkbox-indeterminate"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox ant-checkbox-indeterminate"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
Check all
|
||||
</span>
|
||||
</label>
|
||||
</div>,
|
||||
<br />,
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
Check all
|
||||
</span>
|
||||
</label>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal"
|
||||
role="separator"
|
||||
/>,
|
||||
<div
|
||||
class="ant-checkbox-group"
|
||||
>
|
||||
|
@ -14,70 +14,40 @@ title:
|
||||
The `indeterminate` property can help you to achieve a 'check all' effect.
|
||||
|
||||
```jsx
|
||||
import { Checkbox } from 'antd';
|
||||
import { Checkbox, Divider } from 'antd';
|
||||
|
||||
const CheckboxGroup = Checkbox.Group;
|
||||
|
||||
const plainOptions = ['Apple', 'Pear', 'Orange'];
|
||||
const defaultCheckedList = ['Apple', 'Orange'];
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
checkedList: defaultCheckedList,
|
||||
indeterminate: true,
|
||||
checkAll: false,
|
||||
const App = () => {
|
||||
const [checkedList, setCheckedList] = React.useState(defaultCheckedList);
|
||||
const [indeterminate, setIndeterminate] = React.useState(true);
|
||||
const [checkAll, setCheckAll] = React.useState(false);
|
||||
|
||||
const onChange = list => {
|
||||
setCheckedList(list);
|
||||
setIndeterminate(!!list.length && list.length < plainOptions.length);
|
||||
setCheckAll(list.length === plainOptions.length);
|
||||
};
|
||||
|
||||
onChange = checkedList => {
|
||||
this.setState({
|
||||
checkedList,
|
||||
indeterminate: !!checkedList.length && checkedList.length < plainOptions.length,
|
||||
checkAll: checkedList.length === plainOptions.length,
|
||||
});
|
||||
const onCheckAllChange = e => {
|
||||
setCheckedList(e.target.checked ? plainOptions : []);
|
||||
setIndeterminate(false);
|
||||
setCheckAll(e.target.checked);
|
||||
};
|
||||
|
||||
onCheckAllChange = e => {
|
||||
this.setState({
|
||||
checkedList: e.target.checked ? plainOptions : [],
|
||||
indeterminate: false,
|
||||
checkAll: e.target.checked,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className="site-checkbox-all-wrapper">
|
||||
<Checkbox
|
||||
indeterminate={this.state.indeterminate}
|
||||
onChange={this.onCheckAllChange}
|
||||
checked={this.state.checkAll}
|
||||
>
|
||||
Check all
|
||||
</Checkbox>
|
||||
</div>
|
||||
<br />
|
||||
<CheckboxGroup
|
||||
options={plainOptions}
|
||||
value={this.state.checkedList}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Checkbox indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll}>
|
||||
Check all
|
||||
</Checkbox>
|
||||
<Divider />
|
||||
<CheckboxGroup options={plainOptions} value={checkedList} onChange={onChange} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<App />, mountNode);
|
||||
```
|
||||
|
||||
```css
|
||||
.site-checkbox-all-wrapper {
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
}
|
||||
```
|
||||
|
||||
<style>
|
||||
[data-theme="dark"] .site-checkbox-all-wrapper {
|
||||
border-bottom: 1px solid #303030;
|
||||
}
|
||||
</style>
|
||||
|
@ -24,8 +24,8 @@ const layout = {
|
||||
const validateMessages = {
|
||||
required: '${label} is required!',
|
||||
types: {
|
||||
email: '${label} is not validate email!',
|
||||
number: '${label} is not a validate number!',
|
||||
email: '${label} is not a valid email!',
|
||||
number: '${label} is not a valid number!',
|
||||
},
|
||||
number: {
|
||||
range: '${label} must be between ${min} and ${max}',
|
||||
|
@ -134,8 +134,8 @@
|
||||
animation-name: diffZoomIn3 !important;
|
||||
}
|
||||
|
||||
//select
|
||||
.@{ant-prefix}-select:not(.@{ant-prefix}-select-borderless) {
|
||||
// Select
|
||||
.@{ant-prefix}-select:not(.@{ant-prefix}-select-disabled):not(.@{ant-prefix}-select-customize-input) {
|
||||
.@{ant-prefix}-select-selector {
|
||||
background-color: @form-warning-input-bg;
|
||||
border-color: @warning-color !important;
|
||||
@ -144,13 +144,9 @@
|
||||
&.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector {
|
||||
.active(@warning-color);
|
||||
}
|
||||
&-focused,
|
||||
&:focus {
|
||||
.active(@warning-color);
|
||||
}
|
||||
}
|
||||
|
||||
//input-number, timepicker
|
||||
// InputNumber, TimePicker
|
||||
.@{ant-prefix}-input-number,
|
||||
.@{ant-prefix}-picker {
|
||||
background-color: @form-warning-input-bg;
|
||||
@ -179,8 +175,8 @@
|
||||
animation-name: diffZoomIn2 !important;
|
||||
}
|
||||
|
||||
//select
|
||||
.@{ant-prefix}-select:not(.@{ant-prefix}-select-borderless) {
|
||||
// Select
|
||||
.@{ant-prefix}-select:not(.@{ant-prefix}-select-disabled):not(.@{ant-prefix}-select-customize-input) {
|
||||
.@{ant-prefix}-select-selector {
|
||||
background-color: @form-error-input-bg;
|
||||
border-color: @error-color !important;
|
||||
@ -189,10 +185,6 @@
|
||||
&.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector {
|
||||
.active(@error-color);
|
||||
}
|
||||
&-focused,
|
||||
&:focus {
|
||||
.active(@error-color);
|
||||
}
|
||||
}
|
||||
|
||||
// fixes https://github.com/ant-design/ant-design/issues/20482
|
||||
@ -209,7 +201,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
//input-number, timepicker
|
||||
// InputNumber, TimePicker
|
||||
.@{ant-prefix}-input-number,
|
||||
.@{ant-prefix}-picker {
|
||||
background-color: @form-error-input-bg;
|
||||
|
@ -13,7 +13,7 @@ title:
|
||||
|
||||
## en-US
|
||||
|
||||
`span` `pull` `push` `offset` `order` property can be embedded into `xs` `sm` `md` `lg` `xl` properties to use, where `xs={6}` is equivalent to `xs={{span: 6}}`.
|
||||
`span` `pull` `push` `offset` `order` property can be embedded into `xs` `sm` `md` `lg` `xl` `xxl` properties to use, where `xs={6}` is equivalent to `xs={{span: 6}}`.
|
||||
|
||||
```jsx
|
||||
import { Row, Col } from 'antd';
|
||||
|
@ -7,11 +7,11 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
参照 Bootstrap 的 [响应式设计](http://getbootstrap.com/css/#grid-media-queries),预设六个响应尺寸:`xs` `sm` `md` `lg` `xl` `xxl`。
|
||||
参照 Bootstrap 的 [响应式设计](http://getbootstrap.com/css/#grid-media-queries),预设六个响应尺寸:`xs` `sm` `md` `lg` `xl` `xxl`。
|
||||
|
||||
## en-US
|
||||
|
||||
Referring to the Bootstrap [responsive design](http://getbootstrap.com/css/#grid-media-queries), here preset six dimensions: `xs` `sm` `md` `lg` `xl`.
|
||||
Referring to the Bootstrap [responsive design](http://getbootstrap.com/css/#grid-media-queries), here preset six dimensions: `xs` `sm` `md` `lg` `xl` `xxl`.
|
||||
|
||||
```jsx
|
||||
import { Row, Col } from 'antd';
|
||||
|
@ -13,7 +13,7 @@ title:
|
||||
|
||||
## en-US
|
||||
|
||||
By using `push` and`pull` class you can easily change column order.
|
||||
By using `push` and `pull` class you can easily change column order.
|
||||
|
||||
```jsx
|
||||
import { Row, Col } from 'antd';
|
||||
|
@ -56,7 +56,7 @@ const Search = React.forwardRef<Input, SearchProps>((props, ref) => {
|
||||
const btnClassName = `${prefixCls}-button`;
|
||||
|
||||
let button: React.ReactNode;
|
||||
const enterButtonAsElement = enterButton as React.ReactElement;
|
||||
const enterButtonAsElement = (enterButton || {}) as React.ReactElement;
|
||||
const isAntdButton =
|
||||
enterButtonAsElement.type &&
|
||||
(enterButtonAsElement.type as typeof Button).__ANT_BUTTON === true;
|
||||
|
@ -21,6 +21,12 @@ describe('Input.Search', () => {
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support enterButton null', () => {
|
||||
expect(() => {
|
||||
mount(<Search enterButton={null} />);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should support ReactNode suffix without error', () => {
|
||||
const wrapper = mount(<Search suffix={<div>ok</div>} />);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
|
@ -1,8 +1,21 @@
|
||||
import Layout from './layout';
|
||||
import Sider from './Sider';
|
||||
import InternalLayout, { BasicProps, Content, Footer, Header } from './layout';
|
||||
import Sider, { SiderProps } from './Sider';
|
||||
|
||||
export { BasicProps as LayoutProps } from './layout';
|
||||
export { SiderProps } from './Sider';
|
||||
|
||||
interface LayoutType extends React.FC<BasicProps> {
|
||||
Header: typeof Header;
|
||||
Footer: typeof Footer;
|
||||
Content: typeof Content;
|
||||
Sider: React.ComponentClass<SiderProps>;
|
||||
}
|
||||
|
||||
const Layout = InternalLayout as LayoutType;
|
||||
|
||||
Layout.Header = Header;
|
||||
Layout.Footer = Footer;
|
||||
Layout.Content = Content;
|
||||
Layout.Sider = Sider;
|
||||
|
||||
export default Layout;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { SiderProps } from './Sider';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
export interface GeneratorProps {
|
||||
suffixCls: string;
|
||||
@ -32,28 +31,15 @@ interface BasicPropsWithTagName extends BasicProps {
|
||||
|
||||
function generator({ suffixCls, tagName, displayName }: GeneratorProps) {
|
||||
return (BasicComponent: any) => {
|
||||
return class Adapter extends React.Component<BasicProps, any> {
|
||||
static displayName: string = displayName;
|
||||
const Adapter: React.FC<BasicProps> = props => {
|
||||
const { getPrefixCls } = React.useContext(ConfigContext);
|
||||
const { prefixCls: customizePrefixCls } = props;
|
||||
const prefixCls = getPrefixCls(suffixCls, customizePrefixCls);
|
||||
|
||||
static Header: any;
|
||||
|
||||
static Footer: any;
|
||||
|
||||
static Content: any;
|
||||
|
||||
static Sider: any;
|
||||
|
||||
renderComponent = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { prefixCls: customizePrefixCls } = this.props;
|
||||
const prefixCls = getPrefixCls(suffixCls, customizePrefixCls);
|
||||
|
||||
return <BasicComponent prefixCls={prefixCls} tagName={tagName} {...this.props} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderComponent}</ConfigConsumer>;
|
||||
}
|
||||
return <BasicComponent prefixCls={prefixCls} tagName={tagName} {...props} />;
|
||||
};
|
||||
Adapter.displayName = displayName;
|
||||
return Adapter;
|
||||
};
|
||||
}
|
||||
|
||||
@ -63,60 +49,42 @@ const Basic = (props: BasicPropsWithTagName) => {
|
||||
return React.createElement(tagName, { className: classString, ...others }, children);
|
||||
};
|
||||
|
||||
interface BasicLayoutState {
|
||||
siders: string[];
|
||||
}
|
||||
const BasicLayout: React.FC<BasicPropsWithTagName> = props => {
|
||||
const { direction } = React.useContext(ConfigContext);
|
||||
|
||||
class BasicLayout extends React.Component<BasicPropsWithTagName, BasicLayoutState> {
|
||||
state = { siders: [] };
|
||||
const [siders, setSiders] = React.useState<string[]>([]);
|
||||
|
||||
getSiderHook() {
|
||||
const getSiderHook = () => {
|
||||
return {
|
||||
addSider: (id: string) => {
|
||||
this.setState(state => ({
|
||||
siders: [...state.siders, id],
|
||||
}));
|
||||
setSiders([...siders, id]);
|
||||
},
|
||||
removeSider: (id: string) => {
|
||||
this.setState(state => ({
|
||||
siders: state.siders.filter(currentId => currentId !== id),
|
||||
}));
|
||||
setSiders(siders.filter(currentId => currentId !== id));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
renderComponent = ({ direction }: ConfigConsumerProps) => {
|
||||
const { prefixCls, className, children, hasSider, tagName: Tag, ...others } = this.props;
|
||||
const classString = classNames(
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}-has-sider`]:
|
||||
typeof hasSider === 'boolean' ? hasSider : this.state.siders.length > 0,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
return (
|
||||
<LayoutContext.Provider value={{ siderHook: this.getSiderHook() }}>
|
||||
<Tag className={classString} {...others}>
|
||||
{children}
|
||||
</Tag>
|
||||
</LayoutContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderComponent}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
const { prefixCls, className, children, hasSider, tagName: Tag, ...others } = props;
|
||||
const classString = classNames(
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}-has-sider`]: typeof hasSider === 'boolean' ? hasSider : siders.length > 0,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
const Layout: React.ComponentClass<BasicProps> & {
|
||||
Header: React.ComponentClass<BasicProps>;
|
||||
Footer: React.ComponentClass<BasicProps>;
|
||||
Content: React.ComponentClass<BasicProps>;
|
||||
Sider: React.ComponentClass<SiderProps>;
|
||||
} = generator({
|
||||
return (
|
||||
<LayoutContext.Provider value={{ siderHook: getSiderHook() }}>
|
||||
<Tag className={classString} {...others}>
|
||||
{children}
|
||||
</Tag>
|
||||
</LayoutContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const Layout = generator({
|
||||
suffixCls: 'layout',
|
||||
tagName: 'section',
|
||||
displayName: 'Layout',
|
||||
@ -140,8 +108,6 @@ const Content = generator({
|
||||
displayName: 'Content',
|
||||
})(Basic);
|
||||
|
||||
Layout.Header = Header;
|
||||
Layout.Footer = Footer;
|
||||
Layout.Content = Content;
|
||||
export { Header, Footer, Content };
|
||||
|
||||
export default Layout;
|
||||
|
@ -19,57 +19,46 @@ import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/ico
|
||||
|
||||
const { SubMenu } = Menu;
|
||||
|
||||
class Sider extends React.Component {
|
||||
// submenu keys of first level
|
||||
rootSubmenuKeys = ['sub1', 'sub2', 'sub4'];
|
||||
// submenu keys of first level
|
||||
const rootSubmenuKeys = ['sub1', 'sub2', 'sub4'];
|
||||
|
||||
state = {
|
||||
openKeys: ['sub1'],
|
||||
};
|
||||
const Sider = () => {
|
||||
const [openKeys, setOpenKeys] = React.useState(['sub1']);
|
||||
|
||||
onOpenChange = openKeys => {
|
||||
const latestOpenKey = openKeys.find(key => this.state.openKeys.indexOf(key) === -1);
|
||||
if (this.rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
|
||||
this.setState({ openKeys });
|
||||
const onOpenChange = keys => {
|
||||
const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);
|
||||
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
|
||||
setOpenKeys(keys);
|
||||
} else {
|
||||
this.setState({
|
||||
openKeys: latestOpenKey ? [latestOpenKey] : [],
|
||||
});
|
||||
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Menu
|
||||
mode="inline"
|
||||
openKeys={this.state.openKeys}
|
||||
onOpenChange={this.onOpenChange}
|
||||
style={{ width: 256 }}
|
||||
>
|
||||
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
|
||||
<Menu.Item key="1">Option 1</Menu.Item>
|
||||
<Menu.Item key="2">Option 2</Menu.Item>
|
||||
<Menu.Item key="3">Option 3</Menu.Item>
|
||||
<Menu.Item key="4">Option 4</Menu.Item>
|
||||
return (
|
||||
<Menu mode="inline" openKeys={openKeys} onOpenChange={onOpenChange} style={{ width: 256 }}>
|
||||
<SubMenu key="sub1" icon={<MailOutlined />} title="Navigation One">
|
||||
<Menu.Item key="1">Option 1</Menu.Item>
|
||||
<Menu.Item key="2">Option 2</Menu.Item>
|
||||
<Menu.Item key="3">Option 3</Menu.Item>
|
||||
<Menu.Item key="4">Option 4</Menu.Item>
|
||||
</SubMenu>
|
||||
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
|
||||
<Menu.Item key="5">Option 5</Menu.Item>
|
||||
<Menu.Item key="6">Option 6</Menu.Item>
|
||||
<SubMenu key="sub3" title="Submenu">
|
||||
<Menu.Item key="7">Option 7</Menu.Item>
|
||||
<Menu.Item key="8">Option 8</Menu.Item>
|
||||
</SubMenu>
|
||||
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
|
||||
<Menu.Item key="5">Option 5</Menu.Item>
|
||||
<Menu.Item key="6">Option 6</Menu.Item>
|
||||
<SubMenu key="sub3" title="Submenu">
|
||||
<Menu.Item key="7">Option 7</Menu.Item>
|
||||
<Menu.Item key="8">Option 8</Menu.Item>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<SubMenu key="sub4" icon={<SettingOutlined />} title="Navigation Three">
|
||||
<Menu.Item key="9">Option 9</Menu.Item>
|
||||
<Menu.Item key="10">Option 10</Menu.Item>
|
||||
<Menu.Item key="11">Option 11</Menu.Item>
|
||||
<Menu.Item key="12">Option 12</Menu.Item>
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
}
|
||||
</SubMenu>
|
||||
<SubMenu key="sub4" icon={<SettingOutlined />} title="Navigation Three">
|
||||
<Menu.Item key="9">Option 9</Menu.Item>
|
||||
<Menu.Item key="10">Option 10</Menu.Item>
|
||||
<Menu.Item key="11">Option 11</Menu.Item>
|
||||
<Menu.Item key="12">Option 12</Menu.Item>
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<Sider />, mountNode);
|
||||
```
|
||||
|
@ -25,69 +25,61 @@ import {
|
||||
|
||||
const { SubMenu } = Menu;
|
||||
|
||||
class Sider extends React.Component {
|
||||
state = {
|
||||
mode: 'inline',
|
||||
theme: 'light',
|
||||
const Demo = () => {
|
||||
const [mode, setMode] = React.useState('inline');
|
||||
const [theme, setTheme] = React.useState('light');
|
||||
|
||||
const changeMode = value => {
|
||||
setMode(value ? 'vertical' : 'inline');
|
||||
};
|
||||
|
||||
changeMode = value => {
|
||||
this.setState({
|
||||
mode: value ? 'vertical' : 'inline',
|
||||
});
|
||||
const changeTheme = value => {
|
||||
setTheme(value ? 'dark' : 'light');
|
||||
};
|
||||
|
||||
changeTheme = value => {
|
||||
this.setState({
|
||||
theme: value ? 'dark' : 'light',
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Switch onChange={this.changeMode} /> Change Mode
|
||||
<Divider type="vertical" />
|
||||
<Switch onChange={this.changeTheme} /> Change Style
|
||||
<br />
|
||||
<br />
|
||||
<Menu
|
||||
style={{ width: 256 }}
|
||||
defaultSelectedKeys={['1']}
|
||||
defaultOpenKeys={['sub1']}
|
||||
mode={this.state.mode}
|
||||
theme={this.state.theme}
|
||||
>
|
||||
<Menu.Item key="1" icon={<MailOutlined />}>
|
||||
Navigation One
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2" icon={<CalendarOutlined />}>
|
||||
Navigation Two
|
||||
</Menu.Item>
|
||||
<SubMenu key="sub1" icon={<AppstoreOutlined />} title="Navigation Two">
|
||||
<Menu.Item key="3">Option 3</Menu.Item>
|
||||
<Menu.Item key="4">Option 4</Menu.Item>
|
||||
<SubMenu key="sub1-2" title="Submenu">
|
||||
<Menu.Item key="5">Option 5</Menu.Item>
|
||||
<Menu.Item key="6">Option 6</Menu.Item>
|
||||
</SubMenu>
|
||||
return (
|
||||
<>
|
||||
<Switch onChange={changeMode} /> Change Mode
|
||||
<Divider type="vertical" />
|
||||
<Switch onChange={changeTheme} /> Change Style
|
||||
<br />
|
||||
<br />
|
||||
<Menu
|
||||
style={{ width: 256 }}
|
||||
defaultSelectedKeys={['1']}
|
||||
defaultOpenKeys={['sub1']}
|
||||
mode={mode}
|
||||
theme={theme}
|
||||
>
|
||||
<Menu.Item key="1" icon={<MailOutlined />}>
|
||||
Navigation One
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2" icon={<CalendarOutlined />}>
|
||||
Navigation Two
|
||||
</Menu.Item>
|
||||
<SubMenu key="sub1" icon={<AppstoreOutlined />} title="Navigation Two">
|
||||
<Menu.Item key="3">Option 3</Menu.Item>
|
||||
<Menu.Item key="4">Option 4</Menu.Item>
|
||||
<SubMenu key="sub1-2" title="Submenu">
|
||||
<Menu.Item key="5">Option 5</Menu.Item>
|
||||
<Menu.Item key="6">Option 6</Menu.Item>
|
||||
</SubMenu>
|
||||
<SubMenu key="sub2" icon={<SettingOutlined />} title="Navigation Three">
|
||||
<Menu.Item key="7">Option 7</Menu.Item>
|
||||
<Menu.Item key="8">Option 8</Menu.Item>
|
||||
<Menu.Item key="9">Option 9</Menu.Item>
|
||||
<Menu.Item key="10">Option 10</Menu.Item>
|
||||
</SubMenu>
|
||||
<Menu.Item key="link" icon={<LinkOutlined />}>
|
||||
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
|
||||
Ant Design
|
||||
</a>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
</SubMenu>
|
||||
<SubMenu key="sub2" icon={<SettingOutlined />} title="Navigation Three">
|
||||
<Menu.Item key="7">Option 7</Menu.Item>
|
||||
<Menu.Item key="8">Option 8</Menu.Item>
|
||||
<Menu.Item key="9">Option 9</Menu.Item>
|
||||
<Menu.Item key="10">Option 10</Menu.Item>
|
||||
</SubMenu>
|
||||
<Menu.Item key="link" icon={<LinkOutlined />}>
|
||||
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
|
||||
Ant Design
|
||||
</a>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<Sider />, mountNode);
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
||||
|
@ -63,7 +63,7 @@ More layouts with navigation: [Layout](/components/layout).
|
||||
| key | Unique ID of the menu item | string | - | |
|
||||
| title | Set display title for collapsed item | string | - | |
|
||||
|
||||
> Note: `icon` is a newly added prop in`4.2.0`. For previous versions, please use the following method to define the icon.
|
||||
> Note: `icon` is a newly added prop in `4.2.0`. For previous versions, please use the following method to define the icon.
|
||||
>
|
||||
> ```jsx
|
||||
> <Menu.Item>
|
||||
|
@ -217,6 +217,7 @@
|
||||
&-popup {
|
||||
position: absolute;
|
||||
z-index: @zindex-dropdown;
|
||||
background: transparent;
|
||||
border-radius: @border-radius-base;
|
||||
box-shadow: none;
|
||||
|
||||
|
@ -36,7 +36,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/hAkKTIW0K/Message.svg
|
||||
- `message[level](content, [duration]).then(afterClose)`
|
||||
- `message[level](content, [duration], onClose).then(afterClose)`
|
||||
|
||||
其中`message[level]` 是组件已经提供的静态方法。`then` 接口返回值是 Promise。
|
||||
其中 `message[level]` 是组件已经提供的静态方法。`then` 接口返回值是 Promise。
|
||||
|
||||
也可以对象的形式传递参数:
|
||||
|
||||
|
@ -7,7 +7,7 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
自定义通知框自动关闭的延时,默认`4.5s`,取消自动关闭只要将该值设为 `0` 即可。
|
||||
自定义通知框自动关闭的延时,默认 `4.5s`,取消自动关闭只要将该值设为 `0` 即可。
|
||||
|
||||
## en-US
|
||||
|
||||
|
@ -181,6 +181,7 @@
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
background-color: @pagination-item-link-bg;
|
||||
@ -230,12 +231,12 @@
|
||||
&-size-changer.@{ant-prefix}-select {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&-quick-jumper {
|
||||
display: inline-block;
|
||||
height: @input-height-base;
|
||||
margin-left: @margin-xs;
|
||||
line-height: @input-height-base;
|
||||
vertical-align: top;
|
||||
|
||||
|
@ -50,6 +50,10 @@
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-quick-jumper {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-simple &-simple-pager {
|
||||
|
@ -66,7 +66,12 @@
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
|
||||
&:not(.@{select-prefix-cls}-disabled):hover &-selector {
|
||||
&:not(&-customize-input) &-selector {
|
||||
.select-selector;
|
||||
.select-search-input-without-border;
|
||||
}
|
||||
|
||||
&:not(&-disabled):hover &-selector {
|
||||
.hover();
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,6 @@
|
||||
&-multiple {
|
||||
// ========================= Selector =========================
|
||||
.@{select-prefix-cls}-selector {
|
||||
.select-selector();
|
||||
.select-search-input-without-border();
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
@ -76,10 +76,7 @@
|
||||
// Not customize
|
||||
&:not(.@{select-prefix-cls}-customize-input) {
|
||||
.@{select-prefix-cls}-selector {
|
||||
.select-selector();
|
||||
.select-search-input-without-border();
|
||||
width: 100%;
|
||||
|
||||
height: @input-height-base;
|
||||
padding: 0 @input-padding-horizontal-base;
|
||||
|
||||
|
@ -9,11 +9,11 @@
|
||||
.ant-motion-collapse-legacy {
|
||||
overflow: hidden;
|
||||
&-active {
|
||||
transition: height 0.15s @ease-in-out, opacity 0.15s @ease-in-out !important;
|
||||
transition: height @animation-duration-base @ease-in-out, opacity @animation-duration-base @ease-in-out !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-motion-collapse {
|
||||
overflow: hidden;
|
||||
transition: height 0.15s @ease-in-out, opacity 0.15s @ease-in-out !important;
|
||||
transition: height @animation-duration-base @ease-in-out, opacity @animation-duration-base @ease-in-out !important;
|
||||
}
|
||||
|
@ -16,29 +16,23 @@ Disabled state of `Switch`.
|
||||
```jsx
|
||||
import { Switch, Button } from 'antd';
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
disabled: true,
|
||||
const App = () => {
|
||||
const [disabled, setDisabled] = React.useState(true);
|
||||
|
||||
const toggle = () => {
|
||||
setDisabled(!disabled);
|
||||
};
|
||||
|
||||
toggle = () => {
|
||||
this.setState({
|
||||
disabled: !this.state.disabled,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Switch disabled={this.state.disabled} defaultChecked />
|
||||
<br />
|
||||
<Button type="primary" onClick={this.toggle}>
|
||||
Toggle disabled
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Switch disabled={disabled} defaultChecked />
|
||||
<br />
|
||||
<Button type="primary" onClick={toggle}>
|
||||
Toggle disabled
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<App />, mountNode);
|
||||
```
|
||||
|
@ -11,7 +11,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/zNdJQMhfm/Switch.svg
|
||||
## 何时使用
|
||||
|
||||
- 需要表示开关状态/两种状态之间的切换时;
|
||||
- 和 `checkbox`的区别是,切换 `switch` 会直接触发状态改变,而 `checkbox` 一般用于状态标记,需要和提交操作配合。
|
||||
- 和 `checkbox` 的区别是,切换 `switch` 会直接触发状态改变,而 `checkbox` 一般用于状态标记,需要和提交操作配合。
|
||||
|
||||
## API
|
||||
|
||||
|
@ -141,8 +141,8 @@ const columns = [
|
||||
| render | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return 里面可以设置表格[行/列合并](#components-table-demo-colspan-rowspan) | function(text, record, index) {} | - | |
|
||||
| responsive | 响应式 breakpoint 配置列表。未设置则始终可见。 | [Breakpoint](https://github.com/ant-design/ant-design/blob/015109b42b85c63146371b4e32b883cf97b088e8/components/_util/responsiveObserve.ts#L1)\[] | - | 4.2.0 |
|
||||
| shouldCellUpdate | 自定义单元格渲染时机 | (record, prevRecord) => boolean | - | 4.3.0 |
|
||||
| showSorterTooltip | 表头显示下一次排序的 tooltip 提示, 覆盖 table 中`showSorterTooltip` | boolean | true | |
|
||||
| sortDirections | 支持的排序方式,覆盖`Table`中`sortDirections`, 取值为 `ascend` `descend` | Array | \[`ascend`, `descend`] | |
|
||||
| showSorterTooltip | 表头显示下一次排序的 tooltip 提示, 覆盖 table 中 `showSorterTooltip` | boolean | true | |
|
||||
| sortDirections | 支持的排序方式,覆盖 `Table` 中 `sortDirections`, 取值为 `ascend` `descend` | Array | \[`ascend`, `descend`] | |
|
||||
| sorter | 排序函数,本地排序使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),需要服务端排序可设为 true | function \| boolean | - | |
|
||||
| sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 `ascend` `descend` false | boolean \| string | - | |
|
||||
| title | 列头显示文字(函数用法 `3.10.0` 后支持) | ReactNode \| ({ sortOrder, sortColumn, filters }) => ReactNode | - | |
|
||||
|
@ -12,7 +12,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Vyyeu8jq2/Tooltp.svg
|
||||
|
||||
鼠标移入则显示提示,移出消失,气泡浮层不承载复杂文本和操作。
|
||||
|
||||
可用来代替系统默认的 `title` 提示,提供一个`按钮/文字/操作`的文案解释。
|
||||
可用来代替系统默认的 `title` 提示,提供一个 `按钮/文字/操作` 的文案解释。
|
||||
|
||||
## API
|
||||
|
||||
|
@ -2,18 +2,18 @@ import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { ElementOf, Omit, tuple } from '../_util/type';
|
||||
import Pagination from '../pagination';
|
||||
import { TransferItem } from '.';
|
||||
import { TransferListProps, RenderedItem } from './list';
|
||||
import ListItem from './ListItem';
|
||||
import { PaginationType } from './interface';
|
||||
import { KeyWiseTransferItem } from '.';
|
||||
|
||||
export const OmitProps = tuple('handleFilter', 'handleClear', 'checkedKeys');
|
||||
export type OmitProp = ElementOf<typeof OmitProps>;
|
||||
type PartialTransferListProps = Omit<TransferListProps, OmitProp>;
|
||||
type PartialTransferListProps<RecordType> = Omit<TransferListProps<RecordType>, OmitProp>;
|
||||
|
||||
export interface TransferListBodyProps extends PartialTransferListProps {
|
||||
filteredItems: TransferItem[];
|
||||
filteredRenderItems: RenderedItem[];
|
||||
export interface TransferListBodyProps<RecordType> extends PartialTransferListProps<RecordType> {
|
||||
filteredItems: RecordType[];
|
||||
filteredRenderItems: RenderedItem<RecordType>[];
|
||||
selectedKeys: string[];
|
||||
}
|
||||
|
||||
@ -40,13 +40,16 @@ interface TransferListBodyState {
|
||||
current: number;
|
||||
}
|
||||
|
||||
class ListBody extends React.Component<TransferListBodyProps, TransferListBodyState> {
|
||||
class ListBody<RecordType extends KeyWiseTransferItem> extends React.Component<
|
||||
TransferListBodyProps<RecordType>,
|
||||
TransferListBodyState
|
||||
> {
|
||||
state = {
|
||||
current: 1,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(
|
||||
{ filteredRenderItems, pagination }: TransferListBodyProps,
|
||||
static getDerivedStateFromProps<T>(
|
||||
{ filteredRenderItems, pagination }: TransferListBodyProps<T>,
|
||||
{ current }: TransferListBodyState,
|
||||
) {
|
||||
const mergedPagination = parsePagination(pagination);
|
||||
@ -62,13 +65,13 @@ class ListBody extends React.Component<TransferListBodyProps, TransferListBodySt
|
||||
return null;
|
||||
}
|
||||
|
||||
onItemSelect = (item: TransferItem) => {
|
||||
onItemSelect = (item: RecordType) => {
|
||||
const { onItemSelect, selectedKeys } = this.props;
|
||||
const checked = selectedKeys.indexOf(item.key) >= 0;
|
||||
onItemSelect(item.key, !checked);
|
||||
};
|
||||
|
||||
onItemRemove = (item: TransferItem) => {
|
||||
onItemRemove = (item: RecordType) => {
|
||||
const { onItemRemove } = this.props;
|
||||
onItemRemove?.([item.key]);
|
||||
};
|
||||
@ -133,7 +136,7 @@ class ListBody extends React.Component<TransferListBodyProps, TransferListBodySt
|
||||
})}
|
||||
onScroll={onScroll}
|
||||
>
|
||||
{this.getItems().map(({ renderedEl, renderedText, item }: RenderedItem) => {
|
||||
{this.getItems().map(({ renderedEl, renderedText, item }: RenderedItem<RecordType>) => {
|
||||
const { disabled } = item;
|
||||
const checked = selectedKeys.indexOf(item.key) >= 0;
|
||||
|
||||
|
@ -1,25 +1,25 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
|
||||
import { TransferItem, TransferLocale } from '.';
|
||||
import { KeyWiseTransferItem, TransferLocale } from '.';
|
||||
import defaultLocale from '../locale/default';
|
||||
import Checkbox from '../checkbox';
|
||||
import TransButton from '../_util/transButton';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
|
||||
type ListItemProps = {
|
||||
type ListItemProps<RecordType> = {
|
||||
renderedText?: string | number;
|
||||
renderedEl: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
prefixCls: string;
|
||||
onClick: (item: TransferItem) => void;
|
||||
onRemove?: (item: TransferItem) => void;
|
||||
item: TransferItem;
|
||||
onClick: (item: RecordType) => void;
|
||||
onRemove?: (item: RecordType) => void;
|
||||
item: RecordType;
|
||||
showRemove?: boolean;
|
||||
};
|
||||
|
||||
const ListItem = (props: ListItemProps) => {
|
||||
const ListItem = <RecordType extends KeyWiseTransferItem>(props: ListItemProps<RecordType>) => {
|
||||
const {
|
||||
renderedText,
|
||||
renderedEl,
|
||||
|
@ -21,7 +21,7 @@ One or more elements can be selected from either column, one click on the proper
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| dataSource | Used for setting the source data. The elements that are part of this array will be present the left column. Except the elements whose keys are included in `targetKeys` prop | [TransferItem](https://git.io/vMM64)\[] | \[] | |
|
||||
| dataSource | Used for setting the source data. The elements that are part of this array will be present the left column. Except the elements whose keys are included in `targetKeys` prop | [RecordType extends TransferItem = TransferItem](https://git.io/vMM64)\[] | \[] | |
|
||||
| disabled | Whether disabled transfer | boolean | false | |
|
||||
| filterOption | A function to determine whether an item should show in search result list | (inputValue, option): boolean | - | |
|
||||
| footer | A function used for rendering the footer | (props) => ReactNode | - | |
|
||||
@ -51,7 +51,7 @@ Transfer accept `children` to customize render list, using follow props:
|
||||
| --- | --- | --- | --- |
|
||||
| direction | List render direction | `left` \| `right` | |
|
||||
| disabled | Disable list or not | boolean | |
|
||||
| filteredItems | Filtered items | TransferItem\[] | |
|
||||
| filteredItems | Filtered items | RecordType\[] | |
|
||||
| selectedKeys | Selected items | string\[] | |
|
||||
| onItemSelect | Select item | (key: string, selected: boolean) | |
|
||||
| onItemSelectAll | Select a group of items | (keys: string\[], selected: boolean) | |
|
||||
|
@ -24,14 +24,18 @@ export interface RenderResultObject {
|
||||
export type RenderResult = React.ReactElement | RenderResultObject | string | null;
|
||||
|
||||
export interface TransferItem {
|
||||
key: string;
|
||||
key?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
disabled?: boolean;
|
||||
[name: string]: any;
|
||||
}
|
||||
|
||||
type TransferRender = (item: TransferItem) => RenderResult;
|
||||
export type KeyWise<T> = T & { key: string };
|
||||
|
||||
export type KeyWiseTransferItem = KeyWise<TransferItem>;
|
||||
|
||||
type TransferRender<RecordType> = (item: RecordType) => RenderResult;
|
||||
|
||||
export interface ListStyle {
|
||||
direction: TransferDirection;
|
||||
@ -55,14 +59,14 @@ export interface TransferLocale {
|
||||
removeCurrent: string;
|
||||
}
|
||||
|
||||
export interface TransferProps {
|
||||
export interface TransferProps<RecordType> {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
dataSource: TransferItem[];
|
||||
dataSource: RecordType[];
|
||||
targetKeys?: string[];
|
||||
selectedKeys?: string[];
|
||||
render?: TransferRender;
|
||||
render?: TransferRender<RecordType>;
|
||||
onChange?: (targetKeys: string[], direction: TransferDirection, moveKeys: string[]) => void;
|
||||
onSelectChange?: (sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void;
|
||||
style?: React.CSSProperties;
|
||||
@ -71,13 +75,13 @@ export interface TransferProps {
|
||||
titles?: string[];
|
||||
operations?: string[];
|
||||
showSearch?: boolean;
|
||||
filterOption?: (inputValue: string, item: TransferItem) => boolean;
|
||||
filterOption?: (inputValue: string, item: RecordType) => boolean;
|
||||
locale?: Partial<TransferLocale>;
|
||||
footer?: (props: TransferListProps) => React.ReactNode;
|
||||
rowKey?: (record: TransferItem) => string;
|
||||
footer?: (props: TransferListProps<RecordType>) => React.ReactNode;
|
||||
rowKey?: (record: RecordType) => string;
|
||||
onSearch?: (direction: TransferDirection, value: string) => void;
|
||||
onScroll?: (direction: TransferDirection, e: React.SyntheticEvent<HTMLUListElement>) => void;
|
||||
children?: (props: TransferListBodyProps) => React.ReactNode;
|
||||
children?: (props: TransferListBodyProps<RecordType>) => React.ReactNode;
|
||||
showSelectAll?: boolean;
|
||||
selectAllLabels?: SelectAllLabel[];
|
||||
oneWay?: boolean;
|
||||
@ -89,7 +93,10 @@ interface TransferState {
|
||||
targetSelectedKeys: string[];
|
||||
}
|
||||
|
||||
class Transfer extends React.Component<TransferProps, TransferState> {
|
||||
class Transfer<RecordType extends TransferItem = TransferItem> extends React.Component<
|
||||
TransferProps<RecordType>,
|
||||
TransferState
|
||||
> {
|
||||
// For high-level customized Transfer @dqaria
|
||||
static List = List;
|
||||
|
||||
@ -104,12 +111,12 @@ class Transfer extends React.Component<TransferProps, TransferState> {
|
||||
listStyle: () => {},
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps({
|
||||
static getDerivedStateFromProps<T>({
|
||||
selectedKeys,
|
||||
targetKeys,
|
||||
pagination,
|
||||
children,
|
||||
}: TransferProps) {
|
||||
}: TransferProps<T>) {
|
||||
if (selectedKeys) {
|
||||
const mergedTargetKeys = targetKeys || [];
|
||||
return {
|
||||
@ -128,11 +135,11 @@ class Transfer extends React.Component<TransferProps, TransferState> {
|
||||
}
|
||||
|
||||
separatedDataSource: {
|
||||
leftDataSource: TransferItem[];
|
||||
rightDataSource: TransferItem[];
|
||||
leftDataSource: RecordType[];
|
||||
rightDataSource: RecordType[];
|
||||
} | null = null;
|
||||
|
||||
constructor(props: TransferProps) {
|
||||
constructor(props: TransferProps<RecordType>) {
|
||||
super(props);
|
||||
|
||||
const { selectedKeys = [], targetKeys = [] } = props;
|
||||
@ -318,9 +325,9 @@ class Transfer extends React.Component<TransferProps, TransferState> {
|
||||
separateDataSource() {
|
||||
const { dataSource, rowKey, targetKeys = [] } = this.props;
|
||||
|
||||
const leftDataSource: TransferItem[] = [];
|
||||
const rightDataSource: TransferItem[] = new Array(targetKeys.length);
|
||||
dataSource.forEach(record => {
|
||||
const leftDataSource: KeyWise<RecordType>[] = [];
|
||||
const rightDataSource: KeyWise<RecordType>[] = new Array(targetKeys.length);
|
||||
dataSource.forEach((record: KeyWise<RecordType>) => {
|
||||
if (rowKey) {
|
||||
record.key = rowKey(record);
|
||||
}
|
||||
@ -385,7 +392,7 @@ class Transfer extends React.Component<TransferProps, TransferState> {
|
||||
const selectAllLabels = this.props.selectAllLabels || [];
|
||||
return (
|
||||
<div className={cls} style={style}>
|
||||
<List
|
||||
<List<KeyWise<RecordType>>
|
||||
prefixCls={`${prefixCls}-list`}
|
||||
titleText={titles[0]}
|
||||
dataSource={leftDataSource}
|
||||
@ -421,7 +428,7 @@ class Transfer extends React.Component<TransferProps, TransferState> {
|
||||
direction={direction}
|
||||
oneWay={oneWay}
|
||||
/>
|
||||
<List
|
||||
<List<KeyWise<RecordType>>
|
||||
prefixCls={`${prefixCls}-list`}
|
||||
titleText={titles[1]}
|
||||
dataSource={rightDataSource}
|
||||
|
@ -24,7 +24,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QAXskNI4G/Transfer.svg
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| dataSource | 数据源,其中的数据将会被渲染到左边一栏中,`targetKeys` 中指定的除外 | [TransferItem](https://git.io/vMM64)\[] | \[] | |
|
||||
| dataSource | 数据源,其中的数据将会被渲染到左边一栏中,`targetKeys` 中指定的除外 | [RecordType extends TransferItem = TransferItem](https://git.io/vMM64)\[] | \[] | |
|
||||
| disabled | 是否禁用 | boolean | false | |
|
||||
| filterOption | 接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true,反之则返回 false | (inputValue, option): boolean | - | |
|
||||
| footer | 底部渲染函数 | (props) => ReactNode | - | |
|
||||
@ -53,7 +53,7 @@ Transfer 支持接收 `children` 自定义渲染列表,并返回以下参数
|
||||
| --- | --- | --- | --- |
|
||||
| direction | 渲染列表的方向 | `left` \| `right` | |
|
||||
| disabled | 是否禁用列表 | boolean | |
|
||||
| filteredItems | 过滤后的数据 | TransferItem\[] | |
|
||||
| filteredItems | 过滤后的数据 | RecordType\[] | |
|
||||
| selectedKeys | 选中的条目 | string\[] | |
|
||||
| onItemSelect | 勾选条目 | (key: string, selected: boolean) | |
|
||||
| onItemSelectAll | 勾选一组条目 | (keys: string\[], selected: boolean) | |
|
||||
@ -66,7 +66,7 @@ Transfer 支持接收 `children` 自定义渲染列表,并返回以下参数
|
||||
|
||||
## 注意
|
||||
|
||||
按照 React 的[规范](http://facebook.github.io/react/docs/lists-and-keys.html#keys),所有的组件数组必须绑定 key。在 Transfer 中,`dataSource`里的数据值需要指定 `key` 值。对于 `dataSource` 默认将每列数据的 `key` 属性作为唯一的标识。
|
||||
按照 React 的[规范](http://facebook.github.io/react/docs/lists-and-keys.html#keys),所有的组件数组必须绑定 key。在 Transfer 中,`dataSource` 里的数据值需要指定 `key` 值。对于 `dataSource` 默认将每列数据的 `key` 属性作为唯一的标识。
|
||||
|
||||
如果你的数据没有这个属性,务必使用 `rowKey` 来指定数据列的主键。
|
||||
|
||||
|
@ -6,12 +6,12 @@ import Checkbox from '../checkbox';
|
||||
import Menu from '../menu';
|
||||
import Dropdown from '../dropdown';
|
||||
import {
|
||||
TransferItem,
|
||||
TransferDirection,
|
||||
RenderResult,
|
||||
RenderResultObject,
|
||||
SelectAllLabel,
|
||||
TransferLocale,
|
||||
KeyWiseTransferItem,
|
||||
} from './index';
|
||||
import Search from './search';
|
||||
import DefaultListBody, { TransferListBodyProps, OmitProps } from './ListBody';
|
||||
@ -28,23 +28,23 @@ function isRenderResultPlainObject(result: RenderResult) {
|
||||
);
|
||||
}
|
||||
|
||||
function getEnabledItemKeys(items: TransferItem[]) {
|
||||
function getEnabledItemKeys<RecordType extends KeyWiseTransferItem>(items: RecordType[]) {
|
||||
return items.filter(data => !data.disabled).map(data => data.key);
|
||||
}
|
||||
|
||||
export interface RenderedItem {
|
||||
export interface RenderedItem<RecordType> {
|
||||
renderedText: string;
|
||||
renderedEl: React.ReactNode;
|
||||
item: TransferItem;
|
||||
item: RecordType;
|
||||
}
|
||||
|
||||
type RenderListFunction = (props: TransferListBodyProps) => React.ReactNode;
|
||||
type RenderListFunction<T> = (props: TransferListBodyProps<T>) => React.ReactNode;
|
||||
|
||||
export interface TransferListProps extends TransferLocale {
|
||||
export interface TransferListProps<RecordType> extends TransferLocale {
|
||||
prefixCls: string;
|
||||
titleText: string;
|
||||
dataSource: TransferItem[];
|
||||
filterOption?: (filterText: string, item: TransferItem) => boolean;
|
||||
dataSource: RecordType[];
|
||||
filterOption?: (filterText: string, item: RecordType) => boolean;
|
||||
style?: React.CSSProperties;
|
||||
checkedKeys: string[];
|
||||
handleFilter: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
@ -53,13 +53,13 @@ export interface TransferListProps extends TransferLocale {
|
||||
onItemRemove?: (keys: string[]) => void;
|
||||
handleClear: () => void;
|
||||
/** render item */
|
||||
render?: (item: TransferItem) => RenderResult;
|
||||
render?: (item: RecordType) => RenderResult;
|
||||
showSearch?: boolean;
|
||||
searchPlaceholder: string;
|
||||
itemUnit: string;
|
||||
itemsUnit: string;
|
||||
renderList?: RenderListFunction;
|
||||
footer?: (props: TransferListProps) => React.ReactNode;
|
||||
renderList?: RenderListFunction<RecordType>;
|
||||
footer?: (props: TransferListProps<RecordType>) => React.ReactNode;
|
||||
onScroll: (e: React.UIEvent<HTMLUListElement>) => void;
|
||||
disabled?: boolean;
|
||||
direction: TransferDirection;
|
||||
@ -74,10 +74,9 @@ interface TransferListState {
|
||||
filterValue: string;
|
||||
}
|
||||
|
||||
export default class TransferList extends React.PureComponent<
|
||||
TransferListProps,
|
||||
TransferListState
|
||||
> {
|
||||
export default class TransferList<
|
||||
RecordType extends KeyWiseTransferItem
|
||||
> extends React.PureComponent<TransferListProps<RecordType>, TransferListState> {
|
||||
static defaultProps = {
|
||||
dataSource: [],
|
||||
titleText: '',
|
||||
@ -88,9 +87,9 @@ export default class TransferList extends React.PureComponent<
|
||||
|
||||
triggerScrollTimer: number;
|
||||
|
||||
defaultListBodyRef = React.createRef<DefaultListBody>();
|
||||
defaultListBodyRef = React.createRef<DefaultListBody<RecordType>>();
|
||||
|
||||
constructor(props: TransferListProps) {
|
||||
constructor(props: TransferListProps<RecordType>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
filterValue: '',
|
||||
@ -101,7 +100,7 @@ export default class TransferList extends React.PureComponent<
|
||||
clearTimeout(this.triggerScrollTimer);
|
||||
}
|
||||
|
||||
getCheckStatus(filteredItems: TransferItem[]) {
|
||||
getCheckStatus(filteredItems: RecordType[]) {
|
||||
const { checkedKeys } = this.props;
|
||||
if (checkedKeys.length === 0) {
|
||||
return 'none';
|
||||
@ -114,11 +113,11 @@ export default class TransferList extends React.PureComponent<
|
||||
|
||||
// ================================ Item ================================
|
||||
getFilteredItems(
|
||||
dataSource: TransferItem[],
|
||||
dataSource: RecordType[],
|
||||
filterValue: string,
|
||||
): { filteredItems: TransferItem[]; filteredRenderItems: RenderedItem[] } {
|
||||
const filteredItems: TransferItem[] = [];
|
||||
const filteredRenderItems: RenderedItem[] = [];
|
||||
): { filteredItems: RecordType[]; filteredRenderItems: RenderedItem<RecordType>[] } {
|
||||
const filteredItems: RecordType[] = [];
|
||||
const filteredRenderItems: RenderedItem<RecordType>[] = [];
|
||||
|
||||
dataSource.forEach(item => {
|
||||
const renderedItem = this.renderItem(item);
|
||||
@ -152,7 +151,7 @@ export default class TransferList extends React.PureComponent<
|
||||
handleClear();
|
||||
};
|
||||
|
||||
matchFilter = (text: string, item: TransferItem) => {
|
||||
matchFilter = (text: string, item: RecordType) => {
|
||||
const { filterValue } = this.state;
|
||||
const { filterOption } = this.props;
|
||||
if (filterOption) {
|
||||
@ -164,7 +163,10 @@ export default class TransferList extends React.PureComponent<
|
||||
getCurrentPageItems = () => {};
|
||||
|
||||
// =============================== Render ===============================
|
||||
renderListBody = (renderList: RenderListFunction | undefined, props: TransferListBodyProps) => {
|
||||
renderListBody = (
|
||||
renderList: RenderListFunction<RecordType> | undefined,
|
||||
props: TransferListBodyProps<RecordType>,
|
||||
) => {
|
||||
let bodyContent: React.ReactNode = renderList ? renderList(props) : null;
|
||||
const customize: boolean = !!bodyContent;
|
||||
if (!customize) {
|
||||
@ -180,11 +182,11 @@ export default class TransferList extends React.PureComponent<
|
||||
prefixCls: string,
|
||||
searchPlaceholder: string,
|
||||
filterValue: string,
|
||||
filteredItems: TransferItem[],
|
||||
filteredItems: RecordType[],
|
||||
notFoundContent: React.ReactNode,
|
||||
filteredRenderItems: RenderedItem[],
|
||||
filteredRenderItems: RenderedItem<RecordType>[],
|
||||
checkedKeys: string[],
|
||||
renderList?: RenderListFunction,
|
||||
renderList?: RenderListFunction<RecordType>,
|
||||
showSearch?: boolean,
|
||||
disabled?: boolean,
|
||||
): React.ReactNode {
|
||||
@ -233,7 +235,7 @@ export default class TransferList extends React.PureComponent<
|
||||
}
|
||||
|
||||
getCheckBox(
|
||||
filteredItems: TransferItem[],
|
||||
filteredItems: RecordType[],
|
||||
onItemSelectAll: (dataSource: string[], checkAll: boolean) => void,
|
||||
showSelectAll?: boolean,
|
||||
disabled?: boolean,
|
||||
@ -258,7 +260,7 @@ export default class TransferList extends React.PureComponent<
|
||||
return checkAllCheckbox;
|
||||
}
|
||||
|
||||
renderItem = (item: TransferItem): RenderedItem => {
|
||||
renderItem = (item: RecordType): RenderedItem<RecordType> => {
|
||||
const { render = defaultRender } = this.props;
|
||||
const renderResult: RenderResult = render(item);
|
||||
const isRenderResultPlain = isRenderResultPlainObject(renderResult);
|
||||
|
@ -6,7 +6,7 @@ import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
|
||||
describe('TreeSelect', () => {
|
||||
focusTest(TreeSelect);
|
||||
focusTest(TreeSelect, { refFocus: true });
|
||||
mountTest(TreeSelect);
|
||||
rtlTest(TreeSelect);
|
||||
|
||||
|
@ -8,7 +8,8 @@ import RcTreeSelect, {
|
||||
} from 'rc-tree-select';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { DefaultValueType } from 'rc-tree-select/lib/interface';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import devWarning from '../_util/devWarning';
|
||||
import { AntTreeNodeProps } from '../tree';
|
||||
import getIcons from '../select/utils/iconUtil';
|
||||
@ -35,167 +36,147 @@ export interface TreeSelectProps<T>
|
||||
bordered?: boolean;
|
||||
}
|
||||
|
||||
class TreeSelect<T> extends React.Component<TreeSelectProps<T>, {}> {
|
||||
static TreeNode = TreeNode;
|
||||
export interface RefTreeSelectProps {
|
||||
focus: () => void;
|
||||
blur: () => void;
|
||||
}
|
||||
|
||||
static SHOW_ALL: typeof SHOW_ALL = SHOW_ALL;
|
||||
|
||||
static SHOW_PARENT: typeof SHOW_PARENT = SHOW_PARENT;
|
||||
|
||||
static SHOW_CHILD: typeof SHOW_CHILD = SHOW_CHILD;
|
||||
|
||||
static defaultProps = {
|
||||
transitionName: 'slide-up',
|
||||
choiceTransitionName: '',
|
||||
bordered: true,
|
||||
};
|
||||
|
||||
selectRef = React.createRef<RcTreeSelect>();
|
||||
|
||||
constructor(props: TreeSelectProps<T>) {
|
||||
super(props);
|
||||
|
||||
devWarning(
|
||||
props.multiple !== false || !props.treeCheckable,
|
||||
'TreeSelect',
|
||||
'`multiple` will alway be `true` when `treeCheckable` is true',
|
||||
);
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this.selectRef.current) {
|
||||
this.selectRef.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
blur() {
|
||||
if (this.selectRef.current) {
|
||||
this.selectRef.current.blur();
|
||||
}
|
||||
}
|
||||
|
||||
renderTreeSelect = ({
|
||||
const InternalTreeSelect = <T extends DefaultValueType>(
|
||||
{
|
||||
prefixCls: customizePrefixCls,
|
||||
size: customizeSize,
|
||||
bordered = true,
|
||||
className,
|
||||
treeCheckable,
|
||||
multiple,
|
||||
listHeight = 256,
|
||||
listItemHeight = 26,
|
||||
notFoundContent,
|
||||
switcherIcon,
|
||||
treeLine,
|
||||
getPopupContainer,
|
||||
dropdownClassName,
|
||||
treeIcon = false,
|
||||
transitionName = 'slide-up',
|
||||
choiceTransitionName = '',
|
||||
...props
|
||||
}: TreeSelectProps<T>,
|
||||
ref: React.Ref<RefTreeSelectProps>,
|
||||
) => {
|
||||
const {
|
||||
getPopupContainer: getContextPopupContainer,
|
||||
getPrefixCls,
|
||||
renderEmpty,
|
||||
direction,
|
||||
virtual,
|
||||
dropdownMatchSelectWidth,
|
||||
}: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
size: customizeSize,
|
||||
className,
|
||||
treeCheckable,
|
||||
multiple,
|
||||
listHeight = 256,
|
||||
listItemHeight = 26,
|
||||
notFoundContent,
|
||||
switcherIcon,
|
||||
treeLine,
|
||||
getPopupContainer,
|
||||
dropdownClassName,
|
||||
bordered,
|
||||
treeIcon = false,
|
||||
} = this.props;
|
||||
} = React.useContext(ConfigContext);
|
||||
const size = React.useContext(SizeContext);
|
||||
|
||||
const prefixCls = getPrefixCls('select', customizePrefixCls);
|
||||
const treePrefixCls = getPrefixCls('select-tree', customizePrefixCls);
|
||||
const treeSelectPrefixCls = getPrefixCls('tree-select', customizePrefixCls);
|
||||
devWarning(
|
||||
multiple !== false || !treeCheckable,
|
||||
'TreeSelect',
|
||||
'`multiple` will alway be `true` when `treeCheckable` is true',
|
||||
);
|
||||
|
||||
const mergedDropdownClassName = classNames(
|
||||
dropdownClassName,
|
||||
`${treeSelectPrefixCls}-dropdown`,
|
||||
{
|
||||
[`${treeSelectPrefixCls}-dropdown-rtl`]: direction === 'rtl',
|
||||
},
|
||||
);
|
||||
const prefixCls = getPrefixCls('select', customizePrefixCls);
|
||||
const treePrefixCls = getPrefixCls('select-tree', customizePrefixCls);
|
||||
const treeSelectPrefixCls = getPrefixCls('tree-select', customizePrefixCls);
|
||||
|
||||
const isMultiple = !!(treeCheckable || multiple);
|
||||
const mergedDropdownClassName = classNames(dropdownClassName, `${treeSelectPrefixCls}-dropdown`, {
|
||||
[`${treeSelectPrefixCls}-dropdown-rtl`]: direction === 'rtl',
|
||||
});
|
||||
|
||||
// ===================== Icons =====================
|
||||
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({
|
||||
...this.props,
|
||||
multiple: isMultiple,
|
||||
prefixCls,
|
||||
});
|
||||
const isMultiple = !!(treeCheckable || multiple);
|
||||
|
||||
// ===================== Empty =====================
|
||||
let mergedNotFound: React.ReactNode;
|
||||
if (notFoundContent !== undefined) {
|
||||
mergedNotFound = notFoundContent;
|
||||
} else {
|
||||
mergedNotFound = renderEmpty('Select');
|
||||
}
|
||||
// ===================== Icons =====================
|
||||
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({
|
||||
...props,
|
||||
multiple: isMultiple,
|
||||
prefixCls,
|
||||
});
|
||||
|
||||
// ==================== Render =====================
|
||||
const selectProps = omit(this.props, [
|
||||
'prefixCls',
|
||||
'suffixIcon',
|
||||
'itemIcon',
|
||||
'removeIcon',
|
||||
'clearIcon',
|
||||
'switcherIcon',
|
||||
'size',
|
||||
'bordered',
|
||||
]);
|
||||
|
||||
return (
|
||||
<SizeContext.Consumer>
|
||||
{size => {
|
||||
const mergedSize = customizeSize || size;
|
||||
const mergedClassName = classNames(
|
||||
!customizePrefixCls && treeSelectPrefixCls,
|
||||
{
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
return (
|
||||
<RcTreeSelect
|
||||
virtual={virtual}
|
||||
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
|
||||
{...selectProps}
|
||||
ref={this.selectRef}
|
||||
prefixCls={prefixCls}
|
||||
className={mergedClassName}
|
||||
listHeight={listHeight}
|
||||
listItemHeight={listItemHeight}
|
||||
treeCheckable={
|
||||
treeCheckable ? (
|
||||
<span className={`${prefixCls}-tree-checkbox-inner`} />
|
||||
) : (
|
||||
treeCheckable
|
||||
)
|
||||
}
|
||||
inputIcon={suffixIcon}
|
||||
menuItemSelectedIcon={itemIcon}
|
||||
removeIcon={removeIcon}
|
||||
clearIcon={clearIcon}
|
||||
switcherIcon={(nodeProps: AntTreeNodeProps) =>
|
||||
renderSwitcherIcon(treePrefixCls, switcherIcon, treeLine, nodeProps)
|
||||
}
|
||||
showTreeIcon={treeIcon}
|
||||
notFoundContent={mergedNotFound}
|
||||
getPopupContainer={getPopupContainer || getContextPopupContainer}
|
||||
treeMotion={null}
|
||||
dropdownClassName={mergedDropdownClassName}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</SizeContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderTreeSelect}</ConfigConsumer>;
|
||||
// ===================== Empty =====================
|
||||
let mergedNotFound: React.ReactNode;
|
||||
if (notFoundContent !== undefined) {
|
||||
mergedNotFound = notFoundContent;
|
||||
} else {
|
||||
mergedNotFound = renderEmpty('Select');
|
||||
}
|
||||
|
||||
// ==================== Render =====================
|
||||
const selectProps = omit(props, [
|
||||
'suffixIcon',
|
||||
'itemIcon',
|
||||
'removeIcon',
|
||||
'clearIcon',
|
||||
'switcherIcon',
|
||||
]);
|
||||
|
||||
const mergedSize = customizeSize || size;
|
||||
const mergedClassName = classNames(
|
||||
!customizePrefixCls && treeSelectPrefixCls,
|
||||
{
|
||||
[`${prefixCls}-lg`]: mergedSize === 'large',
|
||||
[`${prefixCls}-sm`]: mergedSize === 'small',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
return (
|
||||
<RcTreeSelect
|
||||
virtual={virtual}
|
||||
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
|
||||
{...selectProps}
|
||||
ref={ref}
|
||||
prefixCls={prefixCls}
|
||||
className={mergedClassName}
|
||||
listHeight={listHeight}
|
||||
listItemHeight={listItemHeight}
|
||||
treeCheckable={
|
||||
treeCheckable ? <span className={`${prefixCls}-tree-checkbox-inner`} /> : treeCheckable
|
||||
}
|
||||
inputIcon={suffixIcon}
|
||||
menuItemSelectedIcon={itemIcon}
|
||||
multiple={multiple}
|
||||
removeIcon={removeIcon}
|
||||
clearIcon={clearIcon}
|
||||
switcherIcon={(nodeProps: AntTreeNodeProps) =>
|
||||
renderSwitcherIcon(treePrefixCls, switcherIcon, treeLine, nodeProps)
|
||||
}
|
||||
showTreeIcon={treeIcon}
|
||||
notFoundContent={mergedNotFound}
|
||||
getPopupContainer={getPopupContainer || getContextPopupContainer}
|
||||
treeMotion={null}
|
||||
dropdownClassName={mergedDropdownClassName}
|
||||
choiceTransitionName={choiceTransitionName}
|
||||
transitionName={transitionName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const TreeSelectRef = React.forwardRef(InternalTreeSelect) as <T extends DefaultValueType>(
|
||||
props: TreeSelectProps<T> & { ref?: React.Ref<RefTreeSelectProps> },
|
||||
) => React.ReactElement;
|
||||
|
||||
type InternalTreeSelectType = typeof TreeSelectRef;
|
||||
|
||||
interface TreeSelectInterface extends InternalTreeSelectType {
|
||||
TreeNode: typeof TreeNode;
|
||||
SHOW_ALL: typeof SHOW_ALL;
|
||||
SHOW_PARENT: typeof SHOW_PARENT;
|
||||
SHOW_CHILD: typeof SHOW_CHILD;
|
||||
}
|
||||
|
||||
const TreeSelect = TreeSelectRef as TreeSelectInterface;
|
||||
|
||||
TreeSelect.TreeNode = TreeNode;
|
||||
TreeSelect.SHOW_ALL = SHOW_ALL;
|
||||
TreeSelect.SHOW_PARENT = SHOW_PARENT;
|
||||
TreeSelect.SHOW_CHILD = SHOW_CHILD;
|
||||
|
||||
export { TreeNode };
|
||||
|
||||
export default TreeSelect;
|
||||
|
@ -112,3 +112,9 @@ File icon realize by using switcherIcon. You can overwrite the style to hide it:
|
||||
### Virtual scroll limitation
|
||||
|
||||
Virtual scroll only render items in visible region. Thus not support auto width (like long `title` with horizontal scroll).
|
||||
|
||||
### What does `disabled` node work logic in the tree?
|
||||
|
||||
Tree change its data by conduction. Includes checked or auto expanded, it will conduction state to parent / children node until current node is `disabled`. So if a controlled node is `disabled`, it will only modify self state and not affect other nodes. For example, a parent node contains 3 child nodes and one of them is `disabled`. When check the parent node, it will only check rest 2 child nodes. As the same, when check these 2 child node, parent will be checked whatever checked state the `disabled` one is.
|
||||
|
||||
This conduction logic prevent that modify `disabled` parent checked state by check children node and user can not modify directly with click parent which makes the interactive conflict. If you want to modify this conduction logic, you can customize it with `checkStrictly` prop.
|
||||
|
@ -21,7 +21,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Xh-oWqg9k/Tree.svg
|
||||
| autoExpandParent | 是否自动展开父节点 | boolean | false | |
|
||||
| blockNode | 是否节点占据一行 | boolean | false | |
|
||||
| checkable | 节点前添加 Checkbox 复选框 | boolean | false | |
|
||||
| checkedKeys | (受控)选中复选框的树节点(注意:父子节点有关联,如果传入父节点 key,则子节点自动选中;相应当子节点 key 都传入,父节点也自动选中。当设置`checkable`和`checkStrictly`,它是一个有`checked`和`halfChecked`属性的对象,并且父子节点的选中与否不再关联 | string\[] \| {checked: string\[], halfChecked: string\[]} | \[] | |
|
||||
| checkedKeys | (受控)选中复选框的树节点(注意:父子节点有关联,如果传入父节点 key,则子节点自动选中;相应当子节点 key 都传入,父节点也自动选中。当设置 `checkable` 和 `checkStrictly`,它是一个有`checked`和`halfChecked`属性的对象,并且父子节点的选中与否不再关联 | string\[] \| {checked: string\[], halfChecked: string\[]} | \[] | |
|
||||
| checkStrictly | checkable 状态下节点选择完全受控(父子节点选中状态不再关联) | boolean | false | |
|
||||
| defaultCheckedKeys | 默认选中复选框的树节点 | string\[] | \[] | |
|
||||
| defaultExpandAll | 默认展开所有树节点 | boolean | false | |
|
||||
@ -78,7 +78,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Xh-oWqg9k/Tree.svg
|
||||
|
||||
## 注意
|
||||
|
||||
在 `3.4.0` 之前:树节点可以有很多,但在设置`checkable`时,将会花费更多的计算时间,因此我们缓存了一些计算结果(`this.treeNodesStates`)来复用,避免多次重复计算,以此提高性能。但这也带来了一些限制,当你异步加载树节点时,你需要这样渲染树:
|
||||
在 `3.4.0` 之前:树节点可以有很多,但在设置 `checkable` 时,将会花费更多的计算时间,因此我们缓存了一些计算结果(`this.treeNodesStates`)来复用,避免多次重复计算,以此提高性能。但这也带来了一些限制,当你异步加载树节点时,你需要这样渲染树:
|
||||
|
||||
```jsx
|
||||
{
|
||||
@ -113,3 +113,9 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Xh-oWqg9k/Tree.svg
|
||||
### 虚拟滚动的限制
|
||||
|
||||
虚拟滚动通过在仅渲染可视区域的元素来提升渲染性能。但是同时由于不会渲染所有节点,所以无法自动拓转横向宽度(比如超长 `title` 的横向滚动条)。
|
||||
|
||||
### `disabled` 节点在树中的关系是什么?
|
||||
|
||||
Tree 通过传导方式进行数据变更。无论是展开还是勾选,它都会从变更的节点开始向上、向下传导变化,直到遍历的当前节点是 `disabled` 时停止。因而如果控制的节点本身为 `disabled` 时,那么它只会修改本身而不会影响其他节点。举例来说,一个父节点包含 3 个子节点,其中一个为 `disabled` 状态。那么勾选父节点,只会影响其余两个子节点变成勾选状态。勾选两个子节点后,无论 `disabled` 节点什么状态,父节点都会变成勾选状态。
|
||||
|
||||
这种传导终止的方式是为了防止通过勾选子节点使得 `disabled` 父节点变成勾选状态,而用户无法直接勾选 `disabled` 父节点更改其状态导致的交互矛盾。如果你有着自己的传导需求,可以通过 `checkStrictly` 自定义勾选逻辑。
|
||||
|
@ -17,7 +17,7 @@ title: 从 v3 到 v4
|
||||
### 设计规范调整
|
||||
|
||||
- 行高从 `1.5`(`21px`) 调整为 `1.5715`(`22px`)。
|
||||
- 基础圆角调整,由`4px` 改为 `2px`。
|
||||
- 基础圆角调整,由 `4px` 改为 `2px`。
|
||||
- Selected 颜色和 Hovered 颜色进行了交换。
|
||||
- 全局阴影优化,调整为三层阴影区分控件层次关系。
|
||||
- 气泡确认框中图标的使用改变,由问号改为感叹号。
|
||||
|
@ -102,7 +102,7 @@ export default ProductList;
|
||||
|
||||
## 简单数据流方案
|
||||
|
||||
`@umijs/plugin-model` 是一种基于 hooks 范式的简单数据流方案,可以在一定情况下替代 dva 来进行中台的全局数据流。我们约定在 `src/models`目录下的文件为项目定义的 model 文件。每个文件需要默认导出一个 function,该 function 定义了一个 Hook,不符合规范的文件我们会过滤掉。
|
||||
`@umijs/plugin-model` 是一种基于 hooks 范式的简单数据流方案,可以在一定情况下替代 dva 来进行中台的全局数据流。我们约定在 `src/models` 目录下的文件为项目定义的 model 文件。每个文件需要默认导出一个 function,该 function 定义了一个 Hook,不符合规范的文件我们会过滤掉。
|
||||
|
||||
文件名则对应最终 model 的 name,你可以通过插件提供的 API 来消费 model 中的数据。
|
||||
|
||||
|
@ -5,7 +5,7 @@ order: 10
|
||||
title: 即时反应
|
||||
---
|
||||
|
||||
「提供邀请」的强大体现在`交互之前`给出反馈,解决易发现性问题;「巧用过渡」的有用体现在它能够在`交互期间`为用户提供视觉反馈;「即时反应」的重要性体现在`交互之后`立即给出反馈。
|
||||
「提供邀请」的强大体现在 `交互之前` 给出反馈,解决易发现性问题;「巧用过渡」的有用体现在它能够在 `交互期间` 为用户提供视觉反馈;「即时反应」的重要性体现在 `交互之后` 立即给出反馈。
|
||||
|
||||
就像「牛顿第三定律」所描述作用力和反作用一样,用户进行了操作或者内部数据发生了变化,系统就应该立即有一个对应的反馈,同时输入量级越大、重要性越高,那么反馈量级越大、重要性越高。
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "antd",
|
||||
"version": "4.8.0",
|
||||
"version": "4.8.1",
|
||||
"description": "An enterprise-class UI design language and React components implementation",
|
||||
"title": "Ant Design",
|
||||
"keywords": [
|
||||
@ -59,7 +59,7 @@
|
||||
"color-less": "node ./scripts/generate-color-less",
|
||||
"compile": "antd-tools run compile",
|
||||
"compile:less": "antd-tools run compile:less",
|
||||
"changelog": "node ./scripts/print-changelog",
|
||||
"changelog": "git fetch origin && node ./scripts/print-changelog",
|
||||
"predeploy": "antd-tools run clean && npm run site && cp CNAME _site && cp -r .circleci _site && npm run site:test",
|
||||
"deploy": "bisheng gh-pages --push-only --dotfiles",
|
||||
"deploy:china-mirror": "git checkout gh-pages && git pull origin gh-pages && git push git@gitee.com:ant-design/ant-design.git gh-pages",
|
||||
|
@ -57,9 +57,10 @@ export default class ColorPicker extends Component<ColorPickerProps> {
|
||||
render() {
|
||||
const { small, position, presetColors } = this.props;
|
||||
const { color, displayColorPicker } = this.state;
|
||||
const width = small ? 80 : 120;
|
||||
const styles = {
|
||||
color: {
|
||||
width: small ? '80px' : '120px',
|
||||
width: `${width}px`,
|
||||
height: small ? '16px' : '24px',
|
||||
borderRadius: '2px',
|
||||
background: color,
|
||||
@ -90,7 +91,7 @@ export default class ColorPicker extends Component<ColorPickerProps> {
|
||||
};
|
||||
|
||||
if (position === 'top') {
|
||||
styles.wrapper.transform = 'translateY(-100%)';
|
||||
styles.wrapper.transform = `translate(calc(-100% + ${width + 8}px), -100%)`;
|
||||
styles.wrapper.paddingBottom = 8;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user