import { clamp } from "./clamp"; export interface GetPushedThumbValuesParams { values: readonly number[]; index: number; nextValue: number; min: number; max: number; step: number; minStepsBetweenValues: number; initialValues?: readonly number[] | undefined; } /** * Returns a new array of slider values where attempting to move the thumb at * `index` beyond its neighbours "pushes" them while respecting * `minStepsBetweenValues`. When `initialValues` is provided, pushed * neighbours restore toward their initial value as space opens back up. */ export function getPushedThumbValues({ values, index, nextValue, min, max, step, minStepsBetweenValues, initialValues, }: GetPushedThumbValuesParams): number[] { if (values.length === 0) { return []; } const nextValues = values.slice(); const minValueDifference = step * minStepsBetweenValues; const lastIndex = nextValues.length - 1; const baseInitialValues = initialValues ?? values; const indexMin = min + index * minValueDifference; const indexMax = max - (lastIndex - index) * minValueDifference; nextValues[index] = clamp(nextValue, indexMin, indexMax); for (let i = index + 1; i <= lastIndex; i += 1) { // biome-ignore lint/style/noNonNullAssertion: loop invariant keeps i-1 in range const minAllowed = nextValues[i - 1]! + minValueDifference; const maxAllowed = max - (lastIndex - i) * minValueDifference; // biome-ignore lint/style/noNonNullAssertion: i is within bounds const initialValue = baseInitialValues[i] ?? nextValues[i]!; // biome-ignore lint/style/noNonNullAssertion: i is within bounds let candidate = Math.max(nextValues[i]!, minAllowed); if (initialValue < candidate) { candidate = Math.max(initialValue, minAllowed); } nextValues[i] = clamp(candidate, minAllowed, maxAllowed); } for (let i = index - 1; i >= 0; i -= 1) { // biome-ignore lint/style/noNonNullAssertion: loop invariant keeps i+1 in range const maxAllowed = nextValues[i + 1]! - minValueDifference; const minAllowed = min + i * minValueDifference; // biome-ignore lint/style/noNonNullAssertion: i is within bounds const initialValue = baseInitialValues[i] ?? nextValues[i]!; // biome-ignore lint/style/noNonNullAssertion: i is within bounds let candidate = Math.min(nextValues[i]!, maxAllowed); if (initialValue > candidate) { candidate = Math.min(initialValue, maxAllowed); } nextValues[i] = clamp(candidate, minAllowed, maxAllowed); } for (let i = 0; i <= lastIndex; i += 1) { // biome-ignore lint/style/noNonNullAssertion: i is within bounds nextValues[i] = Number(nextValues[i]!.toFixed(12)); } return nextValues; }