@@ -125,6 +125,9 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
125125 const [ rawJsonValue , setRawJsonValue ] = useState < string > (
126126 JSON . stringify ( value ?? generateDefaultValue ( schema ) , null , 2 ) ,
127127 ) ;
128+ const [ numericInputDrafts , setNumericInputDrafts ] = useState <
129+ Record < string , string >
130+ > ( { } ) ;
128131
129132 // Use a ref to manage debouncing timeouts to avoid parsing JSON
130133 // on every keystroke which would be inefficient and error-prone
@@ -134,6 +137,37 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
134137 return ! ! jsonError ;
135138 } ;
136139
140+ const getPathKey = ( path : string [ ] ) =>
141+ path . length === 0 ? "$root" : path . join ( "." ) ;
142+
143+ const getNumericDisplayValue = (
144+ path : string [ ] ,
145+ currentValue : JsonValue ,
146+ ) : string => {
147+ const pathKey = getPathKey ( path ) ;
148+ if ( Object . prototype . hasOwnProperty . call ( numericInputDrafts , pathKey ) ) {
149+ return numericInputDrafts [ pathKey ] ;
150+ }
151+ return typeof currentValue === "number" ? currentValue . toString ( ) : "" ;
152+ } ;
153+
154+ const updateNumericDraft = ( path : string [ ] , draftValue : string ) => {
155+ const pathKey = getPathKey ( path ) ;
156+ setNumericInputDrafts ( ( prev ) => ( { ...prev , [ pathKey ] : draftValue } ) ) ;
157+ } ;
158+
159+ const clearNumericDraft = ( path : string [ ] ) => {
160+ const pathKey = getPathKey ( path ) ;
161+ setNumericInputDrafts ( ( prev ) => {
162+ if ( ! Object . prototype . hasOwnProperty . call ( prev , pathKey ) ) {
163+ return prev ;
164+ }
165+ const next = { ...prev } ;
166+ delete next [ pathKey ] ;
167+ return next ;
168+ } ) ;
169+ } ;
170+
137171 // Debounce JSON parsing and parent updates to handle typing gracefully
138172 const debouncedUpdateParent = useCallback (
139173 ( jsonString : string ) => {
@@ -429,9 +463,10 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
429463 return (
430464 < Input
431465 type = "number"
432- value = { ( currentValue as number ) ?. toString ( ) ?? "" }
466+ value = { getNumericDisplayValue ( path , currentValue ) }
433467 onChange = { ( e ) => {
434468 const val = e . target . value ;
469+ updateNumericDraft ( path , val ) ;
435470 if ( ! val && ! isRequired ) {
436471 handleFieldChange ( path , undefined ) ;
437472 } else {
@@ -441,6 +476,19 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
441476 }
442477 }
443478 } }
479+ onBlur = { ( e ) => {
480+ const val = e . target . value ;
481+ if ( ! val ) {
482+ clearNumericDraft ( path ) ;
483+ return ;
484+ }
485+
486+ const num = Number ( val ) ;
487+ if ( ! isNaN ( num ) ) {
488+ handleFieldChange ( path , num ) ;
489+ }
490+ clearNumericDraft ( path ) ;
491+ } }
444492 placeholder = { propSchema . description }
445493 required = { isRequired }
446494 min = { propSchema . minimum }
@@ -453,9 +501,10 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
453501 < Input
454502 type = "number"
455503 step = "1"
456- value = { ( currentValue as number ) ?. toString ( ) ?? "" }
504+ value = { getNumericDisplayValue ( path , currentValue ) }
457505 onChange = { ( e ) => {
458506 const val = e . target . value ;
507+ updateNumericDraft ( path , val ) ;
459508 if ( ! val && ! isRequired ) {
460509 handleFieldChange ( path , undefined ) ;
461510 } else {
@@ -465,6 +514,19 @@ const DynamicJsonForm = forwardRef<DynamicJsonFormRef, DynamicJsonFormProps>(
465514 }
466515 }
467516 } }
517+ onBlur = { ( e ) => {
518+ const val = e . target . value ;
519+ if ( ! val ) {
520+ clearNumericDraft ( path ) ;
521+ return ;
522+ }
523+
524+ const num = Number ( val ) ;
525+ if ( ! isNaN ( num ) && Number . isInteger ( num ) ) {
526+ handleFieldChange ( path , num ) ;
527+ }
528+ clearNumericDraft ( path ) ;
529+ } }
468530 placeholder = { propSchema . description }
469531 required = { isRequired }
470532 min = { propSchema . minimum }
0 commit comments