Compare commits

...

4 Commits

Author SHA1 Message Date
OlgunR
d422d841ff Add zebra-striping to grid rows with theme support
Introduced --grid-stripe-bg CSS variable for both light and dark themes to enable zebra-striping in dxbl-grid components. Applied background color to even-numbered grid rows for improved readability.
2026-03-27 14:13:16 +01:00
OlgunR
ea1b2ea6e4 Update band editor label and move page size selector
Changed band editor toggle label to "Layout" in both CatalogsGrid.razor and MassDataGrid.razor. Relocated the page size selector in MassDataGrid.razor to appear after the band editor section for improved layout consistency.
2026-03-27 09:54:23 +01:00
OlgunR
6101561e72 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.
2026-03-27 09:46:15 +01:00
OlgunR
64fb76b9e6 Add theme variables for band editor background and border
Introduce CSS variables for band editor colors in light and dark themes. Update .band-editor to use these variables, enabling automatic adaptation to the active theme.
2026-03-27 09:11:56 +01:00
3 changed files with 131 additions and 82 deletions

View File

@@ -25,33 +25,42 @@ else if (items.Count == 0)
else else
{ {
<div class="band-editor"> <div class="band-editor">
<div class="band-controls"> <button class="band-editor-toggle" @onclick="() => bandEditorExpanded = !bandEditorExpanded">
<DxButton Text="Band hinzufügen" Click="AddBand" /> <span class="band-editor-toggle-icon @(bandEditorExpanded ? "expanded" : "")">&#9658;</span>
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" /> <span>Layout</span>
<DxButton Text="Layout zurücksetzen" Click="ResetLayoutAsync" /> </button>
</div> @if (bandEditorExpanded)
@foreach (var band in bandLayout.Bands)
{ {
<div class="band-row"> <div class="band-editor-body">
<DxTextBox Text="@band.Caption" TextChanged="@(value => UpdateBandCaption(band, value))" /> <div class="band-controls">
<DxButton Text="Entfernen" Click="@(() => RemoveBand(band))" /> <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> </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>
<div class="grid-section"> <div class="grid-section">
@@ -150,6 +159,7 @@ else
private List<BandOption> bandOptions = new(); private List<BandOption> bandOptions = new();
private Dictionary<string, ColumnDefinition> columnLookup = new(); private Dictionary<string, ColumnDefinition> columnLookup = new();
private bool gridLayoutApplied; private bool gridLayoutApplied;
private bool bandEditorExpanded;
private List<ColumnDefinition> columnDefinitions = new() private List<ColumnDefinition> columnDefinitions = new()
{ {

View File

@@ -24,6 +24,46 @@ else if (items.Count == 0)
} }
else else
{ {
<div class="band-editor">
<button class="band-editor-toggle" @onclick="() => bandEditorExpanded = !bandEditorExpanded">
<span class="band-editor-toggle-icon @(bandEditorExpanded ? "expanded" : "")">&#9658;</span>
<span>Layout</span>
</button>
@if (bandEditorExpanded)
{
<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>
}
</div>
<div class="mb-3 page-size-selector"> <div class="mb-3 page-size-selector">
<span class="page-size-label">Datensätze je Seite:</span> <span class="page-size-label">Datensätze je Seite:</span>
<DxComboBox Data="@pageSizeOptions" <DxComboBox Data="@pageSizeOptions"
@@ -36,36 +76,6 @@ else
CssClass="page-size-combo" /> CssClass="page-size-combo" />
</div> </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)
{
<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>
<div class="grid-section"> <div class="grid-section">
<DxGrid Data="@items" <DxGrid Data="@items"
TItem="MassDataReadDto" TItem="MassDataReadDto"
@@ -178,6 +188,7 @@ else
private List<BandOption> bandOptions = new(); private List<BandOption> bandOptions = new();
private Dictionary<string, ColumnDefinition> columnLookup = new(); private Dictionary<string, ColumnDefinition> columnLookup = new();
private bool gridLayoutApplied; private bool gridLayoutApplied;
private bool bandEditorExpanded;
private List<ColumnDefinition> columnDefinitions = new() private List<ColumnDefinition> columnDefinitions = new()
{ {
@@ -281,7 +292,6 @@ else
{ {
if (string.IsNullOrWhiteSpace(layoutUser)) if (string.IsNullOrWhiteSpace(layoutUser))
return; return;
try try
{ {
CaptureColumnLayoutFromGrid(); CaptureColumnLayoutFromGrid();
@@ -299,41 +309,31 @@ else
{ {
if (string.IsNullOrWhiteSpace(layoutUser)) if (string.IsNullOrWhiteSpace(layoutUser))
return; return;
await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser); await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser);
bandLayout = new BandLayout(); bandLayout = new BandLayout();
columnBandAssignments.Clear(); columnBandAssignments.Clear();
UpdateBandOptions(); UpdateBandOptions();
foreach (var column in columnDefinitions) foreach (var column in columnDefinitions)
column.Width = null; column.Width = null;
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
_sizeMode = SizeMode.Medium; _sizeMode = SizeMode.Medium;
if (gridRef != null) if (gridRef != null)
gridRef.LoadLayout(new GridPersistentLayout()); gridRef.LoadLayout(new GridPersistentLayout());
gridLayoutApplied = false; gridLayoutApplied = false;
infoMessage = "Layout zurückgesetzt."; infoMessage = "Layout zurückgesetzt.";
errorMessage = null; errorMessage = null;
} }
private void CaptureColumnLayoutFromGrid() private void CaptureColumnLayoutFromGrid()
{ {
if (gridRef == null) if (gridRef == null) return;
return;
var layout = gridRef.SaveLayout(); var layout = gridRef.SaveLayout();
bandLayout.GridLayout = layout; bandLayout.GridLayout = layout;
bandLayout.SizeMode = _sizeMode; bandLayout.SizeMode = _sizeMode;
var orderedColumns = layout.Columns var orderedColumns = layout.Columns
.Where(c => !string.IsNullOrWhiteSpace(c.FieldName)) .Where(c => !string.IsNullOrWhiteSpace(c.FieldName))
.OrderBy(c => c.VisibleIndex) .OrderBy(c => c.VisibleIndex)
.ToList(); .ToList();
bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList(); bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList();
bandLayout.ColumnWidths = orderedColumns bandLayout.ColumnWidths = orderedColumns
.Where(c => !string.IsNullOrWhiteSpace(c.Width)) .Where(c => !string.IsNullOrWhiteSpace(c.Width))
@@ -407,15 +407,12 @@ else
builder.OpenComponent<DxGridCommandColumn>(seq++); builder.OpenComponent<DxGridCommandColumn>(seq++);
builder.AddAttribute(seq++, "Width", "120px"); builder.AddAttribute(seq++, "Width", "120px");
builder.CloseComponent(); builder.CloseComponent();
var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase); var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var column in columnDefinitions.Where(c => !grouped.Contains(c.FieldName))) foreach (var column in columnDefinitions.Where(c => !grouped.Contains(c.FieldName)))
BuildDataColumn(builder, ref seq, column); BuildDataColumn(builder, ref seq, column);
foreach (var band in bandLayout.Bands) foreach (var band in bandLayout.Bands)
{ {
if (band.Columns.Count == 0) continue; if (band.Columns.Count == 0) continue;
builder.OpenComponent<DxGridBandColumn>(seq++); builder.OpenComponent<DxGridBandColumn>(seq++);
builder.AddAttribute(seq++, "Caption", band.Caption); builder.AddAttribute(seq++, "Caption", band.Caption);
builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder => builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
@@ -458,14 +455,12 @@ else
private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e) private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e)
{ {
if (validationMessageStore == null || editContext == null) return; if (validationMessageStore == null || editContext == null) return;
if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.UpdateProcedure)) if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.UpdateProcedure))
{ {
validationMessageStore.Clear(); validationMessageStore.Clear();
editContext.NotifyValidationStateChanged(); editContext.NotifyValidationStateChanged();
return; return;
} }
if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.CustomerName)) if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.CustomerName))
{ {
validationMessageStore.Clear(new FieldIdentifier(editContext.Model, nameof(MassDataEditModel.CustomerName))); validationMessageStore.Clear(new FieldIdentifier(editContext.Model, nameof(MassDataEditModel.CustomerName)));
@@ -483,7 +478,6 @@ else
SetPopupHeaderText(true); SetPopupHeaderText(true);
return; return;
} }
var item = (MassDataReadDto)e.DataItem; var item = (MassDataReadDto)e.DataItem;
e.EditModel = new MassDataEditModel e.EditModel = new MassDataEditModel
{ {
@@ -505,7 +499,6 @@ else
infoMessage = null; infoMessage = null;
validationMessageStore?.Clear(); validationMessageStore?.Clear();
editContext?.NotifyValidationStateChanged(); editContext?.NotifyValidationStateChanged();
var editModel = (MassDataEditModel)e.EditModel; var editModel = (MassDataEditModel)e.EditModel;
if (!decimal.TryParse(editModel.AmountText, out var amount)) if (!decimal.TryParse(editModel.AmountText, out var amount))
{ {
@@ -513,7 +506,6 @@ else
e.Cancel = true; e.Cancel = true;
return; return;
} }
if (editModel.IsNew) if (editModel.IsNew)
{ {
var existing = await Api.GetByCustomerNameAsync(editModel.CustomerName); var existing = await Api.GetByCustomerNameAsync(editModel.CustomerName);
@@ -524,7 +516,6 @@ else
return; return;
} }
} }
var dto = new MassDataWriteDto var dto = new MassDataWriteDto
{ {
CustomerName = editModel.CustomerName, CustomerName = editModel.CustomerName,
@@ -532,7 +523,6 @@ else
Category = editModel.Category, Category = editModel.Category,
StatusFlag = editModel.StatusFlag StatusFlag = editModel.StatusFlag
}; };
try try
{ {
var saved = await Api.UpsertAsync(dto); var saved = await Api.UpsertAsync(dto);

View File

@@ -7,9 +7,21 @@ html, body {
font-size: var(--global-size); font-size: var(--global-size);
} }
/* Theme-Variablen */
.app-light {
--band-editor-bg: #f8f9fa;
--band-editor-border: #dee2e6;
--band-toggle-hover-bg: #e9ecef;
--grid-stripe-bg: rgba(0, 0, 0, 0.03);
}
.app-dark { .app-dark {
background-color: #1b1b1b; background-color: #1b1b1b;
color: #f1f1f1; color: #f1f1f1;
--band-editor-bg: #2d2d2d;
--band-editor-border: #444444;
--band-toggle-hover-bg: #3a3a3a;
--grid-stripe-bg: rgba(255, 255, 255, 0.04);
} }
a, .btn-link { a, .btn-link {
@@ -66,15 +78,47 @@ h1:focus {
/* Grid Band-Editor */ /* Grid Band-Editor */
.band-editor { .band-editor {
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; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
padding: 12px; padding: 0 12px 12px 12px;
margin-top: 4px;
margin-bottom: 16px;
border: 1px solid #dee2e6;
border-radius: 4px;
background-color: #f8f9fa;
} }
.band-controls { .band-controls {
@@ -99,6 +143,11 @@ h1:focus {
margin-top: 4px; margin-top: 4px;
} }
/* Grid Zebra-Striping */
dxbl-grid tbody tr:nth-child(even) td {
background-color: var(--grid-stripe-bg) !important;
}
/* MassData-spezifisch */ /* MassData-spezifisch */
.page-size-selector { .page-size-selector {
display: flex; display: flex;