refactor: use stacked layout for waveform, automation, and effects bars
- Replace absolute positioning with flex column layout - Waveform, automation bar, and effects bar now stacked vertically - Removes gaps between bars naturally with stacked layout - Both bars remain collapsible with no position dependencies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -206,8 +206,8 @@ export function TrackList({
|
|||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{tracks.map((track) => (
|
{tracks.map((track) => (
|
||||||
<React.Fragment key={track.id}>
|
<React.Fragment key={track.id}>
|
||||||
{/* Track Waveform Row with Overlays */}
|
{/* Track Waveform Row with bars stacked below */}
|
||||||
<div className="relative">
|
<div className="flex flex-col">
|
||||||
<Track
|
<Track
|
||||||
track={track}
|
track={track}
|
||||||
zoom={zoom}
|
zoom={zoom}
|
||||||
@@ -293,90 +293,9 @@ export function TrackList({
|
|||||||
renderWaveformOnly={true}
|
renderWaveformOnly={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Effects Bar - Always visible at bottom */}
|
{/* Automation Bar - Collapsible */}
|
||||||
{!track.collapsed && (
|
{!track.collapsed && (
|
||||||
<div className="absolute bottom-0 left-0 right-0 z-10 pointer-events-auto bg-card/90 backdrop-blur-sm border-b border-border">
|
<div className="bg-card/90 backdrop-blur-sm border-b border-border">
|
||||||
{/* Effects Header - Collapsible */}
|
|
||||||
<div
|
|
||||||
className="flex items-center gap-2 px-3 py-1.5 cursor-pointer hover:bg-accent/30 transition-colors"
|
|
||||||
onClick={() => {
|
|
||||||
onUpdateTrack(track.id, { effectsExpanded: !track.effectsExpanded });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{track.effectsExpanded ? (
|
|
||||||
<ChevronDown className="h-3 w-3 text-muted-foreground" />
|
|
||||||
) : (
|
|
||||||
<ChevronRight className="h-3 w-3 text-muted-foreground" />
|
|
||||||
)}
|
|
||||||
<span className="text-xs font-medium">Effects</span>
|
|
||||||
<span className="text-xs text-muted-foreground">
|
|
||||||
({track.effectChain.effects.length})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Effects Content - Collapsible, no inner container */}
|
|
||||||
{track.effectsExpanded && (
|
|
||||||
<div className="h-48 overflow-x-auto custom-scrollbar bg-muted/70 border-t border-border">
|
|
||||||
<div className="flex h-full gap-3 p-3">
|
|
||||||
{track.effectChain.effects.length === 0 ? (
|
|
||||||
<div className="text-xs text-muted-foreground text-center py-8 w-full">
|
|
||||||
No effects. Click + to add an effect.
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
track.effectChain.effects.map((effect) => (
|
|
||||||
<EffectDevice
|
|
||||||
key={effect.id}
|
|
||||||
effect={effect}
|
|
||||||
onToggleEnabled={() => {
|
|
||||||
const updatedChain = {
|
|
||||||
...track.effectChain,
|
|
||||||
effects: track.effectChain.effects.map((e) =>
|
|
||||||
e.id === effect.id ? { ...e, enabled: !e.enabled } : e
|
|
||||||
),
|
|
||||||
};
|
|
||||||
onUpdateTrack(track.id, { effectChain: updatedChain });
|
|
||||||
}}
|
|
||||||
onRemove={() => {
|
|
||||||
const updatedChain = {
|
|
||||||
...track.effectChain,
|
|
||||||
effects: track.effectChain.effects.filter((e) => e.id !== effect.id),
|
|
||||||
};
|
|
||||||
onUpdateTrack(track.id, { effectChain: updatedChain });
|
|
||||||
}}
|
|
||||||
onUpdateParameters={(params) => {
|
|
||||||
const updatedChain = {
|
|
||||||
...track.effectChain,
|
|
||||||
effects: track.effectChain.effects.map((e) =>
|
|
||||||
e.id === effect.id ? { ...e, parameters: params } : e
|
|
||||||
),
|
|
||||||
};
|
|
||||||
onUpdateTrack(track.id, { effectChain: updatedChain });
|
|
||||||
}}
|
|
||||||
onToggleExpanded={() => {
|
|
||||||
const updatedEffects = track.effectChain.effects.map((e) =>
|
|
||||||
e.id === effect.id ? { ...e, expanded: !e.expanded } : e
|
|
||||||
);
|
|
||||||
onUpdateTrack(track.id, {
|
|
||||||
effectChain: { ...track.effectChain, effects: updatedEffects },
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Automation Bar - Collapsible, above effects bar at bottom */}
|
|
||||||
{!track.collapsed && (
|
|
||||||
<div
|
|
||||||
className="absolute left-0 right-0 z-10 pointer-events-auto bg-card/90 backdrop-blur-sm"
|
|
||||||
style={{
|
|
||||||
bottom: track.effectsExpanded ? '232px' : '32px' // 32px effects header, or 232px if expanded
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Automation Header - Clickable to toggle */}
|
{/* Automation Header - Clickable to toggle */}
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-2 px-3 py-1.5 cursor-pointer hover:bg-accent/30 transition-colors"
|
className="flex items-center gap-2 px-3 py-1.5 cursor-pointer hover:bg-accent/30 transition-colors"
|
||||||
@@ -465,6 +384,82 @@ export function TrackList({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Effects Bar - Collapsible */}
|
||||||
|
{!track.collapsed && (
|
||||||
|
<div className="bg-card/90 backdrop-blur-sm border-b border-border">
|
||||||
|
{/* Effects Header - Collapsible */}
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-2 px-3 py-1.5 cursor-pointer hover:bg-accent/30 transition-colors"
|
||||||
|
onClick={() => {
|
||||||
|
onUpdateTrack(track.id, { effectsExpanded: !track.effectsExpanded });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{track.effectsExpanded ? (
|
||||||
|
<ChevronDown className="h-3 w-3 text-muted-foreground" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-3 w-3 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
<span className="text-xs font-medium">Effects</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
({track.effectChain.effects.length})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Effects Content - Collapsible, no inner container */}
|
||||||
|
{track.effectsExpanded && (
|
||||||
|
<div className="h-48 overflow-x-auto custom-scrollbar bg-muted/70 border-t border-border">
|
||||||
|
<div className="flex h-full gap-3 p-3">
|
||||||
|
{track.effectChain.effects.length === 0 ? (
|
||||||
|
<div className="text-xs text-muted-foreground text-center py-8 w-full">
|
||||||
|
No effects. Click + to add an effect.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
track.effectChain.effects.map((effect) => (
|
||||||
|
<EffectDevice
|
||||||
|
key={effect.id}
|
||||||
|
effect={effect}
|
||||||
|
onToggleEnabled={() => {
|
||||||
|
const updatedChain = {
|
||||||
|
...track.effectChain,
|
||||||
|
effects: track.effectChain.effects.map((e) =>
|
||||||
|
e.id === effect.id ? { ...e, enabled: !e.enabled } : e
|
||||||
|
),
|
||||||
|
};
|
||||||
|
onUpdateTrack(track.id, { effectChain: updatedChain });
|
||||||
|
}}
|
||||||
|
onRemove={() => {
|
||||||
|
const updatedChain = {
|
||||||
|
...track.effectChain,
|
||||||
|
effects: track.effectChain.effects.filter((e) => e.id !== effect.id),
|
||||||
|
};
|
||||||
|
onUpdateTrack(track.id, { effectChain: updatedChain });
|
||||||
|
}}
|
||||||
|
onUpdateParameters={(params) => {
|
||||||
|
const updatedChain = {
|
||||||
|
...track.effectChain,
|
||||||
|
effects: track.effectChain.effects.map((e) =>
|
||||||
|
e.id === effect.id ? { ...e, parameters: params } : e
|
||||||
|
),
|
||||||
|
};
|
||||||
|
onUpdateTrack(track.id, { effectChain: updatedChain });
|
||||||
|
}}
|
||||||
|
onToggleExpanded={() => {
|
||||||
|
const updatedEffects = track.effectChain.effects.map((e) =>
|
||||||
|
e.id === effect.id ? { ...e, expanded: !e.expanded } : e
|
||||||
|
);
|
||||||
|
onUpdateTrack(track.id, {
|
||||||
|
effectChain: { ...track.effectChain, effects: updatedEffects },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user