diff --git a/packages/@mantine/core/src/components/Slider/RangeSlider/RangeSlider.tsx b/packages/@mantine/core/src/components/Slider/RangeSlider/RangeSlider.tsx index d2b5caf22c2..3f21734f2ef 100644 --- a/packages/@mantine/core/src/components/Slider/RangeSlider/RangeSlider.tsx +++ b/packages/@mantine/core/src/components/Slider/RangeSlider/RangeSlider.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from 'react'; -import { useMergedRef, useMove, useUncontrolled } from '@mantine/hooks'; +import { clamp, useMergedRef, useMove, useUncontrolled } from '@mantine/hooks'; import { BoxProps, createVarsResolver, @@ -58,6 +58,9 @@ export interface RangeSliderProps /** Maximum possible value, `100` by default */ max?: number; + /** Domain of the slider, defines the full range of possible values, `[min, max]` by default */ + domain?: [number, number]; + /** Number by which value will be incremented/decremented with thumb drag and arrows, `1` by default */ step?: number; @@ -175,6 +178,7 @@ export const RangeSlider = factory((_props, ref) => { size, min, max, + domain, minRange, maxRange, step, @@ -228,9 +232,10 @@ export const RangeSlider = factory((_props, ref) => { const thumbs = useRef([]); const root = useRef(null); const thumbIndex = useRef(undefined); + const [domainMin, domainMax] = domain || [min!, max!]; const positions = [ - getPosition({ value: _value[0], min: min!, max: max! }), - getPosition({ value: _value[1], min: min!, max: max! }), + getPosition({ value: _value[0], min: domainMin, max: domainMax }), + getPosition({ value: _value[1], min: domainMin, max: domainMax }), ]; const precision = _precision ?? getPrecision(step!); @@ -281,33 +286,34 @@ export const RangeSlider = factory((_props, ref) => { } } } else { - clone[index] = val; + const clampedVal = clamp(val, min!, max!); + clone[index] = clampedVal; if (index === 0) { - if (val > clone[1] - (minRange! - 0.000000001)) { - clone[1] = Math.min(val + minRange!, max!); + if (clampedVal > clone[1] - (minRange! - 0.000000001)) { + clone[1] = Math.min(clampedVal + minRange!, max!); } - if (val > (max! - (minRange! - 0.000000001) || min!)) { + if (clampedVal > (max! - (minRange! - 0.000000001) || min!)) { clone[index] = valueRef.current[index]; } - if (clone[1] - val > maxRange!) { - clone[1] = val + maxRange!; + if (clone[1] - clampedVal > maxRange!) { + clone[1] = clampedVal + maxRange!; } } if (index === 1) { - if (val < clone[0] + minRange!) { - clone[0] = Math.max(val - minRange!, min!); + if (clampedVal < clone[0] + minRange!) { + clone[0] = Math.max(clampedVal - minRange!, min!); } - if (val < clone[0] + minRange!) { + if (clampedVal < clone[0] + minRange!) { clone[index] = valueRef.current[index]; } - if (val - clone[0] > maxRange!) { - clone[0] = val - maxRange!; + if (clampedVal - clone[0] > maxRange!) { + clone[0] = clampedVal - maxRange!; } } } @@ -332,8 +338,8 @@ export const RangeSlider = factory((_props, ref) => { if (!disabled) { const nextValue = getChangeValue({ value: val, - min: min!, - max: max!, + min: domainMin, + max: domainMax, step: step!, precision, }); @@ -497,8 +503,8 @@ export const RangeSlider = factory((_props, ref) => { filled={positions[1] - positions[0]} marks={marks} inverted={inverted} - min={min!} - max={max!} + min={domainMin} + max={domainMax} value={_value[1]} disabled={disabled} containerProps={{ diff --git a/packages/@mantine/core/src/components/Slider/Slider.story.tsx b/packages/@mantine/core/src/components/Slider/Slider.story.tsx index 814d815119a..628091b16e8 100644 --- a/packages/@mantine/core/src/components/Slider/Slider.story.tsx +++ b/packages/@mantine/core/src/components/Slider/Slider.story.tsx @@ -61,6 +61,15 @@ export function SizeSlider() { ); } +export function DomainSlider() { + return ( +
+ + +
+ ); +} + export function Range() { return (
diff --git a/packages/@mantine/core/src/components/Slider/Slider/Slider.tsx b/packages/@mantine/core/src/components/Slider/Slider/Slider.tsx index 1dca9bd37bd..eb31614f769 100644 --- a/packages/@mantine/core/src/components/Slider/Slider/Slider.tsx +++ b/packages/@mantine/core/src/components/Slider/Slider/Slider.tsx @@ -55,6 +55,9 @@ export interface SliderProps /** Maximum possible value, `100` by default */ max?: number; + /** Domain of the slider, defines the full range of possible values, `[min, max]` by default */ + domain?: [number, number]; + /** Number by which value will be incremented/decremented with thumb drag and arrows, `1` by default */ step?: number; @@ -164,6 +167,7 @@ export const Slider = factory((_props, ref) => { size, min, max, + domain, step, precision: _precision, defaultValue, @@ -219,7 +223,8 @@ export const Slider = factory((_props, ref) => { const root = useRef(null); const thumb = useRef(null); - const position = getPosition({ value: _value, min: min!, max: max! }); + const [domainMin, domainMax] = domain || [min!, max!]; + const position = getPosition({ value: _value, min: domainMin, max: domainMax }); const scaledValue = scale!(_value); const _label = typeof label === 'function' ? label(scaledValue) : label; const precision = _precision ?? getPrecision(step!); @@ -229,23 +234,24 @@ export const Slider = factory((_props, ref) => { if (!disabled) { const nextValue = getChangeValue({ value: x, - min: min!, - max: max!, + min: domainMin, + max: domainMax, step: step!, precision, }); + const clampedValue = clamp(nextValue, min!, max!); setValue( restrictToMarks && marks?.length ? findClosestNumber( - nextValue, + clampedValue, marks.map((mark) => mark.value) ) - : nextValue + : clampedValue ); - valueRef.current = nextValue; + valueRef.current = clampedValue; } }, - [disabled, min, max, step, precision, setValue, marks, restrictToMarks] + [disabled, min, max, domainMin, domainMax, step, precision, setValue, marks, restrictToMarks] ); const handleScrubEnd = useCallback(() => { @@ -409,8 +415,8 @@ export const Slider = factory((_props, ref) => { offset={0} filled={position} marks={marks} - min={min!} - max={max!} + min={domainMin} + max={domainMax} value={scaledValue} disabled={disabled} containerProps={{ @@ -420,8 +426,8 @@ export const Slider = factory((_props, ref) => { }} >