Tighten tables layout and grid behavior

This commit is contained in:
2026-04-11 23:45:09 +02:00
parent c892a6d07a
commit 6719967907
9 changed files with 221 additions and 419 deletions

View File

@@ -11,7 +11,7 @@
<PageTitle>Critical Tables</PageTitle>
<section class="panel tables-page">
<TablesPageHeader />
<TablesPageHeader/>
@if (referenceData is null)
{
@@ -31,7 +31,7 @@
PinnedTableSlugs="PinnedTablesState.Items.Select(item => item.Slug).ToArray()"
RecentTableSlugs="RecentTablesState.Items.Select(item => item.Slug).ToArray()"
IsPinned="PinnedTablesState.IsPinned"
OnSelectTable="SelectTableAsync" />
OnSelectTable="SelectTableAsync"/>
</aside>
<div class="tables-reference-main">
@@ -57,19 +57,9 @@
<TablesContextBar
Detail="detail"
IsPinned="PinnedTablesState.IsPinned(detail.Slug)"
CurrentMode="referenceMode"
SelectedGroupKey="selectedGroupKey"
SelectedColumnKey="selectedColumnKey"
RollJumpValue="rollJumpValue"
DensityMode="densityMode"
OnTogglePin="TogglePinnedTableAsync"
IsLegendOpen="isLegendOpen"
OnToggleLegend="ToggleLegend"
OnModeChanged="UpdateReferenceModeAsync"
OnGroupChanged="UpdateSelectedGroupAsync"
OnColumnChanged="UpdateSelectedColumnAsync"
OnRollJumpChanged="UpdateRollJumpAsync"
OnDensityChanged="UpdateDensityModeAsync" />
OnToggleLegend="ToggleLegend"/>
<TablesCanvas
Detail="detail"
@@ -79,32 +69,21 @@
RollJumpValue="rollJumpValue"
DensityMode="densityMode"
SelectedCell="selectedCell"
OnSelectCell="SelectCell" />
OnSelectCell="SelectCell"/>
@if (isLegendOpen)
{
<TablesLegend LegendEntries="@(detail.Legend ?? Array.Empty<CriticalTableLegendEntry>())" />
<TablesLegend LegendEntries="@(detail.Legend ?? Array.Empty<CriticalTableLegendEntry>())"/>
}
</div>
}
</div>
@if (tableDetail is not null)
{
<aside class="tables-reference-inspector-shell">
<TablesInspector
SelectedCellDetail="SelectedCellDetail"
OnEdit="OpenSelectedCellEditorAsync"
OnCurate="OpenSelectedCellCurationAsync" />
</aside>
}
</div>
<TablesInspectorSheet
<TablesSelectionMenu
SelectedCellDetail="SelectedCellDetail"
OnClose="ClearSelectedCell"
OnEdit="OpenSelectedCellEditorAsync"
OnCurate="OpenSelectedCellCurationAsync" />
OnCurate="OpenSelectedCellCurationAsync"/>
}
</section>
@@ -125,7 +104,7 @@
OnEdit="OpenEditorFromCurationAsync"
OnEnterQuickParse="EnterCurationQuickParseMode"
OnCancelQuickParse="CancelCurationQuickParseMode"
OnReparse="ReparseCurationCellAsync" />
OnReparse="ReparseCurationCellAsync"/>
}
@if (isEditorOpen)
@@ -142,7 +121,7 @@
SaveErrorMessage="@editorSaveError"
OnClose="CloseCellEditorAsync"
OnReparse="ReparseCellEditorAsync"
OnSave="SaveCellEditorAsync" />
OnSave="SaveCellEditorAsync"/>
}
@code {
@@ -151,9 +130,7 @@
private CriticalTableDetail? tableDetail;
private string selectedTableSlug = string.Empty;
private bool isDetailLoading;
private bool isReferenceDataLoading = true;
private string? detailError;
private bool IsTableSelectionDisabled => isReferenceDataLoading || (referenceData?.CriticalTables.Count ?? 0) == 0;
private bool isEditorOpen;
private bool isEditorLoading;
private bool isEditorReparsing;
@@ -181,17 +158,16 @@
private TablesCellSelection? selectedCell;
private bool isLegendOpen;
private bool hasResolvedStoredTableSelection;
private CriticalTableReference? SelectedTableReference =>
referenceData?.CriticalTables.FirstOrDefault(item => string.Equals(item.Key, selectedTableSlug, StringComparison.OrdinalIgnoreCase));
private CriticalTableCellDetail? SelectedCellDetail =>
selectedCell is null
? null
: tableDetail?.Cells.FirstOrDefault(cell => cell.ResultId == selectedCell.ResultId);
selectedCell is null ? null : tableDetail?.Cells.FirstOrDefault(cell => cell.ResultId == selectedCell.ResultId);
protected override async Task OnInitializedAsync()
{
referenceData = await LookupService.GetReferenceDataAsync();
isReferenceDataLoading = false;
}
private async Task SelectTableAsync(string tableSlug)
@@ -247,17 +223,12 @@
if (!hasResolvedStoredTableSelection && referenceData?.CriticalTables.Count > 0)
{
var initialContext = await TableContextState.RestoreAsync(
NavigationManager.Uri,
ContextDestination,
referenceData.CriticalTables,
RolemasterDb.App.Frontend.AppState.TableContextMode.Reference);
var initialContext = await TableContextState.RestoreAsync(NavigationManager.Uri, ContextDestination, referenceData.CriticalTables, RolemasterDb.App.Frontend.AppState.TableContextMode.Reference);
hasResolvedStoredTableSelection = true;
var resolvedTableSlug = initialContext.TableSlug ?? string.Empty;
if (string.IsNullOrWhiteSpace(selectedTableSlug) ||
!string.Equals(resolvedTableSlug, selectedTableSlug, StringComparison.OrdinalIgnoreCase))
if (string.IsNullOrWhiteSpace(selectedTableSlug) || !string.Equals(resolvedTableSlug, selectedTableSlug, StringComparison.OrdinalIgnoreCase))
{
selectedTableSlug = resolvedTableSlug;
await LoadTableDetailAsync();
@@ -489,11 +460,7 @@
return null;
}
var orderedCells = tableDetail.Cells
.OrderBy(cell => GetGroupSortOrder(cell.GroupKey))
.ThenBy(cell => GetColumnSortOrder(cell.ColumnKey))
.ThenBy(cell => GetRollBandSortOrder(cell.RollBand))
.ToList();
var orderedCells = tableDetail.Cells.OrderBy(cell => GetGroupSortOrder(cell.GroupKey)).ThenBy(cell => GetColumnSortOrder(cell.ColumnKey)).ThenBy(cell => GetRollBandSortOrder(cell.RollBand)).ToList();
var currentIndex = orderedCells.FindIndex(cell => cell.ResultId == currentResultId);
for (var index = currentIndex + 1; index < orderedCells.Count; index++)
@@ -514,9 +481,7 @@
return 0;
}
return tableDetail.Groups
.FirstOrDefault(group => string.Equals(group.Key, groupKey, StringComparison.OrdinalIgnoreCase))
?.SortOrder ?? int.MaxValue;
return tableDetail.Groups.FirstOrDefault(group => string.Equals(group.Key, groupKey, StringComparison.OrdinalIgnoreCase))?.SortOrder ?? int.MaxValue;
}
private int GetColumnSortOrder(string columnKey)
@@ -526,9 +491,7 @@
return int.MaxValue;
}
return tableDetail.Columns
.FirstOrDefault(column => string.Equals(column.Key, columnKey, StringComparison.OrdinalIgnoreCase))
?.SortOrder ?? int.MaxValue;
return tableDetail.Columns.FirstOrDefault(column => string.Equals(column.Key, columnKey, StringComparison.OrdinalIgnoreCase))?.SortOrder ?? int.MaxValue;
}
private int GetRollBandSortOrder(string rollBandLabel)
@@ -538,9 +501,7 @@
return int.MaxValue;
}
return tableDetail.RollBands
.FirstOrDefault(rollBand => string.Equals(rollBand.Label, rollBandLabel, StringComparison.OrdinalIgnoreCase))
?.SortOrder ?? int.MaxValue;
return tableDetail.RollBands.FirstOrDefault(rollBand => string.Equals(rollBand.Label, rollBandLabel, StringComparison.OrdinalIgnoreCase))?.SortOrder ?? int.MaxValue;
}
private async Task CloseCellEditorAsync()
@@ -634,11 +595,7 @@
return Task.CompletedTask;
}
return PinnedTablesState.ToggleAsync(
selectedTable.Key,
selectedTable.Label,
selectedTable.Family,
selectedTable.CurationPercentage);
return PinnedTablesState.ToggleAsync(selectedTable.Key, selectedTable.Label, selectedTable.Family, selectedTable.CurationPercentage);
}
private Task RecordRecentTableVisitAsync()
@@ -648,11 +605,7 @@
return Task.CompletedTask;
}
return RecentTablesState.RecordVisitAsync(
selectedTable.Key,
selectedTable.Label,
selectedTable.Family,
selectedTable.CurationPercentage);
return RecentTablesState.RecordVisitAsync(selectedTable.Key, selectedTable.Label, selectedTable.Family, selectedTable.CurationPercentage);
}
private async Task PersistAndSyncTableContextAsync()
@@ -670,42 +623,7 @@
}
private RolemasterDb.App.Frontend.AppState.TableContextSnapshot BuildCurrentTableContext() =>
new(
TableSlug: selectedTableSlug,
Mode: RolemasterDb.App.Frontend.AppState.TableContextMode.Reference);
private Task UpdateReferenceModeAsync(string mode)
{
referenceMode = NormalizeMode(mode);
NormalizeSelectedCellForCurrentView();
return Task.CompletedTask;
}
private Task UpdateSelectedGroupAsync(string groupKey)
{
selectedGroupKey = NormalizeOptionalFilter(groupKey);
NormalizeSelectedCellForCurrentView();
return Task.CompletedTask;
}
private Task UpdateSelectedColumnAsync(string columnKey)
{
selectedColumnKey = NormalizeOptionalFilter(columnKey);
NormalizeSelectedCellForCurrentView();
return Task.CompletedTask;
}
private Task UpdateRollJumpAsync(string rollValue)
{
rollJumpValue = NormalizeRollInput(rollValue);
return Task.CompletedTask;
}
private Task UpdateDensityModeAsync(string mode)
{
densityMode = NormalizeDensityMode(mode);
return Task.CompletedTask;
}
new(TableSlug: selectedTableSlug, Mode: RolemasterDb.App.Frontend.AppState.TableContextMode.Reference);
private void SelectCell(TablesCellSelection selection)
{
@@ -718,21 +636,11 @@
selectedCell = selection;
}
private Task ClearSelectedCell()
{
selectedCell = null;
return Task.CompletedTask;
}
private Task OpenSelectedCellEditorAsync() =>
selectedCell is null
? Task.CompletedTask
: OpenCellEditorAsync(selectedCell.ResultId);
selectedCell is null ? Task.CompletedTask : OpenCellEditorAsync(selectedCell.ResultId);
private Task OpenSelectedCellCurationAsync() =>
selectedCell is null
? Task.CompletedTask
: OpenCellCurationAsync(selectedCell.ResultId);
selectedCell is null ? Task.CompletedTask : OpenCellCurationAsync(selectedCell.ResultId);
private Task ToggleLegend()
{
@@ -759,10 +667,7 @@
selectedGroupKey = string.Empty;
}
var matchingColumns = tableDetail.Columns
.Where(column => string.IsNullOrWhiteSpace(selectedColumnKey)
|| string.Equals(column.Key, selectedColumnKey, StringComparison.OrdinalIgnoreCase))
.ToList();
var matchingColumns = tableDetail.Columns.Where(column => string.IsNullOrWhiteSpace(selectedColumnKey) || string.Equals(column.Key, selectedColumnKey, StringComparison.OrdinalIgnoreCase)).ToList();
if (matchingColumns.Count == 0)
{
@@ -784,17 +689,12 @@
mode switch
{
TablesReferenceMode.NeedsCuration => TablesReferenceMode.NeedsCuration,
TablesReferenceMode.Curated => TablesReferenceMode.Curated,
_ => TablesReferenceMode.Reference
TablesReferenceMode.Curated => TablesReferenceMode.Curated,
_ => TablesReferenceMode.Reference
};
private static string NormalizeOptionalFilter(string? value) =>
string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
private static string NormalizeDensityMode(string? mode) =>
string.Equals(mode, TablesDensityMode.Dense, StringComparison.Ordinal)
? TablesDensityMode.Dense
: TablesDensityMode.Comfortable;
string.Equals(mode, TablesDensityMode.Dense, StringComparison.Ordinal) ? TablesDensityMode.Dense : TablesDensityMode.Comfortable;
private static string NormalizeRollInput(string? value)
{
@@ -823,14 +723,12 @@
private bool MatchesCurrentView(CriticalTableCellDetail cell)
{
if (!string.IsNullOrWhiteSpace(selectedGroupKey) &&
!string.Equals(cell.GroupKey, selectedGroupKey, StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrWhiteSpace(selectedGroupKey) && !string.Equals(cell.GroupKey, selectedGroupKey, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (!string.IsNullOrWhiteSpace(selectedColumnKey) &&
!string.Equals(cell.ColumnKey, selectedColumnKey, StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrWhiteSpace(selectedColumnKey) && !string.Equals(cell.ColumnKey, selectedColumnKey, StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -838,8 +736,9 @@
return referenceMode switch
{
TablesReferenceMode.NeedsCuration => !cell.IsCurated,
TablesReferenceMode.Curated => cell.IsCurated,
_ => true
TablesReferenceMode.Curated => cell.IsCurated,
_ => true
};
}
}
}

View File

@@ -44,7 +44,7 @@
<div class="critical-table-cell-actions">
@if (string.Equals(CurrentMode, TablesReferenceMode.Reference, StringComparison.Ordinal))
{
<StatusIndicator Tone="@(cell.IsCurated ? "success" : "warning")" CssClass="tables-cell-status-indicator" />
<StatusIndicator Tone="@(cell.IsCurated ? "success" : "warning")" CssClass="tables-cell-status-indicator"/>
}
else if (cell.IsCurated)
{
@@ -64,7 +64,7 @@
<CompactCriticalCell
Description="@(cell.Description ?? string.Empty)"
Effects="@(cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())"/>
</div>
</div>
}
@@ -170,66 +170,60 @@
cellIndex.TryGetValue((rollBand, groupKey, columnKey), out cell);
private bool MatchesGroupFilter(CriticalGroupReference group) =>
string.IsNullOrWhiteSpace(SelectedGroupKey)
|| string.Equals(group.Key, SelectedGroupKey, StringComparison.OrdinalIgnoreCase);
string.IsNullOrWhiteSpace(SelectedGroupKey) || string.Equals(group.Key, SelectedGroupKey, StringComparison.OrdinalIgnoreCase);
private bool MatchesColumnFilter(CriticalColumnReference column) =>
string.IsNullOrWhiteSpace(SelectedColumnKey)
|| string.Equals(column.Key, SelectedColumnKey, StringComparison.OrdinalIgnoreCase);
string.IsNullOrWhiteSpace(SelectedColumnKey) || string.Equals(column.Key, SelectedColumnKey, StringComparison.OrdinalIgnoreCase);
private IReadOnlyList<CriticalGroupReference> ResolveVisibleGroups()
{
var filteredGroups = Detail.Groups
.Where(MatchesGroupFilter)
.ToList();
var filteredGroups = Detail.Groups.Where(MatchesGroupFilter).ToList();
return filteredGroups.Count > 0
? filteredGroups
: Detail.Groups;
return filteredGroups.Count > 0 ? filteredGroups : Detail.Groups;
}
private IReadOnlyList<CriticalColumnReference> ResolveVisibleColumns()
{
var filteredColumns = Detail.Columns
.Where(MatchesColumnFilter)
.ToList();
var filteredColumns = Detail.Columns.Where(MatchesColumnFilter).ToList();
return filteredColumns.Count > 0
? filteredColumns
: Detail.Columns;
return filteredColumns.Count > 0 ? filteredColumns : Detail.Columns;
}
private bool MatchesModeFilter(CriticalTableCellDetail cell) =>
CurrentMode switch
{
TablesReferenceMode.NeedsCuration => !cell.IsCurated,
TablesReferenceMode.Curated => cell.IsCurated,
_ => true
TablesReferenceMode.Curated => cell.IsCurated,
_ => true
};
private string? ActiveRollBand =>
!string.IsNullOrWhiteSpace(SelectedCell?.RollBand)
? SelectedCell.RollBand
: ResolveRollJumpBandLabel();
!string.IsNullOrWhiteSpace(SelectedCell?.RollBand) ? SelectedCell.RollBand : ResolveRollJumpBandLabel();
private string? ActiveColumnKey =>
!string.IsNullOrWhiteSpace(SelectedCell?.ColumnKey)
? SelectedCell.ColumnKey
: (!string.IsNullOrWhiteSpace(SelectedColumnKey) ? SelectedColumnKey : null);
!string.IsNullOrWhiteSpace(SelectedCell?.ColumnKey) ? SelectedCell.ColumnKey : (!string.IsNullOrWhiteSpace(SelectedColumnKey) ? SelectedColumnKey : null);
private string? ActiveGroupKey =>
!string.IsNullOrWhiteSpace(SelectedCell?.GroupKey)
? SelectedCell.GroupKey
: (!string.IsNullOrWhiteSpace(SelectedGroupKey) ? SelectedGroupKey : null);
!string.IsNullOrWhiteSpace(SelectedCell?.GroupKey) ? SelectedCell.GroupKey : (!string.IsNullOrWhiteSpace(SelectedGroupKey) ? SelectedGroupKey : null);
private string BuildGridCssClass() =>
string.Equals(DensityMode, TablesDensityMode.Dense, StringComparison.Ordinal)
? "is-dense"
: "is-comfortable";
private string BuildGridCssClass()
{
var classes = new List<string>
{
string.Equals(DensityMode, TablesDensityMode.Dense, StringComparison.Ordinal) ? "is-dense" : "is-comfortable",
Detail.Groups.Count > 0 ? "has-groups" : "has-no-groups"
};
return string.Join(' ', classes);
}
private string BuildGroupHeaderCssClass(string groupKey)
{
var classes = new List<string> { "critical-table-grid-header-cell", "critical-table-grid-group-header" };
var classes = new List<string>
{
"critical-table-grid-header-cell",
"critical-table-grid-group-header"
};
if (string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
{
classes.Add("is-active-group");
@@ -240,15 +234,18 @@
private string BuildColumnHeaderCssClass(string? groupKey, string columnKey)
{
var classes = new List<string> { "critical-table-grid-header-cell", "critical-table-grid-column-header" };
var classes = new List<string>
{
"critical-table-grid-header-cell",
"critical-table-grid-column-header"
};
if (string.Equals(columnKey, ActiveColumnKey, StringComparison.OrdinalIgnoreCase))
{
classes.Add("is-active-column");
}
if (!string.IsNullOrWhiteSpace(groupKey) &&
string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrWhiteSpace(groupKey) && string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
{
classes.Add("is-active-group");
}
@@ -258,7 +255,11 @@
private string BuildRollBandCssClass(string rollBandLabel)
{
var classes = new List<string> { "critical-table-grid-header-cell", "critical-table-grid-roll-band" };
var classes = new List<string>
{
"critical-table-grid-header-cell",
"critical-table-grid-roll-band"
};
if (string.Equals(rollBandLabel, ActiveRollBand, StringComparison.OrdinalIgnoreCase))
{
classes.Add("is-active-row");
@@ -290,8 +291,7 @@
classes.Add("is-active-column");
}
if (!string.IsNullOrWhiteSpace(groupKey) &&
string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrWhiteSpace(groupKey) && string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
{
classes.Add("is-active-group");
}
@@ -340,9 +340,7 @@
private Task HandleCellKeyDown(KeyboardEventArgs args, CriticalTableCellDetail cell)
{
if (string.Equals(args.Key, "Enter", StringComparison.Ordinal) ||
string.Equals(args.Key, " ", StringComparison.Ordinal) ||
string.Equals(args.Key, "Spacebar", StringComparison.Ordinal))
if (string.Equals(args.Key, "Enter", StringComparison.Ordinal) || string.Equals(args.Key, " ", StringComparison.Ordinal) || string.Equals(args.Key, "Spacebar", StringComparison.Ordinal))
{
return SelectCell(cell);
}
@@ -351,4 +349,4 @@
}
private static string BuildColumnSpanStyle(int span) => $"grid-column: span {span};";
}
}

View File

@@ -1,9 +1,6 @@
<header class="table-browser-header tables-context-bar">
<div class="tables-context-primary">
<div>
<h2 class="panel-title">@Detail.DisplayName</h2>
<p class="table-browser-reading-hint">@GetReadingHint()</p>
</div>
<h2 class="panel-title">@Detail.DisplayName</h2>
<div class="action-row">
<button type="button" class="btn btn-link" @onclick="() => OnTogglePin.InvokeAsync()">
@(IsPinned ? "Unpin table" : "Pin table")
@@ -11,116 +8,11 @@
<button type="button" class="btn btn-link" @onclick="() => OnToggleLegend.InvokeAsync()">
@(IsLegendOpen ? "Hide help" : "Reading help")
</button>
<p class="table-browser-edit-hint">Select a result to inspect it beside the table.</p>
</div>
</div>
<div class="tables-context-controls">
<div class="tables-context-tab-row">
<SegmentedTabs
Items="modeTabs"
SelectedValue="CurrentMode"
SelectedValueChanged="OnModeChanged"
AriaLabel="Reference mode"
CssClass="tables-context-mode-tabs" />
<SegmentedTabs
Items="densityTabs"
SelectedValue="DensityMode"
SelectedValueChanged="OnDensityChanged"
AriaLabel="Table density"
CssClass="tables-context-density-tabs" />
</div>
<div class="tables-context-fields">
@if (Detail.Groups.Count > 1)
{
<label class="tables-context-field">
<span>Variant</span>
<select class="input-shell" value="@SelectedGroupKey" @onchange="HandleGroupChanged">
<option value="">All variants</option>
@foreach (var group in Detail.Groups)
{
<option value="@group.Key">@group.Label</option>
}
</select>
</label>
}
@if (Detail.Columns.Count > 1)
{
<label class="tables-context-field">
<span>Severity focus</span>
<select class="input-shell" value="@SelectedColumnKey" @onchange="HandleColumnChanged">
<option value="">All severities</option>
@foreach (var column in Detail.Columns)
{
<option value="@column.Key">@column.Label</option>
}
</select>
</label>
}
<label class="tables-context-field">
<span>Roll jump</span>
<input
class="input-shell"
inputmode="numeric"
pattern="[0-9]*"
placeholder="e.g. 66"
value="@RollJumpValue"
@oninput="HandleRollJumpChanged" />
</label>
</div>
@if (HasActiveFilters())
{
<div class="tables-context-filter-chips" aria-label="Active table filters">
@if (!string.IsNullOrWhiteSpace(SelectedGroupLabel))
{
<button type="button" class="tables-context-filter-chip" @onclick="() => OnGroupChanged.InvokeAsync(string.Empty)">
Variant: @SelectedGroupLabel
</button>
}
@if (!string.IsNullOrWhiteSpace(SelectedColumnLabel))
{
<button type="button" class="tables-context-filter-chip" @onclick="() => OnColumnChanged.InvokeAsync(string.Empty)">
Severity: @SelectedColumnLabel
</button>
}
@if (!string.IsNullOrWhiteSpace(RollJumpValue))
{
<button type="button" class="tables-context-filter-chip" @onclick="() => OnRollJumpChanged.InvokeAsync(string.Empty)">
Roll: @RollJumpValue
</button>
}
@if (!string.Equals(CurrentMode, TablesReferenceMode.Reference, StringComparison.Ordinal))
{
<button type="button" class="tables-context-filter-chip" @onclick="() => OnModeChanged.InvokeAsync(TablesReferenceMode.Reference)">
Mode: @GetModeLabel(CurrentMode)
</button>
}
</div>
}
</div>
</header>
@code {
private readonly IReadOnlyList<SegmentedTabItem> modeTabs =
[
new(TablesReferenceMode.Reference, "Reference"),
new(TablesReferenceMode.NeedsCuration, "Needs Curation"),
new(TablesReferenceMode.Curated, "Curated")
];
private readonly IReadOnlyList<SegmentedTabItem> densityTabs =
[
new(TablesDensityMode.Comfortable, "Comfortable"),
new(TablesDensityMode.Dense, "Dense")
];
[Parameter, EditorRequired]
public CriticalTableDetail Detail { get; set; } = default!;
@@ -128,21 +20,6 @@
[Parameter]
public bool IsPinned { get; set; }
[Parameter]
public string CurrentMode { get; set; } = TablesReferenceMode.Reference;
[Parameter]
public string SelectedGroupKey { get; set; } = string.Empty;
[Parameter]
public string SelectedColumnKey { get; set; } = string.Empty;
[Parameter]
public string RollJumpValue { get; set; } = string.Empty;
[Parameter]
public string DensityMode { get; set; } = TablesDensityMode.Comfortable;
[Parameter]
public EventCallback OnTogglePin { get; set; }
@@ -152,52 +29,4 @@
[Parameter]
public EventCallback OnToggleLegend { get; set; }
[Parameter]
public EventCallback<string> OnModeChanged { get; set; }
[Parameter]
public EventCallback<string> OnGroupChanged { get; set; }
[Parameter]
public EventCallback<string> OnColumnChanged { get; set; }
[Parameter]
public EventCallback<string> OnRollJumpChanged { get; set; }
[Parameter]
public EventCallback<string> OnDensityChanged { get; set; }
private string GetReadingHint() =>
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.";
private string? SelectedGroupLabel =>
Detail.Groups.FirstOrDefault(group => string.Equals(group.Key, SelectedGroupKey, StringComparison.OrdinalIgnoreCase))?.Label;
private string? SelectedColumnLabel =>
Detail.Columns.FirstOrDefault(column => string.Equals(column.Key, SelectedColumnKey, StringComparison.OrdinalIgnoreCase))?.Label;
private bool HasActiveFilters() =>
!string.IsNullOrWhiteSpace(SelectedGroupKey)
|| !string.IsNullOrWhiteSpace(SelectedColumnKey)
|| !string.IsNullOrWhiteSpace(RollJumpValue)
|| !string.Equals(CurrentMode, TablesReferenceMode.Reference, StringComparison.Ordinal);
private Task HandleGroupChanged(ChangeEventArgs args) =>
OnGroupChanged.InvokeAsync(args.Value?.ToString() ?? string.Empty);
private Task HandleColumnChanged(ChangeEventArgs args) =>
OnColumnChanged.InvokeAsync(args.Value?.ToString() ?? string.Empty);
private Task HandleRollJumpChanged(ChangeEventArgs args) =>
OnRollJumpChanged.InvokeAsync(args.Value?.ToString() ?? string.Empty);
private static string GetModeLabel(string mode) =>
mode switch
{
TablesReferenceMode.NeedsCuration => "Needs Curation",
TablesReferenceMode.Curated => "Curated",
_ => "Reference"
};
}
}

View File

@@ -1,7 +1,6 @@
<section class="tables-index-rail" aria-labelledby="tables-index-heading">
<div class="tables-index-rail-header">
<h2 id="tables-index-heading" class="tables-index-title">Table Index</h2>
<p class="tables-index-copy">Choose a table, then read from the roll band across to the result you need.</p>
</div>
<div class="tables-index-controls" @onkeydown="HandleRailKeyDown">
@@ -12,7 +11,7 @@
type="search"
placeholder="Search tables"
value="@searchText"
@oninput="HandleSearchInput" />
@oninput="HandleSearchInput"/>
@if (familyFilters.Count > 1)
{
@@ -74,7 +73,7 @@
@if (filteredTables.Count == 0)
{
<p class="tables-index-empty">No tables match the current search.</p>
<div class="tables-index-empty">No tables match the current search.</div>
}
else
{
@@ -130,11 +129,7 @@
familyFilters.Clear();
familyFilters.Add(string.Empty);
foreach (var family in Tables
.Select(table => table.Family)
.Where(family => !string.IsNullOrWhiteSpace(family))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(family => family, StringComparer.OrdinalIgnoreCase))
foreach (var family in Tables.Select(table => table.Family).Where(family => !string.IsNullOrWhiteSpace(family)).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy(family => family, StringComparer.OrdinalIgnoreCase))
{
familyFilters.Add(family);
}
@@ -203,8 +198,7 @@
private bool MatchesFilters(CriticalTableReference table)
{
if (!string.IsNullOrEmpty(selectedFamily) &&
!string.Equals(table.Family, selectedFamily, StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrEmpty(selectedFamily) && !string.Equals(table.Family, selectedFamily, StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -214,9 +208,7 @@
return true;
}
return table.Label.Contains(searchText, StringComparison.OrdinalIgnoreCase)
|| table.Key.Contains(searchText, StringComparison.OrdinalIgnoreCase)
|| table.Family.Contains(searchText, StringComparison.OrdinalIgnoreCase);
return table.Label.Contains(searchText, StringComparison.OrdinalIgnoreCase) || table.Key.Contains(searchText, StringComparison.OrdinalIgnoreCase) || table.Family.Contains(searchText, StringComparison.OrdinalIgnoreCase);
}
private void HandleSearchInput(ChangeEventArgs args)
@@ -237,9 +229,7 @@
string.Equals(selectedFamily, family, StringComparison.OrdinalIgnoreCase);
private string GetFamilyFilterCssClass(string family) =>
IsFamilyFilterSelected(family)
? "tables-family-filter is-selected"
: "tables-family-filter";
IsFamilyFilterSelected(family) ? "tables-family-filter is-selected" : "tables-family-filter";
private void HandleRailKeyDown(KeyboardEventArgs args)
{
@@ -280,9 +270,7 @@
currentIndex = keyboardOptions.FindIndex(item => string.Equals(item.Key, SelectedTableSlug, StringComparison.OrdinalIgnoreCase));
}
var nextIndex = currentIndex < 0
? 0
: Math.Clamp(currentIndex + offset, 0, keyboardOptions.Count - 1);
var nextIndex = currentIndex < 0 ? 0 : Math.Clamp(currentIndex + offset, 0, keyboardOptions.Count - 1);
activeOptionSlug = keyboardOptions[nextIndex].Key;
}
@@ -295,8 +283,7 @@
return;
}
if (!string.IsNullOrWhiteSpace(activeOptionSlug) &&
keyboardOptions.Any(item => string.Equals(item.Key, activeOptionSlug, StringComparison.OrdinalIgnoreCase)))
if (!string.IsNullOrWhiteSpace(activeOptionSlug) && keyboardOptions.Any(item => string.Equals(item.Key, activeOptionSlug, StringComparison.OrdinalIgnoreCase)))
{
return;
}
@@ -338,22 +325,23 @@
private void SetActiveOption(string tableSlug) => activeOptionSlug = tableSlug;
private RenderFragment RenderTableOption(CriticalTableReference table) => @<button
type="button"
role="option"
aria-selected="@string.Equals(table.Key, SelectedTableSlug, StringComparison.OrdinalIgnoreCase)"
class="table-index-option @GetTableOptionCssClass(table)"
@onfocus="() => SetActiveOption(table.Key)"
@onclick="() => OnSelectTable.InvokeAsync(table.Key)">
<span class="table-index-option-copy">
<strong class="table-index-option-title">@table.Label</strong>
<span class="table-index-option-meta">@table.Family</span>
</span>
<span class="table-index-option-chips">
@if (GetIsPinned(table.Key))
{
<StatusChip Tone="accent">Pinned</StatusChip>
}
<StatusChip Tone="@GetCurationTone(table)">@($"{table.CurationPercentage}%")</StatusChip>
</span>
</button>;
}
type="button"
role="option"
aria-selected="@string.Equals(table.Key, SelectedTableSlug, StringComparison.OrdinalIgnoreCase)"
class="table-index-option @GetTableOptionCssClass(table)"
@onfocus="() => SetActiveOption(table.Key)"
@onclick="() => OnSelectTable.InvokeAsync(table.Key)">
<span class="table-index-option-copy">
<strong class="table-index-option-title">@table.Label</strong>
<span class="table-index-option-meta">@table.Family</span>
</span>
<span class="table-index-option-chips">
@if (GetIsPinned(table.Key))
{
<StatusChip Tone="accent">Pinned</StatusChip>
}
<StatusChip Tone="@GetCurationTone(table)">@($"{table.CurationPercentage}%")</StatusChip>
</span>
</button>;
}

View File

@@ -3,7 +3,6 @@
<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)
@@ -21,6 +20,8 @@
}
@code {
[Parameter]
public IReadOnlyList<CriticalTableLegendEntry> LegendEntries { get; set; } = Array.Empty<CriticalTableLegendEntry>();
}
}

View File

@@ -1,7 +1,5 @@
<header class="tables-page-header">
<div class="tables-page-header-copy">
<p class="tables-page-eyebrow">Reference</p>
<h1 class="panel-title">Critical Tables</h1>
<p class="tables-page-intro">Browse the index, open a table, and read the result directly from the grid without leaving the page.</p>
</div>
</header>
</header>

View File

@@ -0,0 +1,24 @@
@if (SelectedCellDetail is not null)
{
<div class="tables-selection-menu" aria-label="Selected result actions">
@if (!SelectedCellDetail.IsCurated)
{
<button type="button" class="btn btn-secondary" @onclick="OnCurate">Open curation</button>
}
<button type="button" class="btn btn-primary" @onclick="OnEdit">Open editor</button>
</div>
}
@code {
[Parameter]
public CriticalTableCellDetail? SelectedCellDetail { get; set; }
[Parameter]
public EventCallback OnEdit { get; set; }
[Parameter]
public EventCallback OnCurate { get; set; }
}