From 75b2ff8561b971fff9edbdc9bcf5bf3eb19cbd8f Mon Sep 17 00:00:00 2001 From: Cagatay Civici Date: Mon, 15 Feb 2021 15:35:42 +0300 Subject: [PATCH] #974 --- src/components/dropdown/Dropdown.vue | 233 +++++++++++++++++++++------ 1 file changed, 180 insertions(+), 53 deletions(-) diff --git a/src/components/dropdown/Dropdown.vue b/src/components/dropdown/Dropdown.vue index 3403a59ab..678efd727 100755 --- a/src/components/dropdown/Dropdown.vue +++ b/src/components/dropdown/Dropdown.vue @@ -7,9 +7,7 @@ - - {{label}} - + {{label}}
@@ -17,6 +15,7 @@
+
@@ -25,15 +24,32 @@
    -
  • - - {{getOptionLabel(option)}} - + + +
  • + {{emptyFilterMessageText}} +
  • +
  • + {{emptyMessageText}}
  • -
  • {{emptyFilterMessage}}
+
@@ -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: {