var VueMultiselect = (function (exports, vue) {
  'use strict';

  function isEmpty (opt) {
    if (opt === 0) return false
    if (Array.isArray(opt) && opt.length === 0) return true
    return !opt
  }

  function not (fun) {
    return (...params) => !fun(...params)
  }

  function includes (str, query) {
    /* istanbul ignore else */
    if (str === undefined) str = 'undefined';
    if (str === null) str = 'null';
    if (str === false) str = 'false';
    const text = str.toString().toLowerCase();
    return text.indexOf(query.trim()) !== -1
  }

  function filterOptions (options, search, label, customLabel) {
    return search ? options
      .filter((option) => includes(customLabel(option, label), search))
      .sort((a, b) => customLabel(a, label).length - customLabel(b, label).length) : options
  }

  function stripGroups (options) {
    return options.filter((option) => !option.$isLabel)
  }

  function flattenOptions (values, label) {
    return (options) =>
      options.reduce((prev, curr) => {
        /* istanbul ignore else */
        if (curr[values] && curr[values].length) {
          prev.push({
            $groupLabel: curr[label],
            $isLabel: true
          });
          return prev.concat(curr[values])
        }
        return prev
      }, [])
  }

  function filterGroups (search, label, values, groupLabel, customLabel) {
    return (groups) =>
      groups.map((group) => {
        /* istanbul ignore else */
        if (!group[values]) {
          console.warn(`Options passed to vue-multiselect do not contain groups, despite the config.`);
          return []
        }
        const groupOptions = filterOptions(group[values], search, label, customLabel);

        return groupOptions.length
          ? {
            [groupLabel]: group[groupLabel],
            [values]: groupOptions
          }
          : []
      })
  }

  const flow = (...fns) => (x) => fns.reduce((v, f) => f(v), x);

  var multiselectMixin = {
    data () {
      return {
        search: '',
        isOpen: false,
        preferredOpenDirection: 'below',
        optimizedHeight: this.maxHeight
      }
    },
    props: {
      /**
       * Decide whether to filter the results based on search query.
       * Useful for async filtering, where we search through more complex data.
       * @type {Boolean}
       */
      internalSearch: {
        type: Boolean,
        default: true
      },
      /**
       * Array of available options: Objects, Strings or Integers.
       * If array of objects, visible label will default to option.label.
       * If `labal` prop is passed, label will equal option['label']
       * @type {Array}
       */
      options: {
        type: Array,
        required: true
      },
      /**
       * Equivalent to the `multiple` attribute on a `<select>` input.
       * @default false
       * @type {Boolean}
       */
      multiple: {
        type: Boolean,
        default: false
      },
      /**
       * Key to compare objects
       * @default 'id'
       * @type {String}
       */
      trackBy: {
        type: String
      },
      /**
       * Label to look for in option Object
       * @default 'label'
       * @type {String}
       */
      label: {
        type: String
      },
      /**
       * Enable/disable search in options
       * @default true
       * @type {Boolean}
       */
      searchable: {
        type: Boolean,
        default: true
      },
      /**
       * Clear the search input after `)
       * @default true
       * @type {Boolean}
       */
      clearOnSelect: {
        type: Boolean,
        default: true
      },
      /**
       * Hide already selected options
       * @default false
       * @type {Boolean}
       */
      hideSelected: {
        type: Boolean,
        default: false
      },
      /**
       * Equivalent to the `placeholder` attribute on a `<select>` input.
       * @default 'Select option'
       * @type {String}
       */
      placeholder: {
        type: String,
        default: 'Select option'
      },
      /**
       * Allow to remove all selected values
       * @default true
       * @type {Boolean}
       */
      allowEmpty: {
        type: Boolean,
        default: true
      },
      /**
       * Reset this.internalValue, this.search after this.internalValue changes.
       * Useful if want to create a stateless dropdown.
       * @default false
       * @type {Boolean}
       */
      resetAfter: {
        type: Boolean,
        default: false
      },
      /**
       * Enable/disable closing after selecting an option
       * @default true
       * @type {Boolean}
       */
      closeOnSelect: {
        type: Boolean,
        default: true
      },
      /**
       * Function to interpolate the custom label
       * @default false
       * @type {Function}
       */
      customLabel: {
        type: Function,
        default (option, label) {
          if (isEmpty(option)) return ''
          return label ? option[label] : option
        }
      },
      /**
       * Disable / Enable tagging
       * @default false
       * @type {Boolean}
       */
      taggable: {
        type: Boolean,
        default: false
      },
      /**
       * String to show when highlighting a potential tag
       * @default 'Press enter to create a tag'
       * @type {String}
      */
      tagPlaceholder: {
        type: String,
        default: 'Press enter to create a tag'
      },
      /**
       * By default new tags will appear above the search results.
       * Changing to 'bottom' will revert this behaviour
       * and will proritize the search results
       * @default 'top'
       * @type {String}
      */
      tagPosition: {
        type: String,
        default: 'top'
      },
      /**
       * Number of allowed selected options. No limit if 0.
       * @default 0
       * @type {Number}
      */
      max: {
        type: [Number, Boolean],
        default: false
      },
      /**
       * Will be passed with all events as second param.
       * Useful for identifying events origin.
       * @default null
       * @type {String|Integer}
      */
      id: {
        default: null
      },
      /**
       * Limits the options displayed in the dropdown
       * to the first X options.
       * @default 1000
       * @type {Integer}
      */
      optionsLimit: {
        type: Number,
        default: 1000
      },
      /**
       * Name of the property containing
       * the group values
       * @default 1000
       * @type {String}
      */
      groupValues: {
        type: String
      },
      /**
       * Name of the property containing
       * the group label
       * @default 1000
       * @type {String}
      */
      groupLabel: {
        type: String
      },
      /**
       * Allow to select all group values
       * by selecting the group label
       * @default false
       * @type {Boolean}
       */
      groupSelect: {
        type: Boolean,
        default: false
      },
      /**
       * Array of keyboard keys to block
       * when selecting
       * @default 1000
       * @type {String}
      */
      blockKeys: {
        type: Array,
        default () {
          return []
        }
      },
      /**
       * Prevent from wiping up the search value
       * @default false
       * @type {Boolean}
      */
      preserveSearch: {
        type: Boolean,
        default: false
      },
      /**
       * Select 1st options if value is empty
       * @default false
       * @type {Boolean}
      */
      preselectFirst: {
        type: Boolean,
        default: false
      },
      /**
       * Prevent autofocus
       * @default false
       * @type {Boolean}
      */
      preventAutofocus: {
        type: Boolean,
        default: false
      }
    },
    mounted () {
      /* istanbul ignore else */
      if (!this.multiple && this.max) {
        console.warn('[Vue-Multiselect warn]: Max prop should not be used when prop Multiple equals false.');
      }
      if (
        this.preselectFirst &&
        !this.internalValue.length &&
        this.options.length
      ) {
        this.select(this.filteredOptions[0]);
      }
    },
    computed: {
      internalValue () {
        return this.modelValue || this.modelValue === 0
          ? Array.isArray(this.modelValue) ? this.modelValue : [this.modelValue]
          : []
      },
      filteredOptions () {
        const search = this.search || '';
        const normalizedSearch = search.toLowerCase().trim();

        let options = this.options.concat();

        /* istanbul ignore else */
        if (this.internalSearch) {
          options = this.groupValues
            ? this.filterAndFlat(options, normalizedSearch, this.label)
            : filterOptions(options, normalizedSearch, this.label, this.customLabel);
        } else {
          options = this.groupValues ? flattenOptions(this.groupValues, this.groupLabel)(options) : options;
        }

        options = this.hideSelected
          ? options.filter(not(this.isSelected))
          : options;

        /* istanbul ignore else */
        if (this.taggable && normalizedSearch.length && !this.isExistingOption(normalizedSearch)) {
          if (this.tagPosition === 'bottom') {
            options.push({isTag: true, label: search});
          } else {
            options.unshift({isTag: true, label: search});
          }
        }

        return options.slice(0, this.optionsLimit)
      },
      valueKeys () {
        if (this.trackBy) {
          return this.internalValue.map((element) => element[this.trackBy])
        } else {
          return this.internalValue
        }
      },
      optionKeys () {
        const options = this.groupValues ? this.flatAndStrip(this.options) : this.options;
        return options.map((element) => this.customLabel(element, this.label).toString().toLowerCase())
      },
      currentOptionLabel () {
        return this.multiple
          ? this.searchable ? '' : this.placeholder
          : this.internalValue.length
            ? this.getOptionLabel(this.internalValue[0])
            : this.searchable ? '' : this.placeholder
      }
    },
    watch: {
      internalValue: {
        handler () {
        /* istanbul ignore else */
          if (this.resetAfter && this.internalValue.length) {
            this.search = '';
            this.$emit('update:modelValue', this.multiple ? [] : null);
          }
        },
        deep: true
      },
      search () {
        this.$emit('search-change', this.search);
      }
    },
    emits: ['open', 'search-change', 'close', 'select', 'update:modelValue', 'remove', 'tag'],
    methods: {
      /**
       * Returns the internalValue in a way it can be emited to the parent
       * @returns {Object||Array||String||Integer}
       */
      getValue () {
        return this.multiple
          ? this.internalValue
          : this.internalValue.length === 0
            ? null
            : this.internalValue[0]
      },
      /**
       * Filters and then flattens the options list
       * @param  {Array}
       * @return {Array} returns a filtered and flat options list
       */
      filterAndFlat (options, search, label) {
        return flow(
          filterGroups(search, label, this.groupValues, this.groupLabel, this.customLabel),
          flattenOptions(this.groupValues, this.groupLabel)
        )(options)
      },
      /**
       * Flattens and then strips the group labels from the options list
       * @param  {Array}
       * @return {Array} returns a flat options list without group labels
       */
      flatAndStrip (options) {
        return flow(
          flattenOptions(this.groupValues, this.groupLabel),
          stripGroups
        )(options)
      },
      /**
       * Updates the search value
       * @param  {String}
       */
      updateSearch (query) {
        this.search = query;
      },
      /**
       * Finds out if the given query is already present
       * in the available options
       * @param  {String}
       * @return {Boolean} returns true if element is available
       */
      isExistingOption (query) {
        return !this.options
          ? false
          : this.optionKeys.indexOf(query) > -1
      },
      /**
       * Finds out if the given element is already present
       * in the result value
       * @param  {Object||String||Integer} option passed element to check
       * @returns {Boolean} returns true if element is selected
       */
      isSelected (option) {
        const opt = this.trackBy
          ? option[this.trackBy]
          : option;
        return this.valueKeys.indexOf(opt) > -1
      },
      /**
       * Finds out if the given option is disabled
       * @param  {Object||String||Integer} option passed element to check
       * @returns {Boolean} returns true if element is disabled
       */
      isOptionDisabled (option) {
        return !!option.$isDisabled
      },
      /**
       * Returns empty string when options is null/undefined
       * Returns tag query if option is tag.
       * Returns the customLabel() results and casts it to string.
       *
       * @param  {Object||String||Integer} Passed option
       * @returns {Object||String}
       */
      getOptionLabel (option) {
        if (isEmpty(option)) return ''
        /* istanbul ignore else */
        if (option.isTag) return option.label
        /* istanbul ignore else */
        if (option.$isLabel) return option.$groupLabel

        const label = this.customLabel(option, this.label);
        /* istanbul ignore else */
        if (isEmpty(label)) return ''
        return label
      },
      /**
       * Add the given option to the list of selected options
       * or sets the option as the selected option.
       * If option is already selected -> remove it from the results.
       *
       * @param  {Object||String||Integer} option to select/deselect
       * @param  {Boolean} block removing
       */
      select (option, key) {
        /* istanbul ignore else */
        if (option.$isLabel && this.groupSelect) {
          this.selectGroup(option);
          return
        }
        if (this.blockKeys.indexOf(key) !== -1 ||
          this.disabled ||
          option.$isDisabled ||
          option.$isLabel
        ) return
        /* istanbul ignore else */
        if (this.max && this.multiple && this.internalValue.length === this.max) return
        /* istanbul ignore else */
        if (key === 'Tab' && !this.pointerDirty) return
        if (option.isTag) {
          this.$emit('tag', option.label, this.id);
          this.search = '';
          if (this.closeOnSelect && !this.multiple) this.deactivate();
        } else {
          const isSelected = this.isSelected(option);

          if (isSelected) {
            if (key !== 'Tab') this.removeElement(option);
            return
          }

          if (this.multiple) {
            this.$emit('update:modelValue', this.internalValue.concat([option]));
          } else {
            this.$emit('update:modelValue', option);
          }

          this.$emit('select', option, this.id);

          /* istanbul ignore else */
          if (this.clearOnSelect) this.search = '';
        }
        /* istanbul ignore else */
        if (this.closeOnSelect) this.deactivate();
      },
      /**
       * Add the given group options to the list of selected options
       * If all group optiona are already selected -> remove it from the results.
       *
       * @param  {Object||String||Integer} group to select/deselect
       */
      selectGroup (selectedGroup) {
        const group = this.options.find((option) => {
          return option[this.groupLabel] === selectedGroup.$groupLabel
        });

        if (!group) return

        if (this.wholeGroupSelected(group)) {
          this.$emit('remove', group[this.groupValues], this.id);

          const groupValues = this.trackBy ? group[this.groupValues].map(val => val[this.trackBy]) : group[this.groupValues];
          const newValue = this.internalValue.filter(
            option => groupValues.indexOf(this.trackBy ? option[this.trackBy] : option) === -1
          );

          this.$emit('update:modelValue', newValue);
        } else {
          let optionsToAdd = group[this.groupValues].filter(
            option => !(this.isOptionDisabled(option) || this.isSelected(option))
          );

          // if max is defined then just select options respecting max
          if (this.max) {
            optionsToAdd.splice(this.max - this.internalValue.length);
          }

          this.$emit('select', optionsToAdd, this.id);
          this.$emit(
            'update:modelValue',
            this.internalValue.concat(optionsToAdd)
          );
        }

        if (this.closeOnSelect) this.deactivate();
      },
      /**
       * Helper to identify if all values in a group are selected
       *
       * @param {Object} group to validated selected values against
       */
      wholeGroupSelected (group) {
        return group[this.groupValues].every((option) => this.isSelected(option) || this.isOptionDisabled(option)
        )
      },
      /**
       * Helper to identify if all values in a group are disabled
       *
       * @param {Object} group to check for disabled values
       */
      wholeGroupDisabled (group) {
        return group[this.groupValues].every(this.isOptionDisabled)
      },
      /**
       * Removes the given option from the selected options.
       * Additionally checks this.allowEmpty prop if option can be removed when
       * it is the last selected option.
       *
       * @param  {type} option description
       * @return {type}        description
       */
      removeElement (option, shouldClose = true) {
        /* istanbul ignore else */
        if (this.disabled) return
        /* istanbul ignore else */
        if (option.$isDisabled) return
        /* istanbul ignore else */
        if (!this.allowEmpty && this.internalValue.length <= 1) {
          this.deactivate();
          return
        }

        const index = typeof option === 'object'
          ? this.valueKeys.indexOf(option[this.trackBy])
          : this.valueKeys.indexOf(option);

        if (this.multiple) {
          const newValue = this.internalValue.slice(0, index).concat(this.internalValue.slice(index + 1));
          this.$emit('update:modelValue', newValue);
        } else {
          this.$emit('update:modelValue', null);
        }
        this.$emit('remove', option, this.id);

        /* istanbul ignore else */
        if (this.closeOnSelect && shouldClose) this.deactivate();
      },
      /**
       * Calls this.removeElement() with the last element
       * from this.internalValue (selected element Array)
       *
       * @fires this#removeElement
       */
      removeLastElement () {
        /* istanbul ignore else */
        if (this.blockKeys.indexOf('Delete') !== -1) return
        /* istanbul ignore else */
        if (this.search.length === 0 && Array.isArray(this.internalValue) && this.internalValue.length) {
          this.removeElement(this.internalValue[this.internalValue.length - 1], false);
        }
      },
      /**
       * Opens the multiselect’s dropdown.
       * Sets this.isOpen to TRUE
       */
      activate () {
        /* istanbul ignore else */
        if (this.isOpen || this.disabled) return

        this.adjustPosition();
        /* istanbul ignore else  */
        if (this.groupValues && this.pointer === 0 && this.filteredOptions.length) {
          this.pointer = 1;
        }

        this.isOpen = true;
        /* istanbul ignore else  */
        if (this.searchable) {
          if (!this.preserveSearch) this.search = '';
          if (!this.preventAutofocus) this.$nextTick(() => this.$refs.search && this.$refs.search.focus());
        } else if (!this.preventAutofocus) {
          if (typeof this.$el !== 'undefined') this.$el.focus();
        }
        this.$emit('open', this.id);
      },
      /**
       * Closes the multiselect’s dropdown.
       * Sets this.isOpen to FALSE
       */
      deactivate () {
        /* istanbul ignore else */
        if (!this.isOpen) return

        this.isOpen = false;
        /* istanbul ignore else  */
        if (this.searchable) {
          if (this.$refs.search !== null && typeof this.$refs.search !== 'undefined') this.$refs.search.blur();
        } else {
          if (typeof this.$el !== 'undefined') this.$el.blur();
        }
        if (!this.preserveSearch) this.search = '';
        this.$emit('close', this.getValue(), this.id);
      },
      /**
       * Call this.activate() or this.deactivate()
       * depending on this.isOpen value.
       *
       * @fires this#activate || this#deactivate
       * @property {Boolean} isOpen indicates if dropdown is open
       */
      toggle () {
        this.isOpen
          ? this.deactivate()
          : this.activate();
      },
      /**
       * Updates the hasEnoughSpace variable used for
       * detecting where to expand the dropdown
       */
      adjustPosition () {
        if (typeof window === 'undefined') return

        const spaceAbove = this.$el.getBoundingClientRect().top;
        const spaceBelow = window.innerHeight - this.$el.getBoundingClientRect().bottom;
        const hasEnoughSpaceBelow = spaceBelow > this.maxHeight;

        if (hasEnoughSpaceBelow || spaceBelow > spaceAbove || this.openDirection === 'below' || this.openDirection === 'bottom') {
          this.preferredOpenDirection = 'below';
          this.optimizedHeight = Math.min(spaceBelow - 40, this.maxHeight);
        } else {
          this.preferredOpenDirection = 'above';
          this.optimizedHeight = Math.min(spaceAbove - 40, this.maxHeight);
        }
      }
    }
  };

  var pointerMixin = {
    data () {
      return {
        pointer: 0,
        pointerDirty: false
      }
    },
    props: {
      /**
       * Enable/disable highlighting of the pointed value.
       * @type {Boolean}
       * @default true
       */
      showPointer: {
        type: Boolean,
        default: true
      },
      optionHeight: {
        type: Number,
        default: 40
      }
    },
    computed: {
      pointerPosition () {
        return this.pointer * this.optionHeight
      },
      visibleElements () {
        return this.optimizedHeight / this.optionHeight
      }
    },
    watch: {
      filteredOptions () {
        this.pointerAdjust();
      },
      isOpen () {
        this.pointerDirty = false;
      },
      pointer () {
        this.$refs.search && this.$refs.search.setAttribute('aria-activedescendant', this.id + '-' + this.pointer.toString());
      }
    },
    methods: {
      optionHighlight (index, option) {
        return {
          'multiselect__option--highlight': index === this.pointer && this.showPointer,
          'multiselect__option--selected': this.isSelected(option)
        }
      },
      groupHighlight (index, selectedGroup) {
        if (!this.groupSelect) {
          return [
            'multiselect__option--disabled',
            {'multiselect__option--group': selectedGroup.$isLabel}
          ]
        }

        const group = this.options.find((option) => {
          return option[this.groupLabel] === selectedGroup.$groupLabel
        });

        return group && !this.wholeGroupDisabled(group) ? [
          'multiselect__option--group',
          {'multiselect__option--highlight': index === this.pointer && this.showPointer},
          {'multiselect__option--group-selected': this.wholeGroupSelected(group)}
        ] : 'multiselect__option--disabled'
      },
      addPointerElement ({key} = 'Enter') {
        /* istanbul ignore else */
        if (this.filteredOptions.length > 0) {
          this.select(this.filteredOptions[this.pointer], key);
        }
        this.pointerReset();
      },
      pointerForward () {
        /* istanbul ignore else */
        if (this.pointer < this.filteredOptions.length - 1) {
          this.pointer++;
          /* istanbul ignore next */
          if (this.$refs.list.scrollTop <= this.pointerPosition - (this.visibleElements - 1) * this.optionHeight) {
            this.$refs.list.scrollTop = this.pointerPosition - (this.visibleElements - 1) * this.optionHeight;
          }
          /* istanbul ignore else */
          if (
            this.filteredOptions[this.pointer] &&
            this.filteredOptions[this.pointer].$isLabel &&
            !this.groupSelect
          ) this.pointerForward();
        }
        this.pointerDirty = true;
      },
      pointerBackward () {
        if (this.pointer > 0) {
          this.pointer--;
          /* istanbul ignore else */
          if (this.$refs.list.scrollTop >= this.pointerPosition) {
            this.$refs.list.scrollTop = this.pointerPosition;
          }
          /* istanbul ignore else */
          if (
            this.filteredOptions[this.pointer] &&
            this.filteredOptions[this.pointer].$isLabel &&
            !this.groupSelect
          ) this.pointerBackward();
        } else {
          /* istanbul ignore else */
          if (
            this.filteredOptions[this.pointer] &&
            this.filteredOptions[0].$isLabel &&
            !this.groupSelect
          ) this.pointerForward();
        }
        this.pointerDirty = true;
      },
      pointerReset () {
        /* istanbul ignore else */
        if (!this.closeOnSelect) return
        this.pointer = 0;
        /* istanbul ignore else */
        if (this.$refs.list) {
          this.$refs.list.scrollTop = 0;
        }
      },
      pointerAdjust () {
        /* istanbul ignore else */
        if (this.pointer >= this.filteredOptions.length - 1) {
          this.pointer = this.filteredOptions.length
            ? this.filteredOptions.length - 1
            : 0;
        }

        if (this.filteredOptions.length > 0 &&
          this.filteredOptions[this.pointer].$isLabel &&
          !this.groupSelect
        ) {
          this.pointerForward();
        }
      },
      pointerSet (index) {
        this.pointer = index;
        this.pointerDirty = true;
      }
    }
  };

  var script = {
    name: 'vue-multiselect',
    mixins: [multiselectMixin, pointerMixin],
    compatConfig: {
      MODE: 3,
      ATTR_ENUMERATED_COERCION: false
    },
    props: {
      /**
         * name attribute to match optional label element
         * @default ''
         * @type {String}
         */
      name: {
        type: String,
        default: ''
      },
      /**
         * Presets the selected options value.
         * @type {Object||Array||String||Integer}
         */
      modelValue: {
        type: null,
        default () {
          return []
        }
      },
      /**
         * String to show when pointing to an option
         * @default 'Press enter to select'
         * @type {String}
         */
      selectLabel: {
        type: String,
        default: 'Press enter to select'
      },
      /**
         * String to show when pointing to an option
         * @default 'Press enter to select'
         * @type {String}
         */
      selectGroupLabel: {
        type: String,
        default: 'Press enter to select group'
      },
      /**
         * String to show next to selected option
         * @default 'Selected'
         * @type {String}
         */
      selectedLabel: {
        type: String,
        default: 'Selected'
      },
      /**
         * String to show when pointing to an already selected option
         * @default 'Press enter to remove'
         * @type {String}
         */
      deselectLabel: {
        type: String,
        default: 'Press enter to remove'
      },
      /**
         * String to show when pointing to an already selected option
         * @default 'Press enter to remove'
         * @type {String}
         */
      deselectGroupLabel: {
        type: String,
        default: 'Press enter to deselect group'
      },
      /**
         * Decide whether to show pointer labels
         * @default true
         * @type {Boolean}
         */
      showLabels: {
        type: Boolean,
        default: true
      },
      /**
         * Limit the display of selected options. The rest will be hidden within the limitText string.
         * @default 99999
         * @type {Integer}
         */
      limit: {
        type: Number,
        default: 99999
      },
      /**
         * Sets maxHeight style value of the dropdown
         * @default 300
         * @type {Integer}
         */
      maxHeight: {
        type: Number,
        default: 300
      },
      /**
         * Function that process the message shown when selected
         * elements pass the defined limit.
         * @default 'and * more'
         * @param {Int} count Number of elements more than limit
         * @type {Function}
         */
      limitText: {
        type: Function,
        default: (count) => `and ${count} more`
      },
      /**
         * Set true to trigger the loading spinner.
         * @default False
         * @type {Boolean}
         */
      loading: {
        type: Boolean,
        default: false
      },
      /**
         * Disables the multiselect if true.
         * @default false
         * @type {Boolean}
         */
      disabled: {
        type: Boolean,
        default: false
      },
      /**
         * Fixed opening direction
         * @default ''
         * @type {String}
         */
      openDirection: {
        type: String,
        default: ''
      },
      /**
         * Shows slot with message about empty options
         * @default true
         * @type {Boolean}
         */
      showNoOptions: {
        type: Boolean,
        default: true
      },
      showNoResults: {
        type: Boolean,
        default: true
      },
      tabindex: {
        type: Number,
        default: 0
      }
    },
    computed: {
      hasOptionGroup () {
        return this.groupValues && this.groupLabel && this.groupSelect
      },
      isSingleLabelVisible () {
        return (
          (this.singleValue || this.singleValue === 0) &&
            (!this.isOpen || !this.searchable) &&
            !this.visibleValues.length
        )
      },
      isPlaceholderVisible () {
        return !this.internalValue.length && (!this.searchable || !this.isOpen)
      },
      visibleValues () {
        return this.multiple ? this.internalValue.slice(0, this.limit) : []
      },
      singleValue () {
        return this.internalValue[0]
      },
      deselectLabelText () {
        return this.showLabels ? this.deselectLabel : ''
      },
      deselectGroupLabelText () {
        return this.showLabels ? this.deselectGroupLabel : ''
      },
      selectLabelText () {
        return this.showLabels ? this.selectLabel : ''
      },
      selectGroupLabelText () {
        return this.showLabels ? this.selectGroupLabel : ''
      },
      selectedLabelText () {
        return this.showLabels ? this.selectedLabel : ''
      },
      inputStyle () {
        if (
          this.searchable ||
            (this.multiple && this.modelValue && this.modelValue.length)
        ) {
          // Hide input by setting the width to 0 allowing it to receive focus
          return this.isOpen
            ? {width: '100%'}
            : {width: '0', position: 'absolute', padding: '0'}
        }
        return ''
      },
      contentStyle () {
        return this.options.length
          ? {display: 'inline-block'}
          : {display: 'block'}
      },
      isAbove () {
        if (this.openDirection === 'above' || this.openDirection === 'top') {
          return true
        } else if (
          this.openDirection === 'below' ||
            this.openDirection === 'bottom'
        ) {
          return false
        } else {
          return this.preferredOpenDirection === 'above'
        }
      },
      showSearchInput () {
        return (
          this.searchable &&
            (this.hasSingleSelectedSlot &&
              (this.visibleSingleValue || this.visibleSingleValue === 0)
              ? this.isOpen
              : true)
        )
      }
    }
  };

  const _hoisted_1 = {
    ref: "tags",
    class: "multiselect__tags"
  };
  const _hoisted_2 = { class: "multiselect__tags-wrap" };
  const _hoisted_3 = { class: "multiselect__spinner" };
  const _hoisted_4 = { key: 0 };
  const _hoisted_5 = { class: "multiselect__option" };
  const _hoisted_6 = { class: "multiselect__option" };
  const _hoisted_7 = /*#__PURE__*/vue.createTextVNode("No elements found. Consider changing the search query.");
  const _hoisted_8 = { class: "multiselect__option" };
  const _hoisted_9 = /*#__PURE__*/vue.createTextVNode("List is empty.");

  function render(_ctx, _cache, $props, $setup, $data, $options) {
    return (vue.openBlock(), vue.createBlock("div", {
      tabindex: _ctx.searchable ? -1 : $props.tabindex,
      class: [{ 'multiselect--active': _ctx.isOpen, 'multiselect--disabled': $props.disabled, 'multiselect--above': $options.isAbove, 'multiselect--has-options-group': $options.hasOptionGroup }, "multiselect"],
      onFocus: _cache[14] || (_cache[14] = $event => (_ctx.activate())),
      onBlur: _cache[15] || (_cache[15] = $event => (_ctx.searchable ? false : _ctx.deactivate())),
      onKeydown: [
        _cache[16] || (_cache[16] = vue.withKeys(vue.withModifiers($event => (_ctx.pointerForward()), ["self","prevent"]), ["down"])),
        _cache[17] || (_cache[17] = vue.withKeys(vue.withModifiers($event => (_ctx.pointerBackward()), ["self","prevent"]), ["up"]))
      ],
      onKeypress: _cache[18] || (_cache[18] = vue.withKeys(vue.withModifiers($event => (_ctx.addPointerElement($event)), ["stop","self"]), ["enter","tab"])),
      onKeyup: _cache[19] || (_cache[19] = vue.withKeys($event => (_ctx.deactivate()), ["esc"])),
      role: "combobox",
      "aria-owns": 'listbox-'+_ctx.id
    }, [
      vue.renderSlot(_ctx.$slots, "caret", { toggle: _ctx.toggle }, () => [
        vue.createVNode("div", {
          onMousedown: _cache[1] || (_cache[1] = vue.withModifiers($event => (_ctx.toggle()), ["prevent","stop"])),
          class: "multiselect__select"
        }, null, 32 /* HYDRATE_EVENTS */)
      ]),
      vue.renderSlot(_ctx.$slots, "clear", { search: _ctx.search }),
      vue.createVNode("div", _hoisted_1, [
        vue.renderSlot(_ctx.$slots, "selection", {
          search: _ctx.search,
          remove: _ctx.removeElement,
          values: $options.visibleValues,
          isOpen: _ctx.isOpen
        }, () => [
          vue.withDirectives(vue.createVNode("div", _hoisted_2, [
            (vue.openBlock(true), vue.createBlock(vue.Fragment, null, vue.renderList($options.visibleValues, (option, index) => {
              return vue.renderSlot(_ctx.$slots, "tag", {
                option: option,
                search: _ctx.search,
                remove: _ctx.removeElement
              }, () => [
                (vue.openBlock(), vue.createBlock("span", {
                  class: "multiselect__tag",
                  key: index
                }, [
                  vue.createVNode("span", {
                    textContent: vue.toDisplayString(_ctx.getOptionLabel(option))
                  }, null, 8 /* PROPS */, ["textContent"]),
                  vue.createVNode("i", {
                    tabindex: "1",
                    onKeypress: vue.withKeys(vue.withModifiers($event => (_ctx.removeElement(option)), ["prevent"]), ["enter"]),
                    onMousedown: vue.withModifiers($event => (_ctx.removeElement(option)), ["prevent"]),
                    class: "multiselect__tag-icon"
                  }, null, 40 /* PROPS, HYDRATE_EVENTS */, ["onKeypress", "onMousedown"])
                ]))
              ])
            }), 256 /* UNKEYED_FRAGMENT */))
          ], 512 /* NEED_PATCH */), [
            [vue.vShow, $options.visibleValues.length > 0]
          ]),
          (_ctx.internalValue && _ctx.internalValue.length > $props.limit)
            ? vue.renderSlot(_ctx.$slots, "limit", { key: 0 }, () => [
                vue.createVNode("strong", {
                  class: "multiselect__strong",
                  textContent: vue.toDisplayString($props.limitText(_ctx.internalValue.length - $props.limit))
                }, null, 8 /* PROPS */, ["textContent"])
              ])
            : vue.createCommentVNode("v-if", true)
        ]),
        vue.createVNode(vue.Transition, { name: "multiselect__loading" }, {
          default: vue.withCtx(() => [
            vue.renderSlot(_ctx.$slots, "loading", {}, () => [
              vue.withDirectives(vue.createVNode("div", _hoisted_3, null, 512 /* NEED_PATCH */), [
                [vue.vShow, $props.loading]
              ])
            ])
          ]),
          _: 3 /* FORWARDED */
        }),
        (_ctx.searchable)
          ? (vue.openBlock(), vue.createBlock("input", {
              key: 0,
              ref: "search",
              name: $props.name,
              id: _ctx.id,
              type: "text",
              autocomplete: "off",
              spellcheck: false,
              placeholder: _ctx.placeholder,
              style: $options.inputStyle,
              value: _ctx.search,
              disabled: $props.disabled,
              tabindex: $props.tabindex,
              onInput: _cache[2] || (_cache[2] = $event => (_ctx.updateSearch($event.target.value))),
              onFocus: _cache[3] || (_cache[3] = vue.withModifiers($event => (_ctx.activate()), ["prevent"])),
              onBlur: _cache[4] || (_cache[4] = vue.withModifiers($event => (_ctx.deactivate()), ["prevent"])),
              onKeyup: _cache[5] || (_cache[5] = vue.withKeys($event => (_ctx.deactivate()), ["esc"])),
              onKeydown: [
                _cache[6] || (_cache[6] = vue.withKeys(vue.withModifiers($event => (_ctx.pointerForward()), ["prevent"]), ["down"])),
                _cache[7] || (_cache[7] = vue.withKeys(vue.withModifiers($event => (_ctx.pointerBackward()), ["prevent"]), ["up"])),
                _cache[9] || (_cache[9] = vue.withKeys(vue.withModifiers($event => (_ctx.removeLastElement()), ["stop"]), ["delete"]))
              ],
              onKeypress: _cache[8] || (_cache[8] = vue.withKeys(vue.withModifiers($event => (_ctx.addPointerElement($event)), ["prevent","stop","self"]), ["enter"])),
              class: "multiselect__input",
              "aria-controls": 'listbox-'+_ctx.id
            }, null, 44 /* STYLE, PROPS, HYDRATE_EVENTS */, ["name", "id", "placeholder", "value", "disabled", "tabindex", "aria-controls"]))
          : vue.createCommentVNode("v-if", true),
        ($options.isSingleLabelVisible)
          ? (vue.openBlock(), vue.createBlock("span", {
              key: 1,
              class: "multiselect__single",
              onMousedown: _cache[10] || (_cache[10] = vue.withModifiers((...args) => (_ctx.toggle && _ctx.toggle(...args)), ["prevent"]))
            }, [
              vue.renderSlot(_ctx.$slots, "singleLabel", { option: $options.singleValue }, () => [
                vue.createTextVNode(vue.toDisplayString(_ctx.currentOptionLabel), 1 /* TEXT */)
              ])
            ], 32 /* HYDRATE_EVENTS */))
          : vue.createCommentVNode("v-if", true),
        ($options.isPlaceholderVisible)
          ? (vue.openBlock(), vue.createBlock("span", {
              key: 2,
              class: "multiselect__placeholder",
              onMousedown: _cache[11] || (_cache[11] = vue.withModifiers((...args) => (_ctx.toggle && _ctx.toggle(...args)), ["prevent"]))
            }, [
              vue.renderSlot(_ctx.$slots, "placeholder", {}, () => [
                vue.createTextVNode(vue.toDisplayString(_ctx.placeholder), 1 /* TEXT */)
              ])
            ], 32 /* HYDRATE_EVENTS */))
          : vue.createCommentVNode("v-if", true)
      ], 512 /* NEED_PATCH */),
      vue.createVNode(vue.Transition, { name: "multiselect" }, {
        default: vue.withCtx(() => [
          vue.withDirectives(vue.createVNode("div", {
            class: "multiselect__content-wrapper",
            onFocus: _cache[12] || (_cache[12] = (...args) => (_ctx.activate && _ctx.activate(...args))),
            tabindex: "-1",
            onMousedown: _cache[13] || (_cache[13] = vue.withModifiers(() => {}, ["prevent"])),
            style: { maxHeight: _ctx.optimizedHeight + 'px' },
            ref: "list"
          }, [
            vue.createVNode("ul", {
              class: "multiselect__content",
              style: $options.contentStyle,
              role: "listbox",
              id: 'listbox-'+_ctx.id
            }, [
              vue.renderSlot(_ctx.$slots, "beforeList"),
              (_ctx.multiple && _ctx.max === _ctx.internalValue.length)
                ? (vue.openBlock(), vue.createBlock("li", _hoisted_4, [
                    vue.createVNode("span", _hoisted_5, [
                      vue.renderSlot(_ctx.$slots, "maxElements", {}, () => [
                        vue.createTextVNode("Maximum of " + vue.toDisplayString(_ctx.max) + " options selected. First remove a selected option to select another.", 1 /* TEXT */)
                      ])
                    ])
                  ]))
                : vue.createCommentVNode("v-if", true),
              (!_ctx.max || _ctx.internalValue.length < _ctx.max)
                ? (vue.openBlock(true), vue.createBlock(vue.Fragment, { key: 1 }, vue.renderList(_ctx.filteredOptions, (option, index) => {
                    return (vue.openBlock(), vue.createBlock("li", {
                      class: "multiselect__element",
                      key: index,
                      id: _ctx.id + '-' + index,
                      role: !(option && (option.$isLabel || option.$isDisabled)) ? 'option' : null
                    }, [
                      (!(option && (option.$isLabel || option.$isDisabled)))
                        ? (vue.openBlock(), vue.createBlock("span", {
                            key: 0,
                            class: [_ctx.optionHighlight(index, option), "multiselect__option"],
                            onClick: vue.withModifiers($event => (_ctx.select(option)), ["stop"]),
                            onMouseenter: vue.withModifiers($event => (_ctx.pointerSet(index)), ["self"]),
                            "data-select": option && option.isTag ? _ctx.tagPlaceholder : $options.selectLabelText,
                            "data-selected": $options.selectedLabelText,
                            "data-deselect": $options.deselectLabelText
                          }, [
                            vue.renderSlot(_ctx.$slots, "option", {
                              option: option,
                              search: _ctx.search,
                              index: index
                            }, () => [
                              vue.createVNode("span", null, vue.toDisplayString(_ctx.getOptionLabel(option)), 1 /* TEXT */)
                            ])
                          ], 42 /* CLASS, PROPS, HYDRATE_EVENTS */, ["onClick", "onMouseenter", "data-select", "data-selected", "data-deselect"]))
                        : vue.createCommentVNode("v-if", true),
                      (option && (option.$isLabel || option.$isDisabled))
                        ? (vue.openBlock(), vue.createBlock("span", {
                            key: 1,
                            "data-select": _ctx.groupSelect && $options.selectGroupLabelText,
                            "data-deselect": _ctx.groupSelect && $options.deselectGroupLabelText,
                            class: [_ctx.groupHighlight(index, option), "multiselect__option"],
                            onMouseenter: vue.withModifiers($event => (_ctx.groupSelect && _ctx.pointerSet(index)), ["self"]),
                            onMousedown: vue.withModifiers($event => (_ctx.selectGroup(option)), ["prevent"])
                          }, [
                            vue.renderSlot(_ctx.$slots, "option", {
                              option: option,
                              search: _ctx.search,
                              index: index
                            }, () => [
                              vue.createVNode("span", null, vue.toDisplayString(_ctx.getOptionLabel(option)), 1 /* TEXT */)
                            ])
                          ], 42 /* CLASS, PROPS, HYDRATE_EVENTS */, ["data-select", "data-deselect", "onMouseenter", "onMousedown"]))
                        : vue.createCommentVNode("v-if", true)
                    ], 8 /* PROPS */, ["id", "role"]))
                  }), 128 /* KEYED_FRAGMENT */))
                : vue.createCommentVNode("v-if", true),
              vue.withDirectives(vue.createVNode("li", null, [
                vue.createVNode("span", _hoisted_6, [
                  vue.renderSlot(_ctx.$slots, "noResult", { search: _ctx.search }, () => [
                    _hoisted_7
                  ])
                ])
              ], 512 /* NEED_PATCH */), [
                [vue.vShow, $props.showNoResults && (_ctx.filteredOptions.length === 0 && _ctx.search && !$props.loading)]
              ]),
              vue.withDirectives(vue.createVNode("li", null, [
                vue.createVNode("span", _hoisted_8, [
                  vue.renderSlot(_ctx.$slots, "noOptions", {}, () => [
                    _hoisted_9
                  ])
                ])
              ], 512 /* NEED_PATCH */), [
                [vue.vShow, $props.showNoOptions && ((_ctx.options.length === 0 || ($options.hasOptionGroup === true && _ctx.filteredOptions.length === 0)) && !_ctx.search && !$props.loading)]
              ]),
              vue.renderSlot(_ctx.$slots, "afterList")
            ], 12 /* STYLE, PROPS */, ["id"])
          ], 36 /* STYLE, HYDRATE_EVENTS */), [
            [vue.vShow, _ctx.isOpen]
          ])
        ]),
        _: 3 /* FORWARDED */
      })
    ], 42 /* CLASS, PROPS, HYDRATE_EVENTS */, ["tabindex", "aria-owns"]))
  }

  script.render = render;

  exports.Multiselect = script;
  exports.default = script;
  exports.multiselectMixin = multiselectMixin;
  exports.pointerMixin = pointerMixin;

  Object.defineProperty(exports, '__esModule', { value: true });

  return exports;

}({}, Vue));
