Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | 20x 20x 20x 20x 20x 20x 20x 2x 20x 2x 20x | import { Box } from "@mui/material";
import React, { useEffect, useState, useRef } from "react";
import { GiveInput } from "./GiveInput";
import { debounce } from "lodash";
function GiveRangeInputs({
value: defaultValue,
onChange,
validate = true,
isError: isInputError = false,
currency = "USD",
maxDigitsPerSide = 14,
}: {
value: string;
onChange: (val: string) => void;
validate?: boolean;
isError?: boolean;
currency?: string;
maxDigitsPerSide?: number;
}) {
const [value, setValue] = useState(defaultValue);
const [isError, setIsError] = useState(isInputError);
const inputRef = useRef<HTMLInputElement | null>(null);
const validateInput = (newValue: string) => {
if (!newValue) {
setIsError(true);
return;
}
const parts = newValue.split("-");
if (parts.length === 1) {
setIsError(true);
return;
}
const [minStr, maxStr] = parts;
const min = minStr ? Number(minStr) : NaN;
const max = maxStr ? Number(maxStr) : NaN;
if (minStr === "" || maxStr === "") {
setIsError(true);
return;
}
setIsError(!isNaN(min) && !isNaN(max) && max < min);
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
let newValue = event.target.value;
// Allow digits and optional decimals, with optional range (e.g. "10", "10.5", "10-20.5")
if (/^\d*\.?\d*(-\d*\.?\d*)?$/.test(newValue) || newValue === "-") {
const parts = newValue.split("-");
// Enforce max digits on each side (excluding dot) and limit to 2 decimal places
const sanitizePart = (part: string) => {
// Split into integer and decimal parts
const [integerPart, decimalPart] = part.split(".");
// Limit integer part digits
const digitsOnly = integerPart.replace(/\D/g, "");
const limitedInteger =
digitsOnly.length > maxDigitsPerSide
? digitsOnly.slice(0, maxDigitsPerSide)
: digitsOnly;
// If there's a decimal part, limit to 2 digits
if (decimalPart !== undefined) {
const limitedDecimal = decimalPart.slice(0, 2);
return `${limitedInteger}.${limitedDecimal}`;
}
return limitedInteger;
};
if (parts.length === 1) {
newValue = sanitizePart(parts[0]);
} else if (parts.length === 2) {
newValue = `${sanitizePart(parts[0])}-${sanitizePart(parts[1])}`;
}
setValue(newValue);
validate && validateInput(newValue);
}
};
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (
(event.key === "Enter" || event.key === " " || event.key === "-") &&
value &&
!value.includes("-")
) {
event.preventDefault();
setValue(value + "-");
}
};
const handleUpdateOnChange = debounce(() => {
onChange?.(value);
}, 300);
useEffect(() => {
handleUpdateOnChange();
}, [isError, value]);
return (
<Box sx={{ width: "100%" }}>
<GiveInput
inputMode="numeric"
fullWidth
currency={currency}
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
error={isError || isInputError}
inputRef={inputRef}
data-testid="range-input-id"
/>
</Box>
);
}
export default GiveRangeInputs;
|