Merge p12-w4-t1-ambient-slot into dev (12.C: ambient visualizer slot on scaffold + popover controls on all detail hosts)
This commit is contained in:
@@ -5,7 +5,17 @@
|
||||
hero visual and metadata block. The Cut/Session/Mix detail pages all compose this;
|
||||
per-medium variance rides the Hero and MetaContent render fragments. *@
|
||||
|
||||
<div class="deepdrft-track-detail-container">
|
||||
@* Ambient environment layer (Phase 12 §3c/§3f mode B): an optional full-bleed layer rendered BEHIND
|
||||
the scaffold content. The visualizer mounted here positions itself fixed/inset:0 (its own CSS), so
|
||||
this slot's only job is to render it before the content and put the content into a foreground
|
||||
stacking context above it. Absent slot = no ambient layer and no foreground promotion → today's
|
||||
plain background, byte-for-byte (Liskov). *@
|
||||
@if (Ambient is not null)
|
||||
{
|
||||
@Ambient
|
||||
}
|
||||
|
||||
<div class="deepdrft-track-detail-container @(Ambient is not null ? "deepdrft-track-detail-foreground" : null)">
|
||||
|
||||
@* Two-end top row: back link (left) | optional action (right), on one SpaceBetween row. The action
|
||||
slot stays null for media that don't supply it (Track/Cut/Session), so SpaceBetween collapses to
|
||||
|
||||
@@ -51,6 +51,16 @@ public partial class ReleaseDetailScaffold : ComponentBase
|
||||
/// <summary>Medium-specific hero visual (cover art, hero image, or waveform background).</summary>
|
||||
[Parameter] public RenderFragment? Hero { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional full-bleed ambient layer rendered BEHIND the scaffold content (Phase 12 §3c/§3f mode B).
|
||||
/// A host that wants a living environment behind hero+content — e.g. Cut supplying a
|
||||
/// <c>WaveformVisualizer</c> — places it here. The mounted layer positions itself fixed/inset:0
|
||||
/// (its own CSS), so the scaffold only promotes its content into a foreground stacking context above
|
||||
/// it. Absent = today's plain background, no regression (Liskov). Mode A (Mix) and mode C (the
|
||||
/// NowPlaying card) mount the engine without this slot — see §3f.
|
||||
/// </summary>
|
||||
[Parameter] public RenderFragment? Ambient { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional body region rendered below the meta block — the Cut album's multi-track listing.
|
||||
/// Single-track media leave it null.
|
||||
|
||||
@@ -3,3 +3,13 @@
|
||||
justify-content: center;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
/* Foreground stacking context — applied only when an Ambient layer is present. Lifts the scaffold
|
||||
content above the fixed full-bleed visualizer (z-index: 0) so hero + meta + body render over the
|
||||
living waveform field (Phase 12 §3c — promotes the former Mix-bespoke .mix-detail-foreground into
|
||||
the shared scaffold). Without an Ambient slot this class is absent and the container keeps its
|
||||
default flow, so a slot-less host renders exactly as before. */
|
||||
.deepdrft-track-detail-foreground {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,21 @@ else
|
||||
BackHref="/cuts"
|
||||
BackLabel="All cuts"
|
||||
ShowShareRow="false">
|
||||
<Ambient>
|
||||
@* Ambient living waveform behind the album hero + track list (Phase 12 §3c/§3f mode B).
|
||||
Cut is multi-track: anchor to the release's EntryKey and default to the first track by
|
||||
TrackNumber. The bridge follows the live playing track within the release automatically
|
||||
(keys on TrackId match OR shared ReleaseEntryKey), so the field re-renders to whichever
|
||||
track the listener starts; TrackEntryKey is the at-rest datum before playback. *@
|
||||
<WaveformVisualizer ReleaseEntryKey="@release.EntryKey"
|
||||
TrackId="@firstTrack?.Id"
|
||||
TrackEntryKey="@firstTrack?.EntryKey" />
|
||||
</Ambient>
|
||||
<TopRightAction>
|
||||
@* Lava-lamp icon → popover panel (full parity, §3d-revised). Sits top-right across from the
|
||||
back link, clear of the header's own Play/Share affordances below. *@
|
||||
<WaveformVisualizerControlPopover />
|
||||
</TopRightAction>
|
||||
<Header>
|
||||
@* Header split: meta + Play/Share on the LEFT, bordered cover on the RIGHT (spec §3.1). *@
|
||||
<div class="cut-detail-header">
|
||||
|
||||
@@ -55,27 +55,12 @@ else
|
||||
ShowHeader="false"
|
||||
ShowMeta="false"
|
||||
ShowShareRow="false">
|
||||
<TopContent>
|
||||
@* The eight-knob band lives in its own full-width area below the back/lamp top row.
|
||||
Phase 10 §4: the control is ALWAYS rendered; the lava-lamp toggle feeds its Visible
|
||||
parameter, and the control itself @if-gates the knobs while holding the container's
|
||||
reserved height — so content below never pops on toggle. The band mutates the shared
|
||||
WaveformVisualizerControlState; the backdrop bridge pushes the dials. A knob drag does not
|
||||
toggle it — the lamp's click does. *@
|
||||
<WaveformVisualizerControls Visible="@_controlsExpanded" />
|
||||
</TopContent>
|
||||
<TopRightAction>
|
||||
@* Lava-lamp button top-right, across from the back link. Toggles the knob band below the
|
||||
row. The icon swaps to its FILLED variant while the band is shown (§7f / Part B). *@
|
||||
<MudTooltip Text="Visualizer settings">
|
||||
<MudIconButton Icon="@(_controlsExpanded ? DDIcons.LavaLampFilled : DDIcons.LavaLamp)"
|
||||
Size="Size.Large"
|
||||
Color="Color.Secondary"
|
||||
Disabled="@(!RendererInfo.IsInteractive)"
|
||||
OnClick="@ToggleSettings"
|
||||
aria-label="Visualizer settings"
|
||||
aria-expanded="@_controlsExpanded" />
|
||||
</MudTooltip>
|
||||
@* Lava-lamp icon → popover panel, top-right across from the back link (Phase 12
|
||||
§3d-revised). Replaces the former inline TopContent knob-bar: the icon IS the toggle
|
||||
and the popover IS the panel. Mix takes the cleanest anchor case (§8e) — the popover's
|
||||
default bottom-right anchor opens down over the full-bleed field. *@
|
||||
<WaveformVisualizerControlPopover />
|
||||
</TopRightAction>
|
||||
<Hero>
|
||||
@* Cover-as-background hero with all metadata overlaid, square `mix-hero` sizing. The
|
||||
@@ -128,11 +113,4 @@ else
|
||||
await PlayerService.SelectTrackStreaming(track);
|
||||
}
|
||||
}
|
||||
|
||||
// Lava-lamp knob-band visibility. Pure presentation over WaveformVisualizerControlState — gates whether
|
||||
// the seven-knob WaveformVisualizerControls is rendered into the TopContent band; toggling it touches no
|
||||
// control value or bridge push. The lava-lamp button's filled/outline glyph is driven off this flag.
|
||||
private bool _controlsExpanded;
|
||||
|
||||
private void ToggleSettings() => _controlsExpanded = !_controlsExpanded;
|
||||
}
|
||||
|
||||
@@ -40,11 +40,26 @@ else
|
||||
// Hero image precedence: the session's dedicated hero, then the release cover, then a placeholder.
|
||||
var heroImage = !string.IsNullOrEmpty(heroKey) ? heroKey : release.ImagePath;
|
||||
|
||||
<MudContainer MaxWidth="MaxWidth.Large" Class="session-detail-page">
|
||||
@* Ambient living waveform behind the hero overlay (Phase 12 §3e option b / §3f mode B). Session does
|
||||
NOT compose ReleaseDetailScaffold, so it mounts the shared engine directly with its own thin
|
||||
full-bleed wrapper — the engine is single-source either way, only the mount differs (§3b). The
|
||||
visualizer positions itself fixed/inset:0; the session-detail-foreground class lifts the content
|
||||
above it. The bridge follows the live playing track; TrackEntryKey is the at-rest datum. *@
|
||||
<WaveformVisualizer ReleaseEntryKey="@release.EntryKey"
|
||||
TrackId="@ViewModel.Track?.Id"
|
||||
TrackEntryKey="@ViewModel.Track?.EntryKey" />
|
||||
|
||||
<MudLink Href="/sessions" Typo="Typo.body2" Class="deepdrft-track-detail-back">
|
||||
← All sessions
|
||||
</MudLink>
|
||||
<MudContainer MaxWidth="MaxWidth.Large" Class="session-detail-page session-detail-foreground">
|
||||
|
||||
<div class="session-detail-top-row">
|
||||
<MudLink Href="/sessions" Typo="Typo.body2" Class="deepdrft-track-detail-back">
|
||||
← All sessions
|
||||
</MudLink>
|
||||
|
||||
@* Lava-lamp icon → popover panel (full parity, §3e/§3d-revised). Anchored top-right, clear of
|
||||
the hero overlay and the share/play affordances overlaid on the hero below. *@
|
||||
<WaveformVisualizerControlPopover />
|
||||
</div>
|
||||
|
||||
@* The overlay shows the cover thumbnail only when it differs from the resolved hero image —
|
||||
when there is no dedicated hero, heroImage already falls back to release.ImagePath, so the
|
||||
|
||||
@@ -6,3 +6,20 @@
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
|
||||
/* Lifts the session content above the fixed full-bleed waveform layer (z-index: 0). Session mounts the
|
||||
visualizer directly (it does not compose ReleaseDetailScaffold), so the foreground stacking context
|
||||
lives here rather than on the scaffold (Phase 12 §3e option b). The class lands on the MudContainer's
|
||||
rendered root, so ::deep is required to reach it. */
|
||||
::deep .session-detail-foreground {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Back link (left) | lava-lamp popover trigger (right) on one row, mirroring the scaffold's top row.
|
||||
The popover icon clears the hero overlay below and the share/play affordances overlaid on it. */
|
||||
.session-detail-top-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user