index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. "use client";
  2. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  3. import React, { useCallback, useContext } from 'react';
  4. import classNames from 'classnames';
  5. import useMultipleSelect from '../_util/hooks/useMultipleSelect';
  6. import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
  7. import { groupDisabledKeysMap, groupKeysMap } from '../_util/transKeys';
  8. import { devUseWarning } from '../_util/warning';
  9. import { ConfigContext } from '../config-provider';
  10. import DefaultRenderEmpty from '../config-provider/defaultRenderEmpty';
  11. import { FormItemInputContext } from '../form/context';
  12. import { useLocale } from '../locale';
  13. import defaultLocale from '../locale/en_US';
  14. import useData from './hooks/useData';
  15. import useSelection from './hooks/useSelection';
  16. import List from './list';
  17. import Operation from './operation';
  18. import Search from './search';
  19. import useStyle from './style';
  20. const Transfer = props => {
  21. const {
  22. dataSource,
  23. targetKeys = [],
  24. selectedKeys,
  25. selectAllLabels = [],
  26. operations = [],
  27. style = {},
  28. listStyle = {},
  29. locale = {},
  30. titles,
  31. disabled,
  32. showSearch = false,
  33. operationStyle,
  34. showSelectAll,
  35. oneWay,
  36. pagination,
  37. status: customStatus,
  38. prefixCls: customizePrefixCls,
  39. className,
  40. rootClassName,
  41. selectionsIcon,
  42. filterOption,
  43. render,
  44. footer,
  45. children,
  46. rowKey,
  47. onScroll,
  48. onChange,
  49. onSearch,
  50. onSelectChange
  51. } = props;
  52. const {
  53. getPrefixCls,
  54. renderEmpty,
  55. direction: dir,
  56. transfer
  57. } = useContext(ConfigContext);
  58. const prefixCls = getPrefixCls('transfer', customizePrefixCls);
  59. const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
  60. // Fill record with `key`
  61. const [mergedDataSource, leftDataSource, rightDataSource] = useData(dataSource, rowKey, targetKeys);
  62. // Get direction selected keys
  63. const [
  64. // Keys
  65. sourceSelectedKeys, targetSelectedKeys,
  66. // Setters
  67. setSourceSelectedKeys, setTargetSelectedKeys] = useSelection(leftDataSource, rightDataSource, selectedKeys);
  68. const [leftMultipleSelect, updateLeftPrevSelectedIndex] = useMultipleSelect(item => item.key);
  69. const [rightMultipleSelect, updateRightPrevSelectedIndex] = useMultipleSelect(item => item.key);
  70. if (process.env.NODE_ENV !== 'production') {
  71. const warning = devUseWarning('Transfer');
  72. process.env.NODE_ENV !== "production" ? warning(!pagination || !children, 'usage', '`pagination` not support customize render list.') : void 0;
  73. }
  74. const setStateKeys = useCallback((direction, keys) => {
  75. if (direction === 'left') {
  76. const nextKeys = typeof keys === 'function' ? keys(sourceSelectedKeys || []) : keys;
  77. setSourceSelectedKeys(nextKeys);
  78. } else {
  79. const nextKeys = typeof keys === 'function' ? keys(targetSelectedKeys || []) : keys;
  80. setTargetSelectedKeys(nextKeys);
  81. }
  82. }, [sourceSelectedKeys, targetSelectedKeys]);
  83. const setPrevSelectedIndex = (direction, value) => {
  84. const isLeftDirection = direction === 'left';
  85. const updatePrevSelectedIndex = isLeftDirection ? updateLeftPrevSelectedIndex : updateRightPrevSelectedIndex;
  86. updatePrevSelectedIndex(value);
  87. };
  88. const handleSelectChange = useCallback((direction, holder) => {
  89. if (direction === 'left') {
  90. onSelectChange === null || onSelectChange === void 0 ? void 0 : onSelectChange(holder, targetSelectedKeys);
  91. } else {
  92. onSelectChange === null || onSelectChange === void 0 ? void 0 : onSelectChange(sourceSelectedKeys, holder);
  93. }
  94. }, [sourceSelectedKeys, targetSelectedKeys]);
  95. const getTitles = transferLocale => {
  96. var _a;
  97. return (_a = titles !== null && titles !== void 0 ? titles : transferLocale.titles) !== null && _a !== void 0 ? _a : [];
  98. };
  99. const handleLeftScroll = e => {
  100. onScroll === null || onScroll === void 0 ? void 0 : onScroll('left', e);
  101. };
  102. const handleRightScroll = e => {
  103. onScroll === null || onScroll === void 0 ? void 0 : onScroll('right', e);
  104. };
  105. const moveTo = direction => {
  106. const moveKeys = direction === 'right' ? sourceSelectedKeys : targetSelectedKeys;
  107. const dataSourceDisabledKeysMap = groupDisabledKeysMap(mergedDataSource);
  108. // filter the disabled options
  109. const newMoveKeys = moveKeys.filter(key => !dataSourceDisabledKeysMap.has(key));
  110. const newMoveKeysMap = groupKeysMap(newMoveKeys);
  111. // move items to target box
  112. const newTargetKeys = direction === 'right' ? newMoveKeys.concat(targetKeys) : targetKeys.filter(targetKey => !newMoveKeysMap.has(targetKey));
  113. // empty checked keys
  114. const oppositeDirection = direction === 'right' ? 'left' : 'right';
  115. setStateKeys(oppositeDirection, []);
  116. handleSelectChange(oppositeDirection, []);
  117. onChange === null || onChange === void 0 ? void 0 : onChange(newTargetKeys, direction, newMoveKeys);
  118. };
  119. const moveToLeft = () => {
  120. moveTo('left');
  121. setPrevSelectedIndex('left', null);
  122. };
  123. const moveToRight = () => {
  124. moveTo('right');
  125. setPrevSelectedIndex('right', null);
  126. };
  127. const onItemSelectAll = (direction, keys, checkAll) => {
  128. setStateKeys(direction, prevKeys => {
  129. let mergedCheckedKeys = [];
  130. if (checkAll === 'replace') {
  131. mergedCheckedKeys = keys;
  132. } else if (checkAll) {
  133. // Merge current keys with origin key
  134. mergedCheckedKeys = Array.from(new Set([].concat(_toConsumableArray(prevKeys), _toConsumableArray(keys))));
  135. } else {
  136. const selectedKeysMap = groupKeysMap(keys);
  137. // Remove current keys from origin keys
  138. mergedCheckedKeys = prevKeys.filter(key => !selectedKeysMap.has(key));
  139. }
  140. handleSelectChange(direction, mergedCheckedKeys);
  141. return mergedCheckedKeys;
  142. });
  143. setPrevSelectedIndex(direction, null);
  144. };
  145. const onLeftItemSelectAll = (keys, checkAll) => {
  146. onItemSelectAll('left', keys, checkAll);
  147. };
  148. const onRightItemSelectAll = (keys, checkAll) => {
  149. onItemSelectAll('right', keys, checkAll);
  150. };
  151. const leftFilter = e => onSearch === null || onSearch === void 0 ? void 0 : onSearch('left', e.target.value);
  152. const rightFilter = e => onSearch === null || onSearch === void 0 ? void 0 : onSearch('right', e.target.value);
  153. const handleLeftClear = () => onSearch === null || onSearch === void 0 ? void 0 : onSearch('left', '');
  154. const handleRightClear = () => onSearch === null || onSearch === void 0 ? void 0 : onSearch('right', '');
  155. const handleSingleSelect = (direction, holder, selectedKey, checked, currentSelectedIndex) => {
  156. const isSelected = holder.has(selectedKey);
  157. if (isSelected) {
  158. holder.delete(selectedKey);
  159. setPrevSelectedIndex(direction, null);
  160. }
  161. if (checked) {
  162. holder.add(selectedKey);
  163. setPrevSelectedIndex(direction, currentSelectedIndex);
  164. }
  165. };
  166. const handleMultipleSelect = (direction, data, holder, currentSelectedIndex) => {
  167. const isLeftDirection = direction === 'left';
  168. const multipleSelect = isLeftDirection ? leftMultipleSelect : rightMultipleSelect;
  169. multipleSelect(currentSelectedIndex, data, holder);
  170. };
  171. const onItemSelect = (direction, selectedKey, checked, multiple) => {
  172. const isLeftDirection = direction === 'left';
  173. const holder = _toConsumableArray(isLeftDirection ? sourceSelectedKeys : targetSelectedKeys);
  174. const holderSet = new Set(holder);
  175. const data = _toConsumableArray(isLeftDirection ? leftDataSource : rightDataSource).filter(item => !(item === null || item === void 0 ? void 0 : item.disabled));
  176. const currentSelectedIndex = data.findIndex(item => item.key === selectedKey);
  177. // multiple select by hold down the shift key
  178. if (multiple && holder.length > 0) {
  179. handleMultipleSelect(direction, data, holderSet, currentSelectedIndex);
  180. } else {
  181. handleSingleSelect(direction, holderSet, selectedKey, checked, currentSelectedIndex);
  182. }
  183. const holderArr = Array.from(holderSet);
  184. handleSelectChange(direction, holderArr);
  185. if (!props.selectedKeys) {
  186. setStateKeys(direction, holderArr);
  187. }
  188. };
  189. const onLeftItemSelect = (selectedKey, checked, e) => {
  190. onItemSelect('left', selectedKey, checked, e === null || e === void 0 ? void 0 : e.shiftKey);
  191. };
  192. const onRightItemSelect = (selectedKey, checked, e) => {
  193. onItemSelect('right', selectedKey, checked, e === null || e === void 0 ? void 0 : e.shiftKey);
  194. };
  195. const onRightItemRemove = keys => {
  196. setStateKeys('right', []);
  197. onChange === null || onChange === void 0 ? void 0 : onChange(targetKeys.filter(key => !keys.includes(key)), 'left', _toConsumableArray(keys));
  198. };
  199. const handleListStyle = direction => {
  200. if (typeof listStyle === 'function') {
  201. return listStyle({
  202. direction
  203. });
  204. }
  205. return listStyle || {};
  206. };
  207. const formItemContext = useContext(FormItemInputContext);
  208. const {
  209. hasFeedback,
  210. status
  211. } = formItemContext;
  212. const getLocale = transferLocale => Object.assign(Object.assign(Object.assign({}, transferLocale), {
  213. notFoundContent: (renderEmpty === null || renderEmpty === void 0 ? void 0 : renderEmpty('Transfer')) || /*#__PURE__*/React.createElement(DefaultRenderEmpty, {
  214. componentName: "Transfer"
  215. })
  216. }), locale);
  217. const mergedStatus = getMergedStatus(status, customStatus);
  218. const mergedPagination = !children && pagination;
  219. const leftActive = rightDataSource.filter(d => targetSelectedKeys.includes(d.key) && !d.disabled).length > 0;
  220. const rightActive = leftDataSource.filter(d => sourceSelectedKeys.includes(d.key) && !d.disabled).length > 0;
  221. const cls = classNames(prefixCls, {
  222. [`${prefixCls}-disabled`]: disabled,
  223. [`${prefixCls}-customize-list`]: !!children,
  224. [`${prefixCls}-rtl`]: dir === 'rtl'
  225. }, getStatusClassNames(prefixCls, mergedStatus, hasFeedback), transfer === null || transfer === void 0 ? void 0 : transfer.className, className, rootClassName, hashId, cssVarCls);
  226. const [contextLocale] = useLocale('Transfer', defaultLocale.Transfer);
  227. const listLocale = getLocale(contextLocale);
  228. const [leftTitle, rightTitle] = getTitles(listLocale);
  229. const mergedSelectionsIcon = selectionsIcon !== null && selectionsIcon !== void 0 ? selectionsIcon : transfer === null || transfer === void 0 ? void 0 : transfer.selectionsIcon;
  230. return wrapCSSVar(/*#__PURE__*/React.createElement("div", {
  231. className: cls,
  232. style: Object.assign(Object.assign({}, transfer === null || transfer === void 0 ? void 0 : transfer.style), style)
  233. }, /*#__PURE__*/React.createElement(List, Object.assign({
  234. prefixCls: `${prefixCls}-list`,
  235. titleText: leftTitle,
  236. dataSource: leftDataSource,
  237. filterOption: filterOption,
  238. style: handleListStyle('left'),
  239. checkedKeys: sourceSelectedKeys,
  240. handleFilter: leftFilter,
  241. handleClear: handleLeftClear,
  242. onItemSelect: onLeftItemSelect,
  243. onItemSelectAll: onLeftItemSelectAll,
  244. render: render,
  245. showSearch: showSearch,
  246. renderList: children,
  247. footer: footer,
  248. onScroll: handleLeftScroll,
  249. disabled: disabled,
  250. direction: dir === 'rtl' ? 'right' : 'left',
  251. showSelectAll: showSelectAll,
  252. selectAllLabel: selectAllLabels[0],
  253. pagination: mergedPagination,
  254. selectionsIcon: mergedSelectionsIcon
  255. }, listLocale)), /*#__PURE__*/React.createElement(Operation, {
  256. className: `${prefixCls}-operation`,
  257. rightActive: rightActive,
  258. rightArrowText: operations[0],
  259. moveToRight: moveToRight,
  260. leftActive: leftActive,
  261. leftArrowText: operations[1],
  262. moveToLeft: moveToLeft,
  263. style: operationStyle,
  264. disabled: disabled,
  265. direction: dir,
  266. oneWay: oneWay
  267. }), /*#__PURE__*/React.createElement(List, Object.assign({
  268. prefixCls: `${prefixCls}-list`,
  269. titleText: rightTitle,
  270. dataSource: rightDataSource,
  271. filterOption: filterOption,
  272. style: handleListStyle('right'),
  273. checkedKeys: targetSelectedKeys,
  274. handleFilter: rightFilter,
  275. handleClear: handleRightClear,
  276. onItemSelect: onRightItemSelect,
  277. onItemSelectAll: onRightItemSelectAll,
  278. onItemRemove: onRightItemRemove,
  279. render: render,
  280. showSearch: showSearch,
  281. renderList: children,
  282. footer: footer,
  283. onScroll: handleRightScroll,
  284. disabled: disabled,
  285. direction: dir === 'rtl' ? 'left' : 'right',
  286. showSelectAll: showSelectAll,
  287. selectAllLabel: selectAllLabels[1],
  288. showRemove: oneWay,
  289. pagination: mergedPagination,
  290. selectionsIcon: mergedSelectionsIcon
  291. }, listLocale))));
  292. };
  293. if (process.env.NODE_ENV !== 'production') {
  294. Transfer.displayName = 'Transfer';
  295. }
  296. Transfer.List = List;
  297. Transfer.Search = Search;
  298. Transfer.Operation = Operation;
  299. export default Transfer;