Refactor InfoPanel: non-generic, nested grid support
- Replace generic InfoPanel with non-generic version using IInfoPanelBase - Add ParentGrid, GetRootGrid, and InfoPanelInstance to IMgGridBase for nested grid hierarchy - Only root grid displays InfoPanel; nested grids inherit context - InfoPanel now handles any data type via reflection and object - All grid-to-InfoPanel communication routed through root grid - Add option to show/hide readonly fields in edit mode - Improve InfoPanel CSS for up to 4 responsive columns - Remove redundant code and add debug output for InfoPanel data flow
This commit is contained in:
parent
109a4b82b4
commit
90f12a160e
|
|
@ -27,6 +27,16 @@ public interface IMgGridBase : IGrid
|
||||||
/// </summary>
|
/// </summary>
|
||||||
MgGridEditState GridEditState { get; }
|
MgGridEditState GridEditState { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parent grid in nested grid hierarchy (null if this is a root grid)
|
||||||
|
/// </summary>
|
||||||
|
IMgGridBase? ParentGrid { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the root grid in the hierarchy
|
||||||
|
/// </summary>
|
||||||
|
IMgGridBase GetRootGrid();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Navigates to the previous row in the grid
|
/// Navigates to the previous row in the grid
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -36,6 +46,16 @@ public interface IMgGridBase : IGrid
|
||||||
/// Navigates to the next row in the grid
|
/// Navigates to the next row in the grid
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void StepNextRow();
|
void StepNextRow();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this grid shows an InfoPanel
|
||||||
|
/// </summary>
|
||||||
|
bool ShowInfoPanel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// InfoPanel instance for displaying row details
|
||||||
|
/// </summary>
|
||||||
|
IInfoPanelBase? InfoPanelInstance { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient> : DxGrid, IMgGridBase, IAsyncDisposable
|
public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient> : DxGrid, IMgGridBase, IAsyncDisposable
|
||||||
|
|
@ -61,11 +81,30 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MgGridEditState GridEditState { get; private set; } = MgGridEditState.None;
|
public MgGridEditState GridEditState { get; private set; } = MgGridEditState.None;
|
||||||
|
|
||||||
[Parameter] public bool ShowInfoPanel { get; set; } = true;
|
/// <inheritdoc />
|
||||||
|
[CascadingParameter]
|
||||||
|
public IMgGridBase? ParentGrid { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IMgGridBase GetRootGrid()
|
||||||
|
{
|
||||||
|
var current = (IMgGridBase)this;
|
||||||
|
while (current.ParentGrid != null)
|
||||||
|
{
|
||||||
|
current = current.ParentGrid;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Parameter] public bool ShowInfoPanel { get; set; } = false;
|
||||||
|
|
||||||
private object _focusedDataItem;
|
private object _focusedDataItem;
|
||||||
private MgGridInfoPanel<TDataItem>? _infoPanelInstance;
|
|
||||||
|
/// <summary>
|
||||||
|
/// InfoPanel instance for displaying row details
|
||||||
|
/// </summary>
|
||||||
|
public IInfoPanelBase? InfoPanelInstance { get; set; }
|
||||||
|
|
||||||
public MgGridBase() : base()
|
public MgGridBase() : base()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -137,54 +176,65 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
||||||
|
|
||||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
{
|
{
|
||||||
if (!ShowInfoPanel)
|
|
||||||
{
|
|
||||||
base.BuildRenderTree(builder);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var seq = 0;
|
var seq = 0;
|
||||||
|
|
||||||
builder.OpenComponent<DxSplitter>(seq++);
|
// Wrap everything in a CascadingValue to provide this grid as ParentGrid to nested grids
|
||||||
builder.AddAttribute(seq++, "Width", "100%");
|
builder.OpenComponent<CascadingValue<IMgGridBase>>(seq++);
|
||||||
builder.AddAttribute(seq++, "CssClass", "mg-grid-splitter");
|
builder.AddAttribute(seq++, "Value", (IMgGridBase)this);
|
||||||
builder.AddAttribute(seq++, "Orientation", Orientation.Horizontal);
|
builder.AddAttribute(seq++, "ChildContent", (RenderFragment)(contentBuilder =>
|
||||||
|
|
||||||
builder.AddAttribute(seq++, "Panes", (RenderFragment)(panesBuilder =>
|
|
||||||
{
|
{
|
||||||
var paneSeq = 0;
|
// Nested grids or root without InfoPanel: render base grid only
|
||||||
|
if (ParentGrid != null || !ShowInfoPanel)
|
||||||
// Left pane - Grid
|
|
||||||
panesBuilder.OpenComponent<DxSplitterPane>(paneSeq++);
|
|
||||||
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(gridBuilder =>
|
|
||||||
{
|
{
|
||||||
base.BuildRenderTree(gridBuilder);
|
base.BuildRenderTree(contentBuilder);
|
||||||
}));
|
return;
|
||||||
panesBuilder.CloseComponent();
|
}
|
||||||
|
|
||||||
// Right pane - InfoPanel (sticky to viewport)
|
// Root grid with InfoPanel enabled: render splitter with grid + InfoPanel
|
||||||
panesBuilder.OpenComponent<DxSplitterPane>(paneSeq++);
|
var innerSeq = 0;
|
||||||
panesBuilder.AddAttribute(paneSeq++, "Size", "400px");
|
|
||||||
panesBuilder.AddAttribute(paneSeq++, "MinSize", "0px");
|
|
||||||
panesBuilder.AddAttribute(paneSeq++, "MaxSize", "100%");
|
|
||||||
panesBuilder.AddAttribute(paneSeq++, "AllowCollapse", true);
|
|
||||||
panesBuilder.AddAttribute(paneSeq++, "CssClass", "mg-info-panel-pane");
|
|
||||||
|
|
||||||
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder =>
|
contentBuilder.OpenComponent<DxSplitter>(innerSeq++);
|
||||||
|
contentBuilder.AddAttribute(innerSeq++, "Width", "100%");
|
||||||
|
contentBuilder.AddAttribute(innerSeq++, "CssClass", "mg-grid-splitter");
|
||||||
|
contentBuilder.AddAttribute(innerSeq++, "Orientation", Orientation.Horizontal);
|
||||||
|
|
||||||
|
contentBuilder.AddAttribute(innerSeq++, "Panes", (RenderFragment)(panesBuilder =>
|
||||||
{
|
{
|
||||||
var infoPanelSeq = 0;
|
var paneSeq = 0;
|
||||||
infoPanelBuilder.OpenComponent<MgGridInfoPanel<TDataItem>>(infoPanelSeq++);
|
|
||||||
infoPanelBuilder.AddComponentReferenceCapture(infoPanelSeq++, instance =>
|
// Left pane - Grid
|
||||||
|
panesBuilder.OpenComponent<DxSplitterPane>(paneSeq++);
|
||||||
|
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(gridBuilder =>
|
||||||
{
|
{
|
||||||
_infoPanelInstance = (MgGridInfoPanel<TDataItem>)instance;
|
base.BuildRenderTree(gridBuilder);
|
||||||
});
|
}));
|
||||||
infoPanelBuilder.CloseComponent();
|
panesBuilder.CloseComponent();
|
||||||
|
|
||||||
|
// Right pane - InfoPanel
|
||||||
|
panesBuilder.OpenComponent<DxSplitterPane>(paneSeq++);
|
||||||
|
panesBuilder.AddAttribute(paneSeq++, "Size", "400px");
|
||||||
|
panesBuilder.AddAttribute(paneSeq++, "MinSize", "0px");
|
||||||
|
panesBuilder.AddAttribute(paneSeq++, "MaxSize", "100%");
|
||||||
|
panesBuilder.AddAttribute(paneSeq++, "AllowCollapse", true);
|
||||||
|
panesBuilder.AddAttribute(paneSeq++, "CssClass", "mg-info-panel-pane");
|
||||||
|
|
||||||
|
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder =>
|
||||||
|
{
|
||||||
|
var infoPanelSeq = 0;
|
||||||
|
infoPanelBuilder.OpenComponent<MgGridInfoPanel>(infoPanelSeq++);
|
||||||
|
infoPanelBuilder.AddComponentReferenceCapture(infoPanelSeq++, instance =>
|
||||||
|
{
|
||||||
|
InfoPanelInstance = (MgGridInfoPanel)instance;
|
||||||
|
});
|
||||||
|
infoPanelBuilder.CloseComponent();
|
||||||
|
}));
|
||||||
|
|
||||||
|
panesBuilder.CloseComponent();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
panesBuilder.CloseComponent();
|
contentBuilder.CloseComponent();
|
||||||
}));
|
}));
|
||||||
|
builder.CloseComponent(); // Close CascadingValue
|
||||||
builder.CloseComponent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//protected override Task RaiseFocusedRowChangedAsync(GridFocusedRowChangedEventArgsBase args)
|
//protected override Task RaiseFocusedRowChangedAsync(GridFocusedRowChangedEventArgsBase args)
|
||||||
|
|
@ -311,6 +361,7 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
||||||
throw new NullReferenceException($"[{GetType().Name}] SignalRClient == null");
|
throw new NullReferenceException($"[{GetType().Name}] SignalRClient == null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShowInfoPanel = IsMasterGrid;
|
||||||
var crudTags = new SignalRCrudTags(GetAllMessageTag, GetItemMessageTag, AddMessageTag, UpdateMessageTag, RemoveMessageTag);
|
var crudTags = new SignalRCrudTags(GetAllMessageTag, GetItemMessageTag, AddMessageTag, UpdateMessageTag, RemoveMessageTag);
|
||||||
|
|
||||||
_dataSource = (TSignalRDataSource)Activator.CreateInstance(typeof(TSignalRDataSource), SignalRClient, crudTags, ContextIds)!;
|
_dataSource = (TSignalRDataSource)Activator.CreateInstance(typeof(TSignalRDataSource), SignalRClient, crudTags, ContextIds)!;
|
||||||
|
|
@ -450,40 +501,44 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
||||||
|
|
||||||
await OnGridCustomizeEditModel.InvokeAsync(e);
|
await OnGridCustomizeEditModel.InvokeAsync(e);
|
||||||
|
|
||||||
// Frissítjük az InfoPanel-t edit módba - itt az EditModel már elérhető
|
// Update InfoPanel to edit mode
|
||||||
if (ShowInfoPanel && _infoPanelInstance != null)
|
var rootGrid = GetRootGrid();
|
||||||
|
if (rootGrid.ShowInfoPanel && rootGrid.InfoPanelInstance != null)
|
||||||
{
|
{
|
||||||
_infoPanelInstance.SetEditMode(editModel);
|
rootGrid.InfoPanelInstance.SetEditMode(editModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force grid refresh to apply edit mode styling
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnEditStart(GridEditStartEventArgs e)
|
private async Task OnEditStart(GridEditStartEventArgs e)
|
||||||
{
|
{
|
||||||
await OnGridEditStart.InvokeAsync(e);
|
await OnGridEditStart.InvokeAsync(e);
|
||||||
// Az InfoPanel-t és az EditMode-ot a CustomizeEditModel-ben frissítjük, mert ott az EditModel már elérhető
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual async Task OnFocusedRowChanged(GridFocusedRowChangedEventArgs e)
|
protected virtual async Task OnFocusedRowChanged(GridFocusedRowChangedEventArgs e)
|
||||||
{
|
{
|
||||||
_focusedDataItem = e.DataItem;
|
_focusedDataItem = e.DataItem;
|
||||||
|
|
||||||
|
var rootGrid = GetRootGrid();
|
||||||
|
|
||||||
if (ShowInfoPanel && _infoPanelInstance != null)
|
if (!rootGrid.ShowInfoPanel) return;
|
||||||
|
|
||||||
|
// Get the root grid's InfoPanel (the visible one)
|
||||||
|
var infoPanelInstance = rootGrid.InfoPanelInstance;
|
||||||
|
|
||||||
|
if (infoPanelInstance != null && e.DataItem != null)
|
||||||
{
|
{
|
||||||
// Ha edit módban vagyunk, de a felhasználó egy másik sorra kattintott,
|
// Ha edit módban vagyunk, de a felhasználó egy másik sorra kattintott,
|
||||||
// akkor kilépünk az edit módból
|
// akkor kilépünk az edit módból
|
||||||
if (GridEditState != MgGridEditState.None)
|
if (GridEditState != MgGridEditState.None)
|
||||||
{
|
{
|
||||||
_infoPanelInstance.ClearEditMode();
|
infoPanelInstance.ClearEditMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frissítjük az InfoPanel-t az új sor adataival
|
// Frissítjük az InfoPanel-t az új sor adataival
|
||||||
if (e.DataItem is TDataItem dataItem)
|
// The InfoPanel is now non-generic and works with any data type!
|
||||||
{
|
infoPanelInstance.RefreshData(this, e.DataItem, e.VisibleIndex);
|
||||||
_infoPanelInstance.RefreshData(this, dataItem, e.VisibleIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await OnGridFocusedRowChanged.InvokeAsync(e);
|
await OnGridFocusedRowChanged.InvokeAsync(e);
|
||||||
|
|
@ -516,29 +571,27 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
||||||
}
|
}
|
||||||
else await UpdateDataItemAsync(dataItem);
|
else await UpdateDataItemAsync(dataItem);
|
||||||
|
|
||||||
// Kilépés edit módból
|
|
||||||
GridEditState = MgGridEditState.None;
|
GridEditState = MgGridEditState.None;
|
||||||
|
|
||||||
if (ShowInfoPanel && _infoPanelInstance != null)
|
var rootGrid = GetRootGrid();
|
||||||
|
if (rootGrid.ShowInfoPanel && rootGrid.InfoPanelInstance != null)
|
||||||
{
|
{
|
||||||
_infoPanelInstance.ClearEditMode();
|
rootGrid.InfoPanelInstance.ClearEditMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force grid refresh to remove edit mode styling
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnEditCanceling(GridEditCancelingEventArgs e)
|
private async Task OnEditCanceling(GridEditCancelingEventArgs e)
|
||||||
{
|
{
|
||||||
// Kilépés edit módból
|
|
||||||
GridEditState = MgGridEditState.None;
|
GridEditState = MgGridEditState.None;
|
||||||
|
|
||||||
if (ShowInfoPanel && _infoPanelInstance != null)
|
var rootGrid = GetRootGrid();
|
||||||
|
if (rootGrid.ShowInfoPanel && rootGrid.InfoPanelInstance != null)
|
||||||
{
|
{
|
||||||
_infoPanelInstance.ClearEditMode();
|
rootGrid.InfoPanelInstance.ClearEditMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force grid refresh to remove edit mode styling
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
@using DevExpress.Blazor
|
@using DevExpress.Blazor
|
||||||
@using Microsoft.AspNetCore.Components.Rendering
|
@using Microsoft.AspNetCore.Components.Rendering
|
||||||
@using System.Reflection
|
@using System.Reflection
|
||||||
@typeparam TDataItem where TDataItem : class
|
|
||||||
|
|
||||||
<div @ref="_panelElement" class="mg-grid-info-panel @(_isEditMode ? "edit-mode" : "view-mode")">
|
<div @ref="_panelElement" class="mg-grid-info-panel @(_isEditMode ? "edit-mode" : "view-mode")">
|
||||||
@* Header - matches grid toolbar height *@
|
@* Header *@
|
||||||
<div class="dxbl-grid-header-panel px-3 py-2 border-bottom">
|
<div class="dxbl-grid-header-panel px-3 py-2 border-bottom">
|
||||||
<span class="fw-semibold">Sor részletei</span>
|
<span class="fw-semibold">Sor részletei</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@* Content - scrollable area *@
|
@* Content *@
|
||||||
<div class="mg-info-panel-content">
|
<div class="mg-info-panel-content">
|
||||||
@if (GetActiveDataItem() != null && _currentGrid != null)
|
@if (GetActiveDataItem() != null && _currentGrid != null)
|
||||||
{
|
{
|
||||||
var dataItem = GetActiveDataItem()!;
|
var dataItem = GetActiveDataItem()!;
|
||||||
|
var dataItemType = dataItem.GetType();
|
||||||
|
|
||||||
<div class="mg-info-panel-grid">
|
<div class="mg-info-panel-grid">
|
||||||
@foreach (var column in _allDataColumns)
|
@foreach (var column in GetVisibleColumns())
|
||||||
{
|
{
|
||||||
var displayText = GetDisplayTextFromGrid(column);
|
var displayText = GetDisplayTextFromGrid(column);
|
||||||
var value = GetCellValue(column);
|
var value = GetCellValue(column);
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
<div class="dxbl-fl-ec">
|
<div class="dxbl-fl-ec">
|
||||||
@if (_isEditMode && !column.ReadOnly)
|
@if (_isEditMode && !column.ReadOnly)
|
||||||
{
|
{
|
||||||
@RenderEditableCell(column, dataItem, value, displayText, settingsType)
|
@RenderEditableCell(column, dataItem, dataItemType, value, displayText, settingsType)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -53,30 +53,40 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string GetColumnCaption(DxGridDataColumn column)
|
/// <summary>
|
||||||
|
/// Gets the columns to display based on edit mode and ShowReadOnlyFieldsInEditMode setting
|
||||||
|
/// </summary>
|
||||||
|
private IEnumerable<DxGridDataColumn> GetVisibleColumns()
|
||||||
|
{
|
||||||
|
if (!_isEditMode || ShowReadOnlyFieldsInEditMode)
|
||||||
|
{
|
||||||
|
return _allDataColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In edit mode with ShowReadOnlyFieldsInEditMode=false, hide readonly columns
|
||||||
|
return _allDataColumns.Where(c => !c.ReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetColumnCaption(DxGridDataColumn column)
|
||||||
{
|
{
|
||||||
return !string.IsNullOrWhiteSpace(column.Caption) ? column.Caption : column.FieldName;
|
return !string.IsNullOrWhiteSpace(column.Caption) ? column.Caption : column.FieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCaptionCssClass(bool isReadOnly)
|
private static string GetCaptionCssClass(bool isReadOnly)
|
||||||
{
|
{
|
||||||
return isReadOnly ? "fw-semibold" : "fw-semibold text-primary";
|
return isReadOnly ? "fw-semibold" : "fw-semibold text-primary";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private RenderFragment RenderEditableCell(DxGridDataColumn column, object dataItem, Type dataItemType, object? value, string displayText, EditSettingsType settingsType)
|
||||||
/// Renders an editable cell with two-way binding to the EditModel
|
|
||||||
/// </summary>
|
|
||||||
private RenderFragment RenderEditableCell(DxGridDataColumn column, TDataItem dataItem, object? value, string displayText, EditSettingsType settingsType)
|
|
||||||
{
|
{
|
||||||
return builder =>
|
return builder =>
|
||||||
{
|
{
|
||||||
var seq = 0;
|
var seq = 0;
|
||||||
var fieldName = column.FieldName;
|
var fieldName = column.FieldName;
|
||||||
var propertyInfo = typeof(TDataItem).GetProperty(fieldName);
|
var propertyInfo = dataItemType.GetProperty(fieldName);
|
||||||
|
|
||||||
if (propertyInfo == null)
|
if (propertyInfo == null)
|
||||||
{
|
{
|
||||||
// Fallback to readonly if property not found
|
|
||||||
RenderCellContent(column, value, displayText, settingsType)(builder);
|
RenderCellContent(column, value, displayText, settingsType)(builder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -84,10 +94,10 @@
|
||||||
var propertyType = propertyInfo.PropertyType;
|
var propertyType = propertyInfo.PropertyType;
|
||||||
var underlyingType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
var underlyingType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
||||||
|
|
||||||
// ComboBox columns
|
// ComboBox
|
||||||
if (settingsType == EditSettingsType.ComboBox)
|
if (settingsType == EditSettingsType.ComboBox)
|
||||||
{
|
{
|
||||||
var comboSettings = GetEditSettings(fieldName) as DxComboBoxSettings;
|
var comboSettings = GetEditSettings(column.FieldName) as DxComboBoxSettings;
|
||||||
if (comboSettings != null)
|
if (comboSettings != null)
|
||||||
{
|
{
|
||||||
RenderComboBoxEditor(builder, ref seq, dataItem, propertyInfo, comboSettings);
|
RenderComboBoxEditor(builder, ref seq, dataItem, propertyInfo, comboSettings);
|
||||||
|
|
@ -95,54 +105,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render based on property type
|
// Render based on type
|
||||||
if (underlyingType == typeof(bool))
|
if (underlyingType == typeof(bool))
|
||||||
{
|
|
||||||
RenderCheckBoxEditor(builder, ref seq, dataItem, propertyInfo);
|
RenderCheckBoxEditor(builder, ref seq, dataItem, propertyInfo);
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(DateTime))
|
else if (underlyingType == typeof(DateTime))
|
||||||
{
|
|
||||||
RenderDateTimeEditor(builder, ref seq, dataItem, propertyInfo, column.DisplayFormat);
|
RenderDateTimeEditor(builder, ref seq, dataItem, propertyInfo, column.DisplayFormat);
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(DateOnly))
|
else if (underlyingType == typeof(DateOnly))
|
||||||
{
|
|
||||||
RenderDateOnlyEditor(builder, ref seq, dataItem, propertyInfo, column.DisplayFormat);
|
RenderDateOnlyEditor(builder, ref seq, dataItem, propertyInfo, column.DisplayFormat);
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(TimeOnly))
|
|
||||||
{
|
|
||||||
RenderTimeOnlyEditor(builder, ref seq, dataItem, propertyInfo);
|
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(TimeSpan))
|
|
||||||
{
|
|
||||||
RenderTimeSpanEditor(builder, ref seq, dataItem, propertyInfo);
|
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(int))
|
else if (underlyingType == typeof(int))
|
||||||
{
|
|
||||||
RenderSpinIntEditor(builder, ref seq, dataItem, propertyInfo);
|
RenderSpinIntEditor(builder, ref seq, dataItem, propertyInfo);
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(decimal))
|
else if (underlyingType == typeof(decimal))
|
||||||
{
|
|
||||||
RenderSpinDecimalEditor(builder, ref seq, dataItem, propertyInfo);
|
RenderSpinDecimalEditor(builder, ref seq, dataItem, propertyInfo);
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(double))
|
else if (underlyingType == typeof(double))
|
||||||
{
|
|
||||||
RenderSpinDoubleEditor(builder, ref seq, dataItem, propertyInfo);
|
RenderSpinDoubleEditor(builder, ref seq, dataItem, propertyInfo);
|
||||||
}
|
else if (settingsType == EditSettingsType.Memo)
|
||||||
else if (settingsType == EditSettingsType.Memo || (propertyType == typeof(string) && fieldName.Contains("Comment", StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
RenderMemoEditor(builder, ref seq, dataItem, propertyInfo);
|
RenderMemoEditor(builder, ref seq, dataItem, propertyInfo);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
RenderTextBoxEditor(builder, ref seq, dataItem, propertyInfo);
|
RenderTextBoxEditor(builder, ref seq, dataItem, propertyInfo);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderCheckBoxEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
private void RenderCheckBoxEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||||
{
|
{
|
||||||
var currentValue = (bool)(propertyInfo.GetValue(dataItem) ?? false);
|
var currentValue = (bool)(propertyInfo.GetValue(dataItem) ?? false);
|
||||||
|
|
||||||
builder.OpenComponent<DxCheckBox<bool>>(seq++);
|
builder.OpenComponent<DxCheckBox<bool>>(seq++);
|
||||||
builder.AddAttribute(seq++, "Checked", currentValue);
|
builder.AddAttribute(seq++, "Checked", currentValue);
|
||||||
builder.AddAttribute(seq++, "CheckedChanged", EventCallback.Factory.Create<bool>(this, newValue =>
|
builder.AddAttribute(seq++, "CheckedChanged", EventCallback.Factory.Create<bool>(this, newValue =>
|
||||||
|
|
@ -153,7 +138,7 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderDateTimeEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, string? displayFormat)
|
private void RenderDateTimeEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, string? displayFormat)
|
||||||
{
|
{
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||||
var currentValue = propertyInfo.GetValue(dataItem);
|
var currentValue = propertyInfo.GetValue(dataItem);
|
||||||
|
|
@ -182,7 +167,7 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderDateOnlyEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, string? displayFormat)
|
private void RenderDateOnlyEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, string? displayFormat)
|
||||||
{
|
{
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||||
var currentValue = propertyInfo.GetValue(dataItem);
|
var currentValue = propertyInfo.GetValue(dataItem);
|
||||||
|
|
@ -211,63 +196,7 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderTimeOnlyEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
private void RenderSpinIntEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||||
{
|
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
|
||||||
var currentValue = propertyInfo.GetValue(dataItem);
|
|
||||||
|
|
||||||
if (isNullable)
|
|
||||||
{
|
|
||||||
builder.OpenComponent<DxTimeEdit<TimeOnly?>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", (TimeOnly?)currentValue);
|
|
||||||
builder.AddAttribute(seq++, "TimeChanged", EventCallback.Factory.Create<TimeOnly?>(this, newValue =>
|
|
||||||
{
|
|
||||||
propertyInfo.SetValue(dataItem, newValue);
|
|
||||||
InvokeAsync(StateHasChanged);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.OpenComponent<DxTimeEdit<TimeOnly>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", (TimeOnly)(currentValue ?? TimeOnly.MinValue));
|
|
||||||
builder.AddAttribute(seq++, "TimeChanged", EventCallback.Factory.Create<TimeOnly>(this, newValue =>
|
|
||||||
{
|
|
||||||
propertyInfo.SetValue(dataItem, newValue);
|
|
||||||
InvokeAsync(StateHasChanged);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
builder.CloseComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RenderTimeSpanEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
|
||||||
{
|
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
|
||||||
var currentValue = propertyInfo.GetValue(dataItem);
|
|
||||||
|
|
||||||
if (isNullable)
|
|
||||||
{
|
|
||||||
builder.OpenComponent<DxTimeEdit<TimeSpan?>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", (TimeSpan?)currentValue);
|
|
||||||
builder.AddAttribute(seq++, "TimeChanged", EventCallback.Factory.Create<TimeSpan?>(this, newValue =>
|
|
||||||
{
|
|
||||||
propertyInfo.SetValue(dataItem, newValue);
|
|
||||||
InvokeAsync(StateHasChanged);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder.OpenComponent<DxTimeEdit<TimeSpan>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", (TimeSpan)(currentValue ?? TimeSpan.Zero));
|
|
||||||
builder.AddAttribute(seq++, "TimeChanged", EventCallback.Factory.Create<TimeSpan>(this, newValue =>
|
|
||||||
{
|
|
||||||
propertyInfo.SetValue(dataItem, newValue);
|
|
||||||
InvokeAsync(StateHasChanged);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
builder.CloseComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RenderSpinIntEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
|
||||||
{
|
{
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||||
var currentValue = propertyInfo.GetValue(dataItem);
|
var currentValue = propertyInfo.GetValue(dataItem);
|
||||||
|
|
@ -295,7 +224,7 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderSpinDecimalEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
private void RenderSpinDecimalEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||||
{
|
{
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||||
var currentValue = propertyInfo.GetValue(dataItem);
|
var currentValue = propertyInfo.GetValue(dataItem);
|
||||||
|
|
@ -323,7 +252,7 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderSpinDoubleEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
private void RenderSpinDoubleEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||||
{
|
{
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||||
var currentValue = propertyInfo.GetValue(dataItem);
|
var currentValue = propertyInfo.GetValue(dataItem);
|
||||||
|
|
@ -351,10 +280,9 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderTextBoxEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
private void RenderTextBoxEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||||
{
|
{
|
||||||
var currentValue = propertyInfo.GetValue(dataItem)?.ToString() ?? string.Empty;
|
var currentValue = propertyInfo.GetValue(dataItem)?.ToString() ?? string.Empty;
|
||||||
|
|
||||||
builder.OpenComponent<DxTextBox>(seq++);
|
builder.OpenComponent<DxTextBox>(seq++);
|
||||||
builder.AddAttribute(seq++, "Text", currentValue);
|
builder.AddAttribute(seq++, "Text", currentValue);
|
||||||
builder.AddAttribute(seq++, "TextChanged", EventCallback.Factory.Create<string>(this, newValue =>
|
builder.AddAttribute(seq++, "TextChanged", EventCallback.Factory.Create<string>(this, newValue =>
|
||||||
|
|
@ -365,10 +293,9 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderMemoEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
private void RenderMemoEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||||
{
|
{
|
||||||
var currentValue = propertyInfo.GetValue(dataItem)?.ToString() ?? string.Empty;
|
var currentValue = propertyInfo.GetValue(dataItem)?.ToString() ?? string.Empty;
|
||||||
|
|
||||||
builder.OpenComponent<DxMemo>(seq++);
|
builder.OpenComponent<DxMemo>(seq++);
|
||||||
builder.AddAttribute(seq++, "Text", currentValue);
|
builder.AddAttribute(seq++, "Text", currentValue);
|
||||||
builder.AddAttribute(seq++, "TextChanged", EventCallback.Factory.Create<string>(this, newValue =>
|
builder.AddAttribute(seq++, "TextChanged", EventCallback.Factory.Create<string>(this, newValue =>
|
||||||
|
|
@ -380,40 +307,29 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderComboBoxEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings)
|
private void RenderComboBoxEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings)
|
||||||
{
|
{
|
||||||
var currentValue = propertyInfo.GetValue(dataItem);
|
var currentValue = propertyInfo.GetValue(dataItem);
|
||||||
var propertyType = propertyInfo.PropertyType;
|
var propertyType = propertyInfo.PropertyType;
|
||||||
var underlyingType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
var underlyingType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
||||||
|
|
||||||
// Determine TData type from settings.Data
|
|
||||||
var dataType = settings.Data?.GetType();
|
var dataType = settings.Data?.GetType();
|
||||||
var itemType = typeof(object);
|
var itemType = typeof(object);
|
||||||
if (dataType != null && dataType.IsGenericType)
|
if (dataType?.IsGenericType == true)
|
||||||
{
|
{
|
||||||
var genericArgs = dataType.GetGenericArguments();
|
var genericArgs = dataType.GetGenericArguments();
|
||||||
if (genericArgs.Length > 0)
|
if (genericArgs.Length > 0)
|
||||||
{
|
|
||||||
itemType = genericArgs[0];
|
itemType = genericArgs[0];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle common value types for FK fields
|
|
||||||
if (underlyingType == typeof(int))
|
if (underlyingType == typeof(int))
|
||||||
{
|
|
||||||
RenderComboBoxInt(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
RenderComboBoxInt(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(long))
|
else if (underlyingType == typeof(long))
|
||||||
{
|
|
||||||
RenderComboBoxLong(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
RenderComboBoxLong(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
||||||
}
|
|
||||||
else if (underlyingType == typeof(Guid))
|
else if (underlyingType == typeof(Guid))
|
||||||
{
|
|
||||||
RenderComboBoxGuid(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
RenderComboBoxGuid(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Fallback: render as TextBox with display text
|
|
||||||
var displayText = ResolveComboBoxDisplayText(settings, currentValue ?? new object()) ?? currentValue?.ToString() ?? string.Empty;
|
var displayText = ResolveComboBoxDisplayText(settings, currentValue ?? new object()) ?? currentValue?.ToString() ?? string.Empty;
|
||||||
builder.OpenComponent<DxTextBox>(seq++);
|
builder.OpenComponent<DxTextBox>(seq++);
|
||||||
builder.AddAttribute(seq++, "Text", displayText);
|
builder.AddAttribute(seq++, "Text", displayText);
|
||||||
|
|
@ -422,12 +338,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderComboBoxInt(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
private void RenderComboBoxInt(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||||
{
|
{
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||||
|
var comboType = isNullable
|
||||||
// Create the generic ComboBox type: DxComboBox<TItem, int> or DxComboBox<TItem, int?>
|
|
||||||
var comboType = isNullable
|
|
||||||
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(int?))
|
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(int?))
|
||||||
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(int));
|
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(int));
|
||||||
|
|
||||||
|
|
@ -435,7 +349,7 @@
|
||||||
builder.AddAttribute(seq++, "Data", settings.Data);
|
builder.AddAttribute(seq++, "Data", settings.Data);
|
||||||
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
||||||
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
||||||
|
|
||||||
if (isNullable)
|
if (isNullable)
|
||||||
{
|
{
|
||||||
builder.AddAttribute(seq++, "Value", currentValue as int?);
|
builder.AddAttribute(seq++, "Value", currentValue as int?);
|
||||||
|
|
@ -454,16 +368,14 @@
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderComboBoxLong(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
private void RenderComboBoxLong(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||||
{
|
{
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||||
|
var comboType = isNullable
|
||||||
var comboType = isNullable
|
|
||||||
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(long?))
|
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(long?))
|
||||||
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(long));
|
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(long));
|
||||||
|
|
||||||
|
|
@ -471,7 +383,7 @@
|
||||||
builder.AddAttribute(seq++, "Data", settings.Data);
|
builder.AddAttribute(seq++, "Data", settings.Data);
|
||||||
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
||||||
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
||||||
|
|
||||||
if (isNullable)
|
if (isNullable)
|
||||||
{
|
{
|
||||||
builder.AddAttribute(seq++, "Value", currentValue as long?);
|
builder.AddAttribute(seq++, "Value", currentValue as long?);
|
||||||
|
|
@ -490,16 +402,14 @@
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderComboBoxGuid(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
private void RenderComboBoxGuid(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||||
{
|
{
|
||||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||||
|
var comboType = isNullable
|
||||||
var comboType = isNullable
|
|
||||||
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(Guid?))
|
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(Guid?))
|
||||||
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(Guid));
|
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(Guid));
|
||||||
|
|
||||||
|
|
@ -507,7 +417,7 @@
|
||||||
builder.AddAttribute(seq++, "Data", settings.Data);
|
builder.AddAttribute(seq++, "Data", settings.Data);
|
||||||
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
||||||
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
||||||
|
|
||||||
if (isNullable)
|
if (isNullable)
|
||||||
{
|
{
|
||||||
builder.AddAttribute(seq++, "Value", currentValue as Guid?);
|
builder.AddAttribute(seq++, "Value", currentValue as Guid?);
|
||||||
|
|
@ -526,7 +436,6 @@
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
@ -537,11 +446,9 @@
|
||||||
{
|
{
|
||||||
var seq = 0;
|
var seq = 0;
|
||||||
|
|
||||||
// If column has EditSettings, render based on that
|
|
||||||
switch (settingsType)
|
switch (settingsType)
|
||||||
{
|
{
|
||||||
case EditSettingsType.ComboBox:
|
case EditSettingsType.ComboBox:
|
||||||
// ComboBox columns show resolved display text
|
|
||||||
builder.OpenElement(seq++, "div");
|
builder.OpenElement(seq++, "div");
|
||||||
builder.AddAttribute(seq++, "title", displayText);
|
builder.AddAttribute(seq++, "title", displayText);
|
||||||
builder.AddAttribute(seq++, "class", "info-panel-text-wrapper");
|
builder.AddAttribute(seq++, "class", "info-panel-text-wrapper");
|
||||||
|
|
@ -559,26 +466,6 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case EditSettingsType.DateEdit:
|
|
||||||
RenderDateEditor(builder, ref seq, value, column.DisplayFormat);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case EditSettingsType.TimeEdit:
|
|
||||||
RenderTimeEditor(builder, ref seq, value, column.DisplayFormat);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case EditSettingsType.SpinEdit:
|
|
||||||
builder.OpenElement(seq++, "div");
|
|
||||||
builder.AddAttribute(seq++, "title", displayText);
|
|
||||||
builder.AddAttribute(seq++, "class", "info-panel-text-wrapper");
|
|
||||||
builder.OpenComponent<DxTextBox>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Text", displayText);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.AddAttribute(seq++, "CssClass", "text-end");
|
|
||||||
builder.CloseComponent();
|
|
||||||
builder.CloseElement();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case EditSettingsType.Memo:
|
case EditSettingsType.Memo:
|
||||||
builder.OpenElement(seq++, "div");
|
builder.OpenElement(seq++, "div");
|
||||||
builder.AddAttribute(seq++, "title", displayText);
|
builder.AddAttribute(seq++, "title", displayText);
|
||||||
|
|
@ -591,7 +478,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default: render based on value type
|
// Default by value type
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
case bool boolValue:
|
case bool boolValue:
|
||||||
|
|
@ -617,20 +504,6 @@
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TimeOnly timeOnlyValue:
|
|
||||||
builder.OpenComponent<DxTimeEdit<TimeOnly>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", timeOnlyValue);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TimeSpan timeSpanValue:
|
|
||||||
builder.OpenComponent<DxTimeEdit<TimeSpan>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", timeSpanValue);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case decimal or double or float or int or long or short:
|
case decimal or double or float or int or long or short:
|
||||||
builder.OpenElement(seq++, "div");
|
builder.OpenElement(seq++, "div");
|
||||||
builder.AddAttribute(seq++, "title", displayText);
|
builder.AddAttribute(seq++, "title", displayText);
|
||||||
|
|
@ -656,69 +529,4 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderDateEditor(RenderTreeBuilder builder, ref int seq, object? value, string? displayFormat)
|
|
||||||
{
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case DateTime dateTime:
|
|
||||||
builder.OpenComponent<DxDateEdit<DateTime>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Date", dateTime);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.AddAttribute(seq++, "DisplayFormat", displayFormat ?? "yyyy-MM-dd HH:mm");
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
case DateOnly dateOnly:
|
|
||||||
builder.OpenComponent<DxDateEdit<DateOnly>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Date", dateOnly);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.AddAttribute(seq++, "DisplayFormat", displayFormat ?? "yyyy-MM-dd");
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
case DateTimeOffset dateTimeOffset:
|
|
||||||
builder.OpenComponent<DxDateEdit<DateTimeOffset>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Date", dateTimeOffset);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.AddAttribute(seq++, "DisplayFormat", displayFormat ?? "yyyy-MM-dd HH:mm");
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
builder.OpenComponent<DxTextBox>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Text", value?.ToString() ?? string.Empty);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RenderTimeEditor(RenderTreeBuilder builder, ref int seq, object? value, string? displayFormat)
|
|
||||||
{
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case TimeOnly timeOnly:
|
|
||||||
builder.OpenComponent<DxTimeEdit<TimeOnly>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", timeOnly);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
case TimeSpan timeSpan:
|
|
||||||
builder.OpenComponent<DxTimeEdit<TimeSpan>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", timeSpan);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
case DateTime dateTime:
|
|
||||||
builder.OpenComponent<DxTimeEdit<DateTime>>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Time", dateTime);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
builder.OpenComponent<DxTextBox>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Text", value?.ToString() ?? string.Empty);
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
builder.CloseComponent();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,40 @@ using Microsoft.JSInterop;
|
||||||
|
|
||||||
namespace AyCode.Blazor.Components.Components.Grids;
|
namespace AyCode.Blazor.Components.Components.Grids;
|
||||||
|
|
||||||
public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposable where TDataItem : class
|
/// <summary>
|
||||||
|
/// Interface for InfoPanel to support grid access
|
||||||
|
/// </summary>
|
||||||
|
public interface IInfoPanelBase
|
||||||
|
{
|
||||||
|
void ClearEditMode();
|
||||||
|
void SetEditMode(object editModel);
|
||||||
|
void RefreshData(DxGrid grid, object? dataItem, int visibleIndex = -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Non-generic version of the InfoPanel component
|
||||||
|
/// </summary>
|
||||||
|
public partial class MgGridInfoPanel : ComponentBase, IAsyncDisposable, IInfoPanelBase
|
||||||
{
|
{
|
||||||
[Inject] private IJSRuntime JSRuntime { get; set; } = null!;
|
[Inject] private IJSRuntime JSRuntime { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to show readonly fields when in edit mode. Default is false.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public bool ShowReadOnlyFieldsInEditMode { get; set; } = false;
|
||||||
|
|
||||||
private ElementReference _panelElement;
|
private ElementReference _panelElement;
|
||||||
private IJSObjectReference? _jsModule;
|
|
||||||
private bool _isJsInitialized;
|
private bool _isJsInitialized;
|
||||||
private const int DefaultTopOffset = 300; // Increased from 180 to account for header + tabs + toolbar
|
private const int DefaultTopOffset = 300; // Increased from 180 to account for header + tabs + toolbar
|
||||||
|
|
||||||
private DxGrid? _currentGrid;
|
private DxGrid? _currentGrid;
|
||||||
private TDataItem? _currentDataItem;
|
private object? _currentDataItem;
|
||||||
private int _focusedRowVisibleIndex = -1;
|
private int _focusedRowVisibleIndex = -1;
|
||||||
private List<DxGridDataColumn> _allDataColumns = [];
|
private List<DxGridDataColumn> _allDataColumns = [];
|
||||||
|
|
||||||
// Edit mode state
|
// Edit mode state
|
||||||
private bool _isEditMode;
|
private bool _isEditMode;
|
||||||
private TDataItem? _editModel;
|
private object? _editModel;
|
||||||
|
|
||||||
// Cache for edit settings to avoid repeated lookups
|
// Cache for edit settings to avoid repeated lookups
|
||||||
private readonly Dictionary<string, IEditSettings?> _editSettingsCache = [];
|
private readonly Dictionary<string, IEditSettings?> _editSettingsCache = [];
|
||||||
|
|
@ -52,39 +69,46 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refreshes the InfoPanel with data from the specified grid row (view mode)
|
/// Refreshes the InfoPanel with data from the specified grid row (view mode)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RefreshData(DxGrid grid, TDataItem? dataItem, int visibleIndex = -1)
|
public void RefreshData(DxGrid grid, object? dataItem, int visibleIndex = -1)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(grid);
|
ArgumentNullException.ThrowIfNull(grid);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData called - dataItem type: {dataItem?.GetType().Name ?? "null"}, visibleIndex: {visibleIndex}");
|
||||||
|
|
||||||
_currentGrid = grid;
|
_currentGrid = grid;
|
||||||
_currentDataItem = dataItem;
|
_currentDataItem = dataItem;
|
||||||
_focusedRowVisibleIndex = visibleIndex;
|
_focusedRowVisibleIndex = visibleIndex;
|
||||||
_editSettingsCache.Clear();
|
_editSettingsCache.Clear();
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData - _currentDataItem is null: {_currentDataItem == null}, cast success: {dataItem != null && _currentDataItem != null}");
|
||||||
|
|
||||||
// Ha nem vagyunk edit módban, frissítjük az oszlopokat
|
// Ha nem vagyunk edit módban, frissítjük az oszlopokat
|
||||||
if (!_isEditMode)
|
if (!_isEditMode)
|
||||||
{
|
{
|
||||||
if (_currentGrid != null && _currentDataItem != null)
|
if (_currentGrid != null && _currentDataItem != null)
|
||||||
{
|
{
|
||||||
_allDataColumns = GetAllDataColumns(_currentGrid);
|
_allDataColumns = GetAllDataColumns(_currentGrid);
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData - loaded {_allDataColumns.Count} columns");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_allDataColumns = [];
|
_allDataColumns = [];
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData - cleared columns (grid or dataItem is null)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData - StateHasChanged called");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the InfoPanel to edit mode with the given edit model
|
/// Sets the InfoPanel to edit mode with the given edit model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetEditMode(TDataItem editModel)
|
public void SetEditMode(object editModel)
|
||||||
{
|
{
|
||||||
_editModel = editModel;
|
_editModel = editModel;
|
||||||
_isEditMode = true;
|
_isEditMode = true;
|
||||||
_currentDataItem = editModel;
|
_currentDataItem = _editModel;
|
||||||
|
|
||||||
if (_currentGrid != null)
|
if (_currentGrid != null)
|
||||||
{
|
{
|
||||||
|
|
@ -101,6 +125,7 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
{
|
{
|
||||||
_isEditMode = false;
|
_isEditMode = false;
|
||||||
_editModel = null;
|
_editModel = null;
|
||||||
|
_editSettingsCache.Clear();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,17 +157,12 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
// Ignore disposal errors
|
// Ignore disposal errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_jsModule != null)
|
|
||||||
{
|
|
||||||
await _jsModule.DisposeAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data item to display/edit (EditModel in edit mode, otherwise CurrentDataItem)
|
/// Gets the data item to display/edit (EditModel in edit mode, otherwise CurrentDataItem)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private TDataItem? GetActiveDataItem() => _isEditMode && _editModel != null ? _editModel : _currentDataItem;
|
private object? GetActiveDataItem() => _isEditMode && _editModel != null ? _editModel : _currentDataItem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the display text for a field using the grid's internal formatting.
|
/// Gets the display text for a field using the grid's internal formatting.
|
||||||
|
|
@ -163,9 +183,9 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
|
|
||||||
// Try to resolve display text from EditSettings
|
// Try to resolve display text from EditSettings
|
||||||
var editSettings = GetEditSettings(column.FieldName);
|
var editSettings = GetEditSettings(column.FieldName);
|
||||||
if (editSettings != null)
|
if (editSettings is DxComboBoxSettings comboSettings)
|
||||||
{
|
{
|
||||||
var displayText = ResolveEditSettingsDisplayText(editSettings, value);
|
var displayText = ResolveComboBoxDisplayText(comboSettings, value);
|
||||||
if (!string.IsNullOrEmpty(displayText))
|
if (!string.IsNullOrEmpty(displayText))
|
||||||
return displayText;
|
return displayText;
|
||||||
}
|
}
|
||||||
|
|
@ -206,15 +226,13 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try each EditSettings type
|
// Try each EditSettings type
|
||||||
settings = TryGetEditSettings<DxComboBoxSettings>(fieldName)
|
settings = _currentGrid.GetColumnEditSettings<DxComboBoxSettings>(fieldName)
|
||||||
?? TryGetEditSettings<DxDateEditSettings>(fieldName)
|
?? _currentGrid.GetColumnEditSettings<DxDateEditSettings>(fieldName)
|
||||||
?? TryGetEditSettings<DxTimeEditSettings>(fieldName)
|
?? _currentGrid.GetColumnEditSettings<DxTimeEditSettings>(fieldName)
|
||||||
?? TryGetEditSettings<DxSpinEditSettings>(fieldName)
|
?? _currentGrid.GetColumnEditSettings<DxSpinEditSettings>(fieldName)
|
||||||
?? TryGetEditSettings<DxCheckBoxSettings>(fieldName)
|
?? _currentGrid.GetColumnEditSettings<DxCheckBoxSettings>(fieldName)
|
||||||
?? TryGetEditSettings<DxMemoSettings>(fieldName)
|
?? _currentGrid.GetColumnEditSettings<DxMemoSettings>(fieldName)
|
||||||
?? TryGetEditSettings<DxMaskedInputSettings>(fieldName)
|
?? (IEditSettings?)_currentGrid.GetColumnEditSettings<DxTextBoxSettings>(fieldName);
|
||||||
?? TryGetEditSettings<DxTextBoxSettings>(fieldName)
|
|
||||||
?? (IEditSettings?)TryGetEditSettings<DxDropDownEditSettings>(fieldName);
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
@ -225,33 +243,6 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private T? TryGetEditSettings<T>(string fieldName) where T : class, IEditSettings
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _currentGrid?.GetColumnEditSettings<T>(fieldName);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves display text based on EditSettings type
|
|
||||||
/// </summary>
|
|
||||||
private string? ResolveEditSettingsDisplayText(IEditSettings settings, object value)
|
|
||||||
{
|
|
||||||
return settings switch
|
|
||||||
{
|
|
||||||
DxComboBoxSettings comboSettings => ResolveComboBoxDisplayText(comboSettings, value),
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves the display text from a ComboBox data source
|
|
||||||
/// </summary>
|
|
||||||
private string? ResolveComboBoxDisplayText(DxComboBoxSettings settings, object value)
|
private string? ResolveComboBoxDisplayText(DxComboBoxSettings settings, object value)
|
||||||
{
|
{
|
||||||
if (settings.Data == null || string.IsNullOrEmpty(settings.ValueFieldName) || string.IsNullOrEmpty(settings.TextFieldName))
|
if (settings.Data == null || string.IsNullOrEmpty(settings.ValueFieldName) || string.IsNullOrEmpty(settings.TextFieldName))
|
||||||
|
|
@ -284,7 +275,7 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FormatValue(object? value)
|
private static string FormatValue(object? value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
@ -304,13 +295,16 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DxGridDataColumn> GetAllDataColumns(DxGrid grid)
|
private static List<DxGridDataColumn> GetAllDataColumns(DxGrid grid)
|
||||||
{
|
{
|
||||||
var columns = new List<DxGridDataColumn>();
|
var columns = new List<DxGridDataColumn>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var allColumns = grid.GetDataColumns();
|
var allColumns = grid.GetDataColumns();
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] GetAllDataColumns - grid type: {grid.GetType().Name}, columns count: {allColumns?.Count() ?? 0}");
|
||||||
|
|
||||||
if (allColumns != null)
|
if (allColumns != null)
|
||||||
{
|
{
|
||||||
foreach (var column in allColumns)
|
foreach (var column in allColumns)
|
||||||
|
|
@ -319,15 +313,17 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
||||||
!string.IsNullOrWhiteSpace(dataColumn.FieldName))
|
!string.IsNullOrWhiteSpace(dataColumn.FieldName))
|
||||||
{
|
{
|
||||||
columns.Add(dataColumn);
|
columns.Add(dataColumn);
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] - Column: {dataColumn.FieldName}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Fallback: empty list if GetDataColumns fails
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] GetAllDataColumns error: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[InfoPanel] GetAllDataColumns result: {columns.Count} columns");
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
/* Breakpoint configuration - CHANGE ONLY THESE VALUES */
|
/* Breakpoint configuration - CHANGE ONLY THESE VALUES */
|
||||||
/* 2 column breakpoint: 500px */
|
/* 2 column breakpoint: 500px */
|
||||||
/* 3 column breakpoint: 800px */
|
/* 3 column breakpoint: 800px */
|
||||||
|
/* 4 column breakpoint: 1200px (for 1920px+ screens) */
|
||||||
|
|
||||||
/* Main panel - contained within splitter pane */
|
/* Main panel - contained within splitter pane */
|
||||||
.mg-grid-info-panel {
|
.mg-grid-info-panel {
|
||||||
|
|
@ -61,13 +62,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 3 columns for wider panels (>= 800px) */
|
/* 3 columns for wider panels (800px - 1199px) */
|
||||||
@container infopanel (min-width: 800px) {
|
@container infopanel (min-width: 800px) and (max-width: 1199px) {
|
||||||
.mg-info-panel-grid {
|
.mg-info-panel-grid {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 4 columns for very wide panels (>= 1200px, typically on 1920px+ screens) */
|
||||||
|
@container infopanel (min-width: 1200px) {
|
||||||
|
.mg-info-panel-grid {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mg-info-panel-item {
|
.mg-info-panel-item {
|
||||||
min-width: 0; /* Prevent grid blowout */
|
min-width: 0; /* Prevent grid blowout */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue