The Inline Details Panel
Click the ℹ️ on any camera tile and a full-width panel slides open directly below that tile — live image, management info, location, recent activity — without covering anything and without throwing the grid into chaos. Given what you know from The Grid That Doesn't Lie, you can probably guess the trick: if every tile's row is just a number we computed, inserting a panel is arithmetic.
Insertion by renumbering
Row 1: [Cam A] [Cam B] [Cam C] [Cam D]
Row 2: [========== DETAILS PANEL ==========] ← gridColumn: 1 / -1
Row 3: [Cam E] [Cam F] [Cam G] [Cam H] ← everyone here: row += 1
CameraListComponent finds the open tile's gridRow, increments the row of every tile below
it, and renders the panel div at detailPanelRow + 1 spanning 1 / -1. No DOM reordering, no
layout thrash — three assignments and the browser does the rest.
Only one panel can be open; clicking a different tile's ℹ️ closes the current one first, and clicking the same one toggles it shut.
What opening actually does
The click does more than local state — it drives the URL and the data layer:
setInlineDetailsCameraId(cameraId)— the grid learns where to insert- The URL gains
?primaryCamera=<id>— the panel is deep-linkable TileViewPagesees the param, fetches the camera's data, joins its SignalR groupcameraSelectedstore updates, and the panel content renders from it
So the panel isn't a popover with private state — it's the same "selected camera" machinery Location View uses, embedded in the grid.
Animations, including the exit
Opening animates via a data attribute (data-details-panel='true' →
detailsPanelSlideIn 0.4s, animating opacity and max-height). Closing has the classic
React problem — you can't animate a component that's already unmounted — solved the classic
way: a closingPanelId state keeps the panel mounted for 300 ms with
data-details-panel='closing' while detailsPanelSlideOut runs, then it unmounts. Note that
the row-shift logic uses inlineDetailsCameraId || closingPanelId, so the grid holds the gap
open until the exit animation finishes.
Wrapping up
The panel is three ideas stacked: row-renumbering for insertion, the shared selected-camera machinery for content, and a keep-mounted-while-closing state for the exit animation. All of it falls out of the grid computing its own positions.
Next: what tiles show when the camera has been dark for a week.