6a783a2c22c476a31dc209add234caef659b3de5a022b5262718f65d3b9a128b3bffcdf30d84b4e52d3156a6b540c41250f4f51acf33c3471cbaf0a5827369 25 KB

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