UNPKG

ffr-components

Version:

Fiori styled UI components

594 lines (477 loc) 18.4 kB
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 };