@nolwenture/formik-jsonschema-form 0.1.0
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/.storybook/main.js +13 -0
- package/.storybook/preview.js +9 -0
- package/README.md +46 -0
- package/package.json +90 -0
- package/src/Form.stories.tsx +124 -0
- package/src/Form.test.tsx +10 -0
- package/src/Form.tsx +71 -0
- package/src/FormEditor.stories.tsx +17 -0
- package/src/FormEditor.tsx +53 -0
- package/src/_sampleData.ts +273 -0
- package/src/fields/AnyField.tsx +35 -0
- package/src/fields/ArrayField.tsx +177 -0
- package/src/fields/BooleanField.tsx +23 -0
- package/src/fields/DateField.tsx +20 -0
- package/src/fields/NullField.tsx +12 -0
- package/src/fields/NumberField.tsx +14 -0
- package/src/fields/ObjectField.tsx +68 -0
- package/src/fields/SchemaField.tsx +37 -0
- package/src/fields/StringField.tsx +89 -0
- package/src/fields/index.ts +9 -0
- package/src/fields/types.tsx +10 -0
- package/src/formEditorSchemas.ts +40 -0
- package/src/index.tsx +5 -0
- package/src/setupTests.ts +5 -0
- package/src/styledComponents.tsx +107 -0
- package/src/templates/ArrayFieldTemplate.tsx +46 -0
- package/src/templates/FieldTemplate.tsx +46 -0
- package/src/templates/ObjectFieldTemplate.tsx +22 -0
- package/src/templates/index.ts +3 -0
- package/src/types.ts +260 -0
- package/src/utils.ts +124 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { Schema, UiSchema } from "./types";
|
|
2
|
+
|
|
3
|
+
export type SimpleFormDataType = {
|
|
4
|
+
firstName: string;
|
|
5
|
+
lastName: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const simpleSchema: Schema = {
|
|
9
|
+
title: "TITLE",
|
|
10
|
+
properties: {
|
|
11
|
+
firstName: { type: "string", title: "First name" },
|
|
12
|
+
lastName: { type: "string", title: "Last name" },
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const simpleUiSchema: UiSchema = {
|
|
17
|
+
"ui:description": "Description text",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const completeSchema: Schema = {
|
|
21
|
+
title: "Inventory",
|
|
22
|
+
type: "object",
|
|
23
|
+
required: ["string", "number", "boolean", "enum"],
|
|
24
|
+
properties: {
|
|
25
|
+
string: {
|
|
26
|
+
type: "string",
|
|
27
|
+
title: "String",
|
|
28
|
+
description: "String description",
|
|
29
|
+
},
|
|
30
|
+
number: {
|
|
31
|
+
type: "number",
|
|
32
|
+
title: "Number",
|
|
33
|
+
description: "Number description",
|
|
34
|
+
},
|
|
35
|
+
date: {
|
|
36
|
+
type: "string",
|
|
37
|
+
title: "Date",
|
|
38
|
+
description: "Date description",
|
|
39
|
+
},
|
|
40
|
+
boolean: {
|
|
41
|
+
type: "boolean",
|
|
42
|
+
title: "Boolean",
|
|
43
|
+
description: "Boolean description",
|
|
44
|
+
default: false,
|
|
45
|
+
},
|
|
46
|
+
enum: {
|
|
47
|
+
type: "number",
|
|
48
|
+
title: "Enum",
|
|
49
|
+
description: "Enum description",
|
|
50
|
+
enum: [1, 2, 3, 4],
|
|
51
|
+
},
|
|
52
|
+
enumOneOf: {
|
|
53
|
+
type: "number",
|
|
54
|
+
title: "Enum oneOf (non-standard)",
|
|
55
|
+
description: "Enum oneOf description",
|
|
56
|
+
oneOf: [
|
|
57
|
+
{ const: 1, title: "one" },
|
|
58
|
+
{ const: 2, title: "two" },
|
|
59
|
+
{ const: 3, title: "three" },
|
|
60
|
+
{ const: 4, title: "four" },
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
enumAnyOf: {
|
|
64
|
+
type: "number",
|
|
65
|
+
title: "Enum anyOf (non-standard)",
|
|
66
|
+
description: "Enum anyOf description",
|
|
67
|
+
anyOf: [
|
|
68
|
+
{ const: 1, title: "one" },
|
|
69
|
+
{ const: 2, title: "two" },
|
|
70
|
+
{ const: 3, title: "three" },
|
|
71
|
+
{ const: 4, title: "four" },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
null: { type: "null", title: "Null", description: "Null description" },
|
|
75
|
+
nested: {
|
|
76
|
+
type: "object",
|
|
77
|
+
title: "Nested object",
|
|
78
|
+
description: "Nested object description",
|
|
79
|
+
properties: {
|
|
80
|
+
string: {
|
|
81
|
+
type: "string",
|
|
82
|
+
title: "Nested String",
|
|
83
|
+
description: "Nested String description",
|
|
84
|
+
},
|
|
85
|
+
number: {
|
|
86
|
+
type: "number",
|
|
87
|
+
title: "Nested Number",
|
|
88
|
+
description: "Nested Number description",
|
|
89
|
+
},
|
|
90
|
+
date: {
|
|
91
|
+
type: "string",
|
|
92
|
+
title: "Date",
|
|
93
|
+
description: "Date description",
|
|
94
|
+
},
|
|
95
|
+
boolean: {
|
|
96
|
+
type: "boolean",
|
|
97
|
+
title: "Nested Boolean",
|
|
98
|
+
description: "Nested Boolean description",
|
|
99
|
+
default: false,
|
|
100
|
+
},
|
|
101
|
+
enum: {
|
|
102
|
+
type: "number",
|
|
103
|
+
title: "Nested Enum",
|
|
104
|
+
description: "Nested Enum description",
|
|
105
|
+
enum: [1, 2, 3, 4],
|
|
106
|
+
},
|
|
107
|
+
null: {
|
|
108
|
+
type: "null",
|
|
109
|
+
title: "Nested Null",
|
|
110
|
+
description: "Nested Null description",
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
additionalProperties: {
|
|
114
|
+
type: "string",
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
array: {
|
|
118
|
+
type: "array",
|
|
119
|
+
title: "Array",
|
|
120
|
+
description: "Array description",
|
|
121
|
+
additionalItems: {
|
|
122
|
+
type: "string",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
partiallyFixedArray: {
|
|
126
|
+
type: "array",
|
|
127
|
+
title: "Partially fixed array",
|
|
128
|
+
description: "Partially fixed array description",
|
|
129
|
+
items: [{ type: "string", default: "Hello" }, { type: "string" }],
|
|
130
|
+
additionalItems: {
|
|
131
|
+
type: "string",
|
|
132
|
+
default: "defaultForAdditional",
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
enumArray: {
|
|
136
|
+
type: "array",
|
|
137
|
+
title: "Enum array",
|
|
138
|
+
description: "Enum array description",
|
|
139
|
+
additionalItems: {
|
|
140
|
+
type: "number",
|
|
141
|
+
enum: [1, 2, 3, 4],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
objectArray: {
|
|
145
|
+
type: "array",
|
|
146
|
+
title: "Object array",
|
|
147
|
+
description: "Object array description",
|
|
148
|
+
additionalItems: {
|
|
149
|
+
type: "object",
|
|
150
|
+
title: "Array object",
|
|
151
|
+
description: "Array object description",
|
|
152
|
+
properties: {
|
|
153
|
+
string: {
|
|
154
|
+
type: "string",
|
|
155
|
+
title: "Array String",
|
|
156
|
+
description: "Array String description",
|
|
157
|
+
},
|
|
158
|
+
number: {
|
|
159
|
+
type: "number",
|
|
160
|
+
title: "Array Number",
|
|
161
|
+
description: "Array Number description",
|
|
162
|
+
},
|
|
163
|
+
date: {
|
|
164
|
+
type: "string",
|
|
165
|
+
title: "Date",
|
|
166
|
+
description: "Date description",
|
|
167
|
+
},
|
|
168
|
+
boolean: {
|
|
169
|
+
type: "boolean",
|
|
170
|
+
title: "Array Boolean",
|
|
171
|
+
description: "Array Boolean description",
|
|
172
|
+
default: false,
|
|
173
|
+
},
|
|
174
|
+
enum: {
|
|
175
|
+
type: "array",
|
|
176
|
+
title: "Array Enum",
|
|
177
|
+
description: "Array Enum description",
|
|
178
|
+
items: {
|
|
179
|
+
type: "number",
|
|
180
|
+
enum: [1, 2, 3, 4],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
null: {
|
|
184
|
+
type: "null",
|
|
185
|
+
title: "Array Null",
|
|
186
|
+
description: "Array Null description",
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const completeUiSchema: UiSchema = {
|
|
195
|
+
string: {
|
|
196
|
+
"ui:help": "String help",
|
|
197
|
+
"ui:options": {
|
|
198
|
+
placeholder: "String placeholder",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
number: {
|
|
202
|
+
"ui:help": "Number help",
|
|
203
|
+
"ui:options": {
|
|
204
|
+
placeholder: "Number placeholder",
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
date: {
|
|
208
|
+
"ui:field": "DateField",
|
|
209
|
+
"ui:help": "Date help",
|
|
210
|
+
},
|
|
211
|
+
boolean: {
|
|
212
|
+
"ui:help": "Boolean help",
|
|
213
|
+
"ui:options": {
|
|
214
|
+
valueDescription: "Boolean specific label",
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
enum: {
|
|
218
|
+
"ui:help": "Enum help",
|
|
219
|
+
},
|
|
220
|
+
null: {
|
|
221
|
+
"ui:help": "Null help",
|
|
222
|
+
},
|
|
223
|
+
nested: {
|
|
224
|
+
string: {
|
|
225
|
+
"ui:help": "String help",
|
|
226
|
+
"ui:options": {
|
|
227
|
+
placeholder: "String placeholder",
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
number: {
|
|
231
|
+
"ui:help": "Number help",
|
|
232
|
+
"ui:options": {
|
|
233
|
+
placeholder: "Number placeholder",
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
date: {
|
|
237
|
+
"ui:field": "DateField",
|
|
238
|
+
"ui:help": "Date help",
|
|
239
|
+
},
|
|
240
|
+
boolean: {
|
|
241
|
+
"ui:help": "Boolean help",
|
|
242
|
+
"ui:options": {
|
|
243
|
+
valueDescription: "Boolean specific label",
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
enum: {
|
|
247
|
+
"ui:help": "Enum help",
|
|
248
|
+
},
|
|
249
|
+
null: {
|
|
250
|
+
"ui:help": "Null help",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export const generatedSchema: Schema = {
|
|
256
|
+
type: "object",
|
|
257
|
+
properties: {
|
|
258
|
+
object: {
|
|
259
|
+
type: "object",
|
|
260
|
+
additionalProperties: {},
|
|
261
|
+
},
|
|
262
|
+
value: {},
|
|
263
|
+
array: {
|
|
264
|
+
type: "array",
|
|
265
|
+
additionalItems: {},
|
|
266
|
+
},
|
|
267
|
+
property: {
|
|
268
|
+
type: "string",
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
required: ["object", "value", "property"],
|
|
272
|
+
additionalProperties: false,
|
|
273
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { JSONSchema7, JSONSchema7TypeName } from "json-schema";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { FieldProps } from "../types";
|
|
5
|
+
import { SchemaField } from "./SchemaField";
|
|
6
|
+
|
|
7
|
+
import { COMPONENT_TYPES } from "./types";
|
|
8
|
+
|
|
9
|
+
export function AnyField(props: FieldProps) {
|
|
10
|
+
const [type, setType] = useState<JSONSchema7TypeName>("string");
|
|
11
|
+
const { name, schema, uiSchema } = props;
|
|
12
|
+
const enrichedSchema = useMemo<JSONSchema7>(() => {
|
|
13
|
+
switch (type) {
|
|
14
|
+
case "object":
|
|
15
|
+
return { ...(schema || {}), type, additionalProperties: {} };
|
|
16
|
+
case "array":
|
|
17
|
+
return { ...(schema || {}), type, additionalItems: {} };
|
|
18
|
+
default:
|
|
19
|
+
return { ...(schema || {}), type };
|
|
20
|
+
}
|
|
21
|
+
}, [type, schema]);
|
|
22
|
+
return (
|
|
23
|
+
<div>
|
|
24
|
+
<select
|
|
25
|
+
value={type}
|
|
26
|
+
onChange={(event) => setType(event.target.value as JSONSchema7TypeName)}
|
|
27
|
+
>
|
|
28
|
+
{Object.keys(COMPONENT_TYPES).map((type) => (
|
|
29
|
+
<option value={type}>{type}</option>
|
|
30
|
+
))}
|
|
31
|
+
</select>
|
|
32
|
+
<SchemaField name={name} schema={enrichedSchema} uiSchema={uiSchema} />
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { useContext, useMemo } from "react";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import { FormRegistry } from "../Form";
|
|
4
|
+
import { useField } from "formik";
|
|
5
|
+
import { ArrayFieldButtonPurpose, FieldProps, Schema } from "../types";
|
|
6
|
+
import { useExtractUiSchema } from "../utils";
|
|
7
|
+
|
|
8
|
+
function appendArray(values: any[] | undefined): any[] {
|
|
9
|
+
return values ? [...values, ""] : [""];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function removeFromArray(values: any[], index: number) {
|
|
13
|
+
return [...values.slice(0, index), ...values.slice(index + 1)];
|
|
14
|
+
}
|
|
15
|
+
function moveInArray(values: any[], index: number, direction: -1 | 1) {
|
|
16
|
+
if (direction === -1) {
|
|
17
|
+
return [
|
|
18
|
+
...values.slice(0, index - 1),
|
|
19
|
+
values[index],
|
|
20
|
+
values[index - 1],
|
|
21
|
+
...values.slice(index + 1),
|
|
22
|
+
];
|
|
23
|
+
} else {
|
|
24
|
+
return [
|
|
25
|
+
...values.slice(0, index),
|
|
26
|
+
values[index + 1],
|
|
27
|
+
values[index],
|
|
28
|
+
...values.slice(index + 2),
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ArrayField(props: FieldProps) {
|
|
34
|
+
const {
|
|
35
|
+
fields: { SchemaField },
|
|
36
|
+
templates: { ArrayFieldTemplate },
|
|
37
|
+
components: {
|
|
38
|
+
ArrayFieldItem,
|
|
39
|
+
ArrayFieldList,
|
|
40
|
+
ArrayFieldItemButtons,
|
|
41
|
+
ArrayFieldButton,
|
|
42
|
+
},
|
|
43
|
+
} = useContext(FormRegistry);
|
|
44
|
+
const { name, schema, uiSchema } = props;
|
|
45
|
+
const [{ value }, _, { setValue }] = useField<any[]>(name);
|
|
46
|
+
|
|
47
|
+
const {
|
|
48
|
+
items,
|
|
49
|
+
additionalItems,
|
|
50
|
+
}: { items: Schema[]; additionalItems: Schema | undefined } = useMemo(() => {
|
|
51
|
+
let items =
|
|
52
|
+
schema.items && typeof schema.items !== "boolean" ? schema.items : [];
|
|
53
|
+
if (!Array.isArray(items)) {
|
|
54
|
+
items = [items];
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
items: items.filter((item) => typeof item !== "boolean") as Schema[],
|
|
58
|
+
additionalItems:
|
|
59
|
+
typeof schema.additionalItems !== "boolean"
|
|
60
|
+
? schema.additionalItems
|
|
61
|
+
: undefined,
|
|
62
|
+
};
|
|
63
|
+
}, [schema]);
|
|
64
|
+
|
|
65
|
+
//This whole mess would give backwards compatibility, but maybe we don't do it
|
|
66
|
+
// const {
|
|
67
|
+
// items,
|
|
68
|
+
// additionalItems,
|
|
69
|
+
// }: {
|
|
70
|
+
// items: Schema[];
|
|
71
|
+
// additionalItems: Schema | undefined;
|
|
72
|
+
// } = useMemo(
|
|
73
|
+
// () =>
|
|
74
|
+
// !schema.additionalItems
|
|
75
|
+
// ? {
|
|
76
|
+
// additionalItems:
|
|
77
|
+
// typeof schema.items !== "boolean"
|
|
78
|
+
// ? Array.isArray(schema.items)
|
|
79
|
+
// ? typeof schema.items[0] !== "boolean"
|
|
80
|
+
// ? schema.items[0]
|
|
81
|
+
// : undefined
|
|
82
|
+
// : typeof schema.items !== "boolean"
|
|
83
|
+
// ? schema.items
|
|
84
|
+
// : undefined
|
|
85
|
+
// : undefined,
|
|
86
|
+
// items: [],
|
|
87
|
+
// }
|
|
88
|
+
// : {
|
|
89
|
+
// additionalItems:
|
|
90
|
+
// typeof schema.additionalItems !== "boolean"
|
|
91
|
+
// ? schema.additionalItems
|
|
92
|
+
// : undefined,
|
|
93
|
+
// items: schema.items
|
|
94
|
+
// ? Array.isArray(schema.items)
|
|
95
|
+
// ? (schema.items.filter(
|
|
96
|
+
// (item) => typeof item !== "boolean"
|
|
97
|
+
// ) as Schema[])
|
|
98
|
+
// : typeof schema.items !== "boolean"
|
|
99
|
+
// ? [schema.items]
|
|
100
|
+
// : []
|
|
101
|
+
// : [],
|
|
102
|
+
// },
|
|
103
|
+
// [schema]
|
|
104
|
+
// );
|
|
105
|
+
// Bacwards compatibility ends
|
|
106
|
+
|
|
107
|
+
const { props: inputProps, disabled } = useExtractUiSchema(uiSchema);
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<ArrayFieldTemplate {...props}>
|
|
111
|
+
<ArrayFieldList>
|
|
112
|
+
{(items || []).map((itemSchema, index) => (
|
|
113
|
+
<ArrayFieldItem>
|
|
114
|
+
<SchemaField
|
|
115
|
+
key={index}
|
|
116
|
+
name={`${name}[${index}]`}
|
|
117
|
+
schema={typeof itemSchema !== "boolean" ? itemSchema : {}}
|
|
118
|
+
uiSchema={uiSchema?.[index] || uiSchema?.items}
|
|
119
|
+
/>
|
|
120
|
+
</ArrayFieldItem>
|
|
121
|
+
))}
|
|
122
|
+
{(value?.slice(items.length) || []).map((_, index) => (
|
|
123
|
+
<ArrayFieldItem>
|
|
124
|
+
<SchemaField
|
|
125
|
+
key={index}
|
|
126
|
+
{...{
|
|
127
|
+
disabled,
|
|
128
|
+
...inputProps,
|
|
129
|
+
}}
|
|
130
|
+
name={`${name}[${index + items.length}]`}
|
|
131
|
+
schema={
|
|
132
|
+
additionalItems && typeof additionalItems !== "boolean"
|
|
133
|
+
? additionalItems
|
|
134
|
+
: {}
|
|
135
|
+
}
|
|
136
|
+
uiSchema={{
|
|
137
|
+
disabled,
|
|
138
|
+
...(uiSchema?.[index + items.length] || uiSchema?.items),
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
<ArrayFieldItemButtons>
|
|
142
|
+
<ArrayFieldButton
|
|
143
|
+
purpose={ArrayFieldButtonPurpose.REMOVE}
|
|
144
|
+
onClick={() =>
|
|
145
|
+
setValue(removeFromArray(value, index + items.length))
|
|
146
|
+
}
|
|
147
|
+
/>
|
|
148
|
+
|
|
149
|
+
{value.length > 1 && index > 0 && (
|
|
150
|
+
<ArrayFieldButton
|
|
151
|
+
purpose={ArrayFieldButtonPurpose.MOVE_UP}
|
|
152
|
+
onClick={() =>
|
|
153
|
+
setValue(moveInArray(value, index + items.length, -1))
|
|
154
|
+
}
|
|
155
|
+
/>
|
|
156
|
+
)}
|
|
157
|
+
{value.length > 1 && index < value.length - 1 && (
|
|
158
|
+
<ArrayFieldButton
|
|
159
|
+
purpose={ArrayFieldButtonPurpose.MOVE_DOWN}
|
|
160
|
+
onClick={() =>
|
|
161
|
+
setValue(moveInArray(value, index + items.length, 1))
|
|
162
|
+
}
|
|
163
|
+
/>
|
|
164
|
+
)}
|
|
165
|
+
</ArrayFieldItemButtons>
|
|
166
|
+
</ArrayFieldItem>
|
|
167
|
+
))}
|
|
168
|
+
</ArrayFieldList>
|
|
169
|
+
{additionalItems && (
|
|
170
|
+
<ArrayFieldButton
|
|
171
|
+
purpose={ArrayFieldButtonPurpose.ADD}
|
|
172
|
+
onClick={() => setValue(appendArray(value))}
|
|
173
|
+
/>
|
|
174
|
+
)}
|
|
175
|
+
</ArrayFieldTemplate>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { FormRegistry } from "../Form";
|
|
3
|
+
import { FieldProps } from "../types";
|
|
4
|
+
import { useExtractUiSchema, useSetDefault } from "../utils";
|
|
5
|
+
|
|
6
|
+
export function BooleanField(props: FieldProps) {
|
|
7
|
+
const {
|
|
8
|
+
templates: { FieldTemplate },
|
|
9
|
+
components: { FieldInput },
|
|
10
|
+
} = useContext(FormRegistry);
|
|
11
|
+
useSetDefault(props);
|
|
12
|
+
const { name, uiSchema } = props;
|
|
13
|
+
const { label, props: inputProps } = useExtractUiSchema(uiSchema);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<FieldTemplate {...props}>
|
|
17
|
+
<>
|
|
18
|
+
<FieldInput {...inputProps} name={name} type="checkbox" />
|
|
19
|
+
{label}
|
|
20
|
+
</>
|
|
21
|
+
</FieldTemplate>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { FormRegistry } from "../Form";
|
|
3
|
+
import { FieldProps } from "../types";
|
|
4
|
+
import { useExtractUiSchema, useSetDefault } from "../utils";
|
|
5
|
+
|
|
6
|
+
export function DateField(props: FieldProps) {
|
|
7
|
+
const {
|
|
8
|
+
templates: { FieldTemplate },
|
|
9
|
+
components: { FieldInput },
|
|
10
|
+
} = useContext(FormRegistry);
|
|
11
|
+
useSetDefault(props);
|
|
12
|
+
const { name, uiSchema } = props;
|
|
13
|
+
const { props: inputProps } = useExtractUiSchema(uiSchema);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<FieldTemplate {...props}>
|
|
17
|
+
<FieldInput {...inputProps} name={name} type="date" />
|
|
18
|
+
</FieldTemplate>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { FormRegistry } from "../Form";
|
|
3
|
+
import { FieldProps } from "../types";
|
|
4
|
+
|
|
5
|
+
export function NullField(props: FieldProps) {
|
|
6
|
+
const {
|
|
7
|
+
templates: { FieldTemplate },
|
|
8
|
+
} = useContext(FormRegistry);
|
|
9
|
+
const { name } = props;
|
|
10
|
+
|
|
11
|
+
return <FieldTemplate {...props}></FieldTemplate>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FieldProps } from "../types";
|
|
2
|
+
import { StringField } from "./StringField";
|
|
3
|
+
|
|
4
|
+
export function NumberField(props: FieldProps) {
|
|
5
|
+
return (
|
|
6
|
+
<StringField
|
|
7
|
+
{...props}
|
|
8
|
+
uiSchema={{
|
|
9
|
+
...props.uiSchema,
|
|
10
|
+
"ui:props": { ...(props.uiSchema?.["ui:props"] || {}), type: "number" },
|
|
11
|
+
}}
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useField } from "formik";
|
|
2
|
+
import { useContext, useMemo } from "react";
|
|
3
|
+
import { FormRegistry } from "../Form";
|
|
4
|
+
import { FieldProps, ObjectFieldButtonPurpose } from "../types";
|
|
5
|
+
|
|
6
|
+
export function ObjectField(props: FieldProps) {
|
|
7
|
+
const {
|
|
8
|
+
fields: { SchemaField },
|
|
9
|
+
templates: { ObjectFieldTemplate },
|
|
10
|
+
components: {
|
|
11
|
+
ObjectFieldItem,
|
|
12
|
+
ObjectFieldList,
|
|
13
|
+
ObjectFieldItemButtons,
|
|
14
|
+
ObjectFieldButton,
|
|
15
|
+
},
|
|
16
|
+
} = useContext(FormRegistry);
|
|
17
|
+
const { name, schema, uiSchema } = props;
|
|
18
|
+
const [{ value }, _, { setValue }] = useField<Record<string, any>>(name);
|
|
19
|
+
const { properties, additionalProperties } = schema;
|
|
20
|
+
const propertyKeys = useMemo(
|
|
21
|
+
() => Object.keys(properties || {}),
|
|
22
|
+
[properties]
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const additionalValues = useMemo(
|
|
26
|
+
() =>
|
|
27
|
+
Object.fromEntries(
|
|
28
|
+
Object.entries(value || {}).filter(
|
|
29
|
+
([key, value]) => !propertyKeys.includes(key)
|
|
30
|
+
)
|
|
31
|
+
),
|
|
32
|
+
[value, propertyKeys]
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<ObjectFieldTemplate {...props}>
|
|
37
|
+
{Object.entries(properties || {}).map(([childName, childSchema]) => (
|
|
38
|
+
<SchemaField
|
|
39
|
+
key={childName}
|
|
40
|
+
name={name ? [name, childName].join(".") : childName}
|
|
41
|
+
schema={typeof childSchema !== "boolean" ? childSchema : {}}
|
|
42
|
+
uiSchema={uiSchema?.[childName]}
|
|
43
|
+
/>
|
|
44
|
+
))}
|
|
45
|
+
{Object.entries(additionalValues || {}).map(
|
|
46
|
+
([childName, childSchema]) => (
|
|
47
|
+
<SchemaField
|
|
48
|
+
key={childName}
|
|
49
|
+
name={name ? [name, childName].join(".") : childName}
|
|
50
|
+
schema={typeof childSchema !== "boolean" ? childSchema : {}}
|
|
51
|
+
uiSchema={uiSchema?.[childName]}
|
|
52
|
+
/>
|
|
53
|
+
)
|
|
54
|
+
)}
|
|
55
|
+
{additionalProperties && (
|
|
56
|
+
<ObjectFieldButton
|
|
57
|
+
purpose={ObjectFieldButtonPurpose.ADD}
|
|
58
|
+
onClick={() => {
|
|
59
|
+
const key = prompt("Give key");
|
|
60
|
+
if (key) {
|
|
61
|
+
setValue({ ...value, [key]: undefined });
|
|
62
|
+
}
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
)}
|
|
66
|
+
</ObjectFieldTemplate>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { Field, FieldProps, Schema, UiSchema } from "../types";
|
|
3
|
+
import { useExtractUiSchema, getSchemaType } from "../utils";
|
|
4
|
+
import { FormRegistry } from "../Form";
|
|
5
|
+
|
|
6
|
+
import { COMPONENT_TYPES } from "./types";
|
|
7
|
+
|
|
8
|
+
function getFieldComponent(
|
|
9
|
+
schema: Schema,
|
|
10
|
+
uiSchema: UiSchema,
|
|
11
|
+
fields: Record<string, Field>
|
|
12
|
+
) {
|
|
13
|
+
const { field } = useExtractUiSchema(uiSchema);
|
|
14
|
+
if (typeof field === "function") {
|
|
15
|
+
return field;
|
|
16
|
+
}
|
|
17
|
+
if (typeof field === "string" && field in fields) {
|
|
18
|
+
return fields[field];
|
|
19
|
+
}
|
|
20
|
+
let schemaType = getSchemaType(schema);
|
|
21
|
+
if (Array.isArray(schemaType)) {
|
|
22
|
+
schemaType = schemaType[0];
|
|
23
|
+
}
|
|
24
|
+
const componentName = COMPONENT_TYPES[schemaType || "any"];
|
|
25
|
+
|
|
26
|
+
return componentName in fields
|
|
27
|
+
? fields[componentName]
|
|
28
|
+
: fields.UnsupportedField;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function SchemaField(props: FieldProps) {
|
|
32
|
+
const { schema, uiSchema } = props;
|
|
33
|
+
const { fields } = useContext(FormRegistry);
|
|
34
|
+
const FieldComponent = getFieldComponent(schema, uiSchema, fields);
|
|
35
|
+
|
|
36
|
+
return <FieldComponent {...props} />;
|
|
37
|
+
}
|