Add form support to `AutoComplete`

pull/6632/head
Mert Sincan 2024-10-18 16:16:30 +01:00
parent ae930545db
commit 61208c4d46
3 changed files with 39 additions and 55 deletions

View File

@ -5,12 +5,13 @@
ref="focusInput"
:id="inputId"
type="text"
:name="$formName"
:class="[cx('pcInputText'), inputClass]"
:style="inputStyle"
:value="inputValue"
:placeholder="placeholder"
:tabindex="!disabled ? tabindex : -1"
:fluid="hasFluid"
:fluid="$fluid"
:disabled="disabled"
:invalid="invalid"
:variant="variant"
@ -45,14 +46,14 @@
v-bind="ptm('inputMultiple')"
>
<li
v-for="(option, i) of modelValue"
v-for="(option, i) of d_value"
:key="`${i}_${getOptionLabel(option)}`"
:id="id + '_multiple_option_' + i"
:class="cx('chipItem', { i })"
role="option"
:aria-label="getOptionLabel(option)"
:aria-selected="true"
:aria-setsize="modelValue.length"
:aria-setsize="d_value.length"
:aria-posinset="i + 1"
v-bind="ptm('chipItem')"
>
@ -122,7 +123,7 @@
<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 }" @click="onOverlayClick" @keydown="onOverlayKeyDown" v-bind="ptm('overlay')">
<slot name="header" :value="modelValue" :suggestions="visibleOptions"></slot>
<slot name="header" :value="d_value" :suggestions="visibleOptions"></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 }">
@ -170,7 +171,7 @@
</template>
</VirtualScroller>
</div>
<slot name="footer" :value="modelValue" :suggestions="visibleOptions"></slot>
<slot name="footer" :value="d_value" :suggestions="visibleOptions"></slot>
<span role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenSelectedMessage')" :data-p-hidden-accessible="true">
{{ selectedMessageText }}
</span>
@ -199,7 +200,7 @@ export default {
name: 'AutoComplete',
extends: BaseAutoComplete,
inheritAttrs: false,
emits: ['update:modelValue', 'change', 'focus', 'blur', 'item-select', 'item-unselect', 'option-select', 'option-unselect', 'dropdown-click', 'clear', 'complete', 'before-show', 'before-hide', 'show', 'hide'],
emits: ['change', 'focus', 'blur', 'item-select', 'item-unselect', 'option-select', 'option-unselect', 'dropdown-click', 'clear', 'complete', 'before-show', 'before-hide', 'show', 'hide'],
inject: {
$pcFluid: { default: null }
},
@ -342,6 +343,7 @@ export default {
this.focused = false;
this.focusedOptionIndex = -1;
this.$emit('blur', event);
this.formField.onBlur?.();
},
onKeyDown(event) {
if (this.disabled) {
@ -533,7 +535,7 @@ export default {
this.$refs.focusInput.value = '';
if (!this.isSelected(option)) {
this.updateModel(event, [...(this.modelValue || []), value]);
this.updateModel(event, [...(this.d_value || []), value]);
}
} else {
this.updateModel(event, value);
@ -602,9 +604,9 @@ export default {
this.focusedOptionIndex = -1;
if (this.multiple) {
if (isEmpty(target.value) && this.hasSelectedOption) {
if (isEmpty(target.value) && this.$filled) {
focus(this.$refs.multiContainer);
this.focusedMultipleOptionIndex = this.modelValue.length;
this.focusedMultipleOptionIndex = this.d_value.length;
} else {
event.stopPropagation(); // To prevent onArrowLeftKeyOnMultiple method
}
@ -644,7 +646,7 @@ export default {
onEnterKey(event) {
if (!this.typeahead) {
if (this.multiple) {
this.updateModel(event, [...(this.modelValue || []), event.target.value]);
this.updateModel(event, [...(this.d_value || []), event.target.value]);
this.$refs.focusInput.value = '';
}
} else {
@ -673,11 +675,11 @@ export default {
},
onBackspaceKey(event) {
if (this.multiple) {
if (isNotEmpty(this.modelValue) && !this.$refs.focusInput.value) {
const removedValue = this.modelValue[this.modelValue.length - 1];
const newValue = this.modelValue.slice(0, -1);
if (isNotEmpty(this.d_value) && !this.$refs.focusInput.value) {
const removedValue = this.d_value[this.d_value.length - 1];
const newValue = this.d_value.slice(0, -1);
this.$emit('update:modelValue', newValue);
this.updateValue(newValue, event);
this.$emit('item-unselect', { originalEvent: event, value: removedValue });
this.$emit('option-unselect', { originalEvent: event, value: removedValue });
}
@ -691,7 +693,7 @@ export default {
onArrowRightKeyOnMultiple() {
this.focusedMultipleOptionIndex++;
if (this.focusedMultipleOptionIndex > this.modelValue.length - 1) {
if (this.focusedMultipleOptionIndex > this.d_value.length - 1) {
this.focusedMultipleOptionIndex = -1;
focus(this.$refs.focusInput);
}
@ -810,7 +812,7 @@ export default {
isSelected(option) {
const optionValue = this.getOptionValue(option);
return this.multiple ? (this.modelValue || []).some((value) => this.isEquals(value, optionValue)) : this.isEquals(this.modelValue, this.getOptionValue(option));
return this.multiple ? (this.d_value || []).some((value) => this.isEquals(value, optionValue)) : this.isEquals(this.d_value, this.getOptionValue(option));
},
findFirstOptionIndex() {
return this.visibleOptions.findIndex((option) => this.isValidOption(option));
@ -829,7 +831,7 @@ export default {
return matchedOptionIndex > -1 ? matchedOptionIndex : index;
},
findSelectedOptionIndex() {
return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
return this.$filled ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
},
findFirstFocusedOptionIndex() {
const selectedIndex = this.findSelectedOptionIndex();
@ -856,8 +858,8 @@ export default {
this.$emit('complete', { originalEvent: event, query });
},
removeOption(event, index) {
const removedOption = this.modelValue[index];
const value = this.modelValue.filter((_, i) => i !== index).map((option) => this.getOptionValue(option));
const removedOption = this.d_value[index];
const value = this.d_value.filter((_, i) => i !== index).map((option) => this.getOptionValue(option));
this.updateModel(event, value);
this.$emit('item-unselect', { originalEvent: event, value: removedOption });
@ -888,13 +890,13 @@ export default {
});
},
autoUpdateModel() {
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) {
if (this.selectOnFocus && this.autoOptionFocus && !this.$filled) {
this.focusedOptionIndex = this.findFirstFocusedOptionIndex();
this.onOptionSelect(null, this.visibleOptions[this.focusedOptionIndex], false);
}
},
updateModel(event, value) {
this.$emit('update:modelValue', value);
this.updateValue(value, event);
this.$emit('change', { originalEvent: event, value });
},
flatOptions(options) {
@ -924,23 +926,25 @@ export default {
return this.optionGroupLabel ? this.flatOptions(this.suggestions) : this.suggestions || [];
},
inputValue() {
if (isNotEmpty(this.modelValue)) {
if (typeof this.modelValue === 'object') {
const label = this.getOptionLabel(this.modelValue);
if (this.$filled) {
if (typeof this.d_value === 'object') {
const label = this.getOptionLabel(this.d_value);
return label != null ? label : this.modelValue;
return label != null ? label : this.d_value;
} else {
return this.modelValue;
return this.d_value;
}
} else {
return '';
}
},
// @deprecated use $filled instead.
hasSelectedOption() {
return isNotEmpty(this.modelValue);
return this.$filled;
},
equalityKey() {
return this.dataKey; // TODO: The 'optionValue' properties can be added.
// @todo: The 'optionValue' properties can be added.
return this.dataKey;
},
searchResultMessageText() {
return isNotEmpty(this.visibleOptions) && this.overlayVisible ? this.searchMessageText.replaceAll('{0}', this.visibleOptions.length) : this.emptySearchMessageText;
@ -958,7 +962,7 @@ export default {
return this.emptySelectionMessage || this.$primevue.config.locale.emptySelectionMessage || '';
},
selectedMessageText() {
return this.hasSelectedOption ? this.selectionMessageText.replaceAll('{0}', this.multiple ? this.modelValue.length : '1') : this.emptySelectionMessageText;
return this.$filled ? this.selectionMessageText.replaceAll('{0}', this.multiple ? this.d_value.length : '1') : this.emptySelectionMessageText;
},
listAriaLabel() {
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.listLabel : undefined;
@ -977,9 +981,6 @@ export default {
},
panelId() {
return this.id + '_panel';
},
hasFluid() {
return isEmpty(this.fluid) ? !!this.$pcFluid : this.fluid;
}
},
components: {

View File

@ -1,12 +1,11 @@
<script>
import BaseComponent from '@primevue/core/basecomponent';
import BaseInput from '@primevue/core/baseinput';
import AutoCompleteStyle from 'primevue/autocomplete/style';
export default {
name: 'BaseAutoComplete',
extends: BaseComponent,
extends: BaseInput,
props: {
modelValue: null,
suggestions: {
type: Array,
default: null
@ -35,18 +34,6 @@ export default {
type: Boolean,
default: false
},
variant: {
type: String,
default: null
},
invalid: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: null
@ -178,10 +165,6 @@ export default {
ariaLabelledby: {
type: String,
default: null
},
fluid: {
type: Boolean,
default: null
}
},
style: AutoCompleteStyle,

View File

@ -253,19 +253,19 @@ const classes = {
'p-autocomplete p-component p-inputwrapper',
{
'p-disabled': props.disabled,
'p-invalid': props.invalid,
'p-invalid': instance.$invalid,
'p-focus': instance.focused,
'p-inputwrapper-filled': props.modelValue || isNotEmpty(instance.inputValue),
'p-inputwrapper-filled': instance.$filled || isNotEmpty(instance.inputValue),
'p-inputwrapper-focus': instance.focused,
'p-autocomplete-open': instance.overlayVisible,
'p-autocomplete-fluid': instance.hasFluid
'p-autocomplete-fluid': instance.$fluid
}
],
pcInputText: 'p-autocomplete-input',
inputMultiple: ({ props, instance }) => [
'p-autocomplete-input-multiple',
{
'p-variant-filled': props.variant ? props.variant === 'filled' : instance.$primevue.config.inputStyle === 'filled' || instance.$primevue.config.inputVariant === 'filled'
'p-variant-filled': instance.$variant === 'filled'
}
],
chipItem: ({ instance, i }) => [