feat(slider): add pure value/position math helpers

This commit is contained in:
Ilia Mashkov
2026-06-02 10:50:46 +03:00
parent 67af3d946a
commit 4756682863
2 changed files with 114 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
import {
clampNumber,
roundToStepPrecision,
} from '$shared/lib/utils';
/**
* Geometry/range options shared by the math helpers.
*/
type SliderMathOpts = {
/**
* Minimum value (inclusive)
*/
min: number;
/**
* Maximum value (inclusive)
*/
max: number;
/**
* Step increment
*/
step: number;
};
/**
* Snap a raw value onto the step grid, then clamp to [min, max].
*
* Snapping is anchored to `min` so non-zero ranges land on valid stops.
* `roundToStepPrecision` removes IEEE-754 drift from fractional steps.
*/
export function snapToStep(raw: number, { min, max, step }: SliderMathOpts): number {
if (step <= 0) {
return clampNumber(raw, min, max);
}
const snapped = min + Math.round((raw - min) / step) * step;
return clampNumber(roundToStepPrecision(snapped, step), min, max);
}
/**
* Convert a pointer coordinate into a slider value.
*
* Horizontal maps left→min, right→max. Vertical is inverted so that
* up→max, matching natural slider expectations. The DOMRect is passed in
* to keep this pure and unit-testable without layout.
*/
export function pointerToValue(
point: { clientX: number; clientY: number },
rect: DOMRect,
opts: SliderMathOpts & { orientation: 'horizontal' | 'vertical' },
): number {
const { min, max, orientation } = opts;
const size = orientation === 'vertical' ? rect.height : rect.width;
if (size <= 0) {
return snapToStep(min, opts);
}
const ratio = orientation === 'vertical'
? (rect.bottom - point.clientY) / size
: (point.clientX - rect.left) / size;
return snapToStep(min + clampNumber(ratio, 0, 1) * (max - min), opts);
}