Add form support to `Listbox`

pull/6632/head
Mert Sincan 2024-10-18 16:52:16 +01:00
parent 044a900f9d
commit 1802fcda9d
3 changed files with 25 additions and 33 deletions

View File

@ -1,12 +1,11 @@
<script> <script>
import BaseComponent from '@primevue/core/basecomponent'; import BaseEditableHolder from '@primevue/core/baseeditableholder';
import ListboxStyle from 'primevue/listbox/style'; import ListboxStyle from 'primevue/listbox/style';
export default { export default {
name: 'BaseListbox', name: 'BaseListbox',
extends: BaseComponent, extends: BaseEditableHolder,
props: { props: {
modelValue: null,
options: Array, options: Array,
optionLabel: null, optionLabel: null,
optionValue: null, optionValue: null,
@ -18,14 +17,6 @@ export default {
type: String, type: String,
default: '14rem' default: '14rem'
}, },
invalid: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
dataKey: null, dataKey: null,
multiple: { multiple: {
type: Boolean, type: Boolean,

View File

@ -12,7 +12,7 @@
:data-p-hidden-focusable="true" :data-p-hidden-focusable="true"
></span> ></span>
<div v-if="$slots.header" :class="cx('header')"> <div v-if="$slots.header" :class="cx('header')">
<slot name="header" :value="modelValue" :options="visibleOptions"></slot> <slot name="header" :value="d_value" :options="visibleOptions"></slot>
</div> </div>
<div v-if="filter" :class="cx('header')" v-bind="ptm('header')"> <div v-if="filter" :class="cx('header')" v-bind="ptm('header')">
<IconField :unstyled="unstyled" :pt="ptm('pcFilterContainer')"> <IconField :unstyled="unstyled" :pt="ptm('pcFilterContainer')">
@ -110,7 +110,7 @@
</template> </template>
</VirtualScroller> </VirtualScroller>
</div> </div>
<slot name="footer" :value="modelValue" :options="visibleOptions"></slot> <slot name="footer" :value="d_value" :options="visibleOptions"></slot>
<span v-if="!options || (options && options.length === 0)" role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenEmptyMessage')" :data-p-hidden-accessible="true"> <span v-if="!options || (options && options.length === 0)" role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenEmptyMessage')" :data-p-hidden-accessible="true">
{{ emptyMessageText }} {{ emptyMessageText }}
</span> </span>
@ -150,7 +150,7 @@ export default {
name: 'Listbox', name: 'Listbox',
extends: BaseListbox, extends: BaseListbox,
inheritAttrs: false, inheritAttrs: false,
emits: ['update:modelValue', 'change', 'focus', 'blur', 'filter', 'item-dblclick', 'option-dblclick'], emits: ['change', 'focus', 'blur', 'filter', 'item-dblclick', 'option-dblclick'],
list: null, list: null,
virtualScroller: null, virtualScroller: null,
optionTouched: false, optionTouched: false,
@ -386,11 +386,11 @@ export default {
if (selected) { if (selected) {
value = metaKey ? this.removeOption(option) : [this.getOptionValue(option)]; value = metaKey ? this.removeOption(option) : [this.getOptionValue(option)];
} else { } else {
value = metaKey ? this.modelValue || [] : []; value = metaKey ? this.d_value || [] : [];
value = [...value, this.getOptionValue(option)]; value = [...value, this.getOptionValue(option)];
} }
} else { } else {
value = selected ? this.removeOption(option) : [...(this.modelValue || []), this.getOptionValue(option)]; value = selected ? this.removeOption(option) : [...(this.d_value || []), this.getOptionValue(option)];
} }
this.updateModel(event, value); this.updateModel(event, value);
@ -561,8 +561,8 @@ export default {
isSelected(option) { isSelected(option) {
const optionValue = this.getOptionValue(option); const optionValue = this.getOptionValue(option);
if (this.multiple) return (this.modelValue || []).some((value) => this.isEquals(value, optionValue)); if (this.multiple) return (this.d_value || []).some((value) => this.isEquals(value, optionValue));
else return this.isEquals(this.modelValue, optionValue); else return this.isEquals(this.d_value, optionValue);
}, },
findFirstOptionIndex() { findFirstOptionIndex() {
return this.visibleOptions.findIndex((option) => this.isValidOption(option)); return this.visibleOptions.findIndex((option) => this.isValidOption(option));
@ -581,10 +581,10 @@ export default {
return matchedOptionIndex > -1 ? matchedOptionIndex : index; return matchedOptionIndex > -1 ? matchedOptionIndex : index;
}, },
findSelectedOptionIndex() { findSelectedOptionIndex() {
if (this.hasSelectedOption) { if (this.$filled) {
if (this.multiple) { if (this.multiple) {
for (let index = this.modelValue.length - 1; index >= 0; index--) { for (let index = this.d_value.length - 1; index >= 0; index--) {
const value = this.modelValue[index]; const value = this.d_value[index];
const matchedOptionIndex = this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option) && this.isEquals(value, this.getOptionValue(option))); const matchedOptionIndex = this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option) && this.isEquals(value, this.getOptionValue(option)));
if (matchedOptionIndex > -1) return matchedOptionIndex; if (matchedOptionIndex > -1) return matchedOptionIndex;
@ -597,25 +597,25 @@ export default {
return -1; return -1;
}, },
findFirstSelectedOptionIndex() { findFirstSelectedOptionIndex() {
return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1; return this.$filled ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
}, },
findLastSelectedOptionIndex() { findLastSelectedOptionIndex() {
return this.hasSelectedOption ? findLastIndex(this.visibleOptions, (option) => this.isValidSelectedOption(option)) : -1; return this.$filled ? findLastIndex(this.visibleOptions, (option) => this.isValidSelectedOption(option)) : -1;
}, },
findNextSelectedOptionIndex(index) { findNextSelectedOptionIndex(index) {
const matchedOptionIndex = this.hasSelectedOption && index < this.visibleOptions.length - 1 ? this.visibleOptions.slice(index + 1).findIndex((option) => this.isValidSelectedOption(option)) : -1; const matchedOptionIndex = this.$filled && index < this.visibleOptions.length - 1 ? this.visibleOptions.slice(index + 1).findIndex((option) => this.isValidSelectedOption(option)) : -1;
return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : -1; return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : -1;
}, },
findPrevSelectedOptionIndex(index) { findPrevSelectedOptionIndex(index) {
const matchedOptionIndex = this.hasSelectedOption && index > 0 ? findLastIndex(this.visibleOptions.slice(0, index), (option) => this.isValidSelectedOption(option)) : -1; const matchedOptionIndex = this.$filled && index > 0 ? findLastIndex(this.visibleOptions.slice(0, index), (option) => this.isValidSelectedOption(option)) : -1;
return matchedOptionIndex > -1 ? matchedOptionIndex : -1; return matchedOptionIndex > -1 ? matchedOptionIndex : -1;
}, },
findNearestSelectedOptionIndex(index, firstCheckUp = false) { findNearestSelectedOptionIndex(index, firstCheckUp = false) {
let matchedOptionIndex = -1; let matchedOptionIndex = -1;
if (this.hasSelectedOption) { if (this.$filled) {
if (firstCheckUp) { if (firstCheckUp) {
matchedOptionIndex = this.findPrevSelectedOptionIndex(index); matchedOptionIndex = this.findPrevSelectedOptionIndex(index);
matchedOptionIndex = matchedOptionIndex === -1 ? this.findNextSelectedOptionIndex(index) : matchedOptionIndex; matchedOptionIndex = matchedOptionIndex === -1 ? this.findNextSelectedOptionIndex(index) : matchedOptionIndex;
@ -669,7 +669,7 @@ export default {
}, 500); }, 500);
}, },
removeOption(option) { removeOption(option) {
return this.modelValue.filter((val) => !equals(val, this.getOptionValue(option), this.equalityKey)); return this.d_value.filter((val) => !equals(val, this.getOptionValue(option), this.equalityKey));
}, },
changeFocusedOptionIndex(event, index) { changeFocusedOptionIndex(event, index) {
if (this.focusedOptionIndex !== index) { if (this.focusedOptionIndex !== index) {
@ -694,13 +694,13 @@ export default {
}); });
}, },
autoUpdateModel() { autoUpdateModel() {
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption && !this.multiple && this.focused) { if (this.selectOnFocus && this.autoOptionFocus && !this.$filled && !this.multiple && this.focused) {
this.focusedOptionIndex = this.findFirstFocusedOptionIndex(); this.focusedOptionIndex = this.findFirstFocusedOptionIndex();
this.onOptionSelect(null, this.visibleOptions[this.focusedOptionIndex]); this.onOptionSelect(null, this.visibleOptions[this.focusedOptionIndex]);
} }
}, },
updateModel(event, value) { updateModel(event, value) {
this.$emit('update:modelValue', value); this.updateValue(value, event);
this.$emit('change', { originalEvent: event, value }); this.$emit('change', { originalEvent: event, value });
}, },
flatOptions(options) { flatOptions(options) {
@ -728,8 +728,9 @@ export default {
return this.filterValue ? FilterService.filter(options, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale) : options; return this.filterValue ? FilterService.filter(options, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale) : options;
}, },
// @deprecated use $filled instead
hasSelectedOption() { hasSelectedOption() {
return isNotEmpty(this.modelValue); return isNotEmpty(this.d_value);
}, },
equalityKey() { equalityKey() {
return this.optionValue ? null : this.dataKey; return this.optionValue ? null : this.dataKey;
@ -756,7 +757,7 @@ export default {
return this.emptySelectionMessage || this.$primevue.config.locale.emptySelectionMessage || ''; return this.emptySelectionMessage || this.$primevue.config.locale.emptySelectionMessage || '';
}, },
selectedMessageText() { 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;
}, },
focusedOptionId() { focusedOptionId() {
return this.focusedOptionIndex !== -1 ? `${this.id}_${this.focusedOptionIndex}` : null; return this.focusedOptionIndex !== -1 ? `${this.id}_${this.focusedOptionIndex}` : null;

View File

@ -114,12 +114,12 @@ const theme = ({ dt }) => `
`; `;
const classes = { const classes = {
root: ({ props }) => [ root: ({ instance, props }) => [
'p-listbox p-component', 'p-listbox p-component',
{ {
'p-listbox-striped': props.striped, 'p-listbox-striped': props.striped,
'p-disabled': props.disabled, 'p-disabled': props.disabled,
'p-invalid': props.invalid 'p-invalid': instance.$invalid
} }
], ],
header: 'p-listbox-header', header: 'p-listbox-header',