new-personal-site/components/lib/form/Input/NumberInputButtons.tsx
2026-03-14 07:15:43 +01:00

166 lines
4.8 KiB
TypeScript

import React, { useEffect, useRef, useState } from "react";
import Row from "../../layout/Row";
import { Info, Minus, Plus } from "lucide-react";
import twuiNumberfy from "../../utils/numberfy";
import { InputProps } from ".";
let pressInterval: any;
let pressTimeout: any;
type Props = Pick<InputProps<any>, "min" | "max" | "step" | "decimal"> & {
value: string;
setValue: React.Dispatch<React.SetStateAction<string>>;
buttonDownRef: React.RefObject<boolean>;
inputRef: React.RefObject<HTMLInputElement | null>;
};
/**
* # Input Number Text Buttons
*/
export default function NumberInputButtons({
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(() => {
pressInterval = setInterval(() => {
increment();
}, 50);
}, PRESS_TRIGGER_TIMEOUT);
}
function incrementDownCancel() {
setButtonDown(false);
window.clearTimeout(pressTimeout);
window.clearInterval(pressInterval);
}
function decrementDownPress() {
setButtonDown(true);
pressTimeout = setTimeout(() => {
pressInterval = setInterval(() => {
decrement();
}, 50);
}, PRESS_TRIGGER_TIMEOUT);
}
function decrementDownCancel() {
setButtonDown(false);
window.clearTimeout(pressTimeout);
window.clearInterval(pressInterval);
}
function increment() {
if (!inputRef.current) return;
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 {
new_value = (
existingNumberValue +
twuiNumberfy(step || DEFAULT_STEP, decimal)
).toLocaleString();
}
inputRef.current.value = new_value;
}
function decrement() {
if (!inputRef.current) return;
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 {
new_value = (
existingNumberValue -
twuiNumberfy(step || DEFAULT_STEP, decimal)
).toLocaleString();
}
inputRef.current.value = new_value;
}
return (
<Row className="flex-nowrap gap-1 -my-2 ml-auto -mr-2">
<Row
className="rounded-full w-8 h-8 cursor-pointer touch-none select-none justify-center"
onClick={(e) => {
e.preventDefault();
decrement();
}}
onMouseDown={decrementDownPress}
onTouchStart={decrementDownPress}
onMouseUp={decrementDownCancel}
onTouchEnd={decrementDownCancel}
>
<Minus size={20} />
</Row>
<Row
className="rounded-full w-8 h-8 cursor-pointer touch-none select-none justify-center"
onClick={(e) => {
e.preventDefault();
increment();
}}
onMouseDown={incrementDownPress}
onTouchStart={incrementDownPress}
onMouseUp={incrementDownCancel}
onTouchEnd={incrementDownCancel}
>
<Plus size={20} />
</Row>
</Row>
);
}