246 lines
5.9 KiB
JavaScript
246 lines
5.9 KiB
JavaScript
|
// Mixins
|
||
|
import Colorable from '../colorable';
|
||
|
import Themeable from '../themeable';
|
||
|
import { inject as RegistrableInject } from '../registrable'; // Utilities
|
||
|
|
||
|
import { deepEqual } from '../../util/helpers';
|
||
|
import { consoleError } from '../../util/console';
|
||
|
import mixins from '../../util/mixins';
|
||
|
/* @vue/component */
|
||
|
|
||
|
export default mixins(Colorable, RegistrableInject('form'), Themeable).extend({
|
||
|
name: 'validatable',
|
||
|
props: {
|
||
|
disabled: Boolean,
|
||
|
error: Boolean,
|
||
|
errorCount: {
|
||
|
type: [Number, String],
|
||
|
default: 1
|
||
|
},
|
||
|
errorMessages: {
|
||
|
type: [String, Array],
|
||
|
default: () => []
|
||
|
},
|
||
|
messages: {
|
||
|
type: [String, Array],
|
||
|
default: () => []
|
||
|
},
|
||
|
readonly: Boolean,
|
||
|
rules: {
|
||
|
type: Array,
|
||
|
default: () => []
|
||
|
},
|
||
|
success: Boolean,
|
||
|
successMessages: {
|
||
|
type: [String, Array],
|
||
|
default: () => []
|
||
|
},
|
||
|
validateOnBlur: Boolean,
|
||
|
value: {
|
||
|
required: false
|
||
|
}
|
||
|
},
|
||
|
|
||
|
data() {
|
||
|
return {
|
||
|
errorBucket: [],
|
||
|
hasColor: false,
|
||
|
hasFocused: false,
|
||
|
hasInput: false,
|
||
|
isFocused: false,
|
||
|
isResetting: false,
|
||
|
lazyValue: this.value,
|
||
|
valid: false
|
||
|
};
|
||
|
},
|
||
|
|
||
|
computed: {
|
||
|
computedColor() {
|
||
|
if (this.disabled) return undefined;
|
||
|
if (this.color) return this.color; // It's assumed that if the input is on a
|
||
|
// dark background, the user will want to
|
||
|
// have a white color. If the entire app
|
||
|
// is setup to be dark, then they will
|
||
|
// like want to use their primary color
|
||
|
|
||
|
if (this.isDark && !this.appIsDark) return 'white';else return 'primary';
|
||
|
},
|
||
|
|
||
|
hasError() {
|
||
|
return this.internalErrorMessages.length > 0 || this.errorBucket.length > 0 || this.error;
|
||
|
},
|
||
|
|
||
|
// TODO: Add logic that allows the user to enable based
|
||
|
// upon a good validation
|
||
|
hasSuccess() {
|
||
|
return this.internalSuccessMessages.length > 0 || this.success;
|
||
|
},
|
||
|
|
||
|
externalError() {
|
||
|
return this.internalErrorMessages.length > 0 || this.error;
|
||
|
},
|
||
|
|
||
|
hasMessages() {
|
||
|
return this.validationTarget.length > 0;
|
||
|
},
|
||
|
|
||
|
hasState() {
|
||
|
if (this.disabled) return false;
|
||
|
return this.hasSuccess || this.shouldValidate && this.hasError;
|
||
|
},
|
||
|
|
||
|
internalErrorMessages() {
|
||
|
return this.genInternalMessages(this.errorMessages);
|
||
|
},
|
||
|
|
||
|
internalMessages() {
|
||
|
return this.genInternalMessages(this.messages);
|
||
|
},
|
||
|
|
||
|
internalSuccessMessages() {
|
||
|
return this.genInternalMessages(this.successMessages);
|
||
|
},
|
||
|
|
||
|
internalValue: {
|
||
|
get() {
|
||
|
return this.lazyValue;
|
||
|
},
|
||
|
|
||
|
set(val) {
|
||
|
this.lazyValue = val;
|
||
|
this.$emit('input', val);
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
shouldValidate() {
|
||
|
if (this.externalError) return true;
|
||
|
if (this.isResetting) return false;
|
||
|
return this.validateOnBlur ? this.hasFocused && !this.isFocused : this.hasInput || this.hasFocused;
|
||
|
},
|
||
|
|
||
|
validations() {
|
||
|
return this.validationTarget.slice(0, Number(this.errorCount));
|
||
|
},
|
||
|
|
||
|
validationState() {
|
||
|
if (this.disabled) return undefined;
|
||
|
if (this.hasError && this.shouldValidate) return 'error';
|
||
|
if (this.hasSuccess) return 'success';
|
||
|
if (this.hasColor) return this.computedColor;
|
||
|
return undefined;
|
||
|
},
|
||
|
|
||
|
validationTarget() {
|
||
|
if (this.internalErrorMessages.length > 0) {
|
||
|
return this.internalErrorMessages;
|
||
|
} else if (this.successMessages.length > 0) {
|
||
|
return this.internalSuccessMessages;
|
||
|
} else if (this.messages.length > 0) {
|
||
|
return this.internalMessages;
|
||
|
} else if (this.shouldValidate) {
|
||
|
return this.errorBucket;
|
||
|
} else return [];
|
||
|
}
|
||
|
|
||
|
},
|
||
|
watch: {
|
||
|
rules: {
|
||
|
handler(newVal, oldVal) {
|
||
|
if (deepEqual(newVal, oldVal)) return;
|
||
|
this.validate();
|
||
|
},
|
||
|
|
||
|
deep: true
|
||
|
},
|
||
|
|
||
|
internalValue() {
|
||
|
// If it's the first time we're setting input,
|
||
|
// mark it with hasInput
|
||
|
this.hasInput = true;
|
||
|
this.validateOnBlur || this.$nextTick(this.validate);
|
||
|
},
|
||
|
|
||
|
isFocused(val) {
|
||
|
// Should not check validation
|
||
|
// if disabled or readonly
|
||
|
if (!val && !this.disabled && !this.readonly) {
|
||
|
this.hasFocused = true;
|
||
|
this.validateOnBlur && this.validate();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
isResetting() {
|
||
|
setTimeout(() => {
|
||
|
this.hasInput = false;
|
||
|
this.hasFocused = false;
|
||
|
this.isResetting = false;
|
||
|
this.validate();
|
||
|
}, 0);
|
||
|
},
|
||
|
|
||
|
hasError(val) {
|
||
|
if (this.shouldValidate) {
|
||
|
this.$emit('update:error', val);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
value(val) {
|
||
|
this.lazyValue = val;
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
beforeMount() {
|
||
|
this.validate();
|
||
|
},
|
||
|
|
||
|
created() {
|
||
|
this.form && this.form.register(this);
|
||
|
},
|
||
|
|
||
|
beforeDestroy() {
|
||
|
this.form && this.form.unregister(this);
|
||
|
},
|
||
|
|
||
|
methods: {
|
||
|
genInternalMessages(messages) {
|
||
|
if (!messages) return [];else if (Array.isArray(messages)) return messages;else return [messages];
|
||
|
},
|
||
|
|
||
|
/** @public */
|
||
|
reset() {
|
||
|
this.isResetting = true;
|
||
|
this.internalValue = Array.isArray(this.internalValue) ? [] : undefined;
|
||
|
},
|
||
|
|
||
|
/** @public */
|
||
|
resetValidation() {
|
||
|
this.isResetting = true;
|
||
|
},
|
||
|
|
||
|
/** @public */
|
||
|
validate(force = false, value) {
|
||
|
const errorBucket = [];
|
||
|
value = value || this.internalValue;
|
||
|
if (force) this.hasInput = this.hasFocused = true;
|
||
|
|
||
|
for (let index = 0; index < this.rules.length; index++) {
|
||
|
const rule = this.rules[index];
|
||
|
const valid = typeof rule === 'function' ? rule(value) : rule;
|
||
|
|
||
|
if (typeof valid === 'string') {
|
||
|
errorBucket.push(valid);
|
||
|
} else if (typeof valid !== 'boolean') {
|
||
|
consoleError(`Rules should return a string or boolean, received '${typeof valid}' instead`, this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.errorBucket = errorBucket;
|
||
|
this.valid = errorBucket.length === 0;
|
||
|
return this.valid;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
});
|
||
|
//# sourceMappingURL=index.js.map
|