var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = 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; }

// Styles
import '../../../src/components/VTextField/VTextField.sass';
import '../../../src/components/VSelect/VSelect.sass';
// Components
import VChip from '../VChip';
import VMenu from '../VMenu';
import VSelectList from './VSelectList';
// Extensions
import VTextField from '../VTextField/VTextField';
// Mixins
import Comparable from '../../mixins/comparable';
import Filterable from '../../mixins/filterable';
// Directives
import ClickOutside from '../../directives/click-outside';
// Utilities
import { getPropertyFromItem, keyCodes } from '../../util/helpers';
import { consoleError } from '../../util/console';
// Types
import mixins from '../../util/mixins';
export var defaultMenuProps = {
    closeOnClick: false,
    closeOnContentClick: false,
    disableKeys: true,
    openOnClick: false,
    maxHeight: 300
};
var baseMixins = mixins(VTextField, Comparable, Filterable);
/* @vue/component */
export default baseMixins.extend().extend({
    name: 'v-select',
    directives: {
        ClickOutside: ClickOutside
    },
    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 _extends({}, VTextField.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-' + 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: _extends({}, 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']) {
                consoleError('assert: staticList should not be called if slots are used');
            }
            return this.$createElement(VSelectList, 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 _extends({}, 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) {
            VTextField.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, {
                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))
            }), '' + this.getText(item) + (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 = VTextField.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 = VTextField.options.methods.genInputSlot.call(this);
            render.data.attrs = _extends({}, 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, _extends({}, 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 (<v-menu attach>)
            this.attach === true || // If bound to a boolean (<v-menu :attach="true">)
            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, {
                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 = void 0;
            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 getPropertyFromItem(item, this.itemDisabled, false);
        },
        getText: function getText(item) {
            return getPropertyFromItem(item, this.itemText, item);
        },
        getValue: function getValue(item) {
            return 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 ([keyCodes.enter, keyCodes.space].includes(keyCode)) this.activateMenu();
            if (!menu) return;
            // If menu is active, allow default
            // listIndex change from menu
            if (this.isMenuActive && keyCode !== 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 && [keyCodes.up, keyCodes.down].includes(keyCode)) return this.onUpDown(e);
            // If escape deactivate the menu
            if (keyCode === keyCodes.esc) return this.onEscDown(e);
            // If tab - select item or close menu
            if (keyCode === keyCodes.tab) return this.onTabDown(e);
            // If space preventDefault
            if (keyCode === 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) {
                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;
                }
            }
            VTextField.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();
            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 _loop = function _loop(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]);
                }
            };

            var _iteratorNormalCompletion = true;
            var _didIteratorError = false;
            var _iteratorError = undefined;

            try {
                for (var _iterator = values[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
                    var value = _step.value;

                    _loop(value);
                }
            } catch (err) {
                _didIteratorError = true;
                _iteratorError = err;
            } finally {
                try {
                    if (!_iteratorNormalCompletion && _iterator.return) {
                        _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);
        }
    }
});
//# sourceMappingURL=VSelect.js.map