352 lines
13 KiB
Plaintext
352 lines
13 KiB
Plaintext
<div class="table-scroll">
|
|
<div class="critical-table-grid @BuildGridCssClass()" 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 visibleGroups)
|
|
{
|
|
<div
|
|
class="@BuildGroupHeaderCssClass(group.Key)"
|
|
style="@BuildColumnSpanStyle(visibleColumns.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="@BuildColumnHeaderCssClass(displayColumn.GroupKey, displayColumn.ColumnKey)">
|
|
<span>@displayColumn.ColumnLabel</span>
|
|
</div>
|
|
}
|
|
|
|
@foreach (var rollBand in Detail.RollBands)
|
|
{
|
|
<div class="@BuildRollBandCssClass(rollBand.Label)">@rollBand.Label</div>
|
|
@foreach (var displayColumn in displayColumns)
|
|
{
|
|
if (TryGetCell(rollBand.Label, displayColumn.GroupKey, displayColumn.ColumnKey, out var resolvedCell) && resolvedCell is not null)
|
|
{
|
|
var cell = resolvedCell;
|
|
var isSelectedCell = IsSelectedCell(cell);
|
|
|
|
@if (MatchesModeFilter(cell))
|
|
{
|
|
<div
|
|
class="@GetCellCssClass(cell, displayColumn.GroupKey)"
|
|
role="button"
|
|
tabindex="0"
|
|
aria-pressed="@isSelectedCell"
|
|
@onclick="() => SelectCell(cell)"
|
|
@onkeydown="args => HandleCellKeyDown(args, cell)">
|
|
<div class="critical-table-cell-shell">
|
|
<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"/>
|
|
}
|
|
else if (cell.IsCurated)
|
|
{
|
|
<span class="critical-cell-status-chip is-curated">Curated</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="critical-cell-status-chip needs-curation">Needs Curation</span>
|
|
}
|
|
|
|
@if (isSelectedCell)
|
|
{
|
|
<StatusChip Tone="accent">Selected</StatusChip>
|
|
}
|
|
</div>
|
|
|
|
<CompactCriticalCell
|
|
Description="@(cell.Description ?? string.Empty)"
|
|
Effects="@(cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
|
|
Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())"/>
|
|
</div>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="critical-table-cell critical-table-cell-empty tables-filtered-cell">
|
|
<span class="empty-cell">Filtered</span>
|
|
</div>
|
|
}
|
|
}
|
|
else
|
|
{
|
|
<div class="critical-table-cell critical-table-cell-empty">
|
|
<span class="empty-cell">—</span>
|
|
</div>
|
|
}
|
|
}
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
@code {
|
|
private readonly Dictionary<(string RollBand, string? GroupKey, string ColumnKey), CriticalTableCellDetail> cellIndex = new();
|
|
private readonly List<(string? GroupKey, string ColumnKey, string ColumnLabel)> displayColumns = new();
|
|
private readonly List<CriticalGroupReference> visibleGroups = new();
|
|
private readonly List<CriticalColumnReference> visibleColumns = new();
|
|
private string gridTemplateStyle = string.Empty;
|
|
|
|
[Parameter, EditorRequired]
|
|
public CriticalTableDetail Detail { get; set; } = default!;
|
|
|
|
[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 TablesCellSelection? SelectedCell { get; set; }
|
|
|
|
[Parameter]
|
|
public EventCallback<TablesCellSelection> OnSelectCell { get; set; }
|
|
|
|
protected override void OnParametersSet()
|
|
{
|
|
cellIndex.Clear();
|
|
displayColumns.Clear();
|
|
visibleGroups.Clear();
|
|
visibleColumns.Clear();
|
|
|
|
foreach (var cell in Detail.Cells)
|
|
{
|
|
cellIndex[(cell.RollBand, cell.GroupKey, cell.ColumnKey)] = cell;
|
|
}
|
|
|
|
var columnsToDisplay = ResolveVisibleColumns();
|
|
|
|
if (Detail.Groups.Count == 0)
|
|
{
|
|
foreach (var column in columnsToDisplay)
|
|
{
|
|
visibleColumns.Add(column);
|
|
displayColumns.Add((null, column.Key, column.Label));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var groupsToDisplay = ResolveVisibleGroups();
|
|
|
|
foreach (var group in groupsToDisplay)
|
|
{
|
|
visibleGroups.Add(group);
|
|
}
|
|
|
|
foreach (var column in columnsToDisplay)
|
|
{
|
|
visibleColumns.Add(column);
|
|
}
|
|
|
|
foreach (var group in visibleGroups)
|
|
{
|
|
foreach (var column in visibleColumns)
|
|
{
|
|
displayColumns.Add((group.Key, column.Key, column.Label));
|
|
}
|
|
}
|
|
}
|
|
|
|
var dataColumnCount = displayColumns.Count;
|
|
gridTemplateStyle = $"grid-template-columns: max-content repeat({dataColumnCount}, minmax(0, 1fr));";
|
|
}
|
|
|
|
private bool TryGetCell(string rollBand, string? groupKey, string columnKey, out CriticalTableCellDetail? cell) =>
|
|
cellIndex.TryGetValue((rollBand, groupKey, columnKey), out cell);
|
|
|
|
private bool MatchesGroupFilter(CriticalGroupReference group) =>
|
|
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);
|
|
|
|
private IReadOnlyList<CriticalGroupReference> ResolveVisibleGroups()
|
|
{
|
|
var filteredGroups = Detail.Groups.Where(MatchesGroupFilter).ToList();
|
|
|
|
return filteredGroups.Count > 0 ? filteredGroups : Detail.Groups;
|
|
}
|
|
|
|
private IReadOnlyList<CriticalColumnReference> ResolveVisibleColumns()
|
|
{
|
|
var filteredColumns = Detail.Columns.Where(MatchesColumnFilter).ToList();
|
|
|
|
return filteredColumns.Count > 0 ? filteredColumns : Detail.Columns;
|
|
}
|
|
|
|
private bool MatchesModeFilter(CriticalTableCellDetail cell) =>
|
|
CurrentMode switch
|
|
{
|
|
TablesReferenceMode.NeedsCuration => !cell.IsCurated,
|
|
TablesReferenceMode.Curated => cell.IsCurated,
|
|
_ => true
|
|
};
|
|
|
|
private string? ActiveRollBand =>
|
|
!string.IsNullOrWhiteSpace(SelectedCell?.RollBand) ? SelectedCell.RollBand : ResolveRollJumpBandLabel();
|
|
|
|
private string? ActiveColumnKey =>
|
|
!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);
|
|
|
|
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"
|
|
};
|
|
if (string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
classes.Add("is-active-group");
|
|
}
|
|
|
|
return string.Join(' ', classes);
|
|
}
|
|
|
|
private string BuildColumnHeaderCssClass(string? groupKey, string columnKey)
|
|
{
|
|
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))
|
|
{
|
|
classes.Add("is-active-group");
|
|
}
|
|
|
|
return string.Join(' ', classes);
|
|
}
|
|
|
|
private string BuildRollBandCssClass(string rollBandLabel)
|
|
{
|
|
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");
|
|
}
|
|
|
|
if (string.Equals(rollBandLabel, ResolveRollJumpBandLabel(), StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
classes.Add("is-roll-target");
|
|
}
|
|
|
|
return string.Join(' ', classes);
|
|
}
|
|
|
|
private string GetCellCssClass(CriticalTableCellDetail cell, string? groupKey)
|
|
{
|
|
var classes = new List<string>
|
|
{
|
|
"critical-table-cell",
|
|
cell.IsCurated ? "is-curated" : "needs-curation"
|
|
};
|
|
|
|
if (string.Equals(cell.RollBand, ActiveRollBand, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
classes.Add("is-active-row");
|
|
}
|
|
|
|
if (string.Equals(cell.ColumnKey, ActiveColumnKey, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
classes.Add("is-active-column");
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(groupKey) && string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
classes.Add("is-active-group");
|
|
}
|
|
|
|
if (SelectedCell is not null && cell.ResultId == SelectedCell.ResultId)
|
|
{
|
|
classes.Add("is-selected-cell");
|
|
}
|
|
|
|
if (string.Equals(cell.RollBand, ResolveRollJumpBandLabel(), StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
classes.Add("is-roll-target");
|
|
}
|
|
|
|
return string.Join(' ', classes);
|
|
}
|
|
|
|
private string? ResolveRollJumpBandLabel()
|
|
{
|
|
if (!int.TryParse(RollJumpValue, out var targetRoll))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
foreach (var rollBand in Detail.RollBands)
|
|
{
|
|
if (targetRoll < rollBand.MinRoll)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (rollBand.MaxRoll is null || targetRoll <= rollBand.MaxRoll.Value)
|
|
{
|
|
return rollBand.Label;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Task SelectCell(CriticalTableCellDetail cell) =>
|
|
OnSelectCell.InvokeAsync(new TablesCellSelection(cell.ResultId, cell.RollBand, cell.ColumnKey, cell.GroupKey));
|
|
|
|
private bool IsSelectedCell(CriticalTableCellDetail cell) =>
|
|
SelectedCell is not null && cell.ResultId == SelectedCell.ResultId;
|
|
|
|
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))
|
|
{
|
|
return SelectCell(cell);
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private static string BuildColumnSpanStyle(int span) => $"grid-column: span {span};";
|
|
} |