From 9f8527fc4d851c1fecd6600bd60c490de998676f Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Sun, 8 Mar 2026 17:03:10 +0100 Subject: [PATCH] Updates --- components/form/Input/NumberInputButtons.tsx | 98 ++++++++++++++------ components/form/Input/index.tsx | 57 ++++++------ 2 files changed, 98 insertions(+), 57 deletions(-) diff --git a/components/form/Input/NumberInputButtons.tsx b/components/form/Input/NumberInputButtons.tsx index 3212408..e9bdf03 100644 --- a/components/form/Input/NumberInputButtons.tsx +++ b/components/form/Input/NumberInputButtons.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useRef, useState } from "react"; import Row from "../../layout/Row"; import { Info, Minus, Plus } from "lucide-react"; import twuiNumberfy from "../../utils/numberfy"; @@ -7,10 +7,10 @@ import { InputProps } from "."; let pressInterval: any; let pressTimeout: any; -type Props = Pick, "min" | "max" | "step"> & { +type Props = Pick, "min" | "max" | "step" | "decimal"> & { + value: string; setValue: React.Dispatch>; - getNormalizedValue: (v: string) => void; - buttonDownRef: React.MutableRefObject; + buttonDownRef: React.RefObject; inputRef: React.RefObject; }; @@ -18,21 +18,50 @@ type Props = Pick, "min" | "max" | "step"> & { * # Input Number Text Buttons */ export default function NumberInputButtons({ - getNormalizedValue, + value, setValue, min, max, step, buttonDownRef, inputRef, + decimal, }: Props) { const PRESS_TRIGGER_TIMEOUT = 200; const DEFAULT_STEP = 1; + const [buttonDown, setButtonDown] = useState(false); + + // function getNormalizedValue(value: string) { + // if (numberText) { + // if (props.max && twuiNumberfy(value) > twuiNumberfy(props.max)) + // return getFinalValue(props.max); + + // if (props.min && twuiNumberfy(value) < twuiNumberfy(props.min)) + // return getFinalValue(props.min); + + // return getFinalValue(value); + // } else { + // return value; + // } + // } + + useEffect(() => { + buttonDownRef.current = buttonDown; + if (buttonDown) { + setValue(inputRef.current?.value || ""); + } else { + setTimeout(() => { + setValue(inputRef.current?.value || ""); + }, 50); + } + }, [buttonDown]); + function incrementDownPress() { window.clearTimeout(pressTimeout); + setButtonDown(true); + pressTimeout = setTimeout(() => { - buttonDownRef.current = true; pressInterval = setInterval(() => { increment(); }, 50); @@ -40,14 +69,15 @@ export default function NumberInputButtons({ } function incrementDownCancel() { - buttonDownRef.current = false; + setButtonDown(false); window.clearTimeout(pressTimeout); window.clearInterval(pressInterval); } function decrementDownPress() { + setButtonDown(true); + pressTimeout = setTimeout(() => { - buttonDownRef.current = true; pressInterval = setInterval(() => { decrement(); }, 50); @@ -55,41 +85,51 @@ export default function NumberInputButtons({ } function decrementDownCancel() { - buttonDownRef.current = false; + setButtonDown(false); window.clearTimeout(pressTimeout); window.clearInterval(pressInterval); } function increment() { - const existingValue = inputRef.current?.value; - const existingNumberValue = twuiNumberfy(existingValue); + if (!inputRef.current) return; - if (max && existingNumberValue >= twuiNumberfy(max)) { - return setValue(String(max)); - } else if (min && existingNumberValue < twuiNumberfy(min)) { - return setValue(String(min)); + const existingValue = inputRef.current.value; + const existingNumberValue = twuiNumberfy(existingValue, decimal); + + let new_value = ""; + + if (max && existingNumberValue >= twuiNumberfy(max, decimal)) { + new_value = twuiNumberfy(max, decimal).toLocaleString(); + } else if (min && existingNumberValue < twuiNumberfy(min, decimal)) { + new_value = twuiNumberfy(min, decimal).toLocaleString(); } else { - setValue( - String( - existingNumberValue + twuiNumberfy(step || DEFAULT_STEP), - ), - ); + new_value = ( + existingNumberValue + + twuiNumberfy(step || DEFAULT_STEP, decimal) + ).toLocaleString(); } + + inputRef.current.value = new_value; } function decrement() { - const existingValue = inputRef.current?.value; - const existingNumberValue = twuiNumberfy(existingValue); + if (!inputRef.current) return; - if (min && existingNumberValue <= twuiNumberfy(min)) { - setValue(String(min)); + const existingValue = inputRef.current?.value; + const existingNumberValue = twuiNumberfy(existingValue, decimal); + + let new_value = ""; + + if (min && existingNumberValue <= twuiNumberfy(min, decimal)) { + new_value = twuiNumberfy(min, decimal).toLocaleString(); } else { - setValue( - String( - existingNumberValue - twuiNumberfy(step || DEFAULT_STEP), - ), - ); + new_value = ( + existingNumberValue - + twuiNumberfy(step || DEFAULT_STEP, decimal) + ).toLocaleString(); } + + inputRef.current.value = new_value; } return ( diff --git a/components/form/Input/index.tsx b/components/form/Input/index.tsx index da20b05..8afb635 100644 --- a/components/form/Input/index.tsx +++ b/components/form/Input/index.tsx @@ -6,27 +6,21 @@ import React, { ReactNode, RefObject, TextareaHTMLAttributes, + useRef, } from "react"; import { twMerge } from "tailwind-merge"; import Span from "../../layout/Span"; -import Button from "../../layout/Button"; import { Eye, EyeOff, Info, InfoIcon, X } from "lucide-react"; import { AutocompleteOptions } from "../../types"; import twuiNumberfy from "../../utils/numberfy"; import Dropdown from "../../elements/Dropdown"; -import Card from "../../elements/Card"; import Stack from "../../layout/Stack"; import NumberInputButtons from "./NumberInputButtons"; import twuiSlugToNormalText from "../../utils/slug-to-normal-text"; -import twuiUseReady from "../../hooks/useReady"; import Row from "../../layout/Row"; import Paper from "../../elements/Paper"; import { TWUISelectValidityObject } from "../Select"; -let timeout: any; -let validationFnTimeout: any; -let externalValueChangeTimeout: any; - export type InputProps = Omit< DetailedHTMLProps, HTMLInputElement>, "prefix" | "suffix" @@ -80,6 +74,7 @@ export type InputProps = Omit< React.HTMLAttributes, HTMLDivElement >; + // refreshDefaultValue?: number; }; let refreshes = 0; @@ -121,9 +116,18 @@ export default function Input( validity: existingValidity, clearInputProps, rawNumber, + // refreshDefaultValue, ...props } = inputProps; + const componentRefreshesRef = useRef(0); + let timeoutRef = useRef(null); + let validationFnTimeoutRef = useRef(null); + let externalValueChangeTimeoutRef = useRef(null); + + refreshes++; + componentRefreshesRef.current++; + function getFinalValue(v: any) { if (rawNumber) return twuiNumberfy(v); if (numberText) { @@ -167,21 +171,8 @@ export default function Input( props.placeholder || (props.name ? twuiSlugToNormalText(props.name) : undefined); - function getNormalizedValue(value: string) { - if (numberText) { - if (props.max && twuiNumberfy(value) > twuiNumberfy(props.max)) - return getFinalValue(props.max); - - if (props.min && twuiNumberfy(value) < twuiNumberfy(props.min)) - return getFinalValue(props.min); - - return getFinalValue(value); - } else { - return value; - } - } - React.useEffect(() => { + // if (!existingReady) return; if (!existingValidity) return; setValidity(existingValidity); }, [existingValidity]); @@ -190,8 +181,8 @@ export default function Input( if (buttonDownRef.current) return; if (changeHandler) { - window.clearTimeout(externalValueChangeTimeout); - externalValueChangeTimeout = setTimeout(() => { + window.clearTimeout(externalValueChangeTimeoutRef.current); + externalValueChangeTimeoutRef.current = setTimeout(() => { changeHandler(val); }, finalDebounce); } @@ -208,10 +199,10 @@ export default function Input( return; } - window.clearTimeout(timeout); + window.clearTimeout(timeoutRef.current); if (validationRegex) { - timeout = setTimeout(() => { + timeoutRef.current = setTimeout(() => { setValidity({ isValid: validationRegex.test(val), msg: "Value mismatch", @@ -220,9 +211,9 @@ export default function Input( } if (validationFunction) { - window.clearTimeout(validationFnTimeout); + window.clearTimeout(validationFnTimeoutRef.current); - validationFnTimeout = setTimeout(() => { + validationFnTimeoutRef.current = setTimeout(() => { if (validationRegex && !validationRegex.test(val)) { return; } @@ -235,11 +226,20 @@ export default function Input( }; React.useEffect(() => { + // if (!existingReady) return; if (typeof props.value !== "string" || !props.value.match(/./)) return; setValue(String(props.value)); }, [props.value]); + // React.useEffect(() => { + // if (!refreshDefaultValue) return; + // console.log("Name:", props.title || props.name); + // console.log("props.defaultValue", props.defaultValue); + // // setValue(String(props.defaultValue || "")); + // }, [refreshDefaultValue]); + React.useEffect(() => { + // if (!existingReady) return; if (istextarea && textAreaRef.current) { } else if (inputRef?.current) { inputRef.current.value = getFinalValue(value); @@ -452,11 +452,12 @@ export default function Input( ) : null}