import { extend } from '../../vue' import { NAME_FORM_SELECT } from '../../constants/components' import { EVENT_NAME_CHANGE } from '../../constants/events' import { PROP_TYPE_BOOLEAN, PROP_TYPE_BOOLEAN_STRING, PROP_TYPE_NUMBER } from '../../constants/props' import { SLOT_NAME_FIRST } from '../../constants/slots' import { from as arrayFrom } from '../../utils/array' import { attemptBlur, attemptFocus } from '../../utils/dom' import { htmlOrText } from '../../utils/html' import { isArray } from '../../utils/inspect' import { sortKeys } from '../../utils/object' import { makeProp, makePropsConfigurable } from '../../utils/props' import { formControlMixin, props as formControlProps } from '../../mixins/form-control' import { formCustomMixin, props as formCustomProps } from '../../mixins/form-custom' import { formSizeMixin, props as formSizeProps } from '../../mixins/form-size' import { formStateMixin, props as formStateProps } from '../../mixins/form-state' import { idMixin, props as idProps } from '../../mixins/id' import { MODEL_EVENT_NAME, MODEL_PROP_NAME, modelMixin, props as modelProps } from '../../mixins/model' import { normalizeSlotMixin } from '../../mixins/normalize-slot' import { optionsMixin } from './helpers/mixin-options' import { BFormSelectOption } from './form-select-option' import { BFormSelectOptionGroup } from './form-select-option-group' // --- Props --- export const props = makePropsConfigurable( sortKeys({ ...idProps, ...modelProps, ...formControlProps, ...formCustomProps, ...formSizeProps, ...formStateProps, ariaInvalid: makeProp(PROP_TYPE_BOOLEAN_STRING, false), multiple: makeProp(PROP_TYPE_BOOLEAN, false), // Browsers default size to `0`, which shows 4 rows in most browsers in multiple mode // Size of `1` can bork out Firefox selectSize: makeProp(PROP_TYPE_NUMBER, 0) }), NAME_FORM_SELECT ) // --- Main component --- // @vue/component export const BFormSelect = /*#__PURE__*/ extend({ name: NAME_FORM_SELECT, mixins: [ idMixin, modelMixin, formControlMixin, formSizeMixin, formStateMixin, formCustomMixin, optionsMixin, normalizeSlotMixin ], props, data() { return { localValue: this[MODEL_PROP_NAME] } }, computed: { computedSelectSize() { // Custom selects with a size of zero causes the arrows to be hidden, // so dont render the size attribute in this case return !this.plain && this.selectSize === 0 ? null : this.selectSize }, inputClass() { return [ this.plain ? 'form-control' : 'custom-select', this.size && this.plain ? `form-control-${this.size}` : null, this.size && !this.plain ? `custom-select-${this.size}` : null, this.stateClass ] } }, watch: { value(newValue) { this.localValue = newValue }, localValue() { this.$emit(MODEL_EVENT_NAME, this.localValue) } }, methods: { focus() { attemptFocus(this.$refs.input) }, blur() { attemptBlur(this.$refs.input) }, onChange(event) { const { target } = event const selectedValue = arrayFrom(target.options) .filter(o => o.selected) .map(o => ('_value' in o ? o._value : o.value)) this.localValue = target.multiple ? selectedValue : selectedValue[0] this.$nextTick(() => { this.$emit(EVENT_NAME_CHANGE, this.localValue) }) } }, render(h) { const { name, disabled, required, computedSelectSize: size, localValue: value } = this const $options = this.formOptions.map((option, index) => { const { value, label, options, disabled } = option const key = `option_${index}` return isArray(options) ? h(BFormSelectOptionGroup, { props: { label, options }, key }) : h(BFormSelectOption, { props: { value, disabled }, domProps: htmlOrText(option.html, option.text), key }) }) return h( 'select', { class: this.inputClass, attrs: { id: this.safeId(), name, form: this.form || null, multiple: this.multiple || null, size, disabled, required, 'aria-required': required ? 'true' : null, 'aria-invalid': this.computedAriaInvalid }, on: { change: this.onChange }, directives: [{ name: 'model', value }], ref: 'input' }, [this.normalizeSlot(SLOT_NAME_FIRST), $options, this.normalizeSlot()] ) } })