UNPKG

@coreui/react-pro

Version:

UI Components Library for React.js

250 lines (208 loc) 6.58 kB
import React from 'react' import type { Label, ThumbSize } from './types' export const calculateClickValue = ( event: React.MouseEvent, container: HTMLDivElement, min: number, max: number, step: number, vertical: boolean, rtl: boolean, ) => { const clickPosition = getClickPosition(event, container, vertical, rtl) const value = min + clickPosition * (max - min) return roundToStep(value, step) } export const calculateMoveValue = ( event: MouseEvent, container: HTMLDivElement, min: number, max: number, step: number, vertical: boolean, rtl: boolean, ) => { const rect = container.getBoundingClientRect() const position = vertical ? calculateVerticalPosition(event.clientY, rect) : calculateHorizontalPosition(event.clientX, rect, rtl) if (typeof position === 'string') { return position === 'max' ? max : min } const value = min + position * (max - min) return roundToStep(value, step) } export const calculateVerticalPosition = (mouseY: number, rect: DOMRect) => { if (mouseY < rect.top) { return 'max' } if (mouseY > rect.bottom) { return 'min' } return Math.min(Math.max((rect.bottom - mouseY) / rect.height, 0), 1) } export const calculateHorizontalPosition = (mouseX: number, rect: DOMRect, rtl: boolean) => { if (mouseX < rect.left) { return rtl ? 'max' : 'min' } if (mouseX > rect.right) { return rtl ? 'min' : 'max' } const relativeX = rtl ? rect.right - mouseX : mouseX - rect.left return Math.min(Math.max(relativeX / rect.width, 0), 1) } export const calculateLabelPosition = ( min: number, max: number, labels: Label[], label: Label, index: number, ) => { if (typeof label === 'object' && 'value' in label) { return `${((label.value - min) / (max - min)) * 100}%` } return `${(index / (labels.length - 1)) * 100}%` } export const calculateTooltipPosition = ( min: number, max: number, value: number, thumbSize: ThumbSize, vertical: boolean, rtl: boolean, ) => { const percent = (value - min) / (max - min) const margin = percent > 0.5 ? `-${(percent - 0.5) * thumbSize.value}${thumbSize.unit}` : `${(0.5 - percent) * thumbSize.value}${thumbSize.unit}` if (vertical) { return { bottom: `${percent * 100}%`, marginBottom: margin, } } return rtl ? { right: `${percent * 100}%`, marginRight: margin } : { left: `${percent * 100}%`, marginLeft: margin } } export const getClickPosition = ( event: React.MouseEvent, container: HTMLDivElement, vertical: boolean, rtl: boolean, ) => { // MouseEvent doesn't have `offsetX`/`offsetY`, so we need to use `event.nativeEvent` instead const { offsetX, offsetY } = event.nativeEvent const { offsetWidth, offsetHeight } = container if (vertical) { return 1 - offsetY / offsetHeight } return rtl ? 1 - offsetX / offsetWidth : offsetX / offsetWidth } export const getLabelValue = ( min: number, max: number, labels: Label[], label: Label, index: number, ) => typeof label === 'object' && 'value' in label ? label.value : min + (index / (labels.length - 1)) * (max - min) export const getNearestValueIndex = (value: number, values: number[]) => { const valuesLength = values.length if (value < values[0]) { return 0 } if (value > values[valuesLength - 1]) { return valuesLength - 1 } const distances = values.map((v) => Math.abs(v - value)) const min = Math.min(...distances) const firstIndex = distances.indexOf(min) return value < values[firstIndex] ? firstIndex : distances.lastIndexOf(min) } export const getThumbSize = (element: HTMLDivElement, vertical: boolean): ThumbSize | null => { const value = globalThis .getComputedStyle(element, null) .getPropertyValue( vertical ? '--cui-range-slider-thumb-height' : '--cui-range-slider-thumb-width', ) const regex = /^(\d+\.?\d*)([%a-z]*)$/i const match = value.match(regex) if (match) { return { value: Number.parseFloat(match[1]), unit: match[2] || null, } } return null } export const roundToStep = (number: number, step: number) => { const _step = step === 0 ? 1 : step return Math.round(number / _step) * _step } export const updateGradient = ( min: number, max: number, values: number[], vertical: boolean, rtl: boolean, ) => { const minVal = Math.min(...values) const maxVal = Math.max(...values) const from = ((minVal - min) / (max - min)) * 100 const to = ((maxVal - min) / (max - min)) * 100 const direction = vertical ? 'to top' : rtl ? 'to left' : 'to right' return { backgroundImage: values.length === 1 ? `linear-gradient( ${direction}, var(--cui-range-slider-track-in-range-bg) 0%, var(--cui-range-slider-track-in-range-bg) ${to}%, transparent ${to}%, transparent 100% )` : `linear-gradient( ${direction}, transparent 0%, transparent ${from}%, var(--cui-range-slider-track-in-range-bg) ${from}%, var(--cui-range-slider-track-in-range-bg) ${to}%, transparent ${to}%, transparent 100% )`, } } export const updateValue = (value: number, values: number[], distance: number, index: number) => { const newValue = [...values] newValue[index] = validateValue(value, values, distance, index) return newValue } export const validateValue = (value: number, values: number[], distance: number, index: number) => { // If there's only one value, return it as is if (values.length === 1) { return value } // Determine previous and next values if they exist const prevValue: number | undefined = index > 0 ? values[index - 1] : undefined const nextValue: number | undefined = index < values.length - 1 ? values[index + 1] : undefined // If it's the first element, ensure it's not too close to the next value if (index === 0 && nextValue !== undefined) { return Math.min(value, nextValue - distance) } // If it's the last element, ensure it's not too close to the previous value if (index === values.length - 1 && prevValue !== undefined) { return Math.max(value, prevValue + distance) } // For middle elements, ensure the value is within the allowed distance from both neighbors if (prevValue !== undefined && nextValue !== undefined) { const minVal = prevValue + distance const maxVal = nextValue - distance return Math.min(Math.max(value, minVal), maxVal) } // Fallback: If for some reason prevValue or nextValue is undefined, return the original value return value }