pull/978/head
parent
7b6e8e2e68
commit
75b2ff8561
|
@ -7,9 +7,7 @@
|
|||
<input v-if="editable" type="text" class="p-dropdown-label p-inputtext" :disabled="disabled" @focus="onFocus" @blur="onBlur" :placeholder="placeholder" :value="editableInputValue" @input="onEditableInput"
|
||||
aria-haspopup="listbox" :aria-expanded="overlayVisible">
|
||||
<span v-if="!editable" :class="labelClass">
|
||||
<slot name="value" :value="modelValue" :placeholder="placeholder">
|
||||
{{label}}
|
||||
</slot>
|
||||
<slot name="value" :value="modelValue" :placeholder="placeholder">{{label}}</slot>
|
||||
</span>
|
||||
<i v-if="showClear && modelValue != null" class="p-dropdown-clear-icon pi pi-times" @click="onClearClick($event)"></i>
|
||||
<div class="p-dropdown-trigger" role="button" aria-haspopup="listbox" :aria-expanded="overlayVisible">
|
||||
|
@ -17,6 +15,7 @@
|
|||
</div>
|
||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave">
|
||||
<div :ref="overlayRef" class="p-dropdown-panel p-component" v-if="overlayVisible">
|
||||
<slot name="header" :value="modelValue" :options="visibleOptions"></slot>
|
||||
<div class="p-dropdown-header" v-if="filter">
|
||||
<div class="p-dropdown-filter-container">
|
||||
<input type="text" ref="filterInput" v-model="filterValue" autoComplete="off" class="p-dropdown-filter p-inputtext p-component" :placeholder="filterPlaceholder" @keydown="onFilterKeyDown" @input="onFilterChange"/>
|
||||
|
@ -25,15 +24,32 @@
|
|||
</div>
|
||||
<div class="p-dropdown-items-wrapper" :style="{'max-height': scrollHeight}">
|
||||
<ul class="p-dropdown-items" role="listbox">
|
||||
<li v-for="(option, i) of visibleOptions" :class="['p-dropdown-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple
|
||||
:aria-label="getOptionLabel(option)" :key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" role="option" :aria-selected="isSelected(option)">
|
||||
<slot name="option" :option="option" :index="i">
|
||||
{{getOptionLabel(option)}}
|
||||
</slot>
|
||||
<template v-if="!optionGroupLabel">
|
||||
<li v-for="(option, i) of visibleOptions" :class="['p-dropdown-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple
|
||||
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" role="option" :aria-label="getOptionLabel(option)" :aria-selected="isSelected(option)">
|
||||
<slot name="option" :option="option" :index="i">{{getOptionLabel(option)}}</slot>
|
||||
</li>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-for="(optionGroup, i) of visibleOptions" :key="getOptionGroupRenderKey(optionGroup)">
|
||||
<li class="p-dropdown-item-group" >
|
||||
<slot name="optiongroup" :option="optionGroup" :index="i">{{getOptionGroupLabel(optionGroup)}}</slot>
|
||||
</li>
|
||||
<li v-for="(option, i) of getOptionGroupChildren(optionGroup)" :class="['p-dropdown-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple
|
||||
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" role="option" :aria-label="getOptionLabel(option)" :aria-selected="isSelected(option)">
|
||||
<slot name="option" :option="option" :index="i">{{getOptionLabel(option)}}</slot>
|
||||
</li>
|
||||
</template>
|
||||
</template>
|
||||
<li v-if="filterValue && (!visibleOptions || (visibleOptions && visibleOptions.length === 0))" class="p-dropdown-empty-message">
|
||||
<slot name="emptyfilter">{{emptyFilterMessageText}}</slot>
|
||||
</li>
|
||||
<li v-else-if="(!options || (options && options.length === 0))" class="p-dropdown-empty-message">
|
||||
<slot name="empty">{{emptyMessageText}}</slot>
|
||||
</li>
|
||||
<li v-if="filterValue && (!visibleOptions || (visibleOptions && visibleOptions.length === 0))" class="p-dropdown-empty-message">{{emptyFilterMessage}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<slot name="footer" :value="modelValue" :options="visibleOptions"></slot>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
@ -43,6 +59,7 @@
|
|||
import {ConnectedOverlayScrollHandler} from 'primevue/utils';
|
||||
import {ObjectUtils} from 'primevue/utils';
|
||||
import {DomHandler} from 'primevue/utils';
|
||||
import {FilterService} from 'primevue/api';
|
||||
import Ripple from 'primevue/ripple';
|
||||
|
||||
export default {
|
||||
|
@ -53,6 +70,8 @@ export default {
|
|||
optionLabel: null,
|
||||
optionValue: null,
|
||||
optionDisabled: null,
|
||||
optionGroupLabel: null,
|
||||
optionGroupChildren: null,
|
||||
scrollHeight: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
|
@ -60,6 +79,14 @@ export default {
|
|||
filter: Boolean,
|
||||
filterPlaceholder: String,
|
||||
filterLocale: String,
|
||||
filterMatchMode: {
|
||||
type: String,
|
||||
default: 'contains'
|
||||
},
|
||||
filterFields: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
editable: Boolean,
|
||||
placeholder: String,
|
||||
disabled: Boolean,
|
||||
|
@ -74,7 +101,11 @@ export default {
|
|||
},
|
||||
emptyFilterMessage: {
|
||||
type: String,
|
||||
default: 'No results found'
|
||||
default: null
|
||||
},
|
||||
emptyMessage: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
@ -116,37 +147,48 @@ export default {
|
|||
isOptionDisabled(option) {
|
||||
return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;
|
||||
},
|
||||
getOptionGroupRenderKey(optionGroup) {
|
||||
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel);
|
||||
},
|
||||
getOptionGroupLabel(optionGroup) {
|
||||
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel);
|
||||
},
|
||||
getOptionGroupChildren(optionGroup) {
|
||||
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren);
|
||||
},
|
||||
getSelectedOption() {
|
||||
let selectedOption;
|
||||
|
||||
let index = this.getSelectedOptionIndex();
|
||||
return index !== -1 ? (this.optionGroupLabel ? this.getOptionGroupChildren(this.options[index.group])[index.option]: this.options[index]) : null;
|
||||
},
|
||||
getSelectedOptionIndex() {
|
||||
if (this.modelValue != null && this.options) {
|
||||
for (let option of this.options) {
|
||||
if ((ObjectUtils.equals(this.modelValue, this.getOptionValue(option), this.equalityKey))) {
|
||||
selectedOption = option;
|
||||
break;
|
||||
if (this.optionGroupLabel) {
|
||||
for (let i = 0; i < this.options.length; i++) {
|
||||
let selectedOptionIndex = this.findOptionIndexInList(this.modelValue, this.getOptionGroupChildren(this.options[i]));
|
||||
if (selectedOptionIndex !== -1) {
|
||||
return {group: i, option: selectedOptionIndex};
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this.findOptionIndexInList(this.modelValue, this.options);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
findOptionIndexInList(value, list) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if ((ObjectUtils.equals(value, this.getOptionValue(list[i]), this.equalityKey))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return selectedOption;
|
||||
return -1;
|
||||
},
|
||||
isSelected(option) {
|
||||
return ObjectUtils.equals(this.modelValue, this.getOptionValue(option), this.equalityKey);
|
||||
},
|
||||
getSelectedOptionIndex() {
|
||||
let selectedOptionIndex = -1;
|
||||
|
||||
if (this.modelValue != null && this.visibleOptions) {
|
||||
for (let i = 0; i < this.visibleOptions.length; i++) {
|
||||
if ((ObjectUtils.equals(this.modelValue, this.getOptionValue(this.visibleOptions[i]), this.equalityKey))) {
|
||||
selectedOptionIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectedOptionIndex;
|
||||
},
|
||||
show() {
|
||||
this.$emit('before-show');
|
||||
this.overlayVisible = true;
|
||||
|
@ -230,7 +272,6 @@ export default {
|
|||
}
|
||||
else {
|
||||
let nextOption = this.findNextOption(this.getSelectedOptionIndex());
|
||||
|
||||
if (nextOption) {
|
||||
this.updateModel(event, this.getOptionValue(nextOption));
|
||||
}
|
||||
|
@ -242,7 +283,6 @@ export default {
|
|||
onUpKey(event) {
|
||||
if (this.visibleOptions) {
|
||||
let prevOption = this.findPrevOption(this.getSelectedOptionIndex());
|
||||
|
||||
if (prevOption) {
|
||||
this.updateModel(event, this.getOptionValue(prevOption));
|
||||
}
|
||||
|
@ -251,25 +291,62 @@ export default {
|
|||
event.preventDefault();
|
||||
},
|
||||
findNextOption(index) {
|
||||
let i = index + 1;
|
||||
if (i === this.visibleOptions.length) {
|
||||
if (this.optionGroupLabel) {
|
||||
let groupIndex = index === -1 ? 0 : index.group;
|
||||
let optionIndex = index === -1 ? -1 : index.option;
|
||||
let option = this.findNextOptionInList(this.getOptionGroupChildren(this.visibleOptions[groupIndex]), optionIndex);
|
||||
|
||||
if (option)
|
||||
return option;
|
||||
else if ((groupIndex + 1) !== this.visibleOptions.length)
|
||||
return this.findNextOption({group: (groupIndex + 1), option: -1});
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return this.findNextOptionInList(this.visibleOptions, index);
|
||||
}
|
||||
},
|
||||
findNextOptionInList(list, index) {
|
||||
let i = index + 1;
|
||||
if (i === list.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let option = list[i];
|
||||
if (this.isOptionDisabled(option))
|
||||
return this.findNextOptionInList(i);
|
||||
else
|
||||
return option;
|
||||
},
|
||||
findPrevOption(index) {
|
||||
if (index === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let option = this.visibleOptions[i];
|
||||
if (this.isOptionDisabled(option))
|
||||
return this.findNextOption(i);
|
||||
else
|
||||
return option;
|
||||
if (this.optionGroupLabel) {
|
||||
let groupIndex = index.group;
|
||||
let optionIndex = index.option;
|
||||
let option = this.findPrevOptionInList(this.getOptionGroupChildren(this.visibleOptions[groupIndex]), optionIndex);
|
||||
|
||||
if (option)
|
||||
return option;
|
||||
else if (groupIndex > 0)
|
||||
return this.findPrevOption({group: (groupIndex - 1), option: this.getOptionGroupChildren(this.visibleOptions[groupIndex - 1]).length});
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return this.findPrevOptionInList(this.visibleOptions, index);
|
||||
}
|
||||
},
|
||||
findPrevOption(index) {
|
||||
findPrevOptionInList(list, index) {
|
||||
let i = index - 1;
|
||||
if (i < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let option = this.visibleOptions[i];
|
||||
let option = list[i];
|
||||
if (this.isOptionDisabled(option))
|
||||
return this.findPrevOption(i);
|
||||
else
|
||||
|
@ -406,13 +483,14 @@ export default {
|
|||
else
|
||||
this.searchValue = this.searchValue ? this.searchValue + char : char;
|
||||
|
||||
let searchIndex = this.getSelectedOptionIndex();
|
||||
let newOption = this.searchOption(++searchIndex);
|
||||
|
||||
if (newOption) {
|
||||
this.updateModel(event, this.getOptionValue(newOption));
|
||||
if (this.searchValue) {
|
||||
let searchIndex = this.getSelectedOptionIndex();
|
||||
let newOption = this.optionGroupLabel ? this.searchOptionInGroup(searchIndex) : this.searchOption(++searchIndex);
|
||||
if (newOption) {
|
||||
this.updateModel(event, this.getOptionValue(newOption));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.searchTimeout = setTimeout(() => {
|
||||
this.searchValue = null;
|
||||
}, 250);
|
||||
|
@ -433,14 +511,40 @@ export default {
|
|||
searchOptionInRange(start, end) {
|
||||
for (let i = start; i < end; i++) {
|
||||
let opt = this.visibleOptions[i];
|
||||
let label = this.getOptionLabel(opt).toLocaleLowerCase(this.filterLocale);
|
||||
if (label.startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale))) {
|
||||
if (this.matchesSearchValue(opt)) {
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
searchOptionInGroup(index) {
|
||||
let searchIndex = index === -1 ? {group: 0, option: -1} : index;
|
||||
|
||||
for (let i = searchIndex.group; i < this.visibleOptions.length; i++) {
|
||||
let groupOptions = this.getOptionGroupChildren(this.visibleOptions[i]);
|
||||
for (let j = (searchIndex.group === i ? searchIndex.option + 1 : 0); j < groupOptions.length; j++) {
|
||||
if (this.matchesSearchValue(groupOptions[j])) {
|
||||
return groupOptions[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i <= searchIndex.group; i++) {
|
||||
let groupOptions = this.getOptionGroupChildren(this.visibleOptions[i]);
|
||||
for (let j = 0; j < (searchIndex.group === i ? searchIndex.option: groupOptions.length); j++) {
|
||||
if (this.matchesSearchValue(groupOptions[j])) {
|
||||
return groupOptions[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
matchesSearchValue(option) {
|
||||
let label = this.getOptionLabel(option).toLocaleLowerCase(this.filterLocale);
|
||||
return label.startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale));
|
||||
},
|
||||
appendContainer() {
|
||||
if (this.appendTo) {
|
||||
if (this.appendTo === 'body')
|
||||
|
@ -469,10 +573,24 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
visibleOptions() {
|
||||
if (this.filterValue && this.filterValue.trim().length > 0)
|
||||
return this.options.filter(option => this.getOptionLabel(option).toLocaleLowerCase(this.filterLocale).indexOf(this.filterValue.toLocaleLowerCase(this.filterLocale)) > -1);
|
||||
else
|
||||
if (this.filterValue) {
|
||||
if (this.optionGroupLabel) {
|
||||
let filteredGroups = [];
|
||||
for (let optgroup of this.options) {
|
||||
let filteredSubOptions = FilterService.filter(this.getOptionGroupChildren(optgroup), this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
||||
if (filteredSubOptions && filteredSubOptions.length) {
|
||||
filteredGroups.push({...optgroup, ...{items: filteredSubOptions}});
|
||||
}
|
||||
}
|
||||
return filteredGroups
|
||||
}
|
||||
else {
|
||||
return FilterService.filter(this.options, this.searchFields, this.filterValue, 'contains', this.filterLocale);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this.options;
|
||||
}
|
||||
},
|
||||
containerClass() {
|
||||
return [
|
||||
|
@ -511,6 +629,15 @@ export default {
|
|||
},
|
||||
equalityKey() {
|
||||
return this.optionValue ? null : this.dataKey;
|
||||
},
|
||||
searchFields() {
|
||||
return this.filterFields || [this.optionLabel];
|
||||
},
|
||||
emptyFilterMessageText() {
|
||||
return this.emptyFilterMessage || this.$primevue.config.locale.emptyFilterMessage;
|
||||
},
|
||||
emptyMessageText() {
|
||||
return this.emptyMessage || this.$primevue.config.locale.emptyMessage;
|
||||
}
|
||||
},
|
||||
directives: {
|
||||
|
|
Loading…
Reference in New Issue