diff --git a/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor b/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor new file mode 100644 index 0000000..ad2de7e --- /dev/null +++ b/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor @@ -0,0 +1,31 @@ +@typeparam TItem + +@if (ShowFilterPanel && FilterPanel is not null) +{ +
+ @FilterPanel +
+} + +@if (Data is { Count: > 0 }) +{ +
+ @foreach (var item in PagedItems) + { +
+ @CardTemplate(item) +
+ } +
+ + @if (ShowPager && Data.Count > PageSize) + { + + } +} diff --git a/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor.cs b/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor.cs new file mode 100644 index 0000000..9da93bf --- /dev/null +++ b/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor.cs @@ -0,0 +1,111 @@ +using Microsoft.AspNetCore.Components; + +namespace AyCode.Blazor.Components.Components.CardViews; + +/// +/// Generic card view component that displays items in a responsive grid layout. +/// Uses DxGridLayout + DxLayoutBreakpoint for responsive column management +/// and DxPager for optional pagination. +/// +/// The type of data item displayed in each card. +public partial class MgCardView : ComponentBase +{ + /// + /// The collection of items to display as cards. + /// + [Parameter, EditorRequired] + public IReadOnlyList Data { get; set; } = []; + + /// + /// Template for rendering each card's content. + /// + [Parameter, EditorRequired] + public RenderFragment CardTemplate { get; set; } = null!; + + /// + /// Fired when a card is clicked/tapped. + /// + [Parameter] + public EventCallback OnCardClick { get; set; } + + /// + /// Number of columns on extra-small screens (below 576px). Default: 1. + /// + [Parameter] + public int ColumnCountXs { get; set; } = 1; + + /// + /// Number of columns on small screens (576–768px). Default: 2. + /// + [Parameter] + public int ColumnCountSm { get; set; } = 2; + + /// + /// Number of columns on medium+ screens (769px+). Default: 3. + /// + [Parameter] + public int ColumnCountLg { get; set; } = 3; + + /// + /// Whether to show the pager below the cards. Default: false. + /// + [Parameter] + public bool ShowPager { get; set; } + + /// + /// Number of items per page when paging is enabled. Default: 12. + /// + [Parameter] + public int PageSize { get; set; } = 12; + + /// + /// Additional CSS class for the card view container. + /// + [Parameter] + public string? CssClass { get; set; } + + /// + /// Additional CSS class applied to each individual card wrapper. + /// + [Parameter] + public string? CardCssClass { get; set; } + + /// + /// Whether to show the filter panel above the cards. Default: false. + /// + [Parameter] + public bool ShowFilterPanel { get; set; } + + /// + /// Custom content for the filter panel. Rendered above the card grid when ShowFilterPanel is true. + /// + [Parameter] + public RenderFragment? FilterPanel { get; set; } + + private int _activePageIndex; + + private IReadOnlyList PagedItems + { + get + { + if (!ShowPager) + return Data; + + return Data + .Skip(_activePageIndex * PageSize) + .Take(PageSize) + .ToList(); + } + } + + private async Task OnCardClickInternal(TItem item) + { + if (OnCardClick.HasDelegate) + await OnCardClick.InvokeAsync(item); + } + + private void OnActivePageIndexChanged(int newPageIndex) + { + _activePageIndex = newPageIndex; + } +} diff --git a/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor.css b/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor.css new file mode 100644 index 0000000..949e96e --- /dev/null +++ b/AyCode.Blazor.Components/Components/CardViews/MgCardView.razor.css @@ -0,0 +1,40 @@ +.mg-card-grid { + display: grid; + gap: 1rem; + grid-template-columns: repeat(var(--cols-xs, 1), 1fr); +} + +@media (min-width: 576px) { + .mg-card-grid { + grid-template-columns: repeat(var(--cols-sm, 2), 1fr); + } +} + +@media (min-width: 769px) { + .mg-card-grid { + grid-template-columns: repeat(var(--cols-lg, 3), 1fr); + } +} + +.mg-card { + border: 1px solid #dee2e6; + border-radius: 8px; + padding: 16px; + background-color: #fff; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + transition: box-shadow 0.2s ease, transform 0.15s ease; + height: 100%; +} + +.mg-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + transform: translateY(-2px); +} + +.mg-card-filter-panel { + margin-bottom: 12px; + padding: 12px 16px; + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 8px; +}