Add form support to `Select`
parent
97e27ad77a
commit
a193c1e550
|
@ -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
|
||||||
|
|
|
@ -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' ? ' ' : label ?? 'empty' }}</slot>
|
<slot name="value" :value="d_value" :placeholder="placeholder">{{ label === 'p-emptylabel' ? ' ' : 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: {
|
||||||
|
|
|
@ -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 }) => [
|
||||||
|
|
Loading…
Reference in New Issue