@@ -60,13 +60,7 @@ function getAriaValueText(
6060}
6161
6262const TICK_COUNT = 16 ;
63- const TEXT_PADDING_X = 4 ;
64- const TEXT_PADDING_X_OUTER = 0 ; // Less inset on outer-facing side (near edges)
6563const TEXT_PADDING_Y = 2 ;
66- const DETECTION_MARGIN_X = 12 ;
67- const DETECTION_MARGIN_X_OUTER = 4 ; // Small margin at edges for steep falloff - segments fully close at terminal positions
68- const DETECTION_MARGIN_Y = 12 ;
69- const TRACK_HEIGHT = 48 ;
7064const TEXT_RELEASE_INSET = 8 ;
7165const TRACK_EDGE_INSET = 4 ; // px from track edge - keeps elements visible at extremes
7266const THUMB_WIDTH = 12 ; // w-3
@@ -100,129 +94,6 @@ function toRadixThumbPosition(percent: number): string {
10094 return `calc(${ safePercent } % + ${ offsetPx } px)` ;
10195}
10296
103- function signedDistanceToRoundedRect (
104- px : number ,
105- py : number ,
106- left : number ,
107- right : number ,
108- top : number ,
109- bottom : number ,
110- radiusLeft : number ,
111- radiusRight : number ,
112- ) : number {
113- const innerLeft = left + radiusLeft ;
114- const innerRight = right - radiusRight ;
115- const innerTop = top + Math . max ( radiusLeft , radiusRight ) ;
116- const innerBottom = bottom - Math . max ( radiusLeft , radiusRight ) ;
117-
118- const inLeftCorner = px < innerLeft ;
119- const inRightCorner = px > innerRight ;
120- const inCornerY = py < innerTop || py > innerBottom ;
121-
122- if ( ( inLeftCorner || inRightCorner ) && inCornerY ) {
123- const radius = inLeftCorner ? radiusLeft : radiusRight ;
124- const cornerX = inLeftCorner ? innerLeft : innerRight ;
125- const cornerY = py < innerTop ? top + radius : bottom - radius ;
126- const distToCornerCenter = Math . hypot ( px - cornerX , py - cornerY ) ;
127- return distToCornerCenter - radius ;
128- }
129-
130- const dx = Math . max ( left - px , px - right , 0 ) ;
131- const dy = Math . max ( top - py , py - bottom , 0 ) ;
132-
133- if ( dx === 0 && dy === 0 ) {
134- return - Math . min ( px - left , right - px , py - top , bottom - py ) ;
135- }
136-
137- return Math . max ( dx , dy ) ;
138- }
139-
140- const OUTER_EDGE_RADIUS_FACTOR = 0.3 ; // Reduced radius on outer-facing sides for steeper falloff
141-
142- function calculateGap (
143- thumbCenterX : number ,
144- textRect : { left : number ; right : number ; height : number ; centerY : number } ,
145- isLeftAligned : boolean ,
146- ) : number {
147- const { left, right, height, centerY } = textRect ;
148- // Asymmetric padding/margin: outer-facing side has less padding, more margin
149- const paddingLeft = isLeftAligned ? TEXT_PADDING_X_OUTER : TEXT_PADDING_X ;
150- const paddingRight = isLeftAligned ? TEXT_PADDING_X : TEXT_PADDING_X_OUTER ;
151- const marginLeft = isLeftAligned
152- ? DETECTION_MARGIN_X_OUTER
153- : DETECTION_MARGIN_X ;
154- const marginRight = isLeftAligned
155- ? DETECTION_MARGIN_X
156- : DETECTION_MARGIN_X_OUTER ;
157- const paddingY = TEXT_PADDING_Y ;
158- const marginY = DETECTION_MARGIN_Y ;
159- const thumbCenterY = centerY ;
160-
161- // Inner boundary (where max gap occurs)
162- const innerLeft = left - paddingLeft ;
163- const innerRight = right + paddingRight ;
164- const innerTop = centerY - height / 2 - paddingY ;
165- const innerBottom = centerY + height / 2 + paddingY ;
166- const innerHeight = height + paddingY * 2 ;
167- const innerRadius = innerHeight / 2 ;
168- // Smaller radius on outer-facing side (left for label, right for value)
169- const innerRadiusLeft = isLeftAligned
170- ? innerRadius * OUTER_EDGE_RADIUS_FACTOR
171- : innerRadius ;
172- const innerRadiusRight = isLeftAligned
173- ? innerRadius
174- : innerRadius * OUTER_EDGE_RADIUS_FACTOR ;
175-
176- // Outer boundary (where effect starts) - proportionally larger
177- const outerLeft = left - paddingLeft - marginLeft ;
178- const outerRight = right + paddingRight + marginRight ;
179- const outerTop = centerY - height / 2 - paddingY - marginY ;
180- const outerBottom = centerY + height / 2 + paddingY + marginY ;
181- const outerHeight = height + paddingY * 2 + marginY * 2 ;
182- const outerRadius = outerHeight / 2 ;
183- const outerRadiusLeft = isLeftAligned
184- ? outerRadius * OUTER_EDGE_RADIUS_FACTOR
185- : outerRadius ;
186- const outerRadiusRight = isLeftAligned
187- ? outerRadius
188- : outerRadius * OUTER_EDGE_RADIUS_FACTOR ;
189-
190- const outerDist = signedDistanceToRoundedRect (
191- thumbCenterX ,
192- thumbCenterY ,
193- outerLeft ,
194- outerRight ,
195- outerTop ,
196- outerBottom ,
197- outerRadiusLeft ,
198- outerRadiusRight ,
199- ) ;
200-
201- // Outside outer boundary - no gap
202- if ( outerDist > 0 ) return 0 ;
203-
204- const innerDist = signedDistanceToRoundedRect (
205- thumbCenterX ,
206- thumbCenterY ,
207- innerLeft ,
208- innerRight ,
209- innerTop ,
210- innerBottom ,
211- innerRadiusLeft ,
212- innerRadiusRight ,
213- ) ;
214-
215- // Inside inner boundary - max gap
216- const maxGap = height + paddingY * 2 ;
217- if ( innerDist <= 0 ) return maxGap ;
218-
219- // Between boundaries - linear interpolation
220- // outerDist is negative (inside outer), innerDist is positive (outside inner)
221- const totalDist = Math . abs ( outerDist ) + innerDist ;
222- const t = Math . abs ( outerDist ) / totalDist ;
223-
224- return maxGap * t ;
225- }
22697
22798interface SliderRowProps {
22899 config : SliderConfig ;
@@ -254,7 +125,6 @@ function SliderRow({
254125 const labelRef = useRef < HTMLSpanElement > ( null ) ;
255126 const valueRef = useRef < HTMLSpanElement > ( null ) ;
256127
257- const [ dragGap , setDragGap ] = useState ( 0 ) ;
258128 const [ fullGap , setFullGap ] = useState ( 0 ) ;
259129 const [ intersectsText , setIntersectsText ] = useState ( false ) ;
260130 const [ layoutVersion , setLayoutVersion ] = useState ( 0 ) ;
@@ -294,6 +164,7 @@ function SliderRow({
294164 const valueEl = valueRef . current ;
295165
296166 if ( ! track || ! labelEl || ! valueEl ) return ;
167+ if ( isDragging ) return ;
297168
298169 const trackRect = track . getBoundingClientRect ( ) ;
299170 const labelRect = labelEl . getBoundingClientRect ( ) ;
@@ -307,33 +178,6 @@ function SliderRow({
307178 getRadixThumbInBoundsOffsetPx ( valuePercent ) ;
308179 const thumbHalfWidth = THUMB_WIDTH / 2 ;
309180
310- // Text is raised by TEXT_VERTICAL_OFFSET from center
311- const trackCenterY = TRACK_HEIGHT / 2 - TEXT_VERTICAL_OFFSET ;
312-
313- const labelGap = calculateGap (
314- thumbCenterPx ,
315- {
316- left : labelRect . left - trackRect . left ,
317- right : labelRect . right - trackRect . left ,
318- height : labelRect . height ,
319- centerY : trackCenterY ,
320- } ,
321- true ,
322- ) ; // label is left-aligned
323-
324- const valueGap = calculateGap (
325- thumbCenterPx ,
326- {
327- left : valueRect . left - trackRect . left ,
328- right : valueRect . right - trackRect . left ,
329- height : valueRect . height ,
330- centerY : trackCenterY ,
331- } ,
332- false ,
333- ) ; // value is right-aligned
334-
335- setDragGap ( Math . max ( labelGap , valueGap ) ) ;
336-
337181 // Tight intersection check for release state
338182 // Inset by px-2 (8px) padding to check against actual text, not padded container
339183 const labelLeft = labelRect . left - trackRect . left + TEXT_RELEASE_INSET ;
@@ -362,11 +206,10 @@ function SliderRow({
362206 ? valueFullGap
363207 : 0 ;
364208 setFullGap ( releaseGap ) ;
365- } , [ value , min , max , layoutVersion ] ) ;
209+ } , [ value , min , max , layoutVersion , isDragging ] ) ;
366210
367- // While dragging: gradual separation based on distance
368- // On release: fully open if intersecting text, fully closed otherwise
369- const gap = isDragging ? dragGap : intersectsText ? fullGap : 0 ;
211+ // Keep drag path lightweight; only apply split gap after release.
212+ const gap = isDragging ? 0 : intersectsText ? fullGap : 0 ;
370213
371214 const ticks = useMemo ( ( ) => {
372215 // Generate equidistant ticks regardless of step value
@@ -501,7 +344,7 @@ function SliderRow({
501344 "isolate h-12" ,
502345 isDragging
503346 ? "[&>span]:transition-none"
504- : "[&>span]:transition-[left,transform] [&>span]:duration-120 [&>span]:ease-[cubic-bezier(0.22,1,0.36,1)]" ,
347+ : "[&>span]:transition-[left,transform] [&>span]:duration-90 [&>span]:ease-[cubic-bezier(0.22,1,0.36,1)]" ,
505348 "[&>span]:will-change-[left,transform]" ,
506349 "motion-reduce:[&>span]:transition-none" ,
507350 disabled && "pointer-events-none opacity-50" ,
@@ -532,7 +375,7 @@ function SliderRow({
532375 "absolute inset-0 will-change-[clip-path]" ,
533376 isDragging
534377 ? "transition-none"
535- : "transition-[clip-path] duration-120 ease-[cubic-bezier(0.22,1,0.36,1)]" ,
378+ : "transition-[clip-path] duration-90 ease-[cubic-bezier(0.22,1,0.36,1)]" ,
536379 "motion-reduce:transition-none" ,
537380 resolvedFillClassName ?? "bg-primary/30 dark:bg-primary/40" ,
538381 ) }
@@ -575,7 +418,7 @@ function SliderRow({
575418 "squircle pointer-events-none absolute inset-0 rounded-sm" ,
576419 isDragging
577420 ? "transition-none"
578- : "transition-[opacity,background] duration-120 ease-[cubic-bezier(0.22,1,0.36,1)]" ,
421+ : "transition-[opacity,background] duration-90 ease-[cubic-bezier(0.22,1,0.36,1)]" ,
579422 "motion-reduce:transition-none" ,
580423 ) }
581424 style = { {
0 commit comments