d2-ui
Version:
197 lines (164 loc) • 7.3 kB
JavaScript
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
import Rx from 'rx';
import log from 'loglevel';
import { isFunction } from 'lodash/fp';
export var FormFieldStatuses = {
VALID: 'VALID',
INVALID: 'INVALID',
VALIDATING: 'VALIDATING'
};
function identity(val) {
return val;
}
function getAllPromiseValues(promises) {
return Promise.all(promises.map(function (promise) {
return promise.then(identity, identity);
}));
}
function validatorRunner(fieldName, fieldValue, formSource) {
return function runValidator(validator) {
var result = void 0;
try {
result = validator(fieldValue, fieldName, formSource);
} catch (e) {
log.debug('Validator for \'' + fieldName + '\' ignored because the validator threw an error.');
log.debug('' + validator);
log.debug(e.message);
return Promise.resolve(true);
}
if (result === false) {
return Promise.reject(validator.message);
}
return Promise.resolve(result);
};
}
function awaitAsyncValidators(accumulator, validatorPromise, index, validators) {
if (validatorPromise) {
accumulator.push(validatorPromise);
}
if (validators.length === 0 || validators.length === index + 1) {
return getAllPromiseValues(accumulator);
}
return accumulator;
}
function grabErrorMessages(validationStatuses) {
return validationStatuses.filter(function (s) {
return s !== true;
});
}
function getFieldStatus() {
var statusMessages = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];
return {
status: statusMessages.length === 0 ? FormFieldStatuses.VALID : FormFieldStatuses.INVALID,
messages: statusMessages
};
}
export default function createFormValidator() {
var fieldConfigs = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];
var scheduler = arguments[1];
var validatorQueue = new Rx.Subject();
var statusSubject = new Rx.ReplaySubject(1);
var initialStatuses = fieldConfigs.filter(function (fieldConfig) {
return Array.isArray(fieldConfig.validators) && fieldConfig.validators.length > 0;
}).map(function (fc) {
return [fc.name, { status: FormFieldStatuses.VALID, messages: [] }];
});
var formFieldStatuses = new Map(initialStatuses);
var validatorQueues = new Map(initialStatuses.map(function (_ref) {
var _ref2 = _slicedToArray(_ref, 1);
var name = _ref2[0];
return [name, new Rx.Subject()];
}));
Array.from(validatorQueues.values()).forEach(function (validatorObservable) {
validatorObservable.debounce(300, scheduler).map(function (_ref3) {
var fieldName = _ref3.fieldName;
var fieldValue = _ref3.fieldValue;
var formSource = _ref3.formSource;
var fieldConfig = fieldConfigs.filter(function (fc) {
return fc.name === fieldName;
}).shift();
validatorQueue.onNext(Promise.resolve({ fieldName: fieldName, fieldStatus: { status: FormFieldStatuses.VALIDATING, messages: [] } }));
var validatorToRun = fieldConfig.validators.filter(function (validator) {
if (!isFunction(validator)) {
log.warn('Warning: One of the validators for \'' + fieldName + '\' is not a function.');
return false;
}
return isFunction(validator);
}).map(validatorRunner(fieldName, fieldValue, formSource));
if (!validatorToRun.length) {
return Promise.resolve({
fieldName: fieldName,
fieldStatus: getFieldStatus()
});
}
return validatorToRun.reduce(awaitAsyncValidators, []).then(grabErrorMessages).then(function (errorMessages) {
return {
fieldName: fieldName,
fieldStatus: getFieldStatus(errorMessages)
};
}).catch(log.error);
}).concatAll().subscribe(function (_ref4) {
var fieldName = _ref4.fieldName;
var fieldStatus = _ref4.fieldStatus;
formFieldStatuses.set(fieldName, fieldStatus);
statusSubject.onNext(formFieldStatuses);
});
});
validatorQueue.concatAll().subscribe(function (fieldValidatorStatus) {
var fieldName = fieldValidatorStatus.fieldName;
var fieldStatus = fieldValidatorStatus.fieldStatus;
formFieldStatuses.set(fieldName, fieldStatus);
statusSubject.onNext(formFieldStatuses);
});
var formValidator = {
status: statusSubject.debounce(100),
setStatus: function setStatus(status) {
statusSubject.onNext(status);
},
/**
* Start a validation run for a specific field with a provided value. This runs sync and async validators
* and reports the status back using the `formValidator.status` observable.
*
* @param {String} fieldName Name of the field to run the validator for.
* @param {String} fieldValue Value of the field to run the validator for.
* @returns {boolean} Returns true when a validator run has started, otherwise false.
*
* @example
* ```js
* formValidator.runFor('name', 'Mark');
* ```
*/
runFor: function runFor(fieldName, fieldValue, formSource) {
if (validatorQueues.has(fieldName)) {
validatorQueues.get(fieldName).onNext({ fieldName: fieldName, fieldValue: fieldValue, formSource: formSource });
return true;
}
return false;
},
/**
* Returns the current status for the passed field.
*
* @param {String} fieldName Name of the field. Generally this is the `name` property on the `fieldConfig`
* @returns {Object} Status object with a `status` and a `messages` property.
*
* @example
* ```js
* formValidator.getStatusFor('password')
* // {
* // status: FormFieldStatuses.VALID,
* // messages: []
* // }
* ```
*/
getStatusFor: function getStatusFor(fieldName) {
if (formFieldStatuses.has(fieldName)) {
return formFieldStatuses.get(fieldName);
}
return {
status: FormFieldStatuses.VALID,
messages: []
};
}
};
return formValidator;
}