useSelect.mjs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. import { reactive, ref, computed, nextTick, watch, watchEffect, onMounted } from 'vue';
  2. import { debounce, get, findLastIndex, isEqual } from 'lodash-unified';
  3. import { useResizeObserver } from '@vueuse/core';
  4. import { useAllowCreate } from './useAllowCreate.mjs';
  5. import { useProps } from './useProps.mjs';
  6. import { useLocale } from '../../../hooks/use-locale/index.mjs';
  7. import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
  8. import { useFormItem, useFormItemInputId } from '../../form/src/hooks/use-form-item.mjs';
  9. import { useEmptyValues } from '../../../hooks/use-empty-values/index.mjs';
  10. import { useComposition } from '../../../hooks/use-composition/index.mjs';
  11. import { useFocusController } from '../../../hooks/use-focus-controller/index.mjs';
  12. import { debugWarn } from '../../../utils/error.mjs';
  13. import { isArray, isFunction, isObject } from '@vue/shared';
  14. import { ValidateComponentsMap } from '../../../utils/vue/icon.mjs';
  15. import { escapeStringRegexp } from '../../../utils/strings.mjs';
  16. import { useFormSize } from '../../form/src/hooks/use-form-common-props.mjs';
  17. import { EVENT_CODE } from '../../../constants/aria.mjs';
  18. import { isUndefined, isNumber } from '../../../utils/types.mjs';
  19. import { UPDATE_MODEL_EVENT, CHANGE_EVENT } from '../../../constants/event.mjs';
  20. const useSelect = (props, emit) => {
  21. const { t } = useLocale();
  22. const nsSelect = useNamespace("select");
  23. const nsInput = useNamespace("input");
  24. const { form: elForm, formItem: elFormItem } = useFormItem();
  25. const { inputId } = useFormItemInputId(props, {
  26. formItemContext: elFormItem
  27. });
  28. const { aliasProps, getLabel, getValue, getDisabled, getOptions } = useProps(props);
  29. const { valueOnClear, isEmptyValue } = useEmptyValues(props);
  30. const states = reactive({
  31. inputValue: "",
  32. cachedOptions: [],
  33. createdOptions: [],
  34. hoveringIndex: -1,
  35. inputHovering: false,
  36. selectionWidth: 0,
  37. collapseItemWidth: 0,
  38. previousQuery: null,
  39. previousValue: void 0,
  40. selectedLabel: "",
  41. menuVisibleOnFocus: false,
  42. isBeforeHide: false
  43. });
  44. const popperSize = ref(-1);
  45. const selectRef = ref();
  46. const selectionRef = ref();
  47. const tooltipRef = ref();
  48. const tagTooltipRef = ref();
  49. const inputRef = ref();
  50. const prefixRef = ref();
  51. const suffixRef = ref();
  52. const menuRef = ref();
  53. const tagMenuRef = ref();
  54. const collapseItemRef = ref();
  55. const {
  56. isComposing,
  57. handleCompositionStart,
  58. handleCompositionEnd,
  59. handleCompositionUpdate
  60. } = useComposition({
  61. afterComposition: (e) => onInput(e)
  62. });
  63. const { wrapperRef, isFocused, handleBlur } = useFocusController(inputRef, {
  64. beforeFocus() {
  65. return selectDisabled.value;
  66. },
  67. afterFocus() {
  68. if (props.automaticDropdown && !expanded.value) {
  69. expanded.value = true;
  70. states.menuVisibleOnFocus = true;
  71. }
  72. },
  73. beforeBlur(event) {
  74. var _a, _b;
  75. return ((_a = tooltipRef.value) == null ? void 0 : _a.isFocusInsideContent(event)) || ((_b = tagTooltipRef.value) == null ? void 0 : _b.isFocusInsideContent(event));
  76. },
  77. afterBlur() {
  78. var _a;
  79. expanded.value = false;
  80. states.menuVisibleOnFocus = false;
  81. if (props.validateEvent) {
  82. (_a = elFormItem == null ? void 0 : elFormItem.validate) == null ? void 0 : _a.call(elFormItem, "blur").catch((err) => debugWarn(err));
  83. }
  84. }
  85. });
  86. const allOptions = computed(() => filterOptions(""));
  87. const hasOptions = computed(() => {
  88. if (props.loading)
  89. return false;
  90. return props.options.length > 0 || states.createdOptions.length > 0;
  91. });
  92. const filteredOptions = ref([]);
  93. const expanded = ref(false);
  94. const selectDisabled = computed(() => props.disabled || (elForm == null ? void 0 : elForm.disabled));
  95. const needStatusIcon = computed(() => {
  96. var _a;
  97. return (_a = elForm == null ? void 0 : elForm.statusIcon) != null ? _a : false;
  98. });
  99. const popupHeight = computed(() => {
  100. const totalHeight = filteredOptions.value.length * props.itemHeight;
  101. return totalHeight > props.height ? props.height : totalHeight;
  102. });
  103. const hasModelValue = computed(() => {
  104. return props.multiple ? isArray(props.modelValue) && props.modelValue.length > 0 : !isEmptyValue(props.modelValue);
  105. });
  106. const showClearBtn = computed(() => {
  107. return props.clearable && !selectDisabled.value && states.inputHovering && hasModelValue.value;
  108. });
  109. const iconComponent = computed(() => props.remote && props.filterable ? "" : props.suffixIcon);
  110. const iconReverse = computed(() => iconComponent.value && nsSelect.is("reverse", expanded.value));
  111. const validateState = computed(() => (elFormItem == null ? void 0 : elFormItem.validateState) || "");
  112. const validateIcon = computed(() => {
  113. if (!validateState.value)
  114. return;
  115. return ValidateComponentsMap[validateState.value];
  116. });
  117. const debounce$1 = computed(() => props.remote ? 300 : 0);
  118. const emptyText = computed(() => {
  119. if (props.loading) {
  120. return props.loadingText || t("el.select.loading");
  121. } else {
  122. if (props.remote && !states.inputValue && !hasOptions.value)
  123. return false;
  124. if (props.filterable && states.inputValue && hasOptions.value && filteredOptions.value.length === 0) {
  125. return props.noMatchText || t("el.select.noMatch");
  126. }
  127. if (!hasOptions.value) {
  128. return props.noDataText || t("el.select.noData");
  129. }
  130. }
  131. return null;
  132. });
  133. const filterOptions = (query) => {
  134. const regexp = new RegExp(escapeStringRegexp(query), "i");
  135. const isFilterMethodValid = props.filterable && isFunction(props.filterMethod);
  136. const isRemoteMethodValid = props.filterable && props.remote && isFunction(props.remoteMethod);
  137. const isValidOption = (o) => {
  138. if (isFilterMethodValid || isRemoteMethodValid)
  139. return true;
  140. return query ? regexp.test(getLabel(o) || "") : true;
  141. };
  142. if (props.loading) {
  143. return [];
  144. }
  145. return [...states.createdOptions, ...props.options].reduce((all, item) => {
  146. const options = getOptions(item);
  147. if (isArray(options)) {
  148. const filtered = options.filter(isValidOption);
  149. if (filtered.length > 0) {
  150. all.push({
  151. label: getLabel(item),
  152. type: "Group"
  153. }, ...filtered);
  154. }
  155. } else if (props.remote || isValidOption(item)) {
  156. all.push(item);
  157. }
  158. return all;
  159. }, []);
  160. };
  161. const updateOptions = () => {
  162. filteredOptions.value = filterOptions(states.inputValue);
  163. };
  164. const allOptionsValueMap = computed(() => {
  165. const valueMap = /* @__PURE__ */ new Map();
  166. allOptions.value.forEach((option, index) => {
  167. valueMap.set(getValueKey(getValue(option)), { option, index });
  168. });
  169. return valueMap;
  170. });
  171. const filteredOptionsValueMap = computed(() => {
  172. const valueMap = /* @__PURE__ */ new Map();
  173. filteredOptions.value.forEach((option, index) => {
  174. valueMap.set(getValueKey(getValue(option)), { option, index });
  175. });
  176. return valueMap;
  177. });
  178. const optionsAllDisabled = computed(() => filteredOptions.value.every((option) => getDisabled(option)));
  179. const selectSize = useFormSize();
  180. const collapseTagSize = computed(() => selectSize.value === "small" ? "small" : "default");
  181. const calculatePopperSize = () => {
  182. var _a;
  183. if (isNumber(props.fitInputWidth)) {
  184. popperSize.value = props.fitInputWidth;
  185. return;
  186. }
  187. const width = ((_a = selectRef.value) == null ? void 0 : _a.offsetWidth) || 200;
  188. if (!props.fitInputWidth && hasOptions.value) {
  189. nextTick(() => {
  190. popperSize.value = Math.max(width, calculateLabelMaxWidth());
  191. });
  192. } else {
  193. popperSize.value = width;
  194. }
  195. };
  196. const calculateLabelMaxWidth = () => {
  197. var _a, _b;
  198. const canvas = document.createElement("canvas");
  199. const ctx = canvas.getContext("2d");
  200. const selector = nsSelect.be("dropdown", "item");
  201. const dom = ((_b = (_a = menuRef.value) == null ? void 0 : _a.listRef) == null ? void 0 : _b.innerRef) || document;
  202. const dropdownItemEl = dom.querySelector(`.${selector}`);
  203. if (dropdownItemEl === null || ctx === null)
  204. return 0;
  205. const style = getComputedStyle(dropdownItemEl);
  206. const padding = Number.parseFloat(style.paddingLeft) + Number.parseFloat(style.paddingRight);
  207. ctx.font = `bold ${style.font.replace(new RegExp(`\\b${style.fontWeight}\\b`), "")}`;
  208. const maxWidth = filteredOptions.value.reduce((max, option) => {
  209. const metrics = ctx.measureText(getLabel(option));
  210. return Math.max(metrics.width, max);
  211. }, 0);
  212. return maxWidth + padding;
  213. };
  214. const getGapWidth = () => {
  215. if (!selectionRef.value)
  216. return 0;
  217. const style = window.getComputedStyle(selectionRef.value);
  218. return Number.parseFloat(style.gap || "6px");
  219. };
  220. const tagStyle = computed(() => {
  221. const gapWidth = getGapWidth();
  222. const maxWidth = collapseItemRef.value && props.maxCollapseTags === 1 ? states.selectionWidth - states.collapseItemWidth - gapWidth : states.selectionWidth;
  223. return { maxWidth: `${maxWidth}px` };
  224. });
  225. const collapseTagStyle = computed(() => {
  226. return { maxWidth: `${states.selectionWidth}px` };
  227. });
  228. const shouldShowPlaceholder = computed(() => {
  229. if (isArray(props.modelValue)) {
  230. return props.modelValue.length === 0 && !states.inputValue;
  231. }
  232. return props.filterable ? !states.inputValue : true;
  233. });
  234. const currentPlaceholder = computed(() => {
  235. var _a;
  236. const _placeholder = (_a = props.placeholder) != null ? _a : t("el.select.placeholder");
  237. return props.multiple || !hasModelValue.value ? _placeholder : states.selectedLabel;
  238. });
  239. const popperRef = computed(() => {
  240. var _a, _b;
  241. return (_b = (_a = tooltipRef.value) == null ? void 0 : _a.popperRef) == null ? void 0 : _b.contentRef;
  242. });
  243. const indexRef = computed(() => {
  244. if (props.multiple) {
  245. const len = props.modelValue.length;
  246. if (props.modelValue.length > 0 && filteredOptionsValueMap.value.has(props.modelValue[len - 1])) {
  247. const { index } = filteredOptionsValueMap.value.get(props.modelValue[len - 1]);
  248. return index;
  249. }
  250. } else {
  251. if (!isEmptyValue(props.modelValue) && filteredOptionsValueMap.value.has(props.modelValue)) {
  252. const { index } = filteredOptionsValueMap.value.get(props.modelValue);
  253. return index;
  254. }
  255. }
  256. return -1;
  257. });
  258. const dropdownMenuVisible = computed({
  259. get() {
  260. return expanded.value && emptyText.value !== false;
  261. },
  262. set(val) {
  263. expanded.value = val;
  264. }
  265. });
  266. const showTagList = computed(() => {
  267. if (!props.multiple) {
  268. return [];
  269. }
  270. return props.collapseTags ? states.cachedOptions.slice(0, props.maxCollapseTags) : states.cachedOptions;
  271. });
  272. const collapseTagList = computed(() => {
  273. if (!props.multiple) {
  274. return [];
  275. }
  276. return props.collapseTags ? states.cachedOptions.slice(props.maxCollapseTags) : [];
  277. });
  278. const {
  279. createNewOption,
  280. removeNewOption,
  281. selectNewOption,
  282. clearAllNewOption
  283. } = useAllowCreate(props, states);
  284. const toggleMenu = () => {
  285. if (selectDisabled.value)
  286. return;
  287. if (states.menuVisibleOnFocus) {
  288. states.menuVisibleOnFocus = false;
  289. } else {
  290. expanded.value = !expanded.value;
  291. }
  292. };
  293. const onInputChange = () => {
  294. if (states.inputValue.length > 0 && !expanded.value) {
  295. expanded.value = true;
  296. }
  297. createNewOption(states.inputValue);
  298. handleQueryChange(states.inputValue);
  299. };
  300. const debouncedOnInputChange = debounce(onInputChange, debounce$1.value);
  301. const handleQueryChange = (val) => {
  302. if (states.previousQuery === val || isComposing.value) {
  303. return;
  304. }
  305. states.previousQuery = val;
  306. if (props.filterable && isFunction(props.filterMethod)) {
  307. props.filterMethod(val);
  308. } else if (props.filterable && props.remote && isFunction(props.remoteMethod)) {
  309. props.remoteMethod(val);
  310. }
  311. if (props.defaultFirstOption && (props.filterable || props.remote) && filteredOptions.value.length) {
  312. nextTick(checkDefaultFirstOption);
  313. } else {
  314. nextTick(updateHoveringIndex);
  315. }
  316. };
  317. const checkDefaultFirstOption = () => {
  318. const optionsInDropdown = filteredOptions.value.filter((n) => !n.disabled && n.type !== "Group");
  319. const userCreatedOption = optionsInDropdown.find((n) => n.created);
  320. const firstOriginOption = optionsInDropdown[0];
  321. states.hoveringIndex = getValueIndex(filteredOptions.value, userCreatedOption || firstOriginOption);
  322. };
  323. const emitChange = (val) => {
  324. if (!isEqual(props.modelValue, val)) {
  325. emit(CHANGE_EVENT, val);
  326. }
  327. };
  328. const update = (val) => {
  329. emit(UPDATE_MODEL_EVENT, val);
  330. emitChange(val);
  331. states.previousValue = props.multiple ? String(val) : val;
  332. nextTick(() => {
  333. if (props.multiple && isArray(props.modelValue)) {
  334. const cachedOptions = states.cachedOptions.slice();
  335. const selectedOptions = props.modelValue.map((value) => getOption(value, cachedOptions));
  336. if (!isEqual(states.cachedOptions, selectedOptions)) {
  337. states.cachedOptions = selectedOptions;
  338. }
  339. } else {
  340. initStates(true);
  341. }
  342. });
  343. };
  344. const getValueIndex = (arr = [], value) => {
  345. if (!isObject(value)) {
  346. return arr.indexOf(value);
  347. }
  348. const valueKey = props.valueKey;
  349. let index = -1;
  350. arr.some((item, i) => {
  351. if (get(item, valueKey) === get(value, valueKey)) {
  352. index = i;
  353. return true;
  354. }
  355. return false;
  356. });
  357. return index;
  358. };
  359. const getValueKey = (item) => {
  360. return isObject(item) ? get(item, props.valueKey) : item;
  361. };
  362. const handleResize = () => {
  363. calculatePopperSize();
  364. };
  365. const resetSelectionWidth = () => {
  366. states.selectionWidth = Number.parseFloat(window.getComputedStyle(selectionRef.value).width);
  367. };
  368. const resetCollapseItemWidth = () => {
  369. states.collapseItemWidth = collapseItemRef.value.getBoundingClientRect().width;
  370. };
  371. const updateTooltip = () => {
  372. var _a, _b;
  373. (_b = (_a = tooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
  374. };
  375. const updateTagTooltip = () => {
  376. var _a, _b;
  377. (_b = (_a = tagTooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
  378. };
  379. const onSelect = (option) => {
  380. if (props.multiple) {
  381. let selectedOptions = props.modelValue.slice();
  382. const index = getValueIndex(selectedOptions, getValue(option));
  383. if (index > -1) {
  384. selectedOptions = [
  385. ...selectedOptions.slice(0, index),
  386. ...selectedOptions.slice(index + 1)
  387. ];
  388. states.cachedOptions.splice(index, 1);
  389. removeNewOption(option);
  390. } else if (props.multipleLimit <= 0 || selectedOptions.length < props.multipleLimit) {
  391. selectedOptions = [...selectedOptions, getValue(option)];
  392. states.cachedOptions.push(option);
  393. selectNewOption(option);
  394. }
  395. update(selectedOptions);
  396. if (option.created) {
  397. handleQueryChange("");
  398. }
  399. if (props.filterable && !props.reserveKeyword) {
  400. states.inputValue = "";
  401. }
  402. } else {
  403. states.selectedLabel = getLabel(option);
  404. update(getValue(option));
  405. expanded.value = false;
  406. selectNewOption(option);
  407. if (!option.created) {
  408. clearAllNewOption();
  409. }
  410. }
  411. focus();
  412. };
  413. const deleteTag = (event, option) => {
  414. let selectedOptions = props.modelValue.slice();
  415. const index = getValueIndex(selectedOptions, getValue(option));
  416. if (index > -1 && !selectDisabled.value) {
  417. selectedOptions = [
  418. ...props.modelValue.slice(0, index),
  419. ...props.modelValue.slice(index + 1)
  420. ];
  421. states.cachedOptions.splice(index, 1);
  422. update(selectedOptions);
  423. emit("remove-tag", getValue(option));
  424. removeNewOption(option);
  425. }
  426. event.stopPropagation();
  427. focus();
  428. };
  429. const focus = () => {
  430. var _a;
  431. (_a = inputRef.value) == null ? void 0 : _a.focus();
  432. };
  433. const blur = () => {
  434. var _a;
  435. if (expanded.value) {
  436. expanded.value = false;
  437. nextTick(() => {
  438. var _a2;
  439. return (_a2 = inputRef.value) == null ? void 0 : _a2.blur();
  440. });
  441. return;
  442. }
  443. (_a = inputRef.value) == null ? void 0 : _a.blur();
  444. };
  445. const handleEsc = () => {
  446. if (states.inputValue.length > 0) {
  447. states.inputValue = "";
  448. } else {
  449. expanded.value = false;
  450. }
  451. };
  452. const getLastNotDisabledIndex = (value) => findLastIndex(value, (it) => !states.cachedOptions.some((option) => getValue(option) === it && getDisabled(option)));
  453. const handleDel = (e) => {
  454. if (!props.multiple)
  455. return;
  456. if (e.code === EVENT_CODE.delete)
  457. return;
  458. if (states.inputValue.length === 0) {
  459. e.preventDefault();
  460. const selected = props.modelValue.slice();
  461. const lastNotDisabledIndex = getLastNotDisabledIndex(selected);
  462. if (lastNotDisabledIndex < 0)
  463. return;
  464. const removeTagValue = selected[lastNotDisabledIndex];
  465. selected.splice(lastNotDisabledIndex, 1);
  466. const option = states.cachedOptions[lastNotDisabledIndex];
  467. states.cachedOptions.splice(lastNotDisabledIndex, 1);
  468. removeNewOption(option);
  469. update(selected);
  470. emit("remove-tag", removeTagValue);
  471. }
  472. };
  473. const handleClear = () => {
  474. let emptyValue;
  475. if (isArray(props.modelValue)) {
  476. emptyValue = [];
  477. } else {
  478. emptyValue = valueOnClear.value;
  479. }
  480. states.selectedLabel = "";
  481. expanded.value = false;
  482. update(emptyValue);
  483. emit("clear");
  484. clearAllNewOption();
  485. focus();
  486. };
  487. const onKeyboardNavigate = (direction, hoveringIndex = void 0) => {
  488. const options = filteredOptions.value;
  489. if (!["forward", "backward"].includes(direction) || selectDisabled.value || options.length <= 0 || optionsAllDisabled.value || isComposing.value) {
  490. return;
  491. }
  492. if (!expanded.value) {
  493. return toggleMenu();
  494. }
  495. if (isUndefined(hoveringIndex)) {
  496. hoveringIndex = states.hoveringIndex;
  497. }
  498. let newIndex = -1;
  499. if (direction === "forward") {
  500. newIndex = hoveringIndex + 1;
  501. if (newIndex >= options.length) {
  502. newIndex = 0;
  503. }
  504. } else if (direction === "backward") {
  505. newIndex = hoveringIndex - 1;
  506. if (newIndex < 0 || newIndex >= options.length) {
  507. newIndex = options.length - 1;
  508. }
  509. }
  510. const option = options[newIndex];
  511. if (getDisabled(option) || option.type === "Group") {
  512. return onKeyboardNavigate(direction, newIndex);
  513. } else {
  514. states.hoveringIndex = newIndex;
  515. scrollToItem(newIndex);
  516. }
  517. };
  518. const onKeyboardSelect = () => {
  519. if (!expanded.value) {
  520. return toggleMenu();
  521. } else if (~states.hoveringIndex && filteredOptions.value[states.hoveringIndex]) {
  522. onSelect(filteredOptions.value[states.hoveringIndex]);
  523. }
  524. };
  525. const onHoverOption = (idx) => {
  526. states.hoveringIndex = idx != null ? idx : -1;
  527. };
  528. const updateHoveringIndex = () => {
  529. if (!props.multiple) {
  530. states.hoveringIndex = filteredOptions.value.findIndex((item) => {
  531. return getValueKey(item) === getValueKey(props.modelValue);
  532. });
  533. } else {
  534. states.hoveringIndex = filteredOptions.value.findIndex((item) => props.modelValue.some((modelValue) => getValueKey(modelValue) === getValueKey(item)));
  535. }
  536. };
  537. const onInput = (event) => {
  538. states.inputValue = event.target.value;
  539. if (props.remote) {
  540. debouncedOnInputChange();
  541. } else {
  542. return onInputChange();
  543. }
  544. };
  545. const handleClickOutside = (event) => {
  546. expanded.value = false;
  547. if (isFocused.value) {
  548. const _event = new FocusEvent("focus", event);
  549. handleBlur(_event);
  550. }
  551. };
  552. const handleMenuEnter = () => {
  553. states.isBeforeHide = false;
  554. return nextTick(() => {
  555. if (~indexRef.value) {
  556. scrollToItem(states.hoveringIndex);
  557. }
  558. });
  559. };
  560. const scrollToItem = (index) => {
  561. menuRef.value.scrollToItem(index);
  562. };
  563. const getOption = (value, cachedOptions) => {
  564. const selectValue = getValueKey(value);
  565. if (allOptionsValueMap.value.has(selectValue)) {
  566. const { option } = allOptionsValueMap.value.get(selectValue);
  567. return option;
  568. }
  569. if (cachedOptions && cachedOptions.length) {
  570. const option = cachedOptions.find((option2) => getValueKey(getValue(option2)) === selectValue);
  571. if (option) {
  572. return option;
  573. }
  574. }
  575. return {
  576. [aliasProps.value.value]: value,
  577. [aliasProps.value.label]: value
  578. };
  579. };
  580. const initStates = (needUpdateSelectedLabel = false) => {
  581. if (props.multiple) {
  582. if (props.modelValue.length > 0) {
  583. const cachedOptions = states.cachedOptions.slice();
  584. states.cachedOptions.length = 0;
  585. states.previousValue = props.modelValue.toString();
  586. for (const value of props.modelValue) {
  587. const option = getOption(value, cachedOptions);
  588. states.cachedOptions.push(option);
  589. }
  590. } else {
  591. states.cachedOptions = [];
  592. states.previousValue = void 0;
  593. }
  594. } else {
  595. if (hasModelValue.value) {
  596. states.previousValue = props.modelValue;
  597. const options = filteredOptions.value;
  598. const selectedItemIndex = options.findIndex((option) => getValueKey(getValue(option)) === getValueKey(props.modelValue));
  599. if (~selectedItemIndex) {
  600. states.selectedLabel = getLabel(options[selectedItemIndex]);
  601. } else {
  602. if (!states.selectedLabel || needUpdateSelectedLabel) {
  603. states.selectedLabel = getValueKey(props.modelValue);
  604. }
  605. }
  606. } else {
  607. states.selectedLabel = "";
  608. states.previousValue = void 0;
  609. }
  610. }
  611. clearAllNewOption();
  612. calculatePopperSize();
  613. };
  614. watch(() => props.fitInputWidth, () => {
  615. calculatePopperSize();
  616. });
  617. watch(expanded, (val) => {
  618. if (val) {
  619. if (!props.persistent) {
  620. calculatePopperSize();
  621. }
  622. handleQueryChange("");
  623. } else {
  624. states.inputValue = "";
  625. states.previousQuery = null;
  626. states.isBeforeHide = true;
  627. createNewOption("");
  628. }
  629. emit("visible-change", val);
  630. });
  631. watch(() => props.modelValue, (val, oldVal) => {
  632. var _a;
  633. const isValEmpty = !val || isArray(val) && val.length === 0;
  634. if (isValEmpty || props.multiple && !isEqual(val.toString(), states.previousValue) || !props.multiple && getValueKey(val) !== getValueKey(states.previousValue)) {
  635. initStates(true);
  636. }
  637. if (!isEqual(val, oldVal) && props.validateEvent) {
  638. (_a = elFormItem == null ? void 0 : elFormItem.validate) == null ? void 0 : _a.call(elFormItem, "change").catch((err) => debugWarn(err));
  639. }
  640. }, {
  641. deep: true
  642. });
  643. watch(() => props.options, () => {
  644. const input = inputRef.value;
  645. if (!input || input && document.activeElement !== input) {
  646. initStates();
  647. }
  648. }, {
  649. deep: true,
  650. flush: "post"
  651. });
  652. watch(() => filteredOptions.value, () => {
  653. calculatePopperSize();
  654. return menuRef.value && nextTick(menuRef.value.resetScrollTop);
  655. });
  656. watchEffect(() => {
  657. if (states.isBeforeHide)
  658. return;
  659. updateOptions();
  660. });
  661. watchEffect(() => {
  662. const { valueKey, options } = props;
  663. const duplicateValue = /* @__PURE__ */ new Map();
  664. for (const item of options) {
  665. const optionValue = getValue(item);
  666. let v = optionValue;
  667. if (isObject(v)) {
  668. v = get(optionValue, valueKey);
  669. }
  670. if (duplicateValue.get(v)) {
  671. debugWarn("ElSelectV2", `The option values you provided seem to be duplicated, which may cause some problems, please check.`);
  672. break;
  673. } else {
  674. duplicateValue.set(v, true);
  675. }
  676. }
  677. });
  678. onMounted(() => {
  679. initStates();
  680. });
  681. useResizeObserver(selectRef, handleResize);
  682. useResizeObserver(selectionRef, resetSelectionWidth);
  683. useResizeObserver(menuRef, updateTooltip);
  684. useResizeObserver(wrapperRef, updateTooltip);
  685. useResizeObserver(tagMenuRef, updateTagTooltip);
  686. useResizeObserver(collapseItemRef, resetCollapseItemWidth);
  687. return {
  688. inputId,
  689. collapseTagSize,
  690. currentPlaceholder,
  691. expanded,
  692. emptyText,
  693. popupHeight,
  694. debounce: debounce$1,
  695. allOptions,
  696. filteredOptions,
  697. iconComponent,
  698. iconReverse,
  699. tagStyle,
  700. collapseTagStyle,
  701. popperSize,
  702. dropdownMenuVisible,
  703. hasModelValue,
  704. shouldShowPlaceholder,
  705. selectDisabled,
  706. selectSize,
  707. needStatusIcon,
  708. showClearBtn,
  709. states,
  710. isFocused,
  711. nsSelect,
  712. nsInput,
  713. inputRef,
  714. menuRef,
  715. tagMenuRef,
  716. tooltipRef,
  717. tagTooltipRef,
  718. selectRef,
  719. wrapperRef,
  720. selectionRef,
  721. prefixRef,
  722. suffixRef,
  723. collapseItemRef,
  724. popperRef,
  725. validateState,
  726. validateIcon,
  727. showTagList,
  728. collapseTagList,
  729. debouncedOnInputChange,
  730. deleteTag,
  731. getLabel,
  732. getValue,
  733. getDisabled,
  734. getValueKey,
  735. handleClear,
  736. handleClickOutside,
  737. handleDel,
  738. handleEsc,
  739. focus,
  740. blur,
  741. handleMenuEnter,
  742. handleResize,
  743. resetSelectionWidth,
  744. updateTooltip,
  745. updateTagTooltip,
  746. updateOptions,
  747. toggleMenu,
  748. scrollTo: scrollToItem,
  749. onInput,
  750. onKeyboardNavigate,
  751. onKeyboardSelect,
  752. onSelect,
  753. onHover: onHoverOption,
  754. handleCompositionStart,
  755. handleCompositionEnd,
  756. handleCompositionUpdate
  757. };
  758. };
  759. export { useSelect as default };
  760. //# sourceMappingURL=useSelect.mjs.map