index.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { createVNode as _createVNode } from "vue";
  4. import { isLeaf, toPathKey, toPathKeys, toPathValueStr, scrollIntoParentView } from '../utils/commonUtil';
  5. import useActive from './useActive';
  6. import useKeyboard from './useKeyboard';
  7. import { toPathOptions } from '../utils/treeUtil';
  8. import { computed, defineComponent, onMounted, ref, shallowRef, watch, watchEffect } from 'vue';
  9. import { useBaseProps } from '../../vc-select';
  10. import { useInjectCascader } from '../context';
  11. import Column, { FIX_LABEL } from './Column';
  12. export default defineComponent({
  13. compatConfig: {
  14. MODE: 3
  15. },
  16. name: 'OptionList',
  17. inheritAttrs: false,
  18. setup(_props, context) {
  19. const {
  20. attrs,
  21. slots
  22. } = context;
  23. const baseProps = useBaseProps();
  24. const containerRef = ref();
  25. const rtl = computed(() => baseProps.direction === 'rtl');
  26. const {
  27. options,
  28. values,
  29. halfValues,
  30. fieldNames,
  31. changeOnSelect,
  32. onSelect,
  33. searchOptions,
  34. dropdownPrefixCls,
  35. loadData,
  36. expandTrigger,
  37. customSlots
  38. } = useInjectCascader();
  39. const mergedPrefixCls = computed(() => dropdownPrefixCls.value || baseProps.prefixCls);
  40. // ========================= loadData =========================
  41. const loadingKeys = shallowRef([]);
  42. const internalLoadData = valueCells => {
  43. // Do not load when search
  44. if (!loadData.value || baseProps.searchValue) {
  45. return;
  46. }
  47. const optionList = toPathOptions(valueCells, options.value, fieldNames.value);
  48. const rawOptions = optionList.map(_ref => {
  49. let {
  50. option
  51. } = _ref;
  52. return option;
  53. });
  54. const lastOption = rawOptions[rawOptions.length - 1];
  55. if (lastOption && !isLeaf(lastOption, fieldNames.value)) {
  56. const pathKey = toPathKey(valueCells);
  57. loadingKeys.value = [...loadingKeys.value, pathKey];
  58. loadData.value(rawOptions);
  59. }
  60. };
  61. watchEffect(() => {
  62. if (loadingKeys.value.length) {
  63. loadingKeys.value.forEach(loadingKey => {
  64. const valueStrCells = toPathValueStr(loadingKey);
  65. const optionList = toPathOptions(valueStrCells, options.value, fieldNames.value, true).map(_ref2 => {
  66. let {
  67. option
  68. } = _ref2;
  69. return option;
  70. });
  71. const lastOption = optionList[optionList.length - 1];
  72. if (!lastOption || lastOption[fieldNames.value.children] || isLeaf(lastOption, fieldNames.value)) {
  73. loadingKeys.value = loadingKeys.value.filter(key => key !== loadingKey);
  74. }
  75. });
  76. }
  77. });
  78. // ========================== Values ==========================
  79. const checkedSet = computed(() => new Set(toPathKeys(values.value)));
  80. const halfCheckedSet = computed(() => new Set(toPathKeys(halfValues.value)));
  81. // ====================== Accessibility =======================
  82. const [activeValueCells, setActiveValueCells] = useActive();
  83. // =========================== Path ===========================
  84. const onPathOpen = nextValueCells => {
  85. setActiveValueCells(nextValueCells);
  86. // Trigger loadData
  87. internalLoadData(nextValueCells);
  88. };
  89. const isSelectable = option => {
  90. const {
  91. disabled
  92. } = option;
  93. const isMergedLeaf = isLeaf(option, fieldNames.value);
  94. return !disabled && (isMergedLeaf || changeOnSelect.value || baseProps.multiple);
  95. };
  96. const onPathSelect = function (valuePath, leaf) {
  97. let fromKeyboard = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  98. onSelect(valuePath);
  99. if (!baseProps.multiple && (leaf || changeOnSelect.value && (expandTrigger.value === 'hover' || fromKeyboard))) {
  100. baseProps.toggleOpen(false);
  101. }
  102. };
  103. // ========================== Option ==========================
  104. const mergedOptions = computed(() => {
  105. if (baseProps.searchValue) {
  106. return searchOptions.value;
  107. }
  108. return options.value;
  109. });
  110. // ========================== Column ==========================
  111. const optionColumns = computed(() => {
  112. const optionList = [{
  113. options: mergedOptions.value
  114. }];
  115. let currentList = mergedOptions.value;
  116. for (let i = 0; i < activeValueCells.value.length; i += 1) {
  117. const activeValueCell = activeValueCells.value[i];
  118. const currentOption = currentList.find(option => option[fieldNames.value.value] === activeValueCell);
  119. const subOptions = currentOption === null || currentOption === void 0 ? void 0 : currentOption[fieldNames.value.children];
  120. if (!(subOptions === null || subOptions === void 0 ? void 0 : subOptions.length)) {
  121. break;
  122. }
  123. currentList = subOptions;
  124. optionList.push({
  125. options: subOptions
  126. });
  127. }
  128. return optionList;
  129. });
  130. // ========================= Keyboard =========================
  131. const onKeyboardSelect = (selectValueCells, option) => {
  132. if (isSelectable(option)) {
  133. onPathSelect(selectValueCells, isLeaf(option, fieldNames.value), true);
  134. }
  135. };
  136. useKeyboard(context, mergedOptions, fieldNames, activeValueCells, onPathOpen, onKeyboardSelect);
  137. const onListMouseDown = event => {
  138. event.preventDefault();
  139. };
  140. onMounted(() => {
  141. watch(activeValueCells, cells => {
  142. var _a;
  143. for (let i = 0; i < cells.length; i += 1) {
  144. const cellPath = cells.slice(0, i + 1);
  145. const cellKeyPath = toPathKey(cellPath);
  146. const ele = (_a = containerRef.value) === null || _a === void 0 ? void 0 : _a.querySelector(`li[data-path-key="${cellKeyPath.replace(/\\{0,2}"/g, '\\"')}"]`);
  147. if (ele) {
  148. scrollIntoParentView(ele);
  149. }
  150. }
  151. }, {
  152. flush: 'post',
  153. immediate: true
  154. });
  155. });
  156. return () => {
  157. var _a, _b, _c, _d, _e;
  158. // ========================== Render ==========================
  159. const {
  160. notFoundContent = ((_a = slots.notFoundContent) === null || _a === void 0 ? void 0 : _a.call(slots)) || ((_c = (_b = customSlots.value).notFoundContent) === null || _c === void 0 ? void 0 : _c.call(_b)),
  161. multiple,
  162. toggleOpen
  163. } = baseProps;
  164. // >>>>> Empty
  165. const isEmpty = !((_e = (_d = optionColumns.value[0]) === null || _d === void 0 ? void 0 : _d.options) === null || _e === void 0 ? void 0 : _e.length);
  166. const emptyList = [{
  167. [fieldNames.value.value]: '__EMPTY__',
  168. [FIX_LABEL]: notFoundContent,
  169. disabled: true
  170. }];
  171. const columnProps = _extends(_extends({}, attrs), {
  172. multiple: !isEmpty && multiple,
  173. onSelect: onPathSelect,
  174. onActive: onPathOpen,
  175. onToggleOpen: toggleOpen,
  176. checkedSet: checkedSet.value,
  177. halfCheckedSet: halfCheckedSet.value,
  178. loadingKeys: loadingKeys.value,
  179. isSelectable
  180. });
  181. // >>>>> Columns
  182. const mergedOptionColumns = isEmpty ? [{
  183. options: emptyList
  184. }] : optionColumns.value;
  185. const columnNodes = mergedOptionColumns.map((col, index) => {
  186. const prevValuePath = activeValueCells.value.slice(0, index);
  187. const activeValue = activeValueCells.value[index];
  188. return _createVNode(Column, _objectSpread(_objectSpread({
  189. "key": index
  190. }, columnProps), {}, {
  191. "prefixCls": mergedPrefixCls.value,
  192. "options": col.options,
  193. "prevValuePath": prevValuePath,
  194. "activeValue": activeValue
  195. }), null);
  196. });
  197. return _createVNode("div", {
  198. "class": [`${mergedPrefixCls.value}-menus`, {
  199. [`${mergedPrefixCls.value}-menu-empty`]: isEmpty,
  200. [`${mergedPrefixCls.value}-rtl`]: rtl.value
  201. }],
  202. "onMousedown": onListMouseDown,
  203. "ref": containerRef
  204. }, [columnNodes]);
  205. };
  206. }
  207. });