Add mobile tables inspector sheet

This commit is contained in:
2026-03-21 15:13:36 +01:00
parent 9841a5c097
commit 0daef1f769
6 changed files with 142 additions and 48 deletions

View File

@@ -68,6 +68,7 @@ It is intentionally implementation-focused:
| 2026-03-21 | P3.5 | Completed | Reworked the canvas with sticky headers, a sticky roll-band column, row and column emphasis driven by selection and roll-jump state, selected-cell treatment, and a comfortable/dense density toggle. | | 2026-03-21 | P3.5 | Completed | Reworked the canvas with sticky headers, a sticky roll-band column, row and column emphasis driven by selection and roll-jump state, selected-cell treatment, and a comfortable/dense density toggle. |
| 2026-03-21 | P3.6 | Completed | Removed the always-visible cell button stack from resting cells, leaving status-only hints by default and limiting compact edit/curation buttons to the currently selected cell. | | 2026-03-21 | P3.6 | Completed | Removed the always-visible cell button stack from resting cells, leaving status-only hints by default and limiting compact edit/curation buttons to the currently selected cell. |
| 2026-03-21 | P3.7 | Completed | Added a dedicated desktop inspector column that reads from the shared selected-cell state and keeps the selected result readable beside the grid. | | 2026-03-21 | P3.7 | Completed | Added a dedicated desktop inspector column that reads from the shared selected-cell state and keeps the selected result readable beside the grid. |
| 2026-03-21 | P3.8 | Completed | Reused the inspector body inside a mobile bottom sheet with its own backdrop and close affordance so touch users keep the same selection-driven inspector model as desktop. |
### Lessons Learned ### Lessons Learned
@@ -105,6 +106,7 @@ It is intentionally implementation-focused:
- Canvas emphasis becomes maintainable once selection, roll-jump, and density are all fed through one explicit state model. That lets the grid respond to context without hiding selection logic inside CSS-only heuristics. - Canvas emphasis becomes maintainable once selection, roll-jump, and density are all fed through one explicit state model. That lets the grid respond to context without hiding selection logic inside CSS-only heuristics.
- Resting-cell quietness should be enforced structurally, not only visually. Showing actions only for the selected cell prevents future CSS regressions from reintroducing button clutter across the whole grid. - Resting-cell quietness should be enforced structurally, not only visually. Showing actions only for the selected cell prevents future CSS regressions from reintroducing button clutter across the whole grid.
- The inspector should be its own sibling surface in the page layout, not nested inside the table shell. That keeps the content reusable for both desktop and the later mobile sheet without coupling it to canvas markup. - The inspector should be its own sibling surface in the page layout, not nested inside the table shell. That keeps the content reusable for both desktop and the later mobile sheet without coupling it to canvas markup.
- The inspector content itself should be shared independently of its container. Once the body is separated from the desktop column chrome, the mobile bottom sheet can reuse it with almost no behavioral drift.
## Target Outcomes ## Target Outcomes
@@ -472,7 +474,7 @@ Build the shared interaction infrastructure needed by multiple destinations befo
| `P3.5` | Completed | The canvas now supports sticky headers and roll bands, row and column emphasis from selection and roll-jump state, selected-cell treatment, and a comfortable/dense density toggle. | | `P3.5` | Completed | The canvas now supports sticky headers and roll bands, row and column emphasis from selection and roll-jump state, selected-cell treatment, and a comfortable/dense density toggle. |
| `P3.6` | Completed | Resting cells now show only status hints; compact edit/curation buttons appear only for the selected cell. | | `P3.6` | Completed | Resting cells now show only status hints; compact edit/curation buttons appear only for the selected cell. |
| `P3.7` | Completed | Desktop now has a dedicated inspector column driven by the shared selected-cell state instead of forcing result reading back into the grid alone. | | `P3.7` | Completed | Desktop now has a dedicated inspector column driven by the shared selected-cell state instead of forcing result reading back into the grid alone. |
| `P3.8` | Pending | Add the mobile bottom-sheet inspector variant. | | `P3.8` | Completed | Mobile now uses a bottom-sheet inspector that reuses the same selected-cell content as the desktop inspector column. |
| `P3.9` | Pending | Move legend/help to an on-demand secondary surface. | | `P3.9` | Pending | Move legend/help to an on-demand secondary surface. |
| `P3.10` | Pending | Hide maintenance and developer noise in default reference mode. | | `P3.10` | Pending | Hide maintenance and developer noise in default reference mode. |
| `P3.11` | Pending | Preserve editor and curation entry points through the inspector. | | `P3.11` | Pending | Preserve editor and curation entry points through the inspector. |

View File

@@ -91,6 +91,8 @@
</aside> </aside>
} }
</div> </div>
<TablesInspectorSheet SelectedCellDetail="SelectedCellDetail" OnClose="ClearSelectedCell" />
} }
</section> </section>
@@ -700,6 +702,12 @@
selectedCell = selection; selectedCell = selection;
} }
private Task ClearSelectedCell()
{
selectedCell = null;
return Task.CompletedTask;
}
private void NormalizeViewStateForCurrentDetail() private void NormalizeViewStateForCurrentDetail()
{ {
referenceMode = NormalizeMode(referenceMode); referenceMode = NormalizeMode(referenceMode);

View File

@@ -1,50 +1,6 @@
@if (SelectedCellDetail is null)
{
<aside class="tables-inspector" aria-label="Selected result inspector"> <aside class="tables-inspector" aria-label="Selected result inspector">
<InspectorSection Title="Inspector" Description="Select a result in the table to inspect its details here."> <TablesInspectorContent SelectedCellDetail="SelectedCellDetail" />
<p class="tables-inspector-empty">Choose a cell to see its roll band, severity, and readable result without leaving the grid.</p>
</InspectorSection>
</aside> </aside>
}
else
{
var cell = SelectedCellDetail;
<aside class="tables-inspector" aria-label="Selected result inspector">
<InspectorSection Title="Selected Result" Description="Read the selected cell and its context without opening a modal.">
<div class="tables-inspector-summary">
<div>
<p class="tables-inspector-kicker">Roll band</p>
<strong>@cell.RollBand</strong>
</div>
<div>
<p class="tables-inspector-kicker">Severity</p>
<strong>@cell.ColumnLabel</strong>
</div>
@if (!string.IsNullOrWhiteSpace(cell.GroupLabel))
{
<div>
<p class="tables-inspector-kicker">Variant</p>
<strong>@cell.GroupLabel</strong>
</div>
}
<div>
<p class="tables-inspector-kicker">Status</p>
<StatusChip Tone="@(cell.IsCurated ? "success" : "warning")">
@(cell.IsCurated ? "Curated" : "Needs Curation")
</StatusChip>
</div>
</div>
</InspectorSection>
<InspectorSection Title="Result" Description="The selected critical result stays readable while you browse the grid.">
<CompactCriticalCell
Description="@(cell.Description ?? string.Empty)"
Effects="@(cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
</InspectorSection>
</aside>
}
@code { @code {
[Parameter] [Parameter]

View File

@@ -0,0 +1,48 @@
@if (SelectedCellDetail is null)
{
<InspectorSection Title="Inspector" Description="Select a result in the table to inspect its details here.">
<p class="tables-inspector-empty">Choose a cell to see its roll band, severity, and readable result without leaving the grid.</p>
</InspectorSection>
}
else
{
var cell = SelectedCellDetail;
<InspectorSection Title="Selected Result" Description="Read the selected cell and its context without opening a modal.">
<div class="tables-inspector-summary">
<div>
<p class="tables-inspector-kicker">Roll band</p>
<strong>@cell.RollBand</strong>
</div>
<div>
<p class="tables-inspector-kicker">Severity</p>
<strong>@cell.ColumnLabel</strong>
</div>
@if (!string.IsNullOrWhiteSpace(cell.GroupLabel))
{
<div>
<p class="tables-inspector-kicker">Variant</p>
<strong>@cell.GroupLabel</strong>
</div>
}
<div>
<p class="tables-inspector-kicker">Status</p>
<StatusChip Tone="@(cell.IsCurated ? "success" : "warning")">
@(cell.IsCurated ? "Curated" : "Needs Curation")
</StatusChip>
</div>
</div>
</InspectorSection>
<InspectorSection Title="Result" Description="The selected critical result stays readable while you browse the grid.">
<CompactCriticalCell
Description="@(cell.Description ?? string.Empty)"
Effects="@(cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
</InspectorSection>
}
@code {
[Parameter]
public CriticalTableCellDetail? SelectedCellDetail { get; set; }
}

View File

@@ -0,0 +1,29 @@
@if (SelectedCellDetail is not null)
{
<div class="tables-inspector-sheet" role="dialog" aria-modal="true" aria-label="Selected result inspector">
<button type="button" class="tables-inspector-sheet-backdrop" @onclick="OnClose"></button>
<section class="tables-inspector-sheet-panel">
<div class="tables-inspector-sheet-handle" aria-hidden="true"></div>
<header class="tables-inspector-sheet-header">
<div>
<p class="tables-page-eyebrow">Selected Result</p>
<h2 class="panel-title">Mobile Inspector</h2>
</div>
<button type="button" class="btn btn-link" @onclick="OnClose">Close</button>
</header>
<div class="tables-inspector-sheet-body">
<TablesInspectorContent SelectedCellDetail="SelectedCellDetail" />
</div>
</section>
</div>
}
@code {
[Parameter]
public CriticalTableCellDetail? SelectedCellDetail { get; set; }
[Parameter]
public EventCallback OnClose { get; set; }
}

View File

@@ -1351,6 +1351,10 @@ pre,
gap: 0.85rem; gap: 0.85rem;
} }
.tables-inspector-sheet {
display: none;
}
.tables-inspector-empty { .tables-inspector-empty {
margin: 0; margin: 0;
color: var(--ink-soft); color: var(--ink-soft);
@@ -1483,6 +1487,53 @@ pre,
.tables-reference-inspector-shell { .tables-reference-inspector-shell {
display: none; display: none;
} }
.tables-inspector-sheet {
position: fixed;
inset: 0;
display: grid;
align-items: end;
z-index: 60;
}
.tables-inspector-sheet-backdrop {
position: absolute;
inset: 0;
border: none;
background: rgba(17, 18, 19, 0.38);
padding: 0;
}
.tables-inspector-sheet-panel {
position: relative;
display: grid;
gap: 0.85rem;
max-height: min(78vh, 42rem);
padding: 0.85rem 1rem 1rem;
border-radius: 24px 24px 0 0;
background: var(--surface-card-strong);
border: 1px solid rgba(127, 96, 55, 0.18);
box-shadow: 0 -12px 32px rgba(18, 14, 9, 0.18);
}
.tables-inspector-sheet-handle {
width: 3.2rem;
height: 0.32rem;
margin: 0 auto;
border-radius: 999px;
background: rgba(127, 96, 55, 0.22);
}
.tables-inspector-sheet-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 0.85rem;
}
.tables-inspector-sheet-body {
overflow: auto;
}
} }
.table-shell .table-scroll { .table-shell .table-scroll {