Refactor #1451 - For Listbox

pull/1453/head
mertsincan 2021-08-16 13:48:28 +03:00
parent 47edaea7ad
commit ba75e9c51c
2 changed files with 52 additions and 22 deletions

View File

@ -1,4 +1,5 @@
import { VNode } from 'vue'; import { VNode } from 'vue';
import { VirtualScrollerProps } from '../virtualscroller';
interface ListboxProps { interface ListboxProps {
modelValue?: any; modelValue?: any;
@ -20,6 +21,7 @@ interface ListboxProps {
filterFields?: string[]; filterFields?: string[];
emptyFilterMessage?: string; emptyFilterMessage?: string;
emptyMessage?: string; emptyMessage?: string;
virtualScrollerOptions?: VirtualScrollerProps;
} }
declare class Listbox { declare class Listbox {

View File

@ -8,31 +8,38 @@
</div> </div>
</div> </div>
<div class="p-listbox-list-wrapper" :style="listStyle"> <div class="p-listbox-list-wrapper" :style="listStyle">
<ul class="p-listbox-list" role="listbox" aria-multiselectable="multiple"> <VirtualScroller :ref="virtualScrollerRef" v-bind="virtualScrollerOptions" :style="listStyle" :items="visibleOptions" :disabled="virtualScrollerDisabled">
<template v-if="!optionGroupLabel"> <template v-slot:content="{ styleClass, contentRef, items, getItemOptions }">
<li v-for="(option, i) of visibleOptions" :tabindex="isOptionDisabled(option) ? null : '0'" :class="['p-listbox-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple <ul :ref="contentRef" :class="['p-listbox-list', styleClass]" role="listbox" aria-multiselectable="multiple">
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" @touchend="onOptionTouchEnd()" @keydown="onOptionKeyDown($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)" :tabindex="isOptionDisabled(option) ? null : '0'" :class="['p-listbox-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple
</li> :key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" @touchend="onOptionTouchEnd()" @keydown="onOptionKeyDown($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-listbox-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-listbox-item-group">
<slot name="optiongroup" :option="optionGroup" :index="getOptionIndex(i, getItemOptions)">{{getOptionGroupLabel(optionGroup)}}</slot>
</li>
<li v-for="(option, i) of getOptionGroupChildren(optionGroup)" :tabindex="isOptionDisabled(option) ? null : '0'" :class="['p-listbox-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" @touchend="onOptionTouchEnd()" @keydown="onOptionKeyDown($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-listbox-empty-message">
<slot name="emptyfilter">{{emptyFilterMessageText}}</slot>
</li> </li>
<li v-for="(option, i) of getOptionGroupChildren(optionGroup)" :tabindex="isOptionDisabled(option) ? null : '0'" :class="['p-listbox-item', {'p-highlight': isSelected(option), 'p-disabled': isOptionDisabled(option)}]" v-ripple <li v-else-if="(!options || (options && options.length === 0))" class="p-listbox-empty-message">
:key="getOptionRenderKey(option)" @click="onOptionSelect($event, option)" @touchend="onOptionTouchEnd()" @keydown="onOptionKeyDown($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-listbox-empty-message"> <template v-slot:loader="{ options }" v-if="$slots.loader">
<slot name="emptyfilter">{{emptyFilterMessageText}}</slot> <slot name="loader" :options="options"></slot>
</li> </template>
<li v-else-if="(!options || (options && options.length === 0))" class="p-listbox-empty-message"> </VirtualScroller>
<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>
@ -43,6 +50,7 @@ import {ObjectUtils} from 'primevue/utils';
import {DomHandler} from 'primevue/utils'; import {DomHandler} from 'primevue/utils';
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: 'Listbox', name: 'Listbox',
@ -78,15 +86,26 @@ export default {
emptyMessage: { emptyMessage: {
type: String, type: String,
default: null default: null
},
virtualScrollerOptions: {
type: Object,
default: null
} }
}, },
optionTouched: false, optionTouched: false,
virtualScroller: null,
data() { data() {
return { return {
filterValue: null filterValue: null
}; };
}, },
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;
}, },
@ -268,6 +287,9 @@ export default {
}, },
onFilterChange(event) { onFilterChange(event) {
this.$emit('filter', {originalEvent: event, value: event.target.value}); this.$emit('filter', {originalEvent: event, value: event.target.value});
},
virtualScrollerRef(el) {
this.virtualScroller = el;
} }
}, },
computed: { computed: {
@ -302,10 +324,16 @@ export default {
}, },
emptyMessageText() { emptyMessageText() {
return this.emptyMessage || this.$primevue.config.locale.emptyMessage; return this.emptyMessage || this.$primevue.config.locale.emptyMessage;
},
virtualScrollerDisabled() {
return !this.virtualScrollerOptions;
} }
}, },
directives: { directives: {
'ripple': Ripple 'ripple': Ripple
},
components: {
'VirtualScroller': VirtualScroller
} }
} }
</script> </script>