Add form support to `Select`

pull/6632/head
Mert Sincan 2024-10-18 16:59:39 +01:00
parent 97e27ad77a
commit a193c1e550
3 changed files with 21 additions and 42 deletions

View File

@ -1,12 +1,11 @@
<script> <script>
import BaseComponent from '@primevue/core/basecomponent'; import BaseInput from '@primevue/core/baseinput';
import SelectStyle from 'primevue/select/style'; import SelectStyle from 'primevue/select/style';
export default { export default {
name: 'BaseSelect', name: 'BaseSelect',
extends: BaseComponent, extends: BaseInput,
props: { props: {
modelValue: null,
options: Array, options: Array,
optionLabel: [String, Function], optionLabel: [String, Function],
optionValue: [String, Function], optionValue: [String, Function],
@ -33,27 +32,11 @@ export default {
type: String, type: String,
default: null default: null
}, },
variant: {
type: String,
default: null
},
invalid: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
dataKey: null, dataKey: null,
showClear: { showClear: {
type: Boolean, type: Boolean,
default: false default: false
}, },
fluid: {
type: Boolean,
default: null
},
inputId: { inputId: {
type: String, type: String,
default: null default: null

View File

@ -46,7 +46,7 @@
@keydown="onKeyDown" @keydown="onKeyDown"
v-bind="ptm('label')" v-bind="ptm('label')"
> >
<slot name="value" :value="modelValue" :placeholder="placeholder">{{ label === 'p-emptylabel' ? '&nbsp;' : label ?? 'empty' }}</slot> <slot name="value" :value="d_value" :placeholder="placeholder">{{ label === 'p-emptylabel' ? '&nbsp;' : label ?? 'empty' }}</slot>
</span> </span>
<slot v-if="isClearIconVisible" name="clearicon" :class="cx('clearIcon')" :clearCallback="onClearClick"> <slot v-if="isClearIconVisible" name="clearicon" :class="cx('clearIcon')" :clearCallback="onClearClick">
<component :is="clearIcon ? 'i' : 'TimesIcon'" ref="clearIcon" :class="[cx('clearIcon'), clearIcon]" @click="onClearClick" v-bind="ptm('clearIcon')" data-pc-section="clearicon" /> <component :is="clearIcon ? 'i' : 'TimesIcon'" ref="clearIcon" :class="[cx('clearIcon'), clearIcon]" @click="onClearClick" v-bind="ptm('clearIcon')" data-pc-section="clearicon" />
@ -74,7 +74,7 @@
:data-p-hidden-accessible="true" :data-p-hidden-accessible="true"
:data-p-hidden-focusable="true" :data-p-hidden-focusable="true"
></span> ></span>
<slot name="header" :value="modelValue" :options="visibleOptions"></slot> <slot name="header" :value="d_value" :options="visibleOptions"></slot>
<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')">
<InputText <InputText
@ -165,7 +165,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>
@ -191,7 +191,7 @@
<script> <script>
import { absolutePosition, addStyle, findSingle, focus, getFirstFocusableElement, getFocusableElements, getLastFocusableElement, getOuterWidth, isAndroid, isTouchDevice, isVisible, relativePosition } from '@primeuix/utils/dom'; import { absolutePosition, addStyle, findSingle, focus, getFirstFocusableElement, getFocusableElements, getLastFocusableElement, getOuterWidth, isAndroid, isTouchDevice, isVisible, relativePosition } from '@primeuix/utils/dom';
import { equals, findLastIndex, isEmpty, isNotEmpty, isPrintableCharacter, resolveFieldData } from '@primeuix/utils/object'; import { equals, findLastIndex, isNotEmpty, isPrintableCharacter, resolveFieldData } from '@primeuix/utils/object';
import { ZIndex } from '@primeuix/utils/zindex'; import { ZIndex } from '@primeuix/utils/zindex';
import { FilterService } from '@primevue/core/api'; import { FilterService } from '@primevue/core/api';
import { ConnectedOverlayScrollHandler, UniqueComponentId } from '@primevue/core/utils'; import { ConnectedOverlayScrollHandler, UniqueComponentId } from '@primevue/core/utils';
@ -214,10 +214,7 @@ export default {
name: 'Select', name: 'Select',
extends: BaseSelect, extends: BaseSelect,
inheritAttrs: false, inheritAttrs: false,
emits: ['update:modelValue', 'change', 'focus', 'blur', 'before-show', 'before-hide', 'show', 'hide', 'filter'], emits: ['change', 'focus', 'blur', 'before-show', 'before-hide', 'show', 'hide', 'filter'],
inject: {
$pcFluid: { default: null }
},
outsideClickListener: null, outsideClickListener: null,
scrollHandler: null, scrollHandler: null,
resizeListener: null, resizeListener: null,
@ -358,6 +355,7 @@ export default {
this.focusedOptionIndex = -1; this.focusedOptionIndex = -1;
this.searchValue = ''; this.searchValue = '';
this.$emit('blur', event); this.$emit('blur', event);
this.formField.onBlur?.();
}, },
onKeyDown(event) { onKeyDown(event) {
if (this.disabled || isAndroid()) { if (this.disabled || isAndroid()) {
@ -810,7 +808,7 @@ export default {
return this.isValidOption(option) && this.isSelected(option); return this.isValidOption(option) && this.isSelected(option);
}, },
isSelected(option) { isSelected(option) {
return equals(this.modelValue, this.getOptionValue(option), this.equalityKey); return equals(this.d_value, this.getOptionValue(option), this.equalityKey);
}, },
findFirstOptionIndex() { findFirstOptionIndex() {
return this.visibleOptions.findIndex((option) => this.isValidOption(option)); return this.visibleOptions.findIndex((option) => this.isValidOption(option));
@ -829,7 +827,7 @@ export default {
return matchedOptionIndex > -1 ? matchedOptionIndex : index; return matchedOptionIndex > -1 ? matchedOptionIndex : index;
}, },
findSelectedOptionIndex() { findSelectedOptionIndex() {
return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1; return this.$filled ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
}, },
findFirstFocusedOptionIndex() { findFirstFocusedOptionIndex() {
const selectedIndex = this.findSelectedOptionIndex(); const selectedIndex = this.findSelectedOptionIndex();
@ -902,13 +900,13 @@ export default {
}); });
}, },
autoUpdateModel() { autoUpdateModel() {
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) { if (this.selectOnFocus && this.autoOptionFocus && !this.$filled) {
this.focusedOptionIndex = this.findFirstFocusedOptionIndex(); this.focusedOptionIndex = this.findFirstFocusedOptionIndex();
this.onOptionSelect(null, this.visibleOptions[this.focusedOptionIndex], false); this.onOptionSelect(null, this.visibleOptions[this.focusedOptionIndex], false);
} }
}, },
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) {
@ -959,8 +957,9 @@ export default {
return options; return options;
}, },
// @deprecated use $filled instead
hasSelectedOption() { hasSelectedOption() {
return isNotEmpty(this.modelValue); return this.$filled;
}, },
label() { label() {
const selectedOptionIndex = this.findSelectedOptionIndex(); const selectedOptionIndex = this.findSelectedOptionIndex();
@ -970,7 +969,7 @@ export default {
editableInputValue() { editableInputValue() {
const selectedOptionIndex = this.findSelectedOptionIndex(); const selectedOptionIndex = this.findSelectedOptionIndex();
return selectedOptionIndex !== -1 ? this.getOptionLabel(this.visibleOptions[selectedOptionIndex]) : this.modelValue || ''; return selectedOptionIndex !== -1 ? this.getOptionLabel(this.visibleOptions[selectedOptionIndex]) : this.d_value || '';
}, },
equalityKey() { equalityKey() {
return this.optionValue ? null : this.dataKey; return this.optionValue ? null : this.dataKey;
@ -997,7 +996,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}', '1') : this.emptySelectionMessageText; return this.$filled ? this.selectionMessageText.replaceAll('{0}', '1') : this.emptySelectionMessageText;
}, },
focusedOptionId() { focusedOptionId() {
return this.focusedOptionIndex !== -1 ? `${this.id}_${this.focusedOptionIndex}` : null; return this.focusedOptionIndex !== -1 ? `${this.id}_${this.focusedOptionIndex}` : null;
@ -1006,13 +1005,10 @@ export default {
return this.visibleOptions.filter((option) => !this.isOptionGroup(option)).length; return this.visibleOptions.filter((option) => !this.isOptionGroup(option)).length;
}, },
isClearIconVisible() { isClearIconVisible() {
return this.showClear && this.modelValue != null && isNotEmpty(this.options); return this.showClear && this.d_value != null && isNotEmpty(this.options);
}, },
virtualScrollerDisabled() { virtualScrollerDisabled() {
return !this.virtualScrollerOptions; return !this.virtualScrollerOptions;
},
hasFluid() {
return isEmpty(this.fluid) ? !!this.$pcFluid : this.fluid;
} }
}, },
directives: { directives: {

View File

@ -202,13 +202,13 @@ const classes = {
'p-select p-component p-inputwrapper', 'p-select p-component p-inputwrapper',
{ {
'p-disabled': props.disabled, 'p-disabled': props.disabled,
'p-invalid': props.invalid, 'p-invalid': instance.$invalid,
'p-variant-filled': props.variant ? props.variant === 'filled' : instance.$primevue.config.inputStyle === 'filled' || instance.$primevue.config.inputVariant === 'filled', 'p-variant-filled': instance.$variant === 'filled',
'p-focus': state.focused, 'p-focus': state.focused,
'p-inputwrapper-filled': instance.hasSelectedOption, 'p-inputwrapper-filled': instance.$filled,
'p-inputwrapper-focus': state.focused || state.overlayVisible, 'p-inputwrapper-focus': state.focused || state.overlayVisible,
'p-select-open': state.overlayVisible, 'p-select-open': state.overlayVisible,
'p-select-fluid': instance.hasFluid 'p-select-fluid': instance.$fluid
} }
], ],
label: ({ instance, props }) => [ label: ({ instance, props }) => [