UNPKG

bits-ui

Version:

The headless components for Svelte.

220 lines (219 loc) 6.78 kB
export function getRangeStyles(direction, min, max) { const styles = { position: "absolute", }; if (direction === "lr") { styles.left = `${min}%`; styles.right = `${max}%`; } else if (direction === "rl") { styles.right = `${min}%`; styles.left = `${max}%`; } else if (direction === "bt") { styles.bottom = `${min}%`; styles.top = `${max}%`; } else { styles.top = `${min}%`; styles.bottom = `${max}%`; } return styles; } export function getThumbStyles(direction, thumbPos) { const styles = { position: "absolute", }; if (direction === "lr") { styles.left = `${thumbPos}%`; styles.translate = "-50% 0"; } else if (direction === "rl") { styles.right = `${thumbPos}%`; styles.translate = "50% 0"; } else if (direction === "bt") { styles.bottom = `${thumbPos}%`; styles.translate = "0 50%"; } else { styles.top = `${thumbPos}%`; styles.translate = "0 -50%"; } return styles; } export function getTickStyles(direction, tickPosition, offsetPercentage) { const style = { position: "absolute", }; if (direction === "lr") { style.left = `${tickPosition}%`; style.translate = `${offsetPercentage}% 0`; } else if (direction === "rl") { style.right = `${tickPosition}%`; style.translate = `${-offsetPercentage}% 0`; } else if (direction === "bt") { style.bottom = `${tickPosition}%`; style.translate = `0 ${-offsetPercentage}%`; } else { style.top = `${tickPosition}%`; style.translate = `0 ${offsetPercentage}%`; } return style; } export function getTickLabelStyles(direction, tickPosition, labelPosition = "top") { const style = { position: "absolute", }; if (direction === "lr" || direction === "rl") { // Horizontal slider style.left = direction === "lr" ? `${tickPosition}%` : undefined; style.right = direction === "rl" ? `${tickPosition}%` : undefined; style.translate = "-50% 0"; if (labelPosition === "top") { style.bottom = "100%"; } else if (labelPosition === "bottom") { style.top = "100%"; } } else { // Vertical slider - use same positioning as ticks if (direction === "tb") { style.top = `${tickPosition}%`; } else { style.bottom = `${tickPosition}%`; } style.translate = "0 50%"; if (labelPosition === "left") { style.right = "100%"; } else if (labelPosition === "right") { style.left = "100%"; } } return style; } export function getThumbLabelStyles(direction, thumbPosition, labelPosition = "top") { const style = { position: "absolute", }; if (direction === "lr" || direction === "rl") { // Horizontal slider style.left = direction === "lr" ? `${thumbPosition}%` : undefined; style.right = direction === "rl" ? `${thumbPosition}%` : undefined; style.translate = "-50% 0"; if (labelPosition === "top") { style.bottom = "100%"; } else if (labelPosition === "bottom") { style.top = "100%"; } } else { // Vertical slider if (direction === "tb") { style.top = `${thumbPosition}%`; } else { style.bottom = `${thumbPosition}%`; } style.translate = "0 -50%"; if (labelPosition === "left") { style.right = "100%"; } else if (labelPosition === "right") { style.left = "100%"; } } return style; } /** * Gets the number of decimal places in a number */ function getDecimalPlaces(num) { if (Math.floor(num) === num) return 0; const str = num.toString(); if (str.indexOf(".") !== -1 && str.indexOf("e-") === -1) { return str.split(".")[1].length; } else if (str.indexOf("e-") !== -1) { const parts = str.split("e-"); return parseInt(parts[1], 10); } return 0; } /** * Rounds a number to the specified number of decimal places */ function roundToPrecision(num, precision) { const factor = Math.pow(10, precision); return Math.round(num * factor) / factor; } /** * Normalizes step to always be a sorted array of valid values within min/max range */ export function normalizeSteps(step, min, max) { if (typeof step === "number") { // generate regular steps - match original behavior exactly const difference = max - min; let count = Math.ceil(difference / step); // Get precision from step to avoid floating point errors const precision = getDecimalPlaces(step); // Check if difference is divisible by step using integer arithmetic to avoid floating point errors const factor = Math.pow(10, precision); const intDifference = Math.round(difference * factor); const intStep = Math.round(step * factor); if (intDifference % intStep === 0) { count++; } const steps = []; for (let i = 0; i < count; i++) { const value = min + i * step; // Round to the precision of the step to avoid floating point errors const roundedValue = roundToPrecision(value, precision); steps.push(roundedValue); } return steps; } return [...new Set(step)].filter((value) => value >= min && value <= max).sort((a, b) => a - b); } /** * Snaps a value to the nearest step in a custom steps array */ export function snapValueToCustomSteps(value, steps) { if (steps.length === 0) return value; // Find the closest step let closest = steps[0]; let minDistance = Math.abs(value - closest); for (const step of steps) { const distance = Math.abs(value - step); if (distance < minDistance) { minDistance = distance; closest = step; } } return closest; } /** * Gets the next/previous step value for keyboard navigation */ export function getAdjacentStepValue(currentValue, steps, direction) { const currentIndex = steps.indexOf(currentValue); if (currentIndex === -1) { // current value is not in steps, snap to nearest return snapValueToCustomSteps(currentValue, steps); } if (direction === "next") { return currentIndex < steps.length - 1 ? steps[currentIndex + 1] : currentValue; } else { return currentIndex > 0 ? steps[currentIndex - 1] : currentValue; } }