Refactor #1451 - For Dropdown
parent
efc8b36523
commit
1ac34d77ca
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue