/// /// import * as React from "react"; import UpSchemaFormComponentSelector, { PropertyViewModel } from "./UpForm/UpSchemaFormComponentSelector"; import ErrorMemory from "./UpForm/ErrorMemory"; import JsonSchemaHelper from "./helper/JsonSchemaHelper"; import { UpPanel } from "@up-group/react-controls"; import * as _ from "lodash"; type ShouldApplyUpdateRulePolicy = (trackedFieldValue: any) => any; interface UpdateRule { trackedField: string; targetField: string; policyName: string } export interface UpSchemaFormProps { initValue?: any; value?: any; schema: string | JsonSchema; onFormChange: (data: any, hasError: boolean) => void; showError: boolean; ignoredProperties?: string[]; wrapperClassName?: string; viewModels?: PropertyViewModel[]; translate?: (text: string) => any; updateRulePolicies?: ShouldApplyUpdateRulePolicy[]; updateRules?: UpdateRule[]; onSearchButtonClick: (text: string) => any; } export default class UpSchemaForm extends React.Component< UpSchemaFormProps, {data: any} > { static defaultProps = { showError: true, initValue: {} }; private errorMemory = new ErrorMemory(); constructor(p, c) { super(p, c); if (this.props.initValue != null) { this.state = {data: this.props.initValue}; } else { this.state = {data: this.props.value || {}}; } } componentWillReceiveProps(newProps: UpSchemaFormProps){ if(this.props.value != newProps.value){ const newState = { ..._.cloneDeep(newProps.value) }; this.setState({data: newState}, () => {this.forceUpdate()}); } } private getSchema(): JsonSchema { if (this.props.schema == null) { return null; } else if (typeof this.props.schema === "string") { return JsonSchemaHelper.parseSchema(this.props.schema as string); } else { return JsonSchemaHelper.flat( this.props.schema, this.props.schema.definitions, {} ); } } render() { let schema: JsonSchema = this.getSchema(); if (schema == null) return ; if (schema == null || schema.type == null) { return (
{this.props.children}
); } const value = _.cloneDeep(this.state.data); let content = (
{this.props.children}
); if (schema.title) return ( {content} ); return content; } onChange = ( e: React.ChangeEvent, newValue: any, hasError: boolean, node: string ) => { this.errorMemory.errorOn(node, hasError); let nodeArray = node.split("."); nodeArray.shift(); this.addToQueue(this.state.data, nodeArray, newValue, node); }; updateState(node: string) { let schema: JsonSchema = this.getSchema(); if (schema && schema.properties["start_date"] && schema.properties["end_date"] && schema.properties["start_date"].format == "date" && schema.properties["end_date"].format == "date") { if (this.state.data["start_date"]) { schema.properties["end_date"].minimum = this.state.data["start_date"].format() } if (this.state.data["end_date"]) { schema.properties["start_date"].maximum = this.state.data["end_date"].format() } } if (this.props.updateRules && this.props.updateRules.length > 0) { let policies = this.props.updateRules .filter(rule => node.substring(1) === rule.trackedField) .map(rule => { let policy = this.props.updateRulePolicies.filter(p => p.name === rule.policyName); return { rule, policy: policy.length > 0 ? policy[0] : null }; }) .filter(policy => policy.policy !== null) ; if (policies.length > 0) { let changes = {}; for (const policy of policies) { let newValue = policy.policy(_.get(this.state.data, policy.rule.trackedField)); _.set(changes, policy.rule.targetField, newValue); } this.setState({data: {...this.state.data, ...changes }}, () => this.props.onFormChange(_.cloneDeep(this.state.data), this.errorMemory.hasError)) return; } } this.props.onFormChange(_.cloneDeep(this.state.data), this.errorMemory.hasError); } private newObject(nodes, value) { let obj = {}; let prop = nodes.shift(); if (nodes.length == 0) { obj[prop] = value; } else { obj[prop] = this.newObject(nodes, value); } return obj; } private inQueue = false; private assingDataOrder: { obj: any; nodes: any; value: any, node: string }[] = []; private AssignValue(obj, nodes, value) { let data = obj != null ? _.cloneDeep(obj) : {}; let prop = nodes.shift(); if (nodes.length === 0) { data[prop] = value; return data; } else if (data.hasOwnProperty(prop) && typeof data[prop] === "object") { data[prop] = this.AssignValue(data[prop], nodes, value); return data; } else if (data.hasOwnProperty(prop) === false) { data[prop] = {}; data[prop] = this.AssignValue(data[prop], nodes, value); return data; } } addToQueue = (obj, nodes, value, node) => { this.assingDataOrder.push({ obj, nodes, value, node }); if (this.inQueue === false) { this.checkQueue(); } }; private checkQueue = () => { let a = this.assingDataOrder[0]; this.inQueue = true; this.setState({data: this.AssignValue(a.obj, a.nodes, a.value)}, () => { this.assingDataOrder.shift(); if (this.assingDataOrder.length == 0) { this.updateState(a.node); this.inQueue = false; } else { this.assingDataOrder[0].obj = this.state.data; this.checkQueue(); } }); }; } function is(x, y) { if (x === y) { // Steps 1-5, 7-10 // Steps 6.b-6.e: +0 != -0 // Added the nonzero y check to make Flow happy, but it is redundant return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { // Step 6.a: NaN == NaN return x !== x && y !== y; } } function shallowEqual(objA, objB) { if (is(objA, objB)) { return true; } if ( typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null ) { return false; } let keysA = Object.keys(objA); let keysB = Object.keys(objB); if (keysA.length !== keysB.length) { return false; } for (let i = 0; i < keysA.length; i++) { if (objA[keysA[i]] && objA[keysA[i]].constructor === Array) { for (let j = 0; j < objA[keysA[i]].lenght; j++) { if (shallowEqual(objA[keysA[i]][j], objA[keysB[i]][j]) === false) { return false; } } } else if ( objB.hasOwnProperty(keysA[i]) && typeof objA[keysA[i]] === "object" ) { if (shallowEqual(objA[keysA[i]], objB[keysA[i]]) == false) { return false; } } else if ( !objB.hasOwnProperty(keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]]) ) { return false; } } return true; }