Add fullscreen grid support and PDF preview in info panel

- Added fullscreen mode to grid and info panel components, including toolbar toggle and fullscreen styling.
- Introduced embedded PDF viewing in the info panel using PDF.js and a custom JavaScript viewer.
- Updated interfaces, CSS, and toolbar templates to support new features.
- Added new PDF asset (2_BANK  FRA.pdf) for document preview.
- Minor: Added local settings for Bash permission, fixed text encoding, and improved info panel table layout.
- No code changes in other referenced PDF files; added for informational or asset purposes only.
This commit is contained in:
Loretta 2025-12-19 07:15:54 +01:00
parent 5255917210
commit 4c86914884
4 changed files with 216 additions and 21 deletions

View File

@ -53,6 +53,16 @@ public interface IMgGridBase : IGrid
/// InfoPanel instance for displaying row details (from wrapper) /// InfoPanel instance for displaying row details (from wrapper)
/// </summary> /// </summary>
IInfoPanelBase? InfoPanelInstance { get; } IInfoPanelBase? InfoPanelInstance { get; }
/// <summary>
/// Whether the grid/wrapper is currently in fullscreen mode
/// </summary>
bool IsFullscreen { get; }
/// <summary>
/// Toggles fullscreen mode for the grid (or wrapper if available)
/// </summary>
void ToggleFullscreen();
} }
public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient> : DxGrid, IMgGridBase, IAsyncDisposable public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient> : DxGrid, IMgGridBase, IAsyncDisposable
@ -110,6 +120,27 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
set { /* Set through wrapper */ } set { /* Set through wrapper */ }
} }
/// <inheritdoc />
public bool IsFullscreen => GridWrapper?.IsFullscreen ?? _isStandaloneFullscreen;
private bool _isStandaloneFullscreen;
/// <inheritdoc />
public void ToggleFullscreen()
{
if (GridWrapper != null)
{
// Ha van wrapper, azt váltjuk fullscreen-be
GridWrapper.ToggleFullscreen();
}
else
{
// Ha nincs wrapper, saját fullscreen állapotot használunk
_isStandaloneFullscreen = !_isStandaloneFullscreen;
InvokeAsync(StateHasChanged);
}
}
public MgGridBase() : base() public MgGridBase() : base()
{ {
} }
@ -123,7 +154,38 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
builder.AddAttribute(seq++, "Value", (IMgGridBase)this); builder.AddAttribute(seq++, "Value", (IMgGridBase)this);
builder.AddAttribute(seq++, "ChildContent", (RenderFragment)(contentBuilder => builder.AddAttribute(seq++, "ChildContent", (RenderFragment)(contentBuilder =>
{ {
base.BuildRenderTree(contentBuilder); if (_isStandaloneFullscreen && GridWrapper == null)
{
// Standalone fullscreen mode - wrap in DxWindow
contentBuilder.OpenComponent<DxWindow>(0);
contentBuilder.AddAttribute(1, "Visible", true);
contentBuilder.AddAttribute(2, "VisibleChanged", EventCallback.Factory.Create<bool>(this, visible =>
{
if (!visible)
{
_isStandaloneFullscreen = false;
InvokeAsync(StateHasChanged);
}
}));
contentBuilder.AddAttribute(3, "ShowHeader", true);
contentBuilder.AddAttribute(4, "HeaderText", Caption);
contentBuilder.AddAttribute(5, "ShowCloseButton", true);
contentBuilder.AddAttribute(6, "CloseOnEscape", true);
contentBuilder.AddAttribute(7, "ShowFooter", false);
contentBuilder.AddAttribute(8, "Scrollable", true);
contentBuilder.AddAttribute(9, "Width", "100vw");
contentBuilder.AddAttribute(10, "Height", "100vh");
contentBuilder.AddAttribute(11, "CssClass", "mg-fullscreen-window");
contentBuilder.AddAttribute(12, "ChildContent", (RenderFragment)(windowBuilder =>
{
base.BuildRenderTree(windowBuilder);
}));
contentBuilder.CloseComponent();
}
else
{
base.BuildRenderTree(contentBuilder);
}
})); }));
builder.CloseComponent(); builder.CloseComponent();
} }

View File

@ -23,7 +23,7 @@
</Items> </Items>
</DxToolbarItem> </DxToolbarItem>
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Reload data")" BeginGroup="true" Click="ReloadData_Click" IconCssClass="grid-refresh" Enabled="@(!IsSyncing && !_isReloadInProgress && !IsEditing)" /> <DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Reload data")" BeginGroup="true" Click="ReloadData_Click" IconCssClass="grid-refresh" Enabled="@(!IsSyncing && !_isReloadInProgress && !IsEditing)" />
<DxToolbarItem BeginGroup="true"></DxToolbarItem> <DxToolbarItem Text="@(ShowOnlyIcon ? "" : FullscreenButtonText)" Click="Fullscreen_Click" IconCssClass="@FullscreenIconCssClass" Enabled="@(!IsEditing)" />
@ToolbarItemsExtended @ToolbarItemsExtended
} }
</MgGridToolbarBase> </MgGridToolbarBase>
@ -55,6 +55,21 @@
/// </summary> /// </summary>
private bool HasFocusedRow => Grid?.GetFocusedRowIndex() >= 0; private bool HasFocusedRow => Grid?.GetFocusedRowIndex() >= 0;
/// <summary>
/// Whether the grid is currently in fullscreen mode
/// </summary>
private bool IsFullscreenMode => Grid?.IsFullscreen ?? false;
/// <summary>
/// Button text for fullscreen toggle
/// </summary>
private string FullscreenButtonText => IsFullscreenMode ? "Exit Fullscreen" : "Fullscreen";
/// <summary>
/// Icon class for fullscreen toggle button
/// </summary>
private string FullscreenIconCssClass => IsFullscreenMode ? "grid-fullscreen-exit" : "grid-fullscreen";
protected override void OnInitialized() protected override void OnInitialized()
{ {
} }
@ -112,6 +127,11 @@
Grid.ShowColumnChooser(); Grid.ShowColumnChooser();
} }
void Fullscreen_Click()
{
Grid.ToggleFullscreen();
}
async Task ExportXlsxItem_Click() async Task ExportXlsxItem_Click()
{ {
await Grid.ExportToXlsxAsync(ExportFileName); await Grid.ExportToXlsxAsync(ExportFileName);

View File

@ -1,34 +1,35 @@
@using DevExpress.Blazor @using DevExpress.Blazor
<CascadingValue Value="this"> <CascadingValue Value="this">
@if (ShowInfoPanel) @if (_isFullscreen)
{ {
<DxSplitter Width="100%" CssClass="mg-grid-with-info-panel" Orientation="Orientation.Horizontal"> <DxWindow @bind-Visible="_isFullscreen"
<Panes> ShowHeader="true"
<DxSplitterPane> HeaderText="@(_currentGrid?.Caption ?? "Grid")"
@GridContent ShowCloseButton="true"
</DxSplitterPane> CloseOnEscape="true"
<DxSplitterPane Size="@InfoPanelSize" MinSize="0px" MaxSize="100%" AllowCollapse="true" CssClass="mg-info-panel-pane"> ShowFooter="false"
@if (ChildContent != null) Scrollable="false"
{ Width="100vw"
@ChildContent Height="100vh"
} CssClass="mg-fullscreen-window">
else <BodyTemplate>
{ <div class="mg-fullscreen-content">
<MgGridInfoPanel /> @RenderMainContent()
} </div>
</DxSplitterPane> </BodyTemplate>
</Panes> </DxWindow>
</DxSplitter>
} }
else else
{ {
@GridContent @RenderMainContent()
} }
</CascadingValue> </CascadingValue>
@code { @code {
private IInfoPanelBase? _infoPanelInstance; private IInfoPanelBase? _infoPanelInstance;
private IMgGridBase? _currentGrid;
private bool _isFullscreen;
/// <summary> /// <summary>
/// The grid content to display in the left pane /// The grid content to display in the left pane
@ -55,6 +56,11 @@
[Parameter] [Parameter]
public bool ShowInfoPanel { get; set; } = true; public bool ShowInfoPanel { get; set; } = true;
/// <summary>
/// Whether the wrapper is currently in fullscreen mode
/// </summary>
public bool IsFullscreen => _isFullscreen;
/// <summary> /// <summary>
/// Gets or sets the InfoPanel instance for grid-InfoPanel communication /// Gets or sets the InfoPanel instance for grid-InfoPanel communication
/// </summary> /// </summary>
@ -71,4 +77,67 @@
{ {
_infoPanelInstance = infoPanel; _infoPanelInstance = infoPanel;
} }
/// <summary>
/// Registers the grid instance (called by MgGridBase)
/// </summary>
public void RegisterGrid(IMgGridBase grid)
{
_currentGrid = grid;
}
/// <summary>
/// Toggles fullscreen mode
/// </summary>
public void ToggleFullscreen()
{
_isFullscreen = !_isFullscreen;
StateHasChanged();
}
/// <summary>
/// Enters fullscreen mode
/// </summary>
public void EnterFullscreen()
{
_isFullscreen = true;
StateHasChanged();
}
/// <summary>
/// Exits fullscreen mode
/// </summary>
public void ExitFullscreen()
{
_isFullscreen = false;
StateHasChanged();
}
private RenderFragment RenderMainContent() => __builder =>
{
if (ShowInfoPanel)
{
<DxSplitter Width="100%" Height="@(_isFullscreen ? "100%" : null)" CssClass="mg-grid-with-info-panel" Orientation="Orientation.Horizontal">
<Panes>
<DxSplitterPane>
@GridContent
</DxSplitterPane>
<DxSplitterPane Size="@InfoPanelSize" MinSize="0px" MaxSize="100%" AllowCollapse="true" CssClass="mg-info-panel-pane">
@if (ChildContent != null)
{
@ChildContent
}
else
{
<MgGridInfoPanel />
}
</DxSplitterPane>
</Panes>
</DxSplitter>
}
else
{
@GridContent
}
};
} }

View File

@ -115,3 +115,47 @@
.mg-info-panel-pane { .mg-info-panel-pane {
background-color: var(--dxbl-bg-secondary, #f8f9fa); background-color: var(--dxbl-bg-secondary, #f8f9fa);
} }
/* Fullscreen window styling */
.mg-fullscreen-window {
position: fixed !important;
top: 0 !important;
left: 0 !important;
margin: 0 !important;
border-radius: 0 !important;
}
.mg-fullscreen-window .dxbl-window-body {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.mg-fullscreen-content {
flex: 1;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.mg-fullscreen-content .mg-grid-with-info-panel {
flex: 1;
height: 100%;
}
.mg-fullscreen-content .dxbl-grid {
height: 100% !important;
}
/* Fullscreen icon classes */
.grid-fullscreen::before {
content: "\e90c";
font-family: 'devextreme-icons';
}
.grid-fullscreen-exit::before {
content: "\e90d";
font-family: 'devextreme-icons';
}