"use client";
import { noop } from "../../core/utils/noop/noop.mjs";
import { getSize } from "../../core/utils/get-size/get-size.mjs";
import { createVarsResolver } from "../../core/styles-api/create-vars-resolver/create-vars-resolver.mjs";
import { useProps } from "../../core/MantineProvider/use-props/use-props.mjs";
import { useResolvedStylesApi } from "../../core/styles-api/use-resolved-styles-api/use-resolved-styles-api.mjs";
import { useStyles } from "../../core/styles-api/use-styles/use-styles.mjs";
import { genericFactory } from "../../core/factory/factory.mjs";
import { UnstyledButton } from "../UnstyledButton/UnstyledButton.mjs";
import { InputBase } from "../InputBase/InputBase.mjs";
import { NumberInputChevron } from "./NumberInputChevron.mjs";
import NumberInput_module_default from "./NumberInput.module.mjs";
import { useRef } from "react";
import { assignRef, clamp, useMergedRef, useUncontrolled } from "@mantine/hooks";
import cx from "clsx";
import { jsx, jsxs } from "react/jsx-runtime";
import { NumericFormat } from "react-number-format";
//#region packages/@mantine/core/src/components/NumberInput/NumberInput.tsx
const leadingDecimalZeroPattern = /^(0\.0*|-0(\.0*)?)$/;
const leadingZerosPattern = /^-?0\d+(\.\d+)?\.?$/;
const trailingZerosPattern = /\.\d*0$/;
const trailingDecimalSeparatorPattern = /^-?\d+\.$/;
function isNumberString(value) {
	return typeof value === "string" && value !== "" && !Number.isNaN(Number(value));
}
function isBigIntValue(value) {
	return typeof value === "bigint";
}
function canStep(value) {
	if (typeof value === "number") return value < Number.MAX_SAFE_INTEGER;
	return value === "" || isNumberString(value) && Number(value) < Number.MAX_SAFE_INTEGER;
}
function isValidBigIntString(value, allowNegative) {
	if (value === "") return false;
	if (value === "-") return false;
	if (!allowNegative && value.startsWith("-")) return false;
	return /^-?\d+$/.test(value);
}
function canStepBigInt(value, allowNegative) {
	if (typeof value === "bigint") return true;
	return value === "" || isValidBigIntString(value, allowNegative);
}
function parseBigIntFromString(value) {
	if (!/^-?\d+$/.test(value)) return null;
	try {
		return BigInt(value);
	} catch {
		return null;
	}
}
function toBigIntOrUndefined(value) {
	if (typeof value === "bigint") return value;
	if (typeof value === "number" && Number.isFinite(value) && Number.isInteger(value)) return BigInt(value);
}
function clampBigInt(value, min, max) {
	if (min !== void 0 && value < min) return min;
	if (max !== void 0 && value > max) return max;
	return value;
}
function getTotalDigits(inputValue) {
	return inputValue.toString().replace(".", "").length;
}
function isValidNumber(floatValue, value) {
	return (typeof floatValue === "number" ? floatValue < Number.MAX_SAFE_INTEGER : !Number.isNaN(Number(floatValue))) && !Number.isNaN(floatValue) && getTotalDigits(value) < 14 && value !== "";
}
function isInRange(value, min, max) {
	if (value === void 0) return true;
	return (min === void 0 || value >= min) && (max === void 0 || value <= max);
}
const defaultProps = {
	size: "sm",
	step: 1,
	clampBehavior: "blur",
	allowDecimal: true,
	allowNegative: true,
	withKeyboardEvents: true,
	allowLeadingZeros: true,
	trimLeadingZeroesOnBlur: true,
	startValue: 0,
	allowedDecimalSeparators: [".", ","]
};
const varsResolver = createVarsResolver((_, { size }) => ({ controls: { "--ni-chevron-size": getSize(size, "ni-chevron-size") } }));
function clampAndSanitizeInput(sanitizedValue, max, min) {
	const stringValue = sanitizedValue.toString();
	const hasTrailingDecimalSeparator = trailingDecimalSeparatorPattern.test(stringValue);
	const replaced = stringValue.replace(/^0+(?=\d)/, "");
	const parsedValue = parseFloat(replaced);
	if (Number.isNaN(parsedValue)) return replaced;
	if (parsedValue > Number.MAX_SAFE_INTEGER) return max !== void 0 ? max : replaced;
	const clamped = clamp(parsedValue, min, max);
	if (hasTrailingDecimalSeparator) return `${clamped.toString().replace(/^0+(?=\d)/, "")}.`;
	return clamped;
}
function clampAndSanitizeBigIntInput(sanitizedValue, options) {
	if (sanitizedValue === "" || sanitizedValue === "-") return sanitizedValue;
	const parsed = parseBigIntFromString(sanitizedValue);
	if (parsed === null) return sanitizedValue;
	return options.clampBehavior === "blur" ? clampBigInt(parsed, options.min, options.max) : parsed;
}
const NumberInput = genericFactory((_props) => {
	const props = useProps([
		"Input",
		"InputWrapper",
		"NumberInput"
	], defaultProps, _props);
	const { className, classNames, styles, unstyled, vars, onChange, onValueChange, value, defaultValue, max, min, step, hideControls, rightSection, isAllowed, clampBehavior, onBlur, allowDecimal, decimalScale, onKeyDown, onKeyDownCapture, handlersRef, startValue, disabled, rightSectionPointerEvents, allowNegative, readOnly, size, rightSectionWidth, stepHoldInterval, stepHoldDelay, allowLeadingZeros, withKeyboardEvents, trimLeadingZeroesOnBlur, allowedDecimalSeparators, selectAllOnFocus, onMinReached, onMaxReached, onFocus, attributes, ref, ...others } = props;
	const allowNegativeResolved = allowNegative ?? true;
	const allowLeadingZerosResolved = allowLeadingZeros ?? true;
	const getStyles = useStyles({
		name: "NumberInput",
		classes: NumberInput_module_default,
		props,
		classNames,
		styles,
		unstyled,
		attributes,
		vars,
		varsResolver
	});
	const { resolvedClassNames, resolvedStyles } = useResolvedStylesApi({
		classNames,
		styles,
		props
	});
	const valueModeRef = useRef(isBigIntValue(value) || isBigIntValue(defaultValue) ? "bigint" : "number");
	if (isBigIntValue(value)) valueModeRef.current = "bigint";
	else if (typeof value === "number") valueModeRef.current = "number";
	const isBigIntMode = valueModeRef.current === "bigint";
	const [_value, setValue] = useUncontrolled({
		value,
		defaultValue,
		finalValue: "",
		onChange
	});
	const shouldUseStepInterval = stepHoldDelay !== void 0 && stepHoldInterval !== void 0;
	const inputRef = useRef(null);
	const onStepTimeoutRef = useRef(null);
	const stepCountRef = useRef(0);
	const minNumber = typeof min === "number" ? min : void 0;
	const maxNumber = typeof max === "number" ? max : void 0;
	const stepNumber = typeof step === "number" ? step : defaultProps.step;
	const startValueNumber = typeof startValue === "number" ? startValue : defaultProps.startValue;
	const minBigInt = toBigIntOrUndefined(min);
	const maxBigInt = toBigIntOrUndefined(max);
	const stepBigInt = toBigIntOrUndefined(step) ?? BigInt(1);
	const startValueBigInt = toBigIntOrUndefined(startValue) ?? BigInt(0);
	const parseBigIntOrString = (inputValue) => {
		if (!isValidBigIntString(inputValue, allowNegativeResolved) || allowLeadingZerosResolved && leadingZerosPattern.test(inputValue)) return inputValue;
		return parseBigIntFromString(inputValue) ?? inputValue;
	};
	const getBigIntFloatValue = (inputValue) => {
		const numericValue = Number(inputValue);
		return Number.isSafeInteger(numericValue) ? numericValue : void 0;
	};
	const handleValueChange = (payload, event) => {
		if (event.source === "event") if (isBigIntMode) setValue(parseBigIntOrString(payload.value));
		else setValue(isValidNumber(payload.floatValue, payload.value) && !leadingDecimalZeroPattern.test(payload.value) && !(allowLeadingZerosResolved ? leadingZerosPattern.test(payload.value) : false) && !trailingZerosPattern.test(payload.value) && !trailingDecimalSeparatorPattern.test(payload.value) ? payload.floatValue : payload.value);
		onValueChange?.(payload, event);
	};
	const getDecimalPlaces = (inputValue) => {
		const match = String(inputValue).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
		if (!match) return 0;
		return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
	};
	const adjustCursor = (position) => {
		if (inputRef.current && typeof position !== "undefined") inputRef.current.setSelectionRange(position, position);
	};
	const incrementRef = useRef(noop);
	incrementRef.current = () => {
		if (isBigIntMode) {
			if (!canStepBigInt(_value, allowNegativeResolved)) return;
			let val;
			const currentValue = _value;
			if (typeof currentValue === "bigint") {
				const incrementedValue = currentValue + stepBigInt;
				if (maxBigInt !== void 0 && incrementedValue > maxBigInt) onMaxReached?.();
				val = maxBigInt !== void 0 && incrementedValue > maxBigInt ? maxBigInt : incrementedValue;
			} else if (typeof currentValue === "string" && currentValue !== "") {
				const parsed = parseBigIntFromString(currentValue);
				if (parsed === null) return;
				const incrementedValue = parsed + stepBigInt;
				if (maxBigInt !== void 0 && incrementedValue > maxBigInt) onMaxReached?.();
				val = maxBigInt !== void 0 && incrementedValue > maxBigInt ? maxBigInt : incrementedValue;
			} else val = clampBigInt(startValueBigInt, minBigInt, maxBigInt);
			const formattedValue = val.toString();
			setValue(val);
			onValueChange?.({
				floatValue: getBigIntFloatValue(val),
				formattedValue,
				value: formattedValue
			}, { source: "increment" });
			setTimeout(() => adjustCursor(inputRef.current?.value.length), 0);
			return;
		}
		if (!canStep(_value)) return;
		let val;
		const currentValuePrecision = getDecimalPlaces(_value);
		const stepPrecision = getDecimalPlaces(stepNumber);
		const maxPrecision = Math.max(currentValuePrecision, stepPrecision);
		const factor = 10 ** maxPrecision;
		if (!isNumberString(_value) && (typeof _value !== "number" || Number.isNaN(_value))) val = clamp(startValueNumber, minNumber, maxNumber);
		else if (maxNumber !== void 0) {
			const incrementedValue = (Math.round(Number(_value) * factor) + Math.round(stepNumber * factor)) / factor;
			if (incrementedValue > maxNumber) onMaxReached?.();
			val = incrementedValue <= maxNumber ? incrementedValue : maxNumber;
		} else val = (Math.round(Number(_value) * factor) + Math.round(stepNumber * factor)) / factor;
		const formattedValue = val.toFixed(maxPrecision);
		setValue(parseFloat(formattedValue));
		onValueChange?.({
			floatValue: parseFloat(formattedValue),
			formattedValue,
			value: formattedValue
		}, { source: "increment" });
		setTimeout(() => adjustCursor(inputRef.current?.value.length), 0);
	};
	const decrementRef = useRef(noop);
	decrementRef.current = () => {
		if (isBigIntMode) {
			if (!canStepBigInt(_value, allowNegativeResolved)) return;
			let val;
			const minValue = minBigInt !== void 0 ? minBigInt : !allowNegativeResolved ? BigInt(0) : void 0;
			const currentValue = _value;
			if (typeof currentValue === "bigint") {
				const decrementedValue = currentValue - stepBigInt;
				if (minValue !== void 0 && decrementedValue < minValue) onMinReached?.();
				val = minValue !== void 0 && decrementedValue < minValue ? minValue : decrementedValue;
			} else if (typeof currentValue === "string" && currentValue !== "") {
				const parsed = parseBigIntFromString(currentValue);
				if (parsed === null) return;
				const decrementedValue = parsed - stepBigInt;
				if (minValue !== void 0 && decrementedValue < minValue) onMinReached?.();
				val = minValue !== void 0 && decrementedValue < minValue ? minValue : decrementedValue;
			} else val = clampBigInt(startValueBigInt, minValue, maxBigInt);
			const formattedValue = val.toString();
			setValue(val);
			onValueChange?.({
				floatValue: getBigIntFloatValue(val),
				formattedValue,
				value: formattedValue
			}, { source: "decrement" });
			setTimeout(() => adjustCursor(inputRef.current?.value.length), 0);
			return;
		}
		if (!canStep(_value)) return;
		let val;
		const minValue = minNumber !== void 0 ? minNumber : !allowNegativeResolved ? 0 : Number.MIN_SAFE_INTEGER;
		const currentValuePrecision = getDecimalPlaces(_value);
		const stepPrecision = getDecimalPlaces(stepNumber);
		const maxPrecision = Math.max(currentValuePrecision, stepPrecision);
		const factor = 10 ** maxPrecision;
		if (!isNumberString(_value) && typeof _value !== "number" || Number.isNaN(_value)) val = clamp(startValueNumber, minValue, maxNumber);
		else {
			const decrementedValue = (Math.round(Number(_value) * factor) - Math.round(stepNumber * factor)) / factor;
			if (minValue !== void 0 && decrementedValue < minValue) onMinReached?.();
			val = minValue !== void 0 && decrementedValue < minValue ? minValue : decrementedValue;
		}
		const formattedValue = val.toFixed(maxPrecision);
		setValue(parseFloat(formattedValue));
		onValueChange?.({
			floatValue: parseFloat(formattedValue),
			formattedValue,
			value: formattedValue
		}, { source: "decrement" });
		setTimeout(() => adjustCursor(inputRef.current?.value.length), 0);
	};
	const handlePaste = (event) => {
		const pastedText = event.clipboardData.getData("text");
		const _decimalSeparator = others.decimalSeparator || ".";
		const separatorsToReplace = (allowedDecimalSeparators || [".", ","]).filter((s) => s !== _decimalSeparator);
		if (separatorsToReplace.some((s) => pastedText.includes(s))) {
			event.preventDefault();
			let modifiedText = pastedText;
			separatorsToReplace.forEach((s) => {
				modifiedText = modifiedText.split(s).join(_decimalSeparator);
			});
			const input = inputRef.current;
			if (input) {
				const start = input.selectionStart ?? 0;
				const end = input.selectionEnd ?? 0;
				const currentValue = input.value;
				const newValue = currentValue.substring(0, start) + modifiedText + currentValue.substring(end);
				(Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set)?.call(input, newValue);
				input.dispatchEvent(new Event("change", { bubbles: true }));
				const cursorPos = start + modifiedText.length;
				setTimeout(() => adjustCursor(cursorPos), 0);
			}
		}
		others.onPaste?.(event);
	};
	const handleKeyDown = (event) => {
		onKeyDown?.(event);
		if (readOnly || !withKeyboardEvents) return;
		if (event.key === "ArrowUp") {
			event.preventDefault();
			incrementRef.current?.();
		}
		if (event.key === "ArrowDown") {
			event.preventDefault();
			decrementRef.current?.();
		}
	};
	const handleKeyDownCapture = (event) => {
		onKeyDownCapture?.(event);
		if (event.key === "Backspace") {
			const input = inputRef.current;
			if (input && input.selectionStart === 0 && input.selectionStart === input.selectionEnd) {
				event.preventDefault();
				window.setTimeout(() => adjustCursor(0), 0);
			}
		}
	};
	const handleFocus = (event) => {
		if (selectAllOnFocus) setTimeout(() => event.currentTarget.select(), 0);
		onFocus?.(event);
	};
	const handleBlur = (event) => {
		let sanitizedValue = _value;
		if (isBigIntMode) {
			if (clampBehavior === "blur" && typeof sanitizedValue === "bigint") sanitizedValue = clampBigInt(sanitizedValue, minBigInt, maxBigInt);
			if (trimLeadingZeroesOnBlur && typeof sanitizedValue === "string") sanitizedValue = clampAndSanitizeBigIntInput(sanitizedValue, {
				min: minBigInt,
				max: maxBigInt,
				clampBehavior
			});
		} else {
			if (clampBehavior === "blur" && typeof sanitizedValue === "number") sanitizedValue = clamp(sanitizedValue, minNumber, maxNumber);
			if (trimLeadingZeroesOnBlur && typeof sanitizedValue === "string" && getDecimalPlaces(sanitizedValue) < 15) sanitizedValue = clampAndSanitizeInput(sanitizedValue, maxNumber, minNumber);
		}
		if (_value !== sanitizedValue) setValue(sanitizedValue);
		onBlur?.(event);
	};
	assignRef(handlersRef, {
		increment: incrementRef.current,
		decrement: decrementRef.current
	});
	const onStepHandleChange = (isIncrement) => {
		if (isIncrement) incrementRef.current?.();
		else decrementRef.current?.();
		stepCountRef.current += 1;
	};
	const onStepLoop = (isIncrement) => {
		onStepHandleChange(isIncrement);
		if (shouldUseStepInterval) {
			const interval = typeof stepHoldInterval === "number" ? stepHoldInterval : stepHoldInterval(stepCountRef.current);
			onStepTimeoutRef.current = window.setTimeout(() => onStepLoop(isIncrement), interval);
		}
	};
	const onStep = (event, isIncrement) => {
		event.preventDefault();
		inputRef.current?.focus();
		onStepHandleChange(isIncrement);
		if (shouldUseStepInterval) onStepTimeoutRef.current = window.setTimeout(() => onStepLoop(isIncrement), stepHoldDelay);
	};
	const onStepDone = () => {
		if (onStepTimeoutRef.current) window.clearTimeout(onStepTimeoutRef.current);
		onStepTimeoutRef.current = null;
		stepCountRef.current = 0;
	};
	const controls = /* @__PURE__ */ jsxs("div", {
		...getStyles("controls"),
		children: [/* @__PURE__ */ jsx(UnstyledButton, {
			...getStyles("control"),
			tabIndex: -1,
			"aria-hidden": true,
			disabled: disabled || typeof _value === "number" && maxNumber !== void 0 && _value >= maxNumber || typeof _value === "bigint" && maxBigInt !== void 0 && _value >= maxBigInt,
			mod: { direction: "up" },
			onMouseDown: (event) => event.preventDefault(),
			onPointerDown: (event) => {
				onStep(event, true);
			},
			onPointerUp: onStepDone,
			onPointerLeave: onStepDone,
			children: /* @__PURE__ */ jsx(NumberInputChevron, { direction: "up" })
		}), /* @__PURE__ */ jsx(UnstyledButton, {
			...getStyles("control"),
			tabIndex: -1,
			"aria-hidden": true,
			disabled: disabled || typeof _value === "number" && minNumber !== void 0 && _value <= minNumber || typeof _value === "bigint" && minBigInt !== void 0 && _value <= minBigInt,
			mod: { direction: "down" },
			onMouseDown: (event) => event.preventDefault(),
			onPointerDown: (event) => {
				onStep(event, false);
			},
			onPointerUp: onStepDone,
			onPointerLeave: onStepDone,
			children: /* @__PURE__ */ jsx(NumberInputChevron, { direction: "down" })
		})]
	});
	return /* @__PURE__ */ jsx(InputBase, {
		component: NumericFormat,
		allowNegative,
		className: cx(NumberInput_module_default.root, className),
		size,
		...others,
		inputMode: isBigIntMode ? "numeric" : "decimal",
		readOnly,
		disabled,
		value: typeof _value === "bigint" ? _value.toString() : _value,
		getInputRef: useMergedRef(ref, inputRef),
		onValueChange: handleValueChange,
		rightSection: hideControls || readOnly || !(isBigIntMode ? canStepBigInt(_value, allowNegativeResolved) : canStep(_value)) ? rightSection : rightSection || controls,
		classNames: resolvedClassNames,
		styles: resolvedStyles,
		unstyled,
		__staticSelector: "NumberInput",
		decimalScale: isBigIntMode ? 0 : allowDecimal ? decimalScale : 0,
		onPaste: handlePaste,
		onFocus: handleFocus,
		onKeyDown: handleKeyDown,
		onKeyDownCapture: handleKeyDownCapture,
		rightSectionPointerEvents: rightSectionPointerEvents ?? (disabled ? "none" : void 0),
		rightSectionWidth: rightSectionWidth ?? `var(--ni-right-section-width-${size || "sm"})`,
		allowLeadingZeros,
		allowedDecimalSeparators,
		onBlur: handleBlur,
		attributes,
		isAllowed: (val) => {
			if (!(isAllowed ? isAllowed(val) : true)) return false;
			if (clampBehavior !== "strict") return true;
			if (!isBigIntMode) return isInRange(val.floatValue, minNumber, maxNumber);
			if (val.value === "" || val.value === "-") return true;
			const parsed = parseBigIntFromString(val.value);
			if (parsed === null) return true;
			return (minBigInt === void 0 || parsed >= minBigInt) && (maxBigInt === void 0 || parsed <= maxBigInt);
		}
	});
});
NumberInput.classes = {
	...InputBase.classes,
	...NumberInput_module_default
};
NumberInput.varsResolver = varsResolver;
NumberInput.displayName = "@mantine/core/NumberInput";
//#endregion
export { NumberInput };

//# sourceMappingURL=NumberInput.mjs.map