list.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  3. import { createTextVNode as _createTextVNode, Fragment as _Fragment, createVNode as _createVNode } from "vue";
  4. import classNames from '../_util/classNames';
  5. import PropTypes from '../_util/vue-types';
  6. import { isValidElement, splitAttrs, filterEmpty } from '../_util/props-util';
  7. import DownOutlined from "@ant-design/icons-vue/es/icons/DownOutlined";
  8. import Checkbox from '../checkbox';
  9. import Menu from '../menu';
  10. import Dropdown from '../dropdown';
  11. import Search from './search';
  12. import ListBody from './ListBody';
  13. import { watchEffect, computed, defineComponent, ref } from 'vue';
  14. import { stringType, arrayType, booleanType } from '../_util/type';
  15. import { groupKeysMap } from '../_util/transKeys';
  16. const defaultRender = () => null;
  17. function isRenderResultPlainObject(result) {
  18. return !!(result && !isValidElement(result) && Object.prototype.toString.call(result) === '[object Object]');
  19. }
  20. function getEnabledItemKeys(items) {
  21. return items.filter(data => !data.disabled).map(data => data.key);
  22. }
  23. export const transferListProps = {
  24. prefixCls: String,
  25. dataSource: arrayType([]),
  26. filter: String,
  27. filterOption: Function,
  28. checkedKeys: PropTypes.arrayOf(PropTypes.string),
  29. handleFilter: Function,
  30. handleClear: Function,
  31. renderItem: Function,
  32. showSearch: booleanType(false),
  33. searchPlaceholder: String,
  34. notFoundContent: PropTypes.any,
  35. itemUnit: String,
  36. itemsUnit: String,
  37. renderList: PropTypes.any,
  38. disabled: booleanType(),
  39. direction: stringType(),
  40. showSelectAll: booleanType(),
  41. remove: String,
  42. selectAll: String,
  43. selectCurrent: String,
  44. selectInvert: String,
  45. removeAll: String,
  46. removeCurrent: String,
  47. selectAllLabel: PropTypes.any,
  48. showRemove: booleanType(),
  49. pagination: PropTypes.any,
  50. onItemSelect: Function,
  51. onItemSelectAll: Function,
  52. onItemRemove: Function,
  53. onScroll: Function
  54. };
  55. export default defineComponent({
  56. compatConfig: {
  57. MODE: 3
  58. },
  59. name: 'TransferList',
  60. inheritAttrs: false,
  61. props: transferListProps,
  62. // emits: ['scroll', 'itemSelectAll', 'itemRemove', 'itemSelect'],
  63. slots: Object,
  64. setup(props, _ref) {
  65. let {
  66. attrs,
  67. slots
  68. } = _ref;
  69. const filterValue = ref('');
  70. const transferNode = ref();
  71. const defaultListBodyRef = ref();
  72. const renderListBody = (renderList, props) => {
  73. let bodyContent = renderList ? renderList(props) : null;
  74. const customize = !!bodyContent && filterEmpty(bodyContent).length > 0;
  75. if (!customize) {
  76. bodyContent = _createVNode(ListBody, _objectSpread(_objectSpread({}, props), {}, {
  77. "ref": defaultListBodyRef
  78. }), null);
  79. }
  80. return {
  81. customize,
  82. bodyContent
  83. };
  84. };
  85. const renderItemHtml = item => {
  86. const {
  87. renderItem = defaultRender
  88. } = props;
  89. const renderResult = renderItem(item);
  90. const isRenderResultPlain = isRenderResultPlainObject(renderResult);
  91. return {
  92. renderedText: isRenderResultPlain ? renderResult.value : renderResult,
  93. renderedEl: isRenderResultPlain ? renderResult.label : renderResult,
  94. item
  95. };
  96. };
  97. const filteredItems = ref([]);
  98. const filteredRenderItems = ref([]);
  99. watchEffect(() => {
  100. const fItems = [];
  101. const fRenderItems = [];
  102. props.dataSource.forEach(item => {
  103. const renderedItem = renderItemHtml(item);
  104. const {
  105. renderedText
  106. } = renderedItem;
  107. // Filter skip
  108. if (filterValue.value && filterValue.value.trim() && !matchFilter(renderedText, item)) {
  109. return null;
  110. }
  111. fItems.push(item);
  112. fRenderItems.push(renderedItem);
  113. });
  114. filteredItems.value = fItems;
  115. filteredRenderItems.value = fRenderItems;
  116. });
  117. const checkStatus = computed(() => {
  118. const {
  119. checkedKeys
  120. } = props;
  121. if (checkedKeys.length === 0) {
  122. return 'none';
  123. }
  124. const checkedKeysMap = groupKeysMap(checkedKeys);
  125. if (filteredItems.value.every(item => checkedKeysMap.has(item.key) || !!item.disabled)) {
  126. return 'all';
  127. }
  128. return 'part';
  129. });
  130. const enabledItemKeys = computed(() => {
  131. return getEnabledItemKeys(filteredItems.value);
  132. });
  133. const getNewSelectKeys = (keys, unCheckedKeys) => {
  134. return Array.from(new Set([...keys, ...props.checkedKeys])).filter(key => unCheckedKeys.indexOf(key) === -1);
  135. };
  136. const getCheckBox = _ref2 => {
  137. let {
  138. disabled,
  139. prefixCls
  140. } = _ref2;
  141. var _a;
  142. const checkedAll = checkStatus.value === 'all';
  143. const checkAllCheckbox = _createVNode(Checkbox, {
  144. "disabled": ((_a = props.dataSource) === null || _a === void 0 ? void 0 : _a.length) === 0 || disabled,
  145. "checked": checkedAll,
  146. "indeterminate": checkStatus.value === 'part',
  147. "class": `${prefixCls}-checkbox`,
  148. "onChange": () => {
  149. // Only select enabled items
  150. const keys = enabledItemKeys.value;
  151. props.onItemSelectAll(getNewSelectKeys(!checkedAll ? keys : [], checkedAll ? props.checkedKeys : []));
  152. }
  153. }, null);
  154. return checkAllCheckbox;
  155. };
  156. const handleFilter = e => {
  157. var _a;
  158. const {
  159. target: {
  160. value: filter
  161. }
  162. } = e;
  163. filterValue.value = filter;
  164. (_a = props.handleFilter) === null || _a === void 0 ? void 0 : _a.call(props, e);
  165. };
  166. const handleClear = e => {
  167. var _a;
  168. filterValue.value = '';
  169. (_a = props.handleClear) === null || _a === void 0 ? void 0 : _a.call(props, e);
  170. };
  171. const matchFilter = (text, item) => {
  172. const {
  173. filterOption
  174. } = props;
  175. if (filterOption) {
  176. return filterOption(filterValue.value, item);
  177. }
  178. return text.includes(filterValue.value);
  179. };
  180. const getSelectAllLabel = (selectedCount, totalCount) => {
  181. const {
  182. itemsUnit,
  183. itemUnit,
  184. selectAllLabel
  185. } = props;
  186. if (selectAllLabel) {
  187. return typeof selectAllLabel === 'function' ? selectAllLabel({
  188. selectedCount,
  189. totalCount
  190. }) : selectAllLabel;
  191. }
  192. const unit = totalCount > 1 ? itemsUnit : itemUnit;
  193. return _createVNode(_Fragment, null, [(selectedCount > 0 ? `${selectedCount}/` : '') + totalCount, _createTextVNode(" "), unit]);
  194. };
  195. const notFoundContentEle = computed(() => Array.isArray(props.notFoundContent) ? props.notFoundContent[props.direction === 'left' ? 0 : 1] : props.notFoundContent);
  196. const getListBody = (prefixCls, searchPlaceholder, checkedKeys, renderList, showSearch, disabled) => {
  197. const search = showSearch ? _createVNode("div", {
  198. "class": `${prefixCls}-body-search-wrapper`
  199. }, [_createVNode(Search, {
  200. "prefixCls": `${prefixCls}-search`,
  201. "onChange": handleFilter,
  202. "handleClear": handleClear,
  203. "placeholder": searchPlaceholder,
  204. "value": filterValue.value,
  205. "disabled": disabled
  206. }, null)]) : null;
  207. let bodyNode;
  208. const {
  209. onEvents
  210. } = splitAttrs(attrs);
  211. const {
  212. bodyContent,
  213. customize
  214. } = renderListBody(renderList, _extends(_extends(_extends({}, props), {
  215. filteredItems: filteredItems.value,
  216. filteredRenderItems: filteredRenderItems.value,
  217. selectedKeys: checkedKeys
  218. }), onEvents));
  219. // We should wrap customize list body in a classNamed div to use flex layout.
  220. if (customize) {
  221. bodyNode = _createVNode("div", {
  222. "class": `${prefixCls}-body-customize-wrapper`
  223. }, [bodyContent]);
  224. } else {
  225. bodyNode = filteredItems.value.length ? bodyContent : _createVNode("div", {
  226. "class": `${prefixCls}-body-not-found`
  227. }, [notFoundContentEle.value]);
  228. }
  229. return _createVNode("div", {
  230. "class": showSearch ? `${prefixCls}-body ${prefixCls}-body-with-search` : `${prefixCls}-body`,
  231. "ref": transferNode
  232. }, [search, bodyNode]);
  233. };
  234. return () => {
  235. var _a, _b;
  236. const {
  237. prefixCls,
  238. checkedKeys,
  239. disabled,
  240. showSearch,
  241. searchPlaceholder,
  242. selectAll,
  243. selectCurrent,
  244. selectInvert,
  245. removeAll,
  246. removeCurrent,
  247. renderList,
  248. onItemSelectAll,
  249. onItemRemove,
  250. showSelectAll = true,
  251. showRemove,
  252. pagination
  253. } = props;
  254. // Custom Layout
  255. const footerDom = (_a = slots.footer) === null || _a === void 0 ? void 0 : _a.call(slots, _extends({}, props));
  256. const listCls = classNames(prefixCls, {
  257. [`${prefixCls}-with-pagination`]: !!pagination,
  258. [`${prefixCls}-with-footer`]: !!footerDom
  259. });
  260. // ================================= List Body =================================
  261. const listBody = getListBody(prefixCls, searchPlaceholder, checkedKeys, renderList, showSearch, disabled);
  262. const listFooter = footerDom ? _createVNode("div", {
  263. "class": `${prefixCls}-footer`
  264. }, [footerDom]) : null;
  265. const checkAllCheckbox = !showRemove && !pagination && getCheckBox({
  266. disabled,
  267. prefixCls
  268. });
  269. let menu = null;
  270. if (showRemove) {
  271. menu = _createVNode(Menu, null, {
  272. default: () => [pagination && _createVNode(Menu.Item, {
  273. "key": "removeCurrent",
  274. "onClick": () => {
  275. const pageKeys = getEnabledItemKeys((defaultListBodyRef.value.items || []).map(entity => entity.item));
  276. onItemRemove === null || onItemRemove === void 0 ? void 0 : onItemRemove(pageKeys);
  277. }
  278. }, {
  279. default: () => [removeCurrent]
  280. }), _createVNode(Menu.Item, {
  281. "key": "removeAll",
  282. "onClick": () => {
  283. onItemRemove === null || onItemRemove === void 0 ? void 0 : onItemRemove(enabledItemKeys.value);
  284. }
  285. }, {
  286. default: () => [removeAll]
  287. })]
  288. });
  289. } else {
  290. menu = _createVNode(Menu, null, {
  291. default: () => [_createVNode(Menu.Item, {
  292. "key": "selectAll",
  293. "onClick": () => {
  294. const keys = enabledItemKeys.value;
  295. onItemSelectAll(getNewSelectKeys(keys, []));
  296. }
  297. }, {
  298. default: () => [selectAll]
  299. }), pagination && _createVNode(Menu.Item, {
  300. "onClick": () => {
  301. const pageKeys = getEnabledItemKeys((defaultListBodyRef.value.items || []).map(entity => entity.item));
  302. onItemSelectAll(getNewSelectKeys(pageKeys, []));
  303. }
  304. }, {
  305. default: () => [selectCurrent]
  306. }), _createVNode(Menu.Item, {
  307. "key": "selectInvert",
  308. "onClick": () => {
  309. let availableKeys;
  310. if (pagination) {
  311. availableKeys = getEnabledItemKeys((defaultListBodyRef.value.items || []).map(entity => entity.item));
  312. } else {
  313. availableKeys = enabledItemKeys.value;
  314. }
  315. const checkedKeySet = new Set(checkedKeys);
  316. const newCheckedKeys = [];
  317. const newUnCheckedKeys = [];
  318. availableKeys.forEach(key => {
  319. if (checkedKeySet.has(key)) {
  320. newUnCheckedKeys.push(key);
  321. } else {
  322. newCheckedKeys.push(key);
  323. }
  324. });
  325. onItemSelectAll(getNewSelectKeys(newCheckedKeys, newUnCheckedKeys));
  326. }
  327. }, {
  328. default: () => [selectInvert]
  329. })]
  330. });
  331. }
  332. const dropdown = _createVNode(Dropdown, {
  333. "class": `${prefixCls}-header-dropdown`,
  334. "overlay": menu,
  335. "disabled": disabled
  336. }, {
  337. default: () => [_createVNode(DownOutlined, null, null)]
  338. });
  339. return _createVNode("div", {
  340. "class": listCls,
  341. "style": attrs.style
  342. }, [_createVNode("div", {
  343. "class": `${prefixCls}-header`
  344. }, [showSelectAll ? _createVNode(_Fragment, null, [checkAllCheckbox, dropdown]) : null, _createVNode("span", {
  345. "class": `${prefixCls}-header-selected`
  346. }, [_createVNode("span", null, [getSelectAllLabel(checkedKeys.length, filteredItems.value.length)]), _createVNode("span", {
  347. "class": `${prefixCls}-header-title`
  348. }, [(_b = slots.titleText) === null || _b === void 0 ? void 0 : _b.call(slots)])])]), listBody, listFooter]);
  349. };
  350. }
  351. });