Refactor #1451 - For Dropdown

pull/1453/head
mertsincan 2021-08-13 17:29:39 +03:00
parent efc8b36523
commit 1ac34d77ca
4 changed files with 70 additions and 31 deletions

View File

@ -14,6 +14,7 @@ let globalDependencies = {
'primevue/utils': 'primevue.utils', 'primevue/utils': 'primevue.utils',
'primevue/button': 'primevue.button', 'primevue/button': 'primevue.button',
'primevue/inputtext': 'primevue.inputtext', 'primevue/inputtext': 'primevue.inputtext',
'primevue/virtualscroller': 'primevue.virtualscroller',
'primevue/dialog': 'primevue.dialog', 'primevue/dialog': 'primevue.dialog',
'primevue/paginator': 'primevue.paginator', 'primevue/paginator': 'primevue.paginator',
'primevue/confirmationeventbus': 'primevue.confirmationeventbus', 'primevue/confirmationeventbus': 'primevue.confirmationeventbus',
@ -129,4 +130,4 @@ addUtils();
addApi(); addApi();
addServices(); addServices();
export default entries; export default entries;

View File

@ -23,32 +23,36 @@
<span class="p-dropdown-filter-icon pi pi-search"></span> <span class="p-dropdown-filter-icon pi pi-search"></span>
</div> </div>
</div> </div>
<div :ref="itemsWrapperRef" class="p-dropdown-items-wrapper" :style="{'max-height': scrollHeight}"> <div :ref="itemsWrapperRef" class="p-dropdown-items-wrapper" :style="{'max-height': virtualScrollerDisabled ? scrollHeight : ''}">
<ul class="p-dropdown-items" role="listbox"> <VirtualScroller :ref="virtualScrollerRef" v-bind="virtualScrollerOptions" :items="visibleOptions" :style="{'height': scrollHeight}" :disabled="virtualScrollerDisabled">
<template v-if="!optionGroupLabel"> <template v-slot:content="{ styleClass, contentRef, items, getItemOptions }">
<li v-for="(option, i) of visibleOptions" :class="['p-dropdown-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple <ul :ref="contentRef" :class="['p-dropdown-items', styleClass]" role="listbox">
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" role="option" :aria-label="getOptionLabel(option)" :aria-selected="isSelected(option)"> <template v-if="!optionGroupLabel">
<slot name="option" :option="option" :index="i">{{getOptionLabel(option)}}</slot> <li v-for="(option, i) of getVisibleOptions(items)" :class="['p-dropdown-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple
</li> :key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" role="option" :aria-label="getOptionLabel(option)" :aria-selected="isSelected(option)">
</template> <slot name="option" :option="option" :index="getOptionIndex(i, getItemOptions)">{{getOptionLabel(option)}}</slot>
<template v-else> </li>
<template v-for="(optionGroup, i) of visibleOptions" :key="getOptionGroupRenderKey(optionGroup)"> </template>
<li class="p-dropdown-item-group" > <template v-else>
<slot name="optiongroup" :option="optionGroup" :index="i">{{getOptionGroupLabel(optionGroup)}}</slot> <template v-for="(optionGroup, i) of getVisibleOptions(items)" :key="getOptionGroupRenderKey(optionGroup)">
<li class="p-dropdown-item-group">
<slot name="optiongroup" :option="optionGroup" :index="getOptionIndex(i, getItemOptions)">{{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="getOptionIndex(i, getItemOptions)">{{getOptionLabel(option)}}</slot>
</li>
</template>
</template>
<li v-if="filterValue && (!getVisibleOptions(items) || (getVisibleOptions(items) && getVisibleOptions(items).length === 0))" class="p-dropdown-empty-message">
<slot name="emptyfilter">{{emptyFilterMessageText}}</slot>
</li> </li>
<li v-for="(option, i) of getOptionGroupChildren(optionGroup)" :class="['p-dropdown-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple <li v-else-if="(!options || (options && options.length === 0))" class="p-dropdown-empty-message">
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" role="option" :aria-label="getOptionLabel(option)" :aria-selected="isSelected(option)"> <slot name="empty">{{emptyMessageText}}</slot>
<slot name="option" :option="option" :index="i">{{getOptionLabel(option)}}</slot>
</li> </li>
</template> </ul>
</template> </template>
<li v-if="filterValue && (!visibleOptions || (visibleOptions && visibleOptions.length === 0))" class="p-dropdown-empty-message"> </VirtualScroller>
<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>
</ul>
</div> </div>
<slot name="footer" :value="modelValue" :options="visibleOptions"></slot> <slot name="footer" :value="modelValue" :options="visibleOptions"></slot>
</div> </div>
@ -62,6 +66,7 @@ import {ConnectedOverlayScrollHandler,ObjectUtils,DomHandler,ZIndexUtils} from '
import OverlayEventBus from 'primevue/overlayeventbus'; import OverlayEventBus from 'primevue/overlayeventbus';
import {FilterService} from 'primevue/api'; import {FilterService} from 'primevue/api';
import Ripple from 'primevue/ripple'; import Ripple from 'primevue/ripple';
import VirtualScroller from 'primevue/virtualscroller';
export default { export default {
name: 'Dropdown', name: 'Dropdown',
@ -117,6 +122,10 @@ export default {
loadingIcon: { loadingIcon: {
type: String, type: String,
default: 'pi pi-spinner pi-spin' default: 'pi pi-spinner pi-spin'
},
virtualScrollerOptions: {
type: Object,
default: null
} }
}, },
data() { data() {
@ -135,6 +144,7 @@ export default {
searchValue: null, searchValue: null,
overlay: null, overlay: null,
itemsWrapper: null, itemsWrapper: null,
virtualScroller: null,
beforeUnmount() { beforeUnmount() {
this.unbindOutsideClickListener(); this.unbindOutsideClickListener();
this.unbindResizeListener(); this.unbindResizeListener();
@ -152,6 +162,12 @@ export default {
} }
}, },
methods: { methods: {
getVisibleOptions(items) {
return items || this.visibleOptions;
},
getOptionIndex(index, fn) {
return this.virtualScrollerDisabled ? index : (fn && fn(index)['index']);
},
getOptionLabel(option) { getOptionLabel(option) {
return this.optionLabel ? ObjectUtils.resolveFieldData(option, this.optionLabel) : option; return this.optionLabel ? ObjectUtils.resolveFieldData(option, this.optionLabel) : option;
}, },
@ -415,6 +431,13 @@ export default {
this.$refs.filterInput.focus(); this.$refs.filterInput.focus();
} }
if (!this.virtualScrollerDisabled) {
const selectedIndex = this.getSelectedOptionIndex();
if (selectedIndex !== -1) {
this.virtualScroller.scrollToIndex(selectedIndex);
}
}
this.$emit('show'); this.$emit('show');
}, },
onOverlayLeave() { onOverlayLeave() {
@ -581,6 +604,9 @@ export default {
itemsWrapperRef(el) { itemsWrapperRef(el) {
this.itemsWrapper = el; this.itemsWrapper = el;
}, },
virtualScrollerRef(el) {
this.virtualScroller = el;
},
scrollValueInView() { scrollValueInView() {
if (this.overlay) { if (this.overlay) {
let selectedItem = DomHandler.findSingle(this.overlay, 'li.p-highlight'); let selectedItem = DomHandler.findSingle(this.overlay, 'li.p-highlight');
@ -675,6 +701,9 @@ export default {
appendDisabled() { appendDisabled() {
return this.appendTo === 'self'; return this.appendTo === 'self';
}, },
virtualScrollerDisabled() {
return !this.virtualScrollerOptions;
},
appendTarget() { appendTarget() {
return this.appendDisabled ? null : this.appendTo; return this.appendDisabled ? null : this.appendTo;
}, },
@ -684,6 +713,9 @@ export default {
}, },
directives: { directives: {
'ripple': Ripple 'ripple': Ripple
},
components: {
'VirtualScroller': VirtualScroller
} }
} }
</script> </script>

View File

@ -47,6 +47,9 @@
<h5>Loading State</h5> <h5>Loading State</h5>
<Dropdown placeholder="Loading..." loading></Dropdown> <Dropdown placeholder="Loading..." loading></Dropdown>
<h5>Virtual Scroll (100000 Items)</h5>
<Dropdown v-model="selectedItem" :options="items" optionLabel="label" optionValue="value" :virtualScrollerOptions="{ itemSize: 31 }" placeholder="Select Item"></Dropdown>
</div> </div>
</div> </div>
@ -64,6 +67,7 @@ export default {
selectedCity2: null, selectedCity2: null,
selectedCountry: null, selectedCountry: null,
selectedGroupedCity: null, selectedGroupedCity: null,
selectedItem: null,
cities: [ cities: [
{name: 'New York', code: 'NY'}, {name: 'New York', code: 'NY'},
{name: 'Rome', code: 'RM'}, {name: 'Rome', code: 'RM'},
@ -84,7 +88,7 @@ export default {
{name: 'United States', code: 'US'} {name: 'United States', code: 'US'}
], ],
groupedCities: [{ groupedCities: [{
label: 'Germany', code: 'DE', label: 'Germany', code: 'DE',
items: [ items: [
{label: 'Berlin', value: 'Berlin'}, {label: 'Berlin', value: 'Berlin'},
{label: 'Frankfurt', value: 'Frankfurt'}, {label: 'Frankfurt', value: 'Frankfurt'},
@ -93,7 +97,7 @@ export default {
] ]
}, },
{ {
label: 'USA', code: 'US', label: 'USA', code: 'US',
items: [ items: [
{label: 'Chicago', value: 'Chicago'}, {label: 'Chicago', value: 'Chicago'},
{label: 'Los Angeles', value: 'Los Angeles'}, {label: 'Los Angeles', value: 'Los Angeles'},
@ -102,14 +106,15 @@ export default {
] ]
}, },
{ {
label: 'Japan', code: 'JP', label: 'Japan', code: 'JP',
items: [ items: [
{label: 'Kyoto', value: 'Kyoto'}, {label: 'Kyoto', value: 'Kyoto'},
{label: 'Osaka', value: 'Osaka'}, {label: 'Osaka', value: 'Osaka'},
{label: 'Tokyo', value: 'Tokyo'}, {label: 'Tokyo', value: 'Tokyo'},
{label: 'Yokohama', value: 'Yokohama'} {label: 'Yokohama', value: 'Yokohama'}
] ]
}] }],
items: Array.from({ length: 100000 }, (_, i) => ({ label: `Item #${i}`, value: i }))
} }
}, },
components: { components: {

View File

@ -1,4 +1,4 @@
const path = require('path'); const path = require('path');
module.exports = { module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '/primevue/showcase/' : '/', publicPath: process.env.NODE_ENV === 'production' ? '/primevue/showcase/' : '/',
@ -24,7 +24,8 @@ module.exports = {
'primevue/confirmationeventbus': path.resolve(__dirname, 'src/components/confirmationeventbus/ConfirmationEventBus.js'), 'primevue/confirmationeventbus': path.resolve(__dirname, 'src/components/confirmationeventbus/ConfirmationEventBus.js'),
'primevue/toasteventbus': path.resolve(__dirname, 'src/components/toasteventbus/ToastEventBus.js'), 'primevue/toasteventbus': path.resolve(__dirname, 'src/components/toasteventbus/ToastEventBus.js'),
'primevue/overlayeventbus': path.resolve(__dirname, 'src/components/overlayeventbus/OverlayEventBus.js'), 'primevue/overlayeventbus': path.resolve(__dirname, 'src/components/overlayeventbus/OverlayEventBus.js'),
'primevue/terminalservice': path.resolve(__dirname, 'src/components/terminalservice/TerminalService.js') 'primevue/terminalservice': path.resolve(__dirname, 'src/components/terminalservice/TerminalService.js'),
'primevue/virtualscroller': path.resolve(__dirname, 'src/components/virtualscroller/VirtualScroller.vue')
}, },
}, },
output: { output: {
@ -34,4 +35,4 @@ module.exports = {
css: { css: {
extract: false extract: false
} }
} }