From ad2acbc07a3b3e6d02952afc802a44f5d37fcd0c Mon Sep 17 00:00:00 2001 From: imagineer99 Date: Fri, 6 Mar 2026 05:21:44 +0000 Subject: [PATCH 1/2] fix: align slider fill bar with thumb across value range --- studio/frontend/src/components/ui/slider.tsx | 133 +++++++++++-------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/studio/frontend/src/components/ui/slider.tsx b/studio/frontend/src/components/ui/slider.tsx index 4a235e674d..f65b3ffa71 100644 --- a/studio/frontend/src/components/ui/slider.tsx +++ b/studio/frontend/src/components/ui/slider.tsx @@ -1,57 +1,76 @@ -import { Slider as SliderPrimitive } from "radix-ui"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -function Slider({ - className, - defaultValue, - value, - min = 0, - max = 100, - ...props -}: React.ComponentProps) { - const _values = React.useMemo( - () => - Array.isArray(value) - ? value - : Array.isArray(defaultValue) - ? defaultValue - : [min, max], - [value, defaultValue, min, max], - ); - - return ( - - - - - {Array.from({ length: _values.length }, (_, index) => ( - - ))} - - ); -} - -export { Slider }; +import { Slider as SliderPrimitive } from "radix-ui"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +function Slider({ + className, + defaultValue, + value, + min = 0, + max = 100, + ...props +}: React.ComponentProps) { + const _values = React.useMemo( + () => + Array.isArray(value) + ? value + : Array.isArray(defaultValue) + ? defaultValue + : [min, max], + [value, defaultValue, min, max], + ); + + // 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)) + : null; + + return ( + + + + + {isSingleThumb && ( +
+ )} + {Array.from({ length: _values.length }, (_, index) => ( + + ))} + + ); +} + +export { Slider }; From 075bfe961b838a6e05442e420cd24ad475c7ffc0 Mon Sep 17 00:00:00 2001 From: imagineer99 Date: Sun, 8 Mar 2026 10:06:17 +0000 Subject: [PATCH 2/2] fix: track live slider values for uncontrolled mode and scope fill to horizontal --- studio/frontend/src/components/ui/slider.tsx | 46 ++++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) 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) => (