Tighten tables layout and grid behavior
This commit is contained in:
@@ -74,6 +74,7 @@ It is intentionally implementation-focused:
|
||||
| 2026-03-21 | P3.11 | Completed | Moved the live editor and curation entry points into the shared inspector content and removed the last remaining grid-owned action buttons. |
|
||||
| 2026-03-21 | P3.12 | Completed | Added keyboard-selectable cells, visible focus treatment, and selection normalization so changing filters or modes cannot leave the inspector pointing at hidden cells. |
|
||||
| 2026-03-21 | Post-P3 fix 1 | Completed | Added a defensive visible-column fallback in the table canvas and tightened view-state normalization so a stale severity filter cannot collapse the grid to roll bands only. |
|
||||
| 2026-04-11 | Post-P3 fix 2 | Completed | Simplified `/tables` by removing static prose and context controls, dropped the redundant selected-result inspector in favor of a floating action menu, and moved the canvas onto its own scroll region so sticky headers layer correctly beneath the context bar. |
|
||||
|
||||
### Lessons Learned
|
||||
|
||||
|
||||
@@ -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>
|
||||
</div>
|
||||
|
||||
@if (tableDetail is not null)
|
||||
{
|
||||
<aside class="tables-reference-inspector-shell">
|
||||
<TablesInspector
|
||||
<TablesSelectionMenu
|
||||
SelectedCellDetail="SelectedCellDetail"
|
||||
OnEdit="OpenSelectedCellEditorAsync"
|
||||
OnCurate="OpenSelectedCellCurationAsync" />
|
||||
</aside>
|
||||
}
|
||||
</div>
|
||||
|
||||
<TablesInspectorSheet
|
||||
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)
|
||||
{
|
||||
@@ -788,13 +693,8 @@
|
||||
_ => 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;
|
||||
}
|
||||
@@ -842,4 +740,5 @@
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,33 +170,23 @@
|
||||
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) =>
|
||||
@@ -208,28 +198,32 @@
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
<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"
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -356,4 +343,5 @@
|
||||
<StatusChip Tone="@GetCurationTone(table)">@($"{table.CurationPercentage}%")</StatusChip>
|
||||
</span>
|
||||
</button>;
|
||||
|
||||
}
|
||||
@@ -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>();
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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; }
|
||||
|
||||
}
|
||||
@@ -51,6 +51,7 @@
|
||||
--button-secondary-bg-hover: rgba(250, 236, 210, 0.95);
|
||||
--button-secondary-text: #6a4b28;
|
||||
--button-secondary-border: rgba(127, 96, 55, 0.18);
|
||||
--control-height: 3rem;
|
||||
--font-display: "Fraunces", Georgia, serif;
|
||||
--font-body: "IBM Plex Sans", "Segoe UI", sans-serif;
|
||||
--font-ui: "IBM Plex Sans", "Segoe UI", sans-serif;
|
||||
@@ -315,11 +316,13 @@ pre,
|
||||
display: grid;
|
||||
gap: 0.95rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.field-shell {
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.roll-input-row {
|
||||
@@ -341,12 +344,19 @@ pre,
|
||||
|
||||
.input-shell {
|
||||
width: 100%;
|
||||
min-height: var(--control-height);
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--button-secondary-border);
|
||||
background: var(--surface-input);
|
||||
padding: 0.8rem 0.9rem;
|
||||
color: var(--text-primary);
|
||||
box-sizing: border-box;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
input.input-shell,
|
||||
select.input-shell {
|
||||
height: var(--control-height);
|
||||
}
|
||||
|
||||
.input-shell:focus {
|
||||
@@ -425,6 +435,7 @@ pre,
|
||||
padding: 1rem;
|
||||
background: var(--surface-card);
|
||||
border: 1px solid rgba(127, 96, 55, 0.14);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.result-card h3,
|
||||
@@ -510,6 +521,9 @@ pre,
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
min-height: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.critical-cell-status-chip,
|
||||
@@ -543,6 +557,7 @@ pre,
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.65rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.critical-cell-description {
|
||||
@@ -550,12 +565,14 @@ pre,
|
||||
color: #2c1a10;
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.4;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.critical-branch-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.critical-branch-card {
|
||||
@@ -563,6 +580,7 @@ pre,
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(127, 96, 55, 0.12);
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.critical-branch-header {
|
||||
@@ -571,6 +589,7 @@ pre,
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.critical-branch-condition {
|
||||
@@ -579,6 +598,7 @@ pre,
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
color: #6b4c29;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.critical-branch-description {
|
||||
@@ -586,6 +606,7 @@ pre,
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.35;
|
||||
color: #3b2a21;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.critical-branch-header .affix-badge-list {
|
||||
@@ -645,6 +666,8 @@ pre,
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.5rem;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.affix-badge {
|
||||
@@ -1146,7 +1169,7 @@ pre,
|
||||
|
||||
.tables-reference-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(17rem, 20rem) minmax(0, 1fr) minmax(19rem, 24rem);
|
||||
grid-template-columns: minmax(17rem, 20rem) minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
}
|
||||
@@ -1341,6 +1364,23 @@ pre,
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tables-selection-menu {
|
||||
position: fixed;
|
||||
right: 1rem;
|
||||
bottom: calc(var(--shell-mobile-nav-height, 0rem) + 1rem);
|
||||
z-index: 40;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
padding: 0.75rem;
|
||||
border-radius: 18px;
|
||||
background: color-mix(in srgb, var(--surface-card-strong) 96%, transparent);
|
||||
border: 1px solid var(--border-default);
|
||||
box-shadow: var(--shadow-2);
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.tables-reference-inspector-shell {
|
||||
position: sticky;
|
||||
top: calc(var(--shell-header-height) + 1rem);
|
||||
@@ -1382,6 +1422,9 @@ pre,
|
||||
}
|
||||
|
||||
.table-shell {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
min-height: 0;
|
||||
border-radius: 20px;
|
||||
padding: 1.2rem;
|
||||
background: var(--surface-card-strong);
|
||||
@@ -1394,15 +1437,14 @@ pre,
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tables-context-bar {
|
||||
position: sticky;
|
||||
top: calc(var(--shell-header-height) + 1rem);
|
||||
z-index: 4;
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
top: calc(var(--shell-header-height, 5.75rem) + 1rem);
|
||||
z-index: 8;
|
||||
padding-bottom: 0.75rem;
|
||||
background: linear-gradient(180deg, rgba(255, 251, 245, 0.96), rgba(255, 251, 245, 0.92));
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid rgba(127, 96, 55, 0.14);
|
||||
@@ -1414,14 +1456,21 @@ pre,
|
||||
|
||||
.tables-context-primary,
|
||||
.tables-context-controls {
|
||||
display: grid;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.85rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tables-context-controls {
|
||||
margin-top: 0.85rem;
|
||||
}
|
||||
|
||||
.tables-context-primary .panel-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tables-context-tab-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -1495,6 +1544,12 @@ pre,
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tables-selection-menu {
|
||||
right: 0.75rem;
|
||||
left: 0.75rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.tables-inspector-sheet {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -1544,19 +1599,30 @@ pre,
|
||||
}
|
||||
|
||||
.table-shell .table-scroll {
|
||||
overflow-x: auto;
|
||||
overflow: auto;
|
||||
min-width: 0;
|
||||
max-height: min(72dvh, calc(100dvh - var(--shell-header-height, 5.75rem) - var(--shell-mobile-nav-height, 0rem) - 5rem));
|
||||
border-radius: 18px;
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-gutter: stable both-edges;
|
||||
}
|
||||
|
||||
.critical-table-grid {
|
||||
display: grid;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
width: max-content;
|
||||
font-size: 1.5rem;
|
||||
border-top: 1px solid rgba(127, 96, 55, 0.2);
|
||||
border-left: 1px solid rgba(127, 96, 55, 0.2);
|
||||
isolation: isolate;
|
||||
--tables-header-row-height: 3.2rem;
|
||||
--tables-group-header-top: 0;
|
||||
--tables-column-header-top: 3.2rem;
|
||||
--tables-column-header-top: 0;
|
||||
}
|
||||
|
||||
.critical-table-grid.has-groups {
|
||||
--tables-column-header-top: var(--tables-header-row-height);
|
||||
}
|
||||
|
||||
.critical-table-grid-header-cell {
|
||||
@@ -1564,10 +1630,11 @@ pre,
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
min-height: var(--tables-header-row-height);
|
||||
padding: 0.35rem;
|
||||
border-right: 1px solid rgba(127, 96, 55, 0.2);
|
||||
border-bottom: 1px solid rgba(127, 96, 55, 0.2);
|
||||
background: rgba(238, 223, 193, 0.45);
|
||||
background: color-mix(in srgb, var(--surface-card-strong) 94%, var(--accent-1));
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
letter-spacing: 0.08em;
|
||||
@@ -1582,18 +1649,18 @@ pre,
|
||||
.critical-table-grid-group-header {
|
||||
position: sticky;
|
||||
top: var(--tables-group-header-top);
|
||||
z-index: 3;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.critical-table-grid-column-header {
|
||||
position: sticky;
|
||||
top: var(--tables-column-header-top);
|
||||
z-index: 3;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.critical-table-grid.is-dense {
|
||||
font-size: 1.2rem;
|
||||
--tables-column-header-top: 2.6rem;
|
||||
--tables-header-row-height: 2.6rem;
|
||||
}
|
||||
|
||||
.critical-table-grid.is-dense .critical-table-grid-header-cell {
|
||||
@@ -1606,34 +1673,35 @@ pre,
|
||||
|
||||
.critical-table-grid-corner,
|
||||
.critical-table-grid-roll-band-header {
|
||||
background: rgba(255, 247, 230, 0.52);
|
||||
background: color-mix(in srgb, var(--surface-card-strong) 94%, var(--accent-1));
|
||||
}
|
||||
|
||||
.critical-table-grid-corner {
|
||||
position: sticky;
|
||||
top: var(--tables-group-header-top);
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.critical-table-grid-roll-band-header {
|
||||
position: sticky;
|
||||
top: var(--tables-column-header-top);
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.critical-table-grid-roll-band {
|
||||
background: rgba(255, 247, 230, 0.52);
|
||||
background: color-mix(in srgb, var(--surface-card-strong) 94%, var(--accent-1));
|
||||
font-size: 1.5rem;
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.critical-table-cell {
|
||||
position: relative;
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
min-width: 0;
|
||||
padding: 0.55rem;
|
||||
border-right: 1px solid rgba(127, 96, 55, 0.2);
|
||||
@@ -2412,10 +2480,6 @@ pre,
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-browser-edit-hint {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.tables-context-fields {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user