index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { createVNode as _createVNode, resolveDirective as _resolveDirective } from "vue";
  4. import { computed, watchEffect, defineComponent, ref, watch, toRaw } from 'vue';
  5. import PropTypes from '../_util/vue-types';
  6. import { getPropsSlot } from '../_util/props-util';
  7. import classNames from '../_util/classNames';
  8. import List from './list';
  9. import Operation from './operation';
  10. import LocaleReceiver from '../locale-provider/LocaleReceiver';
  11. import defaultLocale from '../locale/en_US';
  12. import { withInstall, stringType, arrayType, someType, booleanType, objectType, functionType } from '../_util/type';
  13. import useConfigInject from '../config-provider/hooks/useConfigInject';
  14. import { FormItemInputContext, useInjectFormItemContext } from '../form/FormItemContext';
  15. import { getStatusClassNames, getMergedStatus } from '../_util/statusUtils';
  16. import { groupKeysMap, groupDisabledKeysMap } from '../_util/transKeys';
  17. // CSSINJS
  18. import useStyle from './style';
  19. export const transferProps = () => ({
  20. id: String,
  21. prefixCls: String,
  22. dataSource: arrayType([]),
  23. disabled: booleanType(),
  24. targetKeys: arrayType(),
  25. selectedKeys: arrayType(),
  26. render: functionType(),
  27. listStyle: someType([Function, Object], () => ({})),
  28. operationStyle: objectType(undefined),
  29. titles: arrayType(),
  30. operations: arrayType(),
  31. showSearch: booleanType(false),
  32. filterOption: functionType(),
  33. searchPlaceholder: String,
  34. notFoundContent: PropTypes.any,
  35. locale: objectType(),
  36. rowKey: functionType(),
  37. showSelectAll: booleanType(),
  38. selectAllLabels: arrayType(),
  39. children: functionType(),
  40. oneWay: booleanType(),
  41. pagination: someType([Object, Boolean]),
  42. status: stringType(),
  43. onChange: functionType(),
  44. onSelectChange: functionType(),
  45. onSearch: functionType(),
  46. onScroll: functionType(),
  47. 'onUpdate:targetKeys': functionType(),
  48. 'onUpdate:selectedKeys': functionType()
  49. });
  50. const Transfer = defineComponent({
  51. compatConfig: {
  52. MODE: 3
  53. },
  54. name: 'ATransfer',
  55. inheritAttrs: false,
  56. props: transferProps(),
  57. slots: Object,
  58. // emits: ['update:targetKeys', 'update:selectedKeys', 'change', 'search', 'scroll', 'selectChange'],
  59. setup(props, _ref) {
  60. let {
  61. emit,
  62. attrs,
  63. slots,
  64. expose
  65. } = _ref;
  66. const {
  67. configProvider,
  68. prefixCls,
  69. direction
  70. } = useConfigInject('transfer', props);
  71. // style
  72. const [wrapSSR, hashId] = useStyle(prefixCls);
  73. const sourceSelectedKeys = ref([]);
  74. const targetSelectedKeys = ref([]);
  75. const formItemContext = useInjectFormItemContext();
  76. const formItemInputContext = FormItemInputContext.useInject();
  77. const mergedStatus = computed(() => getMergedStatus(formItemInputContext.status, props.status));
  78. watch(() => props.selectedKeys, () => {
  79. var _a, _b;
  80. sourceSelectedKeys.value = ((_a = props.selectedKeys) === null || _a === void 0 ? void 0 : _a.filter(key => props.targetKeys.indexOf(key) === -1)) || [];
  81. targetSelectedKeys.value = ((_b = props.selectedKeys) === null || _b === void 0 ? void 0 : _b.filter(key => props.targetKeys.indexOf(key) > -1)) || [];
  82. }, {
  83. immediate: true
  84. });
  85. const getLocale = (transferLocale, renderEmpty) => {
  86. // Keep old locale props still working.
  87. const oldLocale = {
  88. notFoundContent: renderEmpty('Transfer')
  89. };
  90. const notFoundContent = getPropsSlot(slots, props, 'notFoundContent');
  91. if (notFoundContent) {
  92. oldLocale.notFoundContent = notFoundContent;
  93. }
  94. if (props.searchPlaceholder !== undefined) {
  95. oldLocale.searchPlaceholder = props.searchPlaceholder;
  96. }
  97. return _extends(_extends(_extends({}, transferLocale), oldLocale), props.locale);
  98. };
  99. const moveTo = direction => {
  100. const {
  101. targetKeys = [],
  102. dataSource = []
  103. } = props;
  104. const moveKeys = direction === 'right' ? sourceSelectedKeys.value : targetSelectedKeys.value;
  105. const dataSourceDisabledKeysMap = groupDisabledKeysMap(dataSource);
  106. // filter the disabled options
  107. const newMoveKeys = moveKeys.filter(key => !dataSourceDisabledKeysMap.has(key));
  108. const newMoveKeysMap = groupKeysMap(newMoveKeys);
  109. // move items to target box
  110. const newTargetKeys = direction === 'right' ? newMoveKeys.concat(targetKeys) : targetKeys.filter(targetKey => !newMoveKeysMap.has(targetKey));
  111. // empty checked keys
  112. const oppositeDirection = direction === 'right' ? 'left' : 'right';
  113. direction === 'right' ? sourceSelectedKeys.value = [] : targetSelectedKeys.value = [];
  114. emit('update:targetKeys', newTargetKeys);
  115. handleSelectChange(oppositeDirection, []);
  116. emit('change', newTargetKeys, direction, newMoveKeys);
  117. formItemContext.onFieldChange();
  118. };
  119. const moveToLeft = () => {
  120. moveTo('left');
  121. };
  122. const moveToRight = () => {
  123. moveTo('right');
  124. };
  125. const onItemSelectAll = (direction, selectedKeys) => {
  126. handleSelectChange(direction, selectedKeys);
  127. };
  128. const onLeftItemSelectAll = selectedKeys => {
  129. return onItemSelectAll('left', selectedKeys);
  130. };
  131. const onRightItemSelectAll = selectedKeys => {
  132. return onItemSelectAll('right', selectedKeys);
  133. };
  134. const handleSelectChange = (direction, holder) => {
  135. if (direction === 'left') {
  136. if (!props.selectedKeys) {
  137. sourceSelectedKeys.value = holder;
  138. }
  139. emit('update:selectedKeys', [...holder, ...targetSelectedKeys.value]);
  140. emit('selectChange', holder, toRaw(targetSelectedKeys.value));
  141. } else {
  142. if (!props.selectedKeys) {
  143. targetSelectedKeys.value = holder;
  144. }
  145. emit('update:selectedKeys', [...holder, ...sourceSelectedKeys.value]);
  146. emit('selectChange', toRaw(sourceSelectedKeys.value), holder);
  147. }
  148. };
  149. const handleFilter = (direction, e) => {
  150. const value = e.target.value;
  151. emit('search', direction, value);
  152. };
  153. const handleLeftFilter = e => {
  154. handleFilter('left', e);
  155. };
  156. const handleRightFilter = e => {
  157. handleFilter('right', e);
  158. };
  159. const handleClear = direction => {
  160. emit('search', direction, '');
  161. };
  162. const handleLeftClear = () => {
  163. handleClear('left');
  164. };
  165. const handleRightClear = () => {
  166. handleClear('right');
  167. };
  168. const onItemSelect = (direction, selectedKey, checked) => {
  169. const holder = direction === 'left' ? [...sourceSelectedKeys.value] : [...targetSelectedKeys.value];
  170. const index = holder.indexOf(selectedKey);
  171. if (index > -1) {
  172. holder.splice(index, 1);
  173. }
  174. if (checked) {
  175. holder.push(selectedKey);
  176. }
  177. handleSelectChange(direction, holder);
  178. };
  179. const onLeftItemSelect = (selectedKey, checked) => {
  180. return onItemSelect('left', selectedKey, checked);
  181. };
  182. const onRightItemSelect = (selectedKey, checked) => {
  183. return onItemSelect('right', selectedKey, checked);
  184. };
  185. const onRightItemRemove = targetedKeys => {
  186. const {
  187. targetKeys = []
  188. } = props;
  189. const newTargetKeys = targetKeys.filter(key => !targetedKeys.includes(key));
  190. emit('update:targetKeys', newTargetKeys);
  191. emit('change', newTargetKeys, 'left', [...targetedKeys]);
  192. };
  193. const handleScroll = (direction, e) => {
  194. emit('scroll', direction, e);
  195. };
  196. const handleLeftScroll = e => {
  197. handleScroll('left', e);
  198. };
  199. const handleRightScroll = e => {
  200. handleScroll('right', e);
  201. };
  202. const handleListStyle = (listStyle, direction) => {
  203. if (typeof listStyle === 'function') {
  204. return listStyle({
  205. direction
  206. });
  207. }
  208. return listStyle;
  209. };
  210. const leftDataSource = ref([]);
  211. const rightDataSource = ref([]);
  212. watchEffect(() => {
  213. const {
  214. dataSource,
  215. rowKey,
  216. targetKeys = []
  217. } = props;
  218. const ld = [];
  219. const rd = new Array(targetKeys.length);
  220. const targetKeysMap = groupKeysMap(targetKeys);
  221. dataSource.forEach(record => {
  222. if (rowKey) {
  223. record.key = rowKey(record);
  224. }
  225. // rightData should be ordered by targetKeys
  226. // leftData should be ordered by dataSource
  227. if (targetKeysMap.has(record.key)) {
  228. rd[targetKeysMap.get(record.key)] = record;
  229. } else {
  230. ld.push(record);
  231. }
  232. });
  233. leftDataSource.value = ld;
  234. rightDataSource.value = rd;
  235. });
  236. expose({
  237. handleSelectChange
  238. });
  239. const renderTransfer = transferLocale => {
  240. var _a, _b, _c, _d, _e, _f;
  241. const {
  242. disabled,
  243. operations = [],
  244. showSearch,
  245. listStyle,
  246. operationStyle,
  247. filterOption,
  248. showSelectAll,
  249. selectAllLabels = [],
  250. oneWay,
  251. pagination,
  252. id = formItemContext.id.value
  253. } = props;
  254. const {
  255. class: className,
  256. style
  257. } = attrs;
  258. const children = slots.children;
  259. const mergedPagination = !children && pagination;
  260. const renderEmpty = configProvider.renderEmpty;
  261. const locale = getLocale(transferLocale, renderEmpty);
  262. const {
  263. footer
  264. } = slots;
  265. const renderItem = props.render || slots.render;
  266. const leftActive = targetSelectedKeys.value.length > 0;
  267. const rightActive = sourceSelectedKeys.value.length > 0;
  268. const cls = classNames(prefixCls.value, className, {
  269. [`${prefixCls.value}-disabled`]: disabled,
  270. [`${prefixCls.value}-customize-list`]: !!children,
  271. [`${prefixCls.value}-rtl`]: direction.value === 'rtl'
  272. }, getStatusClassNames(prefixCls.value, mergedStatus.value, formItemInputContext.hasFeedback), hashId.value);
  273. const titles = props.titles;
  274. const leftTitle = (_c = (_a = titles && titles[0]) !== null && _a !== void 0 ? _a : (_b = slots.leftTitle) === null || _b === void 0 ? void 0 : _b.call(slots)) !== null && _c !== void 0 ? _c : (locale.titles || ['', ''])[0];
  275. const rightTitle = (_f = (_d = titles && titles[1]) !== null && _d !== void 0 ? _d : (_e = slots.rightTitle) === null || _e === void 0 ? void 0 : _e.call(slots)) !== null && _f !== void 0 ? _f : (locale.titles || ['', ''])[1];
  276. return _createVNode("div", _objectSpread(_objectSpread({}, attrs), {}, {
  277. "class": cls,
  278. "style": style,
  279. "id": id
  280. }), [_createVNode(List, _objectSpread({
  281. "key": "leftList",
  282. "prefixCls": `${prefixCls.value}-list`,
  283. "dataSource": leftDataSource.value,
  284. "filterOption": filterOption,
  285. "style": handleListStyle(listStyle, 'left'),
  286. "checkedKeys": sourceSelectedKeys.value,
  287. "handleFilter": handleLeftFilter,
  288. "handleClear": handleLeftClear,
  289. "onItemSelect": onLeftItemSelect,
  290. "onItemSelectAll": onLeftItemSelectAll,
  291. "renderItem": renderItem,
  292. "showSearch": showSearch,
  293. "renderList": children,
  294. "onScroll": handleLeftScroll,
  295. "disabled": disabled,
  296. "direction": direction.value === 'rtl' ? 'right' : 'left',
  297. "showSelectAll": showSelectAll,
  298. "selectAllLabel": selectAllLabels[0] || slots.leftSelectAllLabel,
  299. "pagination": mergedPagination
  300. }, locale), {
  301. titleText: () => leftTitle,
  302. footer
  303. }), _createVNode(Operation, {
  304. "key": "operation",
  305. "class": `${prefixCls.value}-operation`,
  306. "rightActive": rightActive,
  307. "rightArrowText": operations[0],
  308. "moveToRight": moveToRight,
  309. "leftActive": leftActive,
  310. "leftArrowText": operations[1],
  311. "moveToLeft": moveToLeft,
  312. "style": operationStyle,
  313. "disabled": disabled,
  314. "direction": direction.value,
  315. "oneWay": oneWay
  316. }, null), _createVNode(List, _objectSpread({
  317. "key": "rightList",
  318. "prefixCls": `${prefixCls.value}-list`,
  319. "dataSource": rightDataSource.value,
  320. "filterOption": filterOption,
  321. "style": handleListStyle(listStyle, 'right'),
  322. "checkedKeys": targetSelectedKeys.value,
  323. "handleFilter": handleRightFilter,
  324. "handleClear": handleRightClear,
  325. "onItemSelect": onRightItemSelect,
  326. "onItemSelectAll": onRightItemSelectAll,
  327. "onItemRemove": onRightItemRemove,
  328. "renderItem": renderItem,
  329. "showSearch": showSearch,
  330. "renderList": children,
  331. "onScroll": handleRightScroll,
  332. "disabled": disabled,
  333. "direction": direction.value === 'rtl' ? 'left' : 'right',
  334. "showSelectAll": showSelectAll,
  335. "selectAllLabel": selectAllLabels[1] || slots.rightSelectAllLabel,
  336. "showRemove": oneWay,
  337. "pagination": mergedPagination
  338. }, locale), {
  339. titleText: () => rightTitle,
  340. footer
  341. })]);
  342. };
  343. return () => wrapSSR(_createVNode(LocaleReceiver, {
  344. "componentName": "Transfer",
  345. "defaultLocale": defaultLocale.Transfer,
  346. "children": renderTransfer
  347. }, null));
  348. }
  349. });
  350. export default withInstall(Transfer);