Compare commits

..

No commits in common. "4eb0d1566c350803a88dd6ff9864dc78a78ea812" and "3d377c542d3b524488bfdccef270e5d9267c7d42" have entirely different histories.

4 changed files with 0 additions and 132 deletions

View File

@ -6,7 +6,6 @@ using AyCode.Core.Loggers;
using AyCode.Services.SignalRs;
using AyCode.Utils.Extensions;
using DevExpress.Blazor;
using DevExpress.Data.Filtering;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;
@ -94,13 +93,6 @@ public interface IMgGridBase : IGrid
/// returns true optimistically when server layouts are enabled (no server round-trip)
/// </summary>
Task<bool> HasUserLayoutAsync();
/// <summary>
/// Applies a per-field quick filter to the grid — delegates to DevExpress <c>SetFieldFilterCriteria</c>,
/// which AND-combines it with the user's filter row (and other fields' quick filters). Passing
/// <paramref name="criteria"/> = null clears this field's filter. See ADR 0004.
/// </summary>
void SetFieldQuickFilter(string fieldName, CriteriaOperator? criteria);
}
public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient> : DxGrid, IMgGridBase, IAsyncDisposable
@ -251,7 +243,6 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
// Body
contentBuilder.OpenElement(12, "div");
contentBuilder.AddAttribute(13, "class", "mg-fullscreen-body");
if (FilterPanel != null) contentBuilder.AddContent(14, FilterPanel);
base.BuildRenderTree(contentBuilder);
contentBuilder.CloseElement(); // body div
@ -263,7 +254,6 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
contentBuilder.OpenElement(0, "div");
contentBuilder.SetKey(_gridRenderKey);
contentBuilder.AddAttribute(1, "style", "display: contents;");
if (FilterPanel != null) contentBuilder.AddContent(2, FilterPanel);
base.BuildRenderTree(contentBuilder);
contentBuilder.CloseElement();
}
@ -293,13 +283,6 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
[Parameter] public string Caption { get; set; } = typeof(TDataItem).Name;
/// <summary>
/// Optional quick-filter panel rendered above the grid (typically a <c>GridFilterPanel</c> hosting
/// <see cref="MgTagBox{TData,TValue}"/> controls). Rendered within this grid's <see cref="IMgGridBase"/>
/// cascade, so the controls can reach this grid and apply per-field filters. See ADR 0004.
/// </summary>
[Parameter] public RenderFragment? FilterPanel { get; set; }
/// <summary>
/// Name for auto-saving/loading grid layout. If not set, defaults to "Grid{TDataItem.Name}"
/// </summary>
@ -1021,10 +1004,6 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
return _dataSource.LoadDataSourceAsync(false);
}
/// <inheritdoc />
public void SetFieldQuickFilter(string fieldName, CriteriaOperator? criteria)
=> SetFieldFilterCriteria(fieldName, criteria);
/// <summary>
/// Navigates to the previous row in the grid
/// </summary>

View File

@ -1,32 +0,0 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
namespace AyCode.Blazor.Components.Components.Grids;
/// <summary>
/// Base for a grid quick-filter panel — a styled container placed in a grid's <c>FilterPanel</c> slot,
/// hosting <see cref="MgTagBox{TData,TValue}"/> (or other) per-field quick-filter controls above the grid.
/// <para>
/// Framework base. Projects derive a (typically empty) <c>GridFilterPanel</c> per the Mg* seam policy
/// (cf. <c>MgGridBase</c> → project grid). The grid reference reaches the child controls via the grid's own
/// <see cref="IMgGridBase"/> cascade (<c>MgGridBase</c> provides it) — this panel is layout only, no wiring.
/// </para>
/// </summary>
public class MgGridFilterPanelBase : ComponentBase
{
/// <summary>The quick-filter controls (e.g. <see cref="MgTagBox{TData,TValue}"/>) to render in the panel.</summary>
[Parameter] public RenderFragment? ChildContent { get; set; }
/// <summary>Extra CSS classes appended to the panel container.</summary>
[Parameter] public string? CssClass { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", string.IsNullOrWhiteSpace(CssClass)
? "mg-grid-filter-panel"
: $"mg-grid-filter-panel {CssClass}");
builder.AddContent(2, ChildContent);
builder.CloseElement();
}
}

View File

@ -1,77 +0,0 @@
using AyCode.Blazor.Components.Components.Grids;
using DevExpress.Blazor;
using DevExpress.Data.Filtering;
using Microsoft.AspNetCore.Components;
namespace AyCode.Blazor.Components.Components;
/// <summary>
/// <c>DxTagBox</c> seam (per the Mg* "derive every DevExpress component" policy) with an added grid
/// quick-filter capability.
/// <para>
/// When placed inside a grid's <c>FilterPanel</c>, changing the selection applies a per-field
/// <c>IN(FieldName, selected...)</c> filter to the grid via <see cref="IMgGridBase.SetFieldQuickFilter"/>.
/// DevExpress AND-combines it with the user's filter row (it uses <c>SetFieldFilterCriteria</c>, NOT
/// <c>SetFilterCriteria</c>) — see ADR 0004.
/// </para>
/// The selection is managed internally, so the consumer writes just
/// <c>&lt;MgTagBox FieldName="..." Data="..." /&gt;</c> — no <c>@bind-Values</c> / backing field needed.
/// Outside a grid filter panel (no <see cref="Grid"/> cascade) it behaves as a plain tag box.
/// </summary>
/// <typeparam name="TData">Data item type (as for <c>DxTagBox</c>).</typeparam>
/// <typeparam name="TValue">Selected value type (as for <c>DxTagBox</c>).</typeparam>
public class MgTagBox<TData, TValue> : DxTagBox<TData, TValue>
{
/// <summary>
/// Grid column field this quick-filter targets. Consistent with <c>DxGridDataColumn.FieldName</c> and the
/// <c>SetFieldFilterCriteria(fieldName, ...)</c> parameter. When null/empty, no grid filter is applied.
/// </summary>
[Parameter] public string? FieldName { get; set; }
/// <summary>
/// The grid to filter — supplied by the grid's <c>FilterPanel</c> cascade (<c>MgGridBase</c> provides the
/// <see cref="IMgGridBase"/> cascade). Null when the tag box is not inside a grid filter panel.
/// </summary>
[CascadingParameter] public IMgGridBase? Grid { get; set; }
private bool _initialized;
private IEnumerable<TValue>? _selected;
protected override void OnParametersSet()
{
base.OnParametersSet();
// Own the selection internally so the consumer needs no @bind-Values.
// COMPILE-VERIFY (DevExpress version specific): Blazor re-applies base.Values / base.ValuesChanged from the
// consumer markup on every parameter set, so we re-assert ours here each time. First set adopts any initial Values.
if (!_initialized)
{
_selected = Values;
_initialized = true;
}
base.Values = _selected;
base.ValuesChanged = EventCallback.Factory.Create<IEnumerable<TValue>>(this, OnSelectionChanged);
}
private void OnSelectionChanged(IEnumerable<TValue>? values)
{
_selected = values;
base.Values = values; // reflect the new selection in the editor
ApplyQuickFilter(values);
StateHasChanged();
}
private void ApplyQuickFilter(IEnumerable<TValue>? values)
{
if (Grid is null || string.IsNullOrWhiteSpace(FieldName)) return;
var selected = values?.Where(v => v is not null).Cast<object>().ToArray() ?? [];
// COMPILE-VERIFY: confirm the InOperator(string, IEnumerable / params object[]) overload on your
// DevExpress.Data version. Empty selection => null => clears this field's filter.
CriteriaOperator? criteria = selected.Length > 0 ? new InOperator(FieldName, selected) : null;
Grid.SetFieldQuickFilter(FieldName, criteria);
}
}

View File

@ -2,8 +2,6 @@
For known issues / bugs see `MGGRID_ISSUES.md`.
> **Server-side grid filtering — design in ADR 0004.** MgGrid `FilterMode` (Local/Server), the criteria-string forwarding, and the `DxTagBox` quick-filter template extension are captured in `AyCode.Core/docs/adr/0004-signalr-datasource-server-side-filtering.md` (in AyCode.Core repo, Status: Proposed). Not yet broken into per-step entries — reference the ADR when implementation begins.
## Priority legend
- **P0** blocker · **P1** important · **P2** nice-to-have · **P3** idea