Browse Source

chore: sync master to feature

pull/15283/head
愚道 6 years ago
parent
commit
a4f73ff64e
  1. 64
      components/badge/ScrollNumber.tsx
  2. 25
      components/calendar/index.tsx
  3. 11
      components/form/Form.tsx
  4. 16
      components/form/FormItem.tsx
  5. 2
      components/form/context.ts
  6. 1
      components/form/index.en-US.md
  7. 1
      components/form/index.zh-CN.md
  8. 4
      components/form/style/index.less
  9. 99
      components/list/Item.tsx
  10. 1087
      components/list/__tests__/__snapshots__/demo.test.js.snap
  11. 16
      components/list/__tests__/__snapshots__/pagination.test.js.snap
  12. 2
      components/list/__tests__/pagination.test.js
  13. 4
      components/list/demo/simple.md
  14. 42
      components/list/style/index.less
  15. 8
      components/list/style/responsive.less
  16. 34
      components/menu/index.tsx
  17. 120
      components/skeleton/__tests__/__snapshots__/demo.test.js.snap
  18. 1
      components/style/themes/default.less
  19. 43
      components/transfer/index.tsx

64
components/badge/ScrollNumber.tsx

@ -3,6 +3,7 @@ import { createElement, Component } from 'react';
import omit from 'omit.js';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import { polyfill } from 'react-lifecycles-compat';
function getNumberArray(num: string | number | undefined | null) {
return num
@ -30,13 +31,24 @@ export interface ScrollNumberState {
count?: string | number | null;
}
export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
let lastCount: any;
class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
static defaultProps = {
count: null,
onAnimated() {},
};
lastCount: any;
static getDerivedStateFromProps(nextProps: ScrollNumberProps, nextState: ScrollNumberState) {
if ('count' in nextProps) {
if (nextState.count === nextProps.count) {
return null;
}
lastCount = nextState.count;
return { animateStarted: true };
}
return null;
}
constructor(props: ScrollNumberProps) {
super(props);
@ -51,9 +63,9 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
return 10 + num;
}
const currentDigit = getNumberArray(this.state.count)[i];
const lastDigit = getNumberArray(this.lastCount)[i];
const lastDigit = getNumberArray(lastCount)[i];
// 同方向则在同一侧切换数字
if (this.state.count! > this.lastCount) {
if (this.state.count! > lastCount) {
if (currentDigit >= lastDigit) {
return 10 + num;
}
@ -65,36 +77,14 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
return num;
}
componentWillReceiveProps(nextProps: ScrollNumberProps) {
if ('count' in nextProps) {
if (this.state.count === nextProps.count) {
return;
}
this.lastCount = this.state.count;
// 复原数字初始位置
this.setState(
{
animateStarted: true,
},
() => {
// 等待数字位置复原完毕
// 开始设置完整的数字
setTimeout(() => {
this.setState(
{
animateStarted: false,
count: nextProps.count,
},
() => {
const onAnimated = this.props.onAnimated;
if (onAnimated) {
onAnimated();
}
},
);
}, 5);
},
);
componentDidUpdate() {
if (this.state.animateStarted) {
this.setState({ animateStarted: false, count: this.props.count }, () => {
const onAnimated = this.props.onAnimated;
if (onAnimated) {
onAnimated();
}
});
}
}
@ -114,7 +104,7 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
renderCurrentNumber(prefixCls: string, num: number, i: number) {
const position = this.getPositionByNum(num, i);
const removeTransition =
this.state.animateStarted || getNumberArray(this.lastCount)[i] === undefined;
this.state.animateStarted || getNumberArray(lastCount)[i] === undefined;
return createElement(
'span',
{
@ -189,3 +179,7 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
return <ConfigConsumer>{this.renderScrollNumber}</ConfigConsumer>;
}
}
polyfill(ScrollNumber);
export default ScrollNumber;

25
components/calendar/index.tsx

@ -7,6 +7,7 @@ import enUS from './locale/en_US';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import interopDefault from '../_util/interopDefault';
import { polyfill } from 'react-lifecycles-compat';
export { HeaderProps } from './Header';
@ -48,7 +49,7 @@ export interface CalendarState {
mode?: CalendarMode;
}
export default class Calendar extends React.Component<CalendarProps, CalendarState> {
class Calendar extends React.Component<CalendarProps, CalendarState> {
static defaultProps = {
locale: {},
fullscreen: true,
@ -74,6 +75,13 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
onChange: PropTypes.func,
};
static getDerivedStateFromProps(nextProps: CalendarProps) {
if ('value' in nextProps) {
return { value: nextProps.value! };
}
return null;
}
prefixCls?: string;
constructor(props: CalendarProps) {
@ -92,15 +100,10 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
};
}
componentWillReceiveProps(nextProps: CalendarProps) {
if ('value' in nextProps) {
componentDidUpdate(prevProps: CalendarProps) {
if ('mode' in this.props && this.props.mode !== prevProps.mode) {
this.setState({
value: nextProps.value!,
});
}
if ('mode' in nextProps && nextProps.mode !== this.props.mode) {
this.setState({
mode: nextProps.mode!,
mode: this.props.mode!,
});
}
}
@ -283,3 +286,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
);
}
}
polyfill(Calendar);
export default Calendar;

11
components/form/Form.tsx

@ -38,18 +38,16 @@ export interface FormProps extends React.FormHTMLAttributes<HTMLFormElement> {
className?: string;
prefixCls?: string;
hideRequiredMark?: boolean;
/**
* @since 3.14.0
*/
labelCol?: ColProps;
/**
* @since 3.14.0
*/
wrapperCol?: ColProps;
labelCol?: ColProps;
/**
* @since 3.15.0
*/
colon?: boolean;
labelAlign?: 'left' | 'right';
}
export type ValidationRule = {
@ -262,6 +260,7 @@ export default class Form extends React.Component<FormProps, any> {
'form',
'hideRequiredMark',
'wrapperCol',
'labelAlign',
'labelCol',
'colon',
]);
@ -270,10 +269,10 @@ export default class Form extends React.Component<FormProps, any> {
};
render() {
const { wrapperCol, labelCol, layout, colon } = this.props;
const { wrapperCol, labelAlign, labelCol, layout, colon } = this.props;
return (
<FormContext.Provider
value={{ wrapperCol, labelCol, vertical: layout === 'vertical', colon }}
value={{ wrapperCol, labelAlign, labelCol, vertical: layout === 'vertical', colon }}
>
<ConfigConsumer>{this.renderForm}</ConfigConsumer>
</FormContext.Provider>

16
components/form/FormItem.tsx

@ -19,6 +19,7 @@ export interface FormItemProps {
className?: string;
id?: string;
label?: React.ReactNode;
labelAlign?: 'left' | 'right';
labelCol?: ColProps;
wrapperCol?: ColProps;
help?: React.ReactNode;
@ -42,6 +43,7 @@ export default class FormItem extends React.Component<FormItemProps, any> {
static propTypes = {
prefixCls: PropTypes.string,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
labelAlign: PropTypes.string,
labelCol: PropTypes.object,
help: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),
validateStatus: PropTypes.oneOf(ValidateStatuses),
@ -328,14 +330,24 @@ export default class FormItem extends React.Component<FormItemProps, any> {
renderLabel(prefixCls: string) {
return (
<FormContext.Consumer key="label">
{({ vertical, labelCol: contextLabelCol, colon: contextColon }: FormContextProps) => {
{({
vertical,
labelAlign,
labelCol: contextLabelCol,
colon: contextColon,
}: FormContextProps) => {
const { label, labelCol, colon, id } = this.props;
const required = this.isRequired();
const mergedLabelCol: ColProps =
('labelCol' in this.props ? labelCol : contextLabelCol) || {};
const labelColClassName = classNames(`${prefixCls}-item-label`, mergedLabelCol.className);
const labelClsBasic = `${prefixCls}-item-label`;
const labelColClassName = classNames(
labelClsBasic,
labelAlign === 'left' && `${labelClsBasic}-left`,
mergedLabelCol.className,
);
const labelClassName = classNames({
[`${prefixCls}-item-required`]: required,
});

2
components/form/context.ts

@ -4,10 +4,12 @@ import { ColProps } from '../grid/col';
export interface FormContextProps {
vertical: boolean;
colon?: boolean;
labelAlign?: string;
labelCol?: ColProps;
wrapperCol?: ColProps;
}
export const FormContext: Context<FormContextProps> = createReactContext({
labelAlign: 'right',
vertical: false,
});

1
components/form/index.en-US.md

@ -36,6 +36,7 @@ A form field is defined using `<Form.Item />`.
| -------- | ----------- | ---- | ------------- |
| form | Decorated by `Form.create()` will be automatically set `this.props.form` property | object | n/a |
| hideRequiredMark | Hide required mark of all form items | Boolean | false |
| labelAlign | Label text align | 'left' \| 'right' | 'right' |
| labelCol | (Added in 3.14.0. Previous version can only set on FormItem.) 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>` | [object](https://ant.design/components/grid/#Col) | |
| layout | Define form layout | 'horizontal'\|'vertical'\|'inline' | 'horizontal' |
| onSubmit | Defines a function will be called if form data validation is successful. | Function(e:Event) | |

1
components/form/index.zh-CN.md

@ -38,6 +38,7 @@ title: Form
| --- | --- | --- | --- |
| form | 经 `Form.create()` 包装过的组件会自带 `this.props.form` 属性 | object | - |
| hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false |
| labelAlign | label 标签的文本对齐方式 | 'left' \| 'right' | 'right' |
| labelCol | (3.14.0 新增,之前的版本只能设置到 FormItem 上。)label 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}``sm: {span: 3, offset: 12}` | [object](https://ant.design/components/grid/#Col) | |
| layout | 表单布局 | 'horizontal'\|'vertical'\|'inline' | 'horizontal' |
| onSubmit | 数据验证成功后回调事件 | Function(e:Event) | |

4
components/form/style/index.less

@ -101,6 +101,10 @@ input[type='checkbox'] {
text-align: right;
vertical-align: middle;
&-left {
text-align: left;
}
label {
color: @label-color;

99
components/list/Item.tsx

@ -70,6 +70,17 @@ export default class Item extends React.Component<ListItemProps, any> {
context: any;
isItemContainsTextNode() {
const { children } = this.props;
let result;
React.Children.forEach(children, (element: React.ReactElement<any>) => {
if (typeof element === 'string') {
result = true;
}
});
return result;
}
renderItem = ({ getPrefixCls }: ConfigConsumerProps) => {
const { grid } = this.context;
const {
@ -81,52 +92,38 @@ export default class Item extends React.Component<ListItemProps, any> {
...others
} = this.props;
const prefixCls = getPrefixCls('list', customizePrefixCls);
const classString = classNames(`${prefixCls}-item`, className);
const metaContent: React.ReactElement<any>[] = [];
const otherContent: React.ReactElement<any>[] = [];
React.Children.forEach(children, (element: React.ReactElement<any>) => {
if (element && element.type && element.type === Meta) {
metaContent.push(element);
} else {
otherContent.push(element);
}
});
const contentClassString = classNames(`${prefixCls}-item-content`, {
[`${prefixCls}-item-content-single`]: metaContent.length < 1,
});
const content =
otherContent.length > 0 ? <div className={contentClassString}>{otherContent}</div> : null;
let actionsContent;
if (actions && actions.length > 0) {
const actionsContentItem = (action: React.ReactNode, i: number) => (
<li key={`${prefixCls}-item-action-${i}`}>
{action}
{i !== actions.length - 1 && <em className={`${prefixCls}-item-action-split`} />}
</li>
);
actionsContent = (
<ul className={`${prefixCls}-item-action`}>
{actions.map((action, i) => actionsContentItem(action, i))}
</ul>
);
}
const extraContent = (
<div className={`${prefixCls}-item-extra-wrap`}>
<div className={`${prefixCls}-item-main`}>
{metaContent}
{content}
{actionsContent}
</div>
<div className={`${prefixCls}-item-extra`}>{extra}</div>
const actionsContent = actions && actions.length > 0 && (
<ul className={`${prefixCls}-item-action`} key="actions">
{actions.map((action: React.ReactNode, i: number) => (
<li key={`${prefixCls}-item-action-${i}`}>
{action}
{i !== actions.length - 1 && <em className={`${prefixCls}-item-action-split`} />}
</li>
))}
</ul>
);
const itemChildren = (
<div
{...others}
className={classNames(`${prefixCls}-item`, className, {
[`${prefixCls}-item-no-flex`]: !extra && this.isItemContainsTextNode(),
})}
>
{extra
? [
<div className={`${prefixCls}-item-main`} key="content">
{children}
{actionsContent}
</div>,
<div className={`${prefixCls}-item-extra`} key="extra">
{extra}
</div>,
]
: [children, actionsContent]}
</div>
);
const mainContent = grid ? (
return grid ? (
<Col
span={getGrid(grid, 'column')}
xs={getGrid(grid, 'xs')}
@ -136,23 +133,11 @@ export default class Item extends React.Component<ListItemProps, any> {
xl={getGrid(grid, 'xl')}
xxl={getGrid(grid, 'xxl')}
>
<div {...others} className={classString}>
{extra && extraContent}
{!extra && metaContent}
{!extra && content}
{!extra && actionsContent}
</div>
{itemChildren}
</Col>
) : (
<div {...others} className={classString}>
{extra && extraContent}
{!extra && metaContent}
{!extra && content}
{!extra && actionsContent}
</div>
itemChildren
);
return mainContent;
};
render() {

1087
components/list/__tests__/__snapshots__/demo.test.js.snap

File diff suppressed because it is too large

16
components/list/__tests__/__snapshots__/pagination.test.js.snap

@ -11,22 +11,14 @@ exports[`List.pagination renders pagination correctly 1`] = `
class="ant-spin-container"
>
<div
class="ant-list-item"
class="ant-list-item ant-list-item-no-flex"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
Jack
</div>
Jack
</div>
<div
class="ant-list-item"
class="ant-list-item ant-list-item-no-flex"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
Lucy
</div>
Lucy
</div>
</div>
</div>

2
components/list/__tests__/pagination.test.js

@ -25,7 +25,7 @@ describe('List.pagination', () => {
}
function renderedNames(wrapper) {
return wrapper.find('.ant-list-item-content').map(row => row.text());
return wrapper.find('.ant-list-item').map(row => row.text());
}
it('renders pagination correctly', () => {

4
components/list/demo/simple.md

@ -22,7 +22,7 @@ If a large or small list is desired, set the size property to either large or sm
Customizing the header and footer of list by setting `header` and `footer` property.
````jsx
import { List } from 'antd';
import { List, Typography } from 'antd';
const data = [
'Racing car sprays burning fuel into crowd.',
@ -40,7 +40,7 @@ ReactDOM.render(
footer={<div>Footer</div>}
bordered
dataSource={data}
renderItem={item => (<List.Item>{item}</List.Item>)}
renderItem={item => (<List.Item><Typography.Text mark>[ITEM]</Typography.Text> {item}</List.Item>)}
/>
<h3 style={{ margin: '16px 0' }}>Small Size</h3>
<List

42
components/list/style/index.less

@ -33,8 +33,12 @@
}
&-item {
display: flex;
align-items: center;
padding: @list-item-padding;
&-no-flex {
display: block;
}
&-meta {
display: flex;
flex: 1;
@ -65,14 +69,6 @@
line-height: 22px;
}
}
&-content {
display: flex;
flex: 1;
justify-content: flex-end;
}
&-content-single {
justify-content: flex-start;
}
&-action {
flex: 0 0 auto;
margin-left: 48px;
@ -102,10 +98,6 @@
background-color: @border-color-split;
}
}
&-main {
display: flex;
flex: 1;
}
}
&-header {
@ -159,19 +151,18 @@
}
&-vertical &-item {
display: block;
&-extra-wrap {
display: flex;
}
&-main {
display: block;
flex: 1;
}
&-extra {
margin-left: 58px;
margin-left: 40px;
}
&-meta {
margin-bottom: @list-item-meta-margin-bottom;
&-title {
margin-bottom: @list-item-meta-title-margin-bottom;
color: @heading-color;
@ -179,14 +170,11 @@
line-height: 24px;
}
}
&-content {
display: block;
margin: @list-item-content-margin;
color: @text-color;
font-size: @font-size-base;
}
&-action {
margin-top: @padding-md;
margin-left: auto;
> li {
padding: 0 16px;
&:first-child {
@ -197,14 +185,12 @@
}
&-grid &-item {
display: block;
max-width: 100%;
margin-bottom: 16px;
padding-top: 0;
padding-bottom: 0;
border-bottom: none;
&-content {
display: block;
max-width: 100%;
}
}
}

8
components/list/style/responsive.less

@ -16,7 +16,7 @@
}
}
@media screen and (max-width: @screen-xs) {
@media screen and (max-width: @screen-sm) {
.@{list-prefix-cls} {
&-item {
flex-wrap: wrap;
@ -28,14 +28,12 @@
.@{list-prefix-cls}-vertical {
.@{list-prefix-cls}-item {
&-extra-wrap {
flex-wrap: wrap-reverse;
}
flex-wrap: wrap-reverse;
&-main {
min-width: 220px;
}
&-extra {
margin-left: 0;
margin: auto auto 16px;
}
}
}

34
components/menu/index.tsx

@ -7,7 +7,7 @@ import Item from './MenuItem';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import animation from '../_util/openAnimation';
import warning from '../_util/warning';
import { SiderContext } from '../layout/Sider';
import { polyfill } from 'react-lifecycles-compat';
export interface SelectParam {
key: string;
@ -60,7 +60,7 @@ export interface MenuState {
openKeys: string[];
}
export default class Menu extends React.Component<MenuProps, MenuState> {
class Menu extends React.Component<MenuProps, MenuState> {
static Divider = Divider;
static Item = Item;
static SubMenu = SubMenu;
@ -79,6 +79,13 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
collapsedWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
};
static getDerivedStateFromProps(nextProps: MenuProps) {
if ('openKeys' in nextProps) {
return { openKeys: nextProps.openKeys! };
}
return null;
}
context: any;
switchingModeFromInline: boolean;
inlineOpenKeys: string[] = [];
@ -118,27 +125,16 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
};
}
componentWillReceiveProps(nextProps: MenuProps, nextContext: SiderContext) {
if (this.props.mode === 'inline' && nextProps.mode !== 'inline') {
componentDidUpdate(prevProps: MenuProps) {
if (prevProps.mode === 'inline' && this.props.mode !== 'inline') {
this.switchingModeFromInline = true;
}
if ('openKeys' in nextProps) {
this.setState({ openKeys: nextProps.openKeys! });
return;
}
if (
(nextProps.inlineCollapsed && !this.props.inlineCollapsed) ||
(nextContext.siderCollapsed && !this.context.siderCollapsed)
) {
if (this.props.inlineCollapsed && !prevProps.inlineCollapsed) {
this.switchingModeFromInline = true;
this.inlineOpenKeys = this.state.openKeys;
this.setState({ openKeys: [] });
}
if (
(!nextProps.inlineCollapsed && this.props.inlineCollapsed) ||
(!nextContext.siderCollapsed && this.context.siderCollapsed)
) {
if (!this.props.inlineCollapsed && prevProps.inlineCollapsed) {
this.setState({ openKeys: this.inlineOpenKeys });
this.inlineOpenKeys = [];
}
@ -284,3 +280,7 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
return <ConfigConsumer>{this.renderMenu}</ConfigConsumer>;
}
}
polyfill(Menu);
export default Menu;

120
components/skeleton/__tests__/__snapshots__/demo.test.js.snap

@ -126,32 +126,28 @@ exports[`renders ./components/skeleton/demo/list.md correctly 1`] = `
class="ant-list-item"
>
<div
class="ant-list-item-content ant-list-item-content-single"
class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active"
>
<div
class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active"
class="ant-skeleton-header"
>
<div
class="ant-skeleton-header"
>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width:50%"
/>
<ul
class="ant-skeleton-paragraph"
>
<h3
class="ant-skeleton-title"
style="width:50%"
/>
<ul
class="ant-skeleton-paragraph"
>
<li />
<li />
</ul>
</div>
<li />
<li />
</ul>
</div>
</div>
</div>
@ -159,32 +155,28 @@ exports[`renders ./components/skeleton/demo/list.md correctly 1`] = `
class="ant-list-item"
>
<div
class="ant-list-item-content ant-list-item-content-single"
class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active"
>
<div
class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active"
class="ant-skeleton-header"
>
<div
class="ant-skeleton-header"
>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width:50%"
/>
<ul
class="ant-skeleton-paragraph"
>
<h3
class="ant-skeleton-title"
style="width:50%"
/>
<ul
class="ant-skeleton-paragraph"
>
<li />
<li />
</ul>
</div>
<li />
<li />
</ul>
</div>
</div>
</div>
@ -192,32 +184,28 @@ exports[`renders ./components/skeleton/demo/list.md correctly 1`] = `
class="ant-list-item"
>
<div
class="ant-list-item-content ant-list-item-content-single"
class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active"
>
<div
class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active"
class="ant-skeleton-header"
>
<div
class="ant-skeleton-header"
>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width:50%"
/>
<ul
class="ant-skeleton-paragraph"
>
<h3
class="ant-skeleton-title"
style="width:50%"
/>
<ul
class="ant-skeleton-paragraph"
>
<li />
<li />
</ul>
</div>
<li />
<li />
</ul>
</div>
</div>
</div>

1
components/style/themes/default.less

@ -574,7 +574,6 @@
@list-footer-background: transparent;
@list-empty-text-padding: @padding-md;
@list-item-padding: @padding-sm 0;
@list-item-content-margin: 0 0 @padding-md 0;
@list-item-meta-margin-bottom: @padding-md;
@list-item-meta-avatar-margin-right: @padding-md;
@list-item-meta-title-margin-bottom: @padding-sm;

43
components/transfer/index.tsx

@ -8,6 +8,7 @@ import warning from '../_util/warning';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale-provider/default';
import { ConfigConsumer, ConfigConsumerProps, RenderEmptyHandler } from '../config-provider';
import { polyfill } from 'react-lifecycles-compat';
export { TransferListProps } from './list';
export { TransferOperationProps } from './operation';
@ -62,7 +63,7 @@ export interface TransferLocale {
itemsUnit: string;
}
export default class Transfer extends React.Component<TransferProps, any> {
class Transfer extends React.Component<TransferProps, any> {
// For high-level customized Transfer @dqaria
static List = List;
static Operation = Operation;
@ -100,6 +101,17 @@ export default class Transfer extends React.Component<TransferProps, any> {
lazy: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
};
static getDerivedStateFromProps(nextProps: TransferProps) {
if (nextProps.selectedKeys) {
const targetKeys = nextProps.targetKeys || [];
return {
sourceSelectedKeys: nextProps.selectedKeys.filter(key => !targetKeys.includes(key)),
targetSelectedKeys: nextProps.selectedKeys.filter(key => targetKeys.includes(key)),
};
}
return null;
}
separatedDataSource: {
leftDataSource: TransferItem[];
rightDataSource: TransferItem[];
@ -124,24 +136,23 @@ export default class Transfer extends React.Component<TransferProps, any> {
};
}
componentWillReceiveProps(nextProps: TransferProps) {
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
componentDidUpdate = (prevProps: TransferProps, prevState: any) => {
const { sourceSelectedKeys, targetSelectedKeys } = prevState;
if (
nextProps.targetKeys !== this.props.targetKeys ||
nextProps.dataSource !== this.props.dataSource
prevProps.targetKeys !== this.props.targetKeys ||
prevProps.dataSource !== this.props.dataSource
) {
// clear cached separated dataSource
this.separatedDataSource = null;
if (!nextProps.selectedKeys) {
if (!prevProps.selectedKeys) {
// clear key no longer existed
// clear checkedKeys according to targetKeys
const { dataSource, targetKeys = [] } = nextProps;
const { dataSource, targetKeys = [] } = prevProps;
const newSourceSelectedKeys: String[] = [];
const newTargetSelectedKeys: String[] = [];
dataSource.forEach(({ key }) => {
dataSource.forEach(({ key }: { key: string }) => {
if (sourceSelectedKeys.includes(key) && !targetKeys.includes(key)) {
newSourceSelectedKeys.push(key);
}
@ -155,15 +166,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
});
}
}
if (nextProps.selectedKeys) {
const targetKeys = nextProps.targetKeys || [];
this.setState({
sourceSelectedKeys: nextProps.selectedKeys.filter(key => !targetKeys.includes(key)),
targetSelectedKeys: nextProps.selectedKeys.filter(key => targetKeys.includes(key)),
});
}
}
};
separateDataSource(props: TransferProps) {
if (this.separatedDataSource) {
@ -464,3 +467,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
);
}
}
polyfill(Transfer);
export default Transfer;

Loading…
Cancel
Save