diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridBase.cs b/AyCode.Blazor.Components/Components/Grids/MgGridBase.cs index eb06c58..867e8f7 100644 --- a/AyCode.Blazor.Components/Components/Grids/MgGridBase.cs +++ b/AyCode.Blazor.Components/Components/Grids/MgGridBase.cs @@ -8,8 +8,10 @@ using AyCode.Services.SignalRs; using AyCode.Utils.Extensions; using DevExpress.Blazor; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; using System.ComponentModel; using System.Reflection; +using DevExpress.Blazor.Internal; namespace AyCode.Blazor.Components.Components.Grids; @@ -45,14 +47,141 @@ public abstract class MgGridBase public bool IsSyncing => _dataSource?.IsSyncing ?? false; - + /// public event Action? OnSyncingStateChanged; + [Parameter] public bool ShowInfoPanel { get; set; } = true; + + private object _focusedDataItem; + private MgGridInfoPanel? _infoPanelInstance; + public MgGridBase() : base() { } + //protected override RenderFragment CreateRootComponent() + //{ + // System.Diagnostics.Debug.WriteLine($"[MgGridBase] CreateRootComponent - ShowInfoPanel: {ShowInfoPanel}, GridName: {GridName}"); + + // if (!ShowInfoPanel) + // { + // // Ha nincs InfoPanel, használjuk az alapértelmezett renderelést + // System.Diagnostics.Debug.WriteLine("[MgGridBase] Using base CreateRootComponent"); + + // return base.CreateRootComponent(); + // } + + // // Ha van InfoPanel, akkor splitter-rel burkoljuk be + // System.Diagnostics.Debug.WriteLine("[MgGridBase] Creating splitter wrapper"); + + // return (RenderFragment)(content => (RenderFragment)(builder => + // { + // var seq = 0; + + // // DxSplitter + // builder.OpenComponent(seq++); + // builder.AddAttribute(seq++, "Width", "100%"); + // builder.AddAttribute(seq++, "Height", "100%"); + + // // Panes + // builder.AddAttribute(seq++, "Panes", (RenderFragment)(panesBuilder => + // { + // var paneSeq = 0; + + // // Bal pane - Grid + // panesBuilder.OpenComponent(paneSeq++); + + // panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(gridBuilder => + // { + // // A grid eredeti content-jét rendereljük + // var baseRootComponent = base.CreateRootComponent(); + // baseRootComponent(content)(gridBuilder); + // })); + + // panesBuilder.CloseComponent(); + + // // Jobb pane - Egyelőre üres + // panesBuilder.OpenComponent(paneSeq++); + + // panesBuilder.AddAttribute(paneSeq++, "Size", "0px"); + // panesBuilder.AddAttribute(paneSeq++, "MinSize", "0px"); + // panesBuilder.AddAttribute(paneSeq++, "AllowCollapse", true); + // panesBuilder.AddAttribute(paneSeq++, "Collapsed", true); + + // panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder => + // { + // infoPanelBuilder.OpenElement(0, "div"); + // infoPanelBuilder.AddAttribute(1, "style", "padding: 1rem;"); + // infoPanelBuilder.AddContent(2, "Info Panel - Coming soon..."); + // infoPanelBuilder.CloseElement(); + // })); + + // panesBuilder.CloseComponent(); + + // })); + + // builder.CloseComponent(); + // })); + //} + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (!ShowInfoPanel) + { + base.BuildRenderTree(builder); + return; + } + + var seq = 0; + + builder.OpenComponent(seq++); + builder.AddAttribute(seq++, "Width", "100%"); + builder.AddAttribute(seq++, "Height", "100%"); + + builder.AddAttribute(seq++, "Panes", (RenderFragment)(panesBuilder => + { + var paneSeq = 0; + + // Left pane - Grid + panesBuilder.OpenComponent(paneSeq++); + panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(gridBuilder => + { + base.BuildRenderTree(gridBuilder); + })); + panesBuilder.CloseComponent(); + + // Right pane - InfoPanel + panesBuilder.OpenComponent(paneSeq++); + panesBuilder.AddAttribute(paneSeq++, "Size", "350px"); + panesBuilder.AddAttribute(paneSeq++, "MinSize", "300px"); + panesBuilder.AddAttribute(paneSeq++, "MaxSize", "800px"); + panesBuilder.AddAttribute(paneSeq++, "AllowCollapse", true); + + panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder => + { + var infoPanelSeq = 0; + infoPanelBuilder.OpenComponent>(infoPanelSeq++); + infoPanelBuilder.AddComponentReferenceCapture(infoPanelSeq++, instance => + { + _infoPanelInstance = (MgGridInfoPanel)instance; + }); + infoPanelBuilder.CloseComponent(); + })); + + panesBuilder.CloseComponent(); + })); + + builder.CloseComponent(); + } + + //protected override Task RaiseFocusedRowChangedAsync(GridFocusedRowChangedEventArgsBase args) + //{ + // _focusedDataItem = args.DataItem; + // InvokeAsync(StateHasChanged); + // return base.RaiseFocusedRowChangedAsync(args); + //} + protected bool HasIdValue(TDataItem dataItem) => HasIdValue(dataItem.Id); protected bool HasIdValue(TId id) => !_equalityComparerId.Equals(id, default); protected bool IdEquals(TId id1, TId id2) => _equalityComparerId.Equals(id1, id2); @@ -104,6 +233,9 @@ public abstract class MgGridBase CustomizeEditModel { get; set; } [Parameter] public EventCallback OnGridCustomizeEditModel { get; set; } + protected new EventCallback FocusedRowChanged { get; set; } + [Parameter] public EventCallback OnGridFocusedRowChanged { get; set; } + [Parameter] public EventCallback> OnDataSourceChanged { get; set; } [Parameter] public EventCallback> OnGridItemChanging { get; set; } @@ -328,6 +460,19 @@ public abstract class MgGridBase(this, OnItemSaving); base.CustomizeEditModel = EventCallback.Factory.Create(this, OnCustomizeEditModel); //base.customizecel= EventCallback.Factory.Create(this, OnCustomizeEditModel); + base.FocusedRowChanged = EventCallback.Factory.Create(this, OnFocusedRowChanged); base.EditStart = EventCallback.Factory.Create(this, OnEditStart); CustomizeElement += OnCustomizeElement; diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor new file mode 100644 index 0000000..eda2423 --- /dev/null +++ b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor @@ -0,0 +1,42 @@ +@using DevExpress.Blazor +@using Microsoft.AspNetCore.Components.Rendering +@typeparam TDataItem where TDataItem : class + +
+ @if (_currentDataItem != null && _currentGrid != null) + { + var colSpan = _allDataColumns.Count > 10 ? 6 : 12; + + + @foreach (var column in _allDataColumns) + { + + } + + } + else + { +
+

Válasszon ki egy sort az adatok megtekintéséhez

+
+ } +
+ +@code { + private string GetColumnCaption(DxGridDataColumn column) + { + return !string.IsNullOrWhiteSpace(column.Caption) ? column.Caption : column.FieldName; + } +} diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.cs b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.cs new file mode 100644 index 0000000..f75d4dc --- /dev/null +++ b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.cs @@ -0,0 +1,135 @@ +using DevExpress.Blazor; +using Microsoft.AspNetCore.Components; +using System.Reflection; + +namespace AyCode.Blazor.Components.Components.Grids; + +public partial class MgGridInfoPanel : ComponentBase where TDataItem : class +{ + private DxGrid? _currentGrid; + private TDataItem? _currentDataItem; + private List _allDataColumns = []; + + /// + /// Refreshes the InfoPanel with data from the specified grid row + /// + /// The grid instance + /// The data item from the focused row + public void RefreshData(DxGrid grid, TDataItem? dataItem) + { + ArgumentNullException.ThrowIfNull(grid); + + _currentGrid = grid; + _currentDataItem = dataItem; + + if (_currentGrid != null && _currentDataItem != null) + { + _allDataColumns = GetAllDataColumns(_currentGrid); + } + else + { + _allDataColumns = []; + } + + InvokeAsync(StateHasChanged); + } + + /// + /// Clears the InfoPanel + /// + public void Clear() + { + _currentGrid = null; + _currentDataItem = null; + _allDataColumns = []; + InvokeAsync(StateHasChanged); + } + + private List GetAllDataColumns(DxGrid grid) + { + var columns = new List(); + + try + { + var allColumns = grid.GetDataColumns(); + if (allColumns != null) + { + foreach (var column in allColumns) + { + // Minden DxGridDataColumn-t felveszünk, ha van FieldName-je + // NEM vizsgáljuk a Visible property-t! + if (column is DxGridDataColumn dataColumn && + !string.IsNullOrWhiteSpace(dataColumn.FieldName)) + { + columns.Add(dataColumn); + } + } + } + } + catch (Exception) + { + // Fallback: empty list if GetDataColumns fails + } + + return columns; + } + + private object? GetCellValue(DxGridDataColumn column) + { + if (_currentDataItem == null || string.IsNullOrWhiteSpace(column.FieldName)) + return null; + + try + { + var fieldName = column.FieldName; + var properties = fieldName.Split('.'); + object? currentValue = _currentDataItem; + + foreach (var propertyName in properties) + { + if (currentValue == null) + break; + + var propertyInfo = currentValue.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); + if (propertyInfo == null) + return null; + + currentValue = propertyInfo.GetValue(currentValue); + } + + return currentValue; + } + catch + { + return null; + } + } + + private string GetDisplayText(DxGridDataColumn column, object? value) + { + if (value == null) + return string.Empty; + + if (value is DateTime dateTime) + { + return dateTime.ToString("yyyy-MM-dd HH:mm:ss"); + } + + if (value is bool boolValue) + { + return boolValue ? "Igen" : "Nem"; + } + + if (value is decimal || value is double || value is float) + { + return string.Format("{0:N2}", value); + } + + if (value is int || value is long || value is short || value is byte) + { + return string.Format("{0:N0}", value); + } + + return value.ToString() ?? string.Empty; + } +} diff --git a/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.css b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.css new file mode 100644 index 0000000..fd262fb --- /dev/null +++ b/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor.css @@ -0,0 +1,31 @@ +.mg-grid-info-panel { + height: 100%; + overflow-y: auto; + padding: 1rem; + background-color: #f8f9fa; +} + +.info-panel-form { + width: 100%; +} + +.info-panel-form .fw-semibold { + font-weight: 600; + color: #495057; + font-size: 0.875rem; +} + +.info-panel-empty { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: #6c757d; + font-style: italic; +} + +.info-panel-empty p { + margin: 0; + text-align: center; + padding: 2rem; +}