UNPKG

10.1 kBJavaScriptView Raw
1import React from 'react';
2import Search from './search';
3import classNames from 'classnames';
4import Animate from 'bee-animate';
5import PureRenderMixin from './PureRenderMixin';
6import assign from 'object-assign';
7import { TransferItem } from './index';
8import Item from './item';
9import Checkbox from 'bee-checkbox';
10import Icon from 'bee-icon';
11import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
12import { KeyCode} from 'tinper-bee-core';
13
14function noop() {
15}
16
17const defaultProps = {
18 dataSource: [],
19 titleText: '',
20 showSearch: false,
21 render: noop,
22};
23function isRenderResultPlainObject(result) {
24 return result && !React.isValidElement(result) &&
25 Object.prototype.toString.call(result) === '[object Object]';
26}
27
28class TransferList extends React.Component {
29
30 constructor(props) {
31 super(props);
32 this.state = {
33 mounted: false,
34 };
35 }
36
37 componentDidMount() {
38 this.timer = setTimeout(() => {
39 this.setState({
40 mounted: true,
41 });
42 }, 0);
43 }
44
45 componentWillUnmount() {
46 clearTimeout(this.timer);
47 }
48
49 shouldComponentUpdate(...args) {
50 return PureRenderMixin.shouldComponentUpdate.apply(this, args);
51 }
52
53
54 matchFilter = (text,item) => {
55 //filter:搜索框中的内容
56 //filterOption:用户自定义的搜索过滤方法
57 const { filter, filterOption} = this.props;
58 if (filterOption) {
59 return filterOption(filter, item);
60 }
61 return text.indexOf(filter) >= 0;
62 }
63 /**
64 * 获取Checkbox状态
65 * @param {*} filteredDataSource dataSource中刨去设置为disabled的部分
66 */
67 getCheckStatus(filteredDataSource) {
68 const { checkedKeys } = this.props;
69 if (checkedKeys.length === 0) {
70 return 'none'; //全部未选
71 } else if (filteredDataSource.every(item => checkedKeys.indexOf(item.key) >= 0)) {
72 return 'all'; //全部已选
73 }
74 return 'part'; //部分已选
75 }
76
77 /**
78 * 点击list item,选中或取消选中
79 * @param selectedItem 选中的item的信息,和dataSource数据源中的item信息一致
80 */
81 handleSelect = (selectedItem) => {
82 // checkedKeys:已勾选的Keys数组
83 // result:是否已勾选,true:已勾选 false:未勾选
84 const { checkedKeys } = this.props;
85 const result = checkedKeys.some((key) => key === selectedItem.key);
86 this.props.handleSelect(selectedItem, result);
87 }
88
89 handleFilter = (e) => {
90 this.props.handleFilter(e);
91 }
92
93 handleClear = () => {
94 this.props.handleClear();
95 }
96 renderItem = (item) => {
97 const { render = noop } = this.props;
98 const renderResult = render(item);
99 const isRenderResultPlain = isRenderResultPlainObject(renderResult);
100 return {
101 renderedText: isRenderResultPlain ? renderResult.value : renderResult,
102 renderedEl: isRenderResultPlain ? renderResult.label : renderResult,
103 };
104 }
105 renderCheckbox({ prefixCls, filteredDataSource, checked, checkPart, disabled, checkable }) {
106 const checkAll = (!checkPart) && checked; //非半选 && 全选
107 prefixCls = "u"
108 const checkboxCls = classNames({
109 [`${prefixCls}-checkbox-indeterminate`]: checkPart,
110 [`${prefixCls}-checkbox-disabled`]: disabled,
111 });
112 return (
113 <span
114 className="u-checkbox-wrapper"
115 >
116 <Checkbox
117 onChange={() => this.props.handleSelectAll(filteredDataSource, checkAll)}
118 className={checkboxCls}
119 checked={checkAll}
120 />
121 </span>
122
123 );
124 }
125
126 onKeyDown = (event,provided,snapshot,item) => {
127 if (provided.dragHandleProps) {
128 provided.dragHandleProps.onKeyDown(event);
129 }
130
131 if (event.defaultPrevented) {
132 return;
133 }
134
135 if (snapshot.isDragging) {
136 return;
137 }
138
139 if (event.keyCode !== KeyCode.ENTER) {
140 return;
141 }
142
143 // 为了选择,我们使用此事件 we are using the event for selection
144 event.preventDefault();
145
146 this.performAction(event,item);
147 };
148
149
150 render() {
151 const { prefixCls, dataSource, titleText, filter, checkedKeys, lazy, filterOption,
152 body = noop, footer = noop, showSearch, render = noop, style, id, showCheckbox, draggable, droppableId, draggingItemId } = this.props;
153 let { searchPlaceholder, notFoundContent } = this.props;
154
155 // Custom Layout
156 const footerDom = footer(assign({}, this.props));
157 const bodyDom = body(assign({}, this.props));
158
159 const listCls = classNames(prefixCls, {
160 [`${prefixCls}-with-footer`]: !!footerDom,
161 [`${prefixCls}-draggable`]: !!draggable
162 });
163
164 const filteredDataSource = [];
165 const totalDataSource = [];
166 const showItems = dataSource.map((item,index) => {
167 if(!item){return}
168 const { renderedText, renderedEl } = this.renderItem(item);
169 if (filter && filter.trim() && !this.matchFilter(renderedText, item)) {
170 return null;
171 }
172
173 // all show items
174 totalDataSource.push(item);
175
176 if (!item.disabled) {
177 filteredDataSource.push(item);
178 }
179
180 const checked = checkedKeys.indexOf(item.key) >= 0;
181 return (
182 <Draggable key={item.key} index={index} draggableId={`${item.key}`} isDragDisabled={draggable ? item.disabled : !draggable}>
183 {(provided, snapshot) => (
184 <div
185 ref={provided.innerRef}
186 {...provided.draggableProps}
187 {...provided.dragHandleProps}
188 // onClick={(event) =>this.handleDrag(event, provided, snapshot, item)}
189 onKeyDown={(event) =>
190 this.onKeyDown(event, provided, snapshot, item)
191 }
192 // className={classnames({
193 // ...getClass(this.props,snapshot.isDragging).drag
194 // })}
195 style={{...provided.draggableProps.style}}>
196 <Item
197 // ref={provided.innerRef} //Error: provided.innerRef has not been provided with a HTMLElement
198 // key={item.key}
199 item={item}
200 lazy={lazy}
201 render={render}
202 renderedText={renderedText}
203 renderedEl={renderedEl}
204 filter={filter}
205 filterOption={filterOption}
206 checked={checked}
207 checkedKeys={checkedKeys}
208 prefixCls={prefixCls}
209 onClick={this.handleSelect}
210 showCheckbox={showCheckbox}
211 isMultiDragSource={draggingItemId === item.key}
212 draggingItemId={draggingItemId}
213 />
214 </div>
215 )}
216 </Draggable>)
217 });
218
219 let unit = '';
220 const antLocale = this.context.antLocale;
221 if (antLocale && antLocale.Transfer) {
222 const transferLocale = antLocale.Transfer;
223 unit = dataSource.length > 1 ? transferLocale.itemsUnit : transferLocale.itemUnit;
224 searchPlaceholder = searchPlaceholder || transferLocale.searchPlaceholder;
225 notFoundContent = notFoundContent || transferLocale.notFoundContent;
226 }
227
228 const checkStatus = this.getCheckStatus(filteredDataSource);
229 const outerPrefixCls = prefixCls.replace('-list', '');
230 const search = showSearch ? (
231 <div className={`${prefixCls}-body-search-wrapper`}>
232 <Search
233 prefixCls={`${prefixCls}-search`}
234 onChange={this.handleFilter}
235 handleClear={this.handleClear}
236 placeholder={searchPlaceholder}
237 value={filter}
238 />
239 </div>
240 ) : null;
241
242 const listBody = bodyDom || (
243 <div className={showSearch ? `${prefixCls}-body ${prefixCls}-body-with-search` : `${prefixCls}-body`}>
244 {search}
245 <Droppable droppableId={`droppable_${id}`} direction='vertical' isDropDisabled={!draggable}>
246 {(provided, snapshot) => (
247 <div ref={provided.innerRef} key={id} className={`${prefixCls}-content`}>
248 <div style={{display:'none'}}>{provided.placeholder}</div>
249 <Animate
250 component="ul"
251 transitionName={this.state.mounted ? `${prefixCls}-content-item-highlight` : ''}
252 transitionLeave={false}
253 >
254 {showItems}
255 </Animate>
256 <div className={`${prefixCls}-delete-selected ${snapshot.isDraggingOver && droppableId === 'droppable_2'? 'show': ''}`}>
257 <div className={`${prefixCls}-del-btn`}>
258 <Icon type="uf-arrow-down-2"></Icon>
259 <span>移除已选</span>
260 </div>
261 </div>
262 </div>
263 )}
264 </Droppable>
265 <div className={`${prefixCls}-body-not-found ${dataSource.length == 0? "show" : ""}`}>
266 {notFoundContent}
267 </div>
268 </div>
269 );
270
271 const listFooter = footerDom ? (
272 <div className={`${prefixCls}-footer`}>
273 {footerDom}
274 </div>
275 ) : null;
276
277 const renderedCheckbox = this.renderCheckbox({
278 prefixCls: outerPrefixCls,
279 checked: checkStatus === 'all',
280 checkPart: checkStatus === 'part',
281 checkable: <span className={`${outerPrefixCls}-checkbox-inner`} />,
282 filteredDataSource,
283 disabled: false,
284 });
285
286 return (
287 <div className={listCls} style={style}>
288 <div className={`${prefixCls}-header`}>
289 {showCheckbox ? renderedCheckbox : ''}
290 <span className={`${prefixCls}-header-selected`}>
291 <span>
292 {(checkedKeys.length > 0 ? `${checkedKeys.length}/` : '') + totalDataSource.length} {unit}
293 </span>
294 <span className={`${prefixCls}-header-title`}>
295 {titleText}
296 </span>
297 </span>
298 </div>
299 {listBody}
300 {listFooter}
301 </div>
302 );
303 }
304}
305
306TransferList.defaultProps = defaultProps;
307export default TransferList;
\No newline at end of file