Add MgLazyLoadContent, grid layout refactor, tests
- Introduced MgLazyLoadContent component with JS observer for lazy rendering of heavy content. - Refactored MgGridBase for user-specific, customizable layout persistence using JS interop. - Improved MgGridInfoPanel caching and state batching for performance. - Updated PDF viewer to use lazy loading for better UX. - Added AyCode.Blazor.Components.Tests project with bUnit/MSTest grid layout tests. - Updated solution/project files and removed obsolete code. - Minor UI and JS module loading improvements.
This commit is contained in:
parent
271868b4d5
commit
324f171377
|
|
@ -1,40 +1,28 @@
|
|||
using AyCode.Blazor.Components.Components.Grids;
|
||||
using AyCode.Core.Extensions;
|
||||
using AyCode.Core.Interfaces;
|
||||
using AyCode.Interfaces.Entities;
|
||||
using AyCode.Utils.Extensions;
|
||||
using DevExpress.Blazor;
|
||||
using FruitBank.Common.Models;
|
||||
using FruitBankHybrid.Shared.Services.Loggers;
|
||||
using FruitBankHybrid.Shared.Services.SignalRs;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace FruitBankHybrid.Shared.Components.Grids;
|
||||
|
||||
//var a = new GridDevExtremeDataSource(DataSource.AsQueryable().Where(x=>x.IsMeasurable));
|
||||
|
||||
public class FruitBankGridBase<TDataItem> : MgGridBase<SignalRDataSourceObservable<TDataItem>, TDataItem, int, LoggerClient> where TDataItem : class, IId<int>
|
||||
{
|
||||
[Inject] public required LoggedInModel LoggedInModel { get; set; }
|
||||
[Inject] public required IJSRuntime JSRuntime { get; set; }
|
||||
|
||||
//[Parameter] public bool IsMasterGrid { get; set; } = false;
|
||||
[Parameter] public string AutoSaveLayoutName { get; set; }
|
||||
|
||||
private bool _isFirstInitializeParameterCore;
|
||||
private bool _isFirstInitializeParameters;
|
||||
public bool PreRendered { get; set; }
|
||||
|
||||
//public virtual Task ReloadDataFromDb(bool forceReload = false)
|
||||
//{
|
||||
// throw new NotImplementedException();
|
||||
//}
|
||||
/// <summary>
|
||||
/// Override to provide the logged-in user's ID for layout storage
|
||||
/// </summary>
|
||||
protected override int GetLayoutUserId() => LoggedInModel.CustomerDto?.Id ?? 0;
|
||||
|
||||
protected void OnCustomizeElement(GridCustomizeElementEventArgs e)
|
||||
{
|
||||
//if (!IsMasterGrid) e.CssClass = "hideDetailButton";
|
||||
|
||||
if (IsMasterGrid && e.ElementType == GridElementType.DataRow && e.VisibleIndex % 2 == 1 && !e.Grid.IsRowSelected(e.VisibleIndex) && !e.Grid.IsRowFocused(e.VisibleIndex))
|
||||
{
|
||||
e.CssClass = " alt-item";
|
||||
|
|
@ -48,28 +36,21 @@ public class FruitBankGridBase<TDataItem> : MgGridBase<SignalRDataSourceObservab
|
|||
if (e.ElementType == GridElementType.HeaderCell)
|
||||
{
|
||||
e.Style = "background-color: #E6E6E6;";
|
||||
//e.CssClass = "header-bold";
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task SetParametersAsyncCore(ParameterView parameters)
|
||||
{
|
||||
await base.SetParametersAsyncCore(parameters);
|
||||
|
||||
if (!_isFirstInitializeParameterCore)
|
||||
{
|
||||
//if (typeof(TDataItem) is IId<Guid> || typeof(TDataItem) is IId<int>)
|
||||
KeyFieldName = "Id";
|
||||
|
||||
//base.DataItemDeleting = EventCallback.Factory.Create<GridDataItemDeletingEventArgs>(this, OnItemDeleting);
|
||||
//base.EditModelSaving = EventCallback.Factory.Create<GridEditModelSavingEventArgs>(this, OnItemSaving);
|
||||
|
||||
CustomizeElement += OnCustomizeElement;
|
||||
|
||||
_isFirstInitializeParameterCore = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
|
@ -102,14 +83,8 @@ public class FruitBankGridBase<TDataItem> : MgGridBase<SignalRDataSourceObservab
|
|||
EditMode = GridEditMode.EditRow;
|
||||
FocusedRowEnabled = true;
|
||||
ColumnResizeMode = GridColumnResizeMode.NextColumn;
|
||||
//VirtualScrollingEnabled = IsMasterGrid;
|
||||
PageSizeSelectorVisible = true;
|
||||
|
||||
if (AutoSaveLayoutName.IsNullOrWhiteSpace()) AutoSaveLayoutName = $"Grid{typeof(TDataItem).Name}";
|
||||
|
||||
LayoutAutoLoading = Grid_LayoutAutoLoading;
|
||||
LayoutAutoSaving = Grid_LayoutAutoSaving;
|
||||
|
||||
_isFirstInitializeParameters = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -117,55 +92,5 @@ public class FruitBankGridBase<TDataItem> : MgGridBase<SignalRDataSourceObservab
|
|||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
base.OnAfterRender(firstRender);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
//PreRendered = true;
|
||||
//StateHasChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async Task Grid_LayoutAutoLoading(GridPersistentLayoutEventArgs e)
|
||||
{
|
||||
var masterDetailName = IsMasterGrid ? "Master" : ParentDataItem!.GetType().Name;
|
||||
e.Layout = await LoadLayoutFromLocalStorageAsync($"{AutoSaveLayoutName}_{masterDetailName}_AutoSave_{LoggedInModel.CustomerDto?.Id ?? 0}");
|
||||
}
|
||||
|
||||
private async Task Grid_LayoutAutoSaving(GridPersistentLayoutEventArgs e)
|
||||
{
|
||||
var masterDetailName = IsMasterGrid ? "Master" : ParentDataItem!.GetType().Name;
|
||||
await SaveLayoutToLocalStorageAsync(e.Layout, $"{AutoSaveLayoutName}_{masterDetailName}_AutoSave_{LoggedInModel.CustomerDto?.Id ?? 0}");
|
||||
}
|
||||
|
||||
async Task<GridPersistentLayout?> LoadLayoutFromLocalStorageAsync(string localStorageKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = await JSRuntime.InvokeAsync<string>("localStorage.getItem", localStorageKey);
|
||||
|
||||
if (!json.IsNullOrWhiteSpace()) return json.JsonTo<GridPersistentLayout>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Mute exceptions for the server prerender stage
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
async Task SaveLayoutToLocalStorageAsync(GridPersistentLayout layout, string localStorageKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = layout.ToJson();
|
||||
await JSRuntime.InvokeVoidAsync("localStorage.setItem", localStorageKey, json);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Mute exceptions for the server prerender stage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//public abstract class FruitBankObservableGridBase<TDataItem> : MgGridBase<SignalRDataSourceObservable<TDataItem>, TDataItem, int, LoggerClient> where TDataItem : class, IId<int>
|
||||
//{ }
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
@using AyCode.Blazor.Components.Components.Grids
|
||||
@using AyCode.Blazor.Components.Components
|
||||
@using DevExpress.Blazor
|
||||
@using FruitBank.Common.Entities
|
||||
@using System.IO
|
||||
|
|
@ -37,7 +38,7 @@
|
|||
<tr>
|
||||
<th>Név a dokumentumon</th>
|
||||
<th>Termék neve</th>
|
||||
<th class="text-end">Rakl.</th>
|
||||
<th>Rakl.</th>
|
||||
<th class="text-end">Menny.</th>
|
||||
<th class="text-end">Net.súly</th>
|
||||
<th class="text-end">Br.súly</th>
|
||||
|
|
@ -68,8 +69,14 @@
|
|||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div id="pdfContainer" style="width: 100%; height: 800px; overflow-y: auto; margin-top: 30px;">
|
||||
</div>
|
||||
<MgLazyLoadContent @ref="_lazyContentRef"
|
||||
MinHeight="800px"
|
||||
RootMargin="50px"
|
||||
OnContentVisible="OnPdfContainerVisibleAsync"
|
||||
ContainerStyle="margin-top: 30px;">
|
||||
<div id="pdfContainer" style="width: 100%; height: 800px; overflow-y: auto;">
|
||||
</div>
|
||||
</MgLazyLoadContent>
|
||||
}
|
||||
</AfterColumnsTemplate>
|
||||
|
||||
|
|
@ -90,11 +97,35 @@
|
|||
"3_BP-30M35_20251113_163816.pdf"
|
||||
];
|
||||
|
||||
private MgLazyLoadContent? _lazyContentRef;
|
||||
private string? _currentPdfToRender;
|
||||
private string _randomPdf;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private async Task OnDataItemChangedAsync(object? dataItem)
|
||||
{
|
||||
// Véletlenszerű PDF kiválasztása minden sor váltáskor
|
||||
var randomPdf = _pdfFiles[Random.Shared.Next(_pdfFiles.Length)];
|
||||
var pdfUrls = new[] { $"_content/FruitBankHybrid.Shared/uploads/{randomPdf}" };
|
||||
await JS.InvokeVoidAsync("pdfViewer.renderPdfs", "pdfContainer", pdfUrls);
|
||||
// Store the PDF to render
|
||||
_randomPdf = _pdfFiles[Random.Shared.Next(_pdfFiles.Length)];
|
||||
_currentPdfToRender = $"_content/FruitBankHybrid.Shared/uploads/{_randomPdf}";
|
||||
|
||||
// If MgLazyLoadContent is already visible, render the PDF immediately
|
||||
if (_lazyContentRef is { IsVisible: true })
|
||||
{
|
||||
await _lazyContentRef.TriggerContentVisibleAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnPdfContainerVisibleAsync()
|
||||
{
|
||||
// Render PDF when container becomes visible OR when data changes
|
||||
if (!string.IsNullOrEmpty(_currentPdfToRender))
|
||||
{
|
||||
var pdfUrls = new[] { _currentPdfToRender };
|
||||
await JS.InvokeVoidAsync("pdfViewer.renderPdfs", "pdfContainer", pdfUrls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
<body class="dxbl-theme-fluent">
|
||||
<Routes @rendermode="InteractiveWebAssembly" />
|
||||
<script src="_content/AyCode.Blazor.Components/js/mgGridInfoPanel.js"></script>
|
||||
<script src="_content/AyCode.Blazor.Components/js/lazyContentObserver.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
|
||||
<script>
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.0.11222.15 d18.0
|
||||
VisualStudioVersion = 18.0.11222.15
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FruitBankHybrid", "FruitBankHybrid\FruitBankHybrid.csproj", "{85ADEDE3-C271-47DF-B273-2EDB32792CEF}"
|
||||
EndProject
|
||||
|
|
@ -31,9 +31,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Maui.Core", "..\..\.
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\..\..\Aycode\Source\AyCode.Blazor\AyCode.Blazor.Components\Components\Grids\MgGridSignalRDataSource.txt = ..\..\..\Aycode\Source\AyCode.Blazor\AyCode.Blazor.Components\Components\Grids\MgGridSignalRDataSource.txt
|
||||
SqlSchemaCompare_Dev_to_Prod.scmp = SqlSchemaCompare_Dev_to_Prod.scmp
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Blazor.Components.Tests", "..\..\..\Aycode\Source\AyCode.Blazor\AyCode.Blazor.Components.Tests\AyCode.Blazor.Components.Tests.csproj", "{5EFD44C6-DC9E-FEB8-F229-3E07C2E224FA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -73,7 +76,6 @@ Global
|
|||
{4E4E4917-1CA3-A7D7-40A8-A24A08673EC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4E4E4917-1CA3-A7D7-40A8-A24A08673EC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4E4E4917-1CA3-A7D7-40A8-A24A08673EC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4E4E4917-1CA3-A7D7-40A8-A24A08673EC1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5CE8B5A7-5390-61E4-33B3-FA5F0B75A168}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5CE8B5A7-5390-61E4-33B3-FA5F0B75A168}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5CE8B5A7-5390-61E4-33B3-FA5F0B75A168}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
@ -96,6 +98,9 @@ Global
|
|||
{F7C67754-A59C-C355-2A10-C614F0585627}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F7C67754-A59C-C355-2A10-C614F0585627}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F7C67754-A59C-C355-2A10-C614F0585627}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{5EFD44C6-DC9E-FEB8-F229-3E07C2E224FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5EFD44C6-DC9E-FEB8-F229-3E07C2E224FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5EFD44C6-DC9E-FEB8-F229-3E07C2E224FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
<script src="_framework/blazor.webview.js" autostart="false"></script>
|
||||
<script src="_content/AyCode.Blazor.Components/js/mgGridInfoPanel.js"></script>
|
||||
<script src="_content/AyCode.Blazor.Components/js/lazyContentObserver.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
|
||||
<script>
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
||||
|
|
|
|||
Loading…
Reference in New Issue