Add user layout management to grids with toolbar actions
- Introduced separate auto-save and user-save layout storage keys - Added IMgGridBase methods for saving, loading, resetting, and checking user layouts - Updated grid toolbar with "Layout" menu (load, save, reset) and new icons - Improved layout persistence logic and default layout restore - Enabled forced grid re-render on layout reset - Adjusted grid pager and page size defaults - Updated related components to use new storage keys - Fixed minor bugs and set RELEASE log level to Debug
This commit is contained in:
parent
22821a4b27
commit
3a14b570ef
|
|
@ -107,7 +107,7 @@ public static class FruitBankConstClient
|
|||
|
||||
#if RELEASE
|
||||
public static string SystemEmailAddress = "test@touriam.com";
|
||||
public static LogLevel DefaultLogLevelClient = LogLevel.Error;
|
||||
public static LogLevel DefaultLogLevelClient = LogLevel.Debug;
|
||||
#else
|
||||
public static string SystemEmailAddress = "test@touriam.com";
|
||||
public static LogLevel DefaultLogLevelClient = LogLevel.Detail;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<MgGridWithInfoPanel ShowInfoPanel="@IsMasterGrid">
|
||||
<GridContent>
|
||||
<GridProductDto @ref="Grid" DataSource="ProductDtos" CssClass="@GridCss" AutoSaveLayoutName="GridProductDtoTemplate"
|
||||
Logger="_logger" SignalRClient="FruitBankSignalRClient">
|
||||
Logger="_logger" SignalRClient="FruitBankSignalRClient" Caption="Termék(ek)">
|
||||
<Columns>
|
||||
<MgGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending"
|
||||
UrlLink=@(FruitBankConstClient.BaseUrl + "/Admin/Product/Edit/{Id}") />
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ public class FruitBankGridBase<TDataItem> : MgGridBase<SignalRDataSourceObservab
|
|||
AutoCollapseDetailRow = true;
|
||||
AutoExpandAllGroupRows = false;
|
||||
|
||||
PagerVisible = IsMasterGrid;
|
||||
PageSize = IsMasterGrid ? (SizeMode == DevExpress.Blazor.SizeMode.Small ? 23 : 15) : 50;
|
||||
PagerVisible = true;//IsMasterGrid;
|
||||
PageSize = IsMasterGrid ? (SizeMode == DevExpress.Blazor.SizeMode.Small ? 20 : 15) : 10;
|
||||
|
||||
AllowColumnReorder = true;
|
||||
AllowGroup = IsMasterGrid;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
Logger="_logger"
|
||||
CssClass="@GridCss"
|
||||
ValidationEnabled="false"
|
||||
ShowInfoPanel="true"
|
||||
OnGridFocusedRowChanged="Grid_FocusedRowChanged">
|
||||
<Columns>
|
||||
<DxGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending" ReadOnly="true" />
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@
|
|||
</BeforeColumnsTemplate> *@
|
||||
|
||||
<AfterColumnsTemplate Context="ctx">
|
||||
@if (ctx is { IsEditMode: false, DataItem: ShippingDocument doc })
|
||||
@if (ctx.DataItem is not ShippingDocument && ctx.DataItem is not ShippingItem) return;
|
||||
|
||||
@if (ctx is { DataItem: ShippingDocument doc, IsEditMode: false })
|
||||
{
|
||||
<table class="table table-sm table-bordered table-striped" style="margin-top: 35px;">
|
||||
<colgroup>
|
||||
|
|
@ -68,16 +70,16 @@
|
|||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<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>
|
||||
}
|
||||
|
||||
<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>
|
||||
|
||||
@* <FooterTemplate Context="ctx">
|
||||
|
|
@ -108,10 +110,12 @@
|
|||
|
||||
private async Task OnDataItemChangedAsync(object? dataItem)
|
||||
{
|
||||
@if (dataItem is not ShippingDocument && dataItem is not ShippingItem) return;
|
||||
|
||||
// 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 })
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ public class MgGridBase : DxGrid, IMgGridBase
|
|||
/// <inheritdoc />
|
||||
public bool IsFullscreen => false;
|
||||
|
||||
public string LayoutStorageKey { get; }
|
||||
public string AutomaticLayoutStorageKey => $"{AutoSaveLayoutName}_Master_AutoSave_{LoggedInModel.CustomerDto?.Id ?? 0}";
|
||||
|
||||
private string UserLayoutStorageKey => AutomaticLayoutStorageKey.Replace("_AutoSave_", "_UserSave_");
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ToggleFullscreen()
|
||||
|
|
@ -141,8 +143,8 @@ public class MgGridBase : DxGrid, IMgGridBase
|
|||
AutoCollapseDetailRow = true;
|
||||
AutoExpandAllGroupRows = false;
|
||||
|
||||
PagerVisible = IsMasterGrid;
|
||||
PageSize = IsMasterGrid ? (SizeMode == DevExpress.Blazor.SizeMode.Small ? 23 : 15) : 50;
|
||||
PagerVisible = true; //IsMasterGrid;
|
||||
PageSize = IsMasterGrid ? (SizeMode == DevExpress.Blazor.SizeMode.Small ? 20 : 15) : 10;
|
||||
|
||||
AllowColumnReorder = true;
|
||||
AllowGroup = IsMasterGrid;
|
||||
|
|
@ -204,12 +206,12 @@ public class MgGridBase : DxGrid, IMgGridBase
|
|||
|
||||
async Task Grid_LayoutAutoLoading(GridPersistentLayoutEventArgs e)
|
||||
{
|
||||
e.Layout = await LoadLayoutFromLocalStorageAsync($"{AutoSaveLayoutName}_AutoSave_{LoggedInModel.CustomerDto?.Id ?? 0}");
|
||||
e.Layout = await LoadLayoutFromLocalStorageAsync(AutomaticLayoutStorageKey);
|
||||
}
|
||||
|
||||
private async Task Grid_LayoutAutoSaving(GridPersistentLayoutEventArgs e)
|
||||
{
|
||||
await SaveLayoutToLocalStorageAsync(e.Layout, $"{AutoSaveLayoutName}_AutoSave_{LoggedInModel.CustomerDto?.Id ?? 0}");
|
||||
await SaveLayoutToLocalStorageAsync(e.Layout, AutomaticLayoutStorageKey);
|
||||
}
|
||||
|
||||
async Task<GridPersistentLayout?> LoadLayoutFromLocalStorageAsync(string localStorageKey)
|
||||
|
|
@ -227,6 +229,7 @@ public class MgGridBase : DxGrid, IMgGridBase
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
async Task SaveLayoutToLocalStorageAsync(GridPersistentLayout layout, string localStorageKey)
|
||||
{
|
||||
try
|
||||
|
|
@ -243,54 +246,49 @@ public class MgGridBase : DxGrid, IMgGridBase
|
|||
{
|
||||
try
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("localStorage.removeItem", AutoSaveLayoutName);
|
||||
await JSRuntime.InvokeVoidAsync("localStorage.removeItem", AutomaticLayoutStorageKey);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Mute exceptions for the server prerender stage
|
||||
}
|
||||
}
|
||||
async Task ReloadPageButton_ClickAsync()
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task SaveUserLayoutAsync()
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("location.reload");
|
||||
var layout = SaveLayout();
|
||||
await SaveLayoutToLocalStorageAsync(layout, UserLayoutStorageKey);
|
||||
}
|
||||
async Task ResetLayoutButton_ClickAsync()
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task LoadUserLayoutAsync()
|
||||
{
|
||||
var layout = await LoadLayoutFromLocalStorageAsync(UserLayoutStorageKey);
|
||||
if (layout != null)
|
||||
{
|
||||
LoadLayout(layout);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ResetLayoutAsync()
|
||||
{
|
||||
await RemoveLayoutFromLocalStorageAsync();
|
||||
await JSRuntime.InvokeVoidAsync("location.reload");
|
||||
}
|
||||
//public RenderFragment AddCommandColumn()
|
||||
//{
|
||||
// RenderFragment columns = b =>
|
||||
// {
|
||||
// if (!IsMasterGrid)
|
||||
// {
|
||||
// b.OpenComponent(0, typeof(DxGridCommandColumn));
|
||||
// b.CloseComponent();
|
||||
// }
|
||||
// };
|
||||
|
||||
// this.Columns.ApplyChain(x) = AddCommandColumn();
|
||||
|
||||
// return columns;
|
||||
//}
|
||||
|
||||
//private RenderFragment BuildColumnsGrid()
|
||||
//{
|
||||
// PropertyInfo[] props = DataSource.FirstOrDefault().GetType().GetProperties();
|
||||
// RenderFragment columns = b =>
|
||||
// {
|
||||
// foreach (var prop in props)
|
||||
// {
|
||||
// if (prop.PropertyType == typeof(string))
|
||||
// {
|
||||
|
||||
// b.OpenComponent(0, typeof(DxGridDataColumn));
|
||||
// b.AddAttribute(0, "FieldName", prop.Name);
|
||||
// b.CloseComponent();
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// return columns;
|
||||
//}
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> HasUserLayoutAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await JSRuntime.InvokeAsync<string>("localStorage.getItem", UserLayoutStorageKey);
|
||||
return !string.IsNullOrWhiteSpace(value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -161,10 +161,16 @@ h1:focus {
|
|||
.grid-chevron-up,
|
||||
.grid-chevron-down,
|
||||
.grid-column-chooser,
|
||||
.grid-layout,
|
||||
.grid-layout-load,
|
||||
.grid-layout-save,
|
||||
.grid-layout-reset,
|
||||
.grid-export,
|
||||
.grid-export-xlsx,
|
||||
.grid-export-pdf,
|
||||
.grid-refresh {
|
||||
.grid-refresh,
|
||||
.grid-fullscreen,
|
||||
.grid-fullscreen-exit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -178,10 +184,16 @@ h1:focus {
|
|||
.grid-chevron-up::before,
|
||||
.grid-chevron-down::before,
|
||||
.grid-column-chooser::before,
|
||||
.grid-layout::before,
|
||||
.grid-layout-load::before,
|
||||
.grid-layout-save::before,
|
||||
.grid-layout-reset::before,
|
||||
.grid-export::before,
|
||||
.grid-export-xlsx::before,
|
||||
.grid-export-pdf::before,
|
||||
.grid-refresh::before {
|
||||
.grid-refresh::before,
|
||||
.grid-fullscreen::before,
|
||||
.grid-fullscreen-exit::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 1.25rem;
|
||||
|
|
@ -268,6 +280,42 @@ h1:focus {
|
|||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpolyline points='23 4 23 10 17 10'/%3E%3Cpolyline points='1 20 1 14 7 14'/%3E%3Cpath d='M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* Layout icon (sliders/settings) */
|
||||
.grid-layout::before {
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cline x1='4' y1='21' x2='4' y2='14'/%3E%3Cline x1='4' y1='10' x2='4' y2='3'/%3E%3Cline x1='12' y1='21' x2='12' y2='12'/%3E%3Cline x1='12' y1='8' x2='12' y2='3'/%3E%3Cline x1='20' y1='21' x2='20' y2='16'/%3E%3Cline x1='20' y1='12' x2='20' y2='3'/%3E%3Cline x1='1' y1='14' x2='7' y2='14'/%3E%3Cline x1='9' y1='8' x2='15' y2='8'/%3E%3Cline x1='17' y1='16' x2='23' y2='16'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cline x1='4' y1='21' x2='4' y2='14'/%3E%3Cline x1='4' y1='10' x2='4' y2='3'/%3E%3Cline x1='12' y1='21' x2='12' y2='12'/%3E%3Cline x1='12' y1='8' x2='12' y2='3'/%3E%3Cline x1='20' y1='21' x2='20' y2='16'/%3E%3Cline x1='20' y1='12' x2='20' y2='3'/%3E%3Cline x1='1' y1='14' x2='7' y2='14'/%3E%3Cline x1='9' y1='8' x2='15' y2='8'/%3E%3Cline x1='17' y1='16' x2='23' y2='16'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* Layout Load icon (folder open) */
|
||||
.grid-layout-load::before {
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* Layout Save icon (save/floppy) */
|
||||
.grid-layout-save::before {
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z'/%3E%3Cpolyline points='17 21 17 13 7 13 7 21'/%3E%3Cpolyline points='7 3 7 8 15 8'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z'/%3E%3Cpolyline points='17 21 17 13 7 13 7 21'/%3E%3Cpolyline points='7 3 7 8 15 8'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* Layout Reset icon (rotate-ccw) */
|
||||
.grid-layout-reset::before {
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpolyline points='1 4 1 10 7 10'/%3E%3Cpath d='M3.51 15a9 9 0 1 0 2.13-9.36L1 10'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpolyline points='1 4 1 10 7 10'/%3E%3Cpath d='M3.51 15a9 9 0 1 0 2.13-9.36L1 10'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* Fullscreen icon (maximize) */
|
||||
.grid-fullscreen::before {
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cpolyline points='9 21 3 21 3 15'/%3E%3Cline x1='21' y1='3' x2='14' y2='10'/%3E%3Cline x1='3' y1='21' x2='10' y2='14'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cpolyline points='9 21 3 21 3 15'/%3E%3Cline x1='21' y1='3' x2='14' y2='10'/%3E%3Cline x1='3' y1='21' x2='10' y2='14'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* Fullscreen Exit icon (minimize) */
|
||||
.grid-fullscreen-exit::before {
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpolyline points='4 14 10 14 10 20'/%3E%3Cpolyline points='20 10 14 10 14 4'/%3E%3Cline x1='14' y1='10' x2='21' y2='3'/%3E%3Cline x1='3' y1='21' x2='10' y2='14'/%3E%3C/svg%3E");
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpolyline points='4 14 10 14 10 20'/%3E%3Cpolyline points='20 10 14 10 14 4'/%3E%3Cline x1='14' y1='10' x2='21' y2='3'/%3E%3Cline x1='3' y1='21' x2='10' y2='14'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
MgGrid Splitter with InfoPanel
|
||||
InfoPanel sticky via JavaScript
|
||||
|
|
|
|||
Loading…
Reference in New Issue