pull/978/head
Cagatay Civici 2021-02-15 15:35:42 +03:00
parent 7b6e8e2e68
commit 75b2ff8561
1 changed files with 180 additions and 53 deletions

View File

@ -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: {