From fe1a59a0bd8decdc277a921e7e79bce11cd86148 Mon Sep 17 00:00:00 2001 From: Loretta Date: Wed, 17 Dec 2025 18:31:59 +0100 Subject: [PATCH] Refactor grid toolbar and InfoPanel for reusability Introduce reusable MgGridToolbarBase and MgGridToolbarTemplate components, replacing old toolbar implementations. InfoPanel now displays grid captions and includes a toolbar for quick actions. Refactor InfoPanel value rendering for improved DevExpress-like appearance. Update IMgGridBase and related classes to support captions and use new abstractions. Update usings and project structure accordingly. --- .../Components/Grids/MgGridBase.cs | 3 + .../Components/Grids/MgGridInfoPanel.razor | 114 +++++---------- .../Components/Grids/MgGridInfoPanel.razor.cs | 10 +- .../Grids/MgGridInfoPanel.razor.css | 28 ++++ .../Components/Grids/MgGridToolbarBase.cs | 12 ++ .../Grids/MgGridToolbarTemplate.razor | 134 ++++++++++++++++++ 6 files changed, 218 insertions(+), 83 deletions(-) create mode 100644 AyCode.Blazor.Components/Components/Grids/MgGridToolbarBase.cs create mode 100644 AyCode.Blazor.Components/Components/Grids/MgGridToolbarTemplate.razor diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridBase.cs b/AyCode.Blazor.Components/Components/Grids/MgGridBase.cs index 8c03df1..8c95de8 100644 --- a/AyCode.Blazor.Components/Components/Grids/MgGridBase.cs +++ b/AyCode.Blazor.Components/Components/Grids/MgGridBase.cs @@ -22,6 +22,7 @@ public interface IMgGridBase : IGrid /// bool IsSyncing { get; } + string Caption { get; set; } /// /// Current edit state of the grid (None, New, Edit) /// @@ -254,6 +255,8 @@ public abstract class MgGridBase ParentDataItem == null; protected PropertyInfo? KeyFieldPropertyInfoToParent; diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor index 4472d58..b46480c 100644 --- a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor +++ b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor @@ -5,9 +5,17 @@
@* Header *@
- Sor részletei + @((_currentGrid as IMgGridBase)?.Caption ?? "")
+ @* Toolbar *@ + @if (_currentGrid != null) + { +
+ +
+ } + @* Content *@
@if (GetActiveDataItem() != null && _currentGrid != null) @@ -446,87 +454,37 @@ { var seq = 0; - switch (settingsType) + // View mode: simple text display with DevExpress-like styling (no editors) + builder.OpenElement(seq++, "div"); + builder.AddAttribute(seq++, "class", GetViewModeCssClass(value, settingsType)); + builder.AddAttribute(seq++, "title", displayText); + + // Special handling for boolean - show checkbox icon + if (value is bool boolValue) { - case EditSettingsType.ComboBox: - builder.OpenElement(seq++, "div"); - builder.AddAttribute(seq++, "title", displayText); - builder.AddAttribute(seq++, "class", "info-panel-text-wrapper"); - builder.OpenComponent(seq++); - builder.AddAttribute(seq++, "Text", displayText); - builder.AddAttribute(seq++, "ReadOnly", true); - builder.CloseComponent(); - builder.CloseElement(); - return; - - case EditSettingsType.CheckBox when value is bool boolVal: - builder.OpenComponent>(seq++); - builder.AddAttribute(seq++, "Checked", boolVal); - builder.AddAttribute(seq++, "ReadOnly", true); - builder.CloseComponent(); - return; - - case EditSettingsType.Memo: - builder.OpenElement(seq++, "div"); - builder.AddAttribute(seq++, "title", displayText); - builder.OpenComponent(seq++); - builder.AddAttribute(seq++, "Text", displayText); - builder.AddAttribute(seq++, "ReadOnly", true); - builder.AddAttribute(seq++, "Rows", 3); - builder.CloseComponent(); - builder.CloseElement(); - return; + builder.OpenElement(seq++, "span"); + builder.AddAttribute(seq++, "class", boolValue ? "dx-icon dx-icon-check text-success" : "dx-icon dx-icon-close text-muted"); + builder.CloseElement(); } - - // Default by value type - switch (value) + else { - case bool boolValue: - builder.OpenComponent>(seq++); - builder.AddAttribute(seq++, "Checked", boolValue); - builder.AddAttribute(seq++, "ReadOnly", true); - builder.CloseComponent(); - break; - - case DateTime dateValue: - builder.OpenComponent>(seq++); - builder.AddAttribute(seq++, "Date", dateValue); - builder.AddAttribute(seq++, "ReadOnly", true); - builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat ?? "yyyy-MM-dd HH:mm"); - builder.CloseComponent(); - break; - - case DateOnly dateOnlyValue: - builder.OpenComponent>(seq++); - builder.AddAttribute(seq++, "Date", dateOnlyValue); - builder.AddAttribute(seq++, "ReadOnly", true); - builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat ?? "yyyy-MM-dd"); - builder.CloseComponent(); - break; - - case decimal or double or float or int or long or short: - builder.OpenElement(seq++, "div"); - builder.AddAttribute(seq++, "title", displayText); - builder.AddAttribute(seq++, "class", "info-panel-text-wrapper"); - builder.OpenComponent(seq++); - builder.AddAttribute(seq++, "Text", displayText); - builder.AddAttribute(seq++, "ReadOnly", true); - builder.AddAttribute(seq++, "CssClass", "text-end"); - builder.CloseComponent(); - builder.CloseElement(); - break; - - default: - builder.OpenElement(seq++, "div"); - builder.AddAttribute(seq++, "title", displayText); - builder.AddAttribute(seq++, "class", "info-panel-text-wrapper"); - builder.OpenComponent(seq++); - builder.AddAttribute(seq++, "Text", displayText); - builder.AddAttribute(seq++, "ReadOnly", true); - builder.CloseComponent(); - builder.CloseElement(); - break; + builder.AddContent(seq++, displayText); } + + builder.CloseElement(); + }; + } + + private static string GetViewModeCssClass(object? value, EditSettingsType settingsType) + { + var baseCss = "mg-info-panel-value"; + + return value switch + { + bool => $"{baseCss} mg-info-panel-value-bool", + decimal or double or float or int or long or short => $"{baseCss} mg-info-panel-value-numeric", + DateTime or DateOnly or TimeOnly => $"{baseCss} mg-info-panel-value-date", + _ => baseCss }; } } diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.cs b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.cs index 20cb506..400666b 100644 --- a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.cs +++ b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.cs @@ -11,7 +11,7 @@ public interface IInfoPanelBase { void ClearEditMode(); void SetEditMode(object editModel); - void RefreshData(DxGrid grid, object? dataItem, int visibleIndex = -1); + void RefreshData(IMgGridBase grid, object? dataItem, int visibleIndex = -1); } /// @@ -30,7 +30,7 @@ public partial class MgGridInfoPanel : ComponentBase, IAsyncDisposable, IInfoPan private bool _isJsInitialized; private const int DefaultTopOffset = 300; // Increased from 180 to account for header + tabs + toolbar - private DxGrid? _currentGrid; + private IMgGridBase? _currentGrid; private object? _currentDataItem; private int _focusedRowVisibleIndex = -1; private List _allDataColumns = []; @@ -69,7 +69,7 @@ public partial class MgGridInfoPanel : ComponentBase, IAsyncDisposable, IInfoPan /// /// Refreshes the InfoPanel with data from the specified grid row (view mode) /// - public void RefreshData(DxGrid grid, object? dataItem, int visibleIndex = -1) + public void RefreshData(IMgGridBase grid, object? dataItem, int visibleIndex = -1) { ArgumentNullException.ThrowIfNull(grid); @@ -290,12 +290,12 @@ public partial class MgGridInfoPanel : ComponentBase, IAsyncDisposable, IInfoPan decimal decValue => decValue.ToString("N2"), double dblValue => dblValue.ToString("N2"), float fltValue => fltValue.ToString("N2"), - int or long or short or byte => string.Format("{0:N0}", value), + int or long or short or byte => $"{value:N0}", _ => value.ToString() ?? string.Empty }; } - private static List GetAllDataColumns(DxGrid grid) + private static List GetAllDataColumns(IMgGridBase grid) { var columns = new List(); diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.css b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.css index 01adbaf..b070bc9 100644 --- a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.css +++ b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.css @@ -105,3 +105,31 @@ overflow: hidden; white-space: nowrap; } +/* View mode value styling - matches DevExpress theme */ +.mg-info-panel-value { + padding: 0.375rem 0.75rem; + min-height: 2rem; + display: flex; + align-items: center; + justify-content: flex-start; /* Always align left */ + background-color: var(--dxbl-bg, #fff); + border: 1px solid var(--dxbl-border-color, #dee2e6); + border-radius: var(--dxbl-border-radius, 0.25rem); + font-size: var(--dxbl-font-size, 0.875rem); + color: var(--dxbl-text, #212529); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mg-info-panel-value-numeric { + font-variant-numeric: tabular-nums; +} + +.mg-info-panel-value-bool { + /* Keep left aligned */ +} + +.mg-info-panel-value-date { + font-variant-numeric: tabular-nums; +} diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridToolbarBase.cs b/AyCode.Blazor.Components/Components/Grids/MgGridToolbarBase.cs new file mode 100644 index 0000000..e4ee10d --- /dev/null +++ b/AyCode.Blazor.Components/Components/Grids/MgGridToolbarBase.cs @@ -0,0 +1,12 @@ +using DevExpress.Blazor; +using Microsoft.AspNetCore.Components; + +namespace AyCode.Blazor.Components.Components.Grids +{ + public class MgGridToolbarBase : DxToolbar + { + [Parameter] public IMgGridBase Grid { get; set; } + [Parameter] public Func RefreshClick { get; set; } + [Parameter] public bool ShowOnlyIcon { get; set; } = false; + } +} diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridToolbarTemplate.razor b/AyCode.Blazor.Components/Components/Grids/MgGridToolbarTemplate.razor new file mode 100644 index 0000000..1350c1d --- /dev/null +++ b/AyCode.Blazor.Components/Components/Grids/MgGridToolbarTemplate.razor @@ -0,0 +1,134 @@ +@using AyCode.Blazor.Components.Components.Grids + + + + + + + + + + + + + @if (!OnlyGridEditTools) + { + + + + + + + + + + + + @ToolbarItemsExtended + } + + +@code { + [Parameter] public bool OnlyGridEditTools { get; set; } = false; + [Parameter] public IMgGridBase Grid { get; set; } = null!; + [Parameter] public RenderFragment? ToolbarItemsExtended { get; set; } + [Parameter] public EventCallback OnReloadDataClick { get; set; } + [Parameter] public bool ShowOnlyIcon { get; set; } = false; + + public MgGridToolbarBase GridToolbar { get; set; } = null!; + const string ExportFileName = "ExportResult"; + + private bool _isReloadInProgress; + + /// + /// Whether the grid is currently in edit mode (New or Edit) + /// + private bool IsEditing => Grid?.GridEditState != MgGridEditState.None; + + /// + /// Whether the grid is currently syncing data + /// + private bool IsSyncing => Grid?.IsSyncing ?? false; + + /// + /// Whether there is a focused row in the grid + /// + private bool HasFocusedRow => Grid?.GetFocusedRowIndex() >= 0; + + protected override void OnInitialized() + { + } + + async Task ReloadData_Click(ToolbarItemClickEventArgs e) + { + _isReloadInProgress = true; + try + { + await OnReloadDataClick.InvokeAsync(e); + } + finally + { + _isReloadInProgress = false; + } + } + + async Task NewItem_Click() + { + await Grid.StartEditNewRowAsync(); + } + + async Task EditItem_Click() + { + await Grid.StartEditRowAsync(Grid.GetFocusedRowIndex()); + } + + void DeleteItem_Click() + { + Grid.ShowRowDeleteConfirmation(Grid.GetFocusedRowIndex()); + } + + async Task SaveItem_Click() + { + await Grid.SaveChangesAsync(); + } + + async Task CancelEdit_Click() + { + await Grid.CancelEditAsync(); + } + + void PrevRow_Click() + { + Grid.StepPrevRow(); + } + + void NextRow_Click() + { + Grid.StepNextRow(); + } + + void ColumnChooserItem_Click(ToolbarItemClickEventArgs e) + { + Grid.ShowColumnChooser(); + } + + async Task ExportXlsxItem_Click() + { + await Grid.ExportToXlsxAsync(ExportFileName); + } + + async Task ExportXlsItem_Click() + { + await Grid.ExportToXlsAsync(ExportFileName); + } + + async Task ExportCsvItem_Click() + { + await Grid.ExportToCsvAsync(ExportFileName); + } + + async Task ExportPdfItem_Click() + { + await Grid.ExportToPdfAsync(ExportFileName); + } +} \ No newline at end of file