"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.defaultMenuProps = void 0; require("../../../src/components/VTextField/VTextField.sass"); require("../../../src/components/VSelect/VSelect.sass"); var _VChip = _interopRequireDefault(require("../VChip")); var _VMenu = _interopRequireDefault(require("../VMenu")); var _VSelectList = _interopRequireDefault(require("./VSelectList")); var _VTextField2 = _interopRequireDefault(require("../VTextField/VTextField")); var _comparable = _interopRequireDefault(require("../../mixins/comparable")); var _filterable = _interopRequireDefault(require("../../mixins/filterable")); var _clickOutside = _interopRequireDefault(require("../../directives/click-outside")); var _helpers = require("../../util/helpers"); var _console = require("../../util/console"); var _mixins = _interopRequireDefault(require("../../util/mixins")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var defaultMenuProps = { closeOnClick: false, closeOnContentClick: false, disableKeys: true, openOnClick: false, maxHeight: 300 }; exports.defaultMenuProps = defaultMenuProps; var baseMixins = (0, _mixins.default)(_VTextField2.default, _comparable.default, _filterable.default); /* @vue/component */ var _default2 = baseMixins.extend().extend({ name: 'v-select', directives: { ClickOutside: _clickOutside.default }, props: { appendIcon: { type: String, default: '$vuetify.icons.dropdown' }, attach: { default: false }, cacheItems: Boolean, chips: Boolean, clearable: Boolean, deletableChips: Boolean, dense: Boolean, eager: Boolean, hideSelected: Boolean, items: { type: Array, default: function _default() { return []; } }, itemColor: { type: String, default: 'primary' }, itemDisabled: { type: [String, Array, Function], default: 'disabled' }, itemText: { type: [String, Array, Function], default: 'text' }, itemValue: { type: [String, Array, Function], default: 'value' }, menuProps: { type: [String, Array, Object], default: function _default() { return defaultMenuProps; } }, multiple: Boolean, openOnClear: Boolean, returnObject: Boolean, smallChips: Boolean }, data: function data() { return { cachedItems: this.cacheItems ? this.items : [], content: null, isBooted: false, isMenuActive: false, lastItem: 20, // As long as a value is defined, show it // Otherwise, check if multiple // to determine which default to provide lazyValue: this.value !== undefined ? this.value : this.multiple ? [] : undefined, selectedIndex: -1, selectedItems: [], keyboardLookupPrefix: '', keyboardLookupLastTime: 0 }; }, computed: { /* All items that the select has */ allItems: function allItems() { return this.filterDuplicates(this.cachedItems.concat(this.items)); }, classes: function classes() { return _objectSpread({}, _VTextField2.default.options.computed.classes.call(this), { 'v-select': true, 'v-select--chips': this.hasChips, 'v-select--chips--small': this.smallChips, 'v-select--is-menu-active': this.isMenuActive }); }, /* Used by other components to overwrite */ computedItems: function computedItems() { return this.allItems; }, computedOwns: function computedOwns() { return "list-".concat(this._uid); }, counterValue: function counterValue() { return this.multiple ? this.selectedItems.length : (this.getText(this.selectedItems[0]) || '').toString().length; }, directives: function directives() { return this.isFocused ? [{ name: 'click-outside', value: this.blur, args: { closeConditional: this.closeConditional } }] : undefined; }, dynamicHeight: function dynamicHeight() { return 'auto'; }, hasChips: function hasChips() { return this.chips || this.smallChips; }, hasSlot: function hasSlot() { return Boolean(this.hasChips || this.$scopedSlots.selection); }, isDirty: function isDirty() { return this.selectedItems.length > 0; }, listData: function listData() { var scopeId = this.$vnode && this.$vnode.context.$options._scopeId; var attrs = scopeId ? _defineProperty({}, scopeId, true) : {}; return { attrs: _objectSpread({}, attrs, { id: this.computedOwns }), props: { action: this.multiple, color: this.itemColor, dense: this.dense, hideSelected: this.hideSelected, items: this.virtualizedItems, noDataText: this.$vuetify.lang.t(this.noDataText), selectedItems: this.selectedItems, itemDisabled: this.itemDisabled, itemValue: this.itemValue, itemText: this.itemText }, on: { select: this.selectItem }, scopedSlots: { item: this.$scopedSlots.item } }; }, staticList: function staticList() { if (this.$slots['no-data'] || this.$slots['prepend-item'] || this.$slots['append-item']) { (0, _console.consoleError)('assert: staticList should not be called if slots are used'); } return this.$createElement(_VSelectList.default, this.listData); }, virtualizedItems: function virtualizedItems() { return this.$_menuProps.auto ? this.computedItems : this.computedItems.slice(0, this.lastItem); }, menuCanShow: function menuCanShow() { return true; }, $_menuProps: function $_menuProps() { var normalisedProps = typeof this.menuProps === 'string' ? this.menuProps.split(',') : this.menuProps; if (Array.isArray(normalisedProps)) { normalisedProps = normalisedProps.reduce(function (acc, p) { acc[p.trim()] = true; return acc; }, {}); } return _objectSpread({}, defaultMenuProps, { eager: this.eager, value: this.menuCanShow && this.isMenuActive, nudgeBottom: normalisedProps.offsetY ? 1 : 0 }, normalisedProps); } }, watch: { internalValue: function internalValue(val) { this.initialValue = val; this.setSelectedItems(); }, isBooted: function isBooted() { var _this = this; this.$nextTick(function () { if (_this.content && _this.content.addEventListener) { _this.content.addEventListener('scroll', _this.onScroll, false); } }); }, isMenuActive: function isMenuActive(val) { var _this2 = this; this.$nextTick(function () { return _this2.onMenuActiveChange(val); }); if (!val) return; this.isBooted = true; }, items: { immediate: true, handler: function handler(val) { var _this3 = this; if (this.cacheItems) { // Breaks vue-test-utils if // this isn't calculated // on the next tick this.$nextTick(function () { _this3.cachedItems = _this3.filterDuplicates(_this3.cachedItems.concat(val)); }); } this.setSelectedItems(); } } }, mounted: function mounted() { this.content = this.$refs.menu && this.$refs.menu.$refs.content; }, methods: { /** @public */ blur: function blur(e) { _VTextField2.default.options.methods.blur.call(this, e); this.isMenuActive = false; this.isFocused = false; this.selectedIndex = -1; }, /** @public */ activateMenu: function activateMenu() { if (this.disabled || this.readonly || this.isMenuActive) return; this.isMenuActive = true; }, clearableCallback: function clearableCallback() { var _this4 = this; this.setValue(this.multiple ? [] : undefined); this.$nextTick(function () { return _this4.$refs.input && _this4.$refs.input.focus(); }); if (this.openOnClear) this.isMenuActive = true; }, closeConditional: function closeConditional(e) { return (// Click originates from outside the menu content this.content && !this.content.contains(e.target) && // Click originates from outside the element this.$el && !this.$el.contains(e.target) && e.target !== this.$el ); }, filterDuplicates: function filterDuplicates(arr) { var uniqueValues = new Map(); for (var index = 0; index < arr.length; ++index) { var item = arr[index]; var val = this.getValue(item); // TODO: comparator !uniqueValues.has(val) && uniqueValues.set(val, item); } return Array.from(uniqueValues.values()); }, findExistingIndex: function findExistingIndex(item) { var _this5 = this; var itemValue = this.getValue(item); return (this.internalValue || []).findIndex(function (i) { return _this5.valueComparator(_this5.getValue(i), itemValue); }); }, genChipSelection: function genChipSelection(item, index) { var _this6 = this; var isDisabled = this.disabled || this.readonly || this.getDisabled(item); return this.$createElement(_VChip.default, { staticClass: 'v-chip--select', attrs: { tabindex: -1 }, props: { close: this.deletableChips && !isDisabled, disabled: isDisabled, inputValue: index === this.selectedIndex, small: this.smallChips }, on: { click: function click(e) { if (isDisabled) return; e.stopPropagation(); _this6.selectedIndex = index; }, focus: focus, 'click:close': function clickClose() { return _this6.onChipInput(item); } }, key: JSON.stringify(this.getValue(item)) }, this.getText(item)); }, genCommaSelection: function genCommaSelection(item, index, last) { var color = index === this.selectedIndex && this.color; var isDisabled = this.disabled || this.getDisabled(item); return this.$createElement('div', this.setTextColor(color, { staticClass: 'v-select__selection v-select__selection--comma', class: { 'v-select__selection--disabled': isDisabled }, key: JSON.stringify(this.getValue(item)) }), "".concat(this.getText(item)).concat(last ? '' : ', ')); }, genDefaultSlot: function genDefaultSlot() { var selections = this.genSelections(); var input = this.genInput(); // If the return is an empty array // push the input if (Array.isArray(selections)) { selections.push(input); // Otherwise push it into children } else { selections.children = selections.children || []; selections.children.push(input); } return [this.genFieldset(), this.$createElement('div', { staticClass: 'v-select__slot', directives: this.directives }, [this.genLabel(), this.prefix ? this.genAffix('prefix') : null, selections, this.suffix ? this.genAffix('suffix') : null, this.genClearIcon(), this.genIconSlot()]), this.genMenu(), this.genProgress()]; }, genInput: function genInput() { var input = _VTextField2.default.options.methods.genInput.call(this); input.data.domProps.value = null; input.data.attrs.readonly = true; input.data.attrs.type = 'text'; input.data.attrs['aria-readonly'] = true; input.data.on.keypress = this.onKeyPress; return input; }, genInputSlot: function genInputSlot() { var render = _VTextField2.default.options.methods.genInputSlot.call(this); render.data.attrs = _objectSpread({}, render.data.attrs, { role: 'button', 'aria-haspopup': 'listbox', 'aria-expanded': String(this.isMenuActive), 'aria-owns': this.computedOwns }); return render; }, genList: function genList() { // If there's no slots, we can use a cached VNode to improve performance if (this.$slots['no-data'] || this.$slots['prepend-item'] || this.$slots['append-item']) { return this.genListWithSlot(); } else { return this.staticList; } }, genListWithSlot: function genListWithSlot() { var _this7 = this; var slots = ['prepend-item', 'no-data', 'append-item'].filter(function (slotName) { return _this7.$slots[slotName]; }).map(function (slotName) { return _this7.$createElement('template', { slot: slotName }, _this7.$slots[slotName]); }); // Requires destructuring due to Vue // modifying the `on` property when passed // as a referenced object return this.$createElement(_VSelectList.default, _objectSpread({}, this.listData), slots); }, genMenu: function genMenu() { var _this8 = this; var props = this.$_menuProps; props.activator = this.$refs['input-slot']; // Attach to root el so that // menu covers prepend/append icons if ( // TODO: make this a computed property or helper or something this.attach === '' || // If used as a boolean prop () this.attach === true || // If bound to a boolean () this.attach === 'attach' // If bound as boolean prop in pug (v-menu(attach)) ) { props.attach = this.$el; } else { props.attach = this.attach; } return this.$createElement(_VMenu.default, { attrs: { role: undefined }, props: props, on: { input: function input(val) { _this8.isMenuActive = val; _this8.isFocused = val; } }, ref: 'menu' }, [this.genList()]); }, genSelections: function genSelections() { var length = this.selectedItems.length; var children = new Array(length); var genSelection; if (this.$scopedSlots.selection) { genSelection = this.genSlotSelection; } else if (this.hasChips) { genSelection = this.genChipSelection; } else { genSelection = this.genCommaSelection; } while (length--) { children[length] = genSelection(this.selectedItems[length], length, length === children.length - 1); } return this.$createElement('div', { staticClass: 'v-select__selections' }, children); }, genSlotSelection: function genSlotSelection(item, index) { var _this9 = this; return this.$scopedSlots.selection({ attrs: { class: 'v-chip--select' }, parent: this, item: item, index: index, select: function select(e) { e.stopPropagation(); _this9.selectedIndex = index; }, selected: index === this.selectedIndex, disabled: this.disabled || this.readonly }); }, getMenuIndex: function getMenuIndex() { return this.$refs.menu ? this.$refs.menu.listIndex : -1; }, getDisabled: function getDisabled(item) { return (0, _helpers.getPropertyFromItem)(item, this.itemDisabled, false); }, getText: function getText(item) { return (0, _helpers.getPropertyFromItem)(item, this.itemText, item); }, getValue: function getValue(item) { return (0, _helpers.getPropertyFromItem)(item, this.itemValue, this.getText(item)); }, onBlur: function onBlur(e) { e && this.$emit('blur', e); }, onChipInput: function onChipInput(item) { if (this.multiple) this.selectItem(item);else this.setValue(null); // If all items have been deleted, // open `v-menu` if (this.selectedItems.length === 0) { this.isMenuActive = true; } else { this.isMenuActive = false; } this.selectedIndex = -1; }, onClick: function onClick() { if (this.isDisabled) return; this.isMenuActive = true; if (!this.isFocused) { this.isFocused = true; this.$emit('focus'); } }, onEscDown: function onEscDown(e) { e.preventDefault(); if (this.isMenuActive) { e.stopPropagation(); this.isMenuActive = false; } }, onKeyPress: function onKeyPress(e) { var _this10 = this; if (this.multiple) return; var KEYBOARD_LOOKUP_THRESHOLD = 1000; // milliseconds var now = performance.now(); if (now - this.keyboardLookupLastTime > KEYBOARD_LOOKUP_THRESHOLD) { this.keyboardLookupPrefix = ''; } this.keyboardLookupPrefix += e.key.toLowerCase(); this.keyboardLookupLastTime = now; var index = this.allItems.findIndex(function (item) { var text = (_this10.getText(item) || '').toString(); return text.toLowerCase().startsWith(_this10.keyboardLookupPrefix); }); var item = this.allItems[index]; if (index !== -1) { this.setValue(this.returnObject ? item : this.getValue(item)); setTimeout(function () { return _this10.setMenuIndex(index); }); } }, onKeyDown: function onKeyDown(e) { var keyCode = e.keyCode; var menu = this.$refs.menu; // If enter, space, open menu if ([_helpers.keyCodes.enter, _helpers.keyCodes.space].includes(keyCode)) this.activateMenu(); if (!menu) return; // If menu is active, allow default // listIndex change from menu if (this.isMenuActive && keyCode !== _helpers.keyCodes.tab) { menu.changeListIndex(e); } // If menu is not active, up and down can do // one of 2 things. If multiple, opens the // menu, if not, will cycle through all // available options if (!this.isMenuActive && [_helpers.keyCodes.up, _helpers.keyCodes.down].includes(keyCode)) return this.onUpDown(e); // If escape deactivate the menu if (keyCode === _helpers.keyCodes.esc) return this.onEscDown(e); // If tab - select item or close menu if (keyCode === _helpers.keyCodes.tab) return this.onTabDown(e); // If space preventDefault if (keyCode === _helpers.keyCodes.space) return this.onSpaceDown(e); }, onMenuActiveChange: function onMenuActiveChange(val) { // If menu is closing and mulitple // or menuIndex is already set // skip menu index recalculation if (this.multiple && !val || this.getMenuIndex() > -1) return; var menu = this.$refs.menu; if (!menu || !this.isDirty) return; // When menu opens, set index of first active item for (var i = 0; i < menu.tiles.length; i++) { if (menu.tiles[i].getAttribute('aria-selected') === 'true') { this.setMenuIndex(i); break; } } }, onMouseUp: function onMouseUp(e) { var _this11 = this; if (this.hasMouseDown && e.which !== 3) { var appendInner = this.$refs['append-inner']; // If append inner is present // and the target is itself // or inside, toggle menu if (this.isMenuActive && appendInner && (appendInner === e.target || appendInner.contains(e.target))) { this.$nextTick(function () { return _this11.isMenuActive = !_this11.isMenuActive; }); // If user is clicking in the container // and field is enclosed, activate it } else if (this.isEnclosed && !this.isDisabled) { this.isMenuActive = true; } } _VTextField2.default.options.methods.onMouseUp.call(this, e); }, onScroll: function onScroll() { var _this12 = this; if (!this.isMenuActive) { requestAnimationFrame(function () { return _this12.content.scrollTop = 0; }); } else { if (this.lastItem >= this.computedItems.length) return; var showMoreItems = this.content.scrollHeight - (this.content.scrollTop + this.content.clientHeight) < 200; if (showMoreItems) { this.lastItem += 20; } } }, onSpaceDown: function onSpaceDown(e) { e.preventDefault(); }, onTabDown: function onTabDown(e) { var menu = this.$refs.menu; if (!menu) return; var activeTile = menu.activeTile; // An item that is selected by // menu-index should toggled if (!this.multiple && activeTile && this.isMenuActive) { e.preventDefault(); e.stopPropagation(); activeTile.click(); } else { // If we make it here, // the user has no selected indexes // and is probably tabbing out this.blur(e); } }, onUpDown: function onUpDown(e) { var menu = this.$refs.menu; if (!menu) return; e.preventDefault(); // Multiple selects do not cycle their value // when pressing up or down, instead activate // the menu if (this.multiple) return this.activateMenu(); var keyCode = e.keyCode; // Cycle through available values to achieve // select native behavior menu.getTiles(); _helpers.keyCodes.up === keyCode ? menu.prevTile() : menu.nextTile(); menu.activeTile && menu.activeTile.click(); }, selectItem: function selectItem(item) { var _this13 = this; if (!this.multiple) { this.setValue(this.returnObject ? item : this.getValue(item)); this.isMenuActive = false; } else { var internalValue = (this.internalValue || []).slice(); var i = this.findExistingIndex(item); i !== -1 ? internalValue.splice(i, 1) : internalValue.push(item); this.setValue(internalValue.map(function (i) { return _this13.returnObject ? i : _this13.getValue(i); })); // When selecting multiple // adjust menu after each // selection this.$nextTick(function () { _this13.$refs.menu && _this13.$refs.menu.updateDimensions(); }); // We only need to reset list index for multiple // to keep highlight when an item is toggled // on and off if (!this.multiple) return; var listIndex = this.getMenuIndex(); this.setMenuIndex(-1); // There is no item to re-highlight // when selections are hidden if (this.hideSelected) return; this.$nextTick(function () { return _this13.setMenuIndex(listIndex); }); } }, setMenuIndex: function setMenuIndex(index) { this.$refs.menu && (this.$refs.menu.listIndex = index); }, setSelectedItems: function setSelectedItems() { var _this14 = this; var selectedItems = []; var values = !this.multiple || !Array.isArray(this.internalValue) ? [this.internalValue] : this.internalValue; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { var _loop = function _loop() { var value = _step.value; var index = _this14.allItems.findIndex(function (v) { return _this14.valueComparator(_this14.getValue(v), _this14.getValue(value)); }); if (index > -1) { selectedItems.push(_this14.allItems[index]); } }; for (var _iterator = values[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { _loop(); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } this.selectedItems = selectedItems; }, setValue: function setValue(value) { var oldValue = this.internalValue; this.internalValue = value; value !== oldValue && this.$emit('change', value); } } }); exports.default = _default2; //# sourceMappingURL=VSelect.js.map