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;
|
hero visual and metadata block. The Cut/Session/Mix detail pages all compose this;
|
||||||
per-medium variance rides the Hero and MetaContent render fragments. *@
|
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
|
@* 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
|
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>
|
/// <summary>Medium-specific hero visual (cover art, hero image, or waveform background).</summary>
|
||||||
[Parameter] public RenderFragment? Hero { get; set; }
|
[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>
|
/// <summary>
|
||||||
/// Optional body region rendered below the meta block — the Cut album's multi-track listing.
|
/// Optional body region rendered below the meta block — the Cut album's multi-track listing.
|
||||||
/// Single-track media leave it null.
|
/// Single-track media leave it null.
|
||||||
|
|||||||
@@ -3,3 +3,13 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 1.5rem;
|
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"
|
BackHref="/cuts"
|
||||||
BackLabel="All cuts"
|
BackLabel="All cuts"
|
||||||
ShowShareRow="false">
|
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>
|
||||||
@* Header split: meta + Play/Share on the LEFT, bordered cover on the RIGHT (spec §3.1). *@
|
@* Header split: meta + Play/Share on the LEFT, bordered cover on the RIGHT (spec §3.1). *@
|
||||||
<div class="cut-detail-header">
|
<div class="cut-detail-header">
|
||||||
|
|||||||
@@ -55,27 +55,12 @@ else
|
|||||||
ShowHeader="false"
|
ShowHeader="false"
|
||||||
ShowMeta="false"
|
ShowMeta="false"
|
||||||
ShowShareRow="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>
|
<TopRightAction>
|
||||||
@* Lava-lamp button top-right, across from the back link. Toggles the knob band below the
|
@* Lava-lamp icon → popover panel, top-right across from the back link (Phase 12
|
||||||
row. The icon swaps to its FILLED variant while the band is shown (§7f / Part B). *@
|
§3d-revised). Replaces the former inline TopContent knob-bar: the icon IS the toggle
|
||||||
<MudTooltip Text="Visualizer settings">
|
and the popover IS the panel. Mix takes the cleanest anchor case (§8e) — the popover's
|
||||||
<MudIconButton Icon="@(_controlsExpanded ? DDIcons.LavaLampFilled : DDIcons.LavaLamp)"
|
default bottom-right anchor opens down over the full-bleed field. *@
|
||||||
Size="Size.Large"
|
<WaveformVisualizerControlPopover />
|
||||||
Color="Color.Secondary"
|
|
||||||
Disabled="@(!RendererInfo.IsInteractive)"
|
|
||||||
OnClick="@ToggleSettings"
|
|
||||||
aria-label="Visualizer settings"
|
|
||||||
aria-expanded="@_controlsExpanded" />
|
|
||||||
</MudTooltip>
|
|
||||||
</TopRightAction>
|
</TopRightAction>
|
||||||
<Hero>
|
<Hero>
|
||||||
@* Cover-as-background hero with all metadata overlaid, square `mix-hero` sizing. The
|
@* Cover-as-background hero with all metadata overlaid, square `mix-hero` sizing. The
|
||||||
@@ -128,11 +113,4 @@ else
|
|||||||
await PlayerService.SelectTrackStreaming(track);
|
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,12 +40,27 @@ else
|
|||||||
// Hero image precedence: the session's dedicated hero, then the release cover, then a placeholder.
|
// Hero image precedence: the session's dedicated hero, then the release cover, then a placeholder.
|
||||||
var heroImage = !string.IsNullOrEmpty(heroKey) ? heroKey : release.ImagePath;
|
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" />
|
||||||
|
|
||||||
|
<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">
|
<MudLink Href="/sessions" Typo="Typo.body2" Class="deepdrft-track-detail-back">
|
||||||
← All sessions
|
← All sessions
|
||||||
</MudLink>
|
</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 —
|
@* 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
|
when there is no dedicated hero, heroImage already falls back to release.ImagePath, so the
|
||||||
thumb would duplicate the background. That logic lives in ReleaseHeroOverlay. *@
|
thumb would duplicate the background. That logic lives in ReleaseHeroOverlay. *@
|
||||||
|
|||||||
@@ -6,3 +6,20 @@
|
|||||||
padding-top: 2rem;
|
padding-top: 2rem;
|
||||||
padding-bottom: 4rem;
|
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