UNPKG

buefy

Version:

Lightweight UI components for Vue.js (v3) based on Bulma

207 lines (185 loc) 6.09 kB
import { defineComponent } from 'vue' import config from '../utils/config' import BField from '../components/field/Field.vue' import type { FieldMessageProp, FieldTypeProp } from '../components/field/Field.vue' type BFieldComponent = InstanceType<typeof BField> // HTMLElement that supports contraint validation; // i.e., having `validationMessage` // https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#the_constraint_validation_api type ConstraintValidationElement = | HTMLButtonElement | HTMLFieldSetElement | HTMLInputElement | HTMLOutputElement | HTMLSelectElement | HTMLTextAreaElement const FormElementMixin = defineComponent({ props: { size: String, expanded: Boolean, loading: Boolean, rounded: Boolean, icon: String, iconPack: String, maxlength: [Number, String], useHtml5Validation: { type: Boolean, default: () => config.defaultUseHtml5Validation }, validationMessage: String, locale: { type: [String, Array<string>], default: () => { return config.defaultLocale } }, statusIcon: { type: Boolean, default: () => { return config.defaultStatusIcon } } }, emits: { // eslint-disable-next-line @typescript-eslint/no-unused-vars blur: (event?: Event) => true, // eslint-disable-next-line @typescript-eslint/no-unused-vars focus: (event?: Event) => true }, data() { return { isValid: true, isFocused: false, newIconPack: this.iconPack || config.defaultIconPack, // host component must override this _elementRef: '' } }, computed: { /* * Find parent Field, max 3 levels deep. */ parentField(): BFieldComponent | undefined { let parent = this.$parent for (let i = 0; i < 3; i++) { if (parent && !(parent as BFieldComponent).$data._isField) { parent = parent.$parent } } // TODO: what if parent is defined but not a Field? return parent as BFieldComponent | undefined }, /* * Get the type prop from parent if it's a Field. */ statusType() { const { newType } = this.parentField || {} if (!newType) return if (typeof newType === 'string') { return newType } else { for (const key in newType) { if (newType[key]) { return key } } } return undefined }, /* * Get the message prop from parent if it's a Field. */ statusMessage() { if (!this.parentField) return return this.parentField.newMessage || this.parentField.$slots.message }, /* * Fix icon size for inputs, large was too big */ iconSize() { switch (this.size) { case 'is-small': return this.size case 'is-medium': return case 'is-large': return this.newIconPack === 'mdi' ? 'is-medium' : '' } return undefined } }, methods: { /* * Focus method that work dynamically depending on the component. */ focus() { const el = this.getElement() if (el === undefined) return this.$nextTick(() => { if (el) el.focus() }) }, onBlur($event?: Event) { this.isFocused = false this.$emit('blur', $event) this.checkHtml5Validity() }, onFocus($event?: Event) { this.isFocused = true this.$emit('focus', $event) }, getElement(): ConstraintValidationElement { let el = this.$refs[this.$data._elementRef] while (el != null && typeof el === 'object' && '$refs' in el) { const form = el as unknown as typeof FormElementMixin el = form.$refs[form.$data._elementRef] } // TODO: what if el is not an HTMLElement? possibly null? return el as ConstraintValidationElement }, setInvalid() { const type = 'is-danger' const message = this.validationMessage || this.getElement().validationMessage this.setValidity(type, message) }, setValidity(type: FieldTypeProp | null, message: FieldMessageProp | null) { this.$nextTick(() => { if (this.parentField) { // Set type only if not defined if (!this.parentField.type) { this.parentField.newType = type } // Set message only if not defined if (!this.parentField.message) { this.parentField.newMessage = message } } }) }, /* * Check HTML5 validation, set isValid property. * If validation fail, send 'is-danger' type, * and error message to parent if it's a Field. */ checkHtml5Validity(): boolean { if (!this.useHtml5Validation) { return false } const el = this.getElement() if (el == null) { return false } if (!el.checkValidity()) { this.setInvalid() this.isValid = false } else { this.setValidity(null, null) this.isValid = true } return this.isValid } } }) export default FormElementMixin