47 | export class FieldRenderer extends React.PureComponent
{}
48 |
49 | export type KeyValue = { [key: string]: any }
50 |
51 | export function basePropTypes() {
52 | return {
53 | id: PropTypes.string.isRequired,
54 | className: PropTypes.string,
55 | data: PropTypes.any,
56 | errors: PropTypes.arrayOf(PropTypes.string),
57 | actions: PropTypes.objectOf(PropTypes.func),
58 | config: PropTypes.shape({
59 | type: PropTypes.string,
60 | }),
61 | disabled: PropTypes.bool,
62 | dirty: PropTypes.bool,
63 | };
64 | }
65 |
--------------------------------------------------------------------------------
/packages/core/src/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/schema#",
3 | "description": "(!! WORK IN PROGRESS !!) Language Independed UI definition schema",
4 | "type": "object",
5 | "properties": {
6 | "fields": {
7 | "type": "array",
8 | "items": {
9 | "title": "Field",
10 | "description": "Field definition",
11 | "type": "object",
12 | "properties": {
13 | "id": {
14 | "type": "string"
15 | },
16 | "renderer": {
17 | "oneOf": [
18 | { "$ref": "#/definitions/rendererSimple" },
19 | { "$ref": "#/definitions/rendererConfigurable" }
20 | ]
21 | },
22 | "serializer": {
23 | "type": [ "string", "null" ],
24 | "pattern": "^[^.]+(\\.[^.]+)*$"
25 | },
26 | "validator": {
27 | "oneOf": [
28 | { "$ref": "#/definitions/validatorSimple" },
29 | { "$ref": "#/definitions/validatorComplex" }
30 | ]
31 | },
32 | "actions": {
33 | "type": "object"
34 | }
35 | },
36 | "required": [ "id" ]
37 | }
38 | }
39 | },
40 | "definitions": {
41 | "rendererSimple": {
42 | "type": "string"
43 | },
44 | "rendererConfigurable": {
45 | "type": "object",
46 | "properties": {
47 | "type": { "type": "string" },
48 | "config": { "type": "object" }
49 | }
50 | },
51 | "validatorSimple": {
52 | "type": "string"
53 | },
54 | "validatorComplex": {
55 | "type": "object",
56 | "properties": {
57 | "rule": { "type": "string" },
58 | "value": {},
59 | "isAsync": { "type": "boolean" }
60 | },
61 | "required": [ "rule" ]
62 | }
63 | },
64 | "required": [ "fields" ],
65 | "additionalProperties": false
66 | }
67 |
--------------------------------------------------------------------------------
/packages/core/src/serializer.ts:
--------------------------------------------------------------------------------
1 | import { FormMetaDescription, KeyValue } from './interfaces';
2 |
3 | export function serializeToObject(formData: KeyValue): KeyValue {
4 | const serialized: KeyValue = {}
5 |
6 | for (let fieldId of Object.keys(formData)) {
7 | const fieldData = formData[fieldId]
8 | const value = fieldData.value;
9 |
10 | if (Array.isArray(value)) {
11 | serialized[fieldId] = value.map(serializeToObject);
12 | } else if (value && (typeof value === 'object')) {
13 | serialized[fieldId] = serializeToObject(value);
14 | } else {
15 | serialized[fieldId] = value;
16 | }
17 | }
18 |
19 | return serialized;
20 | }
21 |
22 | export function serializeToObject2(formMeta: FormMetaDescription, formData: KeyValue): KeyValue {
23 | const serialized: KeyValue = {}
24 |
25 | for (let { id} of formMeta.fields) {
26 | // const fieldData = formData[fieldId]
27 | // const value = fieldData.value;
28 |
29 | // if (Array.isArray(value)) {
30 | // serialized[fieldId] = value.map(serializeToObject);
31 | // } else if (value && (typeof value === 'object')) {
32 | // serialized[fieldId] = serializeToObject(value);
33 | // } else {
34 | // serialized[fieldId] = value;
35 | // }
36 | }
37 |
38 | return serialized;
39 | }
40 |
--------------------------------------------------------------------------------
/packages/core/src/utils.ts:
--------------------------------------------------------------------------------
1 | import {
2 | RawMetaDescription,
3 | FormMetaDescription,
4 | RawFieldMetaDescription,
5 | FieldMetaDescription,
6 | KeyValue
7 | } from './interfaces';
8 |
9 | function enhanceFormMeta(meta: RawMetaDescription): FormMetaDescription {
10 | const result: FormMetaDescription = {
11 | fields: []
12 | };
13 |
14 | for (let field of meta.fields) {
15 | result.fields.push(enhanceFieldMeta(field));
16 | }
17 |
18 | return result;
19 | }
20 |
21 | function enhanceFieldMeta(meta: RawFieldMetaDescription): FieldMetaDescription {
22 | return {
23 | id: meta.id,
24 | renderer: computeFieldRenderer(meta),
25 | actions: meta.actions ? { ...meta.actions } : {},
26 | hidden: meta.hidden || false,
27 | disabled: meta.disabled || false
28 | };
29 | }
30 |
31 | function computeFieldRenderer(meta: RawFieldMetaDescription) {
32 | const _renderer = { type: 'text', config: {} };
33 |
34 | if (meta.renderer && typeof meta.renderer === 'object') {
35 | return { ...meta.renderer };
36 | } else if (typeof meta.renderer === 'string') {
37 | _renderer.type = meta.renderer;
38 | }
39 |
40 | return _renderer;
41 | }
42 |
43 | function extractFieldActions(formActions: KeyValue, fieldActions: KeyValue) {
44 | let actions: KeyValue = {};
45 |
46 | for (let callbackName of Object.keys(fieldActions)) {
47 | const actionName = fieldActions[callbackName];
48 | const fn = formActions[actionName];
49 |
50 | if (fn) {
51 | actions[callbackName] = fn;
52 | }
53 | }
54 |
55 | return actions;
56 | }
57 |
58 | /**
59 | * Completes `data` object with default values for known types of renderers.
60 | * Default values for custom renderers can be provided with `defaults` argument.
61 | */
62 | function withDefaults(
63 | data: KeyValue = {},
64 | fieldsMeta: FieldMetaDescription[] = [],
65 | defaults: KeyValue = {}
66 | ): KeyValue {
67 | const _defaults: KeyValue = {
68 | form: {},
69 | list: [],
70 |
71 | checkbox: false,
72 | radiogroup: '',
73 | select: null,
74 | multiple: [],
75 | text: '',
76 | textarea: '',
77 | date: null,
78 | upload: null,
79 |
80 | ...defaults
81 | };
82 |
83 | const resultData = { ...data };
84 |
85 | for (let fieldMeta of fieldsMeta) {
86 | const { id, renderer: { type } } = fieldMeta;
87 | const dataValue = data[id];
88 | let defaultValue;
89 |
90 | if (dataValue === undefined) {
91 | defaultValue = _defaults[type];
92 |
93 | if (defaultValue !== undefined) {
94 | const value =
95 | type === 'list'
96 | ? [withDefaults({}, fieldMeta.renderer.config.fields)] // and what about "form"?
97 | : defaultValue;
98 |
99 | resultData[id] = value;
100 | }
101 | }
102 | }
103 |
104 | return resultData;
105 | }
106 |
107 | function findFieldIdx(fieldId: string, fields: JSX.Element[]) {
108 | return fields.findIndex(({ props }) => props.id === fieldId);
109 | }
110 |
111 | function findFieldMetaById(
112 | fieldId: string,
113 | fieldsMeta: FieldMetaDescription[] = []
114 | ): FieldMetaDescription {
115 | return fieldsMeta.find(meta => meta.id === fieldId);
116 | }
117 |
118 | function makeDirty (data: KeyValue | KeyValue[]): KeyValue {
119 | if (Array.isArray(data)) {
120 | return data.map(makeDirty)
121 | }
122 |
123 | return Object.entries(data).reduce((acc, [key, value]) => {
124 | return { ...acc, [key]: (value && typeof value === 'object') ? makeDirty(value) : true }
125 | }, {})
126 | }
127 |
128 | export {
129 | enhanceFormMeta,
130 | enhanceFieldMeta,
131 | extractFieldActions,
132 | withDefaults,
133 | findFieldIdx,
134 | findFieldMetaById,
135 | makeDirty,
136 | };
137 |
--------------------------------------------------------------------------------
/packages/core/src/validator.ts:
--------------------------------------------------------------------------------
1 | import { KeyValue } from './interfaces';
2 |
3 | export interface ExternalValidator {
4 | (schema: KeyValue, data: KeyValue): ValidationResult;
5 | }
6 |
7 | export interface Validator {
8 | (formValue: KeyValue): ValidationResult;
9 | }
10 |
11 | export interface ValidationResult {
12 | errors: { [key: string]: string[] };
13 | isValid: boolean;
14 | }
15 |
16 | export function buildValidator(
17 | validatorFn: ExternalValidator,
18 | schema: KeyValue
19 | ): Validator {
20 | return (formValue: KeyValue): ValidationResult => {
21 | const { errors, isValid } = validatorFn(schema, formValue);
22 | const errorsByFields: KeyValue = {};
23 |
24 | for (let fieldId of Object.keys(formValue)) {
25 | errorsByFields[fieldId] = errors[fieldId] || []; // empty array for valid fields
26 | }
27 |
28 | return { isValid, errors: errorsByFields };
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./out/",
4 | "declarationDir": "./out/",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "moduleResolution": "node",
8 | "jsx": "react",
9 | "esModuleInterop": true,
10 | "allowSyntheticDefaultImports": true,
11 | "declaration": true,
12 | "sourceMap": true,
13 | "noImplicitAny": true
14 | },
15 | "include": ["./src/**/*"]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/demo/config/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const ProgressBarPlugin = require('progress-bar-webpack-plugin');
4 |
5 | const resolve = dir => path.join(__dirname, '../', dir);
6 |
7 | module.exports = {
8 | entry: {
9 | app: resolve('src/index.jsx')
10 | },
11 |
12 | output: {
13 | path: resolve('out'),
14 | filename: '[name].js'
15 | },
16 |
17 | resolve: {
18 | modules: [resolve('src'), 'node_modules'],
19 | extensions: ['.js', '.jsx', '.json'],
20 |
21 | alias: {
22 | '@': resolve('src'),
23 | '@components': resolve('src/components'),
24 | '@containers': resolve('src/containers'),
25 | '@meta': resolve('src/meta'),
26 | '@validation': resolve('src/validation'),
27 | '@actions': resolve('src/actions')
28 | }
29 | },
30 |
31 | module: {
32 | rules: [
33 | {
34 | test: /\.jsx?$/,
35 | include: [resolve('src')],
36 | loader: 'babel-loader',
37 |
38 | options: {
39 | presets: ['@babel/preset-env', '@babel/preset-react']
40 | }
41 | },
42 |
43 | {
44 | test: /\.css$/i,
45 | use: ['style-loader', 'css-loader'],
46 | }
47 | ]
48 | },
49 |
50 | plugins: [
51 | new ProgressBarPlugin(),
52 |
53 | new HtmlWebpackPlugin({
54 | title: 'react-form-generator demo',
55 | inject: true,
56 | template: resolve('src/index.ejs')
57 | })
58 | ]
59 | };
60 |
--------------------------------------------------------------------------------
/packages/demo/config/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const webpackMerge = require('webpack-merge');
4 | const Visualizer = require('webpack-visualizer-plugin');
5 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
6 | const baseConfig = require('./webpack.common.js')
7 |
8 | const resolve = dir => path.join(__dirname, '../', dir);
9 |
10 | module.exports = webpackMerge(baseConfig, {
11 | devServer: {
12 | contentBase: resolve('out'),
13 | compress: true,
14 | host: '0.0.0.0',
15 | port: 9007,
16 | historyApiFallback: true,
17 | hot: false,
18 | inline: true,
19 | https: false,
20 | noInfo: true,
21 | open: false
22 | },
23 |
24 | devtool: 'inline-source-map',
25 | mode: 'development',
26 |
27 | plugins: [
28 | new webpack.HotModuleReplacementPlugin(),
29 |
30 | new Visualizer({
31 | filename: './statistics.html'
32 | }),
33 |
34 | new BundleAnalyzerPlugin({ analyzerMode: 'server' }),
35 | ]
36 | });
37 |
--------------------------------------------------------------------------------
/packages/demo/config/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpackMerge = require('webpack-merge');
3 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
4 | const Visualizer = require('webpack-visualizer-plugin');
5 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
6 | const baseConfig = require('./webpack.common.js')
7 |
8 |
9 | module.exports = webpackMerge(baseConfig, {
10 | output: {
11 | sourceMapFilename: '[file].map',
12 | },
13 |
14 | devtool: 'source-map',
15 | mode: 'production',
16 |
17 | plugins: [
18 | new UglifyJsPlugin({
19 | sourceMap: true,
20 | parallel: 4,
21 | }),
22 |
23 | new Visualizer({
24 | filename: './statistics.html'
25 | }),
26 |
27 | new BundleAnalyzerPlugin({ analyzerMode: 'static' }),
28 | ]
29 | });
30 |
--------------------------------------------------------------------------------
/packages/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-ui-generator/demo",
3 | "private": true,
4 | "version": "0.7.5",
5 | "description": "Interactive examples for react-ui-generator",
6 | "main": "out/index.js",
7 | "repository": "https://github.com/react-ui-generator/react-ui-generator/packages/demo",
8 | "scripts": {
9 | "test": "echo \"Error: no test specified\" && exit 1",
10 | "build": "yarn run webpack-cli --config ./config/webpack.common.js --mode development",
11 | "build:prod": "yarn run webpack-cli --config ./config/webpack.prod.js",
12 | "start": "yarn run webpack-dev-server --config ./config/webpack.dev.js"
13 | },
14 | "author": "Alexei Zaviruha ",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@babel/core": "^7.5.5",
18 | "@babel/preset-env": "^7.0.0",
19 | "@babel/preset-react": "^7.0.0",
20 | "@types/jest": "^24.0.15",
21 | "@types/lodash-es": "^4.17.3",
22 | "babel-jest": "^24.8.0",
23 | "babel-loader": "^8.0.6",
24 | "babel-upgrade": "^0.0.24",
25 | "css-loader": "^3.2.0",
26 | "html-webpack-plugin": "^3.2.0",
27 | "jest": "^24.8.0",
28 | "progress-bar-webpack-plugin": "^1.11.0",
29 | "react-hot-loader": "^3.1.3",
30 | "source-map-explorer": "^1.5.0",
31 | "source-map-loader": "^0.2.3",
32 | "style-loader": "^1.0.0",
33 | "ts-jest": "^24.0.2",
34 | "typescript": "^3.5.3",
35 | "uglifyjs-webpack-plugin": "^1.2.5",
36 | "webpack": "^4.36.1",
37 | "webpack-bundle-analyzer": "^3.6.0",
38 | "webpack-cleanup-plugin": "^0.5.1",
39 | "webpack-cli": "^3.3.6",
40 | "webpack-dev-server": "^3.7.2",
41 | "webpack-merge": "^4.1.2",
42 | "webpack-visualizer-plugin": "^0.1.11"
43 | },
44 | "dependencies": {
45 | "@react-ui-generator/antd": "^0.7.5",
46 | "@react-ui-generator/bootstrap": "^0.7.5",
47 | "@react-ui-generator/core": "^0.7.5",
48 | "@react-ui-generator/serializers": "^0.5.3",
49 | "@react-ui-generator/validators": "^0.7.5",
50 | "ajv": "^6.0.0",
51 | "antd": "^3.24.2",
52 | "deepmerge": "^2.0.1",
53 | "history": "^4.7.2",
54 | "lodash-es": "^4.17.15",
55 | "moment": "^2.22.1",
56 | "react": "^16.3.2",
57 | "react-dom": "^16.3.2",
58 | "react-redux": "^5.0.6",
59 | "redux": "^3.7.2",
60 | "redux-thunk": "^2.2.0"
61 | },
62 | "gitHead": "f34904c44b092961eef4fa3e5fc9b62b18628838"
63 | }
64 |
--------------------------------------------------------------------------------
/packages/demo/src/actions.js:
--------------------------------------------------------------------------------
1 | import { buildJSONSerializer } from '@react-ui-generator/serializers';
2 |
3 | export const UPDATE_FORM = 'UPDATE_FORM';
4 | export const TOGGLE_SEX = 'TOGGLE_SEX';
5 | export const FORM_SENDING_START = 'FORM_SENDING_START';
6 | export const FORM_SENDING_FINISH = 'FORM_SENDING_FINISH';
7 | export const CLEAR_FORM = 'CLEAR_FORM';
8 | export const ADD_RELATIVE = 'ADD_RELATIVE';
9 | export const REMOVE_RELATIVE = 'REMOVE_RELATIVE';
10 |
11 | export const toggleSex = payload => ({ type: TOGGLE_SEX, payload });
12 | export const updateForm = payload => ({ type: UPDATE_FORM, payload });
13 | export const clearForm = () => ({ type: CLEAR_FORM });
14 |
15 | export const sendForm = () => (dispatch, getState) => {
16 | dispatch({ type: FORM_SENDING_START });
17 |
18 | const serialize = buildJSONSerializer();
19 | const { data, isValid } = getState();
20 | const serializedForm = serialize(data);
21 |
22 | if (isValid) {
23 | setTimeout(() => {
24 | console.log('Serialized data was successfully sent: ', serializedForm);
25 | dispatch({ type: FORM_SENDING_FINISH });
26 | }, 2000);
27 | } else {
28 | // TODO: add form dirtyfying
29 | console.error('Form is invalid');
30 | }
31 | };
32 |
33 | export const addRelative = () => ({ type: ADD_RELATIVE });
34 | export const removeRelative = idx => ({ type: REMOVE_RELATIVE, payload: idx });
35 |
--------------------------------------------------------------------------------
/packages/demo/src/app.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 | import { createStore, applyMiddleware, compose } from 'redux';
4 | import thunkMiddleware from 'redux-thunk';
5 | import createHistory from 'history/createBrowserHistory';
6 |
7 | import reducers from './reducers';
8 | import GeneratedFormExample from '@containers/GeneratedFormExample';
9 |
10 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
11 |
12 | const store = createStore(reducers, composeEnhancers(applyMiddleware(thunkMiddleware)));
13 |
14 | class App extends React.Component {
15 | render() {
16 | return (
17 |
18 |
47 |
48 | );
49 | }
50 | }
51 |
52 | export default App;
53 |
--------------------------------------------------------------------------------
/packages/demo/src/components/CloseButton.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import makeClass from 'classnames';
3 |
4 | class CloseButton extends React.PureComponent {
5 | render() {
6 | const {
7 | actions: { onClick },
8 | disabled,
9 | className
10 | } = this.props;
11 |
12 | return (
13 |
21 | );
22 | }
23 | }
24 |
25 | export default CloseButton;
26 |
--------------------------------------------------------------------------------
/packages/demo/src/containers/GeneratedFormExample.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import Ajv from 'ajv';
4 | import { Form, Row, Col } from 'antd';
5 |
6 | import {
7 | GeneratedForm,
8 | Field,
9 | Fields,
10 | } from '@react-ui-generator/core';
11 |
12 | import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
13 |
14 |
15 | import { buildAjvValidator } from '@react-ui-generator/validators';
16 | import { Renderers, Layouts } from '@react-ui-generator/antd';
17 |
18 | import {
19 | toggleSex,
20 | updateForm,
21 | sendForm,
22 | clearForm,
23 | addRelative,
24 | removeRelative
25 | } from '@actions';
26 |
27 | import CloseButton from '../components/CloseButton';
28 | import validationSchema from '../validation/jsonSchema.json';
29 |
30 | /**
31 | * You can add custon renderers here.
32 | */
33 | const renderers = {
34 | ...Renderers,
35 | closeButton: CloseButton,
36 | };
37 |
38 | const { FormLayout, FieldLayout } = Layouts;
39 |
40 | class GeneratedFormExample extends React.PureComponent {
41 | render() {
42 | const { meta, data, errors, dirtiness, updateForm, ...actions } = this.props;
43 |
44 | return (
45 | updateForm({ data, errors, isValid, dirtiness })}
54 | validator={buildAjvValidator(Ajv, validationSchema)}
55 | >
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | );
104 | }
105 | }
106 |
107 | export default connect(
108 | state => state,
109 | dispatch => ({
110 | updateForm: payload => dispatch(updateForm(payload)),
111 | toggleSex: fieldId => dispatch(toggleSex(fieldId)),
112 | sendForm: () => dispatch(sendForm()),
113 | clearForm: () => dispatch(clearForm()),
114 | addRelative: () => dispatch(addRelative()),
115 | removeRelative: (...payload) => dispatch(removeRelative(...payload))
116 | })
117 | )(GeneratedFormExample);
118 |
--------------------------------------------------------------------------------
/packages/demo/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
13 |
14 |
15 |
16 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/packages/demo/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './app'
4 |
5 | ReactDOM.render(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/packages/demo/src/meta/complete.json:
--------------------------------------------------------------------------------
1 | {
2 | "fields": [
3 | {
4 | "id": "email",
5 | "renderer": {
6 | "type": "text",
7 | "config": {
8 | "label": "E-mail",
9 | "placeholder": "Enter your e-mail",
10 | "showAsterix": true
11 | }
12 | },
13 | "serializer": "auth.email",
14 | "hidden": false
15 | },
16 |
17 | {
18 | "id": "password",
19 | "renderer": {
20 | "type": "password",
21 | "config": {
22 | "label": "Password",
23 | "placeholder": "Enter your password",
24 | "showAsterix": true
25 | }
26 | },
27 | "serializer": "auth.password"
28 | },
29 |
30 | {
31 | "id": "confirmation",
32 | "renderer": {
33 | "type": "password",
34 | "config": {
35 | "label": "Confirmation",
36 | "placeholder": "Enter your password again",
37 | "showAsterix": true
38 | }
39 | },
40 | "serializer": null
41 | },
42 |
43 | {
44 | "id": "birthdate",
45 | "renderer": {
46 | "type": "date",
47 | "config": {
48 | "label": "Birthday",
49 | "placeholder": "Enter your birthday",
50 | "showAsterix": true,
51 | "format": "DD.MM.YYYY"
52 | }
53 | }
54 | },
55 |
56 | {
57 | "id": "sex",
58 | "renderer": {
59 | "type": "select",
60 | "config": {
61 | "label": "Gender",
62 | "placeholder": "Choose your gender",
63 | "allowClear": true,
64 | "showAsterix": true,
65 | "options": [
66 | { "id": "female", "title": "Female" },
67 | { "id": "male", "title": "Male" },
68 | { "id": "dontknow", "title": "I don't know" }
69 | ]
70 | }
71 | }
72 | },
73 |
74 | {
75 | "id": "pl",
76 | "renderer": {
77 | "type": "multiple",
78 | "config": {
79 | "label": "Favorite programming language",
80 | "placeholder": "Choose your favorite programming language",
81 | "allowClear": true,
82 | "showAsterix": true,
83 | "options": [
84 | { "id": "elm", "title": "Elm" },
85 | { "id": "javascript", "title": "JavaScript" },
86 | { "id": "purescript", "title": "PureScript" },
87 | { "id": "reasonml", "title": "ReasonML" },
88 | { "id": "typescript", "title": "TypeScript" },
89 | { "id": "other", "title": "other" }
90 | ]
91 | }
92 | }
93 | },
94 |
95 | {
96 | "id": "isMarried",
97 | "renderer": {
98 | "type": "checkbox",
99 | "config": {
100 | "label": "Are you married?",
101 | "title": "Married",
102 | "showAsterix": true
103 | }
104 | }
105 | },
106 |
107 | {
108 | "id": "aboutMe",
109 | "renderer": {
110 | "type": "textarea",
111 | "config": {
112 | "label": "Short bio",
113 | "placeholder": "Write your short biography here...",
114 | "showAsterix": true,
115 | "rows": 2
116 | }
117 | },
118 | "hidden": false
119 | },
120 |
121 | {
122 | "id": "answer",
123 | "renderer": {
124 | "type": "radiogroup",
125 | "config": {
126 | "label": "What is the only correct answer from this list?",
127 | "showAsterix": true,
128 | "options": [
129 | { "id": 42, "title": "42" },
130 | { "id": 43, "title": "43" }
131 | ]
132 | }
133 | }
134 | },
135 |
136 | {
137 | "id": "cv",
138 | "renderer": {
139 | "type": "upload",
140 | "config": {
141 | "label": "Upload your CV",
142 | "url": "//jsonplaceholder.typicode.com/posts/",
143 | "responsePath": "id",
144 | "showAsterix": true
145 | }
146 | }
147 | },
148 |
149 | {
150 | "id": "relatives",
151 | "serializer": "additional.relatives",
152 | "renderer": {
153 | "type": "list",
154 | "config": {
155 | "label": "Add information about your relatives",
156 | "fields": [
157 | {
158 | "id": "firstName",
159 | "renderer": {
160 | "type": "text",
161 | "config": {
162 | "label": "First name",
163 | "placeholder": "First name",
164 | "showAsterix": true
165 | }
166 | }
167 | },
168 |
169 | {
170 | "id": "lastName",
171 | "renderer": {
172 | "type": "text",
173 | "config": {
174 | "label": "Last name",
175 | "placeholder": "Last name",
176 | "showAsterix": true
177 | }
178 | }
179 | },
180 |
181 | {
182 | "id": "btnRemoveRelative",
183 | "renderer": "closeButton",
184 | "actions": {
185 | "onClick": "removeRelative"
186 | }
187 | }
188 | ]
189 | }
190 | }
191 | },
192 |
193 | {
194 | "id": "btnAddRelative",
195 | "renderer": {
196 | "type": "button",
197 | "config": {
198 | "title": "Add relatives",
199 | "color": "primary",
200 | "outline": true
201 | }
202 | },
203 | "actions": {
204 | "onClick": "addRelative"
205 | }
206 | },
207 |
208 | {
209 | "id": "btnSend",
210 | "renderer": {
211 | "type": "button",
212 | "config": {
213 | "title": "Send",
214 | "color": "primary"
215 | }
216 | },
217 | "actions": {
218 | "onClick": "sendForm"
219 | }
220 | },
221 |
222 | {
223 | "id": "btnClear",
224 | "renderer": {
225 | "type": "button",
226 | "config": {
227 | "title": "Clear",
228 | "color": "primary",
229 | "outline": true
230 | }
231 | },
232 | "actions": {
233 | "onClick": "clearForm"
234 | }
235 | }
236 | ]
237 | }
238 |
--------------------------------------------------------------------------------
/packages/demo/src/meta/complete_bootstrap.json:
--------------------------------------------------------------------------------
1 | {
2 | "fields": [
3 | {
4 | "id": "email",
5 | "renderer": {
6 | "type": "text",
7 | "config": {
8 | "label": "E-mail",
9 | "placeholder": "Enter your e-mail"
10 | }
11 | },
12 | "serializer": "auth.email",
13 | "hidden": false
14 | },
15 |
16 | {
17 | "id": "password",
18 | "renderer": {
19 | "type": "text",
20 | "config": {
21 | "label": "Password",
22 | "placeholder": "Enter your password",
23 | "isPassword": true
24 | }
25 | },
26 | "serializer": "auth.password"
27 | },
28 |
29 | {
30 | "id": "confirmation",
31 | "renderer": {
32 | "type": "text",
33 | "config": {
34 | "label": "Confirmation",
35 | "placeholder": "Enter your password again",
36 | "isPassword": true
37 | }
38 | },
39 | "serializer": null
40 | },
41 |
42 |
43 | {
44 | "id": "sex",
45 | "renderer": {
46 | "type": "select",
47 | "config": {
48 | "label": "Gender",
49 | "title": "Choose your gender",
50 | "options": [
51 | { "id": "male", "title": "Female" },
52 | { "id": "female", "title": "Male" },
53 | { "id": "dontknow", "title": "I don't know" }
54 | ]
55 | }
56 | }
57 | },
58 |
59 | {
60 | "id": "isMarried",
61 | "renderer": {
62 | "type": "checkbox",
63 | "config": {
64 | "label": "Are you married?",
65 | "title": "Married"
66 | }
67 | }
68 | },
69 |
70 | {
71 | "id": "aboutMe",
72 | "renderer": {
73 | "type": "textarea",
74 | "config": {
75 | "label": "Short bio",
76 | "placeholder": "Write your short biography here...",
77 | "rows": 2
78 | }
79 | },
80 | "hidden": false
81 | },
82 |
83 | {
84 | "id": "answer",
85 | "renderer": {
86 | "type": "radiogroup",
87 | "config": {
88 | "label": "What is the only correct answer from this list?",
89 | "options": [
90 | { "id": 42, "title": "42" },
91 | { "id": 43, "title": "43" }
92 | ]
93 | }
94 | }
95 | },
96 |
97 | {
98 | "id": "relatives",
99 | "serializer": "additional.relatives",
100 | "renderer": {
101 | "type": "list",
102 | "config": {
103 | "label": "Add information about your relatives",
104 | "fields": [
105 | {
106 | "id": "firstName",
107 | "renderer": {
108 | "type": "text",
109 | "config": {
110 | "label": "First name",
111 | "placeholder": "First name"
112 | }
113 | }
114 | },
115 |
116 | {
117 | "id": "lastName",
118 | "renderer": {
119 | "type": "text",
120 | "config": {
121 | "label": "Last name",
122 | "placeholder": "Last name"
123 | }
124 | }
125 | },
126 |
127 | {
128 | "id": "btnRemoveRelative",
129 | "renderer": "closeButton",
130 | "actions": {
131 | "onClick": "removeRelative"
132 | }
133 | }
134 | ]
135 | }
136 | }
137 | },
138 |
139 | {
140 | "id": "btnAddRelative",
141 | "renderer": {
142 | "type": "button",
143 | "config": {
144 | "title": "Add relatives",
145 | "color": "primary",
146 | "outline": true
147 | }
148 | },
149 | "actions": {
150 | "onClick": "addRelative"
151 | }
152 | },
153 |
154 | {
155 | "id": "btnSend",
156 | "renderer": {
157 | "type": "button",
158 | "config": {
159 | "title": "Send",
160 | "color": "primary"
161 | }
162 | },
163 | "actions": {
164 | "onClick": "sendForm"
165 | }
166 | },
167 |
168 | {
169 | "id": "btnClear",
170 | "renderer": {
171 | "type": "button",
172 | "config": {
173 | "title": "Clear",
174 | "color": "primary",
175 | "outline": true
176 | }
177 | },
178 | "actions": {
179 | "onClick": "clearForm"
180 | }
181 | }
182 | ]
183 | }
184 |
--------------------------------------------------------------------------------
/packages/demo/src/reducers.js:
--------------------------------------------------------------------------------
1 | import deepmerge from 'deepmerge';
2 |
3 | import {
4 | withDefaults,
5 | findFieldMetaById,
6 | } from '@react-ui-generator/core';
7 |
8 | import {
9 | UPDATE_FORM,
10 | CLEAR_FORM,
11 | FORM_SENDING_START,
12 | FORM_SENDING_FINISH,
13 | ADD_RELATIVE,
14 | REMOVE_RELATIVE
15 | } from '@actions';
16 |
17 | const meta = require('@meta/complete');
18 | const initialState = {
19 | meta,
20 | data: withDefaults({}, meta.fields),
21 | errors: {},
22 | dirtiness: {},
23 | isValid: false,
24 | };
25 |
26 | function reducer(state = initialState, { type: actionType, payload }) {
27 | switch (actionType) {
28 | case UPDATE_FORM: {
29 | return merge(state, payload);
30 | }
31 |
32 | case CLEAR_FORM: {
33 | return { ...initialState };
34 | }
35 |
36 | case FORM_SENDING_START: {
37 | return { ...state, isFetching: true };
38 | }
39 |
40 | case FORM_SENDING_FINISH: {
41 | return { ...state, isFetching: false };
42 | }
43 |
44 | case ADD_RELATIVE: {
45 | const subFormMeta = findFieldMetaById('relatives', state.meta.fields);
46 |
47 | const newState = merge(state, {
48 | data: {
49 | relatives: [
50 | ...state.data.relatives,
51 | withDefaults({}, subFormMeta.renderer.config.fields)
52 | ]
53 | }
54 | });
55 |
56 | return newState;
57 | }
58 |
59 | case REMOVE_RELATIVE: {
60 | const idx = payload;
61 | const subFormMeta = findFieldMetaById('relatives', state.meta.fields);
62 | const newValue = [ ...state.data.relatives ];
63 |
64 | newValue.splice(idx, 1);
65 |
66 | const newState = merge(state, {
67 | data: {
68 | relatives: newValue
69 | }
70 | });
71 |
72 | return newState;
73 | }
74 |
75 | default:
76 | return state;
77 | }
78 | }
79 |
80 | export default reducer;
81 |
82 |
83 | function overwriteMerge(destArray, sourceArray, options) {
84 | return sourceArray;
85 | }
86 |
87 | function merge(dest, source) {
88 | return deepmerge(dest, source, { arrayMerge: overwriteMerge });
89 | }
--------------------------------------------------------------------------------
/packages/demo/src/validation/jsonSchema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/schema#",
3 | "description": "Validation schema for the Registration form",
4 | "type": "object",
5 | "properties": {
6 | "email": {
7 | "type": "string",
8 | "format": "email"
9 | },
10 | "password": {
11 | "type": "string",
12 | "pattern": "^[A-Za-z0-9#$.,_-]*$",
13 | "minLength": 5,
14 | "maxLength": 15
15 | },
16 | "confirmation": {
17 | "const": { "$data": "1/password" }
18 | },
19 | "sex": {
20 | "enum": ["male", "female"]
21 | },
22 | "pl": {
23 | "type": "array",
24 | "items": {
25 | "type": "string"
26 | },
27 | "minItems": 1
28 | },
29 | "isMarried": {
30 | "_type": "boolean",
31 | "const": true
32 | },
33 | "aboutMe": {
34 | "type": "string",
35 | "minLength": 20
36 | },
37 | "relatives": {
38 | "type": "array",
39 | "items": {
40 | "title": "Relative",
41 | "description": "Relative definition",
42 | "type": "object",
43 | "properties": {
44 | "firstName": {
45 | "type": "string",
46 | "pattern": "^[A-Z]+([A-Za-z]+[ -]?)+$"
47 | },
48 | "lastName": {
49 | "type": "string",
50 | "pattern": "^[A-Z]+([A-Za-z]+[ -]?)+$"
51 | }
52 | },
53 | "required": ["firstName", "lastName"]
54 | }
55 | },
56 | "answer": {
57 | "const": 42
58 | }
59 | },
60 | "required": ["email", "password", "pl", "answer"]
61 | }
--------------------------------------------------------------------------------
/packages/metaphor/README.md:
--------------------------------------------------------------------------------
1 | # Metaphor
2 |
3 | DSL for easing baking of metadata for the react-ui-generator.
4 |
5 | ## Example
6 |
7 | ```js
8 | import Metaphor from '@react-ui-generator/metaphor';
9 | import BaseMeta from './meta.json';
10 |
11 | const formToView = new Metaphor(BaseMeta)
12 | .showAll()
13 | .disableAll()
14 | .value();
15 |
16 | const formToEdit = new Metaphor(BaseMeta)
17 | .enableAll()
18 | .disable(['id', 'createdAt', 'author'])
19 | .config
20 | .set('email', { showAsterix: true })
21 | .set(['birthDate', 'employmentDate'], {
22 | showAsterix: true,
23 | format: 'DD.MM.YYYY',
24 | })
25 | .up()
26 | .actions
27 | .set(['btnSave'], { onClick: 'sendFormToServer' })
28 | .set('btnCancel', { onClick: isFormChanged() ? 'confirmEditCancellation' : 'closeForm' })
29 | .up()
30 | .hide(calcSecretFieldsByUserRole(), true)
31 | .value();
32 | ```
33 |
--------------------------------------------------------------------------------
/packages/metaphor/__tests__/mocks/minimal.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | "fields": [
3 | { "id": "foo" },
4 | { "id": "bar" },
5 | { "id": "baz" },
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/packages/metaphor/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 |
4 | const presets = ['@babel/preset-env'];
5 | const plugins = [];
6 |
7 | return {
8 | presets,
9 | plugins
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/packages/metaphor/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strinc'
2 |
3 | const path = require('path')
4 | const fs = require('fs')
5 |
6 | const appDirectory = fs.realpathSync(process.cwd())
7 | const resolve = relativePath => path.resolve(appDirectory, relativePath)
8 |
9 | module.exports = {
10 | root: resolve(''),
11 | src: resolve('src'),
12 | tests: resolve('__tests__'),
13 | tsConfig: resolve('tsconfig.json'),
14 | }
15 |
--------------------------------------------------------------------------------
/packages/metaphor/config/tests/__mocks__/fileMock.js:
--------------------------------------------------------------------------------
1 | module.exports = 'test-file-stub';
2 |
--------------------------------------------------------------------------------
/packages/metaphor/config/tests/jest.config.js:
--------------------------------------------------------------------------------
1 | const paths = require('../paths');
2 |
3 | module.exports = {
4 | verbose: true,
5 | rootDir: paths.root,
6 | preset: 'ts-jest',
7 | testRegex: '(/__tests__/.*(test|spec))\\.tsx?$',
8 | moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'],
9 | moduleNameMapper: {
10 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
11 | '/config/tests/__mocks__/fileMock.js',
12 | '\\.(css|scss)$': 'identity-obj-proxy'
13 | },
14 | transform: {
15 | '^.+\\.js$': 'babel-jest'
16 | },
17 | transformIgnorePatterns: ['/node_modules/(?!lodash-es)'],
18 | };
19 |
--------------------------------------------------------------------------------
/packages/metaphor/config/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { TsConfigPathsPlugin } = require('awesome-typescript-loader');
2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
3 | const path = require('path');
4 |
5 | const resolve = dir => path.join(__dirname, '..', dir);
6 |
7 | module.exports = {
8 | entry: resolve('src/index.ts'),
9 | output: {
10 | filename: 'metaphor.js',
11 | path: resolve('out'),
12 | libraryTarget: 'umd'
13 | },
14 |
15 | devtool: 'source-map',
16 | mode: 'production',
17 |
18 | resolve: {
19 | extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
20 | modules: [resolve('src'), 'node_modules'],
21 | plugins: [new TsConfigPathsPlugin({})]
22 | },
23 |
24 | module: {
25 | rules: [
26 | {
27 | test: /\.tsx?$/,
28 | use: ['babel-loader', 'awesome-typescript-loader'],
29 | exclude: /node_modules/
30 | },
31 |
32 | // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
33 | {
34 | enforce: 'pre',
35 | test: /\.js$/,
36 | loader: 'source-map-loader'
37 | }
38 | ]
39 | },
40 |
41 | externals: [/^lodash-es(\/.+)?$/],
42 |
43 | plugins: [new CleanWebpackPlugin()]
44 | };
45 |
--------------------------------------------------------------------------------
/packages/metaphor/docs/assets/images/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/true-brains/react-ui-generator/6cda840f55111872b91333c26abb1c8e8c9f6879/packages/metaphor/docs/assets/images/icons.png
--------------------------------------------------------------------------------
/packages/metaphor/docs/assets/images/icons@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/true-brains/react-ui-generator/6cda840f55111872b91333c26abb1c8e8c9f6879/packages/metaphor/docs/assets/images/icons@2x.png
--------------------------------------------------------------------------------
/packages/metaphor/docs/assets/images/widgets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/true-brains/react-ui-generator/6cda840f55111872b91333c26abb1c8e8c9f6879/packages/metaphor/docs/assets/images/widgets.png
--------------------------------------------------------------------------------
/packages/metaphor/docs/assets/images/widgets@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/true-brains/react-ui-generator/6cda840f55111872b91333c26abb1c8e8c9f6879/packages/metaphor/docs/assets/images/widgets@2x.png
--------------------------------------------------------------------------------
/packages/metaphor/docs/assets/js/search.js:
--------------------------------------------------------------------------------
1 | var typedoc = typedoc || {};
2 | typedoc.search = typedoc.search || {};
3 | typedoc.search.data = {"kinds":{"1":"External module","64":"Function","128":"Class","256":"Interface","512":"Constructor","1024":"Property","2048":"Method","262144":"Accessor","4194304":"Type alias"},"rows":[{"id":0,"kind":1,"name":"\"lib/Metaphor\"","url":"modules/_lib_metaphor_.html","classes":"tsd-kind-external-module"},{"id":1,"kind":256,"name":"IdsToProcess","url":"interfaces/_lib_metaphor_.idstoprocess.html","classes":"tsd-kind-interface tsd-parent-kind-external-module","parent":"\"lib/Metaphor\""},{"id":2,"kind":128,"name":"Metaphor","url":"classes/_lib_metaphor_.metaphor.html","classes":"tsd-kind-class tsd-parent-kind-external-module","parent":"\"lib/Metaphor\""},{"id":3,"kind":1024,"name":"meta","url":"classes/_lib_metaphor_.metaphor.html#meta","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"\"lib/Metaphor\".Metaphor"},{"id":4,"kind":512,"name":"constructor","url":"classes/_lib_metaphor_.metaphor.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":5,"kind":2048,"name":"togglePropByFieldIds","url":"classes/_lib_metaphor_.metaphor.html#togglepropbyfieldids","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":6,"kind":2048,"name":"value","url":"classes/_lib_metaphor_.metaphor.html#value","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":7,"kind":2048,"name":"get","url":"classes/_lib_metaphor_.metaphor.html#get","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":8,"kind":2048,"name":"set","url":"classes/_lib_metaphor_.metaphor.html#set","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":9,"kind":2048,"name":"show","url":"classes/_lib_metaphor_.metaphor.html#show","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":10,"kind":2048,"name":"showAll","url":"classes/_lib_metaphor_.metaphor.html#showall","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":11,"kind":2048,"name":"hide","url":"classes/_lib_metaphor_.metaphor.html#hide","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":12,"kind":2048,"name":"hideAll","url":"classes/_lib_metaphor_.metaphor.html#hideall","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":13,"kind":2048,"name":"enable","url":"classes/_lib_metaphor_.metaphor.html#enable","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":14,"kind":2048,"name":"enableAll","url":"classes/_lib_metaphor_.metaphor.html#enableall","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":15,"kind":2048,"name":"disable","url":"classes/_lib_metaphor_.metaphor.html#disable","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":16,"kind":2048,"name":"disableAll","url":"classes/_lib_metaphor_.metaphor.html#disableall","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":17,"kind":2048,"name":"clone","url":"classes/_lib_metaphor_.metaphor.html#clone","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":18,"kind":2048,"name":"add","url":"classes/_lib_metaphor_.metaphor.html#add","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":19,"kind":2048,"name":"remove","url":"classes/_lib_metaphor_.metaphor.html#remove","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":20,"kind":262144,"name":"config","url":"classes/_lib_metaphor_.metaphor.html#config","classes":"tsd-kind-get-signature tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":21,"kind":262144,"name":"actions","url":"classes/_lib_metaphor_.metaphor.html#actions","classes":"tsd-kind-get-signature tsd-parent-kind-class","parent":"\"lib/Metaphor\".Metaphor"},{"id":22,"kind":4194304,"name":"FieldBooleanProps","url":"modules/_lib_metaphor_.html#fieldbooleanprops","classes":"tsd-kind-type-alias tsd-parent-kind-external-module","parent":"\"lib/Metaphor\""},{"id":23,"kind":64,"name":"notFoundMessage","url":"modules/_lib_metaphor_.html#notfoundmessage","classes":"tsd-kind-function tsd-parent-kind-external-module","parent":"\"lib/Metaphor\""},{"id":24,"kind":64,"name":"inUseMessage","url":"modules/_lib_metaphor_.html#inusemessage","classes":"tsd-kind-function tsd-parent-kind-external-module","parent":"\"lib/Metaphor\""},{"id":25,"kind":64,"name":"invariantFieldUnique","url":"modules/_lib_metaphor_.html#invariantfieldunique","classes":"tsd-kind-function tsd-parent-kind-external-module","parent":"\"lib/Metaphor\""},{"id":26,"kind":64,"name":"getFieldIdx","url":"modules/_lib_metaphor_.html#getfieldidx","classes":"tsd-kind-function tsd-parent-kind-external-module","parent":"\"lib/Metaphor\""},{"id":27,"kind":1,"name":"\"lib/FieldPart\"","url":"modules/_lib_fieldpart_.html","classes":"tsd-kind-external-module"},{"id":28,"kind":256,"name":"FieldPart","url":"interfaces/_lib_fieldpart_.fieldpart.html","classes":"tsd-kind-interface tsd-parent-kind-external-module","parent":"\"lib/FieldPart\""},{"id":29,"kind":128,"name":"FieldPartGateway","url":"classes/_lib_fieldpart_.fieldpartgateway.html","classes":"tsd-kind-class tsd-parent-kind-external-module","parent":"\"lib/FieldPart\""},{"id":30,"kind":1024,"name":"form","url":"classes/_lib_fieldpart_.fieldpartgateway.html#form","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"\"lib/FieldPart\".FieldPartGateway"},{"id":31,"kind":1024,"name":"path","url":"classes/_lib_fieldpart_.fieldpartgateway.html#path","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"\"lib/FieldPart\".FieldPartGateway"},{"id":32,"kind":512,"name":"constructor","url":"classes/_lib_fieldpart_.fieldpartgateway.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"\"lib/FieldPart\".FieldPartGateway"},{"id":33,"kind":2048,"name":"set","url":"classes/_lib_fieldpart_.fieldpartgateway.html#set","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/FieldPart\".FieldPartGateway"},{"id":34,"kind":2048,"name":"up","url":"classes/_lib_fieldpart_.fieldpartgateway.html#up","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"lib/FieldPart\".FieldPartGateway"},{"id":35,"kind":1,"name":"\"index\"","url":"modules/_index_.html","classes":"tsd-kind-external-module"}]};
--------------------------------------------------------------------------------
/packages/metaphor/docs/globals.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @react-ui-generator/metaphor
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
@react-ui-generator/metaphor
26 |
27 |
49 |
50 |
51 |
52 |
53 |
54 |
59 |
@react-ui-generator/metaphor
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Index
68 |
69 |
70 |
71 | External modules
72 |
77 |
78 |
79 |
80 |
81 |
82 |
104 |
105 |
106 |
166 |
167 |
Generated using TypeDoc
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/packages/metaphor/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @react-ui-generator/metaphor
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
@react-ui-generator/metaphor
26 |
27 |
49 |
50 |
51 |
52 |
53 |
54 |
59 |
@react-ui-generator/metaphor
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | Metaphor
69 |
70 |
DSL for easing baking of metadata for the react-ui-generator.
71 |
72 | Example
73 |
74 |
import Metaphor from '@react-ui-generator/metaphor';
75 | import BaseMeta from './meta.json';
76 |
77 | const formToView = new Metaphor(BaseMeta)
78 | .showAll()
79 | .disableAll()
80 | .value();
81 |
82 | const formToEdit = new Metaphor(BaseMeta)
83 | .enableAll()
84 | .disable(['id', 'createdAt', 'author'])
85 | .config
86 | .set('email', { showAsterix: true })
87 | .set(['birthDate', 'employmentDate'], {
88 | showAsterix: true,
89 | format: 'DD.MM.YYYY',
90 | })
91 | .up()
92 | .actions
93 | .set(['btnSave'], { onClick: 'sendFormToServer' })
94 | .set('btnCancel', { onClick: isFormChanged() ? 'confirmEditCancellation' : 'closeForm' })
95 | .up()
96 | .hide(calcSecretFieldsByUserRole(), true)
97 | .value();
98 |
99 |
100 |
122 |
123 |
124 |
184 |
185 |
Generated using TypeDoc
186 |
187 |
188 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/packages/metaphor/docs/interfaces/_lib_fieldpart_.fieldpart.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | FieldPart | @react-ui-generator/metaphor
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
@react-ui-generator/metaphor
26 |
27 |
49 |
50 |
51 |
52 |
53 |
54 |
65 |
Interface FieldPart
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Hierarchy
74 |
75 | -
76 | FieldPart
77 |
78 |
79 |
80 |
81 | Indexable
82 | [key: string]: any
83 |
84 |
85 |
117 |
118 |
119 |
179 |
180 |
Generated using TypeDoc
181 |
182 |
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/packages/metaphor/docs/modules/_index_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "index" | @react-ui-generator/metaphor
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
@react-ui-generator/metaphor
26 |
27 |
49 |
50 |
51 |
52 |
53 |
54 |
62 |
External module "index"
63 |
64 |
65 |
66 |
94 |
154 |
155 |
Generated using TypeDoc
156 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/packages/metaphor/docs/modules/_lib_fieldpart_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "lib/FieldPart" | @react-ui-generator/metaphor
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
@react-ui-generator/metaphor
26 |
27 |
49 |
50 |
51 |
52 |
53 |
54 |
62 |
External module "lib/FieldPart"
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | Index
71 |
72 |
73 |
79 |
80 | Interfaces
81 |
84 |
85 |
86 |
87 |
88 |
89 |
117 |
118 |
119 |
179 |
180 |
Generated using TypeDoc
181 |
182 |
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/packages/metaphor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-ui-generator/metaphor",
3 | "version": "0.7.5",
4 | "description": "DSL for easing baking of metadata for the react-ui-generator.",
5 | "main": "./out/metaphor.js",
6 | "types": "./out/index.d.ts",
7 | "repository": "https://github.com/react-ui-generator/react-ui-generator/packages/metaphor",
8 | "scripts": {
9 | "build": "webpack --config config/webpack.config.js",
10 | "test": "jest -c config/tests/jest.config.js",
11 | "doc:build": "typedoc --out ./docs/ --exclude ./node_modules/ src/index.ts"
12 | },
13 | "keywords": [
14 | "metadata",
15 | "dsl",
16 | "react-ui-generator"
17 | ],
18 | "author": "A.Zaviruha ",
19 | "license": "MIT",
20 | "devDependencies": {
21 | "@babel/core": "^7.5.5",
22 | "@babel/preset-env": "^7.0.0",
23 | "@babel/register": "^7.7.0",
24 | "@react-ui-generator/core": "^0.7.5",
25 | "@types/invariant": "^2.2.30",
26 | "@types/jest": "^24.0.15",
27 | "@types/lodash-es": "^4.17.3",
28 | "awesome-typescript-loader": "^5.2.1",
29 | "babel-jest": "^24.8.0",
30 | "babel-loader": "^8.0.6",
31 | "babel-upgrade": "^0.0.24",
32 | "jest": "^24.8.0",
33 | "ts-jest": "^24.0.2",
34 | "typedock": "^1.0.9",
35 | "typescript": "^3.5.3",
36 | "webpack": "^4.36.1",
37 | "webpack-cleanup-plugin": "^0.5.1",
38 | "webpack-cli": "^3.3.6",
39 | "webpack-merge": "^4.1.2"
40 | },
41 | "peerDependencies": {
42 | "@react-ui-generator/core": "^0.6.0",
43 | "lodash-es": "^4.17.15"
44 | },
45 | "gitHead": "f34904c44b092961eef4fa3e5fc9b62b18628838",
46 | "dependencies": {
47 | "invariant": "^2.2.4"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/metaphor/src/index.ts:
--------------------------------------------------------------------------------
1 | import Metaphor from './lib/Metaphor';
2 | import FieldPart from './lib/FieldPart';
3 |
4 | export {
5 | Metaphor as default,
6 | Metaphor,
7 | FieldPart,
8 | };
--------------------------------------------------------------------------------
/packages/metaphor/src/lib/FieldPart.ts:
--------------------------------------------------------------------------------
1 | import merge from 'lodash-es/merge'
2 | import cloneDeep from 'lodash-es/cloneDeep'
3 | import { Metaphor } from '../'
4 |
5 | export interface FieldPart {
6 | [key: string]: any
7 | }
8 |
9 | class FieldPartGateway {
10 | private form: Metaphor
11 | private path: string
12 |
13 | constructor(form: Metaphor, path: string) {
14 | this.form = form
15 | this.path = path
16 | }
17 |
18 | set(fieldIds: string[] | string, newPart: FieldPart) {
19 | const idsToProcess = Array.isArray(fieldIds) ? fieldIds : [fieldIds]
20 |
21 | for (let fieldId of idsToProcess) {
22 | const path = `${fieldId}.${this.path}`
23 | const fieldPart = this.form.get(path)
24 | const newFieldPart = cloneDeep(fieldPart)
25 |
26 | merge(newFieldPart, newPart)
27 | this.form.set(path, newFieldPart)
28 | }
29 |
30 | return this
31 | }
32 |
33 | up() {
34 | return this.form
35 | }
36 | }
37 |
38 | export default FieldPartGateway
39 |
--------------------------------------------------------------------------------
/packages/metaphor/src/lib/Metaphor.ts:
--------------------------------------------------------------------------------
1 | import cloneDeep from 'lodash-es/cloneDeep'
2 | import get from 'lodash-es/get'
3 | import set from 'lodash-es/set'
4 | import invariant from 'invariant'
5 |
6 | import {
7 | RawMetaDescription,
8 | FormMetaDescription,
9 | FieldMetaDescription,
10 | enhanceFormMeta,
11 | enhanceFieldMeta,
12 | findFieldMetaById,
13 | } from '@react-ui-generator/core'
14 |
15 | import { FieldPart } from '../'
16 |
17 | export interface IdsToProcess {
18 | [key: string]: boolean
19 | }
20 |
21 | export type FieldBooleanProps = 'hidden' | 'disabled'
22 |
23 | class Metaphor {
24 | private meta: FormMetaDescription
25 |
26 | constructor(baseMeta: RawMetaDescription) {
27 | this.meta = enhanceFormMeta(baseMeta)
28 | }
29 |
30 | togglePropByFieldIds(
31 | propName: FieldBooleanProps,
32 | value: boolean,
33 | reverseIfNotMatch?: boolean,
34 | ids?: string[] | string
35 | ): Metaphor {
36 | const idsArray = typeof ids === 'string' ? [ids] : ids
37 | const fieldsToProcess: IdsToProcess = idsArray
38 | ? idsArray.reduce((acc, id) => ({ ...acc, [id]: true }), {})
39 | : {}
40 |
41 | for (let fieldMeta of this.meta.fields) {
42 | const fieldId = fieldMeta.id
43 |
44 | if (ids === undefined || fieldsToProcess[fieldId]) {
45 | fieldMeta[propName] = value
46 | } else if (reverseIfNotMatch) {
47 | fieldMeta[propName] = !value
48 | }
49 | }
50 |
51 | return this
52 | }
53 |
54 | value(): FormMetaDescription {
55 | return cloneDeep(this.meta)
56 | }
57 |
58 | get(path: string) {
59 | const [fieldId, ...tokens] = path.split('.')
60 | const fieldMeta = findFieldMetaById(fieldId, this.meta.fields)
61 | return get(fieldMeta, tokens)
62 | }
63 |
64 | set(path: string, value: any) {
65 | const [fieldId, ...tokens] = path.split('.')
66 | const fieldMeta = findFieldMetaById(fieldId, this.meta.fields)
67 | set(fieldMeta, tokens, value)
68 |
69 | return this
70 | }
71 |
72 | show(fieldsToShow?: string[] | string, hideNotMatched?: boolean): Metaphor {
73 | return this.togglePropByFieldIds(
74 | 'hidden',
75 | false,
76 | hideNotMatched,
77 | fieldsToShow
78 | )
79 | }
80 |
81 | showAll(): Metaphor {
82 | return this.show()
83 | }
84 |
85 | hide(fieldsToHide?: string[] | string, showNotMatched?: boolean): Metaphor {
86 | return this.togglePropByFieldIds(
87 | 'hidden',
88 | true,
89 | showNotMatched,
90 | fieldsToHide
91 | )
92 | }
93 |
94 | hideAll(): Metaphor {
95 | return this.hide()
96 | }
97 |
98 | enable(
99 | fieldsToEnable?: string[] | string,
100 | disableNotMatched?: boolean
101 | ): Metaphor {
102 | return this.togglePropByFieldIds(
103 | 'disabled',
104 | false,
105 | disableNotMatched,
106 | fieldsToEnable
107 | )
108 | }
109 |
110 | enableAll(): Metaphor {
111 | return this.enable()
112 | }
113 |
114 | disable(
115 | fieldsToDisable?: string[] | string,
116 | enableNotMatched?: boolean
117 | ): Metaphor {
118 | return this.togglePropByFieldIds(
119 | 'disabled',
120 | true,
121 | enableNotMatched,
122 | fieldsToDisable
123 | )
124 | }
125 |
126 | disableAll(): Metaphor {
127 | return this.disable()
128 | }
129 |
130 | /**
131 | * Clones an existing field. Allows to append a cloned field
132 | * just after the original, or to the end of form.
133 | */
134 | clone(
135 | idSrc: string,
136 | idTarget: string,
137 | appendToSrc: boolean = true
138 | ): Metaphor {
139 | const { fields } = this.meta
140 | const idxSrc = getFieldIdx(idSrc, fields)
141 |
142 | invariantFieldUnique(idTarget, fields)
143 |
144 | const fieldMeta = cloneDeep(findFieldMetaById(idSrc, fields))
145 | fieldMeta.id = idTarget
146 |
147 | if (appendToSrc) {
148 | fields.splice(idxSrc + 1, 0, fieldMeta)
149 | } else {
150 | fields.push(fieldMeta)
151 | }
152 |
153 | return this
154 | }
155 |
156 | /**
157 | * Creates a new field. Allows to append it under one of existing fields
158 | * or to the end of form.
159 | */
160 | add(fieldId: string, type: string, underId?: string): Metaphor {
161 | const { fields } = this.meta
162 |
163 | invariantFieldUnique(fieldId, fields)
164 |
165 | const rawMeta = { id: fieldId, renderer: type }
166 | const completeMeta = enhanceFieldMeta(rawMeta)
167 |
168 | if (underId) {
169 | const idx = getFieldIdx(underId, fields)
170 | fields.splice(idx + 1, 0, completeMeta)
171 | } else {
172 | fields.push(completeMeta)
173 | }
174 |
175 | return this
176 | }
177 |
178 | remove(fieldId: string): Metaphor {
179 | const { fields } = this.meta
180 | const idx = getFieldIdx(fieldId, fields)
181 |
182 | fields.splice(idx, 1)
183 | return this
184 | }
185 |
186 | get config(): FieldPart {
187 | return new FieldPart(this, 'renderer.config')
188 | }
189 |
190 | get actions(): FieldPart {
191 | return new FieldPart(this, 'actions')
192 | }
193 | }
194 |
195 | export function notFoundMessage(id: string): string {
196 | return `Field with id "${id}" is not found.`
197 | }
198 |
199 | export function inUseMessage(id: string): string {
200 | return `Id "${id}" is already in use.`
201 | }
202 |
203 | export function invariantFieldUnique(fieldId: string, fields: FieldMetaDescription[]) {
204 | invariant(
205 | fields.every(item => item.id !== fieldId),
206 | inUseMessage(fieldId)
207 | )
208 | }
209 |
210 | export function getFieldIdx(fieldId: string, fields: FieldMetaDescription[]): number {
211 | const idx = fields.findIndex(item => item.id === fieldId)
212 |
213 | invariant(idx !== -1, notFoundMessage(fieldId))
214 | return idx
215 | }
216 |
217 | export default Metaphor
218 |
--------------------------------------------------------------------------------
/packages/metaphor/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./out/",
4 | "sourceMap": true,
5 | "noImplicitAny": true,
6 | "module": "commonjs",
7 | "target": "es5",
8 | "moduleResolution": "node",
9 | "esModuleInterop": true,
10 | "allowSyntheticDefaultImports": true,
11 | "declaration": true,
12 | "declarationDir": "./out/",
13 | },
14 | "include": ["./src/**/*"]
15 | }
--------------------------------------------------------------------------------
/packages/validators/README.md:
--------------------------------------------------------------------------------
1 | # react-ui-generator/validators
2 |
3 | ## Getting started
4 |
5 | `npm i @react-ui-generator/validators ajv --save`
6 |
7 | ## How to write your own binding for Y (example).
8 |
--------------------------------------------------------------------------------
/packages/validators/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 |
4 | const presets = ['@babel/preset-env', '@babel/preset-react'];
5 | const plugins = [];
6 |
7 | return {
8 | presets,
9 | plugins
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/packages/validators/config/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { TsConfigPathsPlugin } = require('awesome-typescript-loader');
2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
3 | const path = require('path');
4 |
5 | const resolve = dir => path.join(__dirname, '..', dir);
6 |
7 | module.exports = {
8 | entry: resolve('src/index.ts'),
9 | output: {
10 | filename: 'validators.js',
11 | path: resolve('out'),
12 | libraryTarget: 'umd'
13 | },
14 |
15 | devtool: 'source-map',
16 | mode: 'production',
17 |
18 | resolve: {
19 | extensions: ['.ts', '.js', '.json'],
20 | modules: [resolve('src'), 'node_modules'],
21 | plugins: [new TsConfigPathsPlugin({})]
22 | },
23 |
24 | module: {
25 | rules: [
26 | {
27 | test: /\.ts$/,
28 | use: ['babel-loader', 'awesome-typescript-loader'],
29 | exclude: /node_modules/
30 | },
31 |
32 | // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
33 | {
34 | enforce: 'pre',
35 | test: /\.js$/,
36 | loader: 'source-map-loader'
37 | }
38 | ]
39 | },
40 | externals: ['@react-ui-generator/core', /^lodash-es(\/.+)?$/],
41 | plugins: [new CleanWebpackPlugin()]
42 | };
43 |
--------------------------------------------------------------------------------
/packages/validators/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-ui-generator/validators",
3 | "version": "0.7.5",
4 | "description": "Collection of bindings for popular validation libraries to react-ui-generator",
5 | "main": "./out/validators.js",
6 | "types": "./out/index.d.ts",
7 | "repository": "https://github.com/react-ui-generator/react-ui-generator/packages/validators",
8 | "scripts": {
9 | "build": "webpack --config config/webpack.config.js",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "author": "Alexei Zaviruha ",
13 | "license": "MIT",
14 | "peerDependencies": {
15 | "@react-ui-generator/core": "^0.6.0",
16 | "lodash-es": "^4.17.15"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.5.5",
20 | "@babel/preset-env": "^7.0.0",
21 | "@react-ui-generator/core": "^0.7.5",
22 | "@types/jest": "^24.0.15",
23 | "@types/lodash-es": "^4.17.3",
24 | "awesome-typescript-loader": "^5.2.1",
25 | "babel-jest": "^24.8.0",
26 | "babel-loader": "^8.0.6",
27 | "babel-upgrade": "^0.0.24",
28 | "jest": "^24.8.0",
29 | "source-map-loader": "^0.2.3",
30 | "ts-jest": "^24.0.2",
31 | "typescript": "^3.5.3",
32 | "webpack": "^4.36.1",
33 | "webpack-cleanup-plugin": "^0.5.1",
34 | "webpack-cli": "^3.3.6",
35 | "webpack-merge": "^4.1.2"
36 | },
37 | "gitHead": "f34904c44b092961eef4fa3e5fc9b62b18628838"
38 | }
39 |
--------------------------------------------------------------------------------
/packages/validators/src/ajv.ts:
--------------------------------------------------------------------------------
1 | import get from 'lodash-es/get';
2 | import set from 'lodash-es/set';
3 | import {
4 | KeyValue,
5 | Validator,
6 | ValidationResult,
7 | buildValidator
8 | } from '@react-ui-generator/core';
9 |
10 | export class Ajv {
11 | constructor(options: KeyValue) {}
12 | }
13 |
14 | export function buildAjvValidator(AjvFn: typeof Ajv, schema: KeyValue): Validator {
15 | function validateWithAjv(schema: KeyValue, data: KeyValue): ValidationResult {
16 | const ajv: KeyValue = new AjvFn({ allErrors: true, $data: true });
17 | const validate = ajv.compile(schema);
18 | const isValid = validate(data);
19 | const errors = validate.errors || [];
20 |
21 | const processedErrors = errors.reduce((acc: KeyValue, item: KeyValue) => {
22 | const [fieldName, subFormIndex] = item.dataPath
23 | .replace(/^\./, '')
24 | .split('[')
25 | .map((item: string) => item.replace(']', ''));
26 |
27 | const fieldErrors = acc[fieldName] || [];
28 | const message =
29 | item.keyword === 'enum' && item.params.allowedValues
30 | ? `${item.message}: [${item.params.allowedValues}]`
31 | : item.message;
32 |
33 | if (subFormIndex) {
34 | const [idx, ...path] = subFormIndex.split('.');
35 | const subFormErrObj = fieldErrors[idx] || {};
36 | const subFormErrors = get(subFormErrObj, path, []);
37 |
38 | fieldErrors[idx] = set(subFormErrObj, path, [...subFormErrors, message]);
39 | acc[fieldName] = fieldErrors;
40 | } else {
41 | acc[fieldName] = [...fieldErrors, message];
42 | }
43 |
44 | return acc;
45 | }, {});
46 |
47 | return { isValid, errors: processedErrors };
48 | }
49 |
50 | return buildValidator(validateWithAjv, schema);
51 | }
52 |
--------------------------------------------------------------------------------
/packages/validators/src/index.ts:
--------------------------------------------------------------------------------
1 | export { buildAjvValidator } from './ajv';
2 |
--------------------------------------------------------------------------------
/packages/validators/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./out/",
4 | "declarationDir": "./out/",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "moduleResolution": "node",
8 | "jsx": "react",
9 | "esModuleInterop": true,
10 | "allowSyntheticDefaultImports": true,
11 | "declaration": true,
12 | "sourceMap": true,
13 | "noImplicitAny": true
14 | },
15 | "include": ["./src/**/*"]
16 | }
17 |
--------------------------------------------------------------------------------