From 765ce7bb675b891e2d6bfe2e9880610bdb7e7bd4 Mon Sep 17 00:00:00 2001 From: Daqi Song Date: Mon, 21 Dec 2015 15:29:02 +0800 Subject: [PATCH] change api --- components/transfer/demo/advanced.md | 41 ++++- components/transfer/demo/basic.md | 36 +++- components/transfer/demo/search.md | 55 +++++- components/transfer/index.jsx | 244 ++++++++++----------------- components/transfer/index.md | 15 +- components/transfer/list.jsx | 79 +++++---- components/transfer/operation.jsx | 7 +- components/transfer/search.jsx | 6 +- style/components/transfer.less | 13 +- 9 files changed, 274 insertions(+), 222 deletions(-) diff --git a/components/transfer/demo/advanced.md b/components/transfer/demo/advanced.md index 8c868010ee..30e8eb833d 100644 --- a/components/transfer/demo/advanced.md +++ b/components/transfer/demo/advanced.md @@ -2,7 +2,7 @@ - order: 2 -高级用法 +穿梭框高级用法 --- @@ -13,7 +13,8 @@ const container = document.getElementById('components-transfer-demo-advanced'); const App = React.createClass({ getInitialState() { return { - mockData: [] + mockData: [], + targetKeys: [], }; }, @@ -22,23 +23,47 @@ const App = React.createClass({ }, getMock() { + let targetKeys = []; let mockData = []; for (let i = 0; i < 20; i++) { - mockData.push({ + const data = { + key: i, title: '内容' + (i + 1), - value: (i + 1), description: '内容' + (i + 1) + '的描述', chosen: Math.random() * 2 > 1 - }); + }; + if (data.chosen) { + targetKeys.push(data.key); + } + mockData.push(data); } this.setState({ - mockData: mockData + mockData: mockData, + targetKeys: targetKeys, }); }, + + handleChange(targetKeys) { + this.setState({ + targetKeys: targetKeys, + }); + }, + + renderFooter(props) { + return ; + }, + render() { return
- - + { return item.title + item.description; }} + footer={this.renderFooter}/>
; } }); diff --git a/components/transfer/demo/basic.md b/components/transfer/demo/basic.md index eea7957bff..aba704a4f5 100644 --- a/components/transfer/demo/basic.md +++ b/components/transfer/demo/basic.md @@ -13,7 +13,8 @@ const container = document.getElementById('components-transfer-demo-basic'); const App = React.createClass({ getInitialState() { return { - mockData: [] + mockData: [], + targetKeys: [], }; }, @@ -22,23 +23,44 @@ const App = React.createClass({ }, getMock() { + let targetKeys = []; let mockData = []; for (let i = 0; i < 20; i++) { - mockData.push({ + const data = { + key: i, title: '内容' + (i + 1), - value: (i + 1), description: '内容' + (i + 1) + '的描述', chosen: Math.random() * 2 > 1 - }); + }; + if (data.chosen) { + targetKeys.push(data.key); + } + mockData.push(data); } this.setState({ - mockData: mockData + mockData: mockData, + targetKeys: targetKeys, }); }, + + handleChange(targetKeys) { + this.setState({ + targetKeys: targetKeys, + }); + }, + + renderFooter(props) { + return ; + }, + render() { return
- - + { return item.title + item.description; }} />
; } }); diff --git a/components/transfer/demo/search.md b/components/transfer/demo/search.md index 782739ea01..b0ce83d294 100644 --- a/components/transfer/demo/search.md +++ b/components/transfer/demo/search.md @@ -2,7 +2,7 @@ - order: 1 -带搜索的穿梭框 +带搜索框的 穿梭框 --- @@ -10,5 +10,56 @@ import { Transfer } from 'antd'; const container = document.getElementById('components-transfer-demo-search'); -ReactDOM.render(, container); +const App = React.createClass({ + getInitialState() { + return { + mockData: [], + targetKeys: [], + }; + }, + + componentDidMount() { + this.getMock(); + }, + + getMock() { + let targetKeys = []; + let mockData = []; + for (let i = 0; i < 20; i++) { + const data = { + key: i, + title: '内容' + (i + 1), + description: '内容' + (i + 1) + '的描述', + chosen: Math.random() * 2 > 1 + }; + if (data.chosen) { + targetKeys.push(data.key); + } + mockData.push(data); + } + this.setState({ + mockData: mockData, + targetKeys: targetKeys, + }); + }, + + handleChange(targetKeys) { + this.setState({ + targetKeys: targetKeys, + }); + }, + + render() { + return
+ { return item.title + item.description;}} /> +
; + } +}); + +ReactDOM.render(, container); ```` diff --git a/components/transfer/index.jsx b/components/transfer/index.jsx index 1026746e1f..b89860fb7b 100644 --- a/components/transfer/index.jsx +++ b/components/transfer/index.jsx @@ -11,194 +11,133 @@ class Transfer extends Component { super(props); this.state = { - dataSource: props.dataSource, leftFilter: '', rightFilter: '', + leftCheckedKeys: [], + rightCheckedKeys: [] }; } - componentWillReceiveProps(nextProps) { - this.setState({ - dataSource: nextProps.dataSource, - }); - } + splitDataSource() { + const { targetKeys, dataSource } = this.props; - checkDirection(direction) { - const { filterKey } = this.props; - let { dataSource } = this.state; - - let result = false; + let leftDataSource = Object.assign([], dataSource); + let rightDataSource = []; - if ( direction === 'right' ) { - dataSource.forEach((data) => { - if ( !data[filterKey] && data.checked ) { - result = true; - } - }); - } else { - dataSource.forEach((data) => { - if ( data[filterKey] && data.checked ) { - result = true; - } + if ( targetKeys.length > 0 ) { + targetKeys.forEach((targetKey) => { + rightDataSource.push(leftDataSource.find((data, index) => { + if( data.key === targetKey ) { + leftDataSource.splice(index, 1); + return true; + } + })); }); } - return result; + + return { + leftDataSource: leftDataSource, + rightDataSource: rightDataSource, + }; } moveTo(direction) { - const { filterKey } = this.props; - let { dataSource } = this.state; - // TODO: 验证是否可点 - if ( direction === 'right' ) { - dataSource.forEach((data) => { - // 左边向右移动 - if ( !data[filterKey] && data.checked ) { - data[filterKey] = true; - data.checked = false; - } - }); - this.setState({ - leftFilter: '', - dataSource: dataSource, - }); - } else { - dataSource.forEach((data) => { - if ( data[filterKey] && data.checked ) { - data[filterKey] = false; - data.checked = false; - } - }); - this.setState({ - rightFilter: '', - dataSource: dataSource, - }); - } + const { targetKeys } = this.props; + const { leftCheckedKeys, rightCheckedKeys } = this.state; + // move items to target box + const newTargetKeys = direction === 'right' ? + leftCheckedKeys.concat(targetKeys) : + targetKeys.filter((targetKey) => !rightCheckedKeys.some((checkedKey) => targetKey === checkedKey)); + + // empty checked keys + this.setState({ + [direction === 'right' ? 'leftCheckedKeys' : 'rightCheckedKeys']: [], + }); + + this.props.onChange(newTargetKeys); } handleSelectAll(direction, globalCheckStatus) { - const { filterKey } = this.props; - const { dataSource, leftFilter, rightFilter } = this.state; - switch ( globalCheckStatus ) { - // 选中部分,则全选 - case 'part': - case 'none': - dataSource.forEach((data)=> { - // data[filterKey] true 时,在右侧 - if ( direction === 'right' && data[filterKey] && this.matchFilter(data.title, rightFilter) - || direction === 'left' && !data[filterKey] && this.matchFilter(data.title, leftFilter)) { - data.checked = true; - } - }); - break; - case 'all': - dataSource.forEach((data)=> { - if ( direction === 'right' && data[filterKey] && this.matchFilter(data.title, rightFilter) - || direction === 'left' && !data[filterKey] && this.matchFilter(data.title, leftFilter)) { - data.checked = false; - } - }); - break; - default: - break; + const { leftDataSource, rightDataSource } = this.splitDataSource(); + const dataSource = direction === 'left' ? leftDataSource : rightDataSource; + let holder = []; + + if ( globalCheckStatus === 'all' ) { + holder = []; + } else { + holder = dataSource.map((data) => data.key); } this.setState({ - dataSource: dataSource, + [direction === 'left' ? 'leftCheckedKeys' : 'rightCheckedKeys']: holder, }); } handleFilter(direction, e) { - const filterText = e.target.value; - if ( direction === 'left') { - this.setState({ - 'leftFilter': filterText, - }); - } else { - this.setState({ - 'rightFilter': filterText, - }); - } + this.setState({ + [direction === 'left' ? 'leftFilter' : 'rightFilter']: e.target.value, + }); } handleClear(direction) { - if ( direction === 'left') { - this.setState({ - 'leftFilter': '', - }); - } else { - this.setState({ - 'rightFilter': '', - }); - } - } - - matchFilter(text, filterText) { - const regex = new RegExp(filterText); - return text.match(regex); - } - - handleSelect(selectedItem, checked) { - const { dataSource } = this.state; - dataSource.forEach((data)=> { - if ( data.value === selectedItem.value ) { - data.checked = checked; - } + this.setState({ + [direction === 'left' ? 'leftFilter' : 'rightFilter']: '', }); + } + handleSelect(direction, selectedItem, checked) { + const { leftCheckedKeys, rightCheckedKeys } = this.state; + const holder = direction === 'left' ? leftCheckedKeys : rightCheckedKeys; + const index = holder.findIndex((key) => key === selectedItem.key); + if ( index > -1 ) { + holder.splice(index, 1); + } + if ( checked ) { + holder.push(selectedItem.key); + } this.setState({ - dataSource: dataSource, + [direction === 'left' ? 'leftCheckedKeys' : 'rightCheckedKeys']: holder, }); } render() { - const { prefixCls, leftConfig, rightConfig, filterKey, showSearch, header, body, footer } = this.props; - const { dataSource, leftFilter, rightFilter } = this.state; - - let leftDataSource = []; - let rightDataSource = []; + const { prefixCls, titles, operations, showSearch, searchPlaceholder, body, footer } = this.props; + const { leftFilter, rightFilter, leftCheckedKeys, rightCheckedKeys } = this.state; - let leftActive = this.checkDirection('left'); - let rightActive = this.checkDirection('right'); - - dataSource.map((item)=> { - // filter item - if ( item[filterKey] ) { - if ( this.matchFilter(item.title, rightFilter) ) { - rightDataSource.push(item); - } - } else { - if ( this.matchFilter(item.title, leftFilter) ) { - leftDataSource.push(item); - } - } - }); + const { leftDataSource, rightDataSource } = this.splitDataSource(); + let leftActive = rightCheckedKeys.length > 0; + let rightActive = leftCheckedKeys.length > 0; return
- - - + @@ -206,41 +145,32 @@ class Transfer extends Component { } } -// onChange-> do operation -// onSelect-> select action row - Transfer.defaultProps = { prefixCls: 'ant-transfer', dataSource: [], - dataIndex: 'title', - filterKey: 'chosen', + render: noop, + targetKeys: [], onChange: noop, - onSelect: noop, - leftConfig: { - title: '源列表', - operationText: '审核入库', - }, - rightConfig: { - title: '目的列表', - operationText: '审核出库', - }, + titles: ['源列表', '目的列表'], + operations: [], showSearch: false, searchPlaceholder: '请输入搜索内容', - header: noop, - footer: noop, body: noop, + footer: noop, }; Transfer.propTypes = { prefixCls: PropTypes.string, dataSource: PropTypes.array, + render: PropTypes.func, + targetKeys: PropTypes.array, + onChange: PropTypes.func, + titles: PropTypes.array, + operations: PropTypes.array, showSearch: PropTypes.bool, searchPlaceholder: PropTypes.string, - operationText: PropTypes.string, - leftTitle: PropTypes.string, - rightTitle: PropTypes.string, - onChange: PropTypes.func, - extraRender: PropTypes.func, + body: PropTypes.func, + footer: PropTypes.func, }; export default Transfer; diff --git a/components/transfer/index.md b/components/transfer/index.md index e6e8f90c90..90a2684697 100644 --- a/components/transfer/index.md +++ b/components/transfer/index.md @@ -11,15 +11,18 @@ ## 何时使用 -- 需要表示开关状态/两种状态之间的切换时; -- 和 `switch`的区别是,切换 `switch` 会直接触发状态改变,而 `checkbox` 一般用于状态标记,需要和提交操作配合。 ## API -### Checkbox +### Transfer | 参数 | 说明 | 类型 | 可选值 |默认值 | |-----------|------------------------------------------|------------|-------|--------| -| dataSource | 指定当前是否选中 | boolean | | false | -| defaultChecked | 初始是否选中 | boolean | | false | -| onChange | 变化时回调函数 | Function(e:Event) | | | | +| dataSource | 数据源 | Array | | [] | +| render | 渲染每行数据 | Function(record) | | false | +| targetKeys | 显示在右侧框的数据 | Array | | | +| onChange | 变化时回调函数 | Function(e:Event) | | | +| titles | 标题集合,顺序从左至右 | Array | | [] | +| operations | 集合,顺序从左至右 | Array | | [] | +| showSearch | 是否显示搜索框 | Boolean | | false | +| searchPlaceholder | 搜索框的默认值 | String | | | diff --git a/components/transfer/list.jsx b/components/transfer/list.jsx index 18345a51f6..cc20d2d89d 100644 --- a/components/transfer/list.jsx +++ b/components/transfer/list.jsx @@ -2,7 +2,6 @@ import React, { Component, PropTypes } from 'react'; import Checkbox from '../checkbox'; import Search from './search.jsx'; import {classSet} from 'rc-util'; - function noop() { } @@ -13,11 +12,13 @@ class TransferList extends Component { } handleSelectALl() { - this.props.handleSelectAll(this.getGlobalCheckStatus()); + this.props.handleSelectAll(this.getGlobalCheckStatus(), this.filter); } handleSelect(selectedItem) { - this.props.handleSelect(selectedItem, !selectedItem.checked); + const { checkedKeys } = this.props; + const result = checkedKeys.some((key) => key === selectedItem.key); + this.props.handleSelect(selectedItem, !result); } handleFilter(e) { @@ -29,18 +30,12 @@ class TransferList extends Component { } getGlobalCheckStatus() { - let { dataSource } = this.props; + const { dataSource, checkedKeys } = this.props; let globalCheckStatus; - let selectedRowLength = 0; - dataSource.forEach((data)=> { - if ( data.checked ) { - selectedRowLength++; - } - }); - if ( selectedRowLength > 0 ) { - if ( selectedRowLength < dataSource.length ) { + if ( checkedKeys.length > 0 ) { + if ( checkedKeys.length < dataSource.length ) { globalCheckStatus = 'part'; } else { globalCheckStatus = 'all'; @@ -72,38 +67,53 @@ class TransferList extends Component { return ({customEle}); } + matchFilter(text, filterText) { + const regex = new RegExp(filterText); + return text.match(regex); + } + render() { - const { prefixCls, config, header, footer, dataSource, filter, body } = this.props; + let self = this; + const { prefixCls, dataSource, title, filter, checkedKeys, body, footer, showSearch } = this.props; let globalCheckStatus = this.getGlobalCheckStatus(); // Custom Layout - const headerDom = header({...this.props, globalCheckStatus}); const footerDom = footer({...this.props, globalCheckStatus}); const bodyDom = body({...this.props, globalCheckStatus}); return (
- { headerDom ?
- { headerDom } -
:
+
{this.renderCheckbox({ prefixCls: 'ant-tree', checked: globalCheckStatus === 'all', checkPart: globalCheckStatus === 'part', checkable: - })} {dataSource.length} 条 - {config.title} -
} - { bodyDom ? bodyDom :
-
+ })} { (checkedKeys.length > 0 ? checkedKeys.length + '/' : '') + dataSource.length} 条 + {title} +
+ { bodyDom ? bodyDom : +
+ { showSearch ?
-
-
    - {dataSource.map((item)=> { - return
  • - - { item.title } -
  • ;})} +
: null } +
    + { dataSource.length > 0 ? + dataSource.map((item)=> { + // apply filter + const itemText = self.props.render(item); + const filterResult = self.matchFilter(itemText, filter); + + if ( filterResult ) { + return
  • + key === item.key)} /> + { self.props.render(item) } +
  • ; + } + }) :
    + Not Found +
    + }
} { footerDom ?
@@ -116,12 +126,13 @@ class TransferList extends Component { TransferList.defaultProps = { prefixCls: 'ant-transfer-list', dataSource: [], - defaultDataSource: [], + showSearch: false, + searchPlaceholder: '', handleFilter: noop, handleSelect: noop, - onChange: noop, + handleSelectAll: noop, + render: noop, //advanced - header: noop, footer: noop, body: noop, }; @@ -129,12 +140,12 @@ TransferList.defaultProps = { TransferList.propTypes = { prefixCls: PropTypes.string, dataSource: PropTypes.array, - footer: PropTypes.func, searchPlaceholder: PropTypes.string, handleFilter: PropTypes.func, handleSelect: PropTypes.func, handleSelectAll: PropTypes.func, - config: PropTypes.object, + body: PropTypes.func, + footer: PropTypes.func, }; export default TransferList; diff --git a/components/transfer/operation.jsx b/components/transfer/operation.jsx index d83debc3fe..930d878307 100644 --- a/components/transfer/operation.jsx +++ b/components/transfer/operation.jsx @@ -1,5 +1,6 @@ import React, { Component, PropTypes } from 'react'; import Button from '../button'; +import Icon from '../icon'; function noop() { } @@ -9,8 +10,10 @@ class TransferOperation extends Component { const { moveToLeft, moveToRight, leftArrowText, rightArrowText, leftActive, rightActive, prefixCls } = this.props; return
- - + { rightArrowText ? : + } + {leftArrowText ? : + }
; } } diff --git a/components/transfer/search.jsx b/components/transfer/search.jsx index c134b64196..02e37dea25 100644 --- a/components/transfer/search.jsx +++ b/components/transfer/search.jsx @@ -15,9 +15,11 @@ class Search extends Component { render() { const {placeholder, value, prefixCls} = this.props; return
- + { value && value.length > 0 ? - + : }
; diff --git a/style/components/transfer.less b/style/components/transfer.less index 86765ec9b0..61031c2dce 100644 --- a/style/components/transfer.less +++ b/style/components/transfer.less @@ -8,7 +8,6 @@ display: inline-block; border-radius: 6px; width: 160px; - height: 191px; &-search { &-action { @@ -40,7 +39,6 @@ font-size: 12px; line-height: 1.5; position: relative; - padding-top: 30px; height: 150px; &-search-wrapper { @@ -50,7 +48,12 @@ height: 28px; padding: 4px; width: 100%; + } + &-not-found { + margin-top: 24px; + color: #ccc; + text-align: center; } ul { @@ -66,10 +69,12 @@ } } + &-body-with-search { + padding-top: 34px; + } + &-footer { border-top: 1px solid #e9e9e9; - padding: 8px 20px 16px 10px; - text-align: right; border-radius: 0 0 5px 5px; } } -- GitLab