@springmicro/forms 0.7.5 → 0.7.7

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.
@@ -1,424 +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();
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();