UNPKG

vue3-select

Version:

A maintained fork of vue-select for Vue 3

1,383 lines (1,382 loc) 41.9 kB
var x = Object.defineProperty, I = Object.defineProperties; var N = Object.getOwnPropertyDescriptors; var A = Object.getOwnPropertySymbols; var j = Object.prototype.hasOwnProperty, K = Object.prototype.propertyIsEnumerable; var k = (e, t, s) => t in e ? x(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s, p = (e, t) => { for (var s in t || (t = {})) j.call(t, s) && k(e, s, t[s]); if (A) for (var s of A(t)) K.call(t, s) && k(e, s, t[s]); return e; }, O = (e, t) => I(e, N(t)); import { openBlock as a, createElementBlock as r, createElementVNode as f, resolveDirective as B, normalizeClass as T, renderSlot as d, normalizeProps as h, guardReactiveProps as c, withDirectives as y, Fragment as P, renderList as D, createTextVNode as _, toDisplayString as v, createBlock as w, resolveDynamicComponent as S, createCommentVNode as V, mergeProps as C, toHandlers as R, vShow as F, createVNode as z, Transition as $, withCtx as G, withModifiers as E } from "vue"; const U = { props: { autoscroll: { type: Boolean, default: !0 } }, watch: { typeAheadPointer() { this.autoscroll && this.maybeAdjustScroll(); }, open(e) { this.autoscroll && e && this.$nextTick(() => this.maybeAdjustScroll()); } }, methods: { /** * Adjust the scroll position of the dropdown list * if the current pointer is outside of the * overflow bounds. * @returns {*} */ maybeAdjustScroll() { var t; const e = ((t = this.$refs.dropdownMenu) == null ? void 0 : t.children[this.typeAheadPointer]) || !1; if (e) { const s = this.getDropdownViewport(), { top: n, bottom: l, height: i } = e.getBoundingClientRect(); if (n < s.top) return this.$refs.dropdownMenu.scrollTop = e.offsetTop; if (l > s.bottom) return this.$refs.dropdownMenu.scrollTop = e.offsetTop - (s.height - i) + 200; } }, /** * The currently viewable portion of the dropdownMenu. * @returns {{top: (string|*|number), bottom: *}} */ getDropdownViewport() { return this.$refs.dropdownMenu ? this.$refs.dropdownMenu.getBoundingClientRect() : { height: 0, top: 0, bottom: 0 }; } } }, q = { data() { return { typeAheadPointer: -1 }; }, watch: { filteredOptions() { for (let e = 0; e < this.filteredOptions.length; e++) if (this.selectable(this.filteredOptions[e])) { this.typeAheadPointer = e; break; } }, open(e) { e && this.typeAheadToLastSelected(); }, selectedValue() { this.open && this.typeAheadToLastSelected(); } }, methods: { updateTypeAheadPointer(e) { this.typeAheadPointer = e; }, /** * Move the typeAheadPointer visually up the list by * setting it to the previous selectable option. * @return {void} */ typeAheadUp() { for (let e = this.typeAheadPointer - 1; e >= 0; e--) if (this.selectable(this.filteredOptions[e])) { this.updateTypeAheadPointer(e); break; } }, /** * Move the typeAheadPointer visually down the list by * setting it to the next selectable option. * @return {void} */ typeAheadDown() { for (let e = this.typeAheadPointer + 1; e < this.filteredOptions.length; e++) if (this.selectable(this.filteredOptions[e])) { this.updateTypeAheadPointer(e); break; } }, /** * Select the option at the current typeAheadPointer position. * Optionally clear the search input on selection. * @return {void} */ typeAheadSelect() { const e = this.filteredOptions[this.typeAheadPointer]; e && this.selectable(e) && this.select(e); }, /** * Moves the pointer to the last selected option. */ typeAheadToLastSelected() { var e; if (this.filteredOptions[0] && typeof this.filteredOptions[0] == "object") { const t = this.label, s = this.filteredOptions.map((n) => n[t]).indexOf((e = this.selectedValue[this.selectedValue.length - 1]) == null ? void 0 : e[t]); this.updateTypeAheadPointer( this.selectedValue.length !== 0 ? s : -1 ); return; } this.updateTypeAheadPointer( this.selectedValue.length !== 0 ? this.filteredOptions.indexOf( this.selectedValue[this.selectedValue.length - 1] ) : -1 ); } } }, J = { props: { /** * Toggles the adding of a 'loading' class to the main * .v-select wrapper. Useful to control UI state when * results are being processed through AJAX. */ loading: { type: Boolean, default: !1 } }, data() { return { mutableLoading: !1 }; }, watch: { /** * Anytime the search string changes, emit the * 'search' event. The event is passed with two * parameters: the search string, and a function * that accepts a boolean parameter to toggle the * loading state. * * @emits search */ search() { this.$emit("search", this.search, this.toggleLoading); }, /** * Sync the loading prop with the internal * mutable loading value. * @param val */ loading(e) { this.mutableLoading = e; } }, methods: { /** * Toggle this.loading. Optionally pass a boolean * value. If no value is provided, this.loading * will be set to the opposite of it's current value. * @param toggle Boolean * @returns {*} */ toggleLoading(e = null) { return e == null ? this.mutableLoading = !this.mutableLoading : this.mutableLoading = e; } } }, L = (e, t) => { const s = e.__vccOpts || e; for (const [n, l] of t) s[n] = l; return s; }, H = {}, X = { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24" }, Y = /* @__PURE__ */ f("path", { fill: "#888888", d: "M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z" }, null, -1), Q = [ Y ]; function Z(e, t) { return a(), r("svg", X, Q); } const W = /* @__PURE__ */ L(H, [["render", Z]]), ee = {}, te = { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, se = /* @__PURE__ */ f("path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M5.23017 7.20938C5.52875 6.92228 6.00353 6.93159 6.29063 7.23017L10 11.1679L13.7094 7.23017C13.9965 6.93159 14.4713 6.92228 14.7698 7.20938C15.0684 7.49647 15.0777 7.97125 14.7906 8.26983L10.5406 12.7698C10.3992 12.9169 10.204 13 10 13C9.79599 13 9.60078 12.9169 9.45938 12.7698L5.20938 8.26983C4.92228 7.97125 4.93159 7.49647 5.23017 7.20938Z", fill: "#0F172A" }, null, -1), ie = [ se ]; function oe(e, t) { return a(), r("svg", te, ie); } const ne = /* @__PURE__ */ L(ee, [["render", oe]]), M = { Deselect: W, OpenIndicator: ne }, le = { mounted(e, { instance: t }) { if (t.appendToBody) { const { height: s, top: n, left: l, width: i } = t.$refs.toggle.getBoundingClientRect(); let m = window.scrollX || window.pageXOffset, b = window.scrollY || window.pageYOffset; e.unbindPosition = t.calculatePosition(e, t, { width: i + "px", left: m + l + "px", top: b + n + s + "px" }), document.body.appendChild(e); } }, unmounted(e, { instance: t }) { t.appendToBody && (e.unbindPosition && typeof e.unbindPosition == "function" && e.unbindPosition(), e.parentNode && e.parentNode.removeChild(e)); } }, ae = { beforeMount: (e, t) => { e.clickOutsideEvent = (s) => { s.stopPropagation(), s.target !== e && !e.contains(s.target) && t.value(s); }, setTimeout(() => { document.addEventListener("click", e.clickOutsideEvent); }, 0); }, unmounted: (e) => { const t = e.clickOutsideEvent; delete e.clickOutsideEvent, document.removeEventListener("click", t); } }; function re(e) { const t = {}; return Object.keys(e).sort().forEach((s) => { t[s] = e[s]; }), JSON.stringify(t); } let de = 0; function he() { return ++de; } const ce = { components: p({}, M), directives: { appendToBody: le, clickOutside: ae }, mixins: [U, q, J], compatConfig: { MODE: 3 }, emits: [ "open", "close", "update:modelValue", "search", "search:compositionstart", "search:compositionend", "search:keydown", "search:blur", "search:focus", "search:input", "option:created", "option:selecting", "option:selected", "option:deselecting", "option:deselected" ], props: { /** * Contains the currently selected value. Very similar to a * `value` attribute on an <input>. You can listen for changes * with the 'input' event. * @type {Object|String|Array|null} */ // eslint-disable-next-line vue/require-default-prop,vue/require-prop-types modelValue: {}, /** * An object with any custom components that you'd like to overwrite * the default implementation of in your app. The keys in this object * will be merged with the defaults. * @see https://vue-select.org/guide/components.html * @type {Function} */ components: { type: Object, default: () => ({}) }, /** * An array of strings or objects to be used as dropdown choices. * If you are using an array of objects, vue-select will look for * a `label` key (ex. [{label: 'This is Foo', value: 'foo'}]). A * custom label key can be set with the `label` prop. * @type {Array} */ options: { type: Array, default() { return []; } }, /** * Disable the entire component. * @type {Boolean} */ disabled: { type: Boolean, default: !1 }, /** * Can the user clear the selected property. * @type {Boolean} */ clearable: { type: Boolean, default: !0 }, /** * Can the user deselect an option by clicking it from * within the dropdown. * @type {Boolean} */ deselectFromDropdown: { type: Boolean, default: !1 }, /** * Sets the value of the 'aria-label' for the search `<input>`. * @type {String} */ ariaLabel: { type: String, default: "Search for option" }, /** * Enable/disable filtering the options. * @type {Boolean} */ searchable: { type: Boolean, default: !0 }, /** * Equivalent to the `multiple` attribute on a `<select>` input. * @type {Boolean} */ multiple: { type: Boolean, default: !1 }, /** * Equivalent to the `placeholder` attribute on an `<input>`. * @type {String} */ placeholder: { type: String, default: "" }, /** * Sets a Vue transition property on the `.vs__dropdown-menu`. * @type {String} */ transition: { type: String, default: "vs__fade" }, /** * Enables/disables clearing the search text when an option is selected. * @type {Boolean} */ clearSearchOnSelect: { type: Boolean, default: !0 }, /** * Close a dropdown when an option is chosen. Set to false to keep the dropdown * open (useful when combined with multi-select, for example) * @type {Boolean} */ closeOnSelect: { type: Boolean, default: !0 }, /** * Tells vue-select what key to use when generating option * labels when each `option` is an object. * @type {String} */ label: { type: String, default: "label" }, /** * Value of the 'autocomplete' field of the input * element. * @type {String} */ autocomplete: { type: String, default: "off" }, /** * When working with objects, the reduce * prop allows you to transform a given * object to only the information you * want passed to a v-model binding * or @input event. */ reduce: { type: Function, default: (e) => e }, /** * Decides whether an option is selectable or not. Not selectable options * are displayed but disabled and cannot be selected. * * @type {Function} * @since 3.3.0 * @param {Object|String} option * @return {Boolean} */ selectable: { type: Function, default: (e) => !0 }, /** * Callback to generate the label text. If {option} * is an object, returns option[this.label] by default. * * Label text is used for filtering comparison and * displaying. If you only need to adjust the * display, you should use the `option` and * `selected-option` slots. * * @type {Function} * @param {Object || String} option * @return {String} */ getOptionLabel: { type: Function, default(e) { return typeof e == "object" ? e.hasOwnProperty(this.label) ? e[this.label] : console.warn( `[vue-select warn]: Label key "option.${this.label}" does not exist in options object ${JSON.stringify(e)}. https://vue-select.org/api/props.html#getoptionlabel` ) : e; } }, /** * Generate a unique identifier for each option. If `option` * is an object and `option.hasOwnProperty('id')` exists, * `option.id` is used by default, otherwise the option * will be serialized to JSON. * * If you are supplying a lot of options, you should * provide your own keys, as JSON.stringify can be * slow with lots of objects. * * The result of this function *must* be unique. * * @type {Function} * @param {Object || String} option * @return {String} */ getOptionKey: { type: Function, default(e) { if (typeof e != "object") return e; try { return e.hasOwnProperty("id") ? e.id : re(e); } catch (t) { return console.warn(`[vue-select warn]: Could not stringify this option to generate unique key. Please provide'getOptionKey' prop to return a unique key for each option. https://vue-select.org/api/props.html#getoptionkey`, e, t); } } }, /** * Select the current value if selectOnTab is enabled * @deprecated since 3.3 */ onTab: { type: Function, default: function() { this.selectOnTab && !this.isComposing && this.typeAheadSelect(); } }, /** * Enable/disable creating options from searchEl. * @type {Boolean} */ taggable: { type: Boolean, default: !1 }, /** * Set the tabindex for the input field. * @type {Number} */ tabindex: { type: Number, default: null }, /** * When true, newly created tags will be added to * the options list. * @type {Boolean} */ pushTags: { type: Boolean, default: !1 }, /** * When true, existing options will be filtered * by the search text. Should not be used in conjunction * with taggable. * @type {Boolean} */ filterable: { type: Boolean, default: !0 }, /** * Callback to determine if the provided option should * match the current search text. Used to determine * if the option should be displayed. * @type {Function} * @param {Object || String} option * @param {String} label * @param {String} search * @return {Boolean} */ filterBy: { type: Function, default(e, t, s) { return (t || "").toLocaleLowerCase().indexOf(s.toLocaleLowerCase()) > -1; } }, /** * Callback to filter results when search text * is provided. Default implementation loops * each option, and returns the result of * this.filterBy. * @type {Function} * @param {Array} list of options * @param {String} search text * @param {Object} vSelect instance * @return {Boolean} */ filter: { type: Function, default(e, t) { return e.filter((s) => { let n = this.getOptionLabel(s); return typeof n == "number" && (n = n.toString()), this.filterBy(s, n, t); }); } }, /** * User defined function for adding Options * @type {Function} */ createOption: { type: Function, default(e) { return typeof this.optionList[0] == "object" ? { [this.label]: e } : e; } }, /** * When false, updating the options will not reset the selected value. Accepts * a `boolean` or `function` that returns a `boolean`. If defined as a function, * it will receive the params listed below. * * @since 3.4 - Type changed to {Boolean|Function} * * @type {Boolean|Function} * @param {Array} newOptions * @param {Array} oldOptions * @param {Array} selectedValue */ resetOnOptionsChange: { default: !1, validator: (e) => ["function", "boolean"].includes(typeof e) }, /** * If search text should clear on blur * @return {Boolean} True when single and clearSearchOnSelect */ clearSearchOnBlur: { type: Function, default: function({ clearSearchOnSelect: e, multiple: t }) { return e && !t; } }, /** * Disable the dropdown entirely. * @type {Boolean} */ noDrop: { type: Boolean, default: !1 }, /** * Sets the id of the input element. * @type {String} * @default {null} */ // eslint-disable-next-line vue/require-default-prop inputId: { type: String }, /** * Sets RTL support. Accepts 'ltr', 'rtl', 'auto'. * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir * @type {String} * @default 'auto' */ dir: { type: String, default: "auto" }, /** * When true, hitting the 'tab' key will select the current select value * @type {Boolean} * @deprecated since 3.3 - use selectOnKeyCodes instead */ selectOnTab: { type: Boolean, default: !1 }, /** * Keycodes that will select the current option. * @type Array */ selectOnKeyCodes: { type: Array, default: () => [13] }, /** * Query Selector used to find the search input * when the 'search' scoped slot is used. * * Must be a valid CSS selector string. * * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector * @type {String} */ searchInputQuerySelector: { type: String, default: "[type=search]" }, /** * Used to modify the default keydown events map * for the search input. Can be used to implement * custom behaviour for key presses. */ mapKeydown: { type: Function, /** * @param map {Object} * @param vm {VueSelect} * @return {Object} */ default: (e, t) => e }, /** * Append the dropdown element to the end of the body * and size/position it dynamically. Use it if you have * overflow or z-index issues. * @type {Boolean} */ appendToBody: { type: Boolean, default: !1 }, /** * When `appendToBody` is true, this function is responsible for * positioning the drop down list. * * If a function is returned from `calculatePosition`, it will * be called when the drop down list is removed from the DOM. * This allows for any garbage collection you may need to do. * * @since v3.7.0 * @see http://vue-select.org/guide/positioning.html */ calculatePosition: { type: Function, /** * @param dropdownList {HTMLUListElement} * @param component {Vue} current instance of vue select * @param width {string} calculated width in pixels of the dropdown menu * @param top {string} absolute position top value in pixels relative to the document * @param left {string} absolute position left value in pixels relative to the document * @return {function|void} */ default(e, t, { width: s, top: n, left: l }) { e.style.top = n, e.style.left = l, e.style.width = s; } }, /** * Determines whether the dropdown should be open. * Receives the component instance as the only argument. * * @since v3.12.0 * @return boolean */ dropdownShouldOpen: { type: Function, default({ noDrop: e, open: t, mutableLoading: s }) { return e ? !1 : t && !s; } }, /** * A unique identifier used to generate IDs in HTML. * Must be unique for every instance of the component. */ uid: { type: [String, Number], default: () => he() } }, data() { return { search: "", open: !1, isComposing: !1, pushedTags: [], // eslint-disable-next-line vue/no-reserved-keys _value: [], // Internal value managed by Vue Select if no `value` prop is passed deselectButtons: [] }; }, computed: { isReducingValues() { return this.$props.reduce !== this.$options.props.reduce.default; }, /** * Determine if the component needs to * track the state of values internally. * @return {boolean} */ isTrackingValues() { return typeof this.modelValue == "undefined" || this.isReducingValues; }, /** * The options that are currently selected. * @return {Array} */ selectedValue() { let e = this.modelValue; return this.isTrackingValues && (e = this.$data._value), e != null && e !== "" ? Array.prototype.concat(e) : []; }, /** * The options available to be chosen * from the dropdown, including any * tags that have been pushed. * * @return {Array} */ optionList() { return this.options.concat(this.pushTags ? this.pushedTags : []); }, /** * Find the search input DOM element. * @returns {HTMLInputElement} */ searchEl() { return this.$slots.search ? this.$refs.selectedOptions.querySelector( this.searchInputQuerySelector ) : this.$refs.search; }, /** * The object to be bound to the $slots.search slot. * @returns {Object} */ scope() { const e = { search: this.search, loading: this.loading, searching: this.searching, filteredOptions: this.filteredOptions }; return { search: { attributes: p({ disabled: this.disabled, placeholder: this.searchPlaceholder, tabindex: this.tabindex, readonly: !this.searchable, id: this.inputId, "aria-autocomplete": "list", "aria-labelledby": `vs${this.uid}__combobox`, "aria-controls": `vs${this.uid}__listbox`, ref: "search", type: "search", autocomplete: this.autocomplete, value: this.search }, this.dropdownOpen && this.filteredOptions[this.typeAheadPointer] ? { "aria-activedescendant": `vs${this.uid}__option-${this.typeAheadPointer}` } : {}), events: { compositionstart: () => this.isComposing = !0, compositionend: () => this.isComposing = !1, keydown: this.onSearchKeyDown, blur: this.onSearchBlur, focus: this.onSearchFocus, input: (t) => this.search = t.target.value } }, spinner: { loading: this.mutableLoading }, noOptions: { search: this.search, loading: this.mutableLoading, searching: this.searching }, openIndicator: { attributes: { ref: "openIndicator", role: "presentation", class: "vs__open-indicator" } }, listHeader: e, listFooter: e, header: O(p({}, e), { deselect: this.deselect }), footer: O(p({}, e), { deselect: this.deselect }) }; }, /** * Returns an object containing the child components * that will be used throughout the component. The * `component` prop can be used to overwrite the defaults. * * @return {Object} */ childComponents() { return p(p({}, M), this.components); }, /** * Holds the current state of the component. * @return {Object} */ stateClasses() { return { "vs--open": this.dropdownOpen, "vs--single": !this.multiple, "vs--multiple": this.multiple, "vs--searching": this.searching && !this.noDrop, "vs--searchable": this.searchable && !this.noDrop, "vs--unsearchable": !this.searchable, "vs--loading": this.mutableLoading, "vs--disabled": this.disabled, "vs--no-drop": this.noDrop }; }, /** * Return the current state of the * search input * @return {Boolean} True if non empty value */ searching() { return !!this.search; }, /** * Return the current state of the * dropdown menu. * @return {Boolean} True if open */ dropdownOpen() { return this.dropdownShouldOpen(this); }, /** * Return the placeholder string if it's set * & there is no value selected. * @return {String} Placeholder text */ searchPlaceholder() { return this.isValueEmpty && this.placeholder ? this.placeholder : void 0; }, /** * The currently displayed options, filtered * by the search elements value. If tagging * true, the search text will be prepended * if it doesn't already exist. * * @return {array} */ filteredOptions() { const e = this.normalizeOptGroups( Array.prototype.concat(this.optionList) ); if (!this.filterable && !this.taggable) return e; const t = this.search.length ? this.filter(e, this.search, this) : e; if (this.taggable && this.search.length) { const s = this.createOption(this.search); this.optionExists(s) || t.unshift(s); } return t; }, /** * Check if there aren't any options selected. * @return {Boolean} */ isValueEmpty() { return this.selectedValue.length === 0; }, /** * Determines if the clear button should be displayed. * @return {Boolean} */ showClearButton() { return !this.multiple && this.clearable && !this.open && !this.isValueEmpty; } }, watch: { /** * Maybe reset the value * when options change. * Make sure selected option * is correct. * @return {[type]} [description] */ options(e, t) { const s = () => typeof this.resetOnOptionsChange == "function" ? this.resetOnOptionsChange( e, t, this.selectedValue ) : this.resetOnOptionsChange; !this.taggable && s() && this.clearSelection(), this.modelValue && this.isTrackingValues && this.setInternalValueFromOptions(this.modelValue); }, /** * Make sure to update internal * value if prop changes outside */ modelValue: { immediate: !0, handler(e) { this.isTrackingValues && this.setInternalValueFromOptions(e); } }, /** * Always reset the value when * the multiple prop changes. * @return {void} */ multiple() { this.clearSelection(); }, open(e) { this.$emit(e ? "open" : "close"); } }, created() { this.mutableLoading = this.loading; }, methods: { clickOutside() { this.open && !this.searchable && (this.open = !1); }, /** * Make sure tracked value is * one option if possible. * @param {Object|String} value * @return {void} */ setInternalValueFromOptions(e) { Array.isArray(e) ? this.$data._value = e.map( (t) => this.findOptionFromReducedValue(t) ) : this.$data._value = this.findOptionFromReducedValue(e); }, /** * Select or deselect a given option. * Allow deselect if clearable or if not the only selected option. * @param {Object|String} option * @return {void} */ select(e) { this.$emit("option:selecting", e), this.isOptionSelected(e) ? this.deselectFromDropdown && (this.clearable || this.multiple && this.selectedValue.length > 1) && this.deselect(e) : (this.taggable && !this.optionExists(e) && (this.$emit("option:created", e), this.pushTag(e)), this.multiple && (e = this.selectedValue.concat(e)), this.updateValue(e), this.$emit("option:selected", e)), this.onAfterSelect(e); }, /** * De-select a given option. * @param {Object|String} option * @return {void} */ deselect(e) { this.$emit("option:deselecting", e), this.updateValue( this.selectedValue.filter((t) => !this.optionComparator(t, e)) ), this.$emit("option:deselected", e); }, /** * Clears the currently selected value(s) * @return {void} */ clearSelection() { this.updateValue(this.multiple ? [] : null); }, /** * Called from this.select after each selection. * @param {Object|String} option * @return {void} */ onAfterSelect(e) { this.closeOnSelect && (this.open = !this.open, this.searchEl.blur()), this.clearSearchOnSelect && (this.search = ""); }, /** * Accepts a selected value, updates local * state when required, and triggers the * input event. * * @emits input * @param value */ updateValue(e) { typeof this.modelValue == "undefined" && (this.$data._value = e), e !== null && (Array.isArray(e) ? e = e.map((t) => this.reduce(t)) : e = this.reduce(e)), this.$emit("update:modelValue", e); }, /** * Toggle the visibility of the dropdown menu. * @param {Event} event * @return {void} */ toggleDropdown(e) { const t = e.target !== this.searchEl; t && e.preventDefault(); const s = [ ...this.deselectButtons || [], this.$refs.clearButton ]; if (this.searchEl === void 0 || s.filter(Boolean).some((n) => n.contains(e.target) || n === e.target)) { e.preventDefault(); return; } this.open && t ? this.searchEl.blur() : this.open && !t && !this.searchable ? this.open = !1 : this.disabled || (this.open = !0, this.searchEl.focus()); }, /** * Check if the given option is currently selected. * @param {Object|String} option * @return {Boolean} True when selected | False otherwise */ isOptionSelected(e) { return this.selectedValue.some( (t) => this.optionComparator(t, e) ); }, /** * Check if the given option is optgroup label * @param {Object|String} option * @return {Boolean} */ isOptGroupOption(e) { return !!e.optgroup; }, /** * Can the current option be removed via the dropdown? */ isOptionDeselectable(e) { return this.isOptionSelected(e) && this.deselectFromDropdown; }, /** * Determine if two option objects are matching. * * @param a {Object} * @param b {Object} * @returns {boolean} */ optionComparator(e, t) { return this.getOptionKey(e) === this.getOptionKey(t); }, /** * Finds an option from the options * where a reduced value matches * the passed in value. * * @param value {Object} * @returns {*} */ findOptionFromReducedValue(e) { const t = (n) => JSON.stringify(this.reduce(n)) === JSON.stringify(e), s = [...this.options, ...this.pushedTags].filter(t); return s.length === 1 ? s[0] : s.find( (n) => this.optionComparator(n, this.$data._value) ) || e; }, /** * Delete the value on Delete keypress when there is no * text in the search input, & there's tags to delete * @return {this.value} */ maybeDeleteValue() { if (!this.searchEl.value.length && this.selectedValue && this.selectedValue.length && this.clearable) { let e = null; this.multiple && (e = [ ...this.selectedValue.slice(0, this.selectedValue.length - 1) ]), this.updateValue(e); } }, /** * Determine if an option exists * within this.optionList array. * * @param {Object || String} option * @return {boolean} */ optionExists(e) { return this.optionList.some( (t) => this.optionComparator(t, e) ); }, /** * Ensures that options are always * passed as objects to scoped slots. * @param option * @return {*} */ normalizeOptionForSlot(e) { return typeof e == "object" ? e : { [this.label]: e }; }, /** * If push-tags is true, push the * given option to `this.pushedTags`. * * @param {Object || String} option * @return {void} */ pushTag(e) { this.pushedTags.push(e); }, /** * If there is any text in the search input, remove it. * Otherwise, blur the search input to close the dropdown. * @return {void} */ onEscape() { this.search.length ? this.search = "" : this.searchEl.blur(); }, /** * Close the dropdown on blur. * @emits {search:blur} * @return {void} */ onSearchBlur() { if (this.open = !1, this.mousedown && !this.searching) this.mousedown = !1; else { const { clearSearchOnSelect: e, multiple: t } = this; this.clearSearchOnBlur({ clearSearchOnSelect: e, multiple: t }) && (this.search = ""), this.$emit("search:blur"); return; } if (this.search.length === 0 && this.options.length === 0) { this.$emit("search:blur"); return; } }, /** * Open the dropdown on focus. * @emits {search:focus} * @return {void} */ onSearchFocus() { this.open = !0, this.$emit("search:focus"); }, /** * Event-Handler to help workaround IE11 (probably fixes 10 as well) * firing a `blur` event when clicking * the dropdown's scrollbar, causing it * to collapse abruptly. * @see https://github.com/sagalbot/vue-select/issues/106 * @return {void} */ onMousedown() { this.mousedown = !0; }, /** * Event-Handler to help workaround IE11 (probably fixes 10 as well) * @see https://github.com/sagalbot/vue-select/issues/106 * @return {void} */ onMouseUp() { this.mousedown = !1; }, /** * Search <input> KeyBoardEvent handler. * @param e {KeyboardEvent} * @return {Function} */ onSearchKeyDown(e) { const t = (l) => (l.preventDefault(), !this.isComposing && this.typeAheadSelect()), s = { // backspace 8: (l) => this.maybeDeleteValue(), // tab 9: (l) => this.onTab(), // esc 27: (l) => this.onEscape(), // up.prevent 38: (l) => (l.preventDefault(), this.typeAheadUp()), // down.prevent 40: (l) => (l.preventDefault(), this.typeAheadDown()) }; this.selectOnKeyCodes.forEach( (l) => s[l] = t ); const n = this.mapKeydown(s, this); if (typeof n[e.keyCode] == "function") return n[e.keyCode](e); }, /** * optgroups * @param {*} options */ normalizeOptGroups(e) { return e.map((t) => t.groupLabel && t.groupOptions && t.groupOptions instanceof Array ? [{ optgroup: t.groupLabel }].concat(t.groupOptions) : [t]).reduce((t, s) => t.concat(s), []); } } }, ue = ["dir"], pe = ["id", "aria-expanded", "aria-owns", "aria-label"], fe = { ref: "selectedOptions", class: "vs__selected-options" }, ge = ["disabled", "title", "aria-label", "onClick"], ye = { ref: "actions", class: "vs__actions" }, me = ["disabled"], be = { class: "vs__spinner" }, Oe = ["id"], _e = ["id", "aria-selected", "onMouseover", "onClick", "onTouchstart"], ve = { key: 0, class: "" }, we = { key: 0, class: "vs__no-options" }, Se = ["id"]; function Ve(e, t, s, n, l, i) { const m = B("click-outside"), b = B("append-to-body"); return a(), r("div", { dir: s.dir, class: T(["v-select", i.stateClasses]) }, [ d(e.$slots, "header", h(c(i.scope.header))), y((a(), r("div", { id: `vs${s.uid}__combobox`, ref: "toggle", class: "vs__dropdown-toggle", role: "combobox", "aria-expanded": i.dropdownOpen.toString(), "aria-owns": `vs${s.uid}__listbox`, "aria-label": s.ariaLabel, onMousedown: t[1] || (t[1] = (o) => i.toggleDropdown(o)) }, [ f("div", fe, [ (a(!0), r(P, null, D(i.selectedValue, (o, u) => d(e.$slots, "selected-option-container", { option: i.normalizeOptionForSlot(o), deselect: i.deselect, multiple: s.multiple, disabled: s.disabled }, () => [ (a(), r("span", { key: s.getOptionKey(o), class: "vs__selected" }, [ d(e.$slots, "selected-option", h(c(i.normalizeOptionForSlot(o))), () => [ _(v(s.getOptionLabel(o)), 1) ]), s.multiple ? (a(), r("button", { key: 0, ref_for: !0, ref: (g) => l.deselectButtons[u] = g, disabled: s.disabled, type: "button", class: "vs__deselect", title: `Deselect ${s.getOptionLabel(o)}`, "aria-label": `Deselect ${s.getOptionLabel(o)}`, onClick: (g) => i.deselect(o) }, [ (a(), w(S(i.childComponents.Deselect))) ], 8, ge)) : V("", !0) ])) ])), 256)), d(e.$slots, "search", h(c(i.scope.search)), () => [ f("input", C({ class: "vs__search" }, i.scope.search.attributes, R(i.scope.search.events, !0)), null, 16) ]) ], 512), f("div", ye, [ y(f("button", { ref: "clearButton", disabled: s.disabled, type: "button", class: "vs__clear", title: "Clear Selected", "aria-label": "Clear Selected", onClick: t[0] || (t[0] = (...o) => i.clearSelection && i.clearSelection(...o)) }, [ (a(), w(S(i.childComponents.Deselect))) ], 8, me), [ [F, i.showClearButton] ]), d(e.$slots, "open-indicator", h(c(i.scope.openIndicator)), () => [ s.noDrop ? V("", !0) : (a(), w(S(i.childComponents.OpenIndicator), h(C({ key: 0 }, i.scope.openIndicator.attributes)), null, 16)) ]), d(e.$slots, "spinner", h(c(i.scope.spinner)), () => [ y(f("div", be, "Loading...", 512), [ [F, e.mutableLoading] ]) ]) ], 512) ], 40, pe)), [ [m, i.clickOutside] ]), z($, { name: s.transition }, { default: G(() => [ i.dropdownOpen ? y((a(), r("ul", { id: `vs${s.uid}__listbox`, ref: "dropdownMenu", key: `vs${s.uid}__listbox`, class: "vs__dropdown-menu", role: "listbox", tabindex: "-1", onMousedown: t[3] || (t[3] = E((...o) => i.onMousedown && i.onMousedown(...o), ["prevent"])), onMouseup: t[4] || (t[4] = (...o) => i.onMouseUp && i.onMouseUp(...o)) }, [ d(e.$slots, "list-header", h(c(i.scope.listHeader))), (a(!0), r(P, null, D(i.filteredOptions, (o, u) => (a(), r("li", { id: `vs${s.uid}__option-${u}`, key: s.getOptionKey(o), role: "option", class: T({ "vs__dropdown-option": !i.isOptGroupOption(o), "vs__dropdown-optgroup-option": i.isOptGroupOption(o), "vs__dropdown-option--deselect": i.isOptionDeselectable(o) && u === e.typeAheadPointer, "vs__dropdown-option--selected": i.isOptionSelected(o), "vs__dropdown-option--highlight": !i.isOptGroupOption(o) && u === e.typeAheadPointer || i.isOptionSelected(o), "vs__dropdown-option--disabled": !s.selectable(o) }), "aria-selected": u === e.typeAheadPointer ? !0 : null, onMouseover: (g) => s.selectable(o) ? e.updateTypeAheadPointer(u) : null, onMouseout: t[2] || (t[2] = (g) => e.updateTypeAheadPointer(-1)), onClick: E((g) => s.selectable(o) ? i.select(o) : null, ["prevent", "stop"]), onTouchstart: (g) => s.selectable(o) ? e.updateTypeAheadPointer(u) : null }, [ o.optgroup ? (a(), r("div", ve, v(o.optgroup), 1)) : d(e.$slots, "option", h(C({ key: 1 }, i.normalizeOptionForSlot(o))), () => [ _(v(s.getOptionLabel(o)), 1) ]) ], 42, _e))), 128)), i.filteredOptions.length === 0 ? (a(), r("li", we, [ d(e.$slots, "no-options", h(c(i.scope.noOptions)), () => [ _(" Sorry, no matching options. ") ]) ])) : V("", !0), d(e.$slots, "list-footer", h(c(i.scope.listFooter))) ], 40, Oe)), [ [b] ]) : (a(), r("ul", { key: 1, id: `vs${s.uid}__listbox`, role: "listbox", style: { display: "none", visibility: "hidden" } }, null, 8, Se)) ]), _: 3 }, 8, ["name"]), d(e.$slots, "footer", h(c(i.scope.footer))) ], 10, ue); } const Ae = /* @__PURE__ */ L(ce, [["render", Ve]]); export { Ae as default };