@coreui/react-pro
Version:
UI Components Library for React.js
250 lines (208 loc) • 6.58 kB
text/typescript
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
}