ffr-components
Version:
Fiori styled UI components
594 lines (477 loc) • 18.4 kB
JavaScript
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/esm/inherits";
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
import React from 'react';
import { Button } from 'fundamental-react/Button';
import util from '../utils';
import { trigger } from './constants';
var _React$createContext = React.createContext({}),
FormProvider = _React$createContext.Provider,
FormConsumer = _React$createContext.Consumer;
var predefinedValidator = {
mandatory: function mandatory(rule) {
return function (val) {
return {
state: Boolean(val) && String(val).length > 0,
message: rule.message
};
};
},
maxLength: function maxLength(rule, max) {
return function (val) {
return {
state: !val || String(val).length <= Number(max),
message: rule.message
};
};
}
};
/**
* data :
* hasError: indicate whether current data pass validate
* value (checked): value from component
* observers : array of observer which other element related to this one, could be active or inactive
* onChange: collect value, used to control the component
* listener: used to trigger update of component
* show:
* disabled:
* state: result of validation. can be normal or error.
* message : validation message, joint used with state
* fieldMeta: an configuration object with following properties:
* name: field name
* path: path in graphql
* readOnly:
* show
* fieldType : number,
* initialValue : default value
* valueName : describe which shoudl be "value" for the elemtent, could be checked, value
* semantics : string,currency, password...
* mandatory :bool
* rules: an array of validators, validator is object with fields function (or string) and message
* rule : { mandatory, message }, {trigger, validate,dependencies}, validate shoudl return object includes message
*
* structor of validation
* name : property name
* rules : array of rule
* trigger : change, blur and sunmit
* validate : method used to verify, return value is an object with optional boolean props state, diabled, show and message (React element or string)
* dependencies : array of dependent properties
*
*/
var DataStore =
/*#__PURE__*/
function () {
function DataStore() {
var _this = this;
_classCallCheck(this, DataStore);
this.getMetaData = function (name) {
return _this._data[name] && _this._data[name].fieldMeta;
};
this.hasMeta = function (name) {
return !!(_this._data[name] && _this._data[name].fieldMeta);
};
this.getAllFormData = function () {
return Object.keys(_this._data).reduce(function (obj, k) {
if (!_this._data[k].show) {
return obj;
}
var val = _this._data[k][_this._data[k].fieldMeta.valueName];
var _this$_data$k$fieldMe = _this._data[k].fieldMeta,
path = _this$_data$k$fieldMe.path,
name = _this$_data$k$fieldMe.name;
var fields = (path || name).split('/');
return fields.reduce(function (_obj, fd, inx) {
if (_obj[fd]) {
return _obj;
}
if (inx < fields.length - 1) {
return _obj[fd] = {};
}
_obj[fd] = val;
return _obj;
}, obj);
}, {});
};
this.cleanDataStatus = function (data) {
var fieldMeta = data.fieldMeta;
data.hasError = false;
data.state = 'normal';
data.message = null;
data.show = fieldMeta.show === false ? false : true;
data.disabled = fieldMeta.readOnly;
};
this.resetData = function () {
Object.keys(_this._data).forEach(function (k) {
var data = _this._data[k];
_this.resetSingleData(data, true);
});
};
this.setSingleValue = function (name, value) {
var refresh = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var data = _this._data[name];
if (data) {
data[data.fieldMeta.valueName] = value;
refresh && data.listener();
}
};
this.resetSingleData = function (data, refresh) {
var fieldMeta = data.fieldMeta;
_this.cleanDataStatus(data);
if (data[fieldMeta.valueName] !== fieldMeta.initialValue) {
data[fieldMeta.valueName] = fieldMeta.initialValue;
refresh && data.listener();
}
};
this.checkAllDataClean = function () {
return !Object.keys(_this._data).some(function (k) {
return _this._data[k].hasError;
});
};
this.validate = function (_trigger, data, currentVal) {
if (!data) {
throw new Error("data for verification should be object, but got ".concat(data));
}
var triggers = Array.isArray(_trigger) ? _trigger : [_trigger];
var value = currentVal || data.value;
if (data.hasError) {
return false;
}
if (data.fieldMeta.rules) {
return data.fieldMeta.rules.filter(function (rule) {
return triggers.indexOf(trigger[rule.trigger]) >= 0;
}).every(function (rule) {
var result = rule.validate(value, _this.getAllFormData());
return _this.__handleResult(result, data, rule.message);
});
}
return true;
};
this.__handleResult = function (result, data, message) {
if (!util.isObject(result)) {
console.error("expect method validate return object, but get ".concat(result));
return false;
}
if (result.state !== undefined) {
data.hasError = !result.state;
data.state = result.state ? 'normal' : 'invalid';
data.message = result.state ? null : result.message || message;
return result.state;
} else if (result.disabled !== undefined) {
data.disabled = result.disabled;
} else if (result.show !== undefined) {
if (!result.show) {
_this.resetSingleData(data);
}
data.show = result.show;
}
return true;
};
this.validateBeforeSubmit = function () {
var hasError = true;
Object.keys(_this._data).forEach(function (k) {
var data = _this._data[k];
var result = _this.validate(trigger.submit, data);
!result && data.listener();
hasError = hasError && result;
});
return hasError;
};
this.notifyByTrigger = function (data, _trigger) {
Array.isArray(data.observers) && data.observers.filter(function (ob) {
return trigger[ob.trigger] === _trigger;
}).forEach(function (ob) {
ob.notify(data[data.fieldMeta.valueName]);
});
};
this.setData = function (name, data) {
_this._data[name] = data;
};
this.getData = function (name) {
return _this._data[name];
};
this._data = {};
}
_createClass(DataStore, [{
key: "formatData",
value: function formatData(name, data) {
return data;
}
}]);
return DataStore;
}();
export var ITEM_LAYOUT = {
horizontal: 'horizontal',
vertical: 'vertical'
};
var Form =
/*#__PURE__*/
function (_React$Component) {
_inherits(Form, _React$Component);
function Form(_props) {
var _this2;
_classCallCheck(this, Form);
_this2 = _possibleConstructorReturn(this, _getPrototypeOf(Form).call(this, _props));
_this2.handleSubmit = function (e) {
e.preventDefault();
_this2.submit();
};
_this2.submit = function () {
var onSubmit = _this2.props.onSubmit;
if (!_this2.dataStore.checkAllDataClean()) {
return;
}
if (!_this2.dataStore.validateBeforeSubmit()) {
return;
}
var formData = _this2.dataStore.getAllFormData();
if (onSubmit) {
onSubmit(formData);
}
};
_this2.__isSameData = function (data1, data2) {
if (util.isObject(data1) && util.isObject(data2)) {
return Object.keys(data1).every(function (k) {
return data2[k] === data1[k];
});
} else {
return data1 === data2;
}
};
_this2.handleChange = function (name) {
return function (e, val) {
// for conbobox, the value is input via second parameter
var value;
var data = _this2.dataStore.getData(name);
if (e.stopPropagation) {
e.stopPropagation();
var target = e.target;
value = val || target[data.fieldMeta.valueName];
} else {
value = e;
}
if (_this2.__isSameData(value, data[data.fieldMeta.valueName])) return;
_this2.dataStore.cleanDataStatus(data);
var result = _this2.dataStore.validate(trigger.change, data, value);
if (result) {
_this2.dataStore.setData(name, _objectSpread({}, data, _defineProperty({}, data.fieldMeta.valueName, value))); // data.listener({ state: elementState.normal, message: null });
}
if (data.originAction && data.originAction[trigger.change]) {
data.originAction[trigger.change].call(null, e, val);
}
data.listener();
_this2.dataStore.notifyByTrigger(_this2.dataStore.getData(name), trigger.change);
};
};
_this2.handleBlur = function (name) {
return function (e) {
e.stopPropagation();
var value = e.target.value;
var data = _this2.dataStore.getData(name);
_this2.dataStore.validate(trigger.blur, data, value);
if (data.originAction && data.originAction[trigger.blur]) {
data.originAction[trigger.blur].call(null, e);
}
data.listener();
_this2.dataStore.notifyByTrigger(data, trigger.blur);
};
};
_this2._retrieveInputProps = function (data) {
var fieldMeta = data.fieldMeta,
listener = data.listener,
hasError = data.hasError,
initialValue = data.initialValue,
originAction = data.originAction,
isCheck = data.isCheck,
inputProps = _objectWithoutProperties(data, ["fieldMeta", "listener", "hasError", "initialValue", "originAction", "isCheck"]);
var mandatory = fieldMeta.mandatory;
return _objectSpread({}, inputProps, {
mandatory: mandatory
});
};
_this2.mapRules = function () {
var rules = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var name = arguments.length > 1 ? arguments[1] : undefined;
return rules.map(function (rule) {
if (Array.isArray(rule.dependencies)) {
_this2.dependencies.push({
name: name,
rule: rule
});
return false;
}
var _rule = _objectSpread({}, rule);
if (!_rule.validate) {
if (_rule.mandatory) {
_rule.validate = predefinedValidator.mandatory(_rule);
} else if (_rule.maxLength) {
_rule.validate = predefinedValidator.maxLength(_rule, _rule.maxLength);
}
}
if (!_rule.trigger) {
_rule.trigger = trigger["default"];
}
return _rule;
}).filter(Boolean);
};
_this2.generateValueName = function (ops) {
return ops.isCheck ? 'checked' : 'value';
};
_this2.generateInitialValue = function (defaultValue, ops) {
if (defaultValue !== null && defaultValue !== undefined) {
return defaultValue;
}
if (ops.isCheck) {
return false;
}
return '';
};
_this2.subscribBlur = function (rules, obj, name, originAction) {
if (originAction && originAction[trigger.blur]) {
obj.onBlur = _this2.handleBlur(name);
return;
}
rules.some(function (rule) {
if (trigger[rule.trigger] === trigger.blur) {
obj.onBlur = _this2.handleBlur(name);
return true;
}
return false;
});
};
_this2.mapProps = function (itemProps) {
var _obj;
var name = itemProps.name,
meta = itemProps.fieldMeta,
listener = itemProps.listener,
isCheck = itemProps.isCheck,
originAction = itemProps.originAction,
defaultValue = itemProps.initialValue,
disabled = itemProps.disabled;
var fieldMeta = _objectSpread({}, meta);
var ops = {
isCheck: isCheck
};
if (!fieldMeta.name) {
fieldMeta.name = name;
}
var rules = fieldMeta.rules;
fieldMeta.valueName = _this2.generateValueName(ops);
fieldMeta.rules = _this2.mapRules(rules, name);
fieldMeta.initialValue = _this2.generateInitialValue(defaultValue, ops);
fieldMeta.readOnly = fieldMeta.readOnly !== undefined ? fieldMeta.readOnly : disabled;
var obj = (_obj = {
fieldMeta: fieldMeta
}, _defineProperty(_obj, fieldMeta.valueName, fieldMeta.initialValue), _defineProperty(_obj, "onChange", _this2.handleChange(name)), _defineProperty(_obj, "listener", listener), _defineProperty(_obj, "originAction", originAction), _defineProperty(_obj, "state", 'normal'), _defineProperty(_obj, "message", null), _defineProperty(_obj, "disabled", fieldMeta.readOnly), _defineProperty(_obj, "show", fieldMeta.show === false ? false : true), _obj);
_this2.subscribBlur(fieldMeta.rules, obj, name, originAction);
_this2.dataStore.setData(name, obj);
return _this2._retrieveInputProps(_objectSpread({}, itemProps, {}, obj));
};
_this2.resetForm = function () {
_this2.dataStore.resetData();
};
_this2.setValue = function (name, value) {
var data = _this2.dataStore.getData(name);
_this2.dataStore.cleanDataStatus(data);
if (_this2.dataStore.validate(trigger.change, data, value)) {
_this2.dataStore.setSingleValue(name, value);
_this2.dataStore.notifyByTrigger(data, trigger.change);
}
};
_this2.connect = function (targetComponent, props) {
var sName = props.name,
fieldMeta = props.fieldMeta;
var name = fieldMeta && fieldMeta.name || sName;
if (!name) {
throw new Error('must map field with a name');
}
var _data = _this2.dataStore.getData(name);
if (_data !== undefined) {
// make sure only connect once
return _this2._retrieveInputProps(_objectSpread({}, props, {}, _data));
}
var listener = targetComponent.forceUpdate.bind(targetComponent);
return _this2.mapProps(_objectSpread({}, props, {
listener: listener
}));
};
_this2.dataStore = new DataStore();
_this2.dependencies = [];
return _this2;
}
_createClass(Form, [{
key: "renderPreventSubmitButton",
value: function renderPreventSubmitButton() {
// used to prevent submit when click enter key in any form element inside form
return React.createElement(Button, {
style: {
display: 'none'
},
onClick: function onClick(e) {
e.preventDefault();
e.stopPropagation();
}
});
}
}, {
key: "renderChildren",
value: function renderChildren() {
var children = this.props.children;
return React.createElement(React.Fragment, null, this.renderPreventSubmitButton(), children);
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
var _this3 = this;
this.dependencies.forEach(function (depRule) {
var name = depRule.name,
rule = depRule.rule;
var _validate = function _validate(val) {
var selfData = _this3.dataStore.getData(name);
var allData = _this3.dataStore.getAllFormData();
var result = rule.validate(selfData.value, allData, val);
_this3.dataStore.__handleResult(result, selfData, rule.message);
selfData.listener();
};
var trigger = rule.trigger;
rule.dependencies.forEach(function (dep) {
var observable = _this3.dataStore.getData(dep);
if (trigger === 'blur' && !observable.onBlur) {
_this3.subscribBlur([rule], observable, dep);
}
var notify = _validate;
observable.observers ? observable.observers.push({
trigger: trigger,
notify: notify
}) : observable.observers = [{
trigger: trigger,
notify: notify
}];
});
});
}
}, {
key: "render",
value: function render() {
var itemLayout = this.props.itemLayout;
return React.createElement(FormProvider, {
value: {
connect: this.connect,
itemLayout: itemLayout
}
}, React.createElement("form", {
onSubmit: this.handleSubmit
}, this.renderChildren()));
}
}]);
return Form;
}(React.Component);
Form.displayName = 'Form';
Form.ITEM_LAYOUT = ITEM_LAYOUT;
export { Form as default };
export { FormConsumer };