d42c9def596d0ebf0c8395a6aecb637c0b646be13f3aacc6f86c62b05171ce6c50ac00e1a32447de9a2be881b8430e3437fed62940d85d8e6e4ba782d08c3f 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var vue = require('vue');
  4. var lodashUnified = require('lodash-unified');
  5. var core = require('@vueuse/core');
  6. var iconsVue = require('@element-plus/icons-vue');
  7. var index$5 = require('../../input/index.js');
  8. var index$3 = require('../../scrollbar/index.js');
  9. var index$2 = require('../../tooltip/index.js');
  10. var index$4 = require('../../icon/index.js');
  11. var autocomplete = require('./autocomplete.js');
  12. var pluginVue_exportHelper = require('../../../_virtual/plugin-vue_export-helper.js');
  13. var input = require('../../input/src/input.js');
  14. var useFormCommonProps = require('../../form/src/hooks/use-form-common-props.js');
  15. var index = require('../../../hooks/use-namespace/index.js');
  16. var index$1 = require('../../../hooks/use-id/index.js');
  17. var shared = require('@vue/shared');
  18. var event = require('../../../constants/event.js');
  19. var error = require('../../../utils/error.js');
  20. const COMPONENT_NAME = "ElAutocomplete";
  21. const __default__ = vue.defineComponent({
  22. name: COMPONENT_NAME,
  23. inheritAttrs: false
  24. });
  25. const _sfc_main = /* @__PURE__ */ vue.defineComponent({
  26. ...__default__,
  27. props: autocomplete.autocompleteProps,
  28. emits: autocomplete.autocompleteEmits,
  29. setup(__props, { expose, emit }) {
  30. const props = __props;
  31. const passInputProps = vue.computed(() => lodashUnified.pick(props, Object.keys(input.inputProps)));
  32. const rawAttrs = vue.useAttrs();
  33. const disabled = useFormCommonProps.useFormDisabled();
  34. const ns = index.useNamespace("autocomplete");
  35. const inputRef = vue.ref();
  36. const regionRef = vue.ref();
  37. const popperRef = vue.ref();
  38. const listboxRef = vue.ref();
  39. let readonly = false;
  40. let ignoreFocusEvent = false;
  41. const suggestions = vue.ref([]);
  42. const highlightedIndex = vue.ref(-1);
  43. const dropdownWidth = vue.ref("");
  44. const activated = vue.ref(false);
  45. const suggestionDisabled = vue.ref(false);
  46. const loading = vue.ref(false);
  47. const listboxId = index$1.useId();
  48. const styles = vue.computed(() => rawAttrs.style);
  49. const suggestionVisible = vue.computed(() => {
  50. const isValidData = suggestions.value.length > 0;
  51. return (isValidData || loading.value) && activated.value;
  52. });
  53. const suggestionLoading = vue.computed(() => !props.hideLoading && loading.value);
  54. const refInput = vue.computed(() => {
  55. if (inputRef.value) {
  56. return Array.from(inputRef.value.$el.querySelectorAll("input"));
  57. }
  58. return [];
  59. });
  60. const onSuggestionShow = () => {
  61. if (suggestionVisible.value) {
  62. dropdownWidth.value = `${inputRef.value.$el.offsetWidth}px`;
  63. }
  64. };
  65. const onHide = () => {
  66. highlightedIndex.value = -1;
  67. };
  68. const getData = async (queryString) => {
  69. if (suggestionDisabled.value)
  70. return;
  71. const cb = (suggestionList) => {
  72. loading.value = false;
  73. if (suggestionDisabled.value)
  74. return;
  75. if (shared.isArray(suggestionList)) {
  76. suggestions.value = suggestionList;
  77. highlightedIndex.value = props.highlightFirstItem ? 0 : -1;
  78. } else {
  79. error.throwError(COMPONENT_NAME, "autocomplete suggestions must be an array");
  80. }
  81. };
  82. loading.value = true;
  83. if (shared.isArray(props.fetchSuggestions)) {
  84. cb(props.fetchSuggestions);
  85. } else {
  86. const result = await props.fetchSuggestions(queryString, cb);
  87. if (shared.isArray(result))
  88. cb(result);
  89. }
  90. };
  91. const debouncedGetData = lodashUnified.debounce(getData, props.debounce);
  92. const handleInput = (value) => {
  93. const valuePresented = !!value;
  94. emit(event.INPUT_EVENT, value);
  95. emit(event.UPDATE_MODEL_EVENT, value);
  96. suggestionDisabled.value = false;
  97. activated.value || (activated.value = valuePresented);
  98. if (!props.triggerOnFocus && !value) {
  99. suggestionDisabled.value = true;
  100. suggestions.value = [];
  101. return;
  102. }
  103. debouncedGetData(value);
  104. };
  105. const handleMouseDown = (event) => {
  106. var _a;
  107. if (disabled.value)
  108. return;
  109. if (((_a = event.target) == null ? void 0 : _a.tagName) !== "INPUT" || refInput.value.includes(document.activeElement)) {
  110. activated.value = true;
  111. }
  112. };
  113. const handleChange = (value) => {
  114. emit(event.CHANGE_EVENT, value);
  115. };
  116. const handleFocus = (evt) => {
  117. var _a;
  118. if (!ignoreFocusEvent) {
  119. activated.value = true;
  120. emit("focus", evt);
  121. const queryString = (_a = props.modelValue) != null ? _a : "";
  122. if (props.triggerOnFocus && !readonly) {
  123. debouncedGetData(String(queryString));
  124. }
  125. } else {
  126. ignoreFocusEvent = false;
  127. }
  128. };
  129. const handleBlur = (evt) => {
  130. setTimeout(() => {
  131. var _a;
  132. if ((_a = popperRef.value) == null ? void 0 : _a.isFocusInsideContent()) {
  133. ignoreFocusEvent = true;
  134. return;
  135. }
  136. activated.value && close();
  137. emit("blur", evt);
  138. });
  139. };
  140. const handleClear = () => {
  141. activated.value = false;
  142. emit(event.UPDATE_MODEL_EVENT, "");
  143. emit("clear");
  144. };
  145. const handleKeyEnter = async () => {
  146. var _a;
  147. if ((_a = inputRef.value) == null ? void 0 : _a.isComposing) {
  148. return;
  149. }
  150. if (suggestionVisible.value && highlightedIndex.value >= 0 && highlightedIndex.value < suggestions.value.length) {
  151. handleSelect(suggestions.value[highlightedIndex.value]);
  152. } else if (props.selectWhenUnmatched) {
  153. emit("select", { value: props.modelValue });
  154. suggestions.value = [];
  155. highlightedIndex.value = -1;
  156. }
  157. };
  158. const handleKeyEscape = (evt) => {
  159. if (suggestionVisible.value) {
  160. evt.preventDefault();
  161. evt.stopPropagation();
  162. close();
  163. }
  164. };
  165. const close = () => {
  166. activated.value = false;
  167. };
  168. const focus = () => {
  169. var _a;
  170. (_a = inputRef.value) == null ? void 0 : _a.focus();
  171. };
  172. const blur = () => {
  173. var _a;
  174. (_a = inputRef.value) == null ? void 0 : _a.blur();
  175. };
  176. const handleSelect = async (item) => {
  177. emit(event.INPUT_EVENT, item[props.valueKey]);
  178. emit(event.UPDATE_MODEL_EVENT, item[props.valueKey]);
  179. emit("select", item);
  180. suggestions.value = [];
  181. highlightedIndex.value = -1;
  182. };
  183. const highlight = (index) => {
  184. var _a, _b;
  185. if (!suggestionVisible.value || loading.value)
  186. return;
  187. if (index < 0) {
  188. highlightedIndex.value = -1;
  189. return;
  190. }
  191. if (index >= suggestions.value.length) {
  192. index = suggestions.value.length - 1;
  193. }
  194. const suggestion = regionRef.value.querySelector(`.${ns.be("suggestion", "wrap")}`);
  195. const suggestionList = suggestion.querySelectorAll(`.${ns.be("suggestion", "list")} li`);
  196. const highlightItem = suggestionList[index];
  197. const scrollTop = suggestion.scrollTop;
  198. const { offsetTop, scrollHeight } = highlightItem;
  199. if (offsetTop + scrollHeight > scrollTop + suggestion.clientHeight) {
  200. suggestion.scrollTop += scrollHeight;
  201. }
  202. if (offsetTop < scrollTop) {
  203. suggestion.scrollTop -= scrollHeight;
  204. }
  205. highlightedIndex.value = index;
  206. (_b = (_a = inputRef.value) == null ? void 0 : _a.ref) == null ? void 0 : _b.setAttribute("aria-activedescendant", `${listboxId.value}-item-${highlightedIndex.value}`);
  207. };
  208. const stopHandle = core.onClickOutside(listboxRef, () => {
  209. var _a;
  210. if ((_a = popperRef.value) == null ? void 0 : _a.isFocusInsideContent())
  211. return;
  212. suggestionVisible.value && close();
  213. });
  214. vue.onBeforeUnmount(() => {
  215. stopHandle == null ? void 0 : stopHandle();
  216. });
  217. vue.onMounted(() => {
  218. var _a;
  219. const inputElement = (_a = inputRef.value) == null ? void 0 : _a.ref;
  220. if (!inputElement)
  221. return;
  222. [
  223. { key: "role", value: "textbox" },
  224. { key: "aria-autocomplete", value: "list" },
  225. { key: "aria-controls", value: "id" },
  226. {
  227. key: "aria-activedescendant",
  228. value: `${listboxId.value}-item-${highlightedIndex.value}`
  229. }
  230. ].forEach(({ key, value }) => inputElement.setAttribute(key, value));
  231. readonly = inputElement.hasAttribute("readonly");
  232. });
  233. expose({
  234. highlightedIndex,
  235. activated,
  236. loading,
  237. inputRef,
  238. popperRef,
  239. suggestions,
  240. handleSelect,
  241. handleKeyEnter,
  242. focus,
  243. blur,
  244. close,
  245. highlight,
  246. getData
  247. });
  248. return (_ctx, _cache) => {
  249. return vue.openBlock(), vue.createBlock(vue.unref(index$2.ElTooltip), {
  250. ref_key: "popperRef",
  251. ref: popperRef,
  252. visible: vue.unref(suggestionVisible),
  253. placement: _ctx.placement,
  254. "fallback-placements": ["bottom-start", "top-start"],
  255. "popper-class": [vue.unref(ns).e("popper"), _ctx.popperClass],
  256. teleported: _ctx.teleported,
  257. "append-to": _ctx.appendTo,
  258. "gpu-acceleration": false,
  259. pure: "",
  260. "manual-mode": "",
  261. effect: "light",
  262. trigger: "click",
  263. transition: `${vue.unref(ns).namespace.value}-zoom-in-top`,
  264. persistent: "",
  265. role: "listbox",
  266. onBeforeShow: onSuggestionShow,
  267. onHide
  268. }, {
  269. content: vue.withCtx(() => [
  270. vue.createElementVNode("div", {
  271. ref_key: "regionRef",
  272. ref: regionRef,
  273. class: vue.normalizeClass([vue.unref(ns).b("suggestion"), vue.unref(ns).is("loading", vue.unref(suggestionLoading))]),
  274. style: vue.normalizeStyle({
  275. [_ctx.fitInputWidth ? "width" : "minWidth"]: dropdownWidth.value,
  276. outline: "none"
  277. }),
  278. role: "region"
  279. }, [
  280. _ctx.$slots.header ? (vue.openBlock(), vue.createElementBlock("div", {
  281. key: 0,
  282. class: vue.normalizeClass(vue.unref(ns).be("suggestion", "header")),
  283. onClick: vue.withModifiers(() => {
  284. }, ["stop"])
  285. }, [
  286. vue.renderSlot(_ctx.$slots, "header")
  287. ], 10, ["onClick"])) : vue.createCommentVNode("v-if", true),
  288. vue.createVNode(vue.unref(index$3.ElScrollbar), {
  289. id: vue.unref(listboxId),
  290. tag: "ul",
  291. "wrap-class": vue.unref(ns).be("suggestion", "wrap"),
  292. "view-class": vue.unref(ns).be("suggestion", "list"),
  293. role: "listbox"
  294. }, {
  295. default: vue.withCtx(() => [
  296. vue.unref(suggestionLoading) ? (vue.openBlock(), vue.createElementBlock("li", { key: 0 }, [
  297. vue.renderSlot(_ctx.$slots, "loading", {}, () => [
  298. vue.createVNode(vue.unref(index$4.ElIcon), {
  299. class: vue.normalizeClass(vue.unref(ns).is("loading"))
  300. }, {
  301. default: vue.withCtx(() => [
  302. vue.createVNode(vue.unref(iconsVue.Loading))
  303. ]),
  304. _: 1
  305. }, 8, ["class"])
  306. ])
  307. ])) : (vue.openBlock(true), vue.createElementBlock(vue.Fragment, { key: 1 }, vue.renderList(suggestions.value, (item, index) => {
  308. return vue.openBlock(), vue.createElementBlock("li", {
  309. id: `${vue.unref(listboxId)}-item-${index}`,
  310. key: index,
  311. class: vue.normalizeClass({ highlighted: highlightedIndex.value === index }),
  312. role: "option",
  313. "aria-selected": highlightedIndex.value === index,
  314. onClick: ($event) => handleSelect(item)
  315. }, [
  316. vue.renderSlot(_ctx.$slots, "default", { item }, () => [
  317. vue.createTextVNode(vue.toDisplayString(item[_ctx.valueKey]), 1)
  318. ])
  319. ], 10, ["id", "aria-selected", "onClick"]);
  320. }), 128))
  321. ]),
  322. _: 3
  323. }, 8, ["id", "wrap-class", "view-class"]),
  324. _ctx.$slots.footer ? (vue.openBlock(), vue.createElementBlock("div", {
  325. key: 1,
  326. class: vue.normalizeClass(vue.unref(ns).be("suggestion", "footer")),
  327. onClick: vue.withModifiers(() => {
  328. }, ["stop"])
  329. }, [
  330. vue.renderSlot(_ctx.$slots, "footer")
  331. ], 10, ["onClick"])) : vue.createCommentVNode("v-if", true)
  332. ], 6)
  333. ]),
  334. default: vue.withCtx(() => [
  335. vue.createElementVNode("div", {
  336. ref_key: "listboxRef",
  337. ref: listboxRef,
  338. class: vue.normalizeClass([vue.unref(ns).b(), _ctx.$attrs.class]),
  339. style: vue.normalizeStyle(vue.unref(styles)),
  340. role: "combobox",
  341. "aria-haspopup": "listbox",
  342. "aria-expanded": vue.unref(suggestionVisible),
  343. "aria-owns": vue.unref(listboxId)
  344. }, [
  345. vue.createVNode(vue.unref(index$5.ElInput), vue.mergeProps({
  346. ref_key: "inputRef",
  347. ref: inputRef
  348. }, vue.mergeProps(vue.unref(passInputProps), _ctx.$attrs), {
  349. "model-value": _ctx.modelValue,
  350. disabled: vue.unref(disabled),
  351. onInput: handleInput,
  352. onChange: handleChange,
  353. onFocus: handleFocus,
  354. onBlur: handleBlur,
  355. onClear: handleClear,
  356. onKeydown: [
  357. vue.withKeys(vue.withModifiers(($event) => highlight(highlightedIndex.value - 1), ["prevent"]), ["up"]),
  358. vue.withKeys(vue.withModifiers(($event) => highlight(highlightedIndex.value + 1), ["prevent"]), ["down"]),
  359. vue.withKeys(handleKeyEnter, ["enter"]),
  360. vue.withKeys(close, ["tab"]),
  361. vue.withKeys(handleKeyEscape, ["esc"])
  362. ],
  363. onMousedown: handleMouseDown
  364. }), vue.createSlots({
  365. _: 2
  366. }, [
  367. _ctx.$slots.prepend ? {
  368. name: "prepend",
  369. fn: vue.withCtx(() => [
  370. vue.renderSlot(_ctx.$slots, "prepend")
  371. ])
  372. } : void 0,
  373. _ctx.$slots.append ? {
  374. name: "append",
  375. fn: vue.withCtx(() => [
  376. vue.renderSlot(_ctx.$slots, "append")
  377. ])
  378. } : void 0,
  379. _ctx.$slots.prefix ? {
  380. name: "prefix",
  381. fn: vue.withCtx(() => [
  382. vue.renderSlot(_ctx.$slots, "prefix")
  383. ])
  384. } : void 0,
  385. _ctx.$slots.suffix ? {
  386. name: "suffix",
  387. fn: vue.withCtx(() => [
  388. vue.renderSlot(_ctx.$slots, "suffix")
  389. ])
  390. } : void 0
  391. ]), 1040, ["model-value", "disabled", "onKeydown"])
  392. ], 14, ["aria-expanded", "aria-owns"])
  393. ]),
  394. _: 3
  395. }, 8, ["visible", "placement", "popper-class", "teleported", "append-to", "transition"]);
  396. };
  397. }
  398. });
  399. var Autocomplete = /* @__PURE__ */ pluginVue_exportHelper["default"](_sfc_main, [["__file", "autocomplete.vue"]]);
  400. exports["default"] = Autocomplete;
  401. //# sourceMappingURL=autocomplete2.js.map