Cascader.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. Object.defineProperty(exports, "SHOW_CHILD", {
  7. enumerable: true,
  8. get: function () {
  9. return _commonUtil.SHOW_CHILD;
  10. }
  11. });
  12. Object.defineProperty(exports, "SHOW_PARENT", {
  13. enumerable: true,
  14. get: function () {
  15. return _commonUtil.SHOW_PARENT;
  16. }
  17. });
  18. exports.default = void 0;
  19. exports.internalCascaderProps = internalCascaderProps;
  20. exports.multipleCascaderProps = multipleCascaderProps;
  21. exports.singleCascaderProps = singleCascaderProps;
  22. var _vue = require("vue");
  23. var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
  24. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  25. var _BaseSelect = require("../vc-select/BaseSelect");
  26. var _omit = _interopRequireDefault(require("../_util/omit"));
  27. var _type = require("../_util/type");
  28. var _vueTypes = _interopRequireDefault(require("../_util/vue-types"));
  29. var _propsUtil = require("../_util/props-util");
  30. var _useId = _interopRequireDefault(require("../vc-select/hooks/useId"));
  31. var _useMergedState = _interopRequireDefault(require("../_util/hooks/useMergedState"));
  32. var _commonUtil = require("./utils/commonUtil");
  33. var _useEntities = _interopRequireDefault(require("./hooks/useEntities"));
  34. var _useSearchConfig = _interopRequireDefault(require("./hooks/useSearchConfig"));
  35. var _useSearchOptions = _interopRequireDefault(require("./hooks/useSearchOptions"));
  36. var _useMissingValues = _interopRequireDefault(require("./hooks/useMissingValues"));
  37. var _treeUtil = require("./utils/treeUtil");
  38. var _conductUtil = require("../vc-tree/utils/conductUtil");
  39. var _useDisplayValues = _interopRequireDefault(require("./hooks/useDisplayValues"));
  40. var _context = require("./context");
  41. var _OptionList = _interopRequireDefault(require("./OptionList"));
  42. var _vcSelect = require("../vc-select");
  43. var _devWarning = _interopRequireDefault(require("../vc-util/devWarning"));
  44. var _useMaxLevel = _interopRequireDefault(require("../vc-tree/useMaxLevel"));
  45. function baseCascaderProps() {
  46. return (0, _extends2.default)((0, _extends2.default)({}, (0, _omit.default)((0, _BaseSelect.baseSelectPropsWithoutPrivate)(), ['tokenSeparators', 'mode', 'showSearch'])), {
  47. // MISC
  48. id: String,
  49. prefixCls: String,
  50. fieldNames: (0, _type.objectType)(),
  51. children: Array,
  52. // Value
  53. value: {
  54. type: [String, Number, Array]
  55. },
  56. defaultValue: {
  57. type: [String, Number, Array]
  58. },
  59. changeOnSelect: {
  60. type: Boolean,
  61. default: undefined
  62. },
  63. displayRender: Function,
  64. checkable: {
  65. type: Boolean,
  66. default: undefined
  67. },
  68. showCheckedStrategy: {
  69. type: String,
  70. default: _commonUtil.SHOW_PARENT
  71. },
  72. // Search
  73. showSearch: {
  74. type: [Boolean, Object],
  75. default: undefined
  76. },
  77. searchValue: String,
  78. onSearch: Function,
  79. // Trigger
  80. expandTrigger: String,
  81. // Options
  82. options: Array,
  83. /** @private Internal usage. Do not use in your production. */
  84. dropdownPrefixCls: String,
  85. loadData: Function,
  86. // Open
  87. /** @deprecated Use `open` instead */
  88. popupVisible: {
  89. type: Boolean,
  90. default: undefined
  91. },
  92. dropdownClassName: String,
  93. dropdownMenuColumnStyle: {
  94. type: Object,
  95. default: undefined
  96. },
  97. /** @deprecated Use `dropdownStyle` instead */
  98. popupStyle: {
  99. type: Object,
  100. default: undefined
  101. },
  102. dropdownStyle: {
  103. type: Object,
  104. default: undefined
  105. },
  106. /** @deprecated Use `placement` instead */
  107. popupPlacement: String,
  108. placement: String,
  109. /** @deprecated Use `onDropdownVisibleChange` instead */
  110. onPopupVisibleChange: Function,
  111. onDropdownVisibleChange: Function,
  112. // Icon
  113. expandIcon: _vueTypes.default.any,
  114. loadingIcon: _vueTypes.default.any
  115. });
  116. }
  117. function singleCascaderProps() {
  118. return (0, _extends2.default)((0, _extends2.default)({}, baseCascaderProps()), {
  119. checkable: Boolean,
  120. onChange: Function
  121. });
  122. }
  123. function multipleCascaderProps() {
  124. return (0, _extends2.default)((0, _extends2.default)({}, baseCascaderProps()), {
  125. checkable: Boolean,
  126. onChange: Function
  127. });
  128. }
  129. function internalCascaderProps() {
  130. return (0, _extends2.default)((0, _extends2.default)({}, baseCascaderProps()), {
  131. onChange: Function,
  132. customSlots: Object
  133. });
  134. }
  135. function isMultipleValue(value) {
  136. return Array.isArray(value) && Array.isArray(value[0]);
  137. }
  138. function toRawValues(value) {
  139. if (!value) {
  140. return [];
  141. }
  142. if (isMultipleValue(value)) {
  143. return value;
  144. }
  145. return (value.length === 0 ? [] : [value]).map(val => Array.isArray(val) ? val : [val]);
  146. }
  147. var _default = exports.default = (0, _vue.defineComponent)({
  148. compatConfig: {
  149. MODE: 3
  150. },
  151. name: 'Cascader',
  152. inheritAttrs: false,
  153. props: (0, _propsUtil.initDefaultProps)(internalCascaderProps(), {}),
  154. setup(props, _ref) {
  155. let {
  156. attrs,
  157. expose,
  158. slots
  159. } = _ref;
  160. const mergedId = (0, _useId.default)((0, _vue.toRef)(props, 'id'));
  161. const multiple = (0, _vue.computed)(() => !!props.checkable);
  162. // =========================== Values ===========================
  163. const [rawValues, setRawValues] = (0, _useMergedState.default)(props.defaultValue, {
  164. value: (0, _vue.computed)(() => props.value),
  165. postState: toRawValues
  166. });
  167. // ========================= FieldNames =========================
  168. const mergedFieldNames = (0, _vue.computed)(() => (0, _commonUtil.fillFieldNames)(props.fieldNames));
  169. // =========================== Option ===========================
  170. const mergedOptions = (0, _vue.computed)(() => props.options || []);
  171. // Only used in multiple mode, this fn will not call in single mode
  172. const pathKeyEntities = (0, _useEntities.default)(mergedOptions, mergedFieldNames);
  173. /** Convert path key back to value format */
  174. const getValueByKeyPath = pathKeys => {
  175. const keyPathEntities = pathKeyEntities.value;
  176. return pathKeys.map(pathKey => {
  177. const {
  178. nodes
  179. } = keyPathEntities[pathKey];
  180. return nodes.map(node => node[mergedFieldNames.value.value]);
  181. });
  182. };
  183. // =========================== Search ===========================
  184. const [mergedSearchValue, setSearchValue] = (0, _useMergedState.default)('', {
  185. value: (0, _vue.computed)(() => props.searchValue),
  186. postState: search => search || ''
  187. });
  188. const onInternalSearch = (searchText, info) => {
  189. setSearchValue(searchText);
  190. if (info.source !== 'blur' && props.onSearch) {
  191. props.onSearch(searchText);
  192. }
  193. };
  194. const {
  195. showSearch: mergedShowSearch,
  196. searchConfig: mergedSearchConfig
  197. } = (0, _useSearchConfig.default)((0, _vue.toRef)(props, 'showSearch'));
  198. const searchOptions = (0, _useSearchOptions.default)(mergedSearchValue, mergedOptions, mergedFieldNames, (0, _vue.computed)(() => props.dropdownPrefixCls || props.prefixCls), mergedSearchConfig, (0, _vue.toRef)(props, 'changeOnSelect'));
  199. // =========================== Values ===========================
  200. const missingValuesInfo = (0, _useMissingValues.default)(mergedOptions, mergedFieldNames, rawValues);
  201. // Fill `rawValues` with checked conduction values
  202. const [checkedValues, halfCheckedValues, missingCheckedValues] = [(0, _vue.ref)([]), (0, _vue.ref)([]), (0, _vue.ref)([])];
  203. const {
  204. maxLevel,
  205. levelEntities
  206. } = (0, _useMaxLevel.default)(pathKeyEntities);
  207. (0, _vue.watchEffect)(() => {
  208. const [existValues, missingValues] = missingValuesInfo.value;
  209. if (!multiple.value || !rawValues.value.length) {
  210. [checkedValues.value, halfCheckedValues.value, missingCheckedValues.value] = [existValues, [], missingValues];
  211. return;
  212. }
  213. const keyPathValues = (0, _commonUtil.toPathKeys)(existValues);
  214. const keyPathEntities = pathKeyEntities.value;
  215. const {
  216. checkedKeys,
  217. halfCheckedKeys
  218. } = (0, _conductUtil.conductCheck)(keyPathValues, true, keyPathEntities, maxLevel.value, levelEntities.value);
  219. // Convert key back to value cells
  220. [checkedValues.value, halfCheckedValues.value, missingCheckedValues.value] = [getValueByKeyPath(checkedKeys), getValueByKeyPath(halfCheckedKeys), missingValues];
  221. });
  222. const deDuplicatedValues = (0, _vue.computed)(() => {
  223. const checkedKeys = (0, _commonUtil.toPathKeys)(checkedValues.value);
  224. const deduplicateKeys = (0, _treeUtil.formatStrategyValues)(checkedKeys, pathKeyEntities.value, props.showCheckedStrategy);
  225. return [...missingCheckedValues.value, ...getValueByKeyPath(deduplicateKeys)];
  226. });
  227. const displayValues = (0, _useDisplayValues.default)(deDuplicatedValues, mergedOptions, mergedFieldNames, multiple, (0, _vue.toRef)(props, 'displayRender'));
  228. // =========================== Change ===========================
  229. const triggerChange = nextValues => {
  230. setRawValues(nextValues);
  231. // Save perf if no need trigger event
  232. if (props.onChange) {
  233. const nextRawValues = toRawValues(nextValues);
  234. const valueOptions = nextRawValues.map(valueCells => (0, _treeUtil.toPathOptions)(valueCells, mergedOptions.value, mergedFieldNames.value).map(valueOpt => valueOpt.option));
  235. const triggerValues = multiple.value ? nextRawValues : nextRawValues[0];
  236. const triggerOptions = multiple.value ? valueOptions : valueOptions[0];
  237. props.onChange(triggerValues, triggerOptions);
  238. }
  239. };
  240. // =========================== Select ===========================
  241. const onInternalSelect = valuePath => {
  242. setSearchValue('');
  243. if (!multiple.value) {
  244. triggerChange(valuePath);
  245. } else {
  246. // Prepare conduct required info
  247. const pathKey = (0, _commonUtil.toPathKey)(valuePath);
  248. const checkedPathKeys = (0, _commonUtil.toPathKeys)(checkedValues.value);
  249. const halfCheckedPathKeys = (0, _commonUtil.toPathKeys)(halfCheckedValues.value);
  250. const existInChecked = checkedPathKeys.includes(pathKey);
  251. const existInMissing = missingCheckedValues.value.some(valueCells => (0, _commonUtil.toPathKey)(valueCells) === pathKey);
  252. // Do update
  253. let nextCheckedValues = checkedValues.value;
  254. let nextMissingValues = missingCheckedValues.value;
  255. if (existInMissing && !existInChecked) {
  256. // Missing value only do filter
  257. nextMissingValues = missingCheckedValues.value.filter(valueCells => (0, _commonUtil.toPathKey)(valueCells) !== pathKey);
  258. } else {
  259. // Update checked key first
  260. const nextRawCheckedKeys = existInChecked ? checkedPathKeys.filter(key => key !== pathKey) : [...checkedPathKeys, pathKey];
  261. // Conduction by selected or not
  262. let checkedKeys;
  263. if (existInChecked) {
  264. ({
  265. checkedKeys
  266. } = (0, _conductUtil.conductCheck)(nextRawCheckedKeys, {
  267. checked: false,
  268. halfCheckedKeys: halfCheckedPathKeys
  269. }, pathKeyEntities.value, maxLevel.value, levelEntities.value));
  270. } else {
  271. ({
  272. checkedKeys
  273. } = (0, _conductUtil.conductCheck)(nextRawCheckedKeys, true, pathKeyEntities.value, maxLevel.value, levelEntities.value));
  274. }
  275. // Roll up to parent level keys
  276. const deDuplicatedKeys = (0, _treeUtil.formatStrategyValues)(checkedKeys, pathKeyEntities.value, props.showCheckedStrategy);
  277. nextCheckedValues = getValueByKeyPath(deDuplicatedKeys);
  278. }
  279. triggerChange([...nextMissingValues, ...nextCheckedValues]);
  280. }
  281. };
  282. // Display Value change logic
  283. const onDisplayValuesChange = (_, info) => {
  284. if (info.type === 'clear') {
  285. triggerChange([]);
  286. return;
  287. }
  288. // Cascader do not support `add` type. Only support `remove`
  289. const {
  290. valueCells
  291. } = info.values[0];
  292. onInternalSelect(valueCells);
  293. };
  294. // ============================ Open ============================
  295. if (process.env.NODE_ENV !== 'production') {
  296. (0, _vue.watchEffect)(() => {
  297. (0, _devWarning.default)(!props.onPopupVisibleChange, 'Cascader', '`popupVisibleChange` is deprecated. Please use `dropdownVisibleChange` instead.');
  298. (0, _devWarning.default)(props.popupVisible === undefined, 'Cascader', '`popupVisible` is deprecated. Please use `open` instead.');
  299. (0, _devWarning.default)(props.popupPlacement === undefined, 'Cascader', '`popupPlacement` is deprecated. Please use `placement` instead.');
  300. (0, _devWarning.default)(props.popupStyle === undefined, 'Cascader', '`popupStyle` is deprecated. Please use `dropdownStyle` instead.');
  301. });
  302. }
  303. const mergedOpen = (0, _vue.computed)(() => props.open !== undefined ? props.open : props.popupVisible);
  304. const mergedDropdownStyle = (0, _vue.computed)(() => props.dropdownStyle || props.popupStyle || {});
  305. const mergedPlacement = (0, _vue.computed)(() => props.placement || props.popupPlacement);
  306. const onInternalDropdownVisibleChange = nextVisible => {
  307. var _a, _b;
  308. (_a = props.onDropdownVisibleChange) === null || _a === void 0 ? void 0 : _a.call(props, nextVisible);
  309. (_b = props.onPopupVisibleChange) === null || _b === void 0 ? void 0 : _b.call(props, nextVisible);
  310. };
  311. const {
  312. changeOnSelect,
  313. checkable,
  314. dropdownPrefixCls,
  315. loadData,
  316. expandTrigger,
  317. expandIcon,
  318. loadingIcon,
  319. dropdownMenuColumnStyle,
  320. customSlots,
  321. dropdownClassName
  322. } = (0, _vue.toRefs)(props);
  323. (0, _context.useProvideCascader)({
  324. options: mergedOptions,
  325. fieldNames: mergedFieldNames,
  326. values: checkedValues,
  327. halfValues: halfCheckedValues,
  328. changeOnSelect,
  329. onSelect: onInternalSelect,
  330. checkable,
  331. searchOptions,
  332. dropdownPrefixCls,
  333. loadData,
  334. expandTrigger,
  335. expandIcon,
  336. loadingIcon,
  337. dropdownMenuColumnStyle,
  338. customSlots
  339. });
  340. const selectRef = (0, _vue.ref)();
  341. expose({
  342. focus() {
  343. var _a;
  344. (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.focus();
  345. },
  346. blur() {
  347. var _a;
  348. (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.blur();
  349. },
  350. scrollTo(arg) {
  351. var _a;
  352. (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.scrollTo(arg);
  353. }
  354. });
  355. const pickProps = (0, _vue.computed)(() => {
  356. return (0, _omit.default)(props, ['id', 'prefixCls', 'fieldNames',
  357. // Value
  358. 'defaultValue', 'value', 'changeOnSelect', 'onChange', 'displayRender', 'checkable',
  359. // Search
  360. 'searchValue', 'onSearch', 'showSearch',
  361. // Trigger
  362. 'expandTrigger',
  363. // Options
  364. 'options', 'dropdownPrefixCls', 'loadData',
  365. // Open
  366. 'popupVisible', 'open', 'dropdownClassName', 'dropdownMenuColumnStyle', 'popupPlacement', 'placement', 'onDropdownVisibleChange', 'onPopupVisibleChange',
  367. // Icon
  368. 'expandIcon', 'loadingIcon', 'customSlots', 'showCheckedStrategy',
  369. // Children
  370. 'children']);
  371. });
  372. return () => {
  373. const emptyOptions = !(mergedSearchValue.value ? searchOptions.value : mergedOptions.value).length;
  374. const {
  375. dropdownMatchSelectWidth = false
  376. } = props;
  377. const dropdownStyle =
  378. // Search to match width
  379. mergedSearchValue.value && mergedSearchConfig.value.matchInputWidth ||
  380. // Empty keep the width
  381. emptyOptions ? {} : {
  382. minWidth: 'auto'
  383. };
  384. return (0, _vue.createVNode)(_vcSelect.BaseSelect, (0, _objectSpread2.default)((0, _objectSpread2.default)((0, _objectSpread2.default)({}, pickProps.value), attrs), {}, {
  385. "ref": selectRef,
  386. "id": mergedId,
  387. "prefixCls": props.prefixCls,
  388. "dropdownMatchSelectWidth": dropdownMatchSelectWidth,
  389. "dropdownStyle": (0, _extends2.default)((0, _extends2.default)({}, mergedDropdownStyle.value), dropdownStyle),
  390. "displayValues": displayValues.value,
  391. "onDisplayValuesChange": onDisplayValuesChange,
  392. "mode": multiple.value ? 'multiple' : undefined,
  393. "searchValue": mergedSearchValue.value,
  394. "onSearch": onInternalSearch,
  395. "showSearch": mergedShowSearch.value,
  396. "OptionList": _OptionList.default,
  397. "emptyOptions": emptyOptions,
  398. "open": mergedOpen.value,
  399. "dropdownClassName": dropdownClassName.value,
  400. "placement": mergedPlacement.value,
  401. "onDropdownVisibleChange": onInternalDropdownVisibleChange,
  402. "getRawInputElement": () => {
  403. var _a;
  404. return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
  405. }
  406. }), slots);
  407. };
  408. }
  409. });