1- import { useCallback } from "react"
1+ import { useCallback , useRef } from "react"
22import { ControlProps , RendererProps } from "@jsonforms/core"
33import { InputNumber as AntdInputNumber } from "antd"
44import { NumericControlOptions } from "../ui-schema"
@@ -12,12 +12,16 @@ import {
1212type AntdInputNumberProps = React . ComponentProps < typeof AntdInputNumber >
1313type InputNumberProps = AntdInputNumberProps & RendererProps & ControlProps
1414
15+ const hasLeadingZero = ( value ?: string ) =>
16+ value && value . substring ( 0 , 1 ) === "0"
17+
1518export function InputNumber ( {
1619 handleChange,
1720 path,
1821 schema,
1922 ...props
2023} : InputNumberProps ) {
24+ const incomingValue = useRef < string > ( )
2125 const ariaLabel = props . label || schema . description || "Value"
2226
2327 const defaultValue = schema . default as number | undefined
@@ -71,6 +75,18 @@ export function InputNumber({
7175 // TODO: Revist useCallback use - was meant to prevent re-renders
7276 const formatter = useCallback (
7377 ( value ?: string | number ) : string => {
78+ /**
79+ * See block comment in `parser()` below.
80+ */
81+ if (
82+ value &&
83+ typeof incomingValue . current === "string" &&
84+ hasLeadingZero ( incomingValue . current ) &&
85+ ! isNaN ( parseFloat ( incomingValue . current ) )
86+ ) {
87+ return incomingValue . current
88+ }
89+
7490 if ( value !== "" && value !== undefined ) {
7591 if ( isPercentage ) {
7692 const valueFloat =
@@ -87,6 +103,21 @@ export function InputNumber({
87103 // TODO: Revist useCallback use - was meant to prevent re-renders
88104 const parser = useCallback (
89105 ( value ?: string ) : number | undefined => {
106+ /**
107+ * When a parser & formatter are both present it triggers a
108+ * double-render of the wrapped AntD InputNumber. This double-render
109+ * also triggers a double-set of the incoming value, which will format
110+ * and remove leading zeros. We'd like to preserve those leading zeros
111+ * if they're part of a valid number, because a user may simply want
112+ * to update a value like 30000 to 300 without having to retype the full
113+ * number.
114+ *
115+ * The incomingValue.current ref updates on every input value which allows
116+ * the subsequent formatter call to access the raw value and make decisions
117+ * based on whether or not it has a leading zero, while avoiding
118+ * any async issues caused by needing to set or wait for state changes.
119+ */
120+ incomingValue . current = value
90121 const isNumeric = value ? ! isNaN ( Number ( value ) ) : false
91122 if ( isNumeric && value !== undefined ) {
92123 if ( isPercentage ) {
0 commit comments