diff --git a/studio/frontend/src/components/ui/slider.tsx b/studio/frontend/src/components/ui/slider.tsx index f65b3ffa71..5d75b88e6d 100644 --- a/studio/frontend/src/components/ui/slider.tsx +++ b/studio/frontend/src/components/ui/slider.tsx @@ -9,25 +9,41 @@ function Slider({ value, min = 0, max = 100, + orientation = "horizontal", + onValueChange, ...props }: React.ComponentProps) { - const _values = React.useMemo( - () => - Array.isArray(value) - ? value - : Array.isArray(defaultValue) - ? defaultValue - : [min, max], - [value, defaultValue, min, max], + const isControlled = Array.isArray(value); + const [uncontrolledValues, setUncontrolledValues] = + React.useState(() => + Array.isArray(defaultValue) ? defaultValue : [min, max], + ); + + const values = isControlled ? value : uncontrolledValues; + const handleValueChange = React.useCallback( + (nextValues: number[]) => { + if (!isControlled) { + setUncontrolledValues(nextValues); + } + onValueChange?.(nextValues); + }, + [isControlled, onValueChange], ); // For single-thumb horizontal sliders, render the fill bar as a sibling of // the track (outside its overflow-hidden container) so it can align flush // with the thumb center without being clipped. The Range inside the track // is hidden in this case to avoid double-painting. - const isSingleThumb = _values.length === 1; - const fillPercent = isSingleThumb - ? Math.min(100, Math.max(0, (((_values[0] ?? min) - min) / (max - min)) * 100)) + const isSingleThumbHorizontal = + values.length === 1 && orientation === "horizontal"; + const fillPercent = isSingleThumbHorizontal + ? Math.min( + 100, + Math.max( + 0, + max === min ? 0 : (((values[0] ?? min) - min) / (max - min)) * 100, + ), + ) : null; return ( @@ -37,6 +53,8 @@ function Slider({ value={value} min={min} max={max} + orientation={orientation} + onValueChange={handleValueChange} className={cn( "data-vertical:min-h-40 relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:w-auto data-vertical:flex-col", className, @@ -51,18 +69,18 @@ function Slider({ data-slot="slider-range" className={cn( "bg-primary absolute select-none data-horizontal:h-full data-vertical:w-full", - isSingleThumb && "opacity-0", + isSingleThumbHorizontal && "opacity-0", )} /> - {isSingleThumb && ( + {isSingleThumbHorizontal && (
)} - {Array.from({ length: _values.length }, (_, index) => ( + {Array.from({ length: values.length }, (_, index) => (