From 300b4ac0ae169d4286a67ea48d8a8cd39543c5e5 Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 8 Oct 2023 03:45:08 +0800 Subject: [PATCH 1/2] feat: NumberInput supports long press --- .../NumberInput/NumberInput.tsx | 52 +++++++++++++++---- src/tests/input-elements/NumberInput.test.tsx | 10 ++-- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/components/input-elements/NumberInput/NumberInput.tsx b/src/components/input-elements/NumberInput/NumberInput.tsx index e5cf34bd5..348dccf2a 100644 --- a/src/components/input-elements/NumberInput/NumberInput.tsx +++ b/src/components/input-elements/NumberInput/NumberInput.tsx @@ -17,6 +17,9 @@ const baseArrowClasses = const enabledArrowClasses = "cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content"; +const stepDelay = 600; +const stepInterval = 100; + const NumberInput = React.forwardRef((props, ref) => { const { onSubmit, enableStepper = true, disabled, onValueChange, onChange, ...other } = props; @@ -38,6 +41,32 @@ const NumberInput = React.forwardRef((props, setIsArrowUpPressed(false); }, []); + // for long press to step up/down + const stepTimeoutRef = useRef(null); + const onStopStepping = () => { + stepTimeoutRef && clearTimeout(stepTimeoutRef.current!); + }; + const onStepMouseDown = React.useCallback((e: React.MouseEvent, up: boolean) => { + e.preventDefault(); + onStopStepping(); + + up ? inputRef.current?.stepUp() : inputRef.current?.stepDown(); + inputRef.current?.dispatchEvent(new Event("input", { bubbles: true })); + + function step() { + up ? inputRef.current?.stepUp() : inputRef.current?.stepDown(); + inputRef.current?.dispatchEvent(new Event("input", { bubbles: true })); + stepTimeoutRef.current = setTimeout(step, stepInterval) as unknown as number; + } + stepTimeoutRef.current = setTimeout(step, stepDelay) as unknown as number; + }, []); + + React.useEffect(() => { + return () => { + onStopStepping(); + }; + }, []); + return ( ((props, makeInputClassName={makeClassName("NumberInput")} onKeyDown={(e) => { if (e.key === "Enter" && !e.ctrlKey && !e.altKey && !e.shiftKey) { + onStopStepping(); const value = inputRef.current?.value; onSubmit?.(parseFloat(value ?? "")); } @@ -76,13 +106,14 @@ const NumberInput = React.forwardRef((props,
e.preventDefault()} - onMouseDown={(e) => e.preventDefault()} - onTouchStart={(e) => e.preventDefault()} - onMouseUp={() => { + onMouseDown={(e) => { + e.preventDefault(); if (disabled) return; - inputRef.current?.stepDown(); - inputRef.current?.dispatchEvent(new Event("input", { bubbles: true })); + onStepMouseDown(e, false); }} + onTouchStart={(e) => e.preventDefault()} + onMouseUp={() => onStopStepping()} + onMouseLeave={() => onStopStepping()} className={tremorTwMerge( !disabled && enabledArrowClasses, baseArrowClasses, @@ -99,13 +130,14 @@ const NumberInput = React.forwardRef((props,
e.preventDefault()} - onMouseDown={(e) => e.preventDefault()} - onTouchStart={(e) => e.preventDefault()} - onMouseUp={() => { + onMouseDown={(e) => { + e.preventDefault(); if (disabled) return; - inputRef.current?.stepUp(); - inputRef.current?.dispatchEvent(new Event("input", { bubbles: true })); + onStepMouseDown(e, true); }} + onTouchStart={(e) => e.preventDefault()} + onMouseUp={() => onStopStepping()} + onMouseLeave={() => onStopStepping()} className={tremorTwMerge( !disabled && enabledArrowClasses, baseArrowClasses, diff --git a/src/tests/input-elements/NumberInput.test.tsx b/src/tests/input-elements/NumberInput.test.tsx index d4546fe8c..3a85121df 100644 --- a/src/tests/input-elements/NumberInput.test.tsx +++ b/src/tests/input-elements/NumberInput.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render, screen, fireEvent } from "@testing-library/react"; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import { NumberInput } from "components"; describe("NumberInput", () => { @@ -21,7 +21,7 @@ describe("NumberInput", () => { const inputEl: HTMLInputElement = screen.getByTestId("base-input"); expect(inputEl.value).toBe("2"); const stepUp = screen.getByTestId("step-up"); - fireEvent.mouseUp(stepUp); + fireEvent.mouseDown(stepUp); expect(inputEl.value).toBe("2.1"); }); @@ -30,10 +30,10 @@ describe("NumberInput", () => { const inputEl: HTMLInputElement = screen.getByTestId("base-input"); const stepUp = screen.getByTestId("step-up"); const stepDown = screen.getByTestId("step-down"); - fireEvent.mouseUp(stepDown); + fireEvent.mouseDown(stepDown); expect(inputEl.value).toBe("1"); - fireEvent.mouseUp(stepUp); - fireEvent.mouseUp(stepUp); + fireEvent.mouseDown(stepUp); + fireEvent.mouseDown(stepUp); expect(inputEl.value).toBe("2"); }); }); From 6d7e83c45c7b47a673f0fa3b24a08b0bc6253727 Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 8 Oct 2023 11:03:24 +0800 Subject: [PATCH 2/2] feat: NumberInput supports long press --- src/tests/input-elements/NumberInput.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/input-elements/NumberInput.test.tsx b/src/tests/input-elements/NumberInput.test.tsx index 3a85121df..c432ad4c8 100644 --- a/src/tests/input-elements/NumberInput.test.tsx +++ b/src/tests/input-elements/NumberInput.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import { render, screen, fireEvent } from "@testing-library/react"; import { NumberInput } from "components"; describe("NumberInput", () => {