Add collapsible band editor to grid components
Introduce a toggleable "Band-Layout konfigurieren" section in CatalogsGrid.razor and MassDataGrid.razor, allowing users to expand or collapse the band editor UI. Added bandEditorExpanded state to control visibility. Updated CSS to style the new toggle button and its expanded/collapsed states, improving usability and reducing UI clutter.
This commit is contained in:
@@ -25,33 +25,42 @@ else if (items.Count == 0)
|
||||
else
|
||||
{
|
||||
<div class="band-editor">
|
||||
<div class="band-controls">
|
||||
<DxButton Text="Band hinzufügen" Click="AddBand" />
|
||||
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" />
|
||||
<DxButton Text="Layout zurücksetzen" Click="ResetLayoutAsync" />
|
||||
</div>
|
||||
@foreach (var band in bandLayout.Bands)
|
||||
<button class="band-editor-toggle" @onclick="() => bandEditorExpanded = !bandEditorExpanded">
|
||||
<span class="band-editor-toggle-icon @(bandEditorExpanded ? "expanded" : "")">►</span>
|
||||
<span>Band-Layout konfigurieren</span>
|
||||
</button>
|
||||
@if (bandEditorExpanded)
|
||||
{
|
||||
<div class="band-row">
|
||||
<DxTextBox Text="@band.Caption" TextChanged="@(value => UpdateBandCaption(band, value))" />
|
||||
<DxButton Text="Entfernen" Click="@(() => RemoveBand(band))" />
|
||||
<div class="band-editor-body">
|
||||
<div class="band-controls">
|
||||
<DxButton Text="Band hinzufügen" Click="AddBand" />
|
||||
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" />
|
||||
<DxButton Text="Layout zurücksetzen" Click="ResetLayoutAsync" />
|
||||
</div>
|
||||
@foreach (var band in bandLayout.Bands)
|
||||
{
|
||||
<div class="band-row">
|
||||
<DxTextBox Text="@band.Caption" TextChanged="@(value => UpdateBandCaption(band, value))" />
|
||||
<DxButton Text="Entfernen" Click="@(() => RemoveBand(band))" />
|
||||
</div>
|
||||
}
|
||||
<DxFormLayout CssClass="band-columns" ColCount="2">
|
||||
@foreach (var column in columnDefinitions)
|
||||
{
|
||||
<DxFormLayoutItem Caption="@column.Caption">
|
||||
<DxComboBox Data="@bandOptions"
|
||||
TData="BandOption"
|
||||
TValue="string"
|
||||
TextFieldName="Caption"
|
||||
ValueFieldName="Id"
|
||||
Value="@GetColumnBand(column.FieldName)"
|
||||
ValueChanged="@(value => UpdateColumnBand(column.FieldName, value))"
|
||||
Width="100%" />
|
||||
</DxFormLayoutItem>
|
||||
}
|
||||
</DxFormLayout>
|
||||
</div>
|
||||
}
|
||||
<DxFormLayout CssClass="band-columns" ColCount="2">
|
||||
@foreach (var column in columnDefinitions)
|
||||
{
|
||||
<DxFormLayoutItem Caption="@column.Caption">
|
||||
<DxComboBox Data="@bandOptions"
|
||||
TData="BandOption"
|
||||
TValue="string"
|
||||
TextFieldName="Caption"
|
||||
ValueFieldName="Id"
|
||||
Value="@GetColumnBand(column.FieldName)"
|
||||
ValueChanged="@(value => UpdateColumnBand(column.FieldName, value))"
|
||||
Width="100%" />
|
||||
</DxFormLayoutItem>
|
||||
}
|
||||
</DxFormLayout>
|
||||
</div>
|
||||
|
||||
<div class="grid-section">
|
||||
@@ -150,6 +159,7 @@ else
|
||||
private List<BandOption> bandOptions = new();
|
||||
private Dictionary<string, ColumnDefinition> columnLookup = new();
|
||||
private bool gridLayoutApplied;
|
||||
private bool bandEditorExpanded;
|
||||
|
||||
private List<ColumnDefinition> columnDefinitions = new()
|
||||
{
|
||||
|
||||
@@ -37,33 +37,42 @@ else
|
||||
</div>
|
||||
|
||||
<div class="band-editor">
|
||||
<div class="band-controls">
|
||||
<DxButton Text="Band hinzufügen" Click="AddBand" />
|
||||
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" />
|
||||
<DxButton Text="Layout zurücksetzen" Click="ResetLayoutAsync" />
|
||||
</div>
|
||||
@foreach (var band in bandLayout.Bands)
|
||||
<button class="band-editor-toggle" @onclick="() => bandEditorExpanded = !bandEditorExpanded">
|
||||
<span class="band-editor-toggle-icon @(bandEditorExpanded ? "expanded" : "")">►</span>
|
||||
<span>Band-Layout konfigurieren</span>
|
||||
</button>
|
||||
@if (bandEditorExpanded)
|
||||
{
|
||||
<div class="band-row">
|
||||
<DxTextBox Text="@band.Caption" TextChanged="@(value => UpdateBandCaption(band, value))" />
|
||||
<DxButton Text="Entfernen" Click="@(() => RemoveBand(band))" />
|
||||
<div class="band-editor-body">
|
||||
<div class="band-controls">
|
||||
<DxButton Text="Band hinzufügen" Click="AddBand" />
|
||||
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" />
|
||||
<DxButton Text="Layout zurücksetzen" Click="ResetLayoutAsync" />
|
||||
</div>
|
||||
@foreach (var band in bandLayout.Bands)
|
||||
{
|
||||
<div class="band-row">
|
||||
<DxTextBox Text="@band.Caption" TextChanged="@(value => UpdateBandCaption(band, value))" />
|
||||
<DxButton Text="Entfernen" Click="@(() => RemoveBand(band))" />
|
||||
</div>
|
||||
}
|
||||
<DxFormLayout CssClass="band-columns" ColCount="2">
|
||||
@foreach (var column in columnDefinitions)
|
||||
{
|
||||
<DxFormLayoutItem Caption="@column.Caption">
|
||||
<DxComboBox Data="@bandOptions"
|
||||
TData="BandOption"
|
||||
TValue="string"
|
||||
TextFieldName="Caption"
|
||||
ValueFieldName="Id"
|
||||
Value="@GetColumnBand(column.FieldName)"
|
||||
ValueChanged="@(value => UpdateColumnBand(column.FieldName, value))"
|
||||
Width="100%" />
|
||||
</DxFormLayoutItem>
|
||||
}
|
||||
</DxFormLayout>
|
||||
</div>
|
||||
}
|
||||
<DxFormLayout CssClass="band-columns" ColCount="2">
|
||||
@foreach (var column in columnDefinitions)
|
||||
{
|
||||
<DxFormLayoutItem Caption="@column.Caption">
|
||||
<DxComboBox Data="@bandOptions"
|
||||
TData="BandOption"
|
||||
TValue="string"
|
||||
TextFieldName="Caption"
|
||||
ValueFieldName="Id"
|
||||
Value="@GetColumnBand(column.FieldName)"
|
||||
ValueChanged="@(value => UpdateColumnBand(column.FieldName, value))"
|
||||
Width="100%" />
|
||||
</DxFormLayoutItem>
|
||||
}
|
||||
</DxFormLayout>
|
||||
</div>
|
||||
|
||||
<div class="grid-section">
|
||||
@@ -178,6 +187,7 @@ else
|
||||
private List<BandOption> bandOptions = new();
|
||||
private Dictionary<string, ColumnDefinition> columnLookup = new();
|
||||
private bool gridLayoutApplied;
|
||||
private bool bandEditorExpanded;
|
||||
|
||||
private List<ColumnDefinition> columnDefinitions = new()
|
||||
{
|
||||
@@ -281,7 +291,6 @@ else
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
CaptureColumnLayoutFromGrid();
|
||||
@@ -299,41 +308,31 @@ else
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||
return;
|
||||
|
||||
await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser);
|
||||
|
||||
bandLayout = new BandLayout();
|
||||
columnBandAssignments.Clear();
|
||||
UpdateBandOptions();
|
||||
|
||||
foreach (var column in columnDefinitions)
|
||||
column.Width = null;
|
||||
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
_sizeMode = SizeMode.Medium;
|
||||
|
||||
if (gridRef != null)
|
||||
gridRef.LoadLayout(new GridPersistentLayout());
|
||||
gridLayoutApplied = false;
|
||||
|
||||
infoMessage = "Layout zurückgesetzt.";
|
||||
errorMessage = null;
|
||||
}
|
||||
|
||||
private void CaptureColumnLayoutFromGrid()
|
||||
{
|
||||
if (gridRef == null)
|
||||
return;
|
||||
|
||||
if (gridRef == null) return;
|
||||
var layout = gridRef.SaveLayout();
|
||||
bandLayout.GridLayout = layout;
|
||||
bandLayout.SizeMode = _sizeMode;
|
||||
|
||||
var orderedColumns = layout.Columns
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c.FieldName))
|
||||
.OrderBy(c => c.VisibleIndex)
|
||||
.ToList();
|
||||
|
||||
bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList();
|
||||
bandLayout.ColumnWidths = orderedColumns
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c.Width))
|
||||
@@ -407,15 +406,12 @@ else
|
||||
builder.OpenComponent<DxGridCommandColumn>(seq++);
|
||||
builder.AddAttribute(seq++, "Width", "120px");
|
||||
builder.CloseComponent();
|
||||
|
||||
var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var column in columnDefinitions.Where(c => !grouped.Contains(c.FieldName)))
|
||||
BuildDataColumn(builder, ref seq, column);
|
||||
|
||||
foreach (var band in bandLayout.Bands)
|
||||
{
|
||||
if (band.Columns.Count == 0) continue;
|
||||
|
||||
builder.OpenComponent<DxGridBandColumn>(seq++);
|
||||
builder.AddAttribute(seq++, "Caption", band.Caption);
|
||||
builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
|
||||
@@ -458,14 +454,12 @@ else
|
||||
private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e)
|
||||
{
|
||||
if (validationMessageStore == null || editContext == null) return;
|
||||
|
||||
if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.UpdateProcedure))
|
||||
{
|
||||
validationMessageStore.Clear();
|
||||
editContext.NotifyValidationStateChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.CustomerName))
|
||||
{
|
||||
validationMessageStore.Clear(new FieldIdentifier(editContext.Model, nameof(MassDataEditModel.CustomerName)));
|
||||
@@ -483,7 +477,6 @@ else
|
||||
SetPopupHeaderText(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var item = (MassDataReadDto)e.DataItem;
|
||||
e.EditModel = new MassDataEditModel
|
||||
{
|
||||
@@ -505,7 +498,6 @@ else
|
||||
infoMessage = null;
|
||||
validationMessageStore?.Clear();
|
||||
editContext?.NotifyValidationStateChanged();
|
||||
|
||||
var editModel = (MassDataEditModel)e.EditModel;
|
||||
if (!decimal.TryParse(editModel.AmountText, out var amount))
|
||||
{
|
||||
@@ -513,7 +505,6 @@ else
|
||||
e.Cancel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (editModel.IsNew)
|
||||
{
|
||||
var existing = await Api.GetByCustomerNameAsync(editModel.CustomerName);
|
||||
@@ -524,7 +515,6 @@ else
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var dto = new MassDataWriteDto
|
||||
{
|
||||
CustomerName = editModel.CustomerName,
|
||||
@@ -532,7 +522,6 @@ else
|
||||
Category = editModel.Category,
|
||||
StatusFlag = editModel.StatusFlag
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var saved = await Api.UpsertAsync(dto);
|
||||
|
||||
@@ -11,6 +11,7 @@ html, body {
|
||||
.app-light {
|
||||
--band-editor-bg: #f8f9fa;
|
||||
--band-editor-border: #dee2e6;
|
||||
--band-toggle-hover-bg: #e9ecef;
|
||||
}
|
||||
|
||||
.app-dark {
|
||||
@@ -18,6 +19,7 @@ html, body {
|
||||
color: #f1f1f1;
|
||||
--band-editor-bg: #2d2d2d;
|
||||
--band-editor-border: #444444;
|
||||
--band-toggle-hover-bg: #3a3a3a;
|
||||
}
|
||||
|
||||
a, .btn-link {
|
||||
@@ -74,15 +76,47 @@ h1:focus {
|
||||
|
||||
/* Grid Band-Editor */
|
||||
.band-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid var(--band-editor-border);
|
||||
border-radius: 4px;
|
||||
background-color: var(--band-editor-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.band-editor-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.band-editor-toggle:hover {
|
||||
background-color: var(--band-toggle-hover-bg);
|
||||
}
|
||||
|
||||
.band-editor-toggle-icon {
|
||||
font-size: 0.7rem;
|
||||
transition: transform 0.2s ease;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.band-editor-toggle-icon.expanded {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.band-editor-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 0 12px 12px 12px;
|
||||
}
|
||||
|
||||
.band-controls {
|
||||
|
||||
Reference in New Issue
Block a user