Compare commits
9 Commits
master
...
draggableT
Author | SHA1 | Date |
---|---|---|
yangchch6 | a74db774cd | |
yangchch6 | 259d69d0cb | |
yangchch6 | dc24d15c96 | |
yangchch6 | c409179a58 | |
yangchch6 | 1f97ec83d6 | |
yangchch6 | 8ec2d4633d | |
yangchch6 | a710536dfe | |
yangchch6 | 98e355409f | |
yangchch6 | b2cac976f0 |
190
README.md
190
README.md
|
@ -1,94 +1,96 @@
|
|||
# bee-transfer
|
||||
|
||||
[![npm version](https://img.shields.io/npm/v/bee-transfer.svg)](https://www.npmjs.com/package/bee-transfer)
|
||||
[![Build Status](https://img.shields.io/travis/tinper-bee/bee-transfer/master.svg)](https://travis-ci.org/tinper-bee/bee-transfer)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/tinper-bee/bee-transfer/badge.svg?branch=master)](https://coveralls.io/github/tinper-bee/bee-transfer?branch=master)
|
||||
[![devDependency Status](https://img.shields.io/david/dev/tinper-bee/bee-transfer.svg)](https://david-dm.org/tinper-bee/bee-transfer#info=devDependencies)
|
||||
[![NPM downloads](http://img.shields.io/npm/dm/bee-transfer.svg?style=flat)](https://npmjs.org/package/bee-transfer)
|
||||
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tinper-bee/bee-transfer.svg)](http://isitmaintained.com/project/tinper-bee/bee-transfer "Average time to resolve an issue")
|
||||
[![Percentage of issues still open](http://isitmaintained.com/badge/open/tinper-bee/bee-transfer.svg)](http://isitmaintained.com/project/tinper-bee/bee-transfer "Percentage of issues still open")
|
||||
|
||||
|
||||
两框之间的元素迁移,非常直观且有效。一个或多个元素选择后点击方向按钮转到另一列框中。左栏是“源”,右边是“目标”
|
||||
|
||||
## 使用
|
||||
|
||||
### 使用单独的transfer包
|
||||
#### 组件引入
|
||||
先进行下载bee-transfer包
|
||||
```
|
||||
npm install --save bee-transfer
|
||||
```
|
||||
组件调用
|
||||
```js
|
||||
import Transfer from 'bee-transfer';
|
||||
|
||||
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: i % 3 < 1,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
<Transfer
|
||||
dataSource={mockData}
|
||||
titles={['Source', 'Target']}
|
||||
render={item => item.title}
|
||||
/>
|
||||
, document.getElementById('target'));
|
||||
```
|
||||
#### 样式引入
|
||||
- 可以使用link引入build目录下Transfer.css
|
||||
```
|
||||
<link rel="stylesheet" href="./node_modules/bee-transfer/build/Transfer.css">
|
||||
```
|
||||
- 可以在js中import样式
|
||||
```js
|
||||
import "./node_modules/bee-transfer/src/Transfer.scss"
|
||||
//或是
|
||||
import "./node_modules/bee-transfer/build/Transfer.css"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
|参数|说明|类型|默认值|
|
||||
|:--|:---:|:--:|---:|
|
||||
|dataSource|设置数据源。当有targetKey props存在时,dataSource的数据刨去targetKey数据,剩下的都放在左边列表|[]|[]|
|
||||
|render|自定义的展示出来的item,需要展示哪些字段|Function(record)|-|
|
||||
|targetKeys|展示在右边列表的数据集|[]|[]|
|
||||
|selectedKeys|所有选中的item的keys|[]|[]|
|
||||
|onChange|当item在穿梭成功后的回调 参数(targetKeys, direction, moveKeys)|func|-|
|
||||
|onSelectChange| 当选中的item发生改变时的回调 参数(sourceSelectedKeys, targetSelectedKeys)|fun|-|
|
||||
|onScroll| 当滑动可选的item列表的回调 参数(direction, event)|func|-|
|
||||
|listStyle|自定义的columns的样式表|object |-|
|
||||
|className|class|string|''|''|
|
||||
|titles|两columns的title|[]|-|
|
||||
|operations|自定义按钮操作|[]|'>', '<'|
|
||||
|showSearch|是否显示搜索框|boolean |false|
|
||||
|filterOption|搜索过滤方法 参数(inputValue, option)|func或者boolean |-|
|
||||
|searchPlaceholder|搜索框的默认显示文字|string|'Search here'|
|
||||
|notFoundContent|当没有相关内容的显示内容|string或ReactNode| 'The list is empty'|
|
||||
|footer|渲染底部的dom|ReactNode|-|
|
||||
|lazy|懒加载dom|object|当tranfer放在bee-modal里 添加参数 lazy={{container:"modal"}}|
|
||||
|onSearchChange|当搜索域变化的回调函数 参数(direction: 'left'|'right', event: Event)|func|-|
|
||||
|
||||
|
||||
#### 开发调试
|
||||
|
||||
```sh
|
||||
$ npm install -g bee-tools
|
||||
$ git clone https://github.com/tinper-bee/bee-transfer
|
||||
$ cd bee-transfer
|
||||
$ npm install
|
||||
$ npm run dev
|
||||
```
|
||||
# bee-transfer
|
||||
|
||||
[![npm version](https://img.shields.io/npm/v/bee-transfer.svg)](https://www.npmjs.com/package/bee-transfer)
|
||||
[![Build Status](https://img.shields.io/travis/tinper-bee/bee-transfer/master.svg)](https://travis-ci.org/tinper-bee/bee-transfer)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/tinper-bee/bee-transfer/badge.svg?branch=master)](https://coveralls.io/github/tinper-bee/bee-transfer?branch=master)
|
||||
[![devDependency Status](https://img.shields.io/david/dev/tinper-bee/bee-transfer.svg)](https://david-dm.org/tinper-bee/bee-transfer#info=devDependencies)
|
||||
[![NPM downloads](http://img.shields.io/npm/dm/bee-transfer.svg?style=flat)](https://npmjs.org/package/bee-transfer)
|
||||
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tinper-bee/bee-transfer.svg)](http://isitmaintained.com/project/tinper-bee/bee-transfer "Average time to resolve an issue")
|
||||
[![Percentage of issues still open](http://isitmaintained.com/badge/open/tinper-bee/bee-transfer.svg)](http://isitmaintained.com/project/tinper-bee/bee-transfer "Percentage of issues still open")
|
||||
|
||||
|
||||
两框之间的元素迁移,非常直观且有效。一个或多个元素选择后点击方向按钮转到另一列框中。左栏是“源”,右边是“目标”
|
||||
|
||||
## 使用
|
||||
|
||||
### 使用单独的transfer包
|
||||
#### 组件引入
|
||||
先进行下载bee-transfer包
|
||||
```
|
||||
npm install --save bee-transfer
|
||||
```
|
||||
组件调用
|
||||
```js
|
||||
import Transfer from 'bee-transfer';
|
||||
|
||||
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: i % 3 < 1,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
<Transfer
|
||||
dataSource={mockData}
|
||||
titles={['Source', 'Target']}
|
||||
render={item => item.title}
|
||||
/>
|
||||
, document.getElementById('target'));
|
||||
```
|
||||
#### 样式引入
|
||||
- 可以使用link引入build目录下Transfer.css
|
||||
```
|
||||
<link rel="stylesheet" href="./node_modules/bee-transfer/build/Transfer.css">
|
||||
```
|
||||
- 可以在js中import样式
|
||||
```js
|
||||
import "./node_modules/bee-transfer/src/Transfer.scss"
|
||||
//或是
|
||||
import "./node_modules/bee-transfer/build/Transfer.css"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
|参数|说明|类型|默认值|
|
||||
|:--|:---:|:--:|---:|
|
||||
|dataSource|设置数据源。当有targetKey props存在时,dataSource的数据刨去targetKey数据,剩下的都放在左边列表|[]|[]|
|
||||
|render|自定义的展示出来的item,需要展示哪些字段|Function(record)|-|
|
||||
|targetKeys|展示在右边列表的数据集|[]|[]|
|
||||
|selectedKeys|所有选中的item的keys|[]|[]|
|
||||
|onChange|当item在穿梭成功后的回调 参数(targetKeys, direction, moveKeys)|func|-|
|
||||
|onSelectChange| 当选中的item发生改变时的回调 参数(sourceSelectedKeys, targetSelectedKeys)|fun|-|
|
||||
|onScroll| 当滑动可选的item列表的回调 参数(direction, event)|func|-|
|
||||
|listStyle|自定义的columns的样式表|object |-|
|
||||
|className|class|string|''|''|
|
||||
|titles|两columns的title|[]|-|
|
||||
|operations|自定义按钮操作|[]|'>', '<'|
|
||||
|showSearch|是否显示搜索框|boolean |false|
|
||||
|filterOption|搜索过滤方法 参数(inputValue, option)|func或者boolean |-|
|
||||
|searchPlaceholder|搜索框的默认显示文字|string|'Search'|
|
||||
|notFoundContent|当没有相关内容的显示内容|string或ReactNode| 'Not Found'|
|
||||
|footer|渲染底部的dom|ReactNode|-|
|
||||
|lazy|懒加载dom|object|当tranfer放在bee-modal里 添加参数 lazy={{container:"modal"}}|
|
||||
|onSearchChange|当搜索域变化的回调函数 参数(direction: 'left'|'right', event: Event)|func|-|
|
||||
|showCheckbox|是否显示Checkbox复选框|bool|true|
|
||||
|draggable|是否可以通过拖拽进行穿梭和排序|bool|false|
|
||||
|
||||
|
||||
#### 开发调试
|
||||
|
||||
```sh
|
||||
$ npm install -g bee-tools
|
||||
$ git clone https://github.com/tinper-bee/bee-transfer
|
||||
$ cd bee-transfer
|
||||
$ npm install
|
||||
$ npm run dev
|
||||
```
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactComponentWithPureRenderMixin
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactComponentWithPureRenderMixin
|
||||
*/
|
||||
|
||||
var shallowEqual = require('shallowequal');
|
||||
|
@ -17,31 +17,31 @@ function shallowCompare(instance, nextProps, nextState) {
|
|||
return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState);
|
||||
}
|
||||
|
||||
/**
|
||||
* If your React component's render function is "pure", e.g. it will render the
|
||||
* same result given the same props and state, provide this mixin for a
|
||||
* considerable performance boost.
|
||||
*
|
||||
* Most React components have pure render functions.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var ReactComponentWithPureRenderMixin =
|
||||
* require('ReactComponentWithPureRenderMixin');
|
||||
* React.createClass({
|
||||
* mixins: [ReactComponentWithPureRenderMixin],
|
||||
*
|
||||
* render: function() {
|
||||
* return <div className={this.props.className}>foo</div>;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* Note: This only checks shallow equality for props and state. If these contain
|
||||
* complex data structures this mixin may have false-negatives for deeper
|
||||
* differences. Only mixin to components which have simple props and state, or
|
||||
* use `forceUpdate()` when you know deep data structures have changed.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/pure-render-mixin.html
|
||||
/**
|
||||
* If your React component's render function is "pure", e.g. it will render the
|
||||
* same result given the same props and state, provide this mixin for a
|
||||
* considerable performance boost.
|
||||
*
|
||||
* Most React components have pure render functions.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var ReactComponentWithPureRenderMixin =
|
||||
* require('ReactComponentWithPureRenderMixin');
|
||||
* React.createClass({
|
||||
* mixins: [ReactComponentWithPureRenderMixin],
|
||||
*
|
||||
* render: function() {
|
||||
* return <div className={this.props.className}>foo</div>;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* Note: This only checks shallow equality for props and state. If these contain
|
||||
* complex data structures this mixin may have false-negatives for deeper
|
||||
* differences. Only mixin to components which have simple props and state, or
|
||||
* use `forceUpdate()` when you know deep data structures have changed.
|
||||
*
|
||||
* See https://facebook.github.io/react/docs/pure-render-mixin.html
|
||||
*/
|
||||
var ReactComponentWithPureRenderMixin = {
|
||||
shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) {
|
||||
|
|
1690
build/Transfer.css
1690
build/Transfer.css
File diff suppressed because it is too large
Load Diff
|
@ -28,14 +28,18 @@ var _propTypes = require('prop-types');
|
|||
|
||||
var _propTypes2 = _interopRequireDefault(_propTypes);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
var _reactBeautifulDnd = require('react-beautiful-dnd');
|
||||
|
||||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
||||
var _utils = require('./utils');
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }
|
||||
|
||||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
||||
|
@ -47,7 +51,11 @@ function noop() {}
|
|||
var defaultProps = {
|
||||
dataSource: [],
|
||||
render: noop,
|
||||
showSearch: false
|
||||
showSearch: false,
|
||||
searchPlaceholder: 'Search',
|
||||
notFoundContent: 'Not Found',
|
||||
showCheckbox: true,
|
||||
draggable: false
|
||||
};
|
||||
|
||||
var propTypes = {
|
||||
|
@ -68,7 +76,9 @@ var propTypes = {
|
|||
body: _propTypes2["default"].func,
|
||||
footer: _propTypes2["default"].func,
|
||||
rowKey: _propTypes2["default"].func,
|
||||
lazy: _propTypes2["default"].object
|
||||
lazy: _propTypes2["default"].object,
|
||||
showCheckbox: _propTypes2["default"].bool,
|
||||
draggable: _propTypes2["default"].bool
|
||||
};
|
||||
|
||||
var defaultTitles = ['', ''];
|
||||
|
@ -96,17 +106,24 @@ var Transfer = function (_React$Component) {
|
|||
}),
|
||||
targetSelectedKeys: selectedKeys.filter(function (key) {
|
||||
return targetKeys.indexOf(key) > -1;
|
||||
})
|
||||
}),
|
||||
leftDataSource: [],
|
||||
rightDataSource: []
|
||||
};
|
||||
_this.cacheTargetKeys = [].concat(_toConsumableArray(targetKeys));
|
||||
return _this;
|
||||
}
|
||||
|
||||
Transfer.prototype.componentDidMount = function componentDidMount() {
|
||||
this.splitDataSource();
|
||||
};
|
||||
|
||||
Transfer.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
|
||||
var _state = this.state,
|
||||
sourceSelectedKeys = _state.sourceSelectedKeys,
|
||||
targetSelectedKeys = _state.targetSelectedKeys;
|
||||
|
||||
if (nextProps.targetKeys !== this.props.targetKeys || nextProps.dataSource !== this.props.dataSource) {
|
||||
if (nextProps.targetKeys !== this.props.targetKeys || nextProps.dataSource !== this.props.dataSource || nextProps.targetKeys !== this.cacheTargetKeys) {
|
||||
var existInDateSourcekey = function existInDateSourcekey(key) {
|
||||
return dataSource.filter(function (item) {
|
||||
return item.key === key;
|
||||
|
@ -134,6 +151,7 @@ var Transfer = function (_React$Component) {
|
|||
}).length > 0;
|
||||
})
|
||||
});
|
||||
this.splitDataSource(targetKeys, dataSource);
|
||||
}
|
||||
if (nextProps.selectedKeys) {
|
||||
var _targetKeys = nextProps.targetKeys;
|
||||
|
@ -147,18 +165,24 @@ var Transfer = function (_React$Component) {
|
|||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 从dataSource中分离出leftDataSource和rightDataSource
|
||||
* @param {*} newTargetKeys 更新后的targetKeys
|
||||
* @param {*} newDataSource 异步加载数据源时,从nextProps中获取的dataSource
|
||||
*/
|
||||
|
||||
Transfer.prototype.splitDataSource = function splitDataSource() {
|
||||
|
||||
Transfer.prototype.splitDataSource = function splitDataSource(newTargetKeys, newDataSource) {
|
||||
// targetKeys:展示在右边列表的数据集
|
||||
if (this.splitedDataSource) {
|
||||
return this.splitedDataSource;
|
||||
}
|
||||
|
||||
var _props = this.props,
|
||||
rowKey = _props.rowKey,
|
||||
dataSource = _props.dataSource,
|
||||
_props$targetKeys2 = _props.targetKeys,
|
||||
targetKeys = _props$targetKeys2 === undefined ? [] : _props$targetKeys2;
|
||||
var rowKey = this.props.rowKey;
|
||||
|
||||
var targetKeys = newTargetKeys || this.props.targetKeys;
|
||||
//异步加载数据源时
|
||||
var dataSource = newDataSource || this.props.dataSource;
|
||||
if (rowKey) {
|
||||
dataSource.forEach(function (record) {
|
||||
record.key = rowKey(record);
|
||||
|
@ -169,25 +193,30 @@ var Transfer = function (_React$Component) {
|
|||
var key = _ref.key;
|
||||
return targetKeys.indexOf(key) === -1;
|
||||
});
|
||||
var rightDataSource = [];
|
||||
targetKeys.forEach(function (targetKey) {
|
||||
var targetItem = dataSource.filter(function (record) {
|
||||
return record.key === targetKey;
|
||||
})[0];
|
||||
if (targetItem) {
|
||||
rightDataSource.push(targetItem);
|
||||
}
|
||||
var rightDataSource = dataSource.filter(function (_ref2) {
|
||||
var key = _ref2.key;
|
||||
return targetKeys.indexOf(key) > -1;
|
||||
});
|
||||
|
||||
this.splitedDataSource = {
|
||||
leftDataSource: leftDataSource,
|
||||
rightDataSource: rightDataSource
|
||||
};
|
||||
this.setState({
|
||||
leftDataSource: leftDataSource,
|
||||
rightDataSource: rightDataSource
|
||||
});
|
||||
|
||||
return this.splitedDataSource;
|
||||
};
|
||||
|
||||
/**
|
||||
* List中的item选中/未选中状态改变时触发
|
||||
* @param {*} direction 'left' or 'right'
|
||||
* @param {*} holder 更新后的'sourceSelectedKeys' or 'targetSelectedKeys'
|
||||
*/
|
||||
Transfer.prototype.handleSelectChange = function handleSelectChange(direction, holder) {
|
||||
// onSelectChange:当选中的item发生改变时的回调 参数(sourceSelectedKeys, targetSelectedKeys)
|
||||
var _state2 = this.state,
|
||||
sourceSelectedKeys = _state2.sourceSelectedKeys,
|
||||
targetSelectedKeys = _state2.targetSelectedKeys;
|
||||
|
@ -204,36 +233,76 @@ var Transfer = function (_React$Component) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 左侧列表全选事件
|
||||
* @param filteredDataSource dataSource中刨去设置为disabled的部分
|
||||
* @param checkAll 是否是全选状态 true:全选
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 搜索框值更改事件
|
||||
* @param direction 'left' or 'right'
|
||||
* @param value 输入的值
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 清空搜索框内容
|
||||
* @param direction 'left' or 'right'
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 点击list item,选中或取消选中
|
||||
* @param direction 'left' or 'right'
|
||||
* @param selectedItem 选中的item的信息,和dataSource数据源中的item信息一致
|
||||
* @param checked 是否已勾选,true:已勾选 false:未勾选
|
||||
*/
|
||||
|
||||
|
||||
Transfer.prototype.getSelectedKeysName = function getSelectedKeysName(direction) {
|
||||
return direction === 'left' ? 'sourceSelectedKeys' : 'targetSelectedKeys';
|
||||
};
|
||||
|
||||
/**
|
||||
* 拖拽结束时触发
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 拖拽开始时触发
|
||||
*/
|
||||
|
||||
|
||||
Transfer.prototype.render = function render() {
|
||||
var _props2 = this.props,
|
||||
_props2$prefixCls = _props2.prefixCls,
|
||||
prefixCls = _props2$prefixCls === undefined ? 'u-transfer' : _props2$prefixCls,
|
||||
_props2$operations = _props2.operations,
|
||||
operations = _props2$operations === undefined ? [] : _props2$operations,
|
||||
showSearch = _props2.showSearch,
|
||||
notFoundContent = _props2.notFoundContent,
|
||||
searchPlaceholder = _props2.searchPlaceholder,
|
||||
body = _props2.body,
|
||||
footer = _props2.footer,
|
||||
listStyle = _props2.listStyle,
|
||||
_props2$className = _props2.className,
|
||||
className = _props2$className === undefined ? '' : _props2$className,
|
||||
filterOption = _props2.filterOption,
|
||||
render = _props2.render,
|
||||
lazy = _props2.lazy;
|
||||
var _props = this.props,
|
||||
_props$prefixCls = _props.prefixCls,
|
||||
prefixCls = _props$prefixCls === undefined ? 'u-transfer' : _props$prefixCls,
|
||||
_props$operations = _props.operations,
|
||||
operations = _props$operations === undefined ? [] : _props$operations,
|
||||
showSearch = _props.showSearch,
|
||||
notFoundContent = _props.notFoundContent,
|
||||
searchPlaceholder = _props.searchPlaceholder,
|
||||
body = _props.body,
|
||||
footer = _props.footer,
|
||||
listStyle = _props.listStyle,
|
||||
_props$className = _props.className,
|
||||
className = _props$className === undefined ? '' : _props$className,
|
||||
filterOption = _props.filterOption,
|
||||
render = _props.render,
|
||||
lazy = _props.lazy,
|
||||
showCheckbox = _props.showCheckbox,
|
||||
draggable = _props.draggable;
|
||||
var _state3 = this.state,
|
||||
leftFilter = _state3.leftFilter,
|
||||
rightFilter = _state3.rightFilter,
|
||||
sourceSelectedKeys = _state3.sourceSelectedKeys,
|
||||
targetSelectedKeys = _state3.targetSelectedKeys;
|
||||
targetSelectedKeys = _state3.targetSelectedKeys,
|
||||
leftDataSource = _state3.leftDataSource,
|
||||
rightDataSource = _state3.rightDataSource;
|
||||
|
||||
var _splitDataSource = this.splitDataSource(this.props),
|
||||
leftDataSource = _splitDataSource.leftDataSource,
|
||||
rightDataSource = _splitDataSource.rightDataSource;
|
||||
// const { leftDataSource, rightDataSource } = this.splitDataSource(this.props);
|
||||
|
||||
var leftActive = targetSelectedKeys.length > 0;
|
||||
var rightActive = sourceSelectedKeys.length > 0;
|
||||
|
@ -244,55 +313,65 @@ var Transfer = function (_React$Component) {
|
|||
return _react2["default"].createElement(
|
||||
'div',
|
||||
{ className: cls },
|
||||
_react2["default"].createElement(_list2["default"], {
|
||||
titleText: titles[0],
|
||||
dataSource: leftDataSource,
|
||||
filter: leftFilter,
|
||||
filterOption: filterOption,
|
||||
style: listStyle,
|
||||
checkedKeys: sourceSelectedKeys,
|
||||
handleFilter: this.handleLeftFilter,
|
||||
handleClear: this.handleLeftClear,
|
||||
handleSelect: this.handleLeftSelect,
|
||||
handleSelectAll: this.handleLeftSelectAll,
|
||||
render: render,
|
||||
showSearch: showSearch,
|
||||
searchPlaceholder: searchPlaceholder,
|
||||
notFoundContent: notFoundContent,
|
||||
body: body,
|
||||
footer: footer,
|
||||
prefixCls: prefixCls + '-list',
|
||||
lazy: lazy
|
||||
}),
|
||||
_react2["default"].createElement(_operation2["default"], {
|
||||
rightActive: rightActive,
|
||||
rightArrowText: operations[0],
|
||||
moveToRight: this.moveToRight,
|
||||
leftActive: leftActive,
|
||||
leftArrowText: operations[1],
|
||||
moveToLeft: this.moveToLeft,
|
||||
className: prefixCls + '-operation'
|
||||
}),
|
||||
_react2["default"].createElement(_list2["default"], {
|
||||
titleText: titles[1],
|
||||
dataSource: rightDataSource,
|
||||
filter: rightFilter,
|
||||
filterOption: filterOption,
|
||||
style: listStyle,
|
||||
checkedKeys: targetSelectedKeys,
|
||||
handleFilter: this.handleRightFilter,
|
||||
handleClear: this.handleRightClear,
|
||||
handleSelect: this.handleRightSelect,
|
||||
handleSelectAll: this.handleRightSelectAll,
|
||||
render: render,
|
||||
showSearch: showSearch,
|
||||
searchPlaceholder: searchPlaceholder,
|
||||
notFoundContent: notFoundContent,
|
||||
body: body,
|
||||
footer: footer,
|
||||
prefixCls: prefixCls + '-list',
|
||||
lazy: lazy
|
||||
})
|
||||
_react2["default"].createElement(
|
||||
_reactBeautifulDnd.DragDropContext,
|
||||
{ onDragEnd: this.onDragEnd, onDragStart: this.onDragStart },
|
||||
_react2["default"].createElement(_list2["default"], {
|
||||
titleText: titles[0] //左侧标题
|
||||
, dataSource: leftDataSource //左侧数据源
|
||||
, filter: leftFilter //搜索框中输入的内容
|
||||
, filterOption: filterOption //搜索过滤方法 参数(inputValue, option)
|
||||
, style: listStyle //自定义的columns的样式表
|
||||
, checkedKeys: sourceSelectedKeys //左侧已勾选的item的keys
|
||||
, handleFilter: this.handleLeftFilter //左侧搜索框值更改事件
|
||||
, handleClear: this.handleLeftClear //清空左侧搜索框内容
|
||||
, handleSelect: this.handleLeftSelect //点击左侧列表中的item,改变选中或取消选中状态
|
||||
, handleSelectAll: this.handleLeftSelectAll //点击左侧全选
|
||||
, render: render,
|
||||
showSearch: showSearch //是否显示搜索框
|
||||
, searchPlaceholder: searchPlaceholder //搜索框placeholder
|
||||
, notFoundContent: notFoundContent //当没有相关内容的显示内容
|
||||
, body: body,
|
||||
footer: footer,
|
||||
prefixCls: prefixCls + '-list',
|
||||
lazy: lazy,
|
||||
showCheckbox: showCheckbox,
|
||||
draggable: draggable,
|
||||
id: '1'
|
||||
}),
|
||||
!draggable ? _react2["default"].createElement(_operation2["default"], {
|
||||
rightActive: rightActive,
|
||||
rightArrowText: operations[0],
|
||||
moveToRight: this.moveToRight,
|
||||
leftActive: leftActive,
|
||||
leftArrowText: operations[1],
|
||||
moveToLeft: this.moveToLeft,
|
||||
className: prefixCls + '-operation'
|
||||
}) : '',
|
||||
_react2["default"].createElement(_list2["default"], {
|
||||
titleText: titles[1] //右侧标题
|
||||
, dataSource: rightDataSource //右侧数据源
|
||||
, filter: rightFilter //搜索框中输入的内容
|
||||
, filterOption: filterOption //搜索过滤方法 参数(inputValue, option)
|
||||
, style: listStyle //自定义的columns的样式表
|
||||
, checkedKeys: targetSelectedKeys //右侧已勾选的item的keys
|
||||
, handleFilter: this.handleRightFilter //右侧搜索框值更改事件
|
||||
, handleClear: this.handleRightClear //清空右侧搜索框内容
|
||||
, handleSelect: this.handleRightSelect //点击右侧列表中的item,改变选中或取消选中状态
|
||||
, handleSelectAll: this.handleRightSelectAll //点击右侧全选
|
||||
, render: render,
|
||||
showSearch: showSearch //是否显示搜索框
|
||||
, searchPlaceholder: searchPlaceholder //搜索框placeholder
|
||||
, notFoundContent: notFoundContent //当没有相关内容的显示内容
|
||||
, body: body,
|
||||
footer: footer,
|
||||
prefixCls: prefixCls + '-list',
|
||||
lazy: lazy,
|
||||
showCheckbox: showCheckbox,
|
||||
draggable: draggable,
|
||||
id: '2'
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -303,10 +382,12 @@ var _initialiseProps = function _initialiseProps() {
|
|||
var _this2 = this;
|
||||
|
||||
this.moveTo = function (direction) {
|
||||
var _props3 = _this2.props,
|
||||
_props3$targetKeys = _props3.targetKeys,
|
||||
targetKeys = _props3$targetKeys === undefined ? [] : _props3$targetKeys,
|
||||
onChange = _props3.onChange;
|
||||
var _props2 = _this2.props,
|
||||
_props2$targetKeys = _props2.targetKeys,
|
||||
targetKeys = _props2$targetKeys === undefined ? [] : _props2$targetKeys,
|
||||
onChange = _props2.onChange;
|
||||
// debugger
|
||||
|
||||
var _state4 = _this2.state,
|
||||
sourceSelectedKeys = _state4.sourceSelectedKeys,
|
||||
targetSelectedKeys = _state4.targetSelectedKeys;
|
||||
|
@ -320,11 +401,13 @@ var _initialiseProps = function _initialiseProps() {
|
|||
// empty checked keys
|
||||
var oppositeDirection = direction === 'right' ? 'left' : 'right';
|
||||
_this2.setState(_defineProperty({}, _this2.getSelectedKeysName(oppositeDirection), []));
|
||||
// debugger
|
||||
_this2.handleSelectChange(oppositeDirection, []);
|
||||
|
||||
if (onChange) {
|
||||
onChange(newTargetKeys, direction, moveKeys);
|
||||
}
|
||||
_this2.splitDataSource(newTargetKeys);
|
||||
};
|
||||
|
||||
this.moveToLeft = function () {
|
||||
|
@ -347,23 +430,23 @@ var _initialiseProps = function _initialiseProps() {
|
|||
};
|
||||
|
||||
this.handleLeftSelectAll = function (filteredDataSource, checkAll) {
|
||||
return _this2.handleSelectAll('left', filteredDataSource, checkAll);
|
||||
_this2.handleSelectAll('left', filteredDataSource, checkAll);
|
||||
};
|
||||
|
||||
this.handleRightSelectAll = function (filteredDataSource, checkAll) {
|
||||
return _this2.handleSelectAll('right', filteredDataSource, checkAll);
|
||||
};
|
||||
|
||||
this.handleFilter = function (direction, e) {
|
||||
_this2.setState(_defineProperty({}, direction + 'Filter', e));
|
||||
this.handleFilter = function (direction, value) {
|
||||
_this2.setState(_defineProperty({}, direction + 'Filter', value));
|
||||
};
|
||||
|
||||
this.handleLeftFilter = function (e) {
|
||||
return _this2.handleFilter('left', e);
|
||||
this.handleLeftFilter = function (v) {
|
||||
return _this2.handleFilter('left', value);
|
||||
};
|
||||
|
||||
this.handleRightFilter = function (e) {
|
||||
return _this2.handleFilter('right', e);
|
||||
this.handleRightFilter = function (v) {
|
||||
return _this2.handleFilter('right', value);
|
||||
};
|
||||
|
||||
this.handleClear = function (direction) {
|
||||
|
@ -385,10 +468,11 @@ var _initialiseProps = function _initialiseProps() {
|
|||
|
||||
var holder = direction === 'left' ? [].concat(_toConsumableArray(sourceSelectedKeys)) : [].concat(_toConsumableArray(targetSelectedKeys));
|
||||
var index = holder.indexOf(selectedItem.key);
|
||||
if (index > -1) {
|
||||
holder.splice(index, 1);
|
||||
}
|
||||
if (checked) {
|
||||
//已勾选
|
||||
holder.splice(index, 1);
|
||||
} else if (index === -1) {
|
||||
//未勾选
|
||||
holder.push(selectedItem.key);
|
||||
}
|
||||
_this2.handleSelectChange(direction, holder);
|
||||
|
@ -415,6 +499,81 @@ var _initialiseProps = function _initialiseProps() {
|
|||
}
|
||||
return defaultTitles;
|
||||
};
|
||||
|
||||
this.id2List = {
|
||||
droppable_1: 'leftDataSource',
|
||||
droppable_2: 'rightDataSource'
|
||||
};
|
||||
|
||||
this.getList = function (id) {
|
||||
return _this2.state[_this2.id2List[id]];
|
||||
};
|
||||
|
||||
this.onDragEnd = function (result) {
|
||||
var source = result.source,
|
||||
destination = result.destination,
|
||||
draggableId = result.draggableId;
|
||||
var _props3 = _this2.props,
|
||||
targetKeys = _props3.targetKeys,
|
||||
onChange = _props3.onChange;
|
||||
|
||||
var sourceIndex = source.index; //初始位置
|
||||
var disIndex = destination.index; //移动后的位置
|
||||
var temp = void 0; //拖拽的元素
|
||||
|
||||
// dropped outside the list
|
||||
if (!destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从右往左拖拽 或 在左侧列表中拖拽
|
||||
if (destination.droppableId === 'droppable_1') {
|
||||
if (source.droppableId === destination.droppableId) return;
|
||||
_this2.moveToLeft();
|
||||
return;
|
||||
}
|
||||
|
||||
// 在右侧列表中上下拖拽进行排序
|
||||
if (source.droppableId === destination.droppableId) {
|
||||
var items = (0, _utils.reorder)(_this2.getList(source.droppableId), targetKeys, source.index, destination.index);
|
||||
var state = { leftDataSource: items.dataArr };
|
||||
if (source.droppableId === 'droppable_2') {
|
||||
state = { rightDataSource: items.dataArr };
|
||||
}
|
||||
state.sourceSelectedKeys = [];
|
||||
state.targetSelectedKeys = [];
|
||||
_this2.setState(state);
|
||||
if (onChange) {
|
||||
onChange(items.targetKeyArr, "", draggableId);
|
||||
}
|
||||
} else {
|
||||
// 从左往右拖拽
|
||||
var _result = (0, _utils.move)(_this2.getList(source.droppableId), _this2.getList(destination.droppableId), source, destination, targetKeys);
|
||||
if (onChange) {
|
||||
onChange(_result.newTargetKeys, "", draggableId);
|
||||
}
|
||||
_this2.setState({
|
||||
leftDataSource: _result.droppable_1,
|
||||
rightDataSource: _result.droppable_2,
|
||||
sourceSelectedKeys: [],
|
||||
targetSelectedKeys: []
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.onDragStart = function (result) {
|
||||
var selectedItem = {};
|
||||
var source = result.source;
|
||||
|
||||
selectedItem.key = result.draggableId;
|
||||
if (source.droppableId === 'droppable_1') {
|
||||
// leftMenu
|
||||
_this2.handleLeftSelect(selectedItem);
|
||||
} else if (source.droppableId === 'droppable_2') {
|
||||
// rightMenu
|
||||
_this2.handleRightSelect(selectedItem);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Transfer.List = Transfer.List;
|
||||
|
|
|
@ -48,34 +48,26 @@ var Item = function (_React$Component) {
|
|||
_inherits(Item, _React$Component);
|
||||
|
||||
function Item() {
|
||||
var _temp, _this, _ret;
|
||||
|
||||
_classCallCheck(this, Item);
|
||||
|
||||
return _possibleConstructorReturn(this, _React$Component.apply(this, arguments));
|
||||
}
|
||||
|
||||
Item.prototype.shouldComponentUpdate = function shouldComponentUpdate() {
|
||||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.matchFilter = function (text) {
|
||||
var _this$props = _this.props,
|
||||
filter = _this$props.filter,
|
||||
filterOption = _this$props.filterOption,
|
||||
item = _this$props.item;
|
||||
|
||||
if (filterOption) {
|
||||
return filterOption(filter, item);
|
||||
}
|
||||
return text.indexOf(filter) >= 0;
|
||||
}, _temp), _possibleConstructorReturn(_this, _ret);
|
||||
}
|
||||
|
||||
Item.prototype.shouldComponentUpdate = function shouldComponentUpdate() {
|
||||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||
args[_key2] = arguments[_key2];
|
||||
}
|
||||
|
||||
return _PureRenderMixin2["default"].shouldComponentUpdate.apply(this, args);
|
||||
};
|
||||
// matchFilter = (text) => {
|
||||
// const { filter, filterOption, item } = this.props;
|
||||
// if (filterOption) {
|
||||
// return filterOption(filter, item);
|
||||
// }
|
||||
// return text.indexOf(filter) >= 0;
|
||||
// }
|
||||
|
||||
|
||||
Item.prototype.render = function render() {
|
||||
var _classNames;
|
||||
|
@ -89,9 +81,10 @@ var Item = function (_React$Component) {
|
|||
prefixCls = _props.prefixCls,
|
||||
onClick = _props.onClick,
|
||||
renderedText = _props.renderedText,
|
||||
renderedEl = _props.renderedEl;
|
||||
renderedEl = _props.renderedEl,
|
||||
showCheckbox = _props.showCheckbox;
|
||||
|
||||
var className = (0, _classnames2["default"])((_classNames = {}, _defineProperty(_classNames, prefixCls + '-content-item', true), _defineProperty(_classNames, prefixCls + '-content-item-disabled', item.disabled), _classNames));
|
||||
var className = (0, _classnames2["default"])((_classNames = {}, _defineProperty(_classNames, prefixCls + '-content-item', true), _defineProperty(_classNames, prefixCls + '-content-item-disabled', item.disabled), _defineProperty(_classNames, prefixCls + '-content-item-selected', checked), _classNames));
|
||||
|
||||
var lazyProps = (0, _objectAssign2["default"])({
|
||||
height: 32,
|
||||
|
@ -137,9 +130,9 @@ var Item = function (_React$Component) {
|
|||
return onClick(item);
|
||||
}
|
||||
},
|
||||
_react2["default"].createElement(_beeCheckbox2["default"], { checked: checked, disabled: item.disabled, onClick: item.disabled ? undefined : function () {
|
||||
showCheckbox ? _react2["default"].createElement(_beeCheckbox2["default"], { checked: checked, disabled: item.disabled, onClick: item.disabled ? undefined : function () {
|
||||
return onClick(item);
|
||||
} }),
|
||||
} }) : '',
|
||||
_react2["default"].createElement(
|
||||
'span',
|
||||
null,
|
||||
|
|
164
build/list.js
164
build/list.js
|
@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|||
value: true
|
||||
});
|
||||
|
||||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||||
|
||||
var _react = require('react');
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
@ -38,6 +40,14 @@ var _beeCheckbox = require('bee-checkbox');
|
|||
|
||||
var _beeCheckbox2 = _interopRequireDefault(_beeCheckbox);
|
||||
|
||||
var _beeIcon = require('bee-icon');
|
||||
|
||||
var _beeIcon2 = _interopRequireDefault(_beeIcon);
|
||||
|
||||
var _reactBeautifulDnd = require('react-beautiful-dnd');
|
||||
|
||||
var _tinperBeeCore = require('tinper-bee-core');
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }
|
||||
|
@ -71,6 +81,8 @@ var TransferList = function (_React$Component) {
|
|||
var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
|
||||
|
||||
_this.matchFilter = function (text, item) {
|
||||
//filter:搜索框中的内容
|
||||
//filterOption:用户自定义的搜索过滤方法
|
||||
var _this$props = _this.props,
|
||||
filter = _this$props.filter,
|
||||
filterOption = _this$props.filterOption;
|
||||
|
@ -82,12 +94,14 @@ var TransferList = function (_React$Component) {
|
|||
};
|
||||
|
||||
_this.handleSelect = function (selectedItem) {
|
||||
// checkedKeys:已勾选的Keys数组
|
||||
// result:是否已勾选,true:已勾选 false:未勾选
|
||||
var checkedKeys = _this.props.checkedKeys;
|
||||
|
||||
var result = checkedKeys.some(function (key) {
|
||||
return key === selectedItem.key;
|
||||
});
|
||||
_this.props.handleSelect(selectedItem, !result);
|
||||
_this.props.handleSelect(selectedItem, result);
|
||||
};
|
||||
|
||||
_this.handleFilter = function (e) {
|
||||
|
@ -110,6 +124,29 @@ var TransferList = function (_React$Component) {
|
|||
};
|
||||
};
|
||||
|
||||
_this.onKeyDown = function (event, provided, snapshot, item) {
|
||||
if (provided.dragHandleProps) {
|
||||
provided.dragHandleProps.onKeyDown(event);
|
||||
}
|
||||
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (snapshot.isDragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.keyCode !== _tinperBeeCore.KeyCode.ENTER) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 为了选择,我们使用此事件 we are using the event for selection
|
||||
event.preventDefault();
|
||||
|
||||
_this.performAction(event, item);
|
||||
};
|
||||
|
||||
_this.state = {
|
||||
mounted: false
|
||||
};
|
||||
|
@ -138,19 +175,29 @@ var TransferList = function (_React$Component) {
|
|||
return _PureRenderMixin2["default"].shouldComponentUpdate.apply(this, args);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取Checkbox状态
|
||||
* @param {*} filteredDataSource dataSource中刨去设置为disabled的部分
|
||||
*/
|
||||
TransferList.prototype.getCheckStatus = function getCheckStatus(filteredDataSource) {
|
||||
var checkedKeys = this.props.checkedKeys;
|
||||
|
||||
if (checkedKeys.length === 0) {
|
||||
return 'none';
|
||||
return 'none'; //全部未选
|
||||
} else if (filteredDataSource.every(function (item) {
|
||||
return checkedKeys.indexOf(item.key) >= 0;
|
||||
})) {
|
||||
return 'all';
|
||||
return 'all'; //全部已选
|
||||
}
|
||||
return 'part';
|
||||
return 'part'; //部分已选
|
||||
};
|
||||
|
||||
/**
|
||||
* 点击list item,选中或取消选中
|
||||
* @param selectedItem 选中的item的信息,和dataSource数据源中的item信息一致
|
||||
*/
|
||||
|
||||
|
||||
TransferList.prototype.renderCheckbox = function renderCheckbox(_ref) {
|
||||
var _classNames,
|
||||
_this3 = this;
|
||||
|
@ -162,7 +209,7 @@ var TransferList = function (_React$Component) {
|
|||
disabled = _ref.disabled,
|
||||
checkable = _ref.checkable;
|
||||
|
||||
var checkAll = !checkPart && checked;
|
||||
var checkAll = !checkPart && checked; //非半选 && 全选
|
||||
prefixCls = "u";
|
||||
var checkboxCls = (0, _classnames2["default"])((_classNames = {}, _defineProperty(_classNames, prefixCls + '-checkbox-indeterminate', checkPart), _defineProperty(_classNames, prefixCls + '-checkbox-disabled', disabled), _classNames));
|
||||
return _react2["default"].createElement(
|
||||
|
@ -181,7 +228,8 @@ var TransferList = function (_React$Component) {
|
|||
};
|
||||
|
||||
TransferList.prototype.render = function render() {
|
||||
var _this4 = this;
|
||||
var _classNames2,
|
||||
_this4 = this;
|
||||
|
||||
var _props = this.props,
|
||||
prefixCls = _props.prefixCls,
|
||||
|
@ -198,7 +246,10 @@ var TransferList = function (_React$Component) {
|
|||
showSearch = _props.showSearch,
|
||||
_props$render = _props.render,
|
||||
render = _props$render === undefined ? noop : _props$render,
|
||||
style = _props.style;
|
||||
style = _props.style,
|
||||
id = _props.id,
|
||||
showCheckbox = _props.showCheckbox,
|
||||
draggable = _props.draggable;
|
||||
var _props2 = this.props,
|
||||
searchPlaceholder = _props2.searchPlaceholder,
|
||||
notFoundContent = _props2.notFoundContent;
|
||||
|
@ -208,11 +259,11 @@ var TransferList = function (_React$Component) {
|
|||
var footerDom = footer((0, _objectAssign2["default"])({}, this.props));
|
||||
var bodyDom = body((0, _objectAssign2["default"])({}, this.props));
|
||||
|
||||
var listCls = (0, _classnames2["default"])(prefixCls, _defineProperty({}, prefixCls + '-with-footer', !!footerDom));
|
||||
var listCls = (0, _classnames2["default"])(prefixCls, (_classNames2 = {}, _defineProperty(_classNames2, prefixCls + '-with-footer', !!footerDom), _defineProperty(_classNames2, prefixCls + '-draggable', !!draggable), _classNames2));
|
||||
|
||||
var filteredDataSource = [];
|
||||
var totalDataSource = [];
|
||||
var showItems = dataSource.map(function (item) {
|
||||
var showItems = dataSource.map(function (item, index) {
|
||||
var _renderItem = _this4.renderItem(item),
|
||||
renderedText = _renderItem.renderedText,
|
||||
renderedEl = _renderItem.renderedEl;
|
||||
|
@ -229,19 +280,41 @@ var TransferList = function (_React$Component) {
|
|||
}
|
||||
|
||||
var checked = checkedKeys.indexOf(item.key) >= 0;
|
||||
return _react2["default"].createElement(_item2["default"], {
|
||||
key: item.key,
|
||||
item: item,
|
||||
lazy: lazy,
|
||||
render: render,
|
||||
renderedText: renderedText,
|
||||
renderedEl: renderedEl,
|
||||
filter: filter,
|
||||
filterOption: filterOption,
|
||||
checked: checked,
|
||||
prefixCls: prefixCls,
|
||||
onClick: _this4.handleSelect
|
||||
});
|
||||
return _react2["default"].createElement(
|
||||
_reactBeautifulDnd.Draggable,
|
||||
{ key: item.key, index: index, draggableId: '' + item.key, isDragDisabled: draggable ? item.disabled : !draggable },
|
||||
function (provided, snapshot) {
|
||||
return _react2["default"].createElement(
|
||||
'div',
|
||||
_extends({
|
||||
ref: provided.innerRef
|
||||
}, provided.draggableProps, provided.dragHandleProps, {
|
||||
// onClick={(event) =>this.handleDrag(event, provided, snapshot, item)}
|
||||
onKeyDown: function onKeyDown(event) {
|
||||
return _this4.onKeyDown(event, provided, snapshot, item);
|
||||
}
|
||||
// className={classnames({
|
||||
// ...getClass(this.props,snapshot.isDragging).drag
|
||||
// })}
|
||||
, style: _extends({}, provided.draggableProps.style) }),
|
||||
_react2["default"].createElement(_item2["default"]
|
||||
// ref={provided.innerRef} //Error: provided.innerRef has not been provided with a HTMLElement
|
||||
// key={item.key}
|
||||
, { item: item,
|
||||
lazy: lazy,
|
||||
render: render,
|
||||
renderedText: renderedText,
|
||||
renderedEl: renderedEl,
|
||||
filter: filter,
|
||||
filterOption: filterOption,
|
||||
checked: checked,
|
||||
prefixCls: prefixCls,
|
||||
onClick: _this4.handleSelect,
|
||||
showCheckbox: showCheckbox
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
var unit = '';
|
||||
|
@ -262,7 +335,7 @@ var TransferList = function (_React$Component) {
|
|||
prefixCls: prefixCls + '-search',
|
||||
onChange: this.handleFilter,
|
||||
handleClear: this.handleClear,
|
||||
placeholder: searchPlaceholder || 'Search',
|
||||
placeholder: searchPlaceholder,
|
||||
value: filter
|
||||
})
|
||||
) : null;
|
||||
|
@ -272,19 +345,42 @@ var TransferList = function (_React$Component) {
|
|||
{ className: showSearch ? prefixCls + '-body ' + prefixCls + '-body-with-search' : prefixCls + '-body' },
|
||||
search,
|
||||
_react2["default"].createElement(
|
||||
_beeAnimate2["default"],
|
||||
{
|
||||
component: 'ul',
|
||||
className: prefixCls + '-content',
|
||||
transitionName: this.state.mounted ? prefixCls + '-content-item-highlight' : '',
|
||||
transitionLeave: false
|
||||
},
|
||||
showItems
|
||||
_reactBeautifulDnd.Droppable,
|
||||
{ droppableId: 'droppable_' + id, direction: 'vertical', isDropDisabled: !draggable },
|
||||
function (provided, snapshot) {
|
||||
return _react2["default"].createElement(
|
||||
'div',
|
||||
{ ref: provided.innerRef, isDraggingOver: snapshot.isDraggingOver, key: id, className: prefixCls + '-content' },
|
||||
_react2["default"].createElement(
|
||||
_beeAnimate2["default"],
|
||||
{
|
||||
component: 'ul',
|
||||
transitionName: _this4.state.mounted ? prefixCls + '-content-item-highlight' : '',
|
||||
transitionLeave: false
|
||||
},
|
||||
showItems
|
||||
),
|
||||
_react2["default"].createElement(
|
||||
'div',
|
||||
{ className: prefixCls + '-delete-selected ' + (snapshot.isDraggingOver && id === '1' ? 'show' : '') },
|
||||
_react2["default"].createElement(
|
||||
'div',
|
||||
{ className: prefixCls + '-del-btn' },
|
||||
_react2["default"].createElement(_beeIcon2["default"], { type: 'uf-arrow-down-2' }),
|
||||
_react2["default"].createElement(
|
||||
'span',
|
||||
null,
|
||||
'\u79FB\u9664\u5DF2\u9009'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
),
|
||||
_react2["default"].createElement(
|
||||
'div',
|
||||
{ className: prefixCls + '-body-not-found' },
|
||||
notFoundContent || 'Not Found'
|
||||
{ className: prefixCls + '-body-not-found ' + (dataSource.length == 0 ? "show" : "") },
|
||||
notFoundContent
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -309,7 +405,7 @@ var TransferList = function (_React$Component) {
|
|||
_react2["default"].createElement(
|
||||
'div',
|
||||
{ className: prefixCls + '-header' },
|
||||
renderedCheckbox,
|
||||
showCheckbox ? renderedCheckbox : '',
|
||||
_react2["default"].createElement(
|
||||
'span',
|
||||
{ className: prefixCls + '-header-selected' },
|
||||
|
|
|
@ -88,6 +88,7 @@ var Search = function (_React$Component) {
|
|||
'div',
|
||||
null,
|
||||
_react2["default"].createElement(_beeFormControl2["default"], {
|
||||
size: 'sm',
|
||||
placeholder: placeholder,
|
||||
className: prefixCls,
|
||||
value: value,
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
||||
|
||||
/**
|
||||
* a little function to help us with reordering the result
|
||||
* @param {*} list
|
||||
* @param {*} targetKeys
|
||||
* @param {*} startIndex
|
||||
* @param {*} endIndex
|
||||
*/
|
||||
var reorder = function reorder(list, targetKeys, startIndex, endIndex) {
|
||||
var result1 = Array.from(list);
|
||||
|
||||
var _result1$splice = result1.splice(startIndex, 1),
|
||||
_result1$splice2 = _slicedToArray(_result1$splice, 1),
|
||||
removed1 = _result1$splice2[0];
|
||||
|
||||
result1.splice(endIndex, 0, removed1);
|
||||
|
||||
var result2 = Array.from(targetKeys);
|
||||
|
||||
var _result2$splice = result2.splice(startIndex, 1),
|
||||
_result2$splice2 = _slicedToArray(_result2$splice, 1),
|
||||
removed2 = _result2$splice2[0];
|
||||
|
||||
result2.splice(endIndex, 0, removed2);
|
||||
|
||||
var result = {};
|
||||
result.dataArr = result1;
|
||||
result.targetKeyArr = result2;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves an item from one list to another list.
|
||||
* @param {*} source
|
||||
* @param {*} destination
|
||||
* @param {*} droppableSource
|
||||
* @param {*} droppableDestination
|
||||
* @param {*} targetKeys
|
||||
*/
|
||||
var move = function move(source, destination, droppableSource, droppableDestination, targetKeys) {
|
||||
var sourceClone = Array.from(source);
|
||||
var destClone = Array.from(destination);
|
||||
|
||||
var _sourceClone$splice = sourceClone.splice(droppableSource.index, 1),
|
||||
_sourceClone$splice2 = _slicedToArray(_sourceClone$splice, 1),
|
||||
removed = _sourceClone$splice2[0];
|
||||
|
||||
destClone.splice(droppableDestination.index, 0, removed);
|
||||
targetKeys.splice(droppableDestination.index, 0, removed.key);
|
||||
|
||||
var result = {};
|
||||
result[droppableSource.droppableId] = sourceClone;
|
||||
result[droppableDestination.droppableId] = destClone;
|
||||
result.newTargetKeys = targetKeys;
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.reorder = reorder;
|
||||
exports.move = move;
|
|
@ -1,75 +1,75 @@
|
|||
/**
|
||||
*
|
||||
* @title 常用可选transfer
|
||||
* @description
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import Transfer from '../../src';
|
||||
|
||||
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: i % 3 < 1,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const targetKeys = mockData
|
||||
.filter(item => +item.key % 3 > 1)
|
||||
.map(item => item.key);
|
||||
|
||||
class Demo1 extends React.Component {
|
||||
state = {
|
||||
targetKeys,
|
||||
selectedKeys: [],
|
||||
showModal: false,
|
||||
modalSize: ''
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
handleScroll = (direction, e) => {
|
||||
console.log('direction:', direction);
|
||||
console.log('target:', e.target);
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const state = this.state;
|
||||
|
||||
return (
|
||||
<Transfer
|
||||
dataSource={mockData}
|
||||
titles={['Source', 'Target']}
|
||||
targetKeys={state.targetKeys}
|
||||
selectedKeys={state.selectedKeys}
|
||||
onChange={this.handleChange}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
onScroll={this.handleScroll}
|
||||
render={item => item.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Demo1
|
||||
/**
|
||||
*
|
||||
* @title 常用可选transfer
|
||||
* @description targetKeys需要通过ES6的扩展运算符进行赋值,实现对象的深拷贝
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import Transfer from '../../src';
|
||||
|
||||
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: i % 3 < 1,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const targetKeys = mockData
|
||||
.filter(item => +item.key % 3 > 1)
|
||||
.map(item => item.key);
|
||||
|
||||
class Demo1 extends React.Component {
|
||||
state = {
|
||||
targetKeys,
|
||||
selectedKeys: [],
|
||||
showModal: false,
|
||||
modalSize: ''
|
||||
}
|
||||
|
||||
handleChange = (nextTargetKeys, direction, moveKeys) => {
|
||||
this.setState({ targetKeys: nextTargetKeys });
|
||||
|
||||
console.log('targetKeys: ', nextTargetKeys);
|
||||
console.log('direction: ', direction);
|
||||
console.log('moveKeys: ', moveKeys);
|
||||
}
|
||||
|
||||
handleSelectChange = (sourceSelectedKeys, targetSelectedKeys) => {
|
||||
this.setState({ selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys] });
|
||||
|
||||
console.log('sourceSelectedKeys: ', sourceSelectedKeys);
|
||||
console.log('targetSelectedKeys: ', targetSelectedKeys);
|
||||
}
|
||||
|
||||
handleScroll = (direction, e) => {
|
||||
console.log('direction:', direction);
|
||||
console.log('target:', e.target);
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const state = this.state;
|
||||
const targetKeys = [...this.state.targetKeys];
|
||||
return (
|
||||
<Transfer
|
||||
dataSource={mockData}
|
||||
titles={['Source', 'Target']}
|
||||
targetKeys={targetKeys}
|
||||
selectedKeys={state.selectedKeys}
|
||||
onChange={this.handleChange}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
onScroll={this.handleScroll}
|
||||
render={item => item.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Demo1
|
||||
|
|
|
@ -1,58 +1,58 @@
|
|||
/**
|
||||
*
|
||||
* @title 带搜索框的tranfer
|
||||
* @description
|
||||
*
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import Transfer from '../../src';
|
||||
|
||||
|
||||
class Demo2 extends React.Component {
|
||||
state = {
|
||||
mockData: [],
|
||||
targetKeys: [],
|
||||
}
|
||||
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,
|
||||
};
|
||||
if (data.chosen) {
|
||||
targetKeys.push(data.key);
|
||||
}
|
||||
mockData.push(data);
|
||||
}
|
||||
this.setState({ mockData, targetKeys });
|
||||
}
|
||||
filterOption = (inputValue, option) => {
|
||||
return option.title.indexOf(inputValue) > -1;
|
||||
}
|
||||
handleChange = (targetKeys) => {
|
||||
this.setState({ targetKeys });
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Transfer
|
||||
dataSource={this.state.mockData}
|
||||
showSearch
|
||||
filterOption={this.filterOption}
|
||||
targetKeys={this.state.targetKeys}
|
||||
onChange={this.handleChange}
|
||||
render={item => item.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Demo2
|
||||
/**
|
||||
*
|
||||
* @title 带搜索框的tranfer
|
||||
* @description
|
||||
*
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import Transfer from '../../src';
|
||||
|
||||
|
||||
class Demo2 extends React.Component {
|
||||
state = {
|
||||
mockData: [],
|
||||
targetKeys: [],
|
||||
}
|
||||
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,
|
||||
};
|
||||
if (data.chosen) {
|
||||
targetKeys.push(data.key);
|
||||
}
|
||||
mockData.push(data);
|
||||
}
|
||||
this.setState({ mockData, targetKeys });
|
||||
}
|
||||
filterOption = (inputValue, option) => {
|
||||
return option.title.indexOf(inputValue) > -1;
|
||||
}
|
||||
handleChange = (targetKeys) => {
|
||||
this.setState({ targetKeys });
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Transfer
|
||||
dataSource={this.state.mockData}
|
||||
showSearch
|
||||
filterOption={this.filterOption}
|
||||
targetKeys={this.state.targetKeys}
|
||||
onChange={this.handleChange}
|
||||
render={item => item.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Demo2
|
||||
|
|
|
@ -1,69 +1,69 @@
|
|||
/**
|
||||
*
|
||||
* @title 底部自定义的transfer
|
||||
* @description
|
||||
*
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import Button from 'bee-button';
|
||||
import Transfer from '../../src';
|
||||
|
||||
class Demo3 extends React.Component {
|
||||
state = {
|
||||
mockData: [],
|
||||
targetKeys: [],
|
||||
}
|
||||
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,
|
||||
};
|
||||
if (data.chosen) {
|
||||
targetKeys.push(data.key);
|
||||
}
|
||||
mockData.push(data);
|
||||
}
|
||||
this.setState({ mockData, targetKeys });
|
||||
}
|
||||
handleChange = (targetKeys) => {
|
||||
this.setState({ targetKeys });
|
||||
}
|
||||
renderFooter = () => {
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
style={{ float: 'right', margin: 5 }}
|
||||
onClick={this.getMock}
|
||||
>
|
||||
reload
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Transfer
|
||||
dataSource={this.state.mockData}
|
||||
showSearch
|
||||
listStyle={{
|
||||
width: 250,
|
||||
height: 300,
|
||||
}}
|
||||
targetKeys={this.state.targetKeys}
|
||||
onChange={this.handleChange}
|
||||
render={item => `${item.title}-${item.description}`}
|
||||
footer={this.renderFooter}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Demo3
|
||||
/**
|
||||
*
|
||||
* @title 底部自定义的transfer
|
||||
* @description
|
||||
*
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import Button from 'bee-button';
|
||||
import Transfer from '../../src';
|
||||
|
||||
class Demo3 extends React.Component {
|
||||
state = {
|
||||
mockData: [],
|
||||
targetKeys: [],
|
||||
}
|
||||
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,
|
||||
};
|
||||
if (data.chosen) {
|
||||
targetKeys.push(data.key);
|
||||
}
|
||||
mockData.push(data);
|
||||
}
|
||||
this.setState({ mockData, targetKeys });
|
||||
}
|
||||
handleChange = (targetKeys) => {
|
||||
this.setState({ targetKeys });
|
||||
}
|
||||
renderFooter = () => {
|
||||
return (
|
||||
<Button
|
||||
size="sm"
|
||||
style={{ float: 'right', margin: 5 }}
|
||||
onClick={this.getMock}
|
||||
>
|
||||
reload
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Transfer
|
||||
dataSource={this.state.mockData}
|
||||
showSearch
|
||||
listStyle={{
|
||||
width: 250,
|
||||
height: 300,
|
||||
}}
|
||||
targetKeys={this.state.targetKeys}
|
||||
onChange={this.handleChange}
|
||||
render={item => `${item.title}-${item.description}`}
|
||||
footer={this.renderFooter}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Demo3
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
*
|
||||
* @title 隐藏复选框
|
||||
* @description 通过`showCheckbox`参数控制复选框显示和隐藏
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import Transfer from '../../src';
|
||||
|
||||
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: i % 3 < 1,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const targetKeys = mockData
|
||||
.filter(item => +item.key % 3 > 1)
|
||||
.map(item => item.key);
|
||||
|
||||
class Demo4 extends React.Component {
|
||||
state = {
|
||||
targetKeys,
|
||||
selectedKeys: [],
|
||||
showModal: false,
|
||||
modalSize: ''
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
handleScroll = (direction, e) => {
|
||||
console.log('direction:', direction);
|
||||
console.log('target:', e.target);
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const state = this.state;
|
||||
|
||||
return (
|
||||
<Transfer
|
||||
dataSource={mockData}
|
||||
showCheckbox={false}
|
||||
titles={['Source', 'Target']}
|
||||
targetKeys={state.targetKeys}
|
||||
selectedKeys={state.selectedKeys}
|
||||
onChange={this.handleChange}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
onScroll={this.handleScroll}
|
||||
render={item => item.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Demo4;
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
*
|
||||
* @title 拖拽穿梭
|
||||
* @description 通过`draggable`参数设置是否可以通过拖拽进行穿梭和排序
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import Transfer from '../../src';
|
||||
|
||||
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: i % 3 < 1,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const targetKeys = mockData
|
||||
.filter(item => +item.key % 3 > 1)
|
||||
.map(item => item.key);
|
||||
|
||||
class Demo5 extends React.Component {
|
||||
state = {
|
||||
targetKeys,
|
||||
selectedKeys: [],
|
||||
showModal: false,
|
||||
modalSize: ''
|
||||
}
|
||||
|
||||
handleChange = (nextTargetKeys, direction, moveKeys) => {
|
||||
this.setState({ targetKeys: nextTargetKeys });
|
||||
|
||||
console.log('targetKeys: ', nextTargetKeys);
|
||||
console.log('direction: ', direction);
|
||||
console.log('moveKeys: ', moveKeys);
|
||||
}
|
||||
|
||||
handleSelectChange = (sourceSelectedKeys, targetSelectedKeys) => {
|
||||
this.setState({ selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys] });
|
||||
|
||||
console.log('sourceSelectedKeys: ', sourceSelectedKeys);
|
||||
console.log('targetSelectedKeys: ', targetSelectedKeys);
|
||||
}
|
||||
|
||||
handleScroll = (direction, e) => {
|
||||
console.log('direction:', direction);
|
||||
console.log('target:', e.target);
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const state = this.state;
|
||||
// targetKeys需要通过数组的扩展运算符进行赋值
|
||||
const targetKeys = [...this.state.targetKeys];
|
||||
return (
|
||||
<Transfer
|
||||
draggable={true}
|
||||
showCheckbox={false}
|
||||
dataSource={mockData}
|
||||
titles={['Source', 'Target']}
|
||||
targetKeys={targetKeys}
|
||||
selectedKeys={state.selectedKeys}
|
||||
onChange={this.handleChange}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
onScroll={this.handleScroll}
|
||||
render={item => item.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Demo5
|
168
demo/index.js
168
demo/index.js
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
102
docs/api.md
102
docs/api.md
|
@ -1,50 +1,52 @@
|
|||
# 穿梭框 Transfer
|
||||
|
||||
两框之间的元素迁移,非常直观且有效。一个或多个元素选择后点击方向按钮转到另一列框中。左栏是“源”,右边是“目标”
|
||||
|
||||
## 何时使用
|
||||
|
||||
需要两框之间的元素迁移时
|
||||
|
||||
## 如何使用
|
||||
|
||||
```
|
||||
import { Transfer } from 'tinper-bee';
|
||||
|
||||
or
|
||||
|
||||
import Transfer from 'bee-transfer';
|
||||
import bee-transfer/build/Transfer.css;
|
||||
|
||||
```
|
||||
|
||||
## 代码演示
|
||||
|
||||
## API
|
||||
|
||||
|参数|说明|类型|默认值|
|
||||
|:--|:---:|:--:|---:|
|
||||
|dataSource|设置数据源。当有targetKey props存在时,dataSource的数据刨去targetKey数据,剩下的都放在左边列表|[]|[]|
|
||||
|render|自定义的展示出来的item,需要展示哪些字段|Function(record)|-|
|
||||
|targetKeys|展示在右边列表的数据集|[]|[]|
|
||||
|selectedKeys|所有选中的item的keys|[]|[]|
|
||||
|onChange|当item在穿梭成功后的回调 参数(targetKeys, direction, moveKeys)|func|-|
|
||||
|onSelectChange| 当选中的item发生改变时的回调 参数(sourceSelectedKeys, targetSelectedKeys)|fun|-|
|
||||
|onScroll| 当滑动可选的item列表的回调 参数(direction, event)|func|-|
|
||||
|listStyle|自定义的columns的样式表|object |-|
|
||||
|className|class|string|''|''|
|
||||
|titles|两columns的title|[]|-|
|
||||
|operations|自定义按钮操作|[]|'>', '<'|
|
||||
|showSearch|是否显示搜索框|boolean |false|
|
||||
|filterOption|搜索过滤方法 参数(inputValue, option)|func或者boolean |-|
|
||||
|searchPlaceholder|搜索框的默认显示文字|string|'Search here'|
|
||||
|notFoundContent|当没有相关内容的显示内容|string或ReactNode| 'The list is empty'|
|
||||
|footer|渲染底部的dom|ReactNode|-|
|
||||
|lazy|懒加载dom|object|当tranfer放在bee-modal里 添加参数 lazy={container:"modal"}|
|
||||
|onSearchChange|当搜索域变化的回调函数 参数(direction: 'left'|'right', event: Event)|func|-|
|
||||
|
||||
## 注意事项
|
||||
|
||||
暂无
|
||||
|
||||
## 更新日志
|
||||
# 穿梭框 Transfer
|
||||
|
||||
两框之间的元素迁移,非常直观且有效。一个或多个元素选择后点击方向按钮转到另一列框中。左栏是“源”,右边是“目标”
|
||||
|
||||
## 何时使用
|
||||
|
||||
需要两框之间的元素迁移时
|
||||
|
||||
## 如何使用
|
||||
|
||||
```
|
||||
import { Transfer } from 'tinper-bee';
|
||||
|
||||
or
|
||||
|
||||
import Transfer from 'bee-transfer';
|
||||
import 'bee-transfer/build/Transfer.css';
|
||||
|
||||
```
|
||||
|
||||
## 代码演示
|
||||
|
||||
## API
|
||||
|
||||
|参数|说明|类型|默认值|
|
||||
|:--|:---:|:--:|---:|
|
||||
|dataSource|设置数据源。当有targetKey props存在时,dataSource的数据刨去targetKey数据,剩下的都放在左边列表|[]|[]|
|
||||
|render|自定义的展示出来的item,需要展示哪些字段|Function(record)|-|
|
||||
|targetKeys|展示在右边列表的数据集|[]|[]|
|
||||
|selectedKeys|所有选中的item的keys|[]|[]|
|
||||
|onChange|当item在穿梭成功后的回调 参数(targetKeys, direction, moveKeys)|func|-|
|
||||
|onSelectChange| 当选中的item发生改变时的回调 参数(sourceSelectedKeys, targetSelectedKeys)|fun|-|
|
||||
|onScroll| 当滑动可选的item列表的回调 参数(direction, event)|func|-|
|
||||
|listStyle|自定义的columns的样式表|object |-|
|
||||
|className|class|string|''|''|
|
||||
|titles|两columns的title|[]|-|
|
||||
|operations|自定义按钮操作|[]|'>', '<'|
|
||||
|showSearch|是否显示搜索框|boolean |false|
|
||||
|filterOption|搜索过滤方法 参数(inputValue, option)|func或者boolean |-|
|
||||
|searchPlaceholder|搜索框的默认显示文字|string|'Search'|
|
||||
|notFoundContent|当没有相关内容的显示内容|string或ReactNode| 'Not Found'|
|
||||
|footer|渲染底部的dom|ReactNode|-|
|
||||
|lazy|懒加载dom|object|当tranfer放在bee-modal里 添加参数 lazy={container:"modal"}|
|
||||
|onSearchChange|当搜索域变化的回调函数 参数(direction: 'left'|'right', event: Event)|func|-|
|
||||
|showCheckbox|是否显示Checkbox复选框|bool|true|
|
||||
|draggable|是否可以通过拖拽进行穿梭和排序|bool|false|
|
||||
|
||||
## 注意事项
|
||||
|
||||
暂无
|
||||
|
||||
## 更新日志
|
||||
|
|
File diff suppressed because it is too large
Load Diff
141
package.json
141
package.json
|
@ -1,70 +1,71 @@
|
|||
{
|
||||
"name": "bee-transfer",
|
||||
"version": "2.0.7",
|
||||
"description": "Transfer ui component for react",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-component",
|
||||
"bee-transfer",
|
||||
"iuap-design",
|
||||
"tinper-bee",
|
||||
"Transfer"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/tinper-bee/bee-transfer.git",
|
||||
"author": "Yonyou FED",
|
||||
"repository": "http://github.com/tinper-bee/bee-transfer",
|
||||
"bugs": "https://github.com/tinper-bee/bee-transfer.git/issues",
|
||||
"license": "MIT",
|
||||
"main": "./build/index.js",
|
||||
"config": {
|
||||
"port": 3000,
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bee-tools run start",
|
||||
"build": "bee-tools run build",
|
||||
"lint": "bee-tools-test run lint",
|
||||
"test": "bee-tools-test run test",
|
||||
"chrome": "bee-tools-test run chrome",
|
||||
"coveralls": "bee-tools-test run coverage",
|
||||
"browsers": "bee-tools-test run browsers",
|
||||
"pub": "bee-tools run pub"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.23.0",
|
||||
"bee-animate": "^1.0.0",
|
||||
"bee-button": "latest",
|
||||
"bee-checkbox": "latest",
|
||||
"bee-form-control": "latest",
|
||||
"bee-icon": "^1.0.4",
|
||||
"classnames": "^2.2.5",
|
||||
"react-lazy-load": "^3.0.12",
|
||||
"shallowequal": "^1.0.2",
|
||||
"tinper-bee-core": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.3.0 || ^16.0",
|
||||
"react-dom": "^15.3.0 || ^16.0",
|
||||
"prop-types": "15.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bee-button": "latest",
|
||||
"bee-clipboard": "^2.0.0",
|
||||
"bee-drawer": "0.0.2",
|
||||
"bee-layout": "latest",
|
||||
"bee-panel": "latest",
|
||||
"chai": "^3.5.0",
|
||||
"console-polyfill": "~0.2.1",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"enzyme": "^2.4.1",
|
||||
"es5-shim": "~4.1.10",
|
||||
"react": "15.3.2",
|
||||
"react-addons-test-utils": "15.3.2",
|
||||
"react-dom": "15.3.2"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "bee-transfer",
|
||||
"version": "2.0.7",
|
||||
"description": "Transfer ui component for react",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-component",
|
||||
"bee-transfer",
|
||||
"iuap-design",
|
||||
"tinper-bee",
|
||||
"Transfer"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/tinper-bee/bee-transfer.git",
|
||||
"author": "Yonyou FED",
|
||||
"repository": "http://github.com/tinper-bee/bee-transfer",
|
||||
"bugs": "https://github.com/tinper-bee/bee-transfer.git/issues",
|
||||
"license": "MIT",
|
||||
"main": "./build/index.js",
|
||||
"config": {
|
||||
"port": 3000,
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bee-tools run start",
|
||||
"build": "bee-tools run build",
|
||||
"lint": "bee-tools-test run lint",
|
||||
"test": "bee-tools-test run test",
|
||||
"chrome": "bee-tools-test run chrome",
|
||||
"coveralls": "bee-tools-test run coverage",
|
||||
"browsers": "bee-tools-test run browsers",
|
||||
"pub": "bee-tools run pub"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.23.0",
|
||||
"bee-animate": "^1.0.0",
|
||||
"bee-button": "latest",
|
||||
"bee-checkbox": "latest",
|
||||
"bee-form-control": "latest",
|
||||
"bee-icon": "^1.0.4",
|
||||
"classnames": "^2.2.5",
|
||||
"react-beautiful-dnd": "^9.0.2",
|
||||
"react-lazy-load": "^3.0.12",
|
||||
"shallowequal": "^1.0.2",
|
||||
"tinper-bee-core": "^2.0.28"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.3.0 || ^16.0",
|
||||
"react-dom": "^15.3.0 || ^16.0",
|
||||
"prop-types": "15.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bee-button": "latest",
|
||||
"bee-clipboard": "^2.0.0",
|
||||
"bee-drawer": "0.0.2",
|
||||
"bee-layout": "latest",
|
||||
"bee-panel": "latest",
|
||||
"chai": "^3.5.0",
|
||||
"console-polyfill": "~0.2.1",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"enzyme": "^2.4.1",
|
||||
"es5-shim": "~4.1.10",
|
||||
"react": "15.3.2",
|
||||
"react-addons-test-utils": "15.3.2",
|
||||
"react-dom": "15.3.2"
|
||||
}
|
||||
}
|
||||
|
|
744
src/Transfer.js
744
src/Transfer.js
|
@ -1,301 +1,443 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import List from './list';
|
||||
import Operation from './operation';
|
||||
import Search from './search';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function noop() {
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
dataSource: [],
|
||||
render: noop,
|
||||
showSearch: false,
|
||||
};
|
||||
|
||||
const propTypes = {
|
||||
prefixCls: PropTypes.string,
|
||||
dataSource: PropTypes.array,
|
||||
render: PropTypes.func,
|
||||
targetKeys: PropTypes.array,
|
||||
onChange: PropTypes.func,
|
||||
height: PropTypes.number,
|
||||
listStyle: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
titles: PropTypes.array,
|
||||
operations: PropTypes.array,
|
||||
showSearch: PropTypes.bool,
|
||||
filterOption: PropTypes.func,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
notFoundContent: PropTypes.node,
|
||||
body: PropTypes.func,
|
||||
footer: PropTypes.func,
|
||||
rowKey: PropTypes.func,
|
||||
lazy: PropTypes.object,
|
||||
};
|
||||
|
||||
const defaultTitles = ['', ''];
|
||||
class Transfer extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { selectedKeys = [], targetKeys = [] } = props;
|
||||
this.state = {
|
||||
leftFilter: '',
|
||||
rightFilter: '',
|
||||
sourceSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) === -1),
|
||||
targetSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) > -1),
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
if (nextProps.targetKeys !== this.props.targetKeys ||
|
||||
nextProps.dataSource !== this.props.dataSource) {
|
||||
// clear cached splited dataSource
|
||||
this.splitedDataSource = null;
|
||||
|
||||
const { dataSource, targetKeys = [] } = nextProps;
|
||||
function existInDateSourcekey(key) {
|
||||
return dataSource.filter(item => item.key === key).length;
|
||||
}
|
||||
// clear key nolonger existed
|
||||
// clear checkedKeys according to targetKeys
|
||||
this.setState({
|
||||
sourceSelectedKeys: sourceSelectedKeys.filter(existInDateSourcekey)
|
||||
.filter(data => targetKeys.filter(key => key === data).length === 0),
|
||||
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() {
|
||||
if (this.splitedDataSource) {
|
||||
return this.splitedDataSource;
|
||||
}
|
||||
|
||||
const { rowKey, dataSource, targetKeys = [] } = this.props;
|
||||
if (rowKey) {
|
||||
dataSource.forEach(record => {
|
||||
record.key = rowKey(record);
|
||||
});
|
||||
}
|
||||
|
||||
const leftDataSource = dataSource.filter(({ key }) => targetKeys.indexOf(key) === -1);
|
||||
const rightDataSource = [];
|
||||
targetKeys.forEach((targetKey) => {
|
||||
const targetItem = dataSource.filter(record => record.key === targetKey)[0];
|
||||
if (targetItem) {
|
||||
rightDataSource.push(targetItem);
|
||||
}
|
||||
});
|
||||
|
||||
this.splitedDataSource = {
|
||||
leftDataSource,
|
||||
rightDataSource,
|
||||
};
|
||||
|
||||
return this.splitedDataSource;
|
||||
}
|
||||
|
||||
moveTo = (direction) => {
|
||||
const { targetKeys = [], onChange } = this.props;
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
const moveKeys = direction === 'right' ? sourceSelectedKeys : targetSelectedKeys;
|
||||
// move items to target box
|
||||
const newTargetKeys = direction === 'right'
|
||||
? moveKeys.concat(targetKeys)
|
||||
: targetKeys.filter(targetKey => moveKeys.indexOf(targetKey) === -1);
|
||||
|
||||
// empty checked keys
|
||||
const oppositeDirection = direction === 'right' ? 'left' : 'right';
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(oppositeDirection)]: [],
|
||||
});
|
||||
this.handleSelectChange(oppositeDirection, []);
|
||||
|
||||
if (onChange) {
|
||||
onChange(newTargetKeys, direction, moveKeys);
|
||||
}
|
||||
}
|
||||
|
||||
moveToLeft = () => this.moveTo('left')
|
||||
moveToRight = () => this.moveTo('right')
|
||||
|
||||
handleSelectChange(direction, holder) {
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
const onSelectChange = this.props.onSelectChange;
|
||||
if (!onSelectChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction === 'left') {
|
||||
onSelectChange(holder, targetSelectedKeys);
|
||||
} else {
|
||||
onSelectChange(sourceSelectedKeys, holder);
|
||||
}
|
||||
}
|
||||
|
||||
handleSelectAll = (direction, filteredDataSource, checkAll) => {
|
||||
const holder = checkAll ? [] : filteredDataSource.map(item => item.key);
|
||||
this.handleSelectChange(direction, holder);
|
||||
|
||||
if (!this.props.selectedKeys) {
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(direction)]: holder,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleLeftSelectAll = (filteredDataSource, checkAll) => (
|
||||
this.handleSelectAll('left', filteredDataSource, checkAll)
|
||||
)
|
||||
handleRightSelectAll = (filteredDataSource, checkAll) => (
|
||||
this.handleSelectAll('right', filteredDataSource, checkAll)
|
||||
)
|
||||
|
||||
handleFilter = (direction, e) => {
|
||||
this.setState({
|
||||
// add filter
|
||||
[`${direction}Filter`]: e,
|
||||
});
|
||||
}
|
||||
|
||||
handleLeftFilter = (e) => this.handleFilter('left', e)
|
||||
handleRightFilter = (e) => this.handleFilter('right', e)
|
||||
|
||||
handleClear = (direction) => {
|
||||
this.setState({
|
||||
[`${direction}Filter`]: '',
|
||||
});
|
||||
}
|
||||
|
||||
handleLeftClear = () => this.handleClear('left')
|
||||
handleRightClear = () => this.handleClear('right')
|
||||
|
||||
handleSelect = (direction, selectedItem, checked) => {
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
const holder = direction === 'left' ? [...sourceSelectedKeys] : [...targetSelectedKeys];
|
||||
const index = holder.indexOf(selectedItem.key);
|
||||
if (index > -1) {
|
||||
holder.splice(index, 1);
|
||||
}
|
||||
if (checked) {
|
||||
holder.push(selectedItem.key);
|
||||
}
|
||||
this.handleSelectChange(direction, holder);
|
||||
|
||||
if (!this.props.selectedKeys) {
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(direction)]: holder,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleLeftSelect = (selectedItem, checked) => this.handleSelect('left', selectedItem, checked);
|
||||
handleRightSelect = (selectedItem, checked) => this.handleSelect('right', selectedItem, checked);
|
||||
|
||||
getTitles = () => {
|
||||
if (this.props.titles) {
|
||||
return this.props.titles;
|
||||
}
|
||||
if (this.context &&
|
||||
this.context.antLocale &&
|
||||
this.context.antLocale.Transfer
|
||||
) {
|
||||
return this.context.antLocale.Transfer.titles || [];
|
||||
}
|
||||
return defaultTitles;
|
||||
}
|
||||
|
||||
getSelectedKeysName(direction) {
|
||||
return direction === 'left' ? 'sourceSelectedKeys' : 'targetSelectedKeys';
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls = 'u-transfer', operations = [], showSearch, notFoundContent,
|
||||
searchPlaceholder, body, footer, listStyle, className = '',
|
||||
filterOption, render, lazy
|
||||
} = this.props;
|
||||
const { leftFilter, rightFilter, sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
|
||||
const { leftDataSource, rightDataSource } = this.splitDataSource(this.props);
|
||||
const leftActive = targetSelectedKeys.length > 0;
|
||||
const rightActive = sourceSelectedKeys.length > 0;
|
||||
|
||||
const cls = classNames(className, prefixCls);
|
||||
|
||||
const titles = this.getTitles();
|
||||
return (
|
||||
<div className={cls}>
|
||||
<List
|
||||
titleText={titles[0]}
|
||||
dataSource={leftDataSource}
|
||||
filter={leftFilter}
|
||||
filterOption={filterOption}
|
||||
style={listStyle}
|
||||
checkedKeys={sourceSelectedKeys}
|
||||
handleFilter={this.handleLeftFilter}
|
||||
handleClear={this.handleLeftClear}
|
||||
handleSelect={this.handleLeftSelect}
|
||||
handleSelectAll={this.handleLeftSelectAll}
|
||||
render={render}
|
||||
showSearch={showSearch}
|
||||
searchPlaceholder={searchPlaceholder}
|
||||
notFoundContent={notFoundContent}
|
||||
body={body}
|
||||
footer={footer}
|
||||
prefixCls={`${prefixCls}-list`}
|
||||
lazy={lazy}
|
||||
/>
|
||||
<Operation
|
||||
rightActive={rightActive}
|
||||
rightArrowText={operations[0]}
|
||||
moveToRight={this.moveToRight}
|
||||
leftActive={leftActive}
|
||||
leftArrowText={operations[1]}
|
||||
moveToLeft={this.moveToLeft}
|
||||
className={`${prefixCls}-operation`}
|
||||
/>
|
||||
<List
|
||||
titleText={titles[1]}
|
||||
dataSource={rightDataSource}
|
||||
filter={rightFilter}
|
||||
filterOption={filterOption}
|
||||
style={listStyle}
|
||||
checkedKeys={targetSelectedKeys}
|
||||
handleFilter={this.handleRightFilter}
|
||||
handleClear={this.handleRightClear}
|
||||
handleSelect={this.handleRightSelect}
|
||||
handleSelectAll={this.handleRightSelectAll}
|
||||
render={render}
|
||||
showSearch={showSearch}
|
||||
searchPlaceholder={searchPlaceholder}
|
||||
notFoundContent={notFoundContent}
|
||||
body={body}
|
||||
footer={footer}
|
||||
prefixCls={`${prefixCls}-list`}
|
||||
lazy={lazy}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Transfer.List = Transfer.List;
|
||||
Transfer.Operation = Transfer.Operation;
|
||||
Transfer.Search = Transfer.Search;
|
||||
|
||||
Transfer.propTypes = propTypes;
|
||||
Transfer.defaultProps = defaultProps;
|
||||
|
||||
export default Transfer;
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import List from './list';
|
||||
import Operation from './operation';
|
||||
import Search from './search';
|
||||
import PropTypes from 'prop-types';
|
||||
import { DragDropContext } from 'react-beautiful-dnd';
|
||||
import { reorder,move } from './utils';
|
||||
|
||||
function noop() {}
|
||||
|
||||
const defaultProps = {
|
||||
dataSource: [],
|
||||
render: noop,
|
||||
showSearch: false,
|
||||
searchPlaceholder: 'Search',
|
||||
notFoundContent: 'Not Found',
|
||||
showCheckbox: true,
|
||||
draggable: false
|
||||
};
|
||||
|
||||
const propTypes = {
|
||||
prefixCls: PropTypes.string,
|
||||
dataSource: PropTypes.array,
|
||||
render: PropTypes.func,
|
||||
targetKeys: PropTypes.array,
|
||||
onChange: PropTypes.func,
|
||||
height: PropTypes.number,
|
||||
listStyle: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
titles: PropTypes.array,
|
||||
operations: PropTypes.array,
|
||||
showSearch: PropTypes.bool,
|
||||
filterOption: PropTypes.func,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
notFoundContent: PropTypes.node,
|
||||
body: PropTypes.func,
|
||||
footer: PropTypes.func,
|
||||
rowKey: PropTypes.func,
|
||||
lazy: PropTypes.object,
|
||||
showCheckbox: PropTypes.bool,
|
||||
draggable: PropTypes.bool
|
||||
};
|
||||
|
||||
const defaultTitles = ['', ''];
|
||||
class Transfer extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { selectedKeys = [], targetKeys = [] } = props;
|
||||
this.state = {
|
||||
leftFilter: '',
|
||||
rightFilter: '',
|
||||
sourceSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) === -1),
|
||||
targetSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) > -1),
|
||||
leftDataSource: [],
|
||||
rightDataSource: []
|
||||
};
|
||||
this.cacheTargetKeys = [...targetKeys];
|
||||
}
|
||||
componentDidMount(){
|
||||
this.splitDataSource();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
if (nextProps.targetKeys !== this.props.targetKeys ||
|
||||
nextProps.dataSource !== this.props.dataSource ||
|
||||
nextProps.targetKeys !== this.cacheTargetKeys) {
|
||||
// clear cached splited dataSource
|
||||
this.splitedDataSource = null;
|
||||
|
||||
const { dataSource, targetKeys = [] } = nextProps;
|
||||
function existInDateSourcekey(key) {
|
||||
return dataSource.filter(item => item.key === key).length;
|
||||
}
|
||||
// clear key nolonger existed
|
||||
// clear checkedKeys according to targetKeys
|
||||
this.setState({
|
||||
sourceSelectedKeys: sourceSelectedKeys.filter(existInDateSourcekey)
|
||||
.filter(data => targetKeys.filter(key => key === data).length === 0),
|
||||
targetSelectedKeys: targetSelectedKeys.filter(existInDateSourcekey)
|
||||
.filter(data => targetKeys.filter(key => key === data).length > 0),
|
||||
});
|
||||
this.splitDataSource(targetKeys,dataSource);
|
||||
}
|
||||
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),
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 从dataSource中分离出leftDataSource和rightDataSource
|
||||
* @param {*} newTargetKeys 更新后的targetKeys
|
||||
* @param {*} newDataSource 异步加载数据源时,从nextProps中获取的dataSource
|
||||
*/
|
||||
splitDataSource(newTargetKeys, newDataSource) {
|
||||
// targetKeys:展示在右边列表的数据集
|
||||
if (this.splitedDataSource) {
|
||||
return this.splitedDataSource;
|
||||
}
|
||||
|
||||
const { rowKey } = this.props;
|
||||
let targetKeys = newTargetKeys || this.props.targetKeys;
|
||||
//异步加载数据源时
|
||||
let dataSource = newDataSource || this.props.dataSource;
|
||||
if (rowKey) {
|
||||
dataSource.forEach(record => {
|
||||
record.key = rowKey(record);
|
||||
});
|
||||
}
|
||||
|
||||
const leftDataSource = dataSource.filter(({ key }) => targetKeys.indexOf(key) === -1);
|
||||
const rightDataSource = dataSource.filter(({key}) => targetKeys.indexOf(key) > -1);
|
||||
|
||||
this.splitedDataSource = {
|
||||
leftDataSource,
|
||||
rightDataSource,
|
||||
};
|
||||
this.setState({
|
||||
leftDataSource,
|
||||
rightDataSource,
|
||||
})
|
||||
|
||||
return this.splitedDataSource;
|
||||
}
|
||||
|
||||
moveTo = (direction) => {
|
||||
const { targetKeys = [], onChange } = this.props;
|
||||
// debugger
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
const moveKeys = direction === 'right' ? sourceSelectedKeys : targetSelectedKeys;
|
||||
// move items to target box
|
||||
const newTargetKeys = direction === 'right'
|
||||
? moveKeys.concat(targetKeys)
|
||||
: targetKeys.filter(targetKey => moveKeys.indexOf(targetKey) === -1);
|
||||
|
||||
// empty checked keys
|
||||
const oppositeDirection = direction === 'right' ? 'left' : 'right';
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(oppositeDirection)]: [],
|
||||
});
|
||||
// debugger
|
||||
this.handleSelectChange(oppositeDirection, []);
|
||||
|
||||
if (onChange) {
|
||||
onChange(newTargetKeys, direction, moveKeys);
|
||||
}
|
||||
this.splitDataSource(newTargetKeys);
|
||||
}
|
||||
|
||||
moveToLeft = () => this.moveTo('left')
|
||||
moveToRight = () => this.moveTo('right')
|
||||
|
||||
/**
|
||||
* List中的item选中/未选中状态改变时触发
|
||||
* @param {*} direction 'left' or 'right'
|
||||
* @param {*} holder 更新后的'sourceSelectedKeys' or 'targetSelectedKeys'
|
||||
*/
|
||||
handleSelectChange(direction, holder) {
|
||||
// onSelectChange:当选中的item发生改变时的回调 参数(sourceSelectedKeys, targetSelectedKeys)
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
const onSelectChange = this.props.onSelectChange;
|
||||
if (!onSelectChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction === 'left') {
|
||||
onSelectChange(holder, targetSelectedKeys);
|
||||
} else {
|
||||
onSelectChange(sourceSelectedKeys, holder);
|
||||
}
|
||||
}
|
||||
|
||||
handleSelectAll = (direction, filteredDataSource, checkAll) => {
|
||||
const holder = checkAll ? [] : filteredDataSource.map(item => item.key);
|
||||
this.handleSelectChange(direction, holder);
|
||||
|
||||
if (!this.props.selectedKeys) {
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(direction)]: holder,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 左侧列表全选事件
|
||||
* @param filteredDataSource dataSource中刨去设置为disabled的部分
|
||||
* @param checkAll 是否是全选状态 true:全选
|
||||
*/
|
||||
handleLeftSelectAll = (filteredDataSource, checkAll) => {
|
||||
this.handleSelectAll('left', filteredDataSource, checkAll)
|
||||
}
|
||||
handleRightSelectAll = (filteredDataSource, checkAll) => (
|
||||
this.handleSelectAll('right', filteredDataSource, checkAll)
|
||||
)
|
||||
|
||||
/**
|
||||
* 搜索框值更改事件
|
||||
* @param direction 'left' or 'right'
|
||||
* @param value 输入的值
|
||||
*/
|
||||
handleFilter = (direction, value) => {
|
||||
this.setState({
|
||||
// add filter
|
||||
[`${direction}Filter`]: value,
|
||||
});
|
||||
}
|
||||
|
||||
handleLeftFilter = (v) => this.handleFilter('left', value)
|
||||
handleRightFilter = (v) => this.handleFilter('right', value)
|
||||
|
||||
/**
|
||||
* 清空搜索框内容
|
||||
* @param direction 'left' or 'right'
|
||||
*/
|
||||
handleClear = (direction) => {
|
||||
this.setState({
|
||||
[`${direction}Filter`]: '',
|
||||
});
|
||||
}
|
||||
|
||||
handleLeftClear = () => this.handleClear('left')
|
||||
handleRightClear = () => this.handleClear('right')
|
||||
|
||||
/**
|
||||
* 点击list item,选中或取消选中
|
||||
* @param direction 'left' or 'right'
|
||||
* @param selectedItem 选中的item的信息,和dataSource数据源中的item信息一致
|
||||
* @param checked 是否已勾选,true:已勾选 false:未勾选
|
||||
*/
|
||||
handleSelect = (direction, selectedItem, checked) => {
|
||||
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
|
||||
const holder = direction === 'left' ? [...sourceSelectedKeys] : [...targetSelectedKeys];
|
||||
const index = holder.indexOf(selectedItem.key);
|
||||
if (checked) { //已勾选
|
||||
holder.splice(index, 1);
|
||||
}else if(index === -1){ //未勾选
|
||||
holder.push(selectedItem.key);
|
||||
}
|
||||
this.handleSelectChange(direction, holder);
|
||||
|
||||
if (!this.props.selectedKeys) {
|
||||
this.setState({
|
||||
[this.getSelectedKeysName(direction)]: holder,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleLeftSelect = (selectedItem, checked) => this.handleSelect('left', selectedItem, checked);
|
||||
handleRightSelect = (selectedItem, checked) => this.handleSelect('right', selectedItem, checked);
|
||||
|
||||
getTitles = () => {
|
||||
if (this.props.titles) {
|
||||
return this.props.titles;
|
||||
}
|
||||
if (this.context &&
|
||||
this.context.antLocale &&
|
||||
this.context.antLocale.Transfer
|
||||
) {
|
||||
return this.context.antLocale.Transfer.titles || [];
|
||||
}
|
||||
return defaultTitles;
|
||||
}
|
||||
|
||||
getSelectedKeysName(direction) {
|
||||
return direction === 'left' ? 'sourceSelectedKeys' : 'targetSelectedKeys';
|
||||
}
|
||||
|
||||
id2List = {
|
||||
droppable_1: 'leftDataSource',
|
||||
droppable_2: 'rightDataSource'
|
||||
};
|
||||
|
||||
getList = id => this.state[this.id2List[id]];
|
||||
|
||||
/**
|
||||
* 拖拽结束时触发
|
||||
*/
|
||||
onDragEnd = result => {
|
||||
const { source, destination,draggableId } = result;
|
||||
let { targetKeys, onChange } = this.props;
|
||||
let sourceIndex = source.index; //初始位置
|
||||
let disIndex = destination.index; //移动后的位置
|
||||
let temp; //拖拽的元素
|
||||
|
||||
// dropped outside the list
|
||||
if (!destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从右往左拖拽 或 在左侧列表中拖拽
|
||||
if (destination.droppableId === 'droppable_1') {
|
||||
if(source.droppableId === destination.droppableId) return;
|
||||
this.moveToLeft();
|
||||
return;
|
||||
}
|
||||
|
||||
// 在右侧列表中上下拖拽进行排序
|
||||
if (source.droppableId === destination.droppableId) {
|
||||
const items = reorder(
|
||||
this.getList(source.droppableId),
|
||||
targetKeys,
|
||||
source.index,
|
||||
destination.index
|
||||
);
|
||||
let state = { leftDataSource:items.dataArr }
|
||||
if (source.droppableId === 'droppable_2'){
|
||||
state = { rightDataSource:items.dataArr }
|
||||
}
|
||||
state.sourceSelectedKeys = [];
|
||||
state.targetSelectedKeys = [];
|
||||
this.setState(state);
|
||||
if (onChange) {
|
||||
onChange(items.targetKeyArr, "", draggableId);
|
||||
}
|
||||
} else { // 从左往右拖拽
|
||||
const result = move(
|
||||
this.getList(source.droppableId),
|
||||
this.getList(destination.droppableId),
|
||||
source,
|
||||
destination,
|
||||
targetKeys
|
||||
)
|
||||
if (onChange) {
|
||||
onChange(result.newTargetKeys, "", draggableId);
|
||||
}
|
||||
this.setState({
|
||||
leftDataSource: result.droppable_1,
|
||||
rightDataSource: result.droppable_2,
|
||||
sourceSelectedKeys: [],
|
||||
targetSelectedKeys: []
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 拖拽开始时触发
|
||||
*/
|
||||
onDragStart = result => {
|
||||
let selectedItem = {};
|
||||
const { source } = result;
|
||||
selectedItem.key = result.draggableId;
|
||||
if(source.droppableId === 'droppable_1'){ // leftMenu
|
||||
this.handleLeftSelect(selectedItem);
|
||||
}else if(source.droppableId === 'droppable_2'){ // rightMenu
|
||||
this.handleRightSelect(selectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls = 'u-transfer', operations = [], showSearch, notFoundContent,
|
||||
searchPlaceholder, body, footer, listStyle, className = '',
|
||||
filterOption, render, lazy, showCheckbox, draggable
|
||||
} = this.props;
|
||||
const { leftFilter, rightFilter, sourceSelectedKeys, targetSelectedKeys, leftDataSource, rightDataSource } = this.state;
|
||||
|
||||
// const { leftDataSource, rightDataSource } = this.splitDataSource(this.props);
|
||||
const leftActive = targetSelectedKeys.length > 0;
|
||||
const rightActive = sourceSelectedKeys.length > 0;
|
||||
|
||||
const cls = classNames(className, prefixCls);
|
||||
|
||||
const titles = this.getTitles();
|
||||
return (
|
||||
<div className={cls}>
|
||||
<DragDropContext onDragEnd={this.onDragEnd} onDragStart={this.onDragStart} >
|
||||
<List
|
||||
titleText={titles[0]} //左侧标题
|
||||
dataSource={leftDataSource} //左侧数据源
|
||||
filter={leftFilter} //搜索框中输入的内容
|
||||
filterOption={filterOption} //搜索过滤方法 参数(inputValue, option)
|
||||
style={listStyle} //自定义的columns的样式表
|
||||
checkedKeys={sourceSelectedKeys} //左侧已勾选的item的keys
|
||||
handleFilter={this.handleLeftFilter} //左侧搜索框值更改事件
|
||||
handleClear={this.handleLeftClear} //清空左侧搜索框内容
|
||||
handleSelect={this.handleLeftSelect} //点击左侧列表中的item,改变选中或取消选中状态
|
||||
handleSelectAll={this.handleLeftSelectAll} //点击左侧全选
|
||||
render={render}
|
||||
showSearch={showSearch} //是否显示搜索框
|
||||
searchPlaceholder={searchPlaceholder} //搜索框placeholder
|
||||
notFoundContent={notFoundContent} //当没有相关内容的显示内容
|
||||
body={body}
|
||||
footer={footer}
|
||||
prefixCls={`${prefixCls}-list`}
|
||||
lazy={lazy}
|
||||
showCheckbox={showCheckbox}
|
||||
draggable={draggable}
|
||||
id={'1'}
|
||||
/>
|
||||
{!draggable?
|
||||
<Operation
|
||||
rightActive={rightActive}
|
||||
rightArrowText={operations[0]}
|
||||
moveToRight={this.moveToRight}
|
||||
leftActive={leftActive}
|
||||
leftArrowText={operations[1]}
|
||||
moveToLeft={this.moveToLeft}
|
||||
className={`${prefixCls}-operation`}
|
||||
/>
|
||||
: ''
|
||||
}
|
||||
<List
|
||||
titleText={titles[1]} //右侧标题
|
||||
dataSource={rightDataSource} //右侧数据源
|
||||
filter={rightFilter} //搜索框中输入的内容
|
||||
filterOption={filterOption} //搜索过滤方法 参数(inputValue, option)
|
||||
style={listStyle} //自定义的columns的样式表
|
||||
checkedKeys={targetSelectedKeys} //右侧已勾选的item的keys
|
||||
handleFilter={this.handleRightFilter} //右侧搜索框值更改事件
|
||||
handleClear={this.handleRightClear} //清空右侧搜索框内容
|
||||
handleSelect={this.handleRightSelect} //点击右侧列表中的item,改变选中或取消选中状态
|
||||
handleSelectAll={this.handleRightSelectAll} //点击右侧全选
|
||||
render={render}
|
||||
showSearch={showSearch} //是否显示搜索框
|
||||
searchPlaceholder={searchPlaceholder} //搜索框placeholder
|
||||
notFoundContent={notFoundContent} //当没有相关内容的显示内容
|
||||
body={body}
|
||||
footer={footer}
|
||||
prefixCls={`${prefixCls}-list`}
|
||||
lazy={lazy}
|
||||
showCheckbox={showCheckbox}
|
||||
draggable={draggable}
|
||||
id={'2'}
|
||||
/>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Transfer.List = Transfer.List;
|
||||
Transfer.Operation = Transfer.Operation;
|
||||
Transfer.Search = Transfer.Search;
|
||||
|
||||
Transfer.propTypes = propTypes;
|
||||
Transfer.defaultProps = defaultProps;
|
||||
|
||||
export default Transfer;
|
||||
|
|
|
@ -1,235 +1,282 @@
|
|||
@import "../node_modules/tinper-bee-core/scss/minxin-variables";
|
||||
@import "../node_modules/tinper-bee-core/scss/minxin-mixins";
|
||||
@import '../node_modules/bee-button/src/Button.scss';
|
||||
@import '../node_modules/bee-form-control/src/FormControl.scss';
|
||||
@import '../node_modules/bee-checkbox/src/Checkbox.scss';
|
||||
@import "../node_modules/bee-icon/src/Icon.scss";
|
||||
|
||||
|
||||
|
||||
.u-transfer {
|
||||
position: relative;
|
||||
line-height: 1.5;
|
||||
|
||||
&-list {
|
||||
font-size: 12px;
|
||||
border: 1px solid $transfer-border-gap-color;
|
||||
display: inline-block;
|
||||
border-radius: $border-radius-base;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
width: 180px;
|
||||
height: 200px;
|
||||
padding-top: 33px;
|
||||
|
||||
&-with-footer {
|
||||
padding-bottom: 33px;
|
||||
}
|
||||
|
||||
&-search {
|
||||
&-action {
|
||||
color: #ccc;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
.uf {
|
||||
transition: all .3s;
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
&:hover {
|
||||
color: rgba(0,0,0,.43);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
padding: 7px 15px;
|
||||
border-radius: $border-radius-base $border-radius-base 0 0;
|
||||
background: #fff;
|
||||
color: rgba(0,0,0,.65);
|
||||
border-bottom: 1px solid #e9e9e9;//$transfer-border-gap-color;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
&-title {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
&-search-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 28px;
|
||||
padding: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-body-with-search {
|
||||
padding-top: 34px;
|
||||
}
|
||||
|
||||
&-content {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
&-item {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
padding: 7px 15px;
|
||||
min-height: 32px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
&-item:not(&-item-disabled):hover {
|
||||
cursor: pointer;
|
||||
background-color: $hover-bg-color-base;
|
||||
}
|
||||
|
||||
&-item-disabled {
|
||||
cursor: not-allowed;
|
||||
color: rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
&-item-highlight-enter {
|
||||
animation: transferHighlightIn 1s ease;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-body-not-found {
|
||||
padding-top: 0;
|
||||
color: #ccc;
|
||||
text-align: center;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
&-content:empty + &-body-not-found {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
border-top: 1px solid #e9e9e9;
|
||||
border-radius: 0 0 $select-border-radius $select-border-radius;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-operation {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
margin: 0 8px;
|
||||
vertical-align: middle;
|
||||
|
||||
button {
|
||||
display: block;
|
||||
|
||||
&:first-child {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.uf {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.u-checkbox+span, .u-checkbox-wrapper+span {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
// .u-checkbox-checked .u-checkbox-label, .u-checkbox-indeterminate .u-checkbox-label {
|
||||
// background-color: #108ee9;
|
||||
// border-color: #108ee9;
|
||||
// }
|
||||
.u-checkbox.u-checkbox-indeterminate .u-checkbox-label:after {
|
||||
color: #fff;
|
||||
content: "\e6ce";
|
||||
line-height: 18px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.u-checkbox .u-checkbox-label:before, .u-checkbox .u-checkbox-label:after{
|
||||
top: 0;
|
||||
}
|
||||
|
||||
// .u-checkbox.u-checkbox-indeterminate .u-checkbox-label:before {
|
||||
// box-shadow: inset 0 0 0 10px rgb(30,136,229);
|
||||
// border-color: rgb(30,136,229);
|
||||
// }
|
||||
.u-button.disabled, .u-button[disabled]:hover {
|
||||
background: #fff;
|
||||
color:#9a9898;
|
||||
border-color: rgb(224,224,224);
|
||||
}
|
||||
}
|
||||
|
||||
.u-transfer-operation .u-button-sm {
|
||||
padding: 0;
|
||||
font-size: 1.2rem;
|
||||
border: 1px solid;
|
||||
color: rgb(109, 107, 107);
|
||||
min-width: 3rem;
|
||||
}
|
||||
|
||||
.u-transfer-operation .u-button-sm{
|
||||
background: $checkbox-color;
|
||||
color: #fff;
|
||||
border-color: $checkbox-color;
|
||||
}
|
||||
.u-transfer-operation .u-button-sm:hover{
|
||||
background: $checkbox-color;
|
||||
border-color: $checkbox-color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.u-transfer-operation .u-button-sm[disabled]{
|
||||
background: #fff;
|
||||
color:#9a9898;
|
||||
border-color: rgb(224,224,224);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.u-transfer-list-search {
|
||||
font-size: 1.2rem;
|
||||
border-color: $transfer-border-gap-color;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
@keyframes transferHighlightIn {
|
||||
0% {
|
||||
background: $checkbox-color;
|
||||
}
|
||||
100% {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
@import "../node_modules/tinper-bee-core/scss/minxin-variables";
|
||||
@import "../node_modules/tinper-bee-core/scss/minxin-mixins";
|
||||
// @import '../node_modules/bee-button/src/Button.scss';
|
||||
// @import '../node_modules/bee-form-control/src/FormControl.scss';
|
||||
// @import '../node_modules/bee-checkbox/src/Checkbox.scss';
|
||||
// @import "../node_modules/bee-icon/src/Icon.scss";
|
||||
|
||||
|
||||
|
||||
.u-transfer {
|
||||
position: relative;
|
||||
line-height: 1.5;
|
||||
|
||||
&-list {
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
border: 1px solid $transfer-border-gap-color;
|
||||
display: inline-block;
|
||||
border-radius: $border-radius-base;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
width: 180px;
|
||||
height: 200px;
|
||||
padding-top: 33px;
|
||||
&.u-transfer-list-draggable:first-child{
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&-with-footer {
|
||||
padding-bottom: 33px;
|
||||
}
|
||||
|
||||
&-search {
|
||||
&-action {
|
||||
color: #505F79;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
text-decoration: none;
|
||||
.uf {
|
||||
transition: all .3s;
|
||||
font-size: 16px;
|
||||
color: #505F79;
|
||||
&:hover {
|
||||
color: rgba(0,0,0,.43);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-header {
|
||||
padding: 7px 15px;
|
||||
border-radius: $border-radius-base $border-radius 0 0;
|
||||
background: #fff;
|
||||
color: $font-color-base;
|
||||
border-bottom: 1px solid #e9e9e9;//$transfer-border-gap-color;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
&-title {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
&-search-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-body-with-search {
|
||||
padding-top: 34px;
|
||||
}
|
||||
|
||||
&-content {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
&.delbtn-backdrop{
|
||||
background: #505F79;
|
||||
opacity: 0.65;
|
||||
&.uf-del{
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
&-item {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
padding: 7px 15px;
|
||||
min-height: 32px;
|
||||
transition: all 0.3s ease;
|
||||
color: $font-color-base;
|
||||
}
|
||||
&-item-selected{
|
||||
background: $selected-bg-color-base;
|
||||
}
|
||||
|
||||
&-item:not(&-item-disabled):hover {
|
||||
cursor: pointer;
|
||||
background-color: $hover-bg-color-base;
|
||||
}
|
||||
|
||||
&-item-disabled {
|
||||
cursor: not-allowed;
|
||||
color: $disabled-color-base;
|
||||
}
|
||||
|
||||
&-item-highlight-enter {
|
||||
animation: transferHighlightIn 1s ease;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
&-delete-selected{
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(52, 69, 99, 0.2);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
color: #fff;
|
||||
&.show{
|
||||
display: block;
|
||||
}
|
||||
.u-transfer-list-del-btn{
|
||||
width:78px;
|
||||
height:78px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-top: -39px;
|
||||
margin-left: -39px;
|
||||
padding: 13px;
|
||||
background:rgba(94,108,132,1);
|
||||
border-radius:$border-radius-base;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
span{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-body-not-found {
|
||||
padding-top: 0;
|
||||
color: #ccc;
|
||||
text-align: center;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
margin-top: -10px;
|
||||
&.show{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&-content:empty + &-body-not-found {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
border-top: 1px solid #e9e9e9;
|
||||
border-radius: 0 0 $select-border-radius $select-border-radius;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-operation {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
margin: 0 8px;
|
||||
vertical-align: middle;
|
||||
|
||||
button {
|
||||
display: block;
|
||||
|
||||
&:first-child {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.uf {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.u-checkbox+span, .u-checkbox-wrapper+span {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
// .u-checkbox-checked .u-checkbox-label, .u-checkbox-indeterminate .u-checkbox-label {
|
||||
// background-color: #108ee9;
|
||||
// border-color: #108ee9;
|
||||
// }
|
||||
.u-checkbox.u-checkbox-indeterminate .u-checkbox-label:after {
|
||||
color: #fff;
|
||||
content: "\e6ce";
|
||||
line-height: 18px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.u-checkbox .u-checkbox-label:before, .u-checkbox .u-checkbox-label:after{
|
||||
top: 0;
|
||||
}
|
||||
|
||||
// .u-checkbox.u-checkbox-indeterminate .u-checkbox-label:before {
|
||||
// box-shadow: inset 0 0 0 10px rgb(30,136,229);
|
||||
// border-color: rgb(30,136,229);
|
||||
// }
|
||||
.u-button.disabled, .u-button[disabled]:hover {
|
||||
background: #fff;
|
||||
color:#9a9898;
|
||||
border-color: rgb(224,224,224);
|
||||
}
|
||||
}
|
||||
|
||||
.u-transfer-operation .u-button-sm {
|
||||
padding: 0;
|
||||
font-size: 1.2rem;
|
||||
border: 1px solid;
|
||||
color: rgb(109, 107, 107);
|
||||
min-width: 3rem;
|
||||
}
|
||||
|
||||
.u-transfer-operation .u-button-sm{
|
||||
background: $checkbox-color;
|
||||
color: #fff;
|
||||
border-color: $checkbox-color;
|
||||
}
|
||||
.u-transfer-operation .u-button-sm:hover{
|
||||
background: $checkbox-color;
|
||||
border-color: $checkbox-color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.u-transfer-operation .u-button-sm[disabled]{
|
||||
background: #fff;
|
||||
color:#9a9898;
|
||||
border-color: rgb(224,224,224);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.u-transfer-list-search {
|
||||
font-size: 1.2rem;
|
||||
border-color: $transfer-border-gap-color;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
@keyframes transferHighlightIn {
|
||||
0% {
|
||||
background: $checkbox-color;
|
||||
}
|
||||
100% {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
|
151
src/item.js
151
src/item.js
|
@ -1,73 +1,78 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PureRenderMixin from './PureRenderMixin';
|
||||
import assign from 'object-assign';
|
||||
import Lazyload from 'react-lazy-load';
|
||||
import Checkbox from 'bee-checkbox';
|
||||
|
||||
function isRenderResultPlainObject(result) {
|
||||
return result && !React.isValidElement(result) &&
|
||||
Object.prototype.toString.call(result) === '[object Object]';
|
||||
}
|
||||
|
||||
class Item extends React.Component{
|
||||
shouldComponentUpdate(...args) {
|
||||
return PureRenderMixin.shouldComponentUpdate.apply(this, args);
|
||||
}
|
||||
matchFilter = (text) => {
|
||||
const { filter, filterOption, item } = this.props;
|
||||
if (filterOption) {
|
||||
return filterOption(filter, item);
|
||||
}
|
||||
return text.indexOf(filter) >= 0;
|
||||
}
|
||||
render() {
|
||||
const { render, filter, item, lazy, checked, prefixCls, onClick,renderedText,renderedEl } = this.props;
|
||||
const className = classNames({
|
||||
[`${prefixCls}-content-item`]: true,
|
||||
[`${prefixCls}-content-item-disabled`]: item.disabled,
|
||||
});
|
||||
|
||||
const lazyProps = assign({
|
||||
height: 32,
|
||||
offset: 500,
|
||||
throttle: 0,
|
||||
debounce: false,
|
||||
}, lazy);
|
||||
|
||||
let lazyFlag = true;
|
||||
if(lazy && lazy.container == "modal")
|
||||
{
|
||||
lazyFlag = false
|
||||
}
|
||||
|
||||
if(!lazyFlag) {
|
||||
return (
|
||||
<li
|
||||
className={className}
|
||||
title={renderedText}
|
||||
onClick={item.disabled ? undefined : () => onClick(item)}
|
||||
>
|
||||
<Checkbox checked={checked} disabled={item.disabled} onClick={item.disabled ? undefined : () => onClick(item)}/>
|
||||
<span>{renderedEl}</span>
|
||||
</li>
|
||||
)
|
||||
}else {
|
||||
return (
|
||||
<Lazyload {...lazyProps}>
|
||||
<li
|
||||
className={className}
|
||||
title={renderedText}
|
||||
onClick={item.disabled ? undefined : () => onClick(item)}
|
||||
>
|
||||
<Checkbox checked={checked} disabled={item.disabled} onClick={item.disabled ? undefined : () => onClick(item)}/>
|
||||
<span>{renderedEl}</span>
|
||||
</li>
|
||||
</Lazyload>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default Item;
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PureRenderMixin from './PureRenderMixin';
|
||||
import assign from 'object-assign';
|
||||
import Lazyload from 'react-lazy-load';
|
||||
import Checkbox from 'bee-checkbox';
|
||||
|
||||
function isRenderResultPlainObject(result) {
|
||||
return result && !React.isValidElement(result) &&
|
||||
Object.prototype.toString.call(result) === '[object Object]';
|
||||
}
|
||||
|
||||
class Item extends React.Component{
|
||||
shouldComponentUpdate(...args) {
|
||||
return PureRenderMixin.shouldComponentUpdate.apply(this, args);
|
||||
}
|
||||
// matchFilter = (text) => {
|
||||
// const { filter, filterOption, item } = this.props;
|
||||
// if (filterOption) {
|
||||
// return filterOption(filter, item);
|
||||
// }
|
||||
// return text.indexOf(filter) >= 0;
|
||||
// }
|
||||
render() {
|
||||
const { render, filter, item, lazy, checked, prefixCls, onClick,renderedText,renderedEl, showCheckbox } = this.props;
|
||||
const className = classNames({
|
||||
[`${prefixCls}-content-item`]: true,
|
||||
[`${prefixCls}-content-item-disabled`]: item.disabled,
|
||||
[`${prefixCls}-content-item-selected`]: checked
|
||||
});
|
||||
|
||||
const lazyProps = assign({
|
||||
height: 32,
|
||||
offset: 500,
|
||||
throttle: 0,
|
||||
debounce: false,
|
||||
}, lazy);
|
||||
|
||||
let lazyFlag = true;
|
||||
if(lazy && lazy.container == "modal")
|
||||
{
|
||||
lazyFlag = false
|
||||
}
|
||||
|
||||
if(!lazyFlag) {
|
||||
return (
|
||||
<li
|
||||
className={className}
|
||||
title={renderedText}
|
||||
onClick={item.disabled ? undefined : () => onClick(item)}
|
||||
>
|
||||
<Checkbox checked={checked} disabled={item.disabled} onClick={item.disabled ? undefined : () => onClick(item)}/>
|
||||
<span>{renderedEl}</span>
|
||||
</li>
|
||||
)
|
||||
}else {
|
||||
return (
|
||||
<Lazyload {...lazyProps}>
|
||||
<li
|
||||
className={className}
|
||||
title={renderedText}
|
||||
onClick={item.disabled ? undefined : () => onClick(item)}
|
||||
>
|
||||
{
|
||||
showCheckbox?
|
||||
<Checkbox checked={checked} disabled={item.disabled} onClick={item.disabled ? undefined : () => onClick(item)}/>
|
||||
:''
|
||||
}
|
||||
<span>{renderedEl}</span>
|
||||
</li>
|
||||
</Lazyload>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default Item;
|
||||
|
|
535
src/list.js
535
src/list.js
|
@ -1,234 +1,303 @@
|
|||
import React from 'react';
|
||||
import Search from './search';
|
||||
import classNames from 'classnames';
|
||||
import Animate from 'bee-animate';
|
||||
import PureRenderMixin from './PureRenderMixin';
|
||||
import assign from 'object-assign';
|
||||
import { TransferItem } from './index';
|
||||
import Item from './item';
|
||||
import Checkbox from 'bee-checkbox';
|
||||
|
||||
function noop() {
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
dataSource: [],
|
||||
titleText: '',
|
||||
showSearch: false,
|
||||
render: noop,
|
||||
};
|
||||
function isRenderResultPlainObject(result) {
|
||||
return result && !React.isValidElement(result) &&
|
||||
Object.prototype.toString.call(result) === '[object Object]';
|
||||
}
|
||||
|
||||
class TransferList extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
mounted: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.timer = setTimeout(() => {
|
||||
this.setState({
|
||||
mounted: true,
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(...args) {
|
||||
return PureRenderMixin.shouldComponentUpdate.apply(this, args);
|
||||
}
|
||||
|
||||
|
||||
matchFilter = (text,item) => {
|
||||
const { filter, filterOption} = this.props;
|
||||
if (filterOption) {
|
||||
return filterOption(filter, item);
|
||||
}
|
||||
return text.indexOf(filter) >= 0;
|
||||
}
|
||||
getCheckStatus(filteredDataSource) {
|
||||
const { checkedKeys } = this.props;
|
||||
if (checkedKeys.length === 0) {
|
||||
return 'none';
|
||||
} else if (filteredDataSource.every(item => checkedKeys.indexOf(item.key) >= 0)) {
|
||||
return 'all';
|
||||
}
|
||||
return 'part';
|
||||
}
|
||||
|
||||
handleSelect = (selectedItem) => {
|
||||
const { checkedKeys } = this.props;
|
||||
const result = checkedKeys.some((key) => key === selectedItem.key);
|
||||
this.props.handleSelect(selectedItem, !result);
|
||||
}
|
||||
|
||||
handleFilter = (e) => {
|
||||
this.props.handleFilter(e);
|
||||
}
|
||||
|
||||
handleClear = () => {
|
||||
this.props.handleClear();
|
||||
}
|
||||
renderItem = (item) => {
|
||||
const { render = noop } = this.props;
|
||||
const renderResult = render(item);
|
||||
const isRenderResultPlain = isRenderResultPlainObject(renderResult);
|
||||
return {
|
||||
renderedText: isRenderResultPlain ? renderResult.value : renderResult,
|
||||
renderedEl: isRenderResultPlain ? renderResult.label : renderResult,
|
||||
};
|
||||
}
|
||||
renderCheckbox({ prefixCls, filteredDataSource, checked, checkPart, disabled, checkable }) {
|
||||
const checkAll = (!checkPart) && checked;
|
||||
prefixCls = "u"
|
||||
const checkboxCls = classNames({
|
||||
[`${prefixCls}-checkbox-indeterminate`]: checkPart,
|
||||
[`${prefixCls}-checkbox-disabled`]: disabled,
|
||||
});
|
||||
return (
|
||||
<span
|
||||
className="u-checkbox-wrapper"
|
||||
>
|
||||
<Checkbox
|
||||
onChange={() => this.props.handleSelectAll(filteredDataSource, checkAll)}
|
||||
className={checkboxCls}
|
||||
checked={checkAll}
|
||||
/>
|
||||
</span>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { prefixCls, dataSource, titleText, filter, checkedKeys, lazy, filterOption,
|
||||
body = noop, footer = noop, showSearch, render = noop, style } = this.props;
|
||||
|
||||
let { searchPlaceholder, notFoundContent } = this.props;
|
||||
|
||||
// Custom Layout
|
||||
const footerDom = footer(assign({}, this.props));
|
||||
const bodyDom = body(assign({}, this.props));
|
||||
|
||||
const listCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-with-footer`]: !!footerDom,
|
||||
});
|
||||
|
||||
const filteredDataSource = [];
|
||||
const totalDataSource = [];
|
||||
const showItems = dataSource.map((item) => {
|
||||
const { renderedText, renderedEl } = this.renderItem(item);
|
||||
if (filter && filter.trim() && !this.matchFilter(renderedText, item)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// all show items
|
||||
totalDataSource.push(item);
|
||||
|
||||
if (!item.disabled) {
|
||||
filteredDataSource.push(item);
|
||||
}
|
||||
|
||||
const checked = checkedKeys.indexOf(item.key) >= 0;
|
||||
return (
|
||||
<Item
|
||||
key={item.key}
|
||||
item={item}
|
||||
lazy={lazy}
|
||||
render={render}
|
||||
renderedText={renderedText}
|
||||
renderedEl={renderedEl}
|
||||
filter={filter}
|
||||
filterOption={filterOption}
|
||||
checked={checked}
|
||||
prefixCls={prefixCls}
|
||||
onClick={this.handleSelect}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
let unit = '';
|
||||
const antLocale = this.context.antLocale;
|
||||
if (antLocale && antLocale.Transfer) {
|
||||
const transferLocale = antLocale.Transfer;
|
||||
unit = dataSource.length > 1 ? transferLocale.itemsUnit : transferLocale.itemUnit;
|
||||
searchPlaceholder = searchPlaceholder || transferLocale.searchPlaceholder;
|
||||
notFoundContent = notFoundContent || transferLocale.notFoundContent;
|
||||
}
|
||||
|
||||
const checkStatus = this.getCheckStatus(filteredDataSource);
|
||||
const outerPrefixCls = prefixCls.replace('-list', '');
|
||||
const search = showSearch ? (
|
||||
<div className={`${prefixCls}-body-search-wrapper`}>
|
||||
<Search
|
||||
prefixCls={`${prefixCls}-search`}
|
||||
onChange={this.handleFilter}
|
||||
handleClear={this.handleClear}
|
||||
placeholder={searchPlaceholder || 'Search'}
|
||||
value={filter}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const listBody = bodyDom || (
|
||||
<div className={showSearch ? `${prefixCls}-body ${prefixCls}-body-with-search` : `${prefixCls}-body`}>
|
||||
{search}
|
||||
<Animate
|
||||
component="ul"
|
||||
className={`${prefixCls}-content`}
|
||||
transitionName={this.state.mounted ? `${prefixCls}-content-item-highlight` : ''}
|
||||
transitionLeave={false}
|
||||
>
|
||||
{showItems}
|
||||
</Animate>
|
||||
<div className={`${prefixCls}-body-not-found`}>
|
||||
{notFoundContent || 'Not Found'}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const listFooter = footerDom ? (
|
||||
<div className={`${prefixCls}-footer`}>
|
||||
{footerDom}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const renderedCheckbox = this.renderCheckbox({
|
||||
prefixCls: outerPrefixCls,
|
||||
checked: checkStatus === 'all',
|
||||
checkPart: checkStatus === 'part',
|
||||
checkable: <span className={`${outerPrefixCls}-checkbox-inner`} />,
|
||||
filteredDataSource,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={listCls} style={style}>
|
||||
<div className={`${prefixCls}-header`}>
|
||||
{renderedCheckbox}
|
||||
<span className={`${prefixCls}-header-selected`}>
|
||||
<span>
|
||||
{(checkedKeys.length > 0 ? `${checkedKeys.length}/` : '') + totalDataSource.length} {unit}
|
||||
</span>
|
||||
<span className={`${prefixCls}-header-title`}>
|
||||
{titleText}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{listBody}
|
||||
{listFooter}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TransferList.defaultProps = defaultProps;
|
||||
import React from 'react';
|
||||
import Search from './search';
|
||||
import classNames from 'classnames';
|
||||
import Animate from 'bee-animate';
|
||||
import PureRenderMixin from './PureRenderMixin';
|
||||
import assign from 'object-assign';
|
||||
import { TransferItem } from './index';
|
||||
import Item from './item';
|
||||
import Checkbox from 'bee-checkbox';
|
||||
import Icon from 'bee-icon';
|
||||
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
|
||||
import { KeyCode} from 'tinper-bee-core';
|
||||
|
||||
function noop() {
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
dataSource: [],
|
||||
titleText: '',
|
||||
showSearch: false,
|
||||
render: noop,
|
||||
};
|
||||
function isRenderResultPlainObject(result) {
|
||||
return result && !React.isValidElement(result) &&
|
||||
Object.prototype.toString.call(result) === '[object Object]';
|
||||
}
|
||||
|
||||
class TransferList extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
mounted: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.timer = setTimeout(() => {
|
||||
this.setState({
|
||||
mounted: true,
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(...args) {
|
||||
return PureRenderMixin.shouldComponentUpdate.apply(this, args);
|
||||
}
|
||||
|
||||
|
||||
matchFilter = (text,item) => {
|
||||
//filter:搜索框中的内容
|
||||
//filterOption:用户自定义的搜索过滤方法
|
||||
const { filter, filterOption} = this.props;
|
||||
if (filterOption) {
|
||||
return filterOption(filter, item);
|
||||
}
|
||||
return text.indexOf(filter) >= 0;
|
||||
}
|
||||
/**
|
||||
* 获取Checkbox状态
|
||||
* @param {*} filteredDataSource dataSource中刨去设置为disabled的部分
|
||||
*/
|
||||
getCheckStatus(filteredDataSource) {
|
||||
const { checkedKeys } = this.props;
|
||||
if (checkedKeys.length === 0) {
|
||||
return 'none'; //全部未选
|
||||
} else if (filteredDataSource.every(item => checkedKeys.indexOf(item.key) >= 0)) {
|
||||
return 'all'; //全部已选
|
||||
}
|
||||
return 'part'; //部分已选
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击list item,选中或取消选中
|
||||
* @param selectedItem 选中的item的信息,和dataSource数据源中的item信息一致
|
||||
*/
|
||||
handleSelect = (selectedItem) => {
|
||||
// checkedKeys:已勾选的Keys数组
|
||||
// result:是否已勾选,true:已勾选 false:未勾选
|
||||
const { checkedKeys } = this.props;
|
||||
const result = checkedKeys.some((key) => key === selectedItem.key);
|
||||
this.props.handleSelect(selectedItem, result);
|
||||
}
|
||||
|
||||
handleFilter = (e) => {
|
||||
this.props.handleFilter(e);
|
||||
}
|
||||
|
||||
handleClear = () => {
|
||||
this.props.handleClear();
|
||||
}
|
||||
renderItem = (item) => {
|
||||
const { render = noop } = this.props;
|
||||
const renderResult = render(item);
|
||||
const isRenderResultPlain = isRenderResultPlainObject(renderResult);
|
||||
return {
|
||||
renderedText: isRenderResultPlain ? renderResult.value : renderResult,
|
||||
renderedEl: isRenderResultPlain ? renderResult.label : renderResult,
|
||||
};
|
||||
}
|
||||
renderCheckbox({ prefixCls, filteredDataSource, checked, checkPart, disabled, checkable }) {
|
||||
const checkAll = (!checkPart) && checked; //非半选 && 全选
|
||||
prefixCls = "u"
|
||||
const checkboxCls = classNames({
|
||||
[`${prefixCls}-checkbox-indeterminate`]: checkPart,
|
||||
[`${prefixCls}-checkbox-disabled`]: disabled,
|
||||
});
|
||||
return (
|
||||
<span
|
||||
className="u-checkbox-wrapper"
|
||||
>
|
||||
<Checkbox
|
||||
onChange={() => this.props.handleSelectAll(filteredDataSource, checkAll)}
|
||||
className={checkboxCls}
|
||||
checked={checkAll}
|
||||
/>
|
||||
</span>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
onKeyDown = (event,provided,snapshot,item) => {
|
||||
if (provided.dragHandleProps) {
|
||||
provided.dragHandleProps.onKeyDown(event);
|
||||
}
|
||||
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (snapshot.isDragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.keyCode !== KeyCode.ENTER) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 为了选择,我们使用此事件 we are using the event for selection
|
||||
event.preventDefault();
|
||||
|
||||
this.performAction(event,item);
|
||||
};
|
||||
|
||||
|
||||
render() {
|
||||
const { prefixCls, dataSource, titleText, filter, checkedKeys, lazy, filterOption,
|
||||
body = noop, footer = noop, showSearch, render = noop, style, id, showCheckbox, draggable } = this.props;
|
||||
|
||||
let { searchPlaceholder, notFoundContent } = this.props;
|
||||
|
||||
// Custom Layout
|
||||
const footerDom = footer(assign({}, this.props));
|
||||
const bodyDom = body(assign({}, this.props));
|
||||
|
||||
const listCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-with-footer`]: !!footerDom,
|
||||
[`${prefixCls}-draggable`]: !!draggable
|
||||
});
|
||||
|
||||
const filteredDataSource = [];
|
||||
const totalDataSource = [];
|
||||
const showItems = dataSource.map((item,index) => {
|
||||
const { renderedText, renderedEl } = this.renderItem(item);
|
||||
if (filter && filter.trim() && !this.matchFilter(renderedText, item)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// all show items
|
||||
totalDataSource.push(item);
|
||||
|
||||
if (!item.disabled) {
|
||||
filteredDataSource.push(item);
|
||||
}
|
||||
|
||||
const checked = checkedKeys.indexOf(item.key) >= 0;
|
||||
return (
|
||||
<Draggable key={item.key} index={index} draggableId={`${item.key}`} isDragDisabled={draggable ? item.disabled : !draggable}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
// onClick={(event) =>this.handleDrag(event, provided, snapshot, item)}
|
||||
onKeyDown={(event) =>
|
||||
this.onKeyDown(event, provided, snapshot, item)
|
||||
}
|
||||
// className={classnames({
|
||||
// ...getClass(this.props,snapshot.isDragging).drag
|
||||
// })}
|
||||
style={{...provided.draggableProps.style}}>
|
||||
<Item
|
||||
// ref={provided.innerRef} //Error: provided.innerRef has not been provided with a HTMLElement
|
||||
// key={item.key}
|
||||
item={item}
|
||||
lazy={lazy}
|
||||
render={render}
|
||||
renderedText={renderedText}
|
||||
renderedEl={renderedEl}
|
||||
filter={filter}
|
||||
filterOption={filterOption}
|
||||
checked={checked}
|
||||
prefixCls={prefixCls}
|
||||
onClick={this.handleSelect}
|
||||
showCheckbox={showCheckbox}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>)
|
||||
});
|
||||
|
||||
let unit = '';
|
||||
const antLocale = this.context.antLocale;
|
||||
if (antLocale && antLocale.Transfer) {
|
||||
const transferLocale = antLocale.Transfer;
|
||||
unit = dataSource.length > 1 ? transferLocale.itemsUnit : transferLocale.itemUnit;
|
||||
searchPlaceholder = searchPlaceholder || transferLocale.searchPlaceholder;
|
||||
notFoundContent = notFoundContent || transferLocale.notFoundContent;
|
||||
}
|
||||
|
||||
const checkStatus = this.getCheckStatus(filteredDataSource);
|
||||
const outerPrefixCls = prefixCls.replace('-list', '');
|
||||
const search = showSearch ? (
|
||||
<div className={`${prefixCls}-body-search-wrapper`}>
|
||||
<Search
|
||||
prefixCls={`${prefixCls}-search`}
|
||||
onChange={this.handleFilter}
|
||||
handleClear={this.handleClear}
|
||||
placeholder={searchPlaceholder}
|
||||
value={filter}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const listBody = bodyDom || (
|
||||
<div className={showSearch ? `${prefixCls}-body ${prefixCls}-body-with-search` : `${prefixCls}-body`}>
|
||||
{search}
|
||||
<Droppable droppableId={`droppable_${id}`} direction='vertical' isDropDisabled={!draggable}>
|
||||
{(provided, snapshot) => (
|
||||
<div ref={provided.innerRef} isDraggingOver={snapshot.isDraggingOver} key={id} className={`${prefixCls}-content`}>
|
||||
<Animate
|
||||
component="ul"
|
||||
transitionName={this.state.mounted ? `${prefixCls}-content-item-highlight` : ''}
|
||||
transitionLeave={false}
|
||||
>
|
||||
{showItems}
|
||||
</Animate>
|
||||
<div className={`${prefixCls}-delete-selected ${snapshot.isDraggingOver && id === '1'? 'show': ''}`}>
|
||||
<div className={`${prefixCls}-del-btn`}>
|
||||
<Icon type="uf-arrow-down-2"></Icon>
|
||||
<span>移除已选</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
<div className={`${prefixCls}-body-not-found ${dataSource.length == 0? "show" : ""}`}>
|
||||
{notFoundContent}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const listFooter = footerDom ? (
|
||||
<div className={`${prefixCls}-footer`}>
|
||||
{footerDom}
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
const renderedCheckbox = this.renderCheckbox({
|
||||
prefixCls: outerPrefixCls,
|
||||
checked: checkStatus === 'all',
|
||||
checkPart: checkStatus === 'part',
|
||||
checkable: <span className={`${outerPrefixCls}-checkbox-inner`} />,
|
||||
filteredDataSource,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={listCls} style={style}>
|
||||
<div className={`${prefixCls}-header`}>
|
||||
{showCheckbox ? renderedCheckbox : ''}
|
||||
<span className={`${prefixCls}-header-selected`}>
|
||||
<span>
|
||||
{(checkedKeys.length > 0 ? `${checkedKeys.length}/` : '') + totalDataSource.length} {unit}
|
||||
</span>
|
||||
<span className={`${prefixCls}-header-title`}>
|
||||
{titleText}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{listBody}
|
||||
{listFooter}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TransferList.defaultProps = defaultProps;
|
||||
export default TransferList;
|
118
src/operation.js
118
src/operation.js
|
@ -1,60 +1,60 @@
|
|||
import React from 'react';
|
||||
import Button from 'bee-button';
|
||||
import Icon from 'bee-icon';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function noop() {
|
||||
}
|
||||
|
||||
const propTypes = {
|
||||
className: PropTypes.string,
|
||||
leftArrowText: PropTypes.string,
|
||||
rightArrowText: PropTypes.string,
|
||||
moveToLeft: PropTypes.func,
|
||||
moveToRight: PropTypes.func,
|
||||
leftActive: PropTypes.boolean,
|
||||
rightActive: PropTypes.boolean,
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
leftArrowText: '',
|
||||
rightArrowText: '',
|
||||
moveToLeft: noop,
|
||||
moveToRight: noop,
|
||||
};
|
||||
|
||||
class TransferOperation extends React.Component{
|
||||
|
||||
render() {
|
||||
const {
|
||||
moveToLeft,
|
||||
moveToRight,
|
||||
leftArrowText,
|
||||
rightArrowText,
|
||||
leftActive,
|
||||
rightActive,
|
||||
className,
|
||||
} = this.props;
|
||||
|
||||
const moveToLeftButton = (
|
||||
<Button size="sm" disabled={!leftActive} onClick={moveToLeft}>
|
||||
{<span><Icon type="uf-arrow-left" />{leftArrowText}</span>}
|
||||
</Button>
|
||||
);
|
||||
const moveToRightButton = (
|
||||
<Button size="sm" disabled={!rightActive} onClick={moveToRight}>
|
||||
{<span>{rightArrowText}<Icon type="uf-arrow-right" /></span>}
|
||||
</Button>
|
||||
);
|
||||
return (
|
||||
<div className={className}>
|
||||
{moveToLeftButton}
|
||||
{moveToRightButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TransferOperation.propsType = propTypes;
|
||||
TransferOperation.defaultProps = defaultProps;
|
||||
import React from 'react';
|
||||
import Button from 'bee-button';
|
||||
import Icon from 'bee-icon';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function noop() {
|
||||
}
|
||||
|
||||
const propTypes = {
|
||||
className: PropTypes.string,
|
||||
leftArrowText: PropTypes.string,
|
||||
rightArrowText: PropTypes.string,
|
||||
moveToLeft: PropTypes.func,
|
||||
moveToRight: PropTypes.func,
|
||||
leftActive: PropTypes.boolean,
|
||||
rightActive: PropTypes.boolean,
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
leftArrowText: '',
|
||||
rightArrowText: '',
|
||||
moveToLeft: noop,
|
||||
moveToRight: noop,
|
||||
};
|
||||
|
||||
class TransferOperation extends React.Component{
|
||||
|
||||
render() {
|
||||
const {
|
||||
moveToLeft,
|
||||
moveToRight,
|
||||
leftArrowText,
|
||||
rightArrowText,
|
||||
leftActive,
|
||||
rightActive,
|
||||
className,
|
||||
} = this.props;
|
||||
|
||||
const moveToLeftButton = (
|
||||
<Button size="sm" disabled={!leftActive} onClick={moveToLeft}>
|
||||
{<span><Icon type="uf-arrow-left" />{leftArrowText}</span>}
|
||||
</Button>
|
||||
);
|
||||
const moveToRightButton = (
|
||||
<Button size="sm" disabled={!rightActive} onClick={moveToRight}>
|
||||
{<span>{rightArrowText}<Icon type="uf-arrow-right" /></span>}
|
||||
</Button>
|
||||
);
|
||||
return (
|
||||
<div className={className}>
|
||||
{moveToLeftButton}
|
||||
{moveToRightButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TransferOperation.propsType = propTypes;
|
||||
TransferOperation.defaultProps = defaultProps;
|
||||
export default TransferOperation;
|
|
@ -44,6 +44,7 @@ class Search extends React.Component {
|
|||
return (
|
||||
<div>
|
||||
<FormControl
|
||||
size="sm"
|
||||
placeholder={placeholder}
|
||||
className={prefixCls}
|
||||
value={value}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* a little function to help us with reordering the result
|
||||
* @param {*} list
|
||||
* @param {*} targetKeys
|
||||
* @param {*} startIndex
|
||||
* @param {*} endIndex
|
||||
*/
|
||||
const reorder = (list,targetKeys, startIndex, endIndex) => {
|
||||
const result1 = Array.from(list);
|
||||
const [removed1] = result1.splice(startIndex, 1);
|
||||
result1.splice(endIndex, 0, removed1);
|
||||
|
||||
const result2 = Array.from(targetKeys);
|
||||
const [removed2] = result2.splice(startIndex, 1);
|
||||
result2.splice(endIndex, 0, removed2);
|
||||
|
||||
let result = {};
|
||||
result.dataArr = result1;
|
||||
result.targetKeyArr = result2;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves an item from one list to another list.
|
||||
* @param {*} source
|
||||
* @param {*} destination
|
||||
* @param {*} droppableSource
|
||||
* @param {*} droppableDestination
|
||||
* @param {*} targetKeys
|
||||
*/
|
||||
const move = (source, destination, droppableSource, droppableDestination, targetKeys) => {
|
||||
const sourceClone = Array.from(source);
|
||||
const destClone = Array.from(destination);
|
||||
const [removed] = sourceClone.splice(droppableSource.index, 1);
|
||||
|
||||
destClone.splice(droppableDestination.index, 0, removed);
|
||||
targetKeys.splice(droppableDestination.index, 0, removed.key);
|
||||
|
||||
const result = {};
|
||||
result[droppableSource.droppableId] = sourceClone;
|
||||
result[droppableDestination.droppableId] = destClone;
|
||||
result.newTargetKeys = targetKeys;
|
||||
return result;
|
||||
};
|
||||
|
||||
export { reorder, move }
|
Loading…
Reference in New Issue