primevue
Version:
PrimeVue is an open source UI library for Vue featuring a rich set of 80+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeBloc
1 lines • 180 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../../src/multiselect/BaseMultiSelect.vue","../../src/multiselect/MultiSelect.vue","../../src/multiselect/MultiSelect.vue?vue&type=template&id=39397ac8&lang.js"],"sourcesContent":["<script>\nimport BaseInput from '@primevue/core/baseinput';\nimport MultiSelectStyle from 'primevue/multiselect/style';\n\nexport default {\n name: 'BaseMultiSelect',\n extends: BaseInput,\n props: {\n options: Array,\n optionLabel: null,\n optionValue: null,\n optionDisabled: null,\n optionGroupLabel: null,\n optionGroupChildren: null,\n scrollHeight: {\n type: String,\n default: '14rem'\n },\n placeholder: String,\n inputId: {\n type: String,\n default: null\n },\n panelClass: {\n type: String,\n default: null\n },\n panelStyle: {\n type: null,\n default: null\n },\n overlayClass: {\n type: String,\n default: null\n },\n overlayStyle: {\n type: null,\n default: null\n },\n dataKey: null,\n showClear: {\n type: Boolean,\n default: false\n },\n clearIcon: {\n type: String,\n default: undefined\n },\n resetFilterOnClear: {\n type: Boolean,\n default: false\n },\n filter: Boolean,\n filterPlaceholder: String,\n filterLocale: String,\n filterMatchMode: {\n type: String,\n default: 'contains'\n },\n filterFields: {\n type: Array,\n default: null\n },\n appendTo: {\n type: [String, Object],\n default: 'body'\n },\n display: {\n type: String,\n default: 'comma'\n },\n selectedItemsLabel: {\n type: String,\n default: null\n },\n maxSelectedLabels: {\n type: Number,\n default: null\n },\n selectionLimit: {\n type: Number,\n default: null\n },\n showToggleAll: {\n type: Boolean,\n default: true\n },\n loading: {\n type: Boolean,\n default: false\n },\n checkboxIcon: {\n type: String,\n default: undefined\n },\n dropdownIcon: {\n type: String,\n default: undefined\n },\n filterIcon: {\n type: String,\n default: undefined\n },\n loadingIcon: {\n type: String,\n default: undefined\n },\n removeTokenIcon: {\n type: String,\n default: undefined\n },\n chipIcon: {\n type: String,\n default: undefined\n },\n selectAll: {\n type: Boolean,\n default: null\n },\n resetFilterOnHide: {\n type: Boolean,\n default: false\n },\n virtualScrollerOptions: {\n type: Object,\n default: null\n },\n autoOptionFocus: {\n type: Boolean,\n default: false\n },\n autoFilterFocus: {\n type: Boolean,\n default: false\n },\n focusOnHover: {\n type: Boolean,\n default: true\n },\n highlightOnSelect: {\n type: Boolean,\n default: false\n },\n filterMessage: {\n type: String,\n default: null\n },\n selectionMessage: {\n type: String,\n default: null\n },\n emptySelectionMessage: {\n type: String,\n default: null\n },\n emptyFilterMessage: {\n type: String,\n default: null\n },\n emptyMessage: {\n type: String,\n default: null\n },\n tabindex: {\n type: Number,\n default: 0\n },\n ariaLabel: {\n type: String,\n default: null\n },\n ariaLabelledby: {\n type: String,\n default: null\n }\n },\n style: MultiSelectStyle,\n provide() {\n return {\n $pcMultiSelect: this,\n $parentInstance: this\n };\n }\n};\n</script>\n","<template>\n <div ref=\"container\" :class=\"cx('root')\" :style=\"sx('root')\" @click=\"onContainerClick\" :data-p=\"containerDataP\" v-bind=\"ptmi('root')\">\n <div class=\"p-hidden-accessible\" v-bind=\"ptm('hiddenInputContainer')\" :data-p-hidden-accessible=\"true\">\n <input\n ref=\"focusInput\"\n :id=\"inputId\"\n type=\"text\"\n readonly\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :tabindex=\"!disabled ? tabindex : -1\"\n role=\"combobox\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"overlayVisible\"\n :aria-controls=\"$id + '_list'\"\n :aria-activedescendant=\"focused ? focusedOptionId : undefined\"\n :aria-invalid=\"invalid || undefined\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @keydown=\"onKeyDown\"\n v-bind=\"ptm('hiddenInput')\"\n />\n </div>\n <div :class=\"cx('labelContainer')\" v-bind=\"ptm('labelContainer')\">\n <div :class=\"cx('label')\" :data-p=\"labelDataP\" v-bind=\"ptm('label')\">\n <slot name=\"value\" :value=\"d_value\" :placeholder=\"placeholder\">\n <template v-if=\"display === 'comma'\">\n {{ label || 'empty' }}\n </template>\n <template v-else-if=\"display === 'chip'\">\n <template v-if=\"chipSelectedItems\">\n <span>{{ label }}</span>\n </template>\n <template v-else>\n <span v-for=\"item of d_value\" :key=\"getLabelByValue(item)\" :class=\"cx('chipItem')\" v-bind=\"ptm('chipItem')\">\n <slot name=\"chip\" :value=\"item\" :removeCallback=\"(event) => removeOption(event, item)\">\n <!-- TODO: removetokenicon and removeTokenIcon deprecated since v4.0. Use chipicon slot and chipIcon prop-->\n <Chip :class=\"cx('pcChip')\" :label=\"getLabelByValue(item)\" :removeIcon=\"chipIcon || removeTokenIcon\" removable :unstyled=\"unstyled\" @remove=\"removeOption($event, item)\" :pt=\"ptm('pcChip')\">\n <template #removeicon>\n <slot :name=\"$slots.chipicon ? 'chipicon' : 'removetokenicon'\" :class=\"cx('chipIcon')\" :item=\"item\" :removeCallback=\"(event) => removeOption(event, item)\" />\n </template>\n </Chip>\n </slot>\n </span>\n </template>\n <template v-if=\"!d_value || d_value.length === 0\">{{ placeholder || 'empty' }}</template>\n </template>\n </slot>\n </div>\n </div>\n <slot v-if=\"isClearIconVisible\" name=\"clearicon\" :class=\"cx('clearIcon')\" :clearCallback=\"onClearClick\">\n <component :is=\"clearIcon ? 'i' : 'TimesIcon'\" ref=\"clearIcon\" :class=\"[cx('clearIcon'), clearIcon]\" @click=\"onClearClick\" v-bind=\"ptm('clearIcon')\" data-pc-section=\"clearicon\" />\n </slot>\n <div :class=\"cx('dropdown')\" v-bind=\"ptm('dropdown')\">\n <slot v-if=\"loading\" name=\"loadingicon\" :class=\"cx('loadingIcon')\">\n <span v-if=\"loadingIcon\" :class=\"[cx('loadingIcon'), 'pi-spin', loadingIcon]\" aria-hidden=\"true\" v-bind=\"ptm('loadingIcon')\" />\n <SpinnerIcon v-else :class=\"cx('loadingIcon')\" spin aria-hidden=\"true\" v-bind=\"ptm('loadingIcon')\" />\n </slot>\n <slot v-else name=\"dropdownicon\" :class=\"cx('dropdownIcon')\">\n <component :is=\"dropdownIcon ? 'span' : 'ChevronDownIcon'\" :class=\"[cx('dropdownIcon'), dropdownIcon]\" aria-hidden=\"true\" :data-p=\"dropdownIconDataP\" v-bind=\"ptm('dropdownIcon')\" />\n </slot>\n </div>\n <Portal :appendTo=\"appendTo\">\n <transition name=\"p-connected-overlay\" @enter=\"onOverlayEnter\" @after-enter=\"onOverlayAfterEnter\" @leave=\"onOverlayLeave\" @after-leave=\"onOverlayAfterLeave\" v-bind=\"ptm('transition')\">\n <div v-if=\"overlayVisible\" :ref=\"overlayRef\" :style=\"[panelStyle, overlayStyle]\" :class=\"[cx('overlay'), panelClass, overlayClass]\" @click=\"onOverlayClick\" @keydown=\"onOverlayKeyDown\" :data-p=\"overlayDataP\" v-bind=\"ptm('overlay')\">\n <span\n ref=\"firstHiddenFocusableElementOnOverlay\"\n role=\"presentation\"\n aria-hidden=\"true\"\n class=\"p-hidden-accessible p-hidden-focusable\"\n :tabindex=\"0\"\n @focus=\"onFirstHiddenFocus\"\n v-bind=\"ptm('hiddenFirstFocusableEl')\"\n :data-p-hidden-accessible=\"true\"\n :data-p-hidden-focusable=\"true\"\n ></span>\n <slot name=\"header\" :value=\"d_value\" :options=\"visibleOptions\"></slot>\n <div v-if=\"(showToggleAll && selectionLimit == null) || filter\" :class=\"cx('header')\" v-bind=\"ptm('header')\">\n <Checkbox\n v-if=\"showToggleAll && selectionLimit == null\"\n :modelValue=\"allSelected\"\n :binary=\"true\"\n :disabled=\"disabled\"\n :variant=\"variant\"\n :aria-label=\"toggleAllAriaLabel\"\n @change=\"onToggleAll\"\n :unstyled=\"unstyled\"\n :pt=\"getHeaderCheckboxPTOptions('pcHeaderCheckbox')\"\n :formControl=\"{ novalidate: true }\"\n >\n <template #icon=\"slotProps\">\n <component v-if=\"$slots.headercheckboxicon\" :is=\"$slots.headercheckboxicon\" :checked=\"slotProps.checked\" :class=\"slotProps.class\" />\n <component v-else-if=\"slotProps.checked\" :is=\"checkboxIcon ? 'span' : 'CheckIcon'\" :class=\"[slotProps.class, { [checkboxIcon]: slotProps.checked }]\" v-bind=\"getHeaderCheckboxPTOptions('pcHeaderCheckbox.icon')\" />\n </template>\n </Checkbox>\n <IconField v-if=\"filter\" :class=\"cx('pcFilterContainer')\" :unstyled=\"unstyled\" :pt=\"ptm('pcFilterContainer')\">\n <InputText\n ref=\"filterInput\"\n :value=\"filterValue\"\n @vue:mounted=\"onFilterUpdated\"\n @vue:updated=\"onFilterUpdated\"\n :class=\"cx('pcFilter')\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n :variant=\"variant\"\n :unstyled=\"unstyled\"\n role=\"searchbox\"\n autocomplete=\"off\"\n :aria-owns=\"$id + '_list'\"\n :aria-activedescendant=\"focusedOptionId\"\n @keydown=\"onFilterKeyDown\"\n @blur=\"onFilterBlur\"\n @input=\"onFilterChange\"\n :pt=\"ptm('pcFilter')\"\n :formControl=\"{ novalidate: true }\"\n />\n <InputIcon :unstyled=\"unstyled\" :pt=\"ptm('pcFilterIconContainer')\">\n <slot name=\"filtericon\">\n <span v-if=\"filterIcon\" :class=\"filterIcon\" v-bind=\"ptm('filterIcon')\" />\n <SearchIcon v-else v-bind=\"ptm('filterIcon')\" />\n </slot>\n </InputIcon>\n </IconField>\n <span v-if=\"filter\" role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\" v-bind=\"ptm('hiddenFilterResult')\" :data-p-hidden-accessible=\"true\">\n {{ filterResultMessageText }}\n </span>\n </div>\n <div :class=\"cx('listContainer')\" :style=\"{ 'max-height': virtualScrollerDisabled ? scrollHeight : '' }\" v-bind=\"ptm('listContainer')\">\n <VirtualScroller :ref=\"virtualScrollerRef\" v-bind=\"virtualScrollerOptions\" :items=\"visibleOptions\" :style=\"{ height: scrollHeight }\" :tabindex=\"-1\" :disabled=\"virtualScrollerDisabled\" :pt=\"ptm('virtualScroller')\">\n <template v-slot:content=\"{ styleClass, contentRef, items, getItemOptions, contentStyle, itemSize }\">\n <ul :ref=\"(el) => listRef(el, contentRef)\" :id=\"$id + '_list'\" :class=\"[cx('list'), styleClass]\" :style=\"contentStyle\" role=\"listbox\" aria-multiselectable=\"true\" :aria-label=\"listAriaLabel\" v-bind=\"ptm('list')\">\n <template v-for=\"(option, i) of items\" :key=\"getOptionRenderKey(option, getOptionIndex(i, getItemOptions))\">\n <li\n v-if=\"isOptionGroup(option)\"\n :id=\"$id + '_' + getOptionIndex(i, getItemOptions)\"\n :style=\"{ height: itemSize ? itemSize + 'px' : undefined }\"\n :class=\"cx('optionGroup')\"\n role=\"option\"\n v-bind=\"ptm('optionGroup')\"\n >\n <slot name=\"optiongroup\" :option=\"option.optionGroup\" :index=\"getOptionIndex(i, getItemOptions)\">{{ getOptionGroupLabel(option.optionGroup) }}</slot>\n </li>\n <li\n v-else\n :id=\"$id + '_' + getOptionIndex(i, getItemOptions)\"\n v-ripple\n :style=\"{ height: itemSize ? itemSize + 'px' : undefined }\"\n :class=\"cx('option', { option, index: i, getItemOptions })\"\n role=\"option\"\n :aria-label=\"getOptionLabel(option)\"\n :aria-selected=\"isSelected(option)\"\n :aria-disabled=\"isOptionDisabled(option)\"\n :aria-setsize=\"ariaSetSize\"\n :aria-posinset=\"getAriaPosInset(getOptionIndex(i, getItemOptions))\"\n @click=\"onOptionSelect($event, option, getOptionIndex(i, getItemOptions), true)\"\n @mousemove=\"onOptionMouseMove($event, getOptionIndex(i, getItemOptions))\"\n v-bind=\"getCheckboxPTOptions(option, getItemOptions, i, 'option')\"\n :data-p-selected=\"isSelected(option)\"\n :data-p-focused=\"focusedOptionIndex === getOptionIndex(i, getItemOptions)\"\n :data-p-disabled=\"isOptionDisabled(option)\"\n >\n <Checkbox\n :defaultValue=\"isSelected(option)\"\n :binary=\"true\"\n :tabindex=\"-1\"\n :variant=\"variant\"\n :unstyled=\"unstyled\"\n :pt=\"getCheckboxPTOptions(option, getItemOptions, i, 'pcOptionCheckbox')\"\n :formControl=\"{ novalidate: true }\"\n >\n <template #icon=\"slotProps\">\n <component v-if=\"$slots.optioncheckboxicon || $slots.itemcheckboxicon\" :is=\"$slots.optioncheckboxicon || $slots.itemcheckboxicon\" :checked=\"slotProps.checked\" :class=\"slotProps.class\" />\n <component\n v-else-if=\"slotProps.checked\"\n :is=\"checkboxIcon ? 'span' : 'CheckIcon'\"\n :class=\"[slotProps.class, { [checkboxIcon]: slotProps.checked }]\"\n v-bind=\"getCheckboxPTOptions(option, getItemOptions, i, 'pcOptionCheckbox.icon')\"\n />\n </template>\n </Checkbox>\n <slot name=\"option\" :option=\"option\" :selected=\"isSelected(option)\" :index=\"getOptionIndex(i, getItemOptions)\">\n <span v-bind=\"ptm('optionLabel')\">{{ getOptionLabel(option) }}</span>\n </slot>\n </li>\n </template>\n <li v-if=\"filterValue && (!items || (items && items.length === 0))\" :class=\"cx('emptyMessage')\" role=\"option\" v-bind=\"ptm('emptyMessage')\">\n <slot name=\"emptyfilter\">{{ emptyFilterMessageText }}</slot>\n </li>\n <li v-else-if=\"!options || (options && options.length === 0)\" :class=\"cx('emptyMessage')\" role=\"option\" v-bind=\"ptm('emptyMessage')\">\n <slot name=\"empty\">{{ emptyMessageText }}</slot>\n </li>\n </ul>\n </template>\n <template v-if=\"$slots.loader\" v-slot:loader=\"{ options }\">\n <slot name=\"loader\" :options=\"options\"></slot>\n </template>\n </VirtualScroller>\n </div>\n <slot name=\"footer\" :value=\"d_value\" :options=\"visibleOptions\"></slot>\n <span v-if=\"!options || (options && options.length === 0)\" role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\" v-bind=\"ptm('hiddenEmptyMessage')\" :data-p-hidden-accessible=\"true\">\n {{ emptyMessageText }}\n </span>\n <span role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\" v-bind=\"ptm('hiddenSelectedMessage')\" :data-p-hidden-accessible=\"true\">\n {{ selectedMessageText }}\n </span>\n <span\n ref=\"lastHiddenFocusableElementOnOverlay\"\n role=\"presentation\"\n aria-hidden=\"true\"\n class=\"p-hidden-accessible p-hidden-focusable\"\n :tabindex=\"0\"\n @focus=\"onLastHiddenFocus\"\n v-bind=\"ptm('hiddenLastFocusableEl')\"\n :data-p-hidden-accessible=\"true\"\n :data-p-hidden-focusable=\"true\"\n ></span>\n </div>\n </transition>\n </Portal>\n </div>\n</template>\n\n<script>\nimport { cn } from '@primeuix/utils';\nimport { absolutePosition, addStyle, findSingle, focus, getFirstFocusableElement, getFocusableElements, getLastFocusableElement, getOuterWidth, isTouchDevice, relativePosition } from '@primeuix/utils/dom';\nimport { equals, findLastIndex, isEmpty, isNotEmpty, isPrintableCharacter, resolveFieldData } from '@primeuix/utils/object';\nimport { ZIndex } from '@primeuix/utils/zindex';\nimport { FilterService } from '@primevue/core/api';\nimport { ConnectedOverlayScrollHandler } from '@primevue/core/utils';\nimport CheckIcon from '@primevue/icons/check';\nimport ChevronDownIcon from '@primevue/icons/chevrondown';\nimport SearchIcon from '@primevue/icons/search';\nimport SpinnerIcon from '@primevue/icons/spinner';\nimport TimesIcon from '@primevue/icons/times';\nimport Checkbox from 'primevue/checkbox';\nimport Chip from 'primevue/chip';\nimport IconField from 'primevue/iconfield';\nimport InputIcon from 'primevue/inputicon';\nimport InputText from 'primevue/inputtext';\nimport OverlayEventBus from 'primevue/overlayeventbus';\nimport Portal from 'primevue/portal';\nimport Ripple from 'primevue/ripple';\nimport VirtualScroller from 'primevue/virtualscroller';\nimport BaseMultiSelect from './BaseMultiSelect.vue';\n\nexport default {\n name: 'MultiSelect',\n extends: BaseMultiSelect,\n inheritAttrs: false,\n emits: ['change', 'focus', 'blur', 'before-show', 'before-hide', 'show', 'hide', 'filter', 'selectall-change'],\n inject: {\n $pcFluid: { default: null }\n },\n outsideClickListener: null,\n scrollHandler: null,\n resizeListener: null,\n overlay: null,\n list: null,\n virtualScroller: null,\n startRangeIndex: -1,\n searchTimeout: null,\n searchValue: '',\n selectOnFocus: false,\n data() {\n return {\n clicked: false,\n focused: false,\n focusedOptionIndex: -1,\n filterValue: null,\n overlayVisible: false\n };\n },\n watch: {\n options() {\n this.autoUpdateModel();\n }\n },\n mounted() {\n this.autoUpdateModel();\n },\n beforeUnmount() {\n this.unbindOutsideClickListener();\n this.unbindResizeListener();\n\n if (this.scrollHandler) {\n this.scrollHandler.destroy();\n this.scrollHandler = null;\n }\n\n if (this.overlay) {\n ZIndex.clear(this.overlay);\n this.overlay = null;\n }\n },\n methods: {\n getOptionIndex(index, fn) {\n return this.virtualScrollerDisabled ? index : fn && fn(index)['index'];\n },\n getOptionLabel(option) {\n return this.optionLabel ? resolveFieldData(option, this.optionLabel) : option;\n },\n getOptionValue(option) {\n return this.optionValue ? resolveFieldData(option, this.optionValue) : option;\n },\n getOptionRenderKey(option, index) {\n return this.dataKey ? resolveFieldData(option, this.dataKey) : this.getOptionLabel(option) + `_${index}`;\n },\n getHeaderCheckboxPTOptions(key) {\n return this.ptm(key, {\n context: {\n selected: this.allSelected\n }\n });\n },\n getCheckboxPTOptions(option, itemOptions, index, key) {\n return this.ptm(key, {\n context: {\n selected: this.isSelected(option),\n focused: this.focusedOptionIndex === this.getOptionIndex(index, itemOptions),\n disabled: this.isOptionDisabled(option)\n }\n });\n },\n isOptionDisabled(option) {\n if (this.maxSelectionLimitReached && !this.isSelected(option)) {\n return true;\n }\n\n return this.optionDisabled ? resolveFieldData(option, this.optionDisabled) : false;\n },\n isOptionGroup(option) {\n return this.optionGroupLabel && option.optionGroup && option.group;\n },\n getOptionGroupLabel(optionGroup) {\n return resolveFieldData(optionGroup, this.optionGroupLabel);\n },\n getOptionGroupChildren(optionGroup) {\n return resolveFieldData(optionGroup, this.optionGroupChildren);\n },\n getAriaPosInset(index) {\n return (this.optionGroupLabel ? index - this.visibleOptions.slice(0, index).filter((option) => this.isOptionGroup(option)).length : index) + 1;\n },\n show(isFocus) {\n this.$emit('before-show');\n this.overlayVisible = true;\n this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.findSelectedOptionIndex();\n\n isFocus && focus(this.$refs.focusInput);\n },\n hide(isFocus) {\n const _hide = () => {\n this.$emit('before-hide');\n this.overlayVisible = false;\n this.clicked = false;\n this.focusedOptionIndex = -1;\n this.searchValue = '';\n\n this.resetFilterOnHide && (this.filterValue = null);\n isFocus && focus(this.$refs.focusInput);\n };\n\n setTimeout(() => {\n _hide();\n }, 0); // For ScreenReaders\n },\n onFocus(event) {\n if (this.disabled) {\n // For ScreenReaders\n return;\n }\n\n this.focused = true;\n\n if (this.overlayVisible) {\n this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.findSelectedOptionIndex();\n !this.autoFilterFocus && this.scrollInView(this.focusedOptionIndex);\n }\n\n this.$emit('focus', event);\n },\n onBlur(event) {\n this.clicked = false;\n this.focused = false;\n this.focusedOptionIndex = -1;\n this.searchValue = '';\n this.$emit('blur', event);\n this.formField.onBlur?.();\n },\n onKeyDown(event) {\n if (this.disabled) {\n event.preventDefault();\n\n return;\n }\n\n const metaKey = event.metaKey || event.ctrlKey;\n\n switch (event.code) {\n case 'ArrowDown':\n this.onArrowDownKey(event);\n break;\n\n case 'ArrowUp':\n this.onArrowUpKey(event);\n break;\n\n case 'Home':\n this.onHomeKey(event);\n break;\n\n case 'End':\n this.onEndKey(event);\n break;\n\n case 'PageDown':\n this.onPageDownKey(event);\n break;\n\n case 'PageUp':\n this.onPageUpKey(event);\n break;\n\n case 'Enter':\n case 'NumpadEnter':\n case 'Space':\n this.onEnterKey(event);\n break;\n\n case 'Escape':\n this.onEscapeKey(event);\n break;\n\n case 'Tab':\n this.onTabKey(event);\n break;\n\n case 'ShiftLeft':\n case 'ShiftRight':\n this.onShiftKey(event);\n break;\n\n default:\n if (event.code === 'KeyA' && metaKey) {\n const value = this.visibleOptions.filter((option) => this.isValidOption(option)).map((option) => this.getOptionValue(option));\n\n this.updateModel(event, value);\n\n event.preventDefault();\n break;\n }\n\n if (!metaKey && isPrintableCharacter(event.key)) {\n !this.overlayVisible && this.show();\n this.searchOptions(event);\n event.preventDefault();\n }\n\n break;\n }\n\n this.clicked = false;\n },\n onContainerClick(event) {\n if (this.disabled || this.loading) {\n return;\n }\n\n if (event.target.tagName === 'INPUT' || event.target.getAttribute('data-pc-section') === 'clearicon' || event.target.closest('[data-pc-section=\"clearicon\"]')) {\n return;\n } else if (!this.overlay || !this.overlay.contains(event.target)) {\n this.overlayVisible ? this.hide(true) : this.show(true);\n }\n\n this.clicked = true;\n },\n onClearClick(event) {\n this.updateModel(event, null);\n this.resetFilterOnClear && (this.filterValue = null);\n },\n onFirstHiddenFocus(event) {\n const focusableEl = event.relatedTarget === this.$refs.focusInput ? getFirstFocusableElement(this.overlay, ':not([data-p-hidden-focusable=\"true\"])') : this.$refs.focusInput;\n\n focus(focusableEl);\n },\n onLastHiddenFocus(event) {\n const focusableEl = event.relatedTarget === this.$refs.focusInput ? getLastFocusableElement(this.overlay, ':not([data-p-hidden-focusable=\"true\"])') : this.$refs.focusInput;\n\n focus(focusableEl);\n },\n onOptionSelect(event, option, index = -1, isFocus = false) {\n if (this.disabled || this.isOptionDisabled(option)) {\n return;\n }\n\n let selected = this.isSelected(option);\n let value = null;\n\n if (selected) value = this.d_value.filter((val) => !equals(val, this.getOptionValue(option), this.equalityKey));\n else value = [...(this.d_value || []), this.getOptionValue(option)];\n\n this.updateModel(event, value);\n index !== -1 && (this.focusedOptionIndex = index);\n isFocus && focus(this.$refs.focusInput);\n },\n onOptionMouseMove(event, index) {\n if (this.focusOnHover) {\n this.changeFocusedOptionIndex(event, index);\n }\n },\n onOptionSelectRange(event, start = -1, end = -1) {\n start === -1 && (start = this.findNearestSelectedOptionIndex(end, true));\n end === -1 && (end = this.findNearestSelectedOptionIndex(start));\n\n if (start !== -1 && end !== -1) {\n const rangeStart = Math.min(start, end);\n const rangeEnd = Math.max(start, end);\n const value = this.visibleOptions\n .slice(rangeStart, rangeEnd + 1)\n .filter((option) => this.isValidOption(option))\n .map((option) => this.getOptionValue(option));\n\n this.updateModel(event, value);\n }\n },\n onFilterChange(event) {\n const value = event.target.value;\n\n this.filterValue = value;\n this.focusedOptionIndex = -1;\n this.$emit('filter', { originalEvent: event, value });\n\n !this.virtualScrollerDisabled && this.virtualScroller.scrollToIndex(0);\n },\n onFilterKeyDown(event) {\n switch (event.code) {\n case 'ArrowDown':\n this.onArrowDownKey(event);\n break;\n\n case 'ArrowUp':\n this.onArrowUpKey(event, true);\n break;\n\n case 'ArrowLeft':\n case 'ArrowRight':\n this.onArrowLeftKey(event, true);\n break;\n\n case 'Home':\n this.onHomeKey(event, true);\n break;\n\n case 'End':\n this.onEndKey(event, true);\n break;\n\n case 'Enter':\n case 'NumpadEnter':\n this.onEnterKey(event);\n break;\n\n case 'Escape':\n this.onEscapeKey(event);\n break;\n\n case 'Tab':\n this.onTabKey(event, true);\n break;\n\n default:\n break;\n }\n },\n onFilterBlur() {\n this.focusedOptionIndex = -1;\n },\n onFilterUpdated() {\n if (this.overlayVisible) {\n this.alignOverlay();\n }\n },\n onOverlayClick(event) {\n OverlayEventBus.emit('overlay-click', {\n originalEvent: event,\n target: this.$el\n });\n },\n onOverlayKeyDown(event) {\n switch (event.code) {\n case 'Escape':\n this.onEscapeKey(event);\n break;\n\n default:\n break;\n }\n },\n onArrowDownKey(event) {\n if (!this.overlayVisible) {\n this.show();\n } else {\n const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findFirstOptionIndex() : this.findFirstFocusedOptionIndex();\n\n if (event.shiftKey) {\n this.onOptionSelectRange(event, this.startRangeIndex, optionIndex);\n }\n\n this.changeFocusedOptionIndex(event, optionIndex);\n }\n\n event.preventDefault();\n },\n onArrowUpKey(event, pressedInInputText = false) {\n if (event.altKey && !pressedInInputText) {\n if (this.focusedOptionIndex !== -1) {\n this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);\n }\n\n this.overlayVisible && this.hide();\n event.preventDefault();\n } else {\n const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findLastOptionIndex() : this.findLastFocusedOptionIndex();\n\n if (event.shiftKey) {\n this.onOptionSelectRange(event, optionIndex, this.startRangeIndex);\n }\n\n this.changeFocusedOptionIndex(event, optionIndex);\n\n !this.overlayVisible && this.show();\n event.preventDefault();\n }\n },\n onArrowLeftKey(event, pressedInInputText = false) {\n pressedInInputText && (this.focusedOptionIndex = -1);\n },\n onHomeKey(event, pressedInInputText = false) {\n if (pressedInInputText) {\n const target = event.currentTarget;\n\n if (event.shiftKey) {\n target.setSelectionRange(0, event.target.selectionStart);\n } else {\n target.setSelectionRange(0, 0);\n this.focusedOptionIndex = -1;\n }\n } else {\n let metaKey = event.metaKey || event.ctrlKey;\n let optionIndex = this.findFirstOptionIndex();\n\n if (event.shiftKey && metaKey) {\n this.onOptionSelectRange(event, optionIndex, this.startRangeIndex);\n }\n\n this.changeFocusedOptionIndex(event, optionIndex);\n\n !this.overlayVisible && this.show();\n }\n\n event.preventDefault();\n },\n onEndKey(event, pressedInInputText = false) {\n if (pressedInInputText) {\n const target = event.currentTarget;\n\n if (event.shiftKey) {\n target.setSelectionRange(event.target.selectionStart, target.value.length);\n } else {\n const len = target.value.length;\n\n target.setSelectionRange(len, len);\n this.focusedOptionIndex = -1;\n }\n } else {\n let metaKey = event.metaKey || event.ctrlKey;\n let optionIndex = this.findLastOptionIndex();\n\n if (event.shiftKey && metaKey) {\n this.onOptionSelectRange(event, this.startRangeIndex, optionIndex);\n }\n\n this.changeFocusedOptionIndex(event, optionIndex);\n\n !this.overlayVisible && this.show();\n }\n\n event.preventDefault();\n },\n onPageUpKey(event) {\n this.scrollInView(0);\n event.preventDefault();\n },\n onPageDownKey(event) {\n this.scrollInView(this.visibleOptions.length - 1);\n event.preventDefault();\n },\n onEnterKey(event) {\n if (!this.overlayVisible) {\n this.focusedOptionIndex = -1; // reset\n this.onArrowDownKey(event);\n } else {\n if (this.focusedOptionIndex !== -1) {\n if (event.shiftKey) this.onOptionSelectRange(event, this.focusedOptionIndex);\n else this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);\n }\n }\n\n event.preventDefault();\n },\n onEscapeKey(event) {\n this.overlayVisible && this.hide(true);\n event.preventDefault();\n },\n onTabKey(event, pressedInInputText = false) {\n if (!pressedInInputText) {\n if (this.overlayVisible && this.hasFocusableElements()) {\n focus(event.shiftKey ? this.$refs.lastHiddenFocusableElementOnOverlay : this.$refs.firstHiddenFocusableElementOnOverlay);\n\n event.preventDefault();\n } else {\n if (this.focusedOptionIndex !== -1) {\n this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);\n }\n\n this.overlayVisible && this.hide(this.filter);\n }\n }\n },\n onShiftKey() {\n this.startRangeIndex = this.focusedOptionIndex;\n },\n onOverlayEnter(el) {\n ZIndex.set('overlay', el, this.$primevue.config.zIndex.overlay);\n\n addStyle(el, { position: 'absolute', top: '0' });\n this.alignOverlay();\n this.scrollInView();\n\n this.autoFilterFocus && focus(this.$refs.filterInput.$el);\n this.autoUpdateModel();\n },\n onOverlayAfterEnter() {\n this.bindOutsideClickListener();\n this.bindScrollListener();\n this.bindResizeListener();\n\n this.$emit('show');\n },\n onOverlayLeave() {\n this.unbindOutsideClickListener();\n this.unbindScrollListener();\n this.unbindResizeListener();\n\n this.$emit('hide');\n this.overlay = null;\n },\n onOverlayAfterLeave(el) {\n ZIndex.clear(el);\n },\n alignOverlay() {\n if (this.appendTo === 'self') {\n relativePosition(this.overlay, this.$el);\n } else {\n this.overlay.style.minWidth = getOuterWidth(this.$el) + 'px';\n absolutePosition(this.overlay, this.$el);\n }\n },\n bindOutsideClickListener() {\n if (!this.outsideClickListener) {\n this.outsideClickListener = (event) => {\n if (this.overlayVisible && this.isOutsideClicked(event)) {\n this.hide();\n }\n };\n\n document.addEventListener('click', this.outsideClickListener, true);\n }\n },\n unbindOutsideClickListener() {\n if (this.outsideClickListener) {\n document.removeEventListener('click', this.outsideClickListener, true);\n this.outsideClickListener = null;\n }\n },\n bindScrollListener() {\n if (!this.scrollHandler) {\n this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.container, () => {\n if (this.overlayVisible) {\n this.hide();\n }\n });\n }\n\n this.scrollHandler.bindScrollListener();\n },\n unbindScrollListener() {\n if (this.scrollHandler) {\n this.scrollHandler.unbindScrollListener();\n }\n },\n bindResizeListener() {\n if (!this.resizeListener) {\n this.resizeListener = () => {\n if (this.overlayVisible && !isTouchDevice()) {\n this.hide();\n }\n };\n\n window.addEventListener('resize', this.resizeListener);\n }\n },\n unbindResizeListener() {\n if (this.resizeListener) {\n window.removeEventListener('resize', this.resizeListener);\n this.resizeListener = null;\n }\n },\n isOutsideClicked(event) {\n return !(this.$el.isSameNode(event.target) || this.$el.contains(event.target) || (this.overlay && this.overlay.contains(event.target)));\n },\n getLabelByValue(value) {\n const options = this.optionGroupLabel ? this.flatOptions(this.options) : this.options || [];\n const matchedOption = options.find((option) => !this.isOptionGroup(option) && equals(this.getOptionValue(option), value, this.equalityKey));\n\n return matchedOption ? this.getOptionLabel(matchedOption) : null;\n },\n getSelectedItemsLabel() {\n let pattern = /{(.*?)}/;\n const selectedItemsLabel = this.selectedItemsLabel || this.$primevue.config.locale.selectionMessage;\n\n if (pattern.test(selectedItemsLabel)) {\n return selectedItemsLabel.replace(selectedItemsLabel.match(pattern)[0], this.d_value.length + '');\n }\n\n return selectedItemsLabel;\n },\n onToggleAll(event) {\n if (this.selectAll !== null) {\n this.$emit('selectall-change', { originalEvent: event, checked: !this.allSelected });\n } else {\n const value = this.allSelected ? [] : this.visibleOptions.filter((option) => this.isValidOption(option)).map((option) => this.getOptionValue(option));\n\n this.updateModel(event, value);\n }\n },\n removeOption(event, optionValue) {\n event.stopPropagation();\n let value = this.d_value.filter((val) => !equals(val, optionValue, this.equalityKey));\n\n this.updateModel(event, value);\n },\n clearFilter() {\n this.filterValue = null;\n },\n hasFocusableElements() {\n return getFocusableElements(this.overlay, ':not([data-p-hidden-focusable=\"true\"])').length > 0;\n },\n isOptionMatched(option) {\n return this.isValidOption(option) && typeof this.getOptionLabel(option) === 'string' && this.getOptionLabel(option)?.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale));\n },\n isValidOption(option) {\n return isNotEmpty(option) && !(this.isOptionDisabled(option) || this.isOptionGroup(option));\n },\n isValidSelectedOption(option) {\n return this.isValidOption(option) && this.isSelected(option);\n },\n isEquals(value1, value2) {\n return equals(value1, value2, this.equalityKey);\n },\n isSelected(option) {\n const optionValue = this.getOptionValue(option);\n\n return (this.d_value || []).some((value) => this.isEquals(value, optionValue));\n },\n findFirstOptionIndex() {\n return this.visibleOptions.findIndex((option) => this.isValidOption(option));\n },\n findLastOptionIndex() {\n return findLastIndex(this.visibleOptions, (option) => this.isValidOption(option));\n },\n findNextOptionIndex(index) {\n const matchedOptionIndex = index < this.visibleOptions.length - 1 ? this.visibleOptions.slice(index + 1).findIndex((option) => this.isValidOption(option)) : -1;\n\n return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;\n },\n findPrevOptionIndex(index) {\n const matchedOptionIndex = index > 0 ? findLastIndex(this.visibleOptions.slice(0, index), (option) => this.isValidOption(option)) : -1;\n\n return matchedOptionIndex > -1 ? matchedOptionIndex : index;\n },\n findSelectedOptionIndex() {\n if (this.$filled) {\n for (let index = this.d_value.length - 1; index >= 0; index--) {\n const value = this.d_value[index];\n const matchedOptionIndex = this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option) && this.isEquals(value, this.getOptionValue(option)));\n\n if (matchedOptionIndex > -1) return matchedOptionIndex;\n }\n }\n\n return -1;\n },\n findFirstSelectedOptionIndex() {\n return this.$filled ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;\n },\n findLastSelectedOptionIndex() {\n return this.$filled ? findLastIndex(this.visibleOptions, (option) => this.isValidSelectedOption(option)) : -1;\n },\n findNextSelectedOptionIndex(index) {\n const matchedOptionIndex = this.$filled && index < this.visibleOptions.length - 1 ? this.visibleOptions.slice(index + 1).findIndex((option) => this.isValidSelectedOption(option)) : -1;\n\n return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : -1;\n },\n findPrevSelectedOptionIndex(index) {\n const matchedOptionIndex = this.$filled && index > 0 ? findLastIndex(this.visibleOptions.slice(0, index), (option) => this.isValidSelectedOption(option)) : -1;\n\n return matchedOptionIndex > -1 ? matchedOptionIndex : -1;\n },\n findNearestSelectedOptionIndex(index, firstCheckUp = false) {\n let matchedOptionIndex = -1;\n\n if (this.$filled) {\n if (firstCheckUp) {\n matchedOptionIndex = this.findPrevSelectedOptionIndex(index);\n matchedOptionIndex = matchedOptionIndex === -1 ? this.findNextSelectedOptionIndex(index) : matchedOptionIndex;\n } else {\n matchedOptionIndex = this.findNextSelectedOptionIndex(index);\n matchedOptionIndex = matchedOptionIndex === -1 ? this.findPrevSelectedOptionIndex(index) : matchedOptionIndex;\n }\n }\n\n return matchedOptionIndex > -1 ? matchedOptionIndex : index;\n },\n findFirstFocusedOptionIndex() {\n const selectedIndex = this.findSelectedOptionIndex();\n\n return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;\n },\n findLastFocusedOptionIndex() {\n const selectedIndex = this.findSelectedOptionIndex();\n\n return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;\n },\n searchOptions(event) {\n this.searchValue = (this.searchValue || '') + event.key;\n\n let optionIndex = -1;\n\n if (isNotEmpty(this.searchValue)) {\n if (this.focusedOptionIndex !== -1) {\n optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));\n optionIndex = optionIndex ==