@zag-js/numeric-range
Version:
Numeric range utilities
145 lines (142 loc) • 5.39 kB
JavaScript
// src/index.ts
function getMinValueAtIndex(index, values, minValue) {
return index === 0 ? minValue : values[index - 1];
}
function getMaxValueAtIndex(index, values, maxValue) {
return index === values.length - 1 ? maxValue : values[index + 1];
}
function isValueAtMax(value, maxValue) {
return value >= maxValue;
}
function isValueAtMin(value, minValue) {
return value <= minValue;
}
function isValueWithinRange(value, minValue, maxValue) {
return value >= minValue && value <= maxValue;
}
function getRoundedValue(value, minValue, step) {
return Math.round((value - minValue) / step) * step + minValue;
}
function clampValue(value, minValue, maxValue) {
return Math.min(Math.max(value, minValue), maxValue);
}
function getValuePercent(value, minValue, maxValue) {
return (value - minValue) / (maxValue - minValue);
}
function getPercentValue(percent, minValue, maxValue, step) {
const value = percent * (maxValue - minValue) + minValue;
const roundedValue = getRoundedValue(value, minValue, step);
return clampValue(roundedValue, minValue, maxValue);
}
function roundToStepPrecision(value, step) {
let roundedValue = value;
let stepString = step.toString();
let pointIndex = stepString.indexOf(".");
let precision = pointIndex >= 0 ? stepString.length - pointIndex : 0;
if (precision > 0) {
let pow = Math.pow(10, precision);
roundedValue = Math.round(roundedValue * pow) / pow;
}
return roundedValue;
}
function snapValueToStep(value, min, max, step) {
min = Number(min);
max = Number(max);
let remainder = (value - (isNaN(min) ? 0 : min)) % step;
let snappedValue = roundToStepPrecision(
Math.abs(remainder) * 2 >= step ? value + Math.sign(remainder) * (step - Math.abs(remainder)) : value - remainder,
step
);
if (!isNaN(min)) {
if (snappedValue < min) {
snappedValue = min;
} else if (!isNaN(max) && snappedValue > max) {
snappedValue = min + Math.floor(roundToStepPrecision((max - min) / step, step)) * step;
}
} else if (!isNaN(max) && snappedValue > max) {
snappedValue = Math.floor(roundToStepPrecision(max / step, step)) * step;
}
snappedValue = roundToStepPrecision(snappedValue, step);
return snappedValue;
}
function setValueAtIndex(values, index, value) {
if (values[index] === value) return values;
return [...values.slice(0, index), value, ...values.slice(index + 1)];
}
function getValueSetterAtIndex(index, ctx) {
const minValueAtIndex = getMinValueAtIndex(index, ctx.values, ctx.min);
const maxValueAtIndex = getMaxValueAtIndex(index, ctx.values, ctx.max);
let nextValues = ctx.values.slice();
return function setValue(value) {
let nextValue = snapValueToStep(value, minValueAtIndex, maxValueAtIndex, ctx.step);
nextValues = setValueAtIndex(nextValues, index, value);
nextValues[index] = nextValue;
return nextValues;
};
}
function getNextStepValue(index, ctx) {
const nextValue = ctx.values[index] + ctx.step;
return getValueSetterAtIndex(index, ctx)(nextValue);
}
function getPreviousStepValue(index, ctx) {
const nextValue = ctx.values[index] - ctx.step;
return getValueSetterAtIndex(index, ctx)(nextValue);
}
function getClosestValueIndex(values, targetValue) {
let targetIndex = values.findIndex((value) => targetValue - value < 0);
if (targetIndex === 0) {
return targetIndex;
}
if (targetIndex === -1) {
return values.length - 1;
}
let valueBefore = values[targetIndex - 1];
let valueAfter = values[targetIndex];
if (Math.abs(valueBefore - targetValue) < Math.abs(valueAfter - targetValue)) {
return targetIndex - 1;
}
return targetIndex;
}
function getValueRanges(values, minValue, maxValue, gap) {
return values.map((value, index) => {
const min = index === 0 ? minValue : values[index - 1] + gap;
const max = index === values.length - 1 ? maxValue : values[index + 1] - gap;
return { min, max, value };
});
}
function getValueTransformer(valueA, valueB) {
const input = { min: valueA[0], max: valueA[1] };
const output = { min: valueB[0], max: valueB[1] };
return function getValue(value) {
if (input.min === input.max || output.min === output.max) return output.min;
const ratio = (output.max - output.min) / (input.max - input.min);
return output.min + ratio * (value - input.min);
};
}
function toFixedNumber(value, digits = 0, base = 10) {
const pow = Math.pow(base, digits);
return Math.round(value * pow) / pow;
}
function mod(value, modulo) {
return (value % modulo + modulo) % modulo;
}
exports.clampValue = clampValue;
exports.getClosestValueIndex = getClosestValueIndex;
exports.getMaxValueAtIndex = getMaxValueAtIndex;
exports.getMinValueAtIndex = getMinValueAtIndex;
exports.getNextStepValue = getNextStepValue;
exports.getPercentValue = getPercentValue;
exports.getPreviousStepValue = getPreviousStepValue;
exports.getRoundedValue = getRoundedValue;
exports.getValuePercent = getValuePercent;
exports.getValueRanges = getValueRanges;
exports.getValueSetterAtIndex = getValueSetterAtIndex;
exports.getValueTransformer = getValueTransformer;
exports.isValueAtMax = isValueAtMax;
exports.isValueAtMin = isValueAtMin;
exports.isValueWithinRange = isValueWithinRange;
exports.mod = mod;
exports.roundToStepPrecision = roundToStepPrecision;
exports.snapValueToStep = snapValueToStep;
exports.toFixedNumber = toFixedNumber;
;