nornj-react
Version:
React bindings for NornJ template engine.
317 lines (267 loc) • 8.61 kB
JavaScript
import React, { Fragment, isValidElement, useMemo } from 'react';
import nj, { registerExtension, as } from 'nornj';
import { observable, runInAction, reaction, extendObservable, isComputedProp, observe } from 'mobx';
import schema from 'async-validator';
import extensionConfigs from '../../../mobx/formData/extensionConfig';
import moment from 'moment';
const createFormData = options => ({
_njMobxFormData: true,
fieldDatas: new Map(),
_operate(name, callback, callbackMulti, callbackMultiReturn) {
if (typeof name === 'string') {
return callback(name);
} else {
const names = Array.isArray(name) ? name : [];
this.fieldDatas.forEach((fieldData, name) => {
(!names.length || names.indexOf(name) > -1) && (callbackMulti || callback)(name);
});
if (callbackMultiReturn) {
return callbackMultiReturn(names);
}
}
},
_validate(name) {
const oFd = this.fieldDatas.get(name);
const value = this[name];
return new Promise((resolve, reject) => {
oFd.validatorSchema.validate({
[name]: value
}, {}, (errors, fields) => {
if (errors) {
var _errors$;
this.error(name, errors === null || errors === void 0 ? void 0 : (_errors$ = errors[0]) === null || _errors$ === void 0 ? void 0 : _errors$.message);
reject({
values: {
[name]: value
},
errors,
fields
});
} else {
this.clear(name, true);
resolve({
[name]: value
});
}
});
});
},
validate(names) {
const validators = [];
return this._operate(names, name => this._validate(name), name => validators.push(this._validate(name)), () => new Promise((resolve, reject) => {
Promise.all(validators).then(values => resolve(Object.assign({}, ...values))).catch(errorInfo => reject(errorInfo));
}));
},
error(name, help) {
const oFd = this.fieldDatas.get(name);
oFd.validateStatus = 'error';
oFd.help = help;
},
_clear(name, success) {
const oFd = this.fieldDatas.get(name);
oFd.validateStatus = success ? 'success' : null;
oFd.help = null;
},
clear(names, success) {
return this._operate(names, name => this._clear(name, success));
},
_reset(name) {
this.clear(name);
const oFd = this.fieldDatas.get(name);
oFd.reset();
},
reset(names) {
return this._operate(names, name => this._reset(name));
},
add(fieldData) {
const {
name,
value,
trigger = 'valueChange',
rules,
...ruleOptions
} = fieldData;
const fd = {
name,
value,
trigger,
rules,
...ruleOptions
};
const _rules = rules ? rules : [ruleOptions];
fd.rules = _rules.map((rule, i) => {
const oRule = observable(rule);
observe(oRule, change => {
const schemaRules = fd.validatorSchema.rules[name];
Object.assign(schemaRules[i], oRule);
});
return oRule;
});
fd.setDefaultRule = rule => {
const schemaRules = fd.validatorSchema.rules[name];
_rules.forEach((r, i) => {
if (r.type == null) {
schemaRules[i].type = rule.type;
}
});
};
fd.validatorSchema = new schema({
[name]: _rules.map(({
type = 'string',
required = false,
transform: _transform,
...others
}) => ({
type,
required,
transform(_value) {
var _value2;
switch (this.type) {
case 'number':
case 'integer':
case 'float':
_value = nj.isString(_value) && ((_value2 = _value) === null || _value2 === void 0 ? void 0 : _value2.trim()) !== '' && !isNaN(_value) ? +_value : _value;
break;
case 'date':
if (moment.isMoment(_value)) {
_value = _value.toDate();
}
break;
}
return _transform ? _transform(_value) : _value;
},
...others
}))
});
fd.reset = function () {
if (this.value !== value) {
this._resetting = true;
}
this.value = value;
};
const oFd = observable(fd);
this.fieldDatas.set(name, oFd);
if (options === null || options === void 0 ? void 0 : options.validateMessages) {
const {
validateMessages
} = options;
fd.validatorSchema.messages(typeof validateMessages === 'function' ? validateMessages(oFd) : validateMessages);
}
!isComputedProp(this, name) && extendObservable(this, Object.defineProperty({}, name, {
get: function () {
var _this$fieldDatas$get;
return (_this$fieldDatas$get = this.fieldDatas.get(name)) === null || _this$fieldDatas$get === void 0 ? void 0 : _this$fieldDatas$get.value;
},
set: function (value) {
this.setValue(name, value);
},
enumerable: true,
configurable: true
}));
if (trigger === 'valueChange') {
oFd._reactionDispose = reaction(() => Array.isArray(this[name]) ? this[name].map(item => item) : this[name], () => {
if (!oFd._resetting) {
this.validate(name).catch(nj.noop);
}
oFd._resetting = false;
});
}
},
delete(name) {
const oFd = this.fieldDatas.get(name);
oFd === null || oFd === void 0 ? void 0 : oFd._reactionDispose();
this.fieldDatas.delete(name);
},
setValue(name, value) {
runInAction(() => {
if (typeof name === 'string') {
const fieldData = this.fieldDatas.get(name);
if (fieldData) {
fieldData.value = value;
}
} else {
this.fieldDatas.forEach((fieldData, fieldName) => {
if (fieldName in name) {
fieldData.value = name[fieldName];
}
});
}
});
},
get formData() {
return this;
}
});
function getChildrenWithoutFragment(children) {
const actualChildren = [];
children.forEach(child => {
if (!isValidElement(child) || child.type !== Fragment) {
if (Array.isArray(child)) {
var _getChildrenWithoutFr;
(_getChildrenWithoutFr = getChildrenWithoutFragment(child)) === null || _getChildrenWithoutFr === void 0 ? void 0 : _getChildrenWithoutFr.forEach(child => actualChildren.push(child));
} else {
actualChildren.push(child);
}
} else {
var _getChildrenWithoutFr2, _ref;
(_getChildrenWithoutFr2 = getChildrenWithoutFragment((_ref = child === null || child === void 0 ? void 0 : child.props) === null || _ref === void 0 ? void 0 : _ref.children)) === null || _getChildrenWithoutFr2 === void 0 ? void 0 : _getChildrenWithoutFr2.forEach(child => actualChildren.push(child));
}
});
return actualChildren;
}
registerExtension('mobxFormData', options => {
const {
children
} = options;
const props = options.props;
let _children = children();
if (!Array.isArray(_children)) {
_children = [_children];
}
const formData = createFormData(props);
getChildrenWithoutFragment(_children).forEach(fieldData => {
fieldData && formData.add(fieldData);
});
return (props === null || props === void 0 ? void 0 : props.observable) ? observable(formData) : formData;
}, extensionConfigs.mobxFormData);
registerExtension('mobxFieldData', options => options.props, extensionConfigs.mobxFieldData);
registerExtension('mobxField', options => {
const {
value,
tagProps
} = options;
const _value = value();
const {
prop,
source
} = _value;
const fieldNames = Array.isArray(prop) ? prop : [prop];
let validateStatus = null;
const help = [];
fieldNames.forEach(fieldName => {
const oFd = source.fieldDatas.get(fieldName);
if (validateStatus == null && oFd.validateStatus != null) {
validateStatus = oFd.validateStatus;
}
if (oFd.help != null) {
help.push(React.createElement("div", {
key: help.length
}, oFd.help));
}
if (tagProps.required == null) {
tagProps.required = oFd.rules.find(rule => rule.required);
}
if (tagProps.label) {
oFd.label = tagProps.label;
} else if (oFd.label) {
tagProps.label = oFd.label;
}
});
tagProps.validateStatus = validateStatus;
if (help.length) {
tagProps.help = React.createElement(React.Fragment, null, help);
}
}, extensionConfigs.mobxField);
export function useFormData(formDataElement, deps = []) {
return useMemo(() => as(observable(formDataElement())), deps);
}