@springmicro/forms 0.1.3 → 0.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +4 -0
- package/dist/index.js +4567 -4761
- package/dist/index.umd.cjs +38 -38
- package/package.json +9 -3
- package/src/builder/bottom-drawer.tsx +429 -0
- package/src/builder/form-builder.tsx +256 -0
- package/src/builder/modal.tsx +39 -0
- package/src/builder/nodes/node-base.tsx +94 -0
- package/src/builder/nodes/node-child-helpers.tsx +273 -0
- package/src/builder/nodes/node-parent.tsx +187 -0
- package/src/builder/nodes/node-types/array-node.tsx +134 -0
- package/src/builder/nodes/node-types/date-node.tsx +60 -0
- package/src/builder/nodes/node-types/file-node.tsx +67 -0
- package/src/builder/nodes/node-types/integer-node.tsx +60 -0
- package/src/builder/nodes/node-types/object-node.tsx +67 -0
- package/src/builder/nodes/node-types/text-node.tsx +66 -0
- package/src/types/form-builder.ts +135 -0
- package/src/types/utils.type.ts +1 -0
- package/src/utils/form-builder.ts +424 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { UseStateType } from "./utils.type";
|
|
2
|
+
|
|
3
|
+
export type FullFormType = {
|
|
4
|
+
form: FormType;
|
|
5
|
+
nodes: FormNodeType[];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type FormType = {
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extracts each key from @type {FormNodeTypes} exctracting the string from @param {T["type"]}
|
|
15
|
+
* which is then mapped to @type {FormNodeKeys}
|
|
16
|
+
*/
|
|
17
|
+
type ExtractTypes<T extends { type: string }> = T["type"];
|
|
18
|
+
export type FormNodeKeys = ExtractTypes<FormNodeType>;
|
|
19
|
+
|
|
20
|
+
export type SearchNodeTypeByKey = {
|
|
21
|
+
[key in FormNodeKeys]: FormNodeType extends { type: infer T }
|
|
22
|
+
? T extends key
|
|
23
|
+
? FormNodeType
|
|
24
|
+
: never
|
|
25
|
+
: never;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export interface BaseNode {
|
|
29
|
+
nodeId: string;
|
|
30
|
+
propertyName?: string;
|
|
31
|
+
title?: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
required?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* =================== NODES ===================
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
export type FormNodeType =
|
|
41
|
+
| FileNode
|
|
42
|
+
| TextNode
|
|
43
|
+
| IntegerNode
|
|
44
|
+
| DateNode
|
|
45
|
+
| ArrayNode
|
|
46
|
+
| ObjectNode;
|
|
47
|
+
|
|
48
|
+
export type TextNode = BaseNode & TextNodeEmpty;
|
|
49
|
+
|
|
50
|
+
export type FileNode = BaseNode & FileNodeEmpty;
|
|
51
|
+
|
|
52
|
+
export type IntegerNode = BaseNode & IntegerNodeEmpty;
|
|
53
|
+
|
|
54
|
+
export type DateNode = BaseNode & DateNodeEmpty;
|
|
55
|
+
|
|
56
|
+
export type ArrayNode = BaseNode & ArrayNodeEmpty;
|
|
57
|
+
|
|
58
|
+
export type ObjectNode = BaseNode & ObjectNodeEmpty;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* =================== NODES ===================
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
export type EmptyFormNodeType =
|
|
65
|
+
| FileNodeEmpty
|
|
66
|
+
| TextNodeEmpty
|
|
67
|
+
| IntegerNodeEmpty
|
|
68
|
+
| DateNodeEmpty
|
|
69
|
+
| ArrayNodeEmpty
|
|
70
|
+
| ObjectNodeEmpty;
|
|
71
|
+
|
|
72
|
+
export interface TextNodeEmpty {
|
|
73
|
+
type: "string";
|
|
74
|
+
default?: string;
|
|
75
|
+
minLength?: number;
|
|
76
|
+
maxLength?: number;
|
|
77
|
+
format?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface FileNodeEmpty {
|
|
81
|
+
type: "file";
|
|
82
|
+
multiple?: boolean;
|
|
83
|
+
filetype?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface IntegerNodeEmpty {
|
|
87
|
+
type: "integer";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface DateNodeEmpty {
|
|
91
|
+
type: "date";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ArrayNodeEmpty {
|
|
95
|
+
type: "array";
|
|
96
|
+
child: EmptyFormNodeType;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ObjectNodeEmpty {
|
|
100
|
+
type: "object";
|
|
101
|
+
children: FormNodeType[]; // a list of nodes
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* ================== UTILS ==================
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
export type CountdownType =
|
|
109
|
+
| undefined
|
|
110
|
+
| number
|
|
111
|
+
| "Saved."
|
|
112
|
+
| "Saving..."
|
|
113
|
+
| "Save failed.";
|
|
114
|
+
|
|
115
|
+
export type EditingStateType = false | string;
|
|
116
|
+
|
|
117
|
+
// The format for creating a field or checkmark inside a node.
|
|
118
|
+
export type FieldArrayType<T> = Array<{
|
|
119
|
+
prop: keyof T;
|
|
120
|
+
tooltip?: string;
|
|
121
|
+
title?: string;
|
|
122
|
+
props?: any;
|
|
123
|
+
}>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* ================== SHARED PROPS ==================
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
export type ChildNodeProps = {
|
|
130
|
+
nodeState: UseStateType<FormNodeType>;
|
|
131
|
+
updateNode: (nodeData: FormNodeType) => void;
|
|
132
|
+
defaultFields: React.JSX.Element[];
|
|
133
|
+
defaultChecks: React.JSX.Element[];
|
|
134
|
+
updatePath: () => void;
|
|
135
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type UseStateType<T> = [T, React.Dispatch<React.SetStateAction<T>>];
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FormNodeKeys,
|
|
3
|
+
FormNodeType,
|
|
4
|
+
FormType,
|
|
5
|
+
FileNode,
|
|
6
|
+
IntegerNode,
|
|
7
|
+
TextNode,
|
|
8
|
+
ArrayNode,
|
|
9
|
+
DateNode,
|
|
10
|
+
ObjectNode,
|
|
11
|
+
} from "../types/form-builder";
|
|
12
|
+
import { UseStateType } from "../types/utils.type";
|
|
13
|
+
import { RJSFSchema, UiSchema } from "@rjsf/utils";
|
|
14
|
+
import { JSONSchema7 } from "json-schema";
|
|
15
|
+
import { v4 as uuidv4 } from "uuid";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* ---------------------- GENERATE NODE DATA ----------------------
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const baseDefaultNode = { required: true, nodeId: "" };
|
|
22
|
+
|
|
23
|
+
export const defaultNodes: {
|
|
24
|
+
[key in FormNodeKeys]: FormNodeType extends { type: infer T }
|
|
25
|
+
? T extends key
|
|
26
|
+
? FormNodeType
|
|
27
|
+
: never
|
|
28
|
+
: never;
|
|
29
|
+
} = {
|
|
30
|
+
string: {
|
|
31
|
+
...baseDefaultNode,
|
|
32
|
+
type: "string",
|
|
33
|
+
title: "New String Field",
|
|
34
|
+
},
|
|
35
|
+
file: {
|
|
36
|
+
...baseDefaultNode,
|
|
37
|
+
type: "file",
|
|
38
|
+
title: "New File Field",
|
|
39
|
+
},
|
|
40
|
+
integer: {
|
|
41
|
+
...baseDefaultNode,
|
|
42
|
+
type: "integer",
|
|
43
|
+
title: "New Integer Field",
|
|
44
|
+
},
|
|
45
|
+
date: {
|
|
46
|
+
...baseDefaultNode,
|
|
47
|
+
type: "date",
|
|
48
|
+
title: "New Date Field",
|
|
49
|
+
},
|
|
50
|
+
array: {
|
|
51
|
+
...baseDefaultNode,
|
|
52
|
+
type: "array",
|
|
53
|
+
title: "New Array Field",
|
|
54
|
+
child: { type: "string" },
|
|
55
|
+
},
|
|
56
|
+
object: {
|
|
57
|
+
...baseDefaultNode,
|
|
58
|
+
type: "object",
|
|
59
|
+
title: "New Object",
|
|
60
|
+
children: [],
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export function generateNodeData(
|
|
65
|
+
type: FormNodeKeys,
|
|
66
|
+
children?: FormNodeType[]
|
|
67
|
+
) {
|
|
68
|
+
return {
|
|
69
|
+
...defaultNodes[type],
|
|
70
|
+
nodeId: uuidv4(),
|
|
71
|
+
children: type !== "object" ? undefined : children ?? [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* ---------------------- FORM JSON -> BUILDER JSON ----------------------
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
export function serializeFormToBuilder(
|
|
80
|
+
rjsfForm: RJSFSchema,
|
|
81
|
+
rjsfUiSchema: UiSchema = {}
|
|
82
|
+
): {
|
|
83
|
+
form: FormType;
|
|
84
|
+
nodes: FormNodeType[];
|
|
85
|
+
} {
|
|
86
|
+
if (rjsfForm === undefined) return { form: {}, nodes: [] };
|
|
87
|
+
function generate(form: RJSFSchema, ui: UiSchema) {
|
|
88
|
+
const nodes: (FormNodeType | undefined)[] = Object.keys(
|
|
89
|
+
form.properties!
|
|
90
|
+
).map((key) => {
|
|
91
|
+
// The two parts of the current working node.
|
|
92
|
+
const formNode = form.properties![key] as JSONSchema7;
|
|
93
|
+
const uiNode = ui[key] ?? {};
|
|
94
|
+
|
|
95
|
+
const type = formNode.type;
|
|
96
|
+
const required = form.required?.includes(key);
|
|
97
|
+
|
|
98
|
+
const baseNode = {
|
|
99
|
+
title: formNode.title!,
|
|
100
|
+
nodeId: uuidv4(),
|
|
101
|
+
description: uiNode["ui:description"],
|
|
102
|
+
required,
|
|
103
|
+
propertyName: key,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (type === "string") {
|
|
107
|
+
if (formNode.format === "data-url") {
|
|
108
|
+
// ==================== FILE ====================
|
|
109
|
+
const node: FileNode = {
|
|
110
|
+
type: "file",
|
|
111
|
+
...baseNode,
|
|
112
|
+
};
|
|
113
|
+
if (uiNode["ui:options"] && uiNode["ui:options"].accept)
|
|
114
|
+
node.filetype = uiNode["ui:options"].accept;
|
|
115
|
+
return node;
|
|
116
|
+
}
|
|
117
|
+
if (formNode.format === "date-time") {
|
|
118
|
+
// ==================== DATE ====================
|
|
119
|
+
const node: DateNode = {
|
|
120
|
+
type: "date",
|
|
121
|
+
...baseNode,
|
|
122
|
+
};
|
|
123
|
+
return node;
|
|
124
|
+
}
|
|
125
|
+
// ==================== TEXT ====================
|
|
126
|
+
const node: TextNode = {
|
|
127
|
+
type: "string",
|
|
128
|
+
...baseNode,
|
|
129
|
+
};
|
|
130
|
+
if (formNode.format) node.format = formNode.format;
|
|
131
|
+
if (formNode.minLength) node.minLength = formNode.minLength;
|
|
132
|
+
if (formNode.maxLength) node.maxLength = formNode.maxLength;
|
|
133
|
+
if (formNode.default) node.default = formNode.default as string;
|
|
134
|
+
return node;
|
|
135
|
+
}
|
|
136
|
+
if (type === "integer") {
|
|
137
|
+
// ==================== INTEGER ====================
|
|
138
|
+
const node: IntegerNode = {
|
|
139
|
+
type: "integer",
|
|
140
|
+
...baseNode,
|
|
141
|
+
};
|
|
142
|
+
return node;
|
|
143
|
+
}
|
|
144
|
+
if (type === "array") {
|
|
145
|
+
const items = formNode.items as JSONSchema7;
|
|
146
|
+
if (items.type === "string" && items.format === "data-url") {
|
|
147
|
+
// ==================== MULTIPLE FILES ====================
|
|
148
|
+
const node: FileNode = {
|
|
149
|
+
type: "file",
|
|
150
|
+
multiple: true,
|
|
151
|
+
...baseNode,
|
|
152
|
+
};
|
|
153
|
+
if (uiNode["ui:options"] && uiNode["ui:options"].accept)
|
|
154
|
+
node.filetype = uiNode["ui:options"].accept;
|
|
155
|
+
return node;
|
|
156
|
+
}
|
|
157
|
+
if (items.type === "string" && items.format === "date-time") {
|
|
158
|
+
// ==================== DATE ARRAY ====================
|
|
159
|
+
const node: ArrayNode = {
|
|
160
|
+
type: "array",
|
|
161
|
+
...baseNode,
|
|
162
|
+
child: { type: "date" },
|
|
163
|
+
};
|
|
164
|
+
return node;
|
|
165
|
+
}
|
|
166
|
+
if (items.type === "string" || items.type === "integer") {
|
|
167
|
+
// ==================== ARRAY ====================
|
|
168
|
+
const node: ArrayNode = {
|
|
169
|
+
type: "array",
|
|
170
|
+
...baseNode,
|
|
171
|
+
child: { type: items.type },
|
|
172
|
+
};
|
|
173
|
+
return node;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (type === "object") {
|
|
177
|
+
// ==================== OBJECT ====================
|
|
178
|
+
const node: ObjectNode = {
|
|
179
|
+
type: "object",
|
|
180
|
+
children: generate(formNode, uiNode).nodes, // Reruns through object as if it was base layer. '.form' is not needed because title is assigned automatically for anything nested inside an object.
|
|
181
|
+
...baseNode,
|
|
182
|
+
};
|
|
183
|
+
return node;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
form: { title: form.title, description: form.description },
|
|
188
|
+
nodes: nodes.filter((n) => n !== undefined) as FormNodeType[],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return generate(rjsfForm, rjsfUiSchema);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* ---------------------- BUILDER JSON -> FORM JSON ----------------------
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
export function serializeBuilderToForm(
|
|
200
|
+
formConfig: FormType,
|
|
201
|
+
nodes: FormNodeType[]
|
|
202
|
+
): { form: RJSFSchema; ui: UiSchema } {
|
|
203
|
+
function generate(_n: FormNodeType[]) {
|
|
204
|
+
const ui: UiSchema = {};
|
|
205
|
+
|
|
206
|
+
const formObjs: {
|
|
207
|
+
required: string[];
|
|
208
|
+
properties: Record<string, JSONSchema7>;
|
|
209
|
+
} = {
|
|
210
|
+
required: [],
|
|
211
|
+
properties: {},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
_n.forEach((node) => {
|
|
215
|
+
let uniqueNum = 0;
|
|
216
|
+
const basePropName = node.propertyName
|
|
217
|
+
? node.propertyName
|
|
218
|
+
: formatPropName(node.title!); // Can't use ?? because node.propertyName can be ""
|
|
219
|
+
let propName = basePropName;
|
|
220
|
+
while (formObjs.properties[propName]) {
|
|
221
|
+
uniqueNum++;
|
|
222
|
+
propName = `${basePropName}-${uniqueNum}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
let propUi: any = node.description
|
|
226
|
+
? {
|
|
227
|
+
"ui:description": node.description,
|
|
228
|
+
"ui:enableMarkdownInDescription": true,
|
|
229
|
+
}
|
|
230
|
+
: {};
|
|
231
|
+
|
|
232
|
+
const props: JSONSchema7 = {};
|
|
233
|
+
switch (node.type) {
|
|
234
|
+
case "string": {
|
|
235
|
+
// ==================== TEXT ====================
|
|
236
|
+
props.type = "string";
|
|
237
|
+
if (node.default) props.default = node.default;
|
|
238
|
+
if (node.format) props.format = node.format;
|
|
239
|
+
if (node.minLength)
|
|
240
|
+
props.minLength = Number.parseInt(`${node.minLength}`);
|
|
241
|
+
if (node.maxLength)
|
|
242
|
+
props.maxLength = Number.parseInt(`${node.maxLength}`);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
case "file": {
|
|
246
|
+
// ==================== FILE ====================
|
|
247
|
+
if (node.filetype) {
|
|
248
|
+
let filetype = node.filetype.trim().toLowerCase();
|
|
249
|
+
if (filetype.charAt(0) !== ".") filetype = "." + filetype;
|
|
250
|
+
propUi["ui:options"] = { accept: filetype };
|
|
251
|
+
}
|
|
252
|
+
if (node.multiple) {
|
|
253
|
+
// ==================== MULTIPLE FILES ====================
|
|
254
|
+
props.type = "array";
|
|
255
|
+
props.items = {
|
|
256
|
+
type: "string",
|
|
257
|
+
format: "data-url",
|
|
258
|
+
};
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
// ==================== FILE CONT. ====================
|
|
262
|
+
props.type = "string";
|
|
263
|
+
props.format = "data-url";
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
case "integer": {
|
|
267
|
+
// ==================== INTEGER ====================
|
|
268
|
+
props.type = "integer";
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case "object": {
|
|
272
|
+
// ==================== OBJECT ====================
|
|
273
|
+
props.type = "object";
|
|
274
|
+
const formData = generate(node.children);
|
|
275
|
+
props.properties = formData.form.properties;
|
|
276
|
+
props.required = formData.form.required;
|
|
277
|
+
propUi = { ...propUi, ...formData.ui };
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case "array": {
|
|
281
|
+
// ==================== ARRAY ====================
|
|
282
|
+
props.type = "array";
|
|
283
|
+
if (node.child.type === "date") {
|
|
284
|
+
// ==================== DATE ARRAY ====================
|
|
285
|
+
props.items = {
|
|
286
|
+
type: "string",
|
|
287
|
+
format: "date-time",
|
|
288
|
+
};
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
if (node.child.type === "string") {
|
|
292
|
+
// ==================== TEXT ARRAY ====================
|
|
293
|
+
props.items = {
|
|
294
|
+
type: "string",
|
|
295
|
+
};
|
|
296
|
+
if (node.child.format) props.items.format = node.child.format;
|
|
297
|
+
if (node.child.default) props.items.default = node.child.default;
|
|
298
|
+
if (node.child.minLength)
|
|
299
|
+
props.items.minLength = Number.parseInt(
|
|
300
|
+
`${node.child.minLength}`
|
|
301
|
+
);
|
|
302
|
+
if (node.child.maxLength)
|
|
303
|
+
props.items.maxLength = Number.parseInt(
|
|
304
|
+
`${node.child.maxLength}`
|
|
305
|
+
);
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
if (node.child.type === "integer") {
|
|
309
|
+
// ==================== INTEGER ARRAY ====================
|
|
310
|
+
props.items = {
|
|
311
|
+
type: "integer",
|
|
312
|
+
};
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
if (node.child.type === "array") {
|
|
316
|
+
// ==================== NESTED ARRAY ====================
|
|
317
|
+
// props.items = {
|
|
318
|
+
// type: "array",
|
|
319
|
+
// };
|
|
320
|
+
|
|
321
|
+
// ? Potentially something that can be done in the future, may need to write a generateArray function for this.
|
|
322
|
+
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
case "date": {
|
|
328
|
+
// ==================== DATE ====================
|
|
329
|
+
props.type = "string";
|
|
330
|
+
props.format = "date-time";
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (Object.keys(props).length > 0) {
|
|
336
|
+
if (node.title) props.title = node.title;
|
|
337
|
+
if (node.required) formObjs.required.push(propName);
|
|
338
|
+
formObjs.properties[propName] = props;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (Object.keys(propUi).length !== 0) ui[propName] = propUi;
|
|
342
|
+
});
|
|
343
|
+
return { form: formObjs, ui: ui };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const formData = generate(nodes);
|
|
347
|
+
|
|
348
|
+
const form: RJSFSchema = {
|
|
349
|
+
title: formConfig.title,
|
|
350
|
+
description: formConfig.description,
|
|
351
|
+
type: "object",
|
|
352
|
+
...formData.form,
|
|
353
|
+
};
|
|
354
|
+
return { form, ui: formData.ui };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* ---------------------- BASE VALIDATION ----------------------
|
|
359
|
+
*/
|
|
360
|
+
|
|
361
|
+
export function baseValidation(key: string, value: string, node: FormNodeType) {
|
|
362
|
+
if (key === "title" && !value && !node.propertyName) return false;
|
|
363
|
+
if (key === "propertyName" && !value && !node.title) return false;
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* ---------------------- GENERATE UPDATE NODE BY KEY LOCAL ----------------------
|
|
369
|
+
*/
|
|
370
|
+
|
|
371
|
+
export function generateUpdateNodeByKeyLocal(
|
|
372
|
+
isValid: (key: string, value: any) => boolean,
|
|
373
|
+
setNode: React.Dispatch<React.SetStateAction<any>>
|
|
374
|
+
) {
|
|
375
|
+
return (
|
|
376
|
+
key: string,
|
|
377
|
+
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
|
378
|
+
type?: string
|
|
379
|
+
) => {
|
|
380
|
+
updateNodeByKey(key, e, isValid, setNode, type);
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* ---------------------- UPDATE NODE BY KEY ----------------------
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
export function updateNodeByKey(
|
|
389
|
+
key: string,
|
|
390
|
+
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
|
391
|
+
isValid: (key: string, value: any) => boolean,
|
|
392
|
+
setNode: UseStateType<any>[1],
|
|
393
|
+
type?: string
|
|
394
|
+
) {
|
|
395
|
+
if (type === "check") {
|
|
396
|
+
// @ts-ignore
|
|
397
|
+
const val = e.target.checked;
|
|
398
|
+
if (!isValid(key, val)) return;
|
|
399
|
+
setNode((n: any) => ({ ...n, [key]: val }));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const val = e.target.value;
|
|
403
|
+
if (!isValid(key, val)) return;
|
|
404
|
+
setNode((n: any) => ({ ...n, [key]: val }));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* ---------------------- FIELD TITLE GENERATOR ----------------------
|
|
409
|
+
*/
|
|
410
|
+
|
|
411
|
+
export const formatTitle = (field: string) =>
|
|
412
|
+
field.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* ---------------------- FORMAT PROP NAME ----------------------
|
|
416
|
+
*/
|
|
417
|
+
|
|
418
|
+
export const formatPropName = (propName: string) =>
|
|
419
|
+
propName
|
|
420
|
+
.replace(/[()]/g, "")
|
|
421
|
+
.replace(/([A-Z])/g, " $1")
|
|
422
|
+
.trim()
|
|
423
|
+
.replace(/ +|--+/g, "-")
|
|
424
|
+
.toLowerCase();
|