Adds initial validation rules and support
This commit is contained in:
parent
a53edaa342
commit
e952e46aad
@ -9,10 +9,11 @@ export default {
|
||||
exports: 'named',
|
||||
globals: {
|
||||
'is-plain-object': 'isPlainObject',
|
||||
'nanoid': 'nanoid'
|
||||
'nanoid': 'nanoid',
|
||||
'is-url': 'isUrl'
|
||||
}
|
||||
},
|
||||
external: ['is-plain-object', 'nanoid'],
|
||||
external: ['is-plain-object', 'nanoid', 'is-url'],
|
||||
plugins: [
|
||||
commonjs(),
|
||||
vue({
|
||||
|
340
dist/formulate.esm.js
vendored
340
dist/formulate.esm.js
vendored
@ -1,3 +1,4 @@
|
||||
import isUrl from 'is-url';
|
||||
import isPlainObject from 'is-plain-object';
|
||||
import nanoid from 'nanoid';
|
||||
|
||||
@ -160,6 +161,291 @@ function arrayify (item) {
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array or string return an array of callables.
|
||||
* @param {array|string} validation
|
||||
* @param {array} rules and array of functions
|
||||
* @return {array} an array of functions
|
||||
*/
|
||||
function parseRules (validation, rules) {
|
||||
if (typeof validation === 'string') {
|
||||
return parseRules(validation.split('|'), rules)
|
||||
}
|
||||
if (!Array.isArray(validation)) {
|
||||
return []
|
||||
}
|
||||
return validation.map(function (rule) { return parseRule(rule, rules); }).filter(function (f) { return !!f; })
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string or function, parse it and return the an array in the format
|
||||
* [fn, [...arguments]]
|
||||
* @param {string|function} rule
|
||||
*/
|
||||
function parseRule (rule, rules) {
|
||||
if (typeof rule === 'function') {
|
||||
return [rule, []]
|
||||
}
|
||||
if (Array.isArray(rule) && rule.length) {
|
||||
if (typeof rule[0] === 'string' && rules.hasOwnProperty(rule[0])) {
|
||||
return [rules[rule.shift()], rule]
|
||||
}
|
||||
if (typeof rule[0] === 'function') {
|
||||
return [rule.shift(), rule]
|
||||
}
|
||||
}
|
||||
if (typeof rule === 'string') {
|
||||
var segments = rule.split(':');
|
||||
var functionName = segments.shift();
|
||||
if (rules.hasOwnProperty(functionName)) {
|
||||
return [rules[functionName], segments.length ? segments.join(':').split(',') : []]
|
||||
} else {
|
||||
throw new Error(("Unknown validation rule " + rule))
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Library of rules
|
||||
*/
|
||||
var rules = {
|
||||
/**
|
||||
* Rule: the value must be "yes", "on", "1", or true
|
||||
*/
|
||||
accepted: function (value) {
|
||||
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: must be a value
|
||||
*/
|
||||
required: function (value, isRequired) {
|
||||
if ( isRequired === void 0 ) isRequired = true;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (!isRequired || ['no', 'false'].includes(isRequired)) {
|
||||
return true
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return !!value.length
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return !!value
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return (!value) ? false : !!Object.keys(value).length
|
||||
}
|
||||
return true
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Value is in an array (stack).
|
||||
*/
|
||||
in: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(stack.find(function (item) {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
}
|
||||
return item === value
|
||||
}) !== undefined)
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Value is not in stack.
|
||||
*/
|
||||
not: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(stack.find(function (item) {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
}
|
||||
return item === value
|
||||
}) === undefined)
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Match the value against a (stack) of patterns or strings
|
||||
*/
|
||||
matches: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(!!stack.find(function (pattern) {
|
||||
if (pattern instanceof RegExp) {
|
||||
return pattern.test(value)
|
||||
}
|
||||
return pattern === value
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a string is a valid url
|
||||
*/
|
||||
url: function (value) {
|
||||
return Promise.resolve(isUrl(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: ensures the value is a date according to Date.parse()
|
||||
*/
|
||||
date: function (value) {
|
||||
return Promise.resolve(!isNaN(Date.parse(value)))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
after: function (value, compare) {
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
var fieldValue = Date.parse(value);
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue > timestamp))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
before: function (value, compare) {
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
var fieldValue = Date.parse(value);
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue < timestamp))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
alpha: function (value, set) {
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
default: /^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-z][A-Z]$/
|
||||
};
|
||||
var selectedSet = sets.hasOwnProperty(set) ? set : 'default';
|
||||
return Promise.resolve(sets[selectedSet].test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
number: function (value) {
|
||||
return Promise.resolve(!isNaN(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is alpha numeric
|
||||
*/
|
||||
alphanumeric: function (value, set) {
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
default: /^[a-zA-Z0-9À-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-zA-Z0-9]$/
|
||||
};
|
||||
var selectedSet = sets.hasOwnProperty(set) ? set : 'default';
|
||||
return Promise.resolve(sets[selectedSet].test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is between two other values
|
||||
*/
|
||||
between: function (value, from, to) {
|
||||
return Promise.resolve((function () {
|
||||
if (from === null || to === null || isNaN(from) || isNaN(to)) {
|
||||
return false
|
||||
}
|
||||
from = Number(from);
|
||||
to = Number(to);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return (value > from && value < to)
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length > from && value.length < to
|
||||
}
|
||||
return false
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: tests
|
||||
*/
|
||||
email: function (value) {
|
||||
// eslint-disable-next-line
|
||||
var isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
|
||||
return Promise.resolve(isEmail.test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the file type is correct.
|
||||
*/
|
||||
mime: function (files) {
|
||||
var types = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
|
||||
for (var i in files) {
|
||||
if (!types.includes(files[i].type)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
min: function (value, minimum) {
|
||||
return Promise.resolve((function () {
|
||||
minimum = Number(minimum);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return value >= minimum
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length >= minimum
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length >= minimum
|
||||
}
|
||||
return false
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
max: function (value, minimum) {
|
||||
return Promise.resolve((function () {
|
||||
minimum = Number(minimum);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return value <= minimum
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length <= minimum
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length <= minimum
|
||||
}
|
||||
return false
|
||||
})())
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* For a single instance of an input, export all of the context needed to fully
|
||||
* render that element.
|
||||
@ -186,7 +472,8 @@ var context = {
|
||||
elementAttributes: elementAttributes,
|
||||
logicalLabelPosition: logicalLabelPosition,
|
||||
isVmodeled: isVmodeled,
|
||||
mergedErrors: mergedErrors
|
||||
mergedErrors: mergedErrors,
|
||||
hasErrors: hasErrors
|
||||
};
|
||||
|
||||
/**
|
||||
@ -299,6 +586,13 @@ function mergedErrors () {
|
||||
.reduce(function (errors, err) { return !errors.includes(err) ? errors.concat(err) : errors; }, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this computed property have errors.
|
||||
*/
|
||||
function hasErrors () {
|
||||
return !!this.mergedErrors.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the model used throughout the existing context.
|
||||
* @param {object} context
|
||||
@ -397,6 +691,17 @@ var script = {
|
||||
type: [String, Array, Boolean],
|
||||
default: false
|
||||
},
|
||||
validation: {
|
||||
type: [String, Boolean, Array],
|
||||
default: false
|
||||
},
|
||||
validationBehavior: {
|
||||
type: String,
|
||||
default: 'blur',
|
||||
validator: function (value) {
|
||||
return ['blur', 'live'].includes(value)
|
||||
}
|
||||
},
|
||||
error: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
@ -406,7 +711,8 @@ var script = {
|
||||
return {
|
||||
defaultId: nanoid(9),
|
||||
localAttributes: {},
|
||||
internalModelProxy: this.formulateValue
|
||||
internalModelProxy: this.formulateValue,
|
||||
validationErrors: []
|
||||
}
|
||||
},
|
||||
computed: Object.assign({}, context,
|
||||
@ -425,6 +731,7 @@ var script = {
|
||||
deep: true
|
||||
},
|
||||
internalModelProxy: function internalModelProxy (newValue, oldValue) {
|
||||
this.performValidation();
|
||||
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
|
||||
this.context.model = newValue;
|
||||
}
|
||||
@ -440,12 +747,29 @@ var script = {
|
||||
this.formulateFormRegister(this.nameOrFallback, this);
|
||||
}
|
||||
this.updateLocalAttributes(this.$attrs);
|
||||
this.performValidation();
|
||||
},
|
||||
methods: {
|
||||
updateLocalAttributes: function updateLocalAttributes (value) {
|
||||
if (!shallowEqualObjects(value, this.localAttributes)) {
|
||||
this.localAttributes = value;
|
||||
}
|
||||
},
|
||||
performValidation: function performValidation () {
|
||||
var this$1 = this;
|
||||
|
||||
var rules = parseRules(this.validation, this.$formulate.rules());
|
||||
Promise.all(
|
||||
rules.map(function (ref) {
|
||||
var rule = ref[0];
|
||||
var args = ref[1];
|
||||
|
||||
return rule.apply(void 0, [ this$1.context.model ].concat( args ))
|
||||
.then(function (res) { return res ? false : 'Validation error!'; })
|
||||
})
|
||||
)
|
||||
.then(function (result) { return result.filter(function (result) { return result; }); })
|
||||
.then(function (errorMessages) { this$1.validationErrors = errorMessages; });
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -539,6 +863,7 @@ var __vue_render__ = function() {
|
||||
staticClass: "formulate-input",
|
||||
attrs: {
|
||||
"data-classification": _vm.classification,
|
||||
"data-has-errors": _vm.hasErrors,
|
||||
"data-type": _vm.type
|
||||
}
|
||||
},
|
||||
@ -1601,7 +1926,8 @@ var Formulate = function Formulate () {
|
||||
FormulateInputSelect: FormulateInputSelect,
|
||||
FormulateInputTextArea: FormulateInputTextArea
|
||||
},
|
||||
library: library
|
||||
library: library,
|
||||
rules: rules
|
||||
};
|
||||
};
|
||||
|
||||
@ -1663,6 +1989,14 @@ Formulate.prototype.component = function component (type) {
|
||||
return false
|
||||
};
|
||||
|
||||
/**
|
||||
* Get validation rules.
|
||||
* @return {object} object of validation functions
|
||||
*/
|
||||
Formulate.prototype.rules = function rules () {
|
||||
return this.options.rules
|
||||
};
|
||||
|
||||
var Formulate$1 = new Formulate();
|
||||
|
||||
export default Formulate$1;
|
||||
|
344
dist/formulate.min.js
vendored
344
dist/formulate.min.js
vendored
@ -1,6 +1,7 @@
|
||||
var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
var Formulate = (function (exports, isUrl, isPlainObject, nanoid) {
|
||||
'use strict';
|
||||
|
||||
isUrl = isUrl && isUrl.hasOwnProperty('default') ? isUrl['default'] : isUrl;
|
||||
isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject;
|
||||
nanoid = nanoid && nanoid.hasOwnProperty('default') ? nanoid['default'] : nanoid;
|
||||
|
||||
@ -163,6 +164,291 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array or string return an array of callables.
|
||||
* @param {array|string} validation
|
||||
* @param {array} rules and array of functions
|
||||
* @return {array} an array of functions
|
||||
*/
|
||||
function parseRules (validation, rules) {
|
||||
if (typeof validation === 'string') {
|
||||
return parseRules(validation.split('|'), rules)
|
||||
}
|
||||
if (!Array.isArray(validation)) {
|
||||
return []
|
||||
}
|
||||
return validation.map(function (rule) { return parseRule(rule, rules); }).filter(function (f) { return !!f; })
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string or function, parse it and return the an array in the format
|
||||
* [fn, [...arguments]]
|
||||
* @param {string|function} rule
|
||||
*/
|
||||
function parseRule (rule, rules) {
|
||||
if (typeof rule === 'function') {
|
||||
return [rule, []]
|
||||
}
|
||||
if (Array.isArray(rule) && rule.length) {
|
||||
if (typeof rule[0] === 'string' && rules.hasOwnProperty(rule[0])) {
|
||||
return [rules[rule.shift()], rule]
|
||||
}
|
||||
if (typeof rule[0] === 'function') {
|
||||
return [rule.shift(), rule]
|
||||
}
|
||||
}
|
||||
if (typeof rule === 'string') {
|
||||
var segments = rule.split(':');
|
||||
var functionName = segments.shift();
|
||||
if (rules.hasOwnProperty(functionName)) {
|
||||
return [rules[functionName], segments.length ? segments.join(':').split(',') : []]
|
||||
} else {
|
||||
throw new Error(("Unknown validation rule " + rule))
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Library of rules
|
||||
*/
|
||||
var rules = {
|
||||
/**
|
||||
* Rule: the value must be "yes", "on", "1", or true
|
||||
*/
|
||||
accepted: function (value) {
|
||||
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: must be a value
|
||||
*/
|
||||
required: function (value, isRequired) {
|
||||
if ( isRequired === void 0 ) isRequired = true;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (!isRequired || ['no', 'false'].includes(isRequired)) {
|
||||
return true
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return !!value.length
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return !!value
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return (!value) ? false : !!Object.keys(value).length
|
||||
}
|
||||
return true
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Value is in an array (stack).
|
||||
*/
|
||||
in: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(stack.find(function (item) {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
}
|
||||
return item === value
|
||||
}) !== undefined)
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Value is not in stack.
|
||||
*/
|
||||
not: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(stack.find(function (item) {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
}
|
||||
return item === value
|
||||
}) === undefined)
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Match the value against a (stack) of patterns or strings
|
||||
*/
|
||||
matches: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(!!stack.find(function (pattern) {
|
||||
if (pattern instanceof RegExp) {
|
||||
return pattern.test(value)
|
||||
}
|
||||
return pattern === value
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a string is a valid url
|
||||
*/
|
||||
url: function (value) {
|
||||
return Promise.resolve(isUrl(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: ensures the value is a date according to Date.parse()
|
||||
*/
|
||||
date: function (value) {
|
||||
return Promise.resolve(!isNaN(Date.parse(value)))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
after: function (value, compare) {
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
var fieldValue = Date.parse(value);
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue > timestamp))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
before: function (value, compare) {
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
var fieldValue = Date.parse(value);
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue < timestamp))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
alpha: function (value, set) {
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
default: /^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-z][A-Z]$/
|
||||
};
|
||||
var selectedSet = sets.hasOwnProperty(set) ? set : 'default';
|
||||
return Promise.resolve(sets[selectedSet].test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
number: function (value) {
|
||||
return Promise.resolve(!isNaN(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is alpha numeric
|
||||
*/
|
||||
alphanumeric: function (value, set) {
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
default: /^[a-zA-Z0-9À-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-zA-Z0-9]$/
|
||||
};
|
||||
var selectedSet = sets.hasOwnProperty(set) ? set : 'default';
|
||||
return Promise.resolve(sets[selectedSet].test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is between two other values
|
||||
*/
|
||||
between: function (value, from, to) {
|
||||
return Promise.resolve((function () {
|
||||
if (from === null || to === null || isNaN(from) || isNaN(to)) {
|
||||
return false
|
||||
}
|
||||
from = Number(from);
|
||||
to = Number(to);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return (value > from && value < to)
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length > from && value.length < to
|
||||
}
|
||||
return false
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: tests
|
||||
*/
|
||||
email: function (value) {
|
||||
// eslint-disable-next-line
|
||||
var isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
|
||||
return Promise.resolve(isEmail.test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the file type is correct.
|
||||
*/
|
||||
mime: function (files) {
|
||||
var types = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
|
||||
for (var i in files) {
|
||||
if (!types.includes(files[i].type)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
min: function (value, minimum) {
|
||||
return Promise.resolve((function () {
|
||||
minimum = Number(minimum);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return value >= minimum
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length >= minimum
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length >= minimum
|
||||
}
|
||||
return false
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
max: function (value, minimum) {
|
||||
return Promise.resolve((function () {
|
||||
minimum = Number(minimum);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return value <= minimum
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length <= minimum
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length <= minimum
|
||||
}
|
||||
return false
|
||||
})())
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* For a single instance of an input, export all of the context needed to fully
|
||||
* render that element.
|
||||
@ -189,7 +475,8 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
elementAttributes: elementAttributes,
|
||||
logicalLabelPosition: logicalLabelPosition,
|
||||
isVmodeled: isVmodeled,
|
||||
mergedErrors: mergedErrors
|
||||
mergedErrors: mergedErrors,
|
||||
hasErrors: hasErrors
|
||||
};
|
||||
|
||||
/**
|
||||
@ -302,6 +589,13 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
.reduce(function (errors, err) { return !errors.includes(err) ? errors.concat(err) : errors; }, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this computed property have errors.
|
||||
*/
|
||||
function hasErrors () {
|
||||
return !!this.mergedErrors.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the model used throughout the existing context.
|
||||
* @param {object} context
|
||||
@ -400,6 +694,17 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
type: [String, Array, Boolean],
|
||||
default: false
|
||||
},
|
||||
validation: {
|
||||
type: [String, Boolean, Array],
|
||||
default: false
|
||||
},
|
||||
validationBehavior: {
|
||||
type: String,
|
||||
default: 'blur',
|
||||
validator: function (value) {
|
||||
return ['blur', 'live'].includes(value)
|
||||
}
|
||||
},
|
||||
error: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
@ -409,7 +714,8 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
return {
|
||||
defaultId: nanoid(9),
|
||||
localAttributes: {},
|
||||
internalModelProxy: this.formulateValue
|
||||
internalModelProxy: this.formulateValue,
|
||||
validationErrors: []
|
||||
}
|
||||
},
|
||||
computed: Object.assign({}, context,
|
||||
@ -428,6 +734,7 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
deep: true
|
||||
},
|
||||
internalModelProxy: function internalModelProxy (newValue, oldValue) {
|
||||
this.performValidation();
|
||||
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
|
||||
this.context.model = newValue;
|
||||
}
|
||||
@ -443,12 +750,29 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
this.formulateFormRegister(this.nameOrFallback, this);
|
||||
}
|
||||
this.updateLocalAttributes(this.$attrs);
|
||||
this.performValidation();
|
||||
},
|
||||
methods: {
|
||||
updateLocalAttributes: function updateLocalAttributes (value) {
|
||||
if (!shallowEqualObjects(value, this.localAttributes)) {
|
||||
this.localAttributes = value;
|
||||
}
|
||||
},
|
||||
performValidation: function performValidation () {
|
||||
var this$1 = this;
|
||||
|
||||
var rules = parseRules(this.validation, this.$formulate.rules());
|
||||
Promise.all(
|
||||
rules.map(function (ref) {
|
||||
var rule = ref[0];
|
||||
var args = ref[1];
|
||||
|
||||
return rule.apply(void 0, [ this$1.context.model ].concat( args ))
|
||||
.then(function (res) { return res ? false : 'Validation error!'; })
|
||||
})
|
||||
)
|
||||
.then(function (result) { return result.filter(function (result) { return result; }); })
|
||||
.then(function (errorMessages) { this$1.validationErrors = errorMessages; });
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -542,6 +866,7 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
staticClass: "formulate-input",
|
||||
attrs: {
|
||||
"data-classification": _vm.classification,
|
||||
"data-has-errors": _vm.hasErrors,
|
||||
"data-type": _vm.type
|
||||
}
|
||||
},
|
||||
@ -1604,7 +1929,8 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
FormulateInputSelect: FormulateInputSelect,
|
||||
FormulateInputTextArea: FormulateInputTextArea
|
||||
},
|
||||
library: library
|
||||
library: library,
|
||||
rules: rules
|
||||
};
|
||||
};
|
||||
|
||||
@ -1666,10 +1992,18 @@ var Formulate = (function (exports, isPlainObject, nanoid) {
|
||||
return false
|
||||
};
|
||||
|
||||
/**
|
||||
* Get validation rules.
|
||||
* @return {object} object of validation functions
|
||||
*/
|
||||
Formulate.prototype.rules = function rules () {
|
||||
return this.options.rules
|
||||
};
|
||||
|
||||
var Formulate$1 = new Formulate();
|
||||
|
||||
exports.default = Formulate$1;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, isPlainObject, nanoid));
|
||||
}({}, isUrl, isPlainObject, nanoid));
|
||||
|
348
dist/formulate.umd.js
vendored
348
dist/formulate.umd.js
vendored
@ -1,9 +1,10 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('is-plain-object'), require('nanoid')) :
|
||||
typeof define === 'function' && define.amd ? define(['exports', 'is-plain-object', 'nanoid'], factory) :
|
||||
(global = global || self, factory(global.Formulate = {}, global.isPlainObject, global.nanoid));
|
||||
}(this, (function (exports, isPlainObject, nanoid) { 'use strict';
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('is-url'), require('is-plain-object'), require('nanoid')) :
|
||||
typeof define === 'function' && define.amd ? define(['exports', 'is-url', 'is-plain-object', 'nanoid'], factory) :
|
||||
(global = global || self, factory(global.Formulate = {}, global.isUrl, global.isPlainObject, global.nanoid));
|
||||
}(this, (function (exports, isUrl, isPlainObject, nanoid) { 'use strict';
|
||||
|
||||
isUrl = isUrl && isUrl.hasOwnProperty('default') ? isUrl['default'] : isUrl;
|
||||
isPlainObject = isPlainObject && isPlainObject.hasOwnProperty('default') ? isPlainObject['default'] : isPlainObject;
|
||||
nanoid = nanoid && nanoid.hasOwnProperty('default') ? nanoid['default'] : nanoid;
|
||||
|
||||
@ -166,6 +167,291 @@
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array or string return an array of callables.
|
||||
* @param {array|string} validation
|
||||
* @param {array} rules and array of functions
|
||||
* @return {array} an array of functions
|
||||
*/
|
||||
function parseRules (validation, rules) {
|
||||
if (typeof validation === 'string') {
|
||||
return parseRules(validation.split('|'), rules)
|
||||
}
|
||||
if (!Array.isArray(validation)) {
|
||||
return []
|
||||
}
|
||||
return validation.map(function (rule) { return parseRule(rule, rules); }).filter(function (f) { return !!f; })
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string or function, parse it and return the an array in the format
|
||||
* [fn, [...arguments]]
|
||||
* @param {string|function} rule
|
||||
*/
|
||||
function parseRule (rule, rules) {
|
||||
if (typeof rule === 'function') {
|
||||
return [rule, []]
|
||||
}
|
||||
if (Array.isArray(rule) && rule.length) {
|
||||
if (typeof rule[0] === 'string' && rules.hasOwnProperty(rule[0])) {
|
||||
return [rules[rule.shift()], rule]
|
||||
}
|
||||
if (typeof rule[0] === 'function') {
|
||||
return [rule.shift(), rule]
|
||||
}
|
||||
}
|
||||
if (typeof rule === 'string') {
|
||||
var segments = rule.split(':');
|
||||
var functionName = segments.shift();
|
||||
if (rules.hasOwnProperty(functionName)) {
|
||||
return [rules[functionName], segments.length ? segments.join(':').split(',') : []]
|
||||
} else {
|
||||
throw new Error(("Unknown validation rule " + rule))
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Library of rules
|
||||
*/
|
||||
var rules = {
|
||||
/**
|
||||
* Rule: the value must be "yes", "on", "1", or true
|
||||
*/
|
||||
accepted: function (value) {
|
||||
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: must be a value
|
||||
*/
|
||||
required: function (value, isRequired) {
|
||||
if ( isRequired === void 0 ) isRequired = true;
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (!isRequired || ['no', 'false'].includes(isRequired)) {
|
||||
return true
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return !!value.length
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return !!value
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return (!value) ? false : !!Object.keys(value).length
|
||||
}
|
||||
return true
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Value is in an array (stack).
|
||||
*/
|
||||
in: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(stack.find(function (item) {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
}
|
||||
return item === value
|
||||
}) !== undefined)
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Value is not in stack.
|
||||
*/
|
||||
not: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(stack.find(function (item) {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
}
|
||||
return item === value
|
||||
}) === undefined)
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Match the value against a (stack) of patterns or strings
|
||||
*/
|
||||
matches: function (value) {
|
||||
var stack = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) stack[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve(!!stack.find(function (pattern) {
|
||||
if (pattern instanceof RegExp) {
|
||||
return pattern.test(value)
|
||||
}
|
||||
return pattern === value
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a string is a valid url
|
||||
*/
|
||||
url: function (value) {
|
||||
return Promise.resolve(isUrl(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: ensures the value is a date according to Date.parse()
|
||||
*/
|
||||
date: function (value) {
|
||||
return Promise.resolve(!isNaN(Date.parse(value)))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
after: function (value, compare) {
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
var fieldValue = Date.parse(value);
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue > timestamp))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
before: function (value, compare) {
|
||||
if ( compare === void 0 ) compare = false;
|
||||
|
||||
var timestamp = Date.parse(compare || new Date());
|
||||
var fieldValue = Date.parse(value);
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue < timestamp))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
alpha: function (value, set) {
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
default: /^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-z][A-Z]$/
|
||||
};
|
||||
var selectedSet = sets.hasOwnProperty(set) ? set : 'default';
|
||||
return Promise.resolve(sets[selectedSet].test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
number: function (value) {
|
||||
return Promise.resolve(!isNaN(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is alpha numeric
|
||||
*/
|
||||
alphanumeric: function (value, set) {
|
||||
if ( set === void 0 ) set = 'default';
|
||||
|
||||
var sets = {
|
||||
default: /^[a-zA-Z0-9À-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-zA-Z0-9]$/
|
||||
};
|
||||
var selectedSet = sets.hasOwnProperty(set) ? set : 'default';
|
||||
return Promise.resolve(sets[selectedSet].test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is between two other values
|
||||
*/
|
||||
between: function (value, from, to) {
|
||||
return Promise.resolve((function () {
|
||||
if (from === null || to === null || isNaN(from) || isNaN(to)) {
|
||||
return false
|
||||
}
|
||||
from = Number(from);
|
||||
to = Number(to);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return (value > from && value < to)
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length > from && value.length < to
|
||||
}
|
||||
return false
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: tests
|
||||
*/
|
||||
email: function (value) {
|
||||
// eslint-disable-next-line
|
||||
var isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
|
||||
return Promise.resolve(isEmail.test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the file type is correct.
|
||||
*/
|
||||
mime: function (files) {
|
||||
var types = [], len = arguments.length - 1;
|
||||
while ( len-- > 0 ) types[ len ] = arguments[ len + 1 ];
|
||||
|
||||
return Promise.resolve((function () {
|
||||
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
|
||||
for (var i in files) {
|
||||
if (!types.includes(files[i].type)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
min: function (value, minimum) {
|
||||
return Promise.resolve((function () {
|
||||
minimum = Number(minimum);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return value >= minimum
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length >= minimum
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length >= minimum
|
||||
}
|
||||
return false
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
max: function (value, minimum) {
|
||||
return Promise.resolve((function () {
|
||||
minimum = Number(minimum);
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value);
|
||||
return value <= minimum
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length <= minimum
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length <= minimum
|
||||
}
|
||||
return false
|
||||
})())
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* For a single instance of an input, export all of the context needed to fully
|
||||
* render that element.
|
||||
@ -192,7 +478,8 @@
|
||||
elementAttributes: elementAttributes,
|
||||
logicalLabelPosition: logicalLabelPosition,
|
||||
isVmodeled: isVmodeled,
|
||||
mergedErrors: mergedErrors
|
||||
mergedErrors: mergedErrors,
|
||||
hasErrors: hasErrors
|
||||
};
|
||||
|
||||
/**
|
||||
@ -305,6 +592,13 @@
|
||||
.reduce(function (errors, err) { return !errors.includes(err) ? errors.concat(err) : errors; }, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this computed property have errors.
|
||||
*/
|
||||
function hasErrors () {
|
||||
return !!this.mergedErrors.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the model used throughout the existing context.
|
||||
* @param {object} context
|
||||
@ -403,6 +697,17 @@
|
||||
type: [String, Array, Boolean],
|
||||
default: false
|
||||
},
|
||||
validation: {
|
||||
type: [String, Boolean, Array],
|
||||
default: false
|
||||
},
|
||||
validationBehavior: {
|
||||
type: String,
|
||||
default: 'blur',
|
||||
validator: function (value) {
|
||||
return ['blur', 'live'].includes(value)
|
||||
}
|
||||
},
|
||||
error: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
@ -412,7 +717,8 @@
|
||||
return {
|
||||
defaultId: nanoid(9),
|
||||
localAttributes: {},
|
||||
internalModelProxy: this.formulateValue
|
||||
internalModelProxy: this.formulateValue,
|
||||
validationErrors: []
|
||||
}
|
||||
},
|
||||
computed: Object.assign({}, context,
|
||||
@ -431,6 +737,7 @@
|
||||
deep: true
|
||||
},
|
||||
internalModelProxy: function internalModelProxy (newValue, oldValue) {
|
||||
this.performValidation();
|
||||
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
|
||||
this.context.model = newValue;
|
||||
}
|
||||
@ -446,12 +753,29 @@
|
||||
this.formulateFormRegister(this.nameOrFallback, this);
|
||||
}
|
||||
this.updateLocalAttributes(this.$attrs);
|
||||
this.performValidation();
|
||||
},
|
||||
methods: {
|
||||
updateLocalAttributes: function updateLocalAttributes (value) {
|
||||
if (!shallowEqualObjects(value, this.localAttributes)) {
|
||||
this.localAttributes = value;
|
||||
}
|
||||
},
|
||||
performValidation: function performValidation () {
|
||||
var this$1 = this;
|
||||
|
||||
var rules = parseRules(this.validation, this.$formulate.rules());
|
||||
Promise.all(
|
||||
rules.map(function (ref) {
|
||||
var rule = ref[0];
|
||||
var args = ref[1];
|
||||
|
||||
return rule.apply(void 0, [ this$1.context.model ].concat( args ))
|
||||
.then(function (res) { return res ? false : 'Validation error!'; })
|
||||
})
|
||||
)
|
||||
.then(function (result) { return result.filter(function (result) { return result; }); })
|
||||
.then(function (errorMessages) { this$1.validationErrors = errorMessages; });
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -545,6 +869,7 @@
|
||||
staticClass: "formulate-input",
|
||||
attrs: {
|
||||
"data-classification": _vm.classification,
|
||||
"data-has-errors": _vm.hasErrors,
|
||||
"data-type": _vm.type
|
||||
}
|
||||
},
|
||||
@ -1607,7 +1932,8 @@
|
||||
FormulateInputSelect: FormulateInputSelect,
|
||||
FormulateInputTextArea: FormulateInputTextArea
|
||||
},
|
||||
library: library
|
||||
library: library,
|
||||
rules: rules
|
||||
};
|
||||
};
|
||||
|
||||
@ -1669,6 +1995,14 @@
|
||||
return false
|
||||
};
|
||||
|
||||
/**
|
||||
* Get validation rules.
|
||||
* @return {object} object of validation functions
|
||||
*/
|
||||
Formulate.prototype.rules = function rules () {
|
||||
return this.options.rules
|
||||
};
|
||||
|
||||
var Formulate$1 = new Formulate();
|
||||
|
||||
exports.default = Formulate$1;
|
||||
|
2
dist/snow.css
vendored
2
dist/snow.css
vendored
@ -12,7 +12,7 @@
|
||||
.formulate-input .formulate-input-help {
|
||||
color: #6d6d6d;
|
||||
font-size: .7em;
|
||||
font-weight: 300;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
margin-bottom: .25em; }
|
||||
.formulate-input .formulate-input-errors {
|
||||
|
4
dist/snow.min.css
vendored
4
dist/snow.min.css
vendored
File diff suppressed because one or more lines are too long
74
package-lock.json
generated
74
package-lock.json
generated
@ -1129,7 +1129,8 @@
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz",
|
||||
"integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@types/babel__core": {
|
||||
"version": "7.1.3",
|
||||
@ -1177,6 +1178,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz",
|
||||
"integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/babel-types": "*"
|
||||
}
|
||||
@ -1412,6 +1414,7 @@
|
||||
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
|
||||
"integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"kind-of": "^3.0.2",
|
||||
"longest": "^1.0.1",
|
||||
@ -1423,6 +1426,7 @@
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"is-buffer": "^1.1.5"
|
||||
}
|
||||
@ -2441,7 +2445,8 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
@ -2692,6 +2697,7 @@
|
||||
"resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz",
|
||||
"integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/babel-types": "^7.0.0",
|
||||
"@types/babylon": "^6.16.2",
|
||||
@ -4226,7 +4232,8 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -4247,12 +4254,14 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -4267,17 +4276,20 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -4394,7 +4406,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -4406,6 +4419,7 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -4420,6 +4434,7 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -4427,12 +4442,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -4451,6 +4468,7 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -4531,7 +4549,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -4543,6 +4562,7 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -4628,7 +4648,8 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -4664,6 +4685,7 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -4683,6 +4705,7 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -4726,12 +4749,14 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -5645,6 +5670,11 @@
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
|
||||
"dev": true
|
||||
},
|
||||
"is-url": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
|
||||
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
|
||||
},
|
||||
"is-utf8": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
|
||||
@ -6241,7 +6271,8 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
|
||||
"integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
@ -6548,7 +6579,8 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
|
||||
"integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
@ -7559,7 +7591,8 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.0.tgz",
|
||||
"integrity": "sha512-uhnEDzAbrcJ8R3g2fANnSuXZMBtkpSjxTTgn2LeSiQlfmq72enQJWdQllXW24MBLYnA1SBD2vfvx2o0Zw3Ielw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
@ -8723,7 +8756,8 @@
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz",
|
||||
"integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"pug-filters": {
|
||||
"version": "3.1.1",
|
||||
@ -8843,7 +8877,8 @@
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz",
|
||||
"integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"pug-strip-comments": {
|
||||
"version": "1.0.4",
|
||||
@ -8859,7 +8894,8 @@
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz",
|
||||
"integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"pump": {
|
||||
"version": "3.0.0",
|
||||
|
@ -77,6 +77,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"is-plain-object": "^3.0.0",
|
||||
"is-url": "^1.2.4",
|
||||
"nanoid": "^2.1.6"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import library from './libs/library'
|
||||
import rules from './libs/rules'
|
||||
import isPlainObject from 'is-plain-object'
|
||||
import FormulateInput from './FormulateInput.vue'
|
||||
import FormulateForm from './FormulateForm.vue'
|
||||
@ -8,6 +9,7 @@ import FormulateInputBox from './inputs/FormulateInputBox.vue'
|
||||
import FormulateInputText from './inputs/FormulateInputText.vue'
|
||||
import FormulateInputSelect from './inputs/FormulateInputSelect.vue'
|
||||
import FormulateInputTextArea from './inputs/FormulateInputTextArea.vue'
|
||||
|
||||
/**
|
||||
* The base formulate library.
|
||||
*/
|
||||
@ -27,7 +29,8 @@ class Formulate {
|
||||
FormulateInputSelect,
|
||||
FormulateInputTextArea
|
||||
},
|
||||
library
|
||||
library,
|
||||
rules
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,6 +91,14 @@ class Formulate {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Get validation rules.
|
||||
* @return {object} object of validation functions
|
||||
*/
|
||||
rules () {
|
||||
return this.options.rules
|
||||
}
|
||||
}
|
||||
|
||||
export default new Formulate()
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
<script>
|
||||
import context from './libs/context'
|
||||
import { shallowEqualObjects } from './libs/utils'
|
||||
import { shallowEqualObjects, parseRules } from './libs/utils'
|
||||
import nanoid from 'nanoid'
|
||||
|
||||
export default {
|
||||
@ -131,7 +131,8 @@ export default {
|
||||
return {
|
||||
defaultId: nanoid(9),
|
||||
localAttributes: {},
|
||||
internalModelProxy: this.formulateValue
|
||||
internalModelProxy: this.formulateValue,
|
||||
validationErrors: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -152,6 +153,7 @@ export default {
|
||||
deep: true
|
||||
},
|
||||
internalModelProxy (newValue, oldValue) {
|
||||
this.performValidation()
|
||||
if (!this.isVmodeled && !shallowEqualObjects(newValue, oldValue)) {
|
||||
this.context.model = newValue
|
||||
}
|
||||
@ -167,12 +169,24 @@ export default {
|
||||
this.formulateFormRegister(this.nameOrFallback, this)
|
||||
}
|
||||
this.updateLocalAttributes(this.$attrs)
|
||||
this.performValidation()
|
||||
},
|
||||
methods: {
|
||||
updateLocalAttributes (value) {
|
||||
if (!shallowEqualObjects(value, this.localAttributes)) {
|
||||
this.localAttributes = value
|
||||
}
|
||||
},
|
||||
performValidation () {
|
||||
const rules = parseRules(this.validation, this.$formulate.rules())
|
||||
Promise.all(
|
||||
rules.map(([rule, args]) => {
|
||||
return rule(this.context.model, ...args)
|
||||
.then(res => res ? false : 'Validation error!')
|
||||
})
|
||||
)
|
||||
.then(result => result.filter(result => result))
|
||||
.then(errorMessages => { this.validationErrors = errorMessages })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import nanoid from 'nanoid'
|
||||
import { map, arrayify, parseRules } from './utils'
|
||||
import { map, arrayify } from './utils'
|
||||
|
||||
/**
|
||||
* For a single instance of an input, export all of the context needed to fully
|
||||
@ -30,8 +30,7 @@ export default {
|
||||
logicalLabelPosition,
|
||||
isVmodeled,
|
||||
mergedErrors,
|
||||
hasErrors,
|
||||
validationRules
|
||||
hasErrors
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,13 +148,6 @@ function hasErrors () {
|
||||
return !!this.mergedErrors.length
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of validation rules to pass.
|
||||
*/
|
||||
function validationRules () {
|
||||
return parseRules(this.validation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the model used throughout the existing context.
|
||||
* @param {object} context
|
||||
|
@ -1,41 +1,220 @@
|
||||
import isUrl from 'is-url'
|
||||
import { shallowEqualObjects } from './utils'
|
||||
|
||||
/**
|
||||
* Library of rules
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* Rule: the value must be "yes", "on", "1", or true
|
||||
*/
|
||||
accepted: function (value) {
|
||||
return Promise.resolve(['yes', 'on', '1', 1, true, 'true'].includes(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: must be a value
|
||||
*/
|
||||
required: async function (value) {
|
||||
if (Array.isArray(value)) {
|
||||
return !!value.length
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return !!value
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return (!value) ? false : !!Object.keys(value).length
|
||||
}
|
||||
return true
|
||||
required: function (value, isRequired = true) {
|
||||
return Promise.resolve((() => {
|
||||
if (!isRequired || ['no', 'false'].includes(isRequired)) {
|
||||
return true
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return !!value.length
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return !!value
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return (!value) ? false : !!Object.keys(value).length
|
||||
}
|
||||
return true
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Value is in an array (stack).
|
||||
*/
|
||||
in: async function (value, ...stack) {
|
||||
return !!stack.find(item => shallowEqualObjects(item, value))
|
||||
in: function (value, ...stack) {
|
||||
return Promise.resolve(stack.find(item => {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
}
|
||||
return item === value
|
||||
}) !== undefined)
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Value is not in stack.
|
||||
*/
|
||||
not: function (value, ...stack) {
|
||||
return Promise.resolve(stack.find(item => {
|
||||
if (typeof item === 'object') {
|
||||
return shallowEqualObjects(item, value)
|
||||
}
|
||||
return item === value
|
||||
}) === undefined)
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: Match the value against a (stack) of patterns or strings
|
||||
*/
|
||||
matches: async function (value, ...stack) {
|
||||
return !!stack.find(pattern => {
|
||||
matches: function (value, ...stack) {
|
||||
return Promise.resolve(!!stack.find(pattern => {
|
||||
if (pattern instanceof RegExp) {
|
||||
return pattern.test(value)
|
||||
}
|
||||
return pattern === value
|
||||
})
|
||||
}))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a string is a valid url
|
||||
*/
|
||||
url: function (value) {
|
||||
return Promise.resolve(isUrl(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: ensures the value is a date according to Date.parse()
|
||||
*/
|
||||
date: function (value) {
|
||||
return Promise.resolve(!isNaN(Date.parse(value)))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
after: function (value, compare = false) {
|
||||
const timestamp = Date.parse(compare || new Date())
|
||||
const fieldValue = Date.parse(value)
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue > timestamp))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if a value is after a given date. Defaults to current time
|
||||
*/
|
||||
before: function (value, compare = false) {
|
||||
const timestamp = Date.parse(compare || new Date())
|
||||
const fieldValue = Date.parse(value)
|
||||
return Promise.resolve(isNaN(fieldValue) ? false : (fieldValue < timestamp))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
alpha: function (value, set = 'default') {
|
||||
const sets = {
|
||||
default: /^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-z][A-Z]$/
|
||||
}
|
||||
const selectedSet = sets.hasOwnProperty(set) ? set : 'default'
|
||||
return Promise.resolve(sets[selectedSet].test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is only alpha numeric
|
||||
*/
|
||||
number: function (value) {
|
||||
return Promise.resolve(!isNaN(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is alpha numeric
|
||||
*/
|
||||
alphanumeric: function (value, set = 'default') {
|
||||
const sets = {
|
||||
default: /^[a-zA-Z0-9À-ÖØ-öø-ÿ]+$/,
|
||||
latin: /^[a-zA-Z0-9]$/
|
||||
}
|
||||
const selectedSet = sets.hasOwnProperty(set) ? set : 'default'
|
||||
return Promise.resolve(sets[selectedSet].test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: checks if the value is between two other values
|
||||
*/
|
||||
between: function (value, from, to) {
|
||||
return Promise.resolve((() => {
|
||||
if (from === null || to === null || isNaN(from) || isNaN(to)) {
|
||||
return false
|
||||
}
|
||||
from = Number(from)
|
||||
to = Number(to)
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value)
|
||||
return (value > from && value < to)
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length > from && value.length < to
|
||||
}
|
||||
return false
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Rule: tests
|
||||
*/
|
||||
email: function (value) {
|
||||
// eslint-disable-next-line
|
||||
const isEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
|
||||
return Promise.resolve(isEmail.test(value))
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the file type is correct.
|
||||
*/
|
||||
mime: function (files, ...types) {
|
||||
return Promise.resolve((() => {
|
||||
if (typeof window !== 'undefined' && typeof FileReader !== 'undefined' && typeof Blob !== 'undefined') {
|
||||
for (const i in files) {
|
||||
if (!types.includes(files[i].type)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
min: function (value, minimum) {
|
||||
return Promise.resolve((() => {
|
||||
minimum = Number(minimum)
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value)
|
||||
return value >= minimum
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length >= minimum
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length >= minimum
|
||||
}
|
||||
return false
|
||||
})())
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the minimum value of a particular.
|
||||
*/
|
||||
max: function (value, minimum) {
|
||||
return Promise.resolve((() => {
|
||||
minimum = Number(minimum)
|
||||
if (!isNaN(value)) {
|
||||
value = Number(value)
|
||||
return value <= minimum
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value.length <= minimum
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.length <= minimum
|
||||
}
|
||||
return false
|
||||
})())
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,10 @@ describe('required', () => {
|
||||
it('passes with a non empty array', async () => expect(await rules.required(['123'])).toBe(true))
|
||||
|
||||
it('passes with a non empty object', async () => expect(await rules.required({a: 'b'})).toBe(true))
|
||||
|
||||
it('passes with empty value if second argument is false', async () => expect(await rules.required('', false)).toBe(true))
|
||||
|
||||
it('passes with empty value if second argument is false string', async () => expect(await rules.required('', 'false')).toBe(true))
|
||||
})
|
||||
|
||||
|
||||
@ -75,3 +79,273 @@ describe('matches', () => {
|
||||
expect(await rules.matches('first-fourth', 'second', /^third/, /fourth$/)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Accepted rule
|
||||
*/
|
||||
describe('accepted', () => {
|
||||
it('passes with true', async () => expect(await rules.accepted('yes')).toBe(true))
|
||||
|
||||
it('passes with on', async () => expect(await rules.accepted('on')).toBe(true))
|
||||
|
||||
it('passes with 1', async () => expect(await rules.accepted('1')).toBe(true))
|
||||
|
||||
it('passes with number 1', async () => expect(await rules.accepted(1)).toBe(true))
|
||||
|
||||
it('passes with boolean true', async () => expect(await rules.accepted(true)).toBe(true))
|
||||
|
||||
it('fail with boolean false', async () => expect(await rules.accepted(false)).toBe(false))
|
||||
|
||||
it('fail with "false"', async () => expect(await rules.accepted('false')).toBe(false))
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Url rule.
|
||||
*
|
||||
* Note: these are just sanity checks because the actual package we use is
|
||||
* well tested: https://github.com/segmentio/is-url/blob/master/test/index.js
|
||||
*/
|
||||
describe('url', () => {
|
||||
it('passes with http://google.com', async () => expect(await rules.url('http://google.com')).toBe(true))
|
||||
|
||||
it('fails with google.com', async () => expect(await rules.url('google.com')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Determines if the string is a date
|
||||
*/
|
||||
describe('date', () => {
|
||||
it('passes with month day year', async () => expect(await rules.date('December 17, 2020')).toBe(true))
|
||||
|
||||
it('passes with month day', async () => expect(await rules.date('December 17')).toBe(true))
|
||||
|
||||
it('passes with short month day', async () => expect(await rules.date('Dec 17')).toBe(true))
|
||||
|
||||
it('passes with short month day', async () => expect(await rules.date('Dec 17 12:34:15')).toBe(true))
|
||||
|
||||
it('passes with out of bounds number', async () => expect(await rules.date('January 77')).toBe(true))
|
||||
|
||||
it('passes with only month', async () => expect(await rules.date('January')).toBe(false))
|
||||
|
||||
it('fails with only day of week', async () => expect(await rules.date('saturday')).toBe(false))
|
||||
|
||||
it('fails with random string', async () => expect(await rules.date('Pepsi 17')).toBe(false))
|
||||
|
||||
it('fails with random number', async () => expect(await rules.date('1872301237')).toBe(false))
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a date is after another date
|
||||
*/
|
||||
describe('after', () => {
|
||||
const today = new Date()
|
||||
const tomorrow = new Date()
|
||||
const yesterday = new Date()
|
||||
tomorrow.setDate(today.getDate() + 1)
|
||||
yesterday.setDate(today.getDate() - 1)
|
||||
|
||||
it('passes with tomorrow’s date object', async () => expect(await rules.after(tomorrow)).toBe(true))
|
||||
|
||||
it('passes with future date', async () => expect(await rules.after('January 15, 2999')).toBe(true))
|
||||
|
||||
it('passes with long past date', async () => expect(await rules.after(yesterday, 'Jan 15, 2000')).toBe(true))
|
||||
|
||||
it('fails with yesterday’s date', async () => expect(await rules.after(yesterday)).toBe(false))
|
||||
|
||||
it('fails with old date string', async () => expect(await rules.after('January, 2000')).toBe(false))
|
||||
|
||||
it('fails with invalid value', async () => expect(await rules.after('')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if a date is after another date
|
||||
*/
|
||||
describe('before', () => {
|
||||
const today = new Date()
|
||||
const tomorrow = new Date()
|
||||
const yesterday = new Date()
|
||||
tomorrow.setDate(today.getDate() + 1)
|
||||
yesterday.setDate(today.getDate() - 1)
|
||||
|
||||
it('fails with tomorrow’s date object', async () => expect(await rules.before(tomorrow)).toBe(false))
|
||||
|
||||
it('fails with future date', async () => expect(await rules.before('January 15, 2999')).toBe(false))
|
||||
|
||||
it('fails with long past date', async () => expect(await rules.before(yesterday, 'Jan 15, 2000')).toBe(false))
|
||||
|
||||
it('passes with yesterday’s date', async () => expect(await rules.before(yesterday)).toBe(true))
|
||||
|
||||
it('passes with old date string', async () => expect(await rules.before('January, 2000')).toBe(true))
|
||||
|
||||
it('fails with invalid value', async () => expect(await rules.after('')).toBe(false))
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a date is after another date
|
||||
*/
|
||||
describe('alpha', () => {
|
||||
it('passes with simple string', async () => expect(await rules.alpha('abc')).toBe(true))
|
||||
|
||||
it('passes with long string', async () => expect(await rules.alpha('lkashdflaosuihdfaisudgflakjsdbflasidufg')).toBe(true))
|
||||
|
||||
it('passes with single character', async () => expect(await rules.alpha('z')).toBe(true))
|
||||
|
||||
it('passes with accented character', async () => expect(await rules.alpha('jüstin')).toBe(true))
|
||||
|
||||
it('passes with lots of accented characters', async () => expect(await rules.alpha('àáâäïíôöÆ')).toBe(true))
|
||||
|
||||
it('passes with lots of accented characters if invalid set', async () => expect(await rules.alpha('àáâäïíôöÆ', 'russian')).toBe(true))
|
||||
|
||||
it('fails with lots of accented characters if latin', async () => expect(await rules.alpha('àáâäïíôöÆ', 'latin')).toBe(false))
|
||||
|
||||
it('fails with numbers', async () => expect(await rules.alpha('justin83')).toBe(false))
|
||||
|
||||
it('fails with symbols', async () => expect(await rules.alpha('-justin')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if a date is after another date
|
||||
*/
|
||||
describe('number', () => {
|
||||
it('passes with simple number string', async () => expect(await rules.number('123')).toBe(true))
|
||||
|
||||
it('passes with simple number', async () => expect(await rules.number(19832461234)).toBe(true))
|
||||
|
||||
it('passes with float', async () => expect(await rules.number(198.32464)).toBe(true))
|
||||
|
||||
it('passes with decimal in string', async () => expect(await rules.number('567.23')).toBe(true))
|
||||
|
||||
it('fails with comma in number string', async () => expect(await rules.number('123,456')).toBe(false))
|
||||
|
||||
it('fails with alpha', async () => expect(await rules.number('123sdf')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if a date alpha and numeric
|
||||
*/
|
||||
describe('alphanumeric', () => {
|
||||
it('passes with simple string', async () => expect(await rules.alphanumeric('567abc')).toBe(true))
|
||||
|
||||
it('passes with long string', async () => expect(await rules.alphanumeric('lkashdfla234osuihdfaisudgflakjsdbfla567sidufg')).toBe(true))
|
||||
|
||||
it('passes with single character', async () => expect(await rules.alphanumeric('z')).toBe(true))
|
||||
|
||||
it('passes with accented character', async () => expect(await rules.alphanumeric('jüst56in')).toBe(true))
|
||||
|
||||
it('passes with lots of accented characters', async () => expect(await rules.alphanumeric('àáâ7567567äïíôöÆ')).toBe(true))
|
||||
|
||||
it('passes with lots of accented characters if invalid set', async () => expect(await rules.alphanumeric('123123àáâäï67íôöÆ', 'russian')).toBe(true))
|
||||
|
||||
it('fails with lots of accented characters if latin', async () => expect(await rules.alphanumeric('àáâäï123123íôöÆ', 'latin')).toBe(false))
|
||||
|
||||
it('fails with decimals in', async () => expect(await rules.alphanumeric('abcABC99.123')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if between
|
||||
*/
|
||||
describe('between', () => {
|
||||
it('passes with simple number', async () => expect(await rules.between(5, 0, 10)).toBe(true))
|
||||
|
||||
it('passes with simple number string', async () => expect(await rules.between('5', '0', '10')).toBe(true))
|
||||
|
||||
it('passes with decimal number string', async () => expect(await rules.between('0.5', '0', '1')).toBe(true))
|
||||
|
||||
it('passes with string length', async () => expect(await rules.between('abc', 2, 4)).toBe(true))
|
||||
|
||||
it('fails with string length too long', async () => expect(await rules.between('abcdef', 2, 4)).toBe(false))
|
||||
|
||||
it('fails with string length too short', async () => expect(await rules.between('abc', 3, 10)).toBe(false))
|
||||
|
||||
it('fails with number to small', async () => expect(await rules.between(0, 3, 10)).toBe(false))
|
||||
|
||||
it('fails with number to large', async () => expect(await rules.between(15, 3, 10)).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Checks if email.
|
||||
*
|
||||
* Note: testing is light, regular expression used is here: http://jsfiddle.net/ghvj4gy9/embedded/result,js/
|
||||
*/
|
||||
describe('email', () => {
|
||||
it('passes normal email', async () => expect(await rules.email('dev+123@wearebraid.com')).toBe(true))
|
||||
|
||||
it('passes numeric email', async () => expect(await rules.email('12345@google.com')).toBe(true))
|
||||
|
||||
it('passes unicode email', async () => expect(await rules.email('àlphä@❤️.ly')).toBe(true))
|
||||
|
||||
it('passes numeric with new tld', async () => expect(await rules.email('12345@google.photography')).toBe(true))
|
||||
|
||||
it('fails string without tld', async () => expect(await rules.email('12345@localhost')).toBe(false))
|
||||
|
||||
it('fails string without tld', async () => expect(await rules.email('12345@localhost')).toBe(false))
|
||||
|
||||
it('fails string without invalid name', async () => expect(await rules.email('1*(123)2345@localhost')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Mime types.
|
||||
*/
|
||||
describe('mime', () => {
|
||||
it('passes basic image/jpeg stack', async () => expect(await rules.mime([{type: 'image/jpeg'}], 'image/png', 'image/jpeg')).toBe(true))
|
||||
|
||||
it('fails when not in stack', async () => expect(await rules.mime([{type: 'application/json'}], 'image/png', 'image/jpeg')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Minimum.
|
||||
*/
|
||||
describe('min', () => {
|
||||
it('passes when a number string', async () => expect(await rules.min('5', '5')).toBe(true))
|
||||
|
||||
it('passes when a number', async () => expect(await rules.min(6, 5)).toBe(true))
|
||||
|
||||
it('passes when a string length', async () => expect(await rules.min('foobar', '6')).toBe(true))
|
||||
|
||||
it('passes when a array length', async () => expect(await rules.min(Array(6), '6')).toBe(true))
|
||||
|
||||
it('fails when a array length', async () => expect(await rules.min(Array(6), '7')).toBe(false))
|
||||
|
||||
it('fails when a string length', async () => expect(await rules.min('bar', 4)).toBe(false))
|
||||
|
||||
it('fails when a number', async () => expect(await rules.min(3, '7')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Maximum.
|
||||
*/
|
||||
describe('max', () => {
|
||||
it('passes when a number string', async () => expect(await rules.max('5', '5')).toBe(true))
|
||||
|
||||
it('passes when a number', async () => expect(await rules.max(5, 6)).toBe(true))
|
||||
|
||||
it('passes when a string length', async () => expect(await rules.max('foobar', '6')).toBe(true))
|
||||
|
||||
it('passes when a array length', async () => expect(await rules.max(Array(6), '6')).toBe(true))
|
||||
|
||||
it('fails when a array length', async () => expect(await rules.max(Array(6), '5')).toBe(false))
|
||||
|
||||
it('fails when a string length', async () => expect(await rules.max('bar', 2)).toBe(false))
|
||||
|
||||
it('fails when a number', async () => expect(await rules.max(10, '7')).toBe(false))
|
||||
})
|
||||
|
||||
/**
|
||||
* Maximum.
|
||||
*/
|
||||
describe('not', () => {
|
||||
it('passes when a number string', async () => expect(await rules.not('5', '6')).toBe(true))
|
||||
|
||||
it('passes when a number', async () => expect(await rules.not(1, 30)).toBe(true))
|
||||
|
||||
it('passes when a string', async () => expect(await rules.not('abc', 'def')).toBe(true))
|
||||
|
||||
it('fails when a shallow equal array', async () => expect(await rules.not(['abc'], ['abc'])).toBe(false))
|
||||
|
||||
it('fails when a shallow equal object', async () => expect(await rules.not({a: 'abc'}, ['123'], {a: 'abc'})).toBe(false))
|
||||
|
||||
it('fails when string is in stack', async () => expect(await rules.not('a', 'b', 'c', 'd', 'a', 'f')).toBe(false))
|
||||
})
|
||||
|
@ -20,7 +20,7 @@
|
||||
.formulate-input-help {
|
||||
color: $formulate-gray-ddd;
|
||||
font-size: .7em;
|
||||
font-weight: 300;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
margin-bottom: .25em;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user