项目文件夹

文件
2026-07-13 13:08:41 +08:00

99 行
2.5 KiB
TypeScript

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
import type { KeyboardEvent } from "react";
type NumericInputOptions = {
allowDecimal?: boolean;
allowNegative?: boolean;
min?: number;
};
const NAVIGATION_KEYS = new Set([
"Backspace",
"Delete",
"End",
"Enter",
"Escape",
"Home",
"Tab",
"ArrowDown",
"ArrowLeft",
"ArrowRight",
"ArrowUp",
]);
function canUseNegative(options: NumericInputOptions) {
if (options.allowNegative != null) return options.allowNegative;
return typeof options.min === "number" ? options.min < 0 : true;
}
export function sanitizeNumericInput(
value: string,
options: NumericInputOptions = {},
) {
const allowDecimal = options.allowDecimal ?? true;
const allowNegative = canUseNegative(options);
let nextValue = "";
let hasDecimal = false;
for (const character of value.trim()) {
if (character >= "0" && character <= "9") {
nextValue += character;
continue;
}
if (allowDecimal && character === "." && !hasDecimal) {
nextValue += character;
hasDecimal = true;
continue;
}
if (
allowNegative &&
(character === "-" || character === "") &&
nextValue.length === 0
) {
nextValue = "-";
}
}
return nextValue;
}
export function numericInputMode(options: NumericInputOptions = {}) {
return options.allowDecimal ?? true ? "decimal" : "numeric";
}
export function preventInvalidNumberInput(
event: KeyboardEvent<HTMLInputElement>,
options: NumericInputOptions = {},
) {
if (event.defaultPrevented || event.metaKey || event.ctrlKey) return false;
if (NAVIGATION_KEYS.has(event.key)) return false;
if (/^\d$/.test(event.key)) return false;
const allowDecimal = options.allowDecimal ?? true;
const allowNegative = canUseNegative(options);
const input = event.currentTarget;
const value = input.value;
const selectionStart = input.selectionStart ?? value.length;
const selectionEnd = input.selectionEnd ?? selectionStart;
const selectedText = value.slice(selectionStart, selectionEnd);
if (allowDecimal && event.key === ".") {
const replacesExistingDecimal = selectedText.includes(".");
if (!value.includes(".") || replacesExistingDecimal) return false;
}
if (allowNegative && (event.key === "-" || event.key === "")) {
const replacesExistingNegative = selectedText.includes("-");
if (
selectionStart === 0 &&
(!value.includes("-") || replacesExistingNegative)
) {
return false;
}
}
event.preventDefault();
return true;
}