diff --git a/components/form/Form.tsx b/components/form/Form.tsx index f963f32134..1c21967936 100755 --- a/components/form/Form.tsx +++ b/components/form/Form.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { PropTypes } from 'react'; import classNames from 'classnames'; +import createDOMForm from 'rc-form/lib/createDOMForm'; import PureRenderMixin from 'rc-util/lib/PureRenderMixin'; import omit from 'omit.js'; -import warning from 'warning'; import assign from 'object-assign'; +import warning from '../_util/warning'; import FormItem from './FormItem'; -import createDOMForm from 'rc-form/lib/createDOMForm'; import { FIELD_META_PROP } from './constants'; export interface FormCreateOption { @@ -121,14 +121,14 @@ export default class Form extends React.Component { }; }, componentWillMount() { - this.getFieldProps = this.deprecatedGetFieldProps; + this.__getFieldProps = this.props.form.getFieldProps; }, deprecatedGetFieldProps(name, option) { warning( false, '`getFieldProps` is not recommended, please use `getFieldDecorator` instead' ); - return this.getFieldProps(name, option); + return this.__getFieldProps(name, option); }, render() { this.props.form.getFieldProps = this.deprecatedGetFieldProps; diff --git a/components/transfer/demo/basic.md b/components/transfer/demo/basic.md index 2a821a990d..67f496d662 100644 --- a/components/transfer/demo/basic.md +++ b/components/transfer/demo/basic.md @@ -16,47 +16,47 @@ The most basic usage of `Transfer` involves providing the source data and target ````jsx import { Transfer } from 'antd'; +const mockData = []; +for (let i = 0; i < 20; i++) { + mockData.push({ + key: i.toString(), + title: `content${i + 1}`, + description: `description of content${i + 1}`, + disabled: Math.random() * 3 < 1, + }); +} + +const targetKeys = mockData + .filter(() => Math.random() * 2 > 1) + .map(item => item.key); + const App = React.createClass({ getInitialState() { return { - mockData: [], - targetKeys: [], + targetKeys, + selectedKeys: [], }; }, - componentDidMount() { - this.getMock(); - }, - getMock() { - const targetKeys = []; - const mockData = []; - for (let i = 0; i < 20; i++) { - const data = { - key: i.toString(), - title: `content${i + 1}`, - description: `description of content${i + 1}`, - chosen: Math.random() * 2 > 1, - disabled: Math.random() * 3 < 1, - }; - if (data.chosen) { - targetKeys.push(data.key); - } - mockData.push(data); - } - this.setState({ mockData, targetKeys }); - }, - handleChange(targetKeys, direction, moveKeys) { - console.log(targetKeys, direction, moveKeys); - this.setState({ targetKeys }); + handleChange(nextTargetKeys, direction, moveKeys) { + this.setState({ targetKeys: nextTargetKeys }); + + console.log('targetKeys: ', targetKeys); + console.log('direction: ', direction); + console.log('moveKeys: ', moveKeys); }, handleSelectChange(sourceSelectedKeys, targetSelectedKeys) { + this.setState({ selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys] }); + console.log('sourceSelectedKeys: ', sourceSelectedKeys); console.log('targetSelectedKeys: ', targetSelectedKeys); }, render() { + const state = this.state; return ( item.title} diff --git a/components/transfer/index.en-US.md b/components/transfer/index.en-US.md index f42d6f9b87..24a29fabfd 100644 --- a/components/transfer/index.en-US.md +++ b/components/transfer/index.en-US.md @@ -19,17 +19,19 @@ To transfer the elements between two columns in an intuitive and efficient way. |-----------|------------------------------------------|------------|--------| | 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. | Array | [] | | render | The function to generate the item shown on a column. Based on an record (element of the dataSource array), this function should return a React element which is generated from that record. | Function(record) | | -| targetKeys | A set of keys of elements that are listed on the right column. | Array | [] | -| onChange | A callback function that is executed when the transfer between columns is complete. | Function(targetKeys, direction, moveKeys) | | -| onSelectChange | A callback function which is executed when selected items are changed. | Function(sourceSelectedKeys, targetSelectedKeys) | | +| targetKeys | A set of keys of elements that are listed on the right column. | string[] | [] | +| selectedKeys | A set of keys of selected items. | string[] | [] | +| onChange | A callback function that is executed when the transfer between columns is complete. | (targetKeys, direction, moveKeys): void | | +| onSelectChange | A callback function which is executed when selected items are changed. | (sourceSelectedKeys, targetSelectedKeys): void | | | listStyle | A custom CSS style used for rendering the transfer columns. | Object | | | className | A custom CSS class. | String | | | titles | A set of titles that are sorted from left to right. | Array | ['Source, 'Target] | | operations | A set of operations that are sorted from top to bottom. | Array | [] | | showSearch | If included, a search box is shown on each column. | Boolean | false | +| filterOption | A function to determine whether an item should show in search result list | (inputValue, option): boolean | | | searchPlaceholder | The hint text of the search box. | String | 'Search here' | -| notFoundContent | Text to display when a column is empty. | React.node | 'The list is empty' | -| footer | A function used for rendering the footer. | Function(props) | | +| notFoundContent | Text to display when a column is empty. | React.ReactNode | 'The list is empty' | +| footer | A function used for rendering the footer. | (props): React.ReactNode | | | lazy | property of [react-lazy-load](https://github.com/loktar00/react-lazy-load) for lazy rendering items | Object | `{ height: 32, offset: 32 }` | ## Warning diff --git a/components/transfer/index.tsx b/components/transfer/index.tsx index 051f974822..447217f613 100644 --- a/components/transfer/index.tsx +++ b/components/transfer/index.tsx @@ -19,18 +19,19 @@ export interface TransferItem { export interface TransferProps { dataSource: TransferItem[]; targetKeys: string[]; - render?: (record: TransferItem) => any; - onChange?: (targetKeys: TransferItem[], direction: string, moveKeys: any) => void; + selectedKeys?: string[]; + render?: (record: TransferItem) => React.ReactNode; + onChange?: (targetKeys: string[], direction: string, moveKeys: any) => void; onSelectChange?: (sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void; listStyle?: React.CSSProperties; - className?: string; prefixCls?: string; + className?: string; titles?: string[]; operations?: string[]; showSearch?: boolean; searchPlaceholder?: string; notFoundContent?: React.ReactNode; - footer?: (props: any) => any; + footer?: (props: any) => React.ReactNode; style?: React.CSSProperties; filterOption: (inputValue: any, item: any) => boolean; body?: (props: any) => any; @@ -46,6 +47,7 @@ export interface TransferContext { const defaultTitles = ['源列表', '目的列表']; export default class Transfer extends React.Component { + // For high-level customized Transfer @dqaria static List = List; static Operation = Operation; static Search = Search; @@ -84,18 +86,20 @@ export default class Transfer extends React.Component { context: TransferContext; splitedDataSource: any; - constructor(props) { + constructor(props: TransferProps) { super(props); + + const { selectedKeys = [], targetKeys = [] } = props; this.state = { leftFilter: '', rightFilter: '', - leftCheckedKeys: [], - rightCheckedKeys: [], + sourceSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) === -1), + targetSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) > -1), }; } - componentWillReceiveProps(nextProps) { - const { leftCheckedKeys, rightCheckedKeys } = this.state; + componentWillReceiveProps(nextProps: TransferProps) { + const { sourceSelectedKeys, targetSelectedKeys } = this.state; if (nextProps.targetKeys !== this.props.targetKeys || nextProps.dataSource !== this.props.dataSource) { // clear cached splited dataSource @@ -108,12 +112,19 @@ export default class Transfer extends React.Component { // clear key nolonger existed // clear checkedKeys according to targetKeys this.setState({ - leftCheckedKeys: leftCheckedKeys.filter(existInDateSourcekey) + sourceSelectedKeys: sourceSelectedKeys.filter(existInDateSourcekey) .filter(data => targetKeys.filter(key => key === data).length === 0), - rightCheckedKeys: rightCheckedKeys.filter(existInDateSourcekey) + targetSelectedKeys: targetSelectedKeys.filter(existInDateSourcekey) .filter(data => targetKeys.filter(key => key === data).length > 0), }); } + if (nextProps.selectedKeys) { + const targetKeys = nextProps.targetKeys; + this.setState({ + sourceSelectedKeys: nextProps.selectedKeys.filter(key => targetKeys.indexOf(key) === -1), + targetSelectedKeys: nextProps.selectedKeys.filter(key => targetKeys.indexOf(key) > -1), + }); + } } splitDataSource(props: TransferProps) { if (this.splitedDataSource) { @@ -146,8 +157,8 @@ export default class Transfer extends React.Component { moveTo = (direction) => { const { targetKeys = [], onChange } = this.props; - const { leftCheckedKeys, rightCheckedKeys } = this.state; - const moveKeys = direction === 'right' ? leftCheckedKeys : rightCheckedKeys; + const { sourceSelectedKeys, targetSelectedKeys } = this.state; + const moveKeys = direction === 'right' ? sourceSelectedKeys : targetSelectedKeys; // move items to target box const newTargetKeys = direction === 'right' ? moveKeys.concat(targetKeys) @@ -156,7 +167,7 @@ export default class Transfer extends React.Component { // empty checked keys const oppositeDirection = direction === 'right' ? 'left' : 'right'; this.setState({ - [`${oppositeDirection}CheckedKeys`]: [], + [this.getSelectedKeysName(oppositeDirection)]: [], }); this.handleSelectChange(oppositeDirection, []); @@ -169,25 +180,28 @@ export default class Transfer extends React.Component { moveToRight = () => this.moveTo('right') handleSelectChange(direction: string, holder: string[]) { - const { leftCheckedKeys, rightCheckedKeys } = this.state; + const { sourceSelectedKeys, targetSelectedKeys } = this.state; const onSelectChange = this.props.onSelectChange; if (!onSelectChange) { return; } if (direction === 'left') { - onSelectChange(holder, rightCheckedKeys); + onSelectChange(holder, targetSelectedKeys); } else { - onSelectChange(leftCheckedKeys, holder); + onSelectChange(sourceSelectedKeys, holder); } } handleSelectAll = (direction, filteredDataSource, checkAll) => { const holder = checkAll ? [] : filteredDataSource.map(item => item.key); - this.setState({ - [`${direction}CheckedKeys`]: holder, - }); this.handleSelectChange(direction, holder); + + if (!this.props.selectedKeys) { + this.setState({ + [this.getSelectedKeysName(direction)]: holder, + }); + } } handleLeftSelectAll = (filteredDataSource, checkAll) => ( @@ -217,8 +231,8 @@ export default class Transfer extends React.Component { handleRightClear = () => this.handleClear('right') handleSelect = (direction, selectedItem, checked) => { - const { leftCheckedKeys, rightCheckedKeys } = this.state; - const holder = direction === 'left' ? [...leftCheckedKeys] : [...rightCheckedKeys]; + const { sourceSelectedKeys, targetSelectedKeys } = this.state; + const holder = direction === 'left' ? [...sourceSelectedKeys] : [...targetSelectedKeys]; let index; holder.forEach((key, i) => { if (key === selectedItem.key) { @@ -231,10 +245,13 @@ export default class Transfer extends React.Component { if (checked) { holder.push(selectedItem.key); } - this.setState({ - [`${direction}CheckedKeys`]: holder, - }); this.handleSelectChange(direction, holder); + + if (!this.props.selectedKeys) { + this.setState({ + [this.getSelectedKeysName(direction)]: holder, + }); + } } handleLeftSelect = (selectedItem, checked) => this.handleSelect('left', selectedItem, checked); @@ -253,17 +270,21 @@ export default class Transfer extends React.Component { return defaultTitles; } + getSelectedKeysName(direction) { + return direction === 'left' ? 'sourceSelectedKeys' : 'targetSelectedKeys'; + } + render() { const { prefixCls = 'ant-transfer', operations = [], showSearch, notFoundContent, searchPlaceholder, body, footer, listStyle, className = '', filterOption, render, lazy, } = this.props; - const { leftFilter, rightFilter, leftCheckedKeys, rightCheckedKeys } = this.state; + const { leftFilter, rightFilter, sourceSelectedKeys, targetSelectedKeys } = this.state; const { leftDataSource, rightDataSource } = this.splitDataSource(this.props); - const leftActive = rightCheckedKeys.length > 0; - const rightActive = leftCheckedKeys.length > 0; + const leftActive = targetSelectedKeys.length > 0; + const rightActive = sourceSelectedKeys.length > 0; const cls = classNames({ [className]: !!className, @@ -279,7 +300,7 @@ export default class Transfer extends React.Component { filter={leftFilter} filterOption={filterOption} style={listStyle} - checkedKeys={leftCheckedKeys} + checkedKeys={sourceSelectedKeys} handleFilter={this.handleLeftFilter} handleClear={this.handleLeftClear} handleSelect={this.handleLeftSelect} @@ -308,7 +329,7 @@ export default class Transfer extends React.Component { filter={rightFilter} filterOption={filterOption} style={listStyle} - checkedKeys={rightCheckedKeys} + checkedKeys={targetSelectedKeys} handleFilter={this.handleRightFilter} handleClear={this.handleRightClear} handleSelect={this.handleRightSelect} diff --git a/components/transfer/index.zh-CN.md b/components/transfer/index.zh-CN.md index 14fae9d0c5..ad51bb1720 100644 --- a/components/transfer/index.zh-CN.md +++ b/components/transfer/index.zh-CN.md @@ -20,18 +20,19 @@ title: Transfer |-----------|------------------------------------------|------------|--------| | dataSource | 数据源,其中的数据将会被渲染到左边一栏中,`targetKeys` 中指定的除外。 | Array | [] | | render | 每行数据渲染函数,该函数的入参为 `dataSource` 中的项,返回值为 React element | Function(record) | | -| targetKeys | 显示在右侧框数据的key集合 | Array | [] | -| onChange | 选项在两栏之间转移时的回调函数 | Function(targetKeys, direction, moveKeys) | | -| onSelectChange | 选中项发生改变时的回调函数 | Function(sourceSelectedKeys, targetSelectedKeys) | | +| targetKeys | 显示在右侧框数据的key集合 | string[] | [] | +| selectedKeys | 设置哪些项应该被选中 | string[] | [] | +| onChange | 选项在两栏之间转移时的回调函数 | (targetKeys, direction, moveKeys): void | | +| onSelectChange | 选中项发生改变时的回调函数 | (sourceSelectedKeys, targetSelectedKeys): void | | | listStyle | 两个穿梭框的自定义样式 | Object | | | className | 自定义类 | String | | | titles | 标题集合,顺序从左至右 | Array | ['源列表', '目的列表'] | | operations | 操作文案集合,顺序从上至下 | Array | [] | | showSearch | 是否显示搜索框 | Boolean | false | -| filterOption | 接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。| Function(inputValue, option) | | -| searchPlaceholder | 搜索框的默认值 | String | '请输入搜索内容' | -| notFoundContent | 当列表为空时显示的内容 | React.node | '列表为空' | -| footer | 底部渲染函数 | Function(props) | | +| filterOption | 接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。| (inputValue, option): boolean | | +| searchPlaceholder | 搜索框的默认值 | string | '请输入搜索内容' | +| notFoundContent | 当列表为空时显示的内容 | React.ReactNode | '列表为空' | +| footer | 底部渲染函数 | (props): React.ReactNode | | | lazy | Transfer 使用了 [react-lazy-load](https://github.com/loktar00/react-lazy-load) 优化性能,这里可以设置相关参数 | Object | `{ height: 32, offset: 32 }` | ## 注意