From f51001894ca2be0215d6c11524dbfc02efe310bf Mon Sep 17 00:00:00 2001 From: Alexis Falaise Date: Mon, 17 Mar 2025 12:44:44 +0100 Subject: [PATCH] chore(blocks, number): migrate NumberInput to ArkUI --- .../src/components/inputs/NumberInput.tsx | 122 ++++++++++++++---- 1 file changed, 95 insertions(+), 27 deletions(-) diff --git a/apps/builder/src/components/inputs/NumberInput.tsx b/apps/builder/src/components/inputs/NumberInput.tsx index f3706f0c5..d770da809 100644 --- a/apps/builder/src/components/inputs/NumberInput.tsx +++ b/apps/builder/src/components/inputs/NumberInput.tsx @@ -1,22 +1,22 @@ import { VariablesButton } from "@/features/variables/components/VariablesButton"; import { - NumberInput as ChakraNumberInput, + NumberInput as ArkNumberInput, + type NumberInputRootProps, +} from "@ark-ui/react/number-input"; +import { FormControl, FormHelperText, FormLabel, HStack, - NumberDecrementStepper, - NumberIncrementStepper, - NumberInputField, - type NumberInputProps, - NumberInputStepper, Stack, Text, } from "@chakra-ui/react"; import { env } from "@typebot.io/env"; +import { ChevronDownIcon } from "@typebot.io/ui/icons/ChevronDownIcon"; +import { ChevronUpIcon } from "@typebot.io/ui/icons/ChevronUpIcon"; import type { Variable, VariableString } from "@typebot.io/variables/schemas"; import type { ReactNode } from "react"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useDebouncedCallback } from "use-debounce"; import { MoreInfoTooltip } from "../MoreInfoTooltip"; @@ -36,9 +36,15 @@ type Props = { helperText?: ReactNode; placeholder?: string; onValueChange: (value?: Value) => void; + width?: string | number; } & Omit< - NumberInputProps, - "defaultValue" | "value" | "onChange" | "isRequired" + NumberInputRootProps, + | "defaultValue" + | "value" + | "onChange" + | "onValueChange" + | "isRequired" + | "width" >; export const NumberInput = ({ @@ -53,10 +59,19 @@ export const NumberInput = ({ suffix, helperText, placeholder, + width, ...props }: Props) => { + const defaultValueStr = defaultValue?.toString() ?? ""; + const isDefaultValueVariable = + typeof defaultValue === "string" && + defaultValue.startsWith("{{") && + defaultValue.endsWith("}}"); + const [isTouched, setIsTouched] = useState(false); - const [value, setValue] = useState(defaultValue?.toString() ?? ""); + const [value, setValue] = useState(defaultValueStr); + const inputRef = useRef(null); + const isVariableRef = useRef(isDefaultValueVariable); const onValueChangeDebounced = useDebouncedCallback( onValueChange, @@ -65,7 +80,16 @@ export const NumberInput = ({ useEffect(() => { if (isTouched || value !== "" || !defaultValue) return; - setValue(defaultValue?.toString() ?? ""); + + const newDefaultValueStr = defaultValue.toString(); + setValue(newDefaultValueStr); + + const isVariable = + typeof defaultValue === "string" && + defaultValue.startsWith("{{") && + defaultValue.endsWith("}}"); + + isVariableRef.current = isVariable; }, [defaultValue, isTouched, value]); useEffect( @@ -77,44 +101,88 @@ export const NumberInput = ({ const handleValueChange = (newValue: string) => { if (!isTouched) setIsTouched(true); - if (value.startsWith("{{") && value.endsWith("}}") && newValue !== "") - return; + + if (isVariableRef.current && newValue === "") return; + setValue(newValue); + if (newValue.endsWith(".") || newValue.endsWith(",")) return; - if (newValue === "") return onValueChangeDebounced(undefined); + if (newValue === "") { + onValueChangeDebounced(undefined); + return; + } + if ( newValue.startsWith("{{") && newValue.endsWith("}}") && newValue.length > 4 && (withVariableButton ?? true) ) { + isVariableRef.current = true; onValueChangeDebounced(newValue as Value); return; } + + isVariableRef.current = false; const numberedValue = Number.parseFloat(newValue); if (isNaN(numberedValue)) return; - onValueChangeDebounced(numberedValue); + onValueChangeDebounced(numberedValue as Value); }; const handleVariableSelected = (variable?: Variable) => { if (!variable) return; const newValue = `{{${variable.name}}}`; - handleValueChange(newValue); + setValue(newValue); + isVariableRef.current = true; + onValueChange(newValue as Value); + }; + + const isValueVariable = + value.startsWith("{{") && value.endsWith("}}") && value.length > 4; + + const CustomInput = () => { + return ( + handleValueChange(e.target.value)} + /> + ); }; const Input = ( - { + if (!isValueVariable) { + handleValueChange(details.value.toString()); + } + }} + className="w-full" {...props} > - - - - - - + + {isValueVariable ? ( + + ) : ( + + )} + + + + + + + + ); return ( @@ -122,7 +190,7 @@ export const NumberInput = ({ as={direction === "column" ? Stack : HStack} isRequired={isRequired} justifyContent="space-between" - width={label || props.width === "full" ? "full" : "auto"} + width={label || width === "full" ? "full" : "auto"} spacing={direction === "column" ? 2 : 3} > {label && (