change api

This commit is contained in:
Daqi Song 2015-12-21 15:29:02 +08:00
parent 0e82e14030
commit 765ce7bb67
9 changed files with 276 additions and 224 deletions

View File

@ -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 <Button type="primary" size="small" style={{ float: 'right', margin: '5' }}
onClick={this.getMock}>刷新</Button>;
},
render() {
return <div>
<Transfer defaultDataSource={this.state.mockData} />
<Button onClick={this.getMock}>刷新数据</Button>
<Transfer
dataSource={this.state.mockData}
showSearch
operations={['hello', 'world']}
targetKeys={this.state.targetKeys}
onChange={this.handleChange}
render={(item) => { return item.title + item.description; }}
footer={this.renderFooter}/>
</div>;
}
});

View File

@ -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 <Button type="primary" size="small" style={{ float: 'right', margin: '5' }}
onClick={this.getMock}>刷新</Button>;
},
render() {
return <div>
<Transfer dataSource={this.state.mockData} />
<Button onClick={this.getMock}>刷新数据</Button>
<Transfer
dataSource={this.state.mockData}
targetKeys={this.state.targetKeys}
onChange={this.handleChange}
render={(item) => { return item.title + item.description; }} />
</div>;
}
});

View File

@ -2,7 +2,7 @@
- order: 1
带搜索的穿梭框
带搜索 穿梭框
---
@ -10,5 +10,56 @@
import { Transfer } from 'antd';
const container = document.getElementById('components-transfer-demo-search');
ReactDOM.render(<Transfer />, 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 <div>
<Transfer
dataSource={this.state.mockData}
showSearch
targetKeys={this.state.targetKeys}
onChange={this.handleChange}
render={(item) => { return item.title + item.description;}} />
</div>;
}
});
ReactDOM.render(<App />, container);
````

View File

@ -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 leftDataSource = Object.assign([], dataSource);
let rightDataSource = [];
let result = false;
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({
dataSource: dataSource,
[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({
[direction === 'left' ? 'leftCheckedKeys' : 'rightCheckedKeys']: holder,
});
}
render() {
const { prefixCls, leftConfig, rightConfig, filterKey, showSearch, header, body, footer } = this.props;
const { dataSource, leftFilter, rightFilter } = this.state;
const { prefixCls, titles, operations, showSearch, searchPlaceholder, body, footer } = this.props;
const { leftFilter, rightFilter, leftCheckedKeys, rightCheckedKeys } = this.state;
let leftDataSource = [];
let rightDataSource = [];
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 <div className={prefixCls}>
<List config={leftConfig}
<List title={titles[0]}
dataSource={leftDataSource}
filter={leftFilter}
checkedKeys={leftCheckedKeys}
handleFilter={this.handleFilter.bind(this, 'left')}
handleClear={this.handleClear.bind(this, 'left')}
handleSelect={this.handleSelect.bind(this)}
handleSelect={this.handleSelect.bind(this, 'left')}
handleSelectAll={this.handleSelectAll.bind(this, 'left')}
position="left"
render={this.props.render}
showSearch={showSearch}
header={header}
searchPlaceholder={searchPlaceholder}
body={body}
footer={footer}
/>
<Operation rightActive={rightActive} rightArrowText={leftConfig.operationText} moveToRight={this.moveTo.bind(this, 'right')}
leftActive={leftActive} leftArrowText={rightConfig.operationText} moveToLeft={this.moveTo.bind(this, 'left')} />
<List config={rightConfig}
<Operation rightActive={rightActive} rightArrowText={operations[0]} moveToRight={this.moveTo.bind(this, 'right')}
leftActive={leftActive} leftArrowText={operations[1]} moveToLeft={this.moveTo.bind(this, 'left')} />
<List title={titles[1]}
dataSource={rightDataSource}
filter={rightFilter}
checkedKeys={rightCheckedKeys}
handleFilter={this.handleFilter.bind(this, 'right')}
handleClear={this.handleClear.bind(this, 'right')}
handleSelect={this.handleSelect.bind(this)}
handleSelect={this.handleSelect.bind(this, 'right')}
handleSelectAll={this.handleSelectAll.bind(this, 'right')}
position="right"
render={this.props.render}
showSearch={showSearch}
header={header}
searchPlaceholder={searchPlaceholder}
body={body}
footer={footer}
/>
@ -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;

View File

@ -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 | | |

View File

@ -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 (<span ref="checkbox" className={classSet(checkboxCls)} onClick={this.handleSelectALl.bind(this)}>{customEle}</span>);
}
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 (<div className={prefixCls} {...this.props}>
{ headerDom ? <div className={`${prefixCls}-header`}>
{ headerDom }
</div> : <div className={`${prefixCls}-header`}>
<div className={`${prefixCls}-header`}>
{this.renderCheckbox({
prefixCls: 'ant-tree',
checked: globalCheckStatus === 'all',
checkPart: globalCheckStatus === 'part',
checkable: <span className={`ant-tree-checkbox-inner`}></span>
})} {dataSource.length}
<span className={`${prefixCls}-header-title`}>{config.title}</span>
</div> }
{ bodyDom ? bodyDom : <div className={`${prefixCls}-body`}>
<div className={`${prefixCls}-body-search-wrapper`}>
})} { (checkedKeys.length > 0 ? checkedKeys.length + '/' : '') + dataSource.length}
<span className={`${prefixCls}-header-title`}>{title}</span>
</div>
{ bodyDom ? bodyDom :
<div className={ showSearch ? `${prefixCls}-body ${prefixCls}-body-with-search` : `${prefixCls}-body`}>
{ showSearch ? <div className={`${prefixCls}-body-search-wrapper`}>
<Search className={`${prefixCls}-body-search-bar`} onChange={this.handleFilter.bind(this)} handleClear={this.handleClear.bind(this)} value={filter} />
</div>
<ul className="">
{dataSource.map((item)=> {
return <li onClick={this.handleSelect.bind(this, item)}>
<Checkbox checked={item.checked} />
{ item.title }
</li>;})}
</div> : null }
<ul>
{ dataSource.length > 0 ?
dataSource.map((item)=> {
// apply filter
const itemText = self.props.render(item);
const filterResult = self.matchFilter(itemText, filter);
if ( filterResult ) {
return <li onClick={this.handleSelect.bind(this, item)}>
<Checkbox checked={checkedKeys.some((key) => key === item.key)} />
{ self.props.render(item) }
</li>;
}
}) : <div className={`${prefixCls}-body-not-found`}>
Not Found
</div>
}
</ul>
</div>}
{ footerDom ? <div className={`${prefixCls}-footer`}>
@ -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;

View File

@ -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 <div className={`${prefixCls}`}>
<Button style={{ 'margin-bottom': '4px' }} disabled={ !rightActive ? 'disabled' : false } onClick={moveToRight}>{rightArrowText + '>'}</Button>
<Button disabled={ !leftActive ? 'disabled' : false } onClick={moveToLeft}>{'<' + leftArrowText}</Button>
{ rightArrowText ? <Button type="primary" style={{ 'margin-bottom': '4px' }} disabled={ !rightActive ? 'disabled' : false } onClick={moveToRight}>{rightArrowText}<Icon type="right" /></Button> :
<Button type="primary" style={{ 'margin-bottom': '4px' }} disabled={ !rightActive ? 'disabled' : false } onClick={moveToRight}><Icon type="right" /></Button>}
{leftArrowText ? <Button type="primary" disabled={ !leftActive ? 'disabled' : false } onClick={moveToLeft}><Icon type="left" />{leftArrowText}</Button> :
<Button type="primary" disabled={ !leftActive ? 'disabled' : false } onClick={moveToLeft}><Icon type="left" /></Button>}
</div>;
}
}

View File

@ -15,9 +15,11 @@ class Search extends Component {
render() {
const {placeholder, value, prefixCls} = this.props;
return <div>
<input placeholder={placeholder} className={ prefixCls + ' ant-input' } value={ value } ref="input" onChange={this.handleChange.bind(this)}/>
<input placeholder={placeholder} className={ prefixCls + ' ant-input' } value={ value } ref="input"
onChange={this.handleChange.bind(this)}/>
{ value && value.length > 0 ?
<a href="javascirpt:;" className={ prefixCls + '-action' } onClick={this.props.handleClear}><i className="anticon anticon-cross-circle"></i></a>
<a href="javascirpt:;" className={ prefixCls + '-action' } onClick={this.props.handleClear}><i
className="anticon anticon-cross-circle"></i></a>
: <span className={ prefixCls + '-action' }><i className="anticon anticon-search"></i></span>
}
</div>;

View File

@ -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;
}
}