Browse Source

Merge pull request #1706 from Avaiga/1703-bug-number-visual-element-does-not-update-when-using-arrow-to-change-value

Update number visual element with stepper button & shift key
Nam Nguyen 8 months ago
parent
commit
0d7e331482
1 changed files with 70 additions and 41 deletions
  1. 70 41
      frontend/taipy-gui/src/components/Taipy/Input.tsx

+ 70 - 41
frontend/taipy-gui/src/components/Taipy/Input.tsx

@@ -80,27 +80,47 @@ const Input = (props: TaipyInputProps) => {
     const min = useDynamicProperty(props.min, props.defaultMin, undefined);
     const max = useDynamicProperty(props.max, props.defaultMax, undefined);
 
-    const textSx = useMemo(() => props.width ? {...numberSx, maxWidth: getCssSize(props.width)} : numberSx, [props.width]);
+    const textSx = useMemo(
+        () =>
+            props.width
+                ? {
+                      ...numberSx,
+                      maxWidth: getCssSize(props.width),
+                  }
+                : numberSx,
+        [props.width]
+    );
 
-    const handleInput = useCallback(
-        (e: React.ChangeEvent<HTMLInputElement>) => {
-            const val = e.target.value;
-            setValue(val);
+    const updateValueWithDelay = useCallback(
+        (value: number | string) => {
+            if (changeDelay === -1) {
+                return;
+            }
             if (changeDelay === 0) {
-                dispatch(createSendUpdateAction(updateVarName, val, module, onChange, propagate));
+                // Workaround using microtask to ensure the value is updated before the next action to avoid the bad setState behavior
+                Promise.resolve().then(() => {
+                    dispatch(createSendUpdateAction(updateVarName, value, module, onChange, propagate));
+                });
                 return;
             }
-            if (changeDelay > 0) {
-                if (delayCall.current > 0) {
-                    clearTimeout(delayCall.current);
-                }
-                delayCall.current = window.setTimeout(() => {
-                    delayCall.current = -1;
-                    dispatch(createSendUpdateAction(updateVarName, val, module, onChange, propagate));
-                }, changeDelay);
+            if (delayCall.current > 0) {
+                clearTimeout(delayCall.current);
             }
+            delayCall.current = window.setTimeout(() => {
+                delayCall.current = -1;
+                dispatch(createSendUpdateAction(updateVarName, value, module, onChange, propagate));
+            }, changeDelay);
         },
-        [updateVarName, dispatch, propagate, onChange, changeDelay, module]
+        [changeDelay, dispatch, updateVarName, module, onChange, propagate]
+    );
+
+    const handleInput = useCallback(
+        (e: React.ChangeEvent<HTMLInputElement>) => {
+            const val = e.target.value;
+            setValue(val);
+            dispatch(createSendUpdateAction(updateVarName, val, module, onChange, propagate));
+        },
+        [updateVarName, dispatch, propagate, onChange, module]
     );
 
     const handleAction = useCallback(
@@ -114,6 +134,7 @@ const Input = (props: TaipyInputProps) => {
                         val = max;
                     }
                     setValue(val.toString());
+                    updateValueWithDelay(val);
                     evt.preventDefault();
                 } else if (evt.key === "ArrowDown") {
                     let val =
@@ -123,6 +144,7 @@ const Input = (props: TaipyInputProps) => {
                         val = min;
                     }
                     setValue(val.toString());
+                    updateValueWithDelay(val);
                     evt.preventDefault();
                 }
             } else if (!evt.shiftKey && !evt.ctrlKey && !evt.altKey && actionKeys.includes(evt.key)) {
@@ -144,13 +166,14 @@ const Input = (props: TaipyInputProps) => {
             step,
             stepMultiplier,
             max,
-            min,
-            changeDelay,
+            updateValueWithDelay,
             onAction,
             dispatch,
             id,
             module,
             updateVarName,
+            min,
+            changeDelay,
             onChange,
             propagate,
         ]
@@ -181,16 +204,22 @@ const Input = (props: TaipyInputProps) => {
                     event.shiftKey,
                     increment
                 );
+
                 if (min !== undefined && Number(newValue) < min) {
+                    updateValueWithDelay(min);
                     return min.toString();
                 }
+
                 if (max !== undefined && Number(newValue) > max) {
+                    updateValueWithDelay(max);
                     return max.toString();
                 }
+
+                updateValueWithDelay(newValue);
                 return newValue;
             });
         },
-        [min, max, step, stepMultiplier, calculateNewValue]
+        [calculateNewValue, step, stepMultiplier, min, max, updateValueWithDelay]
     );
 
     const handleUpStepperMouseDown = useCallback(
@@ -230,27 +259,27 @@ const Input = (props: TaipyInputProps) => {
                       ),
                   }
                 : type == "number"
-                ? {
-                      endAdornment: (
-                          <div style={verticalDivStyle}>
-                              <IconButton
-                                  aria-label="Increment value"
-                                  size="small"
-                                  onMouseDown={handleUpStepperMouseDown}
-                              >
-                                  <ArrowDropUpIcon fontSize="inherit" />
-                              </IconButton>
-                              <IconButton
-                                  aria-label="Decrement value"
-                                  size="small"
-                                  onMouseDown={handleDownStepperMouseDown}
-                              >
-                                  <ArrowDropDownIcon fontSize="inherit" />
-                              </IconButton>
-                          </div>
-                      ),
-                  }
-                : undefined,
+                  ? {
+                        endAdornment: (
+                            <div style={verticalDivStyle}>
+                                <IconButton
+                                    aria-label="Increment value"
+                                    size="small"
+                                    onMouseDown={handleUpStepperMouseDown}
+                                >
+                                    <ArrowDropUpIcon fontSize="inherit" />
+                                </IconButton>
+                                <IconButton
+                                    aria-label="Decrement value"
+                                    size="small"
+                                    onMouseDown={handleDownStepperMouseDown}
+                                >
+                                    <ArrowDropDownIcon fontSize="inherit" />
+                                </IconButton>
+                            </div>
+                        ),
+                    }
+                  : undefined,
         [
             type,
             showPassword,
@@ -270,8 +299,8 @@ const Input = (props: TaipyInputProps) => {
                       max: max,
                   }
                 : type == "password"
-                ? { autoComplete: "current-password" }
-                : undefined,
+                  ? { autoComplete: "current-password" }
+                  : undefined,
         [type, step, min, max]
     );