@@ -113,6 +113,8 @@ const DynamicJsonForm = ({
113113 currentValue : JsonValue ,
114114 path : string [ ] = [ ] ,
115115 depth : number = 0 ,
116+ parentSchema ?: JsonSchemaType ,
117+ propertyName ?: string ,
116118 ) => {
117119 if (
118120 depth >= maxDepth &&
@@ -122,7 +124,7 @@ const DynamicJsonForm = ({
122124 return (
123125 < JsonEditor
124126 value = { JSON . stringify (
125- currentValue ?? generateDefaultValue ( propSchema ) ,
127+ currentValue ?? generateDefaultValue ( propSchema , propertyName , parentSchema ) ,
126128 null ,
127129 2 ,
128130 ) }
@@ -140,6 +142,9 @@ const DynamicJsonForm = ({
140142 ) ;
141143 }
142144
145+ // Check if this property is required in the parent schema
146+ const isRequired = parentSchema ?. required ?. includes ( propertyName || "" ) ?? false ;
147+
143148 switch ( propSchema . type ) {
144149 case "string" :
145150 return (
@@ -148,16 +153,11 @@ const DynamicJsonForm = ({
148153 value = { ( currentValue as string ) ?? "" }
149154 onChange = { ( e ) => {
150155 const val = e . target . value ;
151- // Allow clearing non-required fields by setting undefined
152- // This preserves the distinction between empty string and unset
153- if ( ! val && ! propSchema . required ) {
154- handleFieldChange ( path , undefined ) ;
155- } else {
156- handleFieldChange ( path , val ) ;
157- }
156+ // Always allow setting string values, including empty strings
157+ handleFieldChange ( path , val ) ;
158158 } }
159159 placeholder = { propSchema . description }
160- required = { propSchema . required }
160+ required = { isRequired }
161161 />
162162 ) ;
163163 case "number" :
@@ -167,9 +167,7 @@ const DynamicJsonForm = ({
167167 value = { ( currentValue as number ) ?. toString ( ) ?? "" }
168168 onChange = { ( e ) => {
169169 const val = e . target . value ;
170- // Allow clearing non-required number fields
171- // This preserves the distinction between 0 and unset
172- if ( ! val && ! propSchema . required ) {
170+ if ( ! val && ! isRequired ) {
173171 handleFieldChange ( path , undefined ) ;
174172 } else {
175173 const num = Number ( val ) ;
@@ -179,7 +177,7 @@ const DynamicJsonForm = ({
179177 }
180178 } }
181179 placeholder = { propSchema . description }
182- required = { propSchema . required }
180+ required = { isRequired }
183181 />
184182 ) ;
185183 case "integer" :
@@ -190,9 +188,7 @@ const DynamicJsonForm = ({
190188 value = { ( currentValue as number ) ?. toString ( ) ?? "" }
191189 onChange = { ( e ) => {
192190 const val = e . target . value ;
193- // Allow clearing non-required integer fields
194- // This preserves the distinction between 0 and unset
195- if ( ! val && ! propSchema . required ) {
191+ if ( ! val && ! isRequired ) {
196192 handleFieldChange ( path , undefined ) ;
197193 } else {
198194 const num = Number ( val ) ;
@@ -203,7 +199,7 @@ const DynamicJsonForm = ({
203199 }
204200 } }
205201 placeholder = { propSchema . description }
206- required = { propSchema . required }
202+ required = { isRequired }
207203 />
208204 ) ;
209205 case "boolean" :
@@ -213,7 +209,64 @@ const DynamicJsonForm = ({
213209 checked = { ( currentValue as boolean ) ?? false }
214210 onChange = { ( e ) => handleFieldChange ( path , e . target . checked ) }
215211 className = "w-4 h-4"
216- required = { propSchema . required }
212+ required = { isRequired }
213+ />
214+ ) ;
215+ case "object" :
216+ if ( ! propSchema . properties ) {
217+ return (
218+ < JsonEditor
219+ value = { JSON . stringify ( currentValue ?? { } , null , 2 ) }
220+ onChange = { ( newValue ) => {
221+ try {
222+ const parsed = JSON . parse ( newValue ) ;
223+ handleFieldChange ( path , parsed ) ;
224+ setJsonError ( undefined ) ;
225+ } catch ( err ) {
226+ setJsonError ( err instanceof Error ? err . message : "Invalid JSON" ) ;
227+ }
228+ } }
229+ error = { jsonError }
230+ />
231+ ) ;
232+ }
233+
234+ return (
235+ < div className = "space-y-2 border rounded p-3" >
236+ { Object . entries ( propSchema . properties ) . map ( ( [ key , subSchema ] ) => (
237+ < div key = { key } >
238+ < label className = "block text-sm font-medium mb-1" >
239+ { key }
240+ { propSchema . required ?. includes ( key ) && (
241+ < span className = "text-red-500 ml-1" > *</ span >
242+ ) }
243+ </ label >
244+ { renderFormFields (
245+ subSchema as JsonSchemaType ,
246+ ( currentValue as Record < string , JsonValue > ) ?. [ key ] ,
247+ [ ...path , key ] ,
248+ depth + 1 ,
249+ propSchema ,
250+ key ,
251+ ) }
252+ </ div >
253+ ) ) }
254+ </ div >
255+ ) ;
256+ case "array" :
257+ return (
258+ < JsonEditor
259+ value = { JSON . stringify ( currentValue ?? [ ] , null , 2 ) }
260+ onChange = { ( newValue ) => {
261+ try {
262+ const parsed = JSON . parse ( newValue ) ;
263+ handleFieldChange ( path , parsed ) ;
264+ setJsonError ( undefined ) ;
265+ } catch ( err ) {
266+ setJsonError ( err instanceof Error ? err . message : "Invalid JSON" ) ;
267+ }
268+ } }
269+ error = { jsonError }
217270 />
218271 ) ;
219272 default :
0 commit comments