From 841822d8fe17c9b27af7ff2c5b6a96b4c7149489 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Tue, 16 Jun 2026 20:12:02 -0400 Subject: [PATCH] =?UTF-8?q?fix(mix-visualizer):=20move=20seven-knob=20cont?= =?UTF-8?q?rols=20in-flow=20between=20back=20link=20and=20lava=20lamp=20(P?= =?UTF-8?q?hase=2010=20reframe=20=C2=A77b)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controls/MixVisualizerControls.razor.css | 39 ++++++++++++------- .../Controls/ReleaseDetailScaffold.razor | 24 ++++++++---- .../Controls/ReleaseDetailScaffold.razor.cs | 9 +++++ .../Controls/ReleaseDetailScaffold.razor.css | 9 +++++ DeepDrftPublic.Client/Pages/MixDetail.razor | 39 ++++++++++--------- .../Pages/MixDetail.razor.css | 22 ----------- 6 files changed, 80 insertions(+), 62 deletions(-) diff --git a/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor.css b/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor.css index c3bc9ff..ed994f8 100644 --- a/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor.css +++ b/DeepDrftPublic.Client/Controls/MixVisualizerControls.razor.css @@ -1,21 +1,21 @@ -/* The seven-knob bar lives INLINE in the mix-detail controls area and animates open/closed in place - (lava reframe §7b) — NOT a popover or drawer. Collapsed, it has zero size and is fully transparent; - the @Expanded flag (mirrored to the .is-expanded class) transitions it open. We animate max-width + - max-height + opacity + transform together so the bar reads as the controls growing in place rather - than a panel popping in. Closed state is pointer-events:none + visibility:hidden so collapsed knobs - are not focusable or hit-testable. */ +/* The seven-knob container lives IN-FLOW in the scaffold's top-row center zone, between the back link + and the lava-lamp toggle, and grows open/closed in place (lava reframe §7b) — NOT a popover, drawer, + or floating overlay. Collapsed, it has zero footprint and is fully transparent; the @Expanded flag + (mirrored to the .is-expanded class) transitions it open by growing its width. The PRIMARY animated + axis is horizontal width — the controls area opening between the back link and the lamp; max-height is + a secondary axis for the wrap case. Closed state is pointer-events:none + visibility:hidden so + collapsed knobs are not focusable or hit-testable. */ .mix-visualizer-controls-bar { display: flex; flex-wrap: wrap; align-items: flex-start; - justify-content: flex-end; + justify-content: flex-start; gap: 0.85rem 1rem; - /* Collapsed: zero footprint, slid up toward the toggle, transparent. */ + /* Collapsed: zero horizontal footprint, transparent, no layout space. */ max-width: 0; max-height: 0; opacity: 0; - transform: translateY(-8px); overflow: hidden; visibility: hidden; pointer-events: none; @@ -33,20 +33,33 @@ max-width 0.32s cubic-bezier(0.22, 0.61, 0.36, 1), max-height 0.32s cubic-bezier(0.22, 0.61, 0.36, 1), opacity 0.24s ease, - padding 0.32s cubic-bezier(0.22, 0.61, 0.36, 1), - transform 0.32s cubic-bezier(0.22, 0.61, 0.36, 1); + padding 0.32s cubic-bezier(0.22, 0.61, 0.36, 1); } +/* Expanded: grows horizontally in the row's flow. Wide enough to hold all seven 64px knobs (with + captions and gaps) on one line where the row has room; on a narrower center zone the knobs flex-wrap + to a second in-flow line and max-height absorbs the taller stack — never clipped, never floating. */ .mix-visualizer-controls-bar.is-expanded { - max-width: 640px; + max-width: 720px; max-height: 420px; opacity: 1; - transform: translateY(0); visibility: visible; pointer-events: auto; padding: 0.85rem 1rem; } +/* Narrow rows: the controls container can't sit beside the back link and lamp on one line, so it takes + the full row width and the scaffold's flex-wrapped top row drops it to its own in-flow line below the + back/lamp pair (§7b-responsive). Still fully in-flow — never floats, never clips. The seven knobs get + the row's full width and wrap within it. */ +@media (max-width: 959.98px) { + .mix-visualizer-controls-bar.is-expanded { + flex-basis: 100%; + max-width: 100%; + justify-content: center; + } +} + /* One control: a RadialKnob with its Material icon caption underneath. RadialKnob has no icon slot, so the icon rides adjacent (§7d). Center the pair so the seven read as a tidy bar. */ .mix-visualizer-control { diff --git a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor index 36bb692..49de5d1 100644 --- a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor +++ b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor @@ -7,15 +7,23 @@
- @* Back link top-left, optional medium action top-right, on one SpaceBetween row. The action slot - stays null for media that don't supply it (Track/Session), so they render the back link alone. *@ - - - ← @BackLabel - + @* Three-zone top row: back link (left) | optional center affordance | optional action (right), on + one SpaceBetween row. Both the center and action slots stay null for media that don't supply them + (Track/Cut/Session), so SpaceBetween collapses to the back link alone at the left edge. The Mix + detail page fills the center with its in-flow visualizer-controls container (§7b). The native + wrapper lets the row flex-wrap the center zone to its own line on narrow widths, keeping the + controls in-flow rather than clipping. *@ +
+ + + ← @BackLabel + - @TopRightAction - + @TopRowCenter + + @TopRightAction + +
@TopContent diff --git a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.cs b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.cs index dce760f..6cc9447 100644 --- a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.cs +++ b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.cs @@ -31,6 +31,15 @@ public partial class ReleaseDetailScaffold : ComponentBase /// [Parameter] public RenderFragment? TopContent { get; set; } + /// + /// Optional affordance rendered in the center of the top row, between the back link (left) and + /// (right). The Mix detail page uses it for the in-flow visualizer- + /// controls container that grows in place between the back link and the lava-lamp toggle (§7b); + /// other media leave it null, so the SpaceBetween row collapses the center and renders the back + /// link alone. Variance rides the slot, never a flag (Phase 9 §5.3). + /// + [Parameter] public RenderFragment? TopRowCenter { get; set; } + /// /// Optional action rendered at the top-right of the container, on the same SpaceBetween row as the /// back link (back link left, action right). The Mix detail page uses it for the lava-lamp diff --git a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.css b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.css index 2c5ff4f..41e3445 100644 --- a/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.css +++ b/DeepDrftPublic.Client/Controls/ReleaseDetailScaffold.razor.css @@ -3,3 +3,12 @@ justify-content: center; margin-top: 1.5rem; } + +/* The three-zone top row: back link | center affordance | action. The center zone (the Mix + visualizer-controls container) grows in-flow between the two pinned ends. On narrow widths the row + flex-wraps so the center zone drops to its own line below the back/action pair — keeping the seven + knobs in-flow with the full row width rather than clipping. The MudStack output is a child Razor + component's native div, so ::deep is required to reach it. */ +::deep .deepdrft-track-detail-top-row-stack { + flex-wrap: wrap; +} diff --git a/DeepDrftPublic.Client/Pages/MixDetail.razor b/DeepDrftPublic.Client/Pages/MixDetail.razor index 6aaaf27..f60a8df 100644 --- a/DeepDrftPublic.Client/Pages/MixDetail.razor +++ b/DeepDrftPublic.Client/Pages/MixDetail.razor @@ -48,26 +48,27 @@ else BackLabel="All mixes" ShowMeta="@(hasGenre || hasDate)" ShowShareRow="false"> + + @* In-flow seven-knob control container, between the back link and the lava-lamp toggle. + It grows in place (width/opacity transition) when expanded and collapses to zero + footprint when closed — never a popover, drawer, or floating overlay (§7b). The + container mutates the shared MixVisualizerControlState; the backdrop bridge pushes the + dials. A knob drag does not collapse it — the toggle flips only on the lamp's click. *@ + + - @* Lava-lamp button top-right, across from the back link. Toggles the INLINE seven-knob - control bar that animates open/closed in place below it (lava reframe §7b) — not a - popover or drawer. The icon swaps to its FILLED variant while the bar is expanded - (§7f / Part B). The controls bar mutates the shared MixVisualizerControlState; the - backdrop bridge pushes the dials. A knob drag does not collapse the bar — the toggle - only flips on this button's click, never on a drag landing in the bar. *@ -
- - - - - -
+ @* Lava-lamp button top-right, across from the back link. Toggles the in-flow control + container in the center zone. The icon swaps to its FILLED variant while the + container is expanded (§7f / Part B). *@ + + +
diff --git a/DeepDrftPublic.Client/Pages/MixDetail.razor.css b/DeepDrftPublic.Client/Pages/MixDetail.razor.css index 8af5797..0f0b971 100644 --- a/DeepDrftPublic.Client/Pages/MixDetail.razor.css +++ b/DeepDrftPublic.Client/Pages/MixDetail.razor.css @@ -4,28 +4,6 @@ z-index: 1; } -/* The lava-lamp toggle + its inline knob-bar. The anchor stacks the button over the bar and lets the - bar grow downward/leftward in place when expanded, without shoving the masthead. The bar itself is - absolutely positioned under the button (top-right of the detail body), so its open/close animation - reads as the controls growing out from the icon rather than reflowing the page (lava reframe §7b). */ -.mix-visualizer-controls-anchor { - position: relative; - display: flex; - flex-direction: column; - align-items: flex-end; -} - -/* MixVisualizerControls renders the .mix-visualizer-controls-bar as its single root. It is a child - Razor component, so its scope attribute is not stamped here — reach the bar with ::deep to position - it as a floating-but-inline element anchored to the toggle's bottom-right. */ -.mix-visualizer-controls-anchor ::deep .mix-visualizer-controls-bar { - position: absolute; - top: calc(100% + 0.5rem); - right: 0; - z-index: 3; - transform-origin: top right; -} - /* Medium square cover — deliberately smaller than the 360px cut cover so the waveform backdrop keeps room. The placeholder/art MudPaper fills this frame. */ .mix-detail-cover {