UNPKG

20.9 kBJavaScriptView Raw
1import _toArray from "@babel/runtime/helpers/esm/toArray";
2import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
3import _extends from "@babel/runtime/helpers/esm/extends";
4import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
5import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
6import _typeof from "@babel/runtime/helpers/esm/typeof";
7import * as React from 'react';
8import { useState, useCallback, useMemo } from 'react';
9import DownOutlined from "@ant-design/icons/es/icons/DownOutlined";
10import { convertDataToEntities } from "rc-tree/es/utils/treeUtil";
11import { conductCheck } from "rc-tree/es/utils/conductUtil";
12import { arrAdd, arrDel } from "rc-tree/es/util";
13import { INTERNAL_COL_DEFINE } from 'rc-table';
14import useMergedState from "rc-util/es/hooks/useMergedState";
15import Checkbox from '../../checkbox';
16import Dropdown from '../../dropdown';
17import Menu from '../../menu';
18import Radio from '../../radio';
19import devWarning from '../../_util/devWarning'; // TODO: warning if use ajax!!!
20
21export var SELECTION_ALL = 'SELECT_ALL';
22export var SELECTION_INVERT = 'SELECT_INVERT';
23export var SELECTION_NONE = 'SELECT_NONE';
24
25function getFixedType(column) {
26 return column && column.fixed;
27}
28
29function flattenData(data, childrenColumnName) {
30 var list = [];
31 (data || []).forEach(function (record) {
32 list.push(record);
33
34 if (record && _typeof(record) === 'object' && childrenColumnName in record) {
35 list = [].concat(_toConsumableArray(list), _toConsumableArray(flattenData(record[childrenColumnName], childrenColumnName)));
36 }
37 });
38 return list;
39}
40
41export default function useSelection(rowSelection, config) {
42 var _ref = rowSelection || {},
43 preserveSelectedRowKeys = _ref.preserveSelectedRowKeys,
44 selectedRowKeys = _ref.selectedRowKeys,
45 defaultSelectedRowKeys = _ref.defaultSelectedRowKeys,
46 getCheckboxProps = _ref.getCheckboxProps,
47 onSelectionChange = _ref.onChange,
48 onSelect = _ref.onSelect,
49 onSelectAll = _ref.onSelectAll,
50 onSelectInvert = _ref.onSelectInvert,
51 onSelectNone = _ref.onSelectNone,
52 onSelectMultiple = _ref.onSelectMultiple,
53 selectionColWidth = _ref.columnWidth,
54 selectionType = _ref.type,
55 selections = _ref.selections,
56 fixed = _ref.fixed,
57 customizeRenderCell = _ref.renderCell,
58 hideSelectAll = _ref.hideSelectAll,
59 _ref$checkStrictly = _ref.checkStrictly,
60 checkStrictly = _ref$checkStrictly === void 0 ? true : _ref$checkStrictly;
61
62 var prefixCls = config.prefixCls,
63 data = config.data,
64 pageData = config.pageData,
65 getRecordByKey = config.getRecordByKey,
66 getRowKey = config.getRowKey,
67 expandType = config.expandType,
68 childrenColumnName = config.childrenColumnName,
69 tableLocale = config.locale,
70 expandIconColumnIndex = config.expandIconColumnIndex,
71 getPopupContainer = config.getPopupContainer; // ========================= Keys =========================
72
73 var _useMergedState = useMergedState(selectedRowKeys || defaultSelectedRowKeys || [], {
74 value: selectedRowKeys
75 }),
76 _useMergedState2 = _slicedToArray(_useMergedState, 2),
77 mergedSelectedKeys = _useMergedState2[0],
78 setMergedSelectedKeys = _useMergedState2[1]; // ======================== Caches ========================
79
80
81 var preserveRecordsRef = React.useRef(new Map());
82 var updatePreserveRecordsCache = useCallback(function (keys) {
83 if (preserveSelectedRowKeys) {
84 var newCache = new Map(); // Keep key if mark as preserveSelectedRowKeys
85
86 keys.forEach(function (key) {
87 var record = getRecordByKey(key);
88
89 if (!record && preserveRecordsRef.current.has(key)) {
90 record = preserveRecordsRef.current.get(key);
91 }
92
93 newCache.set(key, record);
94 }); // Refresh to new cache
95
96 preserveRecordsRef.current = newCache;
97 }
98 }, [getRecordByKey, preserveSelectedRowKeys]); // Update cache with selectedKeys
99
100 React.useEffect(function () {
101 updatePreserveRecordsCache(mergedSelectedKeys);
102 }, [mergedSelectedKeys]);
103
104 var _useMemo = useMemo(function () {
105 return checkStrictly ? {
106 keyEntities: null
107 } : convertDataToEntities(data, {
108 externalGetKey: getRowKey,
109 childrenPropName: childrenColumnName
110 });
111 }, [data, getRowKey, checkStrictly, childrenColumnName]),
112 keyEntities = _useMemo.keyEntities; // Get flatten data
113
114
115 var flattedData = useMemo(function () {
116 return flattenData(pageData, childrenColumnName);
117 }, [pageData, childrenColumnName]); // Get all checkbox props
118
119 var checkboxPropsMap = useMemo(function () {
120 var map = new Map();
121 flattedData.forEach(function (record, index) {
122 var key = getRowKey(record, index);
123 var checkboxProps = (getCheckboxProps ? getCheckboxProps(record) : null) || {};
124 map.set(key, checkboxProps);
125
126 if (process.env.NODE_ENV !== 'production' && ('checked' in checkboxProps || 'defaultChecked' in checkboxProps)) {
127 devWarning(false, 'Table', 'Do not set `checked` or `defaultChecked` in `getCheckboxProps`. Please use `selectedRowKeys` instead.');
128 }
129 });
130 return map;
131 }, [flattedData, getRowKey, getCheckboxProps]);
132 var isCheckboxDisabled = useCallback(function (r) {
133 var _a;
134
135 return !!((_a = checkboxPropsMap.get(getRowKey(r))) === null || _a === void 0 ? void 0 : _a.disabled);
136 }, [checkboxPropsMap, getRowKey]);
137
138 var _useMemo2 = useMemo(function () {
139 if (checkStrictly) {
140 return [mergedSelectedKeys || [], []];
141 }
142
143 var _conductCheck = conductCheck(mergedSelectedKeys, true, keyEntities, isCheckboxDisabled),
144 checkedKeys = _conductCheck.checkedKeys,
145 halfCheckedKeys = _conductCheck.halfCheckedKeys;
146
147 return [checkedKeys || [], halfCheckedKeys];
148 }, [mergedSelectedKeys, checkStrictly, keyEntities, isCheckboxDisabled]),
149 _useMemo3 = _slicedToArray(_useMemo2, 2),
150 derivedSelectedKeys = _useMemo3[0],
151 derivedHalfSelectedKeys = _useMemo3[1];
152
153 var derivedSelectedKeySet = useMemo(function () {
154 var keys = selectionType === 'radio' ? derivedSelectedKeys.slice(0, 1) : derivedSelectedKeys;
155 return new Set(keys);
156 }, [derivedSelectedKeys, selectionType]);
157 var derivedHalfSelectedKeySet = useMemo(function () {
158 return selectionType === 'radio' ? new Set() : new Set(derivedHalfSelectedKeys);
159 }, [derivedHalfSelectedKeys, selectionType]); // Save last selected key to enable range selection
160
161 var _useState = useState(null),
162 _useState2 = _slicedToArray(_useState, 2),
163 lastSelectedKey = _useState2[0],
164 setLastSelectedKey = _useState2[1]; // Reset if rowSelection reset
165
166
167 React.useEffect(function () {
168 if (!rowSelection) {
169 setMergedSelectedKeys([]);
170 }
171 }, [!!rowSelection]);
172 var setSelectedKeys = useCallback(function (keys) {
173 var availableKeys;
174 var records;
175 updatePreserveRecordsCache(keys);
176
177 if (preserveSelectedRowKeys) {
178 availableKeys = keys;
179 records = keys.map(function (key) {
180 return preserveRecordsRef.current.get(key);
181 });
182 } else {
183 // Filter key which not exist in the `dataSource`
184 availableKeys = [];
185 records = [];
186 keys.forEach(function (key) {
187 var record = getRecordByKey(key);
188
189 if (record !== undefined) {
190 availableKeys.push(key);
191 records.push(record);
192 }
193 });
194 }
195
196 setMergedSelectedKeys(availableKeys);
197 onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange(availableKeys, records);
198 }, [setMergedSelectedKeys, getRecordByKey, onSelectionChange, preserveSelectedRowKeys]); // ====================== Selections ======================
199 // Trigger single `onSelect` event
200
201 var triggerSingleSelection = useCallback(function (key, selected, keys, event) {
202 if (onSelect) {
203 var rows = keys.map(function (k) {
204 return getRecordByKey(k);
205 });
206 onSelect(getRecordByKey(key), selected, rows, event);
207 }
208
209 setSelectedKeys(keys);
210 }, [onSelect, getRecordByKey, setSelectedKeys]);
211 var mergedSelections = useMemo(function () {
212 if (!selections || hideSelectAll) {
213 return null;
214 }
215
216 var selectionList = selections === true ? [SELECTION_ALL, SELECTION_INVERT, SELECTION_NONE] : selections;
217 return selectionList.map(function (selection) {
218 if (selection === SELECTION_ALL) {
219 return {
220 key: 'all',
221 text: tableLocale.selectionAll,
222 onSelect: function onSelect() {
223 setSelectedKeys(data.map(function (record, index) {
224 return getRowKey(record, index);
225 }));
226 }
227 };
228 }
229
230 if (selection === SELECTION_INVERT) {
231 return {
232 key: 'invert',
233 text: tableLocale.selectInvert,
234 onSelect: function onSelect() {
235 var keySet = new Set(derivedSelectedKeySet);
236 pageData.forEach(function (record, index) {
237 var key = getRowKey(record, index);
238
239 if (keySet.has(key)) {
240 keySet["delete"](key);
241 } else {
242 keySet.add(key);
243 }
244 });
245 var keys = Array.from(keySet);
246
247 if (onSelectInvert) {
248 devWarning(false, 'Table', '`onSelectInvert` will be removed in future. Please use `onChange` instead.');
249 onSelectInvert(keys);
250 }
251
252 setSelectedKeys(keys);
253 }
254 };
255 }
256
257 if (selection === SELECTION_NONE) {
258 return {
259 key: 'none',
260 text: tableLocale.selectNone,
261 onSelect: function onSelect() {
262 onSelectNone === null || onSelectNone === void 0 ? void 0 : onSelectNone();
263 setSelectedKeys([]);
264 }
265 };
266 }
267
268 return selection;
269 });
270 }, [selections, derivedSelectedKeySet, pageData, getRowKey, onSelectInvert, setSelectedKeys]); // ======================= Columns ========================
271
272 var transformColumns = useCallback(function (columns) {
273 if (!rowSelection) {
274 return columns;
275 } // Support selection
276
277
278 var keySet = new Set(derivedSelectedKeySet); // Record key only need check with enabled
279
280 var recordKeys = flattedData.map(getRowKey).filter(function (key) {
281 return !checkboxPropsMap.get(key).disabled;
282 });
283 var checkedCurrentAll = recordKeys.every(function (key) {
284 return keySet.has(key);
285 });
286 var checkedCurrentSome = recordKeys.some(function (key) {
287 return keySet.has(key);
288 });
289
290 var onSelectAllChange = function onSelectAllChange() {
291 var changeKeys = [];
292
293 if (checkedCurrentAll) {
294 recordKeys.forEach(function (key) {
295 keySet["delete"](key);
296 changeKeys.push(key);
297 });
298 } else {
299 recordKeys.forEach(function (key) {
300 if (!keySet.has(key)) {
301 keySet.add(key);
302 changeKeys.push(key);
303 }
304 });
305 }
306
307 var keys = Array.from(keySet);
308 onSelectAll === null || onSelectAll === void 0 ? void 0 : onSelectAll(!checkedCurrentAll, keys.map(function (k) {
309 return getRecordByKey(k);
310 }), changeKeys.map(function (k) {
311 return getRecordByKey(k);
312 }));
313 setSelectedKeys(keys);
314 }; // ===================== Render =====================
315 // Title Cell
316
317
318 var title;
319
320 if (selectionType !== 'radio') {
321 var customizeSelections;
322
323 if (mergedSelections) {
324 var menu = /*#__PURE__*/React.createElement(Menu, {
325 getPopupContainer: getPopupContainer
326 }, mergedSelections.map(function (selection, index) {
327 var key = selection.key,
328 text = selection.text,
329 onSelectionClick = selection.onSelect;
330 return /*#__PURE__*/React.createElement(Menu.Item, {
331 key: key || index,
332 onClick: function onClick() {
333 onSelectionClick === null || onSelectionClick === void 0 ? void 0 : onSelectionClick(recordKeys);
334 }
335 }, text);
336 }));
337 customizeSelections = /*#__PURE__*/React.createElement("div", {
338 className: "".concat(prefixCls, "-selection-extra")
339 }, /*#__PURE__*/React.createElement(Dropdown, {
340 overlay: menu,
341 getPopupContainer: getPopupContainer
342 }, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(DownOutlined, null))));
343 }
344
345 var allDisabledData = flattedData.map(function (record, index) {
346 var key = getRowKey(record, index);
347 var checkboxProps = checkboxPropsMap.get(key) || {};
348 return _extends({
349 checked: keySet.has(key)
350 }, checkboxProps);
351 }).filter(function (_ref2) {
352 var disabled = _ref2.disabled;
353 return disabled;
354 });
355 var allDisabled = !!allDisabledData.length && allDisabledData.length === flattedData.length;
356 var allDisabledAndChecked = allDisabled && allDisabledData.every(function (_ref3) {
357 var checked = _ref3.checked;
358 return checked;
359 });
360 var allDisabledSomeChecked = allDisabled && allDisabledData.some(function (_ref4) {
361 var checked = _ref4.checked;
362 return checked;
363 });
364 title = !hideSelectAll && /*#__PURE__*/React.createElement("div", {
365 className: "".concat(prefixCls, "-selection")
366 }, /*#__PURE__*/React.createElement(Checkbox, {
367 checked: !allDisabled ? !!flattedData.length && checkedCurrentAll : allDisabledAndChecked,
368 indeterminate: !allDisabled ? !checkedCurrentAll && checkedCurrentSome : !allDisabledAndChecked && allDisabledSomeChecked,
369 onChange: onSelectAllChange,
370 disabled: flattedData.length === 0 || allDisabled,
371 skipGroup: true
372 }), customizeSelections);
373 } // Body Cell
374
375
376 var renderCell;
377
378 if (selectionType === 'radio') {
379 renderCell = function renderCell(_, record, index) {
380 var key = getRowKey(record, index);
381 var checked = keySet.has(key);
382 return {
383 node: /*#__PURE__*/React.createElement(Radio, _extends({}, checkboxPropsMap.get(key), {
384 checked: checked,
385 onClick: function onClick(e) {
386 return e.stopPropagation();
387 },
388 onChange: function onChange(event) {
389 if (!keySet.has(key)) {
390 triggerSingleSelection(key, true, [key], event.nativeEvent);
391 }
392 }
393 })),
394 checked: checked
395 };
396 };
397 } else {
398 renderCell = function renderCell(_, record, index) {
399 var _a;
400
401 var key = getRowKey(record, index);
402 var checked = keySet.has(key);
403 var indeterminate = derivedHalfSelectedKeySet.has(key);
404 var checkboxProps = checkboxPropsMap.get(key);
405 var mergedIndeterminate;
406
407 if (expandType === 'nest') {
408 mergedIndeterminate = indeterminate;
409 devWarning(typeof (checkboxProps === null || checkboxProps === void 0 ? void 0 : checkboxProps.indeterminate) !== 'boolean', 'Table', 'set `indeterminate` using `rowSelection.getCheckboxProps` is not allowed with tree structured dataSource.');
410 } else {
411 mergedIndeterminate = (_a = checkboxProps === null || checkboxProps === void 0 ? void 0 : checkboxProps.indeterminate) !== null && _a !== void 0 ? _a : indeterminate;
412 } // Record checked
413
414
415 return {
416 node: /*#__PURE__*/React.createElement(Checkbox, _extends({}, checkboxProps, {
417 indeterminate: mergedIndeterminate,
418 checked: checked,
419 skipGroup: true,
420 onClick: function onClick(e) {
421 return e.stopPropagation();
422 },
423 onChange: function onChange(_ref5) {
424 var nativeEvent = _ref5.nativeEvent;
425 var shiftKey = nativeEvent.shiftKey;
426 var startIndex = -1;
427 var endIndex = -1; // Get range of this
428
429 if (shiftKey && checkStrictly) {
430 var pointKeys = new Set([lastSelectedKey, key]);
431 recordKeys.some(function (recordKey, recordIndex) {
432 if (pointKeys.has(recordKey)) {
433 if (startIndex === -1) {
434 startIndex = recordIndex;
435 } else {
436 endIndex = recordIndex;
437 return true;
438 }
439 }
440
441 return false;
442 });
443 }
444
445 if (endIndex !== -1 && startIndex !== endIndex && checkStrictly) {
446 // Batch update selections
447 var rangeKeys = recordKeys.slice(startIndex, endIndex + 1);
448 var changedKeys = [];
449
450 if (checked) {
451 rangeKeys.forEach(function (recordKey) {
452 if (keySet.has(recordKey)) {
453 changedKeys.push(recordKey);
454 keySet["delete"](recordKey);
455 }
456 });
457 } else {
458 rangeKeys.forEach(function (recordKey) {
459 if (!keySet.has(recordKey)) {
460 changedKeys.push(recordKey);
461 keySet.add(recordKey);
462 }
463 });
464 }
465
466 var keys = Array.from(keySet);
467 onSelectMultiple === null || onSelectMultiple === void 0 ? void 0 : onSelectMultiple(!checked, keys.map(function (recordKey) {
468 return getRecordByKey(recordKey);
469 }), changedKeys.map(function (recordKey) {
470 return getRecordByKey(recordKey);
471 }));
472 setSelectedKeys(keys);
473 } else {
474 // Single record selected
475 var originCheckedKeys = derivedSelectedKeys;
476
477 if (checkStrictly) {
478 var checkedKeys = checked ? arrDel(originCheckedKeys, key) : arrAdd(originCheckedKeys, key);
479 triggerSingleSelection(key, !checked, checkedKeys, nativeEvent);
480 } else {
481 // Always fill first
482 var result = conductCheck([].concat(_toConsumableArray(originCheckedKeys), [key]), true, keyEntities, isCheckboxDisabled);
483 var _checkedKeys = result.checkedKeys,
484 halfCheckedKeys = result.halfCheckedKeys;
485 var nextCheckedKeys = _checkedKeys; // If remove, we do it again to correction
486
487 if (checked) {
488 var tempKeySet = new Set(_checkedKeys);
489 tempKeySet["delete"](key);
490 nextCheckedKeys = conductCheck(Array.from(tempKeySet), {
491 checked: false,
492 halfCheckedKeys: halfCheckedKeys
493 }, keyEntities, isCheckboxDisabled).checkedKeys;
494 }
495
496 triggerSingleSelection(key, !checked, nextCheckedKeys, nativeEvent);
497 }
498 }
499
500 setLastSelectedKey(key);
501 }
502 })),
503 checked: checked
504 };
505 };
506 }
507
508 var renderSelectionCell = function renderSelectionCell(_, record, index) {
509 var _renderCell = renderCell(_, record, index),
510 node = _renderCell.node,
511 checked = _renderCell.checked;
512
513 if (customizeRenderCell) {
514 return customizeRenderCell(checked, record, index, node);
515 }
516
517 return node;
518 }; // Columns
519
520
521 var selectionColumn = _defineProperty({
522 width: selectionColWidth,
523 className: "".concat(prefixCls, "-selection-column"),
524 title: rowSelection.columnTitle || title,
525 render: renderSelectionCell
526 }, INTERNAL_COL_DEFINE, {
527 className: "".concat(prefixCls, "-selection-col")
528 });
529
530 if (expandType === 'row' && columns.length && !expandIconColumnIndex) {
531 var _columns = _toArray(columns),
532 expandColumn = _columns[0],
533 restColumns = _columns.slice(1);
534
535 var selectionFixed = fixed || getFixedType(restColumns[0]);
536
537 if (selectionFixed) {
538 expandColumn.fixed = selectionFixed;
539 }
540
541 return [expandColumn, _extends(_extends({}, selectionColumn), {
542 fixed: selectionFixed
543 })].concat(_toConsumableArray(restColumns));
544 }
545
546 return [_extends(_extends({}, selectionColumn), {
547 fixed: fixed || getFixedType(columns[0])
548 })].concat(_toConsumableArray(columns));
549 }, [getRowKey, flattedData, rowSelection, derivedSelectedKeys, derivedSelectedKeySet, derivedHalfSelectedKeySet, selectionColWidth, mergedSelections, expandType, lastSelectedKey, checkboxPropsMap, onSelectMultiple, triggerSingleSelection, isCheckboxDisabled]);
550 return [transformColumns, derivedSelectedKeySet];
551}
\No newline at end of file