@@ -4,10 +4,16 @@ import { Input } from "@/components/ui/input";
4
4
import { Label } from "@/components/ui/label" ;
5
5
import JsonEditor from "./JsonEditor" ;
6
6
7
- export type JsonValue = string | number | boolean | null | JsonValue [ ] | { [ key : string ] : JsonValue } ;
7
+ export type JsonValue =
8
+ | string
9
+ | number
10
+ | boolean
11
+ | null
12
+ | JsonValue [ ]
13
+ | { [ key : string ] : JsonValue } ;
8
14
9
15
export type JsonSchemaType = {
10
- type : ' string' | ' number' | ' integer' | ' boolean' | ' array' | ' object' ;
16
+ type : " string" | " number" | " integer" | " boolean" | " array" | " object" ;
11
17
description ?: string ;
12
18
properties ?: Record < string , JsonSchemaType > ;
13
19
items ?: JsonSchemaType ;
@@ -24,32 +30,32 @@ interface DynamicJsonFormProps {
24
30
25
31
const formatFieldLabel = ( key : string ) : string => {
26
32
return key
27
- . replace ( / ( [ A - Z ] ) / g, ' $1' ) // Insert space before capital letters
28
- . replace ( / _ / g, ' ' ) // Replace underscores with spaces
29
- . replace ( / ^ \w / , c => c . toUpperCase ( ) ) ; // Capitalize first letter
33
+ . replace ( / ( [ A - Z ] ) / g, " $1" ) // Insert space before capital letters
34
+ . replace ( / _ / g, " " ) // Replace underscores with spaces
35
+ . replace ( / ^ \w / , ( c ) => c . toUpperCase ( ) ) ; // Capitalize first letter
30
36
} ;
31
37
32
38
const DynamicJsonForm = ( {
33
39
schema,
34
40
value,
35
41
onChange,
36
- maxDepth = 3
42
+ maxDepth = 3 ,
37
43
} : DynamicJsonFormProps ) => {
38
44
const [ isJsonMode , setIsJsonMode ] = useState ( false ) ;
39
45
const [ jsonError , setJsonError ] = useState < string > ( ) ;
40
46
41
47
const generateDefaultValue = ( propSchema : JsonSchemaType ) : JsonValue => {
42
48
switch ( propSchema . type ) {
43
- case ' string' :
44
- return '' ;
45
- case ' number' :
46
- case ' integer' :
49
+ case " string" :
50
+ return "" ;
51
+ case " number" :
52
+ case " integer" :
47
53
return 0 ;
48
- case ' boolean' :
54
+ case " boolean" :
49
55
return false ;
50
- case ' array' :
56
+ case " array" :
51
57
return [ ] ;
52
- case ' object' : {
58
+ case " object" : {
53
59
const obj : JsonObject = { } ;
54
60
if ( propSchema . properties ) {
55
61
Object . entries ( propSchema . properties ) . forEach ( ( [ key , prop ] ) => {
@@ -67,20 +73,27 @@ const DynamicJsonForm = ({
67
73
propSchema : JsonSchemaType ,
68
74
currentValue : JsonValue ,
69
75
path : string [ ] = [ ] ,
70
- depth : number = 0
76
+ depth : number = 0 ,
71
77
) => {
72
- if ( depth >= maxDepth && ( propSchema . type === 'object' || propSchema . type === 'array' ) ) {
78
+ if (
79
+ depth >= maxDepth &&
80
+ ( propSchema . type === "object" || propSchema . type === "array" )
81
+ ) {
73
82
// Render as JSON editor when max depth is reached
74
83
return (
75
84
< JsonEditor
76
- value = { JSON . stringify ( currentValue ?? generateDefaultValue ( propSchema ) , null , 2 ) }
85
+ value = { JSON . stringify (
86
+ currentValue ?? generateDefaultValue ( propSchema ) ,
87
+ null ,
88
+ 2 ,
89
+ ) }
77
90
onChange = { ( newValue ) => {
78
91
try {
79
92
const parsed = JSON . parse ( newValue ) ;
80
93
handleFieldChange ( path , parsed ) ;
81
94
setJsonError ( undefined ) ;
82
95
} catch ( err ) {
83
- setJsonError ( err instanceof Error ? err . message : ' Invalid JSON' ) ;
96
+ setJsonError ( err instanceof Error ? err . message : " Invalid JSON" ) ;
84
97
}
85
98
} }
86
99
error = { jsonError }
@@ -89,20 +102,25 @@ const DynamicJsonForm = ({
89
102
}
90
103
91
104
switch ( propSchema . type ) {
92
- case ' string' :
93
- case ' number' :
94
- case ' integer' :
105
+ case " string" :
106
+ case " number" :
107
+ case " integer" :
95
108
return (
96
109
< Input
97
- type = { propSchema . type === 'string' ? 'text' : 'number' }
98
- value = { ( currentValue as string | number ) ?? '' }
99
- onChange = { ( e ) => handleFieldChange ( path ,
100
- propSchema . type === 'string' ? e . target . value : Number ( e . target . value )
101
- ) }
110
+ type = { propSchema . type === "string" ? "text" : "number" }
111
+ value = { ( currentValue as string | number ) ?? "" }
112
+ onChange = { ( e ) =>
113
+ handleFieldChange (
114
+ path ,
115
+ propSchema . type === "string"
116
+ ? e . target . value
117
+ : Number ( e . target . value ) ,
118
+ )
119
+ }
102
120
placeholder = { propSchema . description }
103
121
/>
104
122
) ;
105
- case ' boolean' :
123
+ case " boolean" :
106
124
return (
107
125
< Input
108
126
type = "checkbox"
@@ -111,7 +129,7 @@ const DynamicJsonForm = ({
111
129
className = "w-4 h-4"
112
130
/>
113
131
) ;
114
- case ' object' :
132
+ case " object" :
115
133
if ( ! propSchema . properties ) return null ;
116
134
return (
117
135
< div className = "space-y-4 border rounded-md p-4" >
@@ -122,21 +140,21 @@ const DynamicJsonForm = ({
122
140
prop ,
123
141
( currentValue as JsonObject ) ?. [ key ] ,
124
142
[ ...path , key ] ,
125
- depth + 1
143
+ depth + 1 ,
126
144
) }
127
145
</ div >
128
146
) ) }
129
147
</ div >
130
148
) ;
131
- case ' array' : {
149
+ case " array" : {
132
150
const arrayValue = Array . isArray ( currentValue ) ? currentValue : [ ] ;
133
151
if ( ! propSchema . items ) return null ;
134
152
return (
135
153
< div className = "space-y-4" >
136
154
{ propSchema . description && (
137
155
< p className = "text-sm text-gray-600" > { propSchema . description } </ p >
138
156
) }
139
-
157
+
140
158
{ propSchema . items ?. description && (
141
159
< p className = "text-sm text-gray-500" >
142
160
Items: { propSchema . items . description }
@@ -145,36 +163,40 @@ const DynamicJsonForm = ({
145
163
146
164
< div className = "space-y-2" >
147
165
{ arrayValue . map ( ( item , index ) => (
148
- < div key = { index } className = "flex items-center gap-2" >
149
- { renderFormFields (
150
- propSchema . items as JsonSchemaType ,
151
- item ,
152
- [ ...path , index . toString ( ) ] ,
153
- depth + 1
154
- ) }
155
- < Button
156
- variant = "outline"
157
- size = "sm"
158
- onClick = { ( ) => {
159
- const newArray = [ ...arrayValue ] ;
160
- newArray . splice ( index , 1 ) ;
161
- handleFieldChange ( path , newArray ) ;
162
- } }
163
- >
164
- Remove
165
- </ Button >
166
- </ div >
167
- ) ) }
166
+ < div key = { index } className = "flex items-center gap-2" >
167
+ { renderFormFields (
168
+ propSchema . items as JsonSchemaType ,
169
+ item ,
170
+ [ ...path , index . toString ( ) ] ,
171
+ depth + 1 ,
172
+ ) }
173
+ < Button
174
+ variant = "outline"
175
+ size = "sm"
176
+ onClick = { ( ) => {
177
+ const newArray = [ ...arrayValue ] ;
178
+ newArray . splice ( index , 1 ) ;
179
+ handleFieldChange ( path , newArray ) ;
180
+ } }
181
+ >
182
+ Remove
183
+ </ Button >
184
+ </ div >
185
+ ) ) }
168
186
< Button
169
187
variant = "outline"
170
188
size = "sm"
171
189
onClick = { ( ) => {
172
- handleFieldChange (
173
- path ,
174
- [ ... arrayValue , generateDefaultValue ( propSchema . items as JsonSchemaType ) ]
175
- ) ;
190
+ handleFieldChange ( path , [
191
+ ... arrayValue ,
192
+ generateDefaultValue ( propSchema . items as JsonSchemaType ) ,
193
+ ] ) ;
176
194
} }
177
- title = { propSchema . items ?. description ? `Add new ${ propSchema . items . description } ` : 'Add new item' }
195
+ title = {
196
+ propSchema . items ?. description
197
+ ? `Add new ${ propSchema . items . description } `
198
+ : "Add new item"
199
+ }
178
200
>
179
201
Add Item
180
202
</ Button >
@@ -193,17 +215,21 @@ const DynamicJsonForm = ({
193
215
return ;
194
216
}
195
217
196
- const newValue = { ...( typeof value === 'object' && value !== null && ! Array . isArray ( value ) ? value : { } ) } as JsonObject ;
218
+ const newValue = {
219
+ ...( typeof value === "object" && value !== null && ! Array . isArray ( value )
220
+ ? value
221
+ : { } ) ,
222
+ } as JsonObject ;
197
223
let current : JsonObject = newValue ;
198
-
224
+
199
225
for ( let i = 0 ; i < path . length - 1 ; i ++ ) {
200
226
const key = path [ i ] ;
201
227
if ( ! ( key in current ) ) {
202
228
current [ key ] = { } ;
203
229
}
204
230
current = current [ key ] as JsonObject ;
205
231
}
206
-
232
+
207
233
current [ path [ path . length - 1 ] ] = fieldValue ;
208
234
onChange ( newValue ) ;
209
235
} ;
@@ -228,7 +254,7 @@ const DynamicJsonForm = ({
228
254
onChange ( JSON . parse ( newValue ) ) ;
229
255
setJsonError ( undefined ) ;
230
256
} catch ( err ) {
231
- setJsonError ( err instanceof Error ? err . message : ' Invalid JSON' ) ;
257
+ setJsonError ( err instanceof Error ? err . message : " Invalid JSON" ) ;
232
258
}
233
259
} }
234
260
error = { jsonError }
0 commit comments