AutoComplete option list scroll behavior fixes

pull/6537/head
tugcekucukoglu 2024-10-07 12:58:23 +03:00
parent 0125015937
commit dc34e05a1b
4 changed files with 61 additions and 48 deletions

View File

@ -190,6 +190,10 @@ export interface AutoCompletePassThroughOptions {
* @see {@link VirtualScrollerPassThroughOptionType}
*/
virtualScroller?: VirtualScrollerPassThroughOptionType;
/**
* Used to pass attributes to the list container's DOM element.
*/
listContainer?: AutoCompletePassThroughOptionType;
/**
* Used to pass attributes to the list's DOM element.
*/

View File

@ -121,55 +121,55 @@
</span>
<Portal :appendTo="appendTo">
<transition name="p-connected-overlay" @enter="onOverlayEnter" @after-enter="onOverlayAfterEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave" v-bind="ptm('transition')">
<div
v-if="overlayVisible"
:ref="overlayRef"
:id="panelId"
:class="[cx('overlay'), panelClass, overlayClass]"
:style="{ ...panelStyle, ...overlayStyle, 'max-height': virtualScrollerDisabled ? scrollHeight : '' }"
@click="onOverlayClick"
@keydown="onOverlayKeyDown"
v-bind="ptm('overlay')"
>
<div v-if="overlayVisible" :ref="overlayRef" :id="panelId" :class="[cx('overlay'), panelClass, overlayClass]" :style="{ ...panelStyle, ...overlayStyle }" @click="onOverlayClick" @keydown="onOverlayKeyDown" v-bind="ptm('overlay')">
<slot name="header" :value="modelValue" :suggestions="visibleOptions"></slot>
<VirtualScroller :ref="virtualScrollerRef" v-bind="virtualScrollerOptions" :style="{ height: scrollHeight }" :items="visibleOptions" :tabindex="-1" :disabled="virtualScrollerDisabled" :pt="ptm('virtualScroller')">
<template v-slot:content="{ styleClass, contentRef, items, getItemOptions, contentStyle, itemSize }">
<ul :ref="(el) => listRef(el, contentRef)" :id="id + '_list'" :class="[cx('list'), styleClass]" :style="contentStyle" role="listbox" :aria-label="listAriaLabel" v-bind="ptm('list')">
<template v-for="(option, i) of items" :key="getOptionRenderKey(option, getOptionIndex(i, getItemOptions))">
<li v-if="isOptionGroup(option)" :id="id + '_' + getOptionIndex(i, getItemOptions)" :style="{ height: itemSize ? itemSize + 'px' : undefined }" :class="cx('optionGroup')" role="option" v-bind="ptm('optionGroup')">
<slot name="optiongroup" :option="option.optionGroup" :index="getOptionIndex(i, getItemOptions)">{{ getOptionGroupLabel(option.optionGroup) }}</slot>
<div :class="cx('listContainer')" :style="{ 'max-height': virtualScrollerDisabled ? scrollHeight : '' }" v-bind="ptm('listContainer')">
<VirtualScroller :ref="virtualScrollerRef" v-bind="virtualScrollerOptions" :style="{ height: scrollHeight }" :items="visibleOptions" :tabindex="-1" :disabled="virtualScrollerDisabled" :pt="ptm('virtualScroller')">
<template v-slot:content="{ styleClass, contentRef, items, getItemOptions, contentStyle, itemSize }">
<ul :ref="(el) => listRef(el, contentRef)" :id="id + '_list'" :class="[cx('list'), styleClass]" :style="contentStyle" role="listbox" :aria-label="listAriaLabel" v-bind="ptm('list')">
<template v-for="(option, i) of items" :key="getOptionRenderKey(option, getOptionIndex(i, getItemOptions))">
<li
v-if="isOptionGroup(option)"
:id="id + '_' + getOptionIndex(i, getItemOptions)"
:style="{ height: itemSize ? itemSize + 'px' : undefined }"
:class="cx('optionGroup')"
role="option"
v-bind="ptm('optionGroup')"
>
<slot name="optiongroup" :option="option.optionGroup" :index="getOptionIndex(i, getItemOptions)">{{ getOptionGroupLabel(option.optionGroup) }}</slot>
</li>
<li
v-else
:id="id + '_' + getOptionIndex(i, getItemOptions)"
v-ripple
:style="{ height: itemSize ? itemSize + 'px' : undefined }"
:class="cx('option', { option, i, getItemOptions })"
role="option"
:aria-label="getOptionLabel(option)"
:aria-selected="isSelected(option)"
:aria-disabled="isOptionDisabled(option)"
:aria-setsize="ariaSetSize"
:aria-posinset="getAriaPosInset(getOptionIndex(i, getItemOptions))"
@click="onOptionSelect($event, option)"
@mousemove="onOptionMouseMove($event, getOptionIndex(i, getItemOptions))"
:data-p-selected="isSelected(option)"
:data-p-focus="focusedOptionIndex === getOptionIndex(i, getItemOptions)"
:data-p-disabled="isOptionDisabled(option)"
v-bind="getPTOptions(option, getItemOptions, i, 'option')"
>
<slot name="option" :option="option" :index="getOptionIndex(i, getItemOptions)">{{ getOptionLabel(option) }}</slot>
</li>
</template>
<li v-if="!items || (items && items.length === 0)" :class="cx('emptyMessage')" role="option" v-bind="ptm('emptyMessage')">
<slot name="empty">{{ searchResultMessageText }}</slot>
</li>
<li
v-else
:id="id + '_' + getOptionIndex(i, getItemOptions)"
v-ripple
:style="{ height: itemSize ? itemSize + 'px' : undefined }"
:class="cx('option', { option, i, getItemOptions })"
role="option"
:aria-label="getOptionLabel(option)"
:aria-selected="isSelected(option)"
:aria-disabled="isOptionDisabled(option)"
:aria-setsize="ariaSetSize"
:aria-posinset="getAriaPosInset(getOptionIndex(i, getItemOptions))"
@click="onOptionSelect($event, option)"
@mousemove="onOptionMouseMove($event, getOptionIndex(i, getItemOptions))"
:data-p-selected="isSelected(option)"
:data-p-focus="focusedOptionIndex === getOptionIndex(i, getItemOptions)"
:data-p-disabled="isOptionDisabled(option)"
v-bind="getPTOptions(option, getItemOptions, i, 'option')"
>
<slot name="option" :option="option" :index="getOptionIndex(i, getItemOptions)">{{ getOptionLabel(option) }}</slot>
</li>
</template>
<li v-if="!items || (items && items.length === 0)" :class="cx('emptyMessage')" role="option" v-bind="ptm('emptyMessage')">
<slot name="empty">{{ searchResultMessageText }}</slot>
</li>
</ul>
</template>
<template v-if="$slots.loader" v-slot:loader="{ options }">
<slot name="loader" :options="options"></slot>
</template>
</VirtualScroller>
</ul>
</template>
<template v-if="$slots.loader" v-slot:loader="{ options }">
<slot name="loader" :options="options"></slot>
</template>
</VirtualScroller>
</div>
<slot name="footer" :value="modelValue" :suggestions="visibleOptions"></slot>
<span role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenSelectedMessage')" :data-p-hidden-accessible="true">
{{ selectedMessageText }}

View File

@ -54,6 +54,10 @@ export enum AutoCompleteClasses {
* Class name of the list element
*/
list = 'p-autocomplete-list',
/**
* Class name of the list container element
*/
listContainer = 'p-autocomplete-list-container',
/**
* Class name of the option group element
*/

View File

@ -84,6 +84,10 @@ const theme = ({ dt }) => `
box-shadow: ${dt('autocomplete.overlay.shadow')};
}
.p-autocomplete-list-container {
overflow: auto;
}
.p-autocomplete-list {
margin: 0;
padding: 0;
@ -278,6 +282,7 @@ const classes = {
loader: 'p-autocomplete-loader',
dropdown: 'p-autocomplete-dropdown',
overlay: 'p-autocomplete-overlay p-component',
listContainer: 'p-autocomplete-list-container',
list: 'p-autocomplete-list',
optionGroup: 'p-autocomplete-option-group',
option: ({ instance, option, i, getItemOptions }) => [