fix: constrain fader handles to track lane boundaries

- Fader handles now respect top-8 bottom-8 track padding
- Handle moves only within the visible track lane (60% range)
- Updated both TrackFader and MasterFader components
- Value calculation clamped to track bounds (32px padding top/bottom)
- Handle position mapped to 20%-80% range instead of 0%-100%
- Prevents handle from going beyond visible track area

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-19 18:15:27 +01:00
parent 197bff39fc
commit b1c0ff6f72
2 changed files with 30 additions and 8 deletions

View File

@@ -91,9 +91,18 @@ export function MasterFader({
const rect = containerRef.current.getBoundingClientRect();
const y = clientY - rect.top;
// Track has 32px (2rem) padding on top and bottom (top-8 bottom-8)
const trackPadding = 32;
const trackHeight = rect.height - (trackPadding * 2);
// Clamp y to track bounds
const clampedY = Math.max(trackPadding, Math.min(rect.height - trackPadding, y));
// Inverted: top = max (1), bottom = min (0)
const percentage = Math.max(0, Math.min(1, 1 - (y / rect.height)));
onChange(percentage);
// Map clampedY from [trackPadding, height-trackPadding] to [1, 0]
const percentage = 1 - ((clampedY - trackPadding) / trackHeight);
onChange(Math.max(0, Math.min(1, percentage)));
};
React.useEffect(() => {
@@ -165,8 +174,10 @@ export function MasterFader({
<div
className="absolute left-1/2 -translate-x-1/2 w-10 h-4 bg-primary/80 border-2 border-primary rounded-md shadow-lg cursor-grab active:cursor-grabbing pointer-events-none transition-all"
style={{
// Inverted: value 1 = top, value 0 = bottom
top: `calc(${(1 - value) * 100}% - 0.5rem)`,
// Inverted: value 1 = top of track (20%), value 0 = bottom of track (80%)
// Track has top-8 bottom-8 padding (20% and 80% of h-40 container)
// Handle moves within 60% range (from 20% to 80%)
top: `calc(${20 + (1 - value) * 60}% - 0.5rem)`,
}}
>
{/* Handle grip lines */}

View File

@@ -87,9 +87,18 @@ export function TrackFader({
const rect = containerRef.current.getBoundingClientRect();
const y = clientY - rect.top;
// Track has 32px (2rem) padding on top and bottom (top-8 bottom-8)
const trackPadding = 32;
const trackHeight = rect.height - (trackPadding * 2);
// Clamp y to track bounds
const clampedY = Math.max(trackPadding, Math.min(rect.height - trackPadding, y));
// Inverted: top = max (1), bottom = min (0)
const percentage = Math.max(0, Math.min(1, 1 - (y / rect.height)));
onChange(percentage);
// Map clampedY from [trackPadding, height-trackPadding] to [1, 0]
const percentage = 1 - ((clampedY - trackPadding) / trackHeight);
onChange(Math.max(0, Math.min(1, percentage)));
};
React.useEffect(() => {
@@ -161,8 +170,10 @@ export function TrackFader({
<div
className="absolute left-1/2 -translate-x-1/2 w-10 h-4 bg-primary/80 border-2 border-primary rounded-md shadow-lg cursor-grab active:cursor-grabbing pointer-events-none transition-all"
style={{
// Inverted: value 1 = top, value 0 = bottom
top: `calc(${(1 - value) * 100}% - 0.5rem)`,
// Inverted: value 1 = top of track (20%), value 0 = bottom of track (80%)
// Track has top-8 bottom-8 padding (20% and 80% of h-40 container)
// Handle moves within 60% range (from 20% to 80%)
top: `calc(${20 + (1 - value) * 60}% - 0.5rem)`,
}}
>
{/* Handle grip lines */}