Split Tables page into focused components
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
@page "/tables"
|
||||
@rendermode InteractiveServer
|
||||
@using System
|
||||
@using System.Collections.Generic
|
||||
@using System.Diagnostics.CodeAnalysis
|
||||
@using System.Linq
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject LookupService LookupService
|
||||
@@ -13,65 +11,16 @@
|
||||
<PageTitle>Critical Tables</PageTitle>
|
||||
|
||||
<section class="panel tables-page">
|
||||
<div class="table-browser-toolbar">
|
||||
<div class="table-selector">
|
||||
<label id="critical-table-selector-label">Table</label>
|
||||
<div class="table-select-shell">
|
||||
<button
|
||||
type="button"
|
||||
class="input-shell table-select-trigger"
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded="@isTableMenuOpen"
|
||||
aria-labelledby="critical-table-selector-label critical-table-selector-value"
|
||||
@onclick="ToggleTableMenu"
|
||||
disabled="@IsTableSelectionDisabled">
|
||||
<span class="table-select-trigger-copy">
|
||||
<span id="critical-table-selector-value" class="table-select-trigger-title">@GetSelectedTableLabel()</span>
|
||||
</span>
|
||||
@if (SelectedTableReference is { } selected)
|
||||
{
|
||||
<span class="table-select-trigger-chips">
|
||||
@if (PinnedTablesState.IsPinned(selected.Key))
|
||||
{
|
||||
<StatusChip Tone="accent">Pinned</StatusChip>
|
||||
}
|
||||
<span class="chip">@($"{selected.CurationPercentage}%")</span>
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
@if (isTableMenuOpen && referenceData is not null)
|
||||
{
|
||||
<button type="button" class="table-selector-backdrop" @onclick="CloseTableMenu" aria-label="Close table selector"></button>
|
||||
|
||||
<div class="table-select-menu" role="listbox" aria-labelledby="critical-table-selector-label">
|
||||
@foreach (var table in referenceData.CriticalTables)
|
||||
{
|
||||
<button
|
||||
type="button"
|
||||
role="option"
|
||||
aria-selected="@string.Equals(table.Key, selectedTableSlug, StringComparison.OrdinalIgnoreCase)"
|
||||
class="table-select-option @GetTableOptionCssClass(table)"
|
||||
@onclick="() => SelectTableAsync(table.Key)">
|
||||
<span class="table-select-option-main">
|
||||
<strong class="table-select-option-title">@table.Label</strong>
|
||||
</span>
|
||||
<span class="table-select-option-chips">
|
||||
@if (PinnedTablesState.IsPinned(table.Key))
|
||||
{
|
||||
<StatusChip Tone="accent">Pinned</StatusChip>
|
||||
}
|
||||
<span class="chip">@($"{table.CurationPercentage}%")</span>
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="table-browser-toolbar-copy">Choose a table, then read from the roll band on the left across to the result you need.</p>
|
||||
</div>
|
||||
<TablesPageHeader
|
||||
ReferenceData="referenceData"
|
||||
SelectedTableReference="SelectedTableReference"
|
||||
SelectedTableSlug="selectedTableSlug"
|
||||
IsTableMenuOpen="isTableMenuOpen"
|
||||
IsTableSelectionDisabled="IsTableSelectionDisabled"
|
||||
IsPinned="PinnedTablesState.IsPinned"
|
||||
OnToggleTableMenu="ToggleTableMenu"
|
||||
OnCloseTableMenu="CloseTableMenu"
|
||||
OnSelectTable="SelectTableAsync" />
|
||||
|
||||
@if (referenceData is null)
|
||||
{
|
||||
@@ -99,95 +48,16 @@
|
||||
}
|
||||
else if (tableDetail is { } detail)
|
||||
{
|
||||
var readingHint = detail.Groups.Count > 0
|
||||
? "Find the roll band on the left, then read across to the group and severity you need."
|
||||
: "Find the roll band on the left, then read across to the severity you need.";
|
||||
|
||||
<div class="table-shell">
|
||||
<header class="table-browser-header">
|
||||
<div>
|
||||
<h2 class="panel-title">@detail.DisplayName</h2>
|
||||
<p class="table-browser-reading-hint">@readingHint</p>
|
||||
</div>
|
||||
<div class="action-row">
|
||||
<button type="button" class="btn btn-link" @onclick="TogglePinnedTableAsync">
|
||||
@(PinnedTablesState.IsPinned(detail.Slug) ? "Unpin table" : "Pin table")
|
||||
</button>
|
||||
<p class="table-browser-edit-hint">Use the curation action or edit action on any filled result.</p>
|
||||
</div>
|
||||
</header>
|
||||
<TablesContextBar
|
||||
Detail="detail"
|
||||
IsPinned="PinnedTablesState.IsPinned(detail.Slug)"
|
||||
OnTogglePin="TogglePinnedTableAsync" />
|
||||
|
||||
@{
|
||||
var displayColumns = GetDisplayColumns(detail);
|
||||
var gridTemplateStyle = BuildGridTemplateStyle(detail);
|
||||
}
|
||||
|
||||
<div class="table-scroll">
|
||||
<div class="critical-table-grid" role="group" aria-label="@detail.DisplayName" style="@gridTemplateStyle">
|
||||
@if (detail.Groups.Count > 0)
|
||||
{
|
||||
<div class="critical-table-grid-header-cell critical-table-grid-corner" aria-hidden="true"></div>
|
||||
@foreach (var group in detail.Groups)
|
||||
{
|
||||
<div
|
||||
class="critical-table-grid-header-cell critical-table-grid-group-header"
|
||||
style="@BuildColumnSpanStyle(detail.Columns.Count)">
|
||||
<span>@group.Label</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<div class="critical-table-grid-header-cell critical-table-grid-roll-band-header" aria-hidden="true"></div>
|
||||
@foreach (var displayColumn in displayColumns)
|
||||
{
|
||||
<div class="critical-table-grid-header-cell critical-table-grid-column-header">
|
||||
<span>@displayColumn.ColumnLabel</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@foreach (var rollBand in detail.RollBands)
|
||||
{
|
||||
<div class="critical-table-grid-header-cell critical-table-grid-roll-band">@rollBand.Label</div>
|
||||
@foreach (var displayColumn in displayColumns)
|
||||
{
|
||||
@if (TryGetCell(rollBand.Label, displayColumn.GroupKey, displayColumn.ColumnKey, out var cell))
|
||||
{
|
||||
@RenderCriticalTableCell(cell)
|
||||
}
|
||||
else
|
||||
{
|
||||
@RenderEmptyCriticalTableCell()
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@{
|
||||
var legendEntries = detail.Legend ?? Array.Empty<CriticalTableLegendEntry>();
|
||||
}
|
||||
|
||||
@if (legendEntries.Count > 0)
|
||||
{
|
||||
<div class="critical-legend">
|
||||
<div class="critical-legend-header">
|
||||
<h4>Reading help</h4>
|
||||
<p class="muted">These symbols show the effects attached to a result at a glance.</p>
|
||||
</div>
|
||||
<div class="legend-grid">
|
||||
@foreach (var entry in legendEntries)
|
||||
{
|
||||
<div class="legend-item" title="@entry.Tooltip">
|
||||
<span class="legend-symbol">@entry.Symbol</span>
|
||||
<div>
|
||||
<strong>@entry.Label</strong>
|
||||
<span class="muted">@entry.Description</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<TablesCanvas
|
||||
Detail="detail"
|
||||
OnOpenCuration="OpenCellCurationAsync"
|
||||
OnOpenEditor="OpenCellEditorAsync" />
|
||||
</div>
|
||||
}
|
||||
</section>
|
||||
@@ -237,7 +107,6 @@
|
||||
private bool isDetailLoading;
|
||||
private bool isReferenceDataLoading = true;
|
||||
private string? detailError;
|
||||
private Dictionary<(string RollBand, string? GroupKey, string ColumnKey), CriticalTableCellDetail>? cellIndex;
|
||||
private bool IsTableSelectionDisabled => isReferenceDataLoading || (referenceData?.CriticalTables.Count ?? 0) == 0;
|
||||
private bool isEditorOpen;
|
||||
private bool isEditorLoading;
|
||||
@@ -297,14 +166,12 @@
|
||||
if (string.IsNullOrWhiteSpace(selectedTableSlug))
|
||||
{
|
||||
tableDetail = null;
|
||||
cellIndex = null;
|
||||
return;
|
||||
}
|
||||
|
||||
isDetailLoading = true;
|
||||
detailError = null;
|
||||
tableDetail = null;
|
||||
cellIndex = null;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -324,7 +191,6 @@
|
||||
finally
|
||||
{
|
||||
isDetailLoading = false;
|
||||
BuildCellIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,32 +227,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildCellIndex()
|
||||
{
|
||||
if (tableDetail?.Cells is null)
|
||||
{
|
||||
cellIndex = null;
|
||||
return;
|
||||
}
|
||||
|
||||
cellIndex = new Dictionary<(string, string?, string), CriticalTableCellDetail>();
|
||||
foreach (var cell in tableDetail.Cells)
|
||||
{
|
||||
cellIndex[(cell.RollBand, cell.GroupKey, cell.ColumnKey)] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetCell(string rollBand, string? groupKey, string columnKey, [NotNullWhen(true)] out CriticalTableCellDetail? cell)
|
||||
{
|
||||
if (cellIndex is null)
|
||||
{
|
||||
cell = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return cellIndex.TryGetValue((rollBand, groupKey, columnKey), out cell);
|
||||
}
|
||||
|
||||
private async Task OpenCellEditorAsync(int resultId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(selectedTableSlug))
|
||||
@@ -744,36 +584,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetCellCssClass(CriticalTableCellDetail cell) =>
|
||||
cell.IsCurated
|
||||
? "critical-table-cell is-curated"
|
||||
: "critical-table-cell needs-curation";
|
||||
|
||||
private static IReadOnlyList<(string? GroupKey, string ColumnKey, string ColumnLabel)> GetDisplayColumns(CriticalTableDetail detail)
|
||||
{
|
||||
if (detail.Groups.Count == 0)
|
||||
{
|
||||
return detail.Columns
|
||||
.Select(column => ((string?)null, column.Key, column.Label))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return detail.Groups
|
||||
.SelectMany(group => detail.Columns.Select(column => ((string?)group.Key, column.Key, column.Label)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static string BuildGridTemplateStyle(CriticalTableDetail detail)
|
||||
{
|
||||
var dataColumnCount = detail.Columns.Count * Math.Max(detail.Groups.Count, 1);
|
||||
return $"grid-template-columns: max-content repeat({dataColumnCount}, minmax(0, 1fr));";
|
||||
}
|
||||
|
||||
private static string BuildColumnSpanStyle(int span) => $"grid-column: span {span};";
|
||||
|
||||
private string GetSelectedTableLabel() =>
|
||||
SelectedTableReference?.Label ?? "Select a table";
|
||||
|
||||
private Task TogglePinnedTableAsync()
|
||||
{
|
||||
if (SelectedTableReference is not { } selectedTable)
|
||||
@@ -820,55 +630,4 @@
|
||||
new(
|
||||
TableSlug: selectedTableSlug,
|
||||
Mode: RolemasterDb.App.Frontend.AppState.TableContextMode.Reference);
|
||||
|
||||
private string GetTableOptionCssClass(CriticalTableReference table)
|
||||
{
|
||||
var classes = new List<string>();
|
||||
|
||||
if (string.Equals(table.Key, selectedTableSlug, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
classes.Add("is-selected");
|
||||
}
|
||||
|
||||
classes.Add(table.CurationPercentage >= 100 ? "is-curated" : "needs-curation");
|
||||
return string.Join(' ', classes);
|
||||
}
|
||||
|
||||
private RenderFragment RenderCriticalTableCell(CriticalTableCellDetail cell) => @<div class="@GetCellCssClass(cell)">
|
||||
<div class="critical-table-cell-shell">
|
||||
<div class="critical-table-cell-actions">
|
||||
@if (cell.IsCurated)
|
||||
{
|
||||
<span class="critical-cell-status-chip is-curated">Curated</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button
|
||||
type="button"
|
||||
class="critical-cell-action-button is-curation"
|
||||
title="Open the curation preview for this cell."
|
||||
@onclick="() => OpenCellCurationAsync(cell.ResultId)">
|
||||
Needs Curation
|
||||
</button>
|
||||
}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="critical-cell-action-button is-edit"
|
||||
title="Open the full editor for this cell."
|
||||
@onclick="() => OpenCellEditorAsync(cell.ResultId)">
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<CompactCriticalCell
|
||||
Description="@(cell.Description ?? string.Empty)"
|
||||
Effects="@(cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
|
||||
Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
private static RenderFragment RenderEmptyCriticalTableCell() => @<div class="critical-table-cell critical-table-cell-empty">
|
||||
<span class="empty-cell">—</span>
|
||||
</div>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user