Browse Source

Json Schema Form: Add Fieldset, Array and Date fields.

pull/2150/head
Igor Kulikov 7 years ago
parent
commit
f2a90f18a1
  1. 54
      ui-ngx/package-lock.json
  2. 4
      ui-ngx/package.json
  3. 2
      ui-ngx/src/app/shared/components/json-form/json-form.component.ts
  4. 177
      ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx
  5. 2
      ui-ngx/src/app/shared/components/json-form/react/json-form-checkbox.tsx
  6. 82
      ui-ngx/src/app/shared/components/json-form/react/json-form-date.tsx
  7. 44
      ui-ngx/src/app/shared/components/json-form/react/json-form-fieldset.tsx
  8. 2
      ui-ngx/src/app/shared/components/json-form/react/json-form-rc-select.tsx
  9. 2
      ui-ngx/src/app/shared/components/json-form/react/json-form-react.tsx
  10. 20
      ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx
  11. 57
      ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts
  12. 27
      ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts

54
ui-ngx/package-lock.json

@ -1331,6 +1331,19 @@
} }
} }
}, },
"@date-io/core": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@date-io/core/-/core-1.3.11.tgz",
"integrity": "sha512-Yxf2ei0vjU38Fizswr/Uwub5QeRiLOHiTRiHUuTdg+biVB+1EUk+h5szas9SEWA2pZDlSo73F5TPuu+zKqOIBQ=="
},
"@date-io/date-fns": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-1.3.11.tgz",
"integrity": "sha512-6Pvk4gwCU4L19XYzDUrro861JCQjZkJQjugxAA+M8wsDTW75A5rmSZGa6g2rQQXfg6ox4B7HBx9p6JYDsSPX0g==",
"requires": {
"@date-io/core": "^1.3.11"
}
},
"@emotion/hash": { "@emotion/hash": {
"version": "0.7.3", "version": "0.7.3",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz",
@ -1366,6 +1379,26 @@
"react-transition-group": "^4.3.0" "react-transition-group": "^4.3.0"
} }
}, },
"@material-ui/icons": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.5.1.tgz",
"integrity": "sha512-YZ/BgJbXX4a0gOuKWb30mBaHaoXRqPanlePam83JQPZ/y4kl+3aW0Wv9tlR70hB5EGAkEJGW5m4ktJwMgxQAeA==",
"requires": {
"@babel/runtime": "^7.4.4"
}
},
"@material-ui/pickers": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.2.7.tgz",
"integrity": "sha512-dDi8G8TOXssXZQsGCRM4zoDnWMY4O/vvqVCH4ViIHflvS4ek4v30IlFcSONI5jGzL0dmJhNKso2UEn7qS3iZ3g==",
"requires": {
"@babel/runtime": "^7.6.0",
"@types/styled-jsx": "^2.2.8",
"clsx": "^1.0.2",
"react-transition-group": "^4.0.0",
"rifm": "^0.7.0"
}
},
"@material-ui/styles": { "@material-ui/styles": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.5.0.tgz", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.5.0.tgz",
@ -1651,6 +1684,14 @@
"integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
"dev": true "dev": true
}, },
"@types/styled-jsx": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/@types/styled-jsx/-/styled-jsx-2.2.8.tgz",
"integrity": "sha512-Yjye9VwMdYeXfS71ihueWRSxrruuXTwKCbzue4+5b2rjnQ//AtyM7myZ1BEhNhBQ/nL/RE7bdToUoLln2miKvg==",
"requires": {
"@types/react": "*"
}
},
"@types/tinycolor2": { "@types/tinycolor2": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.2.tgz", "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.2.tgz",
@ -3725,6 +3766,11 @@
"assert-plus": "^1.0.0" "assert-plus": "^1.0.0"
} }
}, },
"date-fns": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.5.0.tgz",
"integrity": "sha512-I6Tkis01//nRcmvMQw/MRE1HAtcuA5Ie6jGPb8bJZJub7494LGOObqkV3ParnsSVviAjk5C8mNKDqYVBzCopWg=="
},
"date-format": { "date-format": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
@ -9131,6 +9177,14 @@
"integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==",
"dev": true "dev": true
}, },
"rifm": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/rifm/-/rifm-0.7.0.tgz",
"integrity": "sha512-DSOJTWHD67860I5ojetXdEQRIBvF6YcpNe53j0vn1vp9EUb9N80EiZTxgP+FkDKorWC8PZw052kTF4C1GOivCQ==",
"requires": {
"@babel/runtime": "^7.3.1"
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",

4
ui-ngx/package.json

@ -24,8 +24,11 @@
"@angular/platform-browser-dynamic": "~8.2.11", "@angular/platform-browser-dynamic": "~8.2.11",
"@angular/router": "~8.2.11", "@angular/router": "~8.2.11",
"@auth0/angular-jwt": "^3.0.0", "@auth0/angular-jwt": "^3.0.0",
"@date-io/date-fns": "^1.3.11",
"@mat-datetimepicker/core": "^2.0.1", "@mat-datetimepicker/core": "^2.0.1",
"@material-ui/core": "^4.5.1", "@material-ui/core": "^4.5.1",
"@material-ui/icons": "^4.5.1",
"@material-ui/pickers": "^3.2.7",
"@ngrx/effects": "^8.2.0", "@ngrx/effects": "^8.2.0",
"@ngrx/store": "^8.2.0", "@ngrx/store": "^8.2.0",
"@ngrx/store-devtools": "^8.2.0", "@ngrx/store-devtools": "^8.2.0",
@ -38,6 +41,7 @@
"base64-js": "^1.3.1", "base64-js": "^1.3.1",
"compass-sass-mixins": "^0.12.7", "compass-sass-mixins": "^0.12.7",
"core-js": "^3.1.4", "core-js": "^3.1.4",
"date-fns": "^2.5.0",
"deep-equal": "^1.0.1", "deep-equal": "^1.0.1",
"flot": "git://github.com/thingsboard/flot.git#0.9-work", "flot": "git://github.com/thingsboard/flot.git#0.9-work",
"flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master", "flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master",

2
ui-ngx/src/app/shared/components/json-form/json-form.component.ts

@ -159,7 +159,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
onFullscreenChanged() {} onFullscreenChanged() {}
private onModelChange(key: string | string[], val: any) { private onModelChange(key: (string | number)[], val: any) {
if (isString(val) && val === '') { if (isString(val) && val === '') {
val = undefined; val = undefined;
} }

177
ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx

@ -0,0 +1,177 @@
/*
* Copyright © 2016-2019 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
import JsonFormUtils from './json-form-utils';
import ThingsboardBaseComponent from './json-form-base-component';
import Button from '@material-ui/core/Button';
import _ from 'lodash';
import IconButton from '@material-ui/core/IconButton';
import ClearIcon from '@material-ui/icons/Clear';
import AddIcon from '@material-ui/icons/Add';
import Tooltip from '@material-ui/core/Tooltip';
import {
JsonFormData,
JsonFormFieldProps,
JsonFormFieldState
} from '@shared/components/json-form/react/json-form.models';
interface ThingsboardArrayState extends JsonFormFieldState {
model: any[];
keys: number[];
}
class ThingsboardArray extends React.Component<JsonFormFieldProps, ThingsboardArrayState> {
constructor(props) {
super(props);
this.onAppend = this.onAppend.bind(this);
this.onDelete = this.onDelete.bind(this);
const model = JsonFormUtils.selectOrSet(this.props.form.key, this.props.model) || [];
const keys: number[] = [];
for (let i = 0; i < model.length; i++) {
keys.push(i);
}
this.state = {
model,
keys
};
}
componentDidMount() {
if (this.props.form.startEmpty !== true && this.state.model.length === 0) {
this.onAppend();
}
}
onAppend() {
let empty;
if (this.props.form && this.props.form.schema && this.props.form.schema.items) {
const items = this.props.form.schema.items;
if (items.type && items.type.indexOf('object') !== -1) {
empty = {};
if (!this.props.options || this.props.options.setSchemaDefaults !== false) {
empty = typeof items.default !== 'undefined' ? items.default : empty;
if (empty) {
JsonFormUtils.traverseSchema(items, (prop, path) => {
if (typeof prop.default !== 'undefined') {
JsonFormUtils.selectOrSet(path, empty, prop.default);
}
});
}
}
} else if (items.type && items.type.indexOf('array') !== -1) {
empty = [];
if (!this.props.options || this.props.options.setSchemaDefaults !== false) {
empty = items.default || empty;
}
} else {
if (!this.props.options || this.props.options.setSchemaDefaults !== false) {
empty = items.default || empty;
}
}
}
const newModel = this.state.model;
newModel.push(empty);
const newKeys = this.state.keys;
let key = 0;
if (newKeys.length > 0) {
key = newKeys[newKeys.length - 1] + 1;
}
newKeys.push(key);
this.setState({
model: newModel,
keys: newKeys
}
);
this.props.onChangeValidate(this.state.model);
}
onDelete(index: number) {
const newModel = this.state.model;
newModel.splice(index, 1);
const newKeys = this.state.keys;
newKeys.splice(index, 1);
this.setState(
{
model: newModel,
keys: newKeys
}
);
this.props.onChangeValidate(this.state.model);
}
setIndex(index: number) {
return (form: JsonFormData) => {
if (form.key) {
form.key[form.key.indexOf('')] = index;
}
};
}
copyWithIndex(form: JsonFormData, index: number): JsonFormData {
const copy: JsonFormData = _.cloneDeep(form);
copy.arrayIndex = index;
JsonFormUtils.traverseForm(copy, this.setIndex(index));
return copy;
}
render() {
const arrays = [];
const fields = [];
const model = this.state.model;
const keys = this.state.keys;
const items = this.props.form.items;
for (let i = 0; i < model.length; i++ ) {
let removeButton: JSX.Element = null;
if (!this.props.form.readonly) {
const boundOnDelete = this.onDelete.bind(this, i);
removeButton = <Tooltip title='Remove'><IconButton onClick={boundOnDelete}><ClearIcon/></IconButton></Tooltip>;
}
const forms = (this.props.form.items as JsonFormData[]).map((form, index) => {
const copy = this.copyWithIndex(form, i);
return this.props.builder(copy, this.props.model, index, this.props.onChange,
this.props.onColorClick, this.props.onToggleFullscreen, this.props.mapper);
});
arrays.push(
<li key={keys[i]} className='list-group-item'>
{removeButton}
{forms}
</li>
);
}
let addButton: JSX.Element = null;
if (!this.props.form.readonly) {
addButton = <Button variant='contained'
color='primary'
startIcon={<AddIcon/>}
onClick={this.onAppend}>{this.props.form.add || 'New'}</Button>;
}
return (
<div>
<div className='tb-container'>
<div className='tb-head-label'>{this.props.form.title}</div>
<ol className='list-group'>
{arrays}
</ol>
</div>
{addButton}
</div>
);
}
}
export default ThingsboardBaseComponent(ThingsboardArray);

2
ui-ngx/src/app/shared/components/json-form/react/json-form-checkbox.tsx

@ -26,7 +26,7 @@ class ThingsboardCheckbox extends React.Component<JsonFormFieldProps, JsonFormFi
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
name={this.props.form.key.slice(-1)[0]} name={this.props.form.key.slice(-1)[0] + ''}
value={this.props.form.key.slice(-1)[0]} value={this.props.form.key.slice(-1)[0]}
defaultChecked={this.props.value || false} defaultChecked={this.props.value || false}
disabled={this.props.form.readonly} disabled={this.props.form.readonly}

82
ui-ngx/src/app/shared/components/json-form/react/json-form-date.tsx

@ -0,0 +1,82 @@
/*
* Copyright © 2016-2019 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
import ThingsboardBaseComponent from './json-form-base-component';
import DateFnsUtils from '@date-io/date-fns';
import {
MuiPickersUtilsProvider,
KeyboardDatePicker
} from '@material-ui/pickers';
import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models';
interface ThingsboardDateState extends JsonFormFieldState {
currentValue: Date | null;
}
class ThingsboardDate extends React.Component<JsonFormFieldProps, ThingsboardDateState> {
constructor(props) {
super(props);
this.onDatePicked = this.onDatePicked.bind(this);
let value: Date | null = null;
if (this.props.value && typeof this.props.value === 'number') {
value = new Date(this.props.value);
}
this.state = {
currentValue: value
};
}
onDatePicked(date: Date | null) {
this.setState({
currentValue: date
});
this.props.onChangeValidate(date ? date.getTime() : null);
}
render() {
let fieldClass = 'tb-date-field';
if (this.props.form.required) {
fieldClass += ' tb-required';
}
if (this.props.form.readonly) {
fieldClass += ' tb-readonly';
}
return (
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<div style={{width: '100%', display: 'block'}}>
<KeyboardDatePicker
disableToolbar
variant='inline'
format='MM/dd/yyyy'
margin='normal'
className={fieldClass}
label={this.props.form.title}
value={this.state.currentValue}
onChange={this.onDatePicked}
disabled={this.props.form.readonly}
style={this.props.form.style || {width: '100%'}}/>
</div>
</MuiPickersUtilsProvider>
);
}
}
export default ThingsboardBaseComponent(ThingsboardDate);

44
ui-ngx/src/app/shared/components/json-form/react/json-form-fieldset.tsx

@ -0,0 +1,44 @@
/*
* Copyright © 2016-2019 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';
import {
JsonFormData,
JsonFormFieldProps,
JsonFormFieldState
} from '@shared/components/json-form/react/json-form.models';
class ThingsboardFieldSet extends React.Component<JsonFormFieldProps, JsonFormFieldState> {
render() {
const forms = (this.props.form.items as JsonFormData[]).map((form: JsonFormData, index) => {
return this.props.builder(form, this.props.model, index, this.props.onChange,
this.props.onColorClick, this.props.onToggleFullscreen, this.props.mapper);
});
return (
<div style={{paddingTop: '20px'}}>
<div className='tb-head-label'>
{this.props.form.title}
</div>
<div>
{forms}
</div>
</div>
);
}
}
export default ThingsboardFieldSet;

2
ui-ngx/src/app/shared/components/json-form/react/json-form-rc-select.tsx

@ -39,7 +39,7 @@ class ThingsboardRcSelect extends React.Component<JsonFormFieldProps, Thingsboar
this.onFocus = this.onFocus.bind(this); this.onFocus = this.onFocus.bind(this);
this.state = { this.state = {
currentValue: this.keyToCurrentValue(this.props.value, this.props.form.schema.type === 'array'), currentValue: this.keyToCurrentValue(this.props.value, this.props.form.schema.type === 'array'),
items: this.props.form.items, items: this.props.form.items as KeyLabelItem[],
focused: false focused: false
}; };
} }

2
ui-ngx/src/app/shared/components/json-form/react/json-form-react.tsx

@ -13,8 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import './json-form.scss';
import * as React from 'react'; import * as React from 'react';
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
import thingsboardTheme from './styles/thingsboardTheme'; import thingsboardTheme from './styles/thingsboardTheme';

20
ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx

@ -16,8 +16,8 @@
import * as React from 'react'; import * as React from 'react';
import JsonFormUtils from './json-form-utils'; import JsonFormUtils from './json-form-utils';
/*import ThingsboardArray from './json-form-array.jsx'; import ThingsboardArray from './json-form-array';
import ThingsboardJavaScript from './json-form-javascript.jsx'; /*import ThingsboardJavaScript from './json-form-javascript.jsx';
import ThingsboardJson from './json-form-json.jsx'; import ThingsboardJson from './json-form-json.jsx';
import ThingsboardHtml from './json-form-html.jsx'; import ThingsboardHtml from './json-form-html.jsx';
import ThingsboardCss from './json-form-css.jsx'; import ThingsboardCss from './json-form-css.jsx';
@ -27,11 +27,11 @@ import ThingsboardNumber from './json-form-number';
import ThingsboardText from './json-form-text'; import ThingsboardText from './json-form-text';
import ThingsboardSelect from './json-form-select'; import ThingsboardSelect from './json-form-select';
import ThingsboardRadios from './json-form-radios'; import ThingsboardRadios from './json-form-radios';
/*import ThingsboardDate from './json-form-date.jsx'; import ThingsboardDate from './json-form-date';
import ThingsboardImage from './json-form-image.jsx';*/ /*import ThingsboardImage from './json-form-image.jsx';*/
import ThingsboardCheckbox from './json-form-checkbox'; import ThingsboardCheckbox from './json-form-checkbox';
import ThingsboardHelp from './json-form-help'; import ThingsboardHelp from './json-form-help';
/*import ThingsboardFieldSet from './json-form-fieldset.jsx';*/ import ThingsboardFieldSet from './json-form-fieldset';
import { JsonFormProps, GroupInfo, JsonFormData } from './json-form.models'; import { JsonFormProps, GroupInfo, JsonFormData } from './json-form.models';
import _ from 'lodash'; import _ from 'lodash';
@ -51,18 +51,18 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
textarea: ThingsboardText, textarea: ThingsboardText,
select: ThingsboardSelect, select: ThingsboardSelect,
radios: ThingsboardRadios, radios: ThingsboardRadios,
// date: ThingsboardDate, date: ThingsboardDate,
// image: ThingsboardImage, // image: ThingsboardImage,
checkbox: ThingsboardCheckbox, checkbox: ThingsboardCheckbox,
help: ThingsboardHelp, help: ThingsboardHelp,
// array: ThingsboardArray, array: ThingsboardArray,
// javascript: ThingsboardJavaScript, // javascript: ThingsboardJavaScript,
// json: ThingsboardJson, // json: ThingsboardJson,
// html: ThingsboardHtml, // html: ThingsboardHtml,
// css: ThingsboardCss, // css: ThingsboardCss,
// color: ThingsboardColor, // color: ThingsboardColor,
'rc-select': ThingsboardRcSelect, 'rc-select': ThingsboardRcSelect,
// fieldset: ThingsboardFieldSet fieldset: ThingsboardFieldSet
}; };
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
@ -71,7 +71,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
this.hasConditions = false; this.hasConditions = false;
} }
onChange(key: string | string[], val: any) { onChange(key: (string | number)[], val: any) {
this.props.onModelChange(key, val); this.props.onModelChange(key, val);
if (this.hasConditions) { if (this.hasConditions) {
this.forceUpdate(); this.forceUpdate();
@ -90,7 +90,7 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
builder(form: JsonFormData, builder(form: JsonFormData,
model: any, model: any,
index: number, index: number,
onChange: (key: string | string[], val: any) => void, onChange: (key: (string | number)[], val: any) => void,
onColorClick: (event: MouseEvent, key: string, val: string) => void, onColorClick: (event: MouseEvent, key: string, val: string) => void,
onToggleFullscreen: () => void, onToggleFullscreen: () => void,
mapper: {[type: string]: any}): JSX.Element { mapper: {[type: string]: any}): JSX.Element {

57
ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts

@ -17,7 +17,13 @@
import * as tv from 'tv4'; import * as tv from 'tv4';
import ObjectPath from 'objectpath'; import ObjectPath from 'objectpath';
import _ from 'lodash'; import _ from 'lodash';
import { SchemaValidationResult, DefaultsFormOptions, FormOption } from './json-form.models'; import {
SchemaValidationResult,
DefaultsFormOptions,
FormOption,
JsonSchemaData,
JsonFormData
} from './json-form.models';
import { isDefined, isString, isUndefined } from '@core/utils'; import { isDefined, isString, isUndefined } from '@core/utils';
import * as equal from 'deep-equal'; import * as equal from 'deep-equal';
@ -433,7 +439,7 @@ function merge(schema: any, form: any[], ignore: { [key: string]: boolean }, opt
})); }));
} }
function selectOrSet(projection: string | string[], obj: any, valueToSet?: any): any { function selectOrSet(projection: string | (string | number)[], obj: any, valueToSet?: any): any {
const numRe = /^\d+$/; const numRe = /^\d+$/;
if (!obj) { if (!obj) {
@ -474,7 +480,7 @@ function selectOrSet(projection: string | string[], obj: any, valueToSet?: any):
return value; return value;
} }
function updateValue(projection: string | string[], obj: any, valueToSet: any): boolean { function updateValue(projection: string | (string | number)[], obj: any, valueToSet: any): boolean {
const numRe = /^\d+$/; const numRe = /^\d+$/;
if (!obj) { if (!obj) {
@ -531,12 +537,55 @@ function setValue(obj: any, key: string, val: any): boolean {
return changed; return changed;
} }
function traverseSchema(schema: JsonSchemaData, fn: (prop: any, path: string[]) => any, path?: string[], ignoreArrays?: boolean) {
ignoreArrays = typeof ignoreArrays !== 'undefined' ? ignoreArrays : true;
path = path || [];
const traverse = ($schema: JsonSchemaData, $fn: (prop: any, path: string[]) => any, $path: string[]) => {
$fn($schema, $path);
for (const k of Object.keys($schema.properties)) {
if ($schema.properties.hasOwnProperty(k)) {
const currentPath = $path.slice();
currentPath.push(k);
traverse($schema.properties[k], $fn, currentPath);
}
}
if (!ignoreArrays && $schema.items) {
const arrPath = $path.slice();
arrPath.push('');
traverse($schema.items, $fn, arrPath);
}
};
traverse(schema, fn, path || []);
}
function traverseForm(form: JsonFormData, fn: (form: JsonFormData) => any) {
fn(form);
if (form.items) {
form.items.forEach((f) => {
traverseForm(f, fn);
});
}
if (form.tabs) {
form.tabs.forEach((tab) => {
tab.items.forEach((f) => {
traverseForm(f, fn);
});
});
}
}
const utils = { const utils = {
validateBySchema, validateBySchema,
validate, validate,
merge, merge,
updateValue, updateValue,
selectOrSet selectOrSet,
traverseSchema,
traverseForm
}; };
export default utils; export default utils;

27
ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts

@ -55,7 +55,7 @@ export interface JsonFormProps {
isFullscreen: boolean; isFullscreen: boolean;
ignore?: {[key: string]: boolean}; ignore?: {[key: string]: boolean};
option: FormOption; option: FormOption;
onModelChange?: (key: string | string[], val: any) => void; onModelChange?: (key: (string | number)[], val: any) => void;
onColorClick?: (event: MouseEvent, key: string, val: string) => void; onColorClick?: (event: MouseEvent, key: string, val: string) => void;
onToggleFullscreen?: () => void; onToggleFullscreen?: () => void;
mapper?: {[type: string]: any}; mapper?: {[type: string]: any};
@ -67,9 +67,16 @@ export interface KeyLabelItem {
value?: string; value?: string;
} }
export interface JsonSchemaData {
type: string;
default: any;
items?: JsonSchemaData;
properties?: any;
}
export interface JsonFormData { export interface JsonFormData {
type: string; type: string;
key: string | string[]; key: (string | number)[];
title: string; title: string;
readonly: boolean; readonly: boolean;
required: boolean; required: boolean;
@ -79,16 +86,15 @@ export interface JsonFormData {
rows?: number; rows?: number;
rowsMax?: number; rowsMax?: number;
placeholder?: string; placeholder?: string;
schema: { schema: JsonSchemaData;
type: string;
default: any;
};
titleMap: { titleMap: {
value: any; value: any;
name: string; name: string;
}[]; }[];
items?: Array<KeyLabelItem>; items?: Array<KeyLabelItem> | Array<JsonFormData>;
tabs?: Array<JsonFormData>;
tags?: any; tags?: any;
startEmpty?: boolean;
[key: string]: any; [key: string]: any;
} }
@ -99,17 +105,20 @@ export interface JsonFormFieldProps {
builder: (form: JsonFormData, builder: (form: JsonFormData,
model: any, model: any,
index: number, index: number,
onChange: (key: string | string[], val: any) => void, onChange: (key: (string | number)[], val: any) => void,
onColorClick: (event: MouseEvent, key: string, val: string) => void, onColorClick: (event: MouseEvent, key: string, val: string) => void,
onToggleFullscreen: () => void, onToggleFullscreen: () => void,
mapper: {[type: string]: any}) => JSX.Element; mapper: {[type: string]: any}) => JSX.Element;
mapper?: {[type: string]: any}; mapper?: {[type: string]: any};
onChange?: (key: string | string[], val: any) => void; onChange?: (key: (string | number)[], val: any) => void;
onColorClick?: (event: MouseEvent, key: string, val: string) => void; onColorClick?: (event: MouseEvent, key: string, val: string) => void;
onChangeValidate?: (e: any) => void; onChangeValidate?: (e: any) => void;
onToggleFullscreen?: () => void; onToggleFullscreen?: () => void;
valid?: boolean; valid?: boolean;
error?: string; error?: string;
options?: {
setSchemaDefaults?: boolean;
};
} }
export interface JsonFormFieldState { export interface JsonFormFieldState {

Loading…
Cancel
Save