UNPKG

@base-ui-components/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

80 lines (78 loc) 2.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.removeFloatingPointErrors = removeFloatingPointErrors; exports.toValidatedNumber = toValidatedNumber; var _clamp = require("../../utils/clamp"); var _formatNumber = require("../../utils/formatNumber"); const STEP_EPSILON_FACTOR = 1e-10; function getFractionDigits(format) { const defaultOptions = (0, _formatNumber.getFormatter)('en-US').resolvedOptions(); const minimumFractionDigits = format?.minimumFractionDigits ?? defaultOptions.minimumFractionDigits ?? 0; const maximumFractionDigits = Math.max(format?.maximumFractionDigits ?? defaultOptions.maximumFractionDigits ?? 20, minimumFractionDigits); return { maximumFractionDigits, minimumFractionDigits }; } function roundToFractionDigits(value, maximumFractionDigits) { if (!Number.isFinite(value)) { return value; } const digits = Math.min(Math.max(maximumFractionDigits, 0), 20); return Number(value.toFixed(digits)); } function removeFloatingPointErrors(value, format) { const { maximumFractionDigits } = getFractionDigits(format); return roundToFractionDigits(value, maximumFractionDigits); } function snapToStep(clampedValue, base, step, mode = 'directional') { if (step === 0) { return clampedValue; } const stepSize = Math.abs(step); const direction = Math.sign(step); const tolerance = stepSize * STEP_EPSILON_FACTOR * direction; const divisor = mode === 'nearest' ? step : stepSize; const rawSteps = (clampedValue - base + tolerance) / divisor; let snappedSteps; if (mode === 'nearest') { snappedSteps = Math.round(rawSteps); } else if (direction > 0) { snappedSteps = Math.floor(rawSteps); } else { snappedSteps = Math.ceil(rawSteps); } const stepForResult = mode === 'nearest' ? step : stepSize; return base + snappedSteps * stepForResult; } function toValidatedNumber(value, { step, minWithDefault, maxWithDefault, minWithZeroDefault, format, snapOnStep, small }) { if (value === null) { return value; } const clampedValue = (0, _clamp.clamp)(value, minWithDefault, maxWithDefault); if (step != null && snapOnStep) { if (step === 0) { return removeFloatingPointErrors(clampedValue, format); } // If a real minimum is provided, use it let base = minWithZeroDefault; if (!small && minWithDefault !== Number.MIN_SAFE_INTEGER) { base = minWithDefault; } const snappedValue = snapToStep(clampedValue, base, step, small ? 'nearest' : 'directional'); return removeFloatingPointErrors(snappedValue, format); } return removeFloatingPointErrors(clampedValue, format); }