Skip to content
This repository has been archived by the owner on Jul 10, 2024. It is now read-only.

Update #204

Merged
merged 11 commits into from
Nov 26, 2020
5 changes: 5 additions & 0 deletions src/BlazorTable.Sample.Shared/NavMenu.razor
Original file line number Diff line number Diff line change
@@ -67,6 +67,11 @@
<span class="oi oi-home" aria-hidden="true"></span> Dynamic Columns
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="ToggleColumnVisibility" Match="NavLinkMatch.All">
<span class="oi oi-eye" aria-hidden="true"></span> Column Visibility
</NavLink>
</li>
</ul>
</div>

69 changes: 69 additions & 0 deletions src/BlazorTable.Sample.Shared/Pages/ToggleColumnVisibility.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
@page "/ToggleColumnVisibility"
@inject HttpClient Http
@using BlazorTable
@using System.ComponentModel

<h1>Toggle Column Visibility</h1>

<button class="btn btn-primary mb-2" @onclick="@(_ => showSearchBar = !showSearchBar)">Toggle Search Bar</button>

<Table TableItem="PersonData" Items="data" PageSize="15" ColumnReorder="true" ShowSearchBar="showSearchBar">
<Column Hideable="true" TableItem="PersonData" Title="Id" Field="@(x => x.id)" Sortable="true" Filterable="true" Width="10%" DefaultSortColumn="true" />
<Column Hideable="true" TableItem="PersonData" Title="Full Name" Field="@(x => x.full_name)" Sortable="true" Filterable="true" Width="20%" />
<Column Hideable="true" TableItem="PersonData" Title="Email" Field="@(x => x.email)" Sortable="true" Filterable="true" Width="20%">
<Template>
<a href="mailto:@context.email">@context.email</a>
</Template>
</Column>
<Column TableItem="PersonData" Title="Paid" Field="@(x => x.paid)" Sortable="true" Filterable="true" Width="10%">
<Template>
@context.paid.ToString()
</Template>
</Column>
<Column Hideable="true" TableItem="PersonData" Title="Price" Field="@(x => x.price)" Sortable="true" Filterable="true" Width="10%" Format="C" Align="Align.Right" />
<Column TableItem="PersonData" Title="Created Date" Field="@(x => x.created_date)" Sortable="true" Filterable="true" Width="10%">
<Template>
@(context.created_date.HasValue ? context.created_date.Value.ToShortDateString() : string.Empty)
</Template>
</Column>
<Column Hideable="true" TableItem="PersonData" Title="Enum" Field="@(x => x.cc_type)" Sortable="true" Filterable="true" Width="10%">
<Template>
@context.cc_type
</Template>
</Column>
<Pager ShowPageNumber="true" ShowTotalCount="true" />
</Table>

@code
{
[Inject]
private HttpClient httpClient { get; set; }

private PersonData[] data;

private bool showSearchBar;

protected override async Task OnInitializedAsync()
{
data = await httpClient.GetFromJsonAsync<PersonData[]>("sample-data/MOCK_DATA.json");
}

public class PersonData
{
public int? id { get; set; }
public string full_name { get; set; }
public string email { get; set; }
public bool? paid { get; set; }
public decimal? price { get; set; }
public CreditCard? cc_type { get; set; }
public DateTime? created_date { get; set; }
}

public enum CreditCard
{
none = 0,
[Description("MasterCard")]
MasterCard = 1,
Visa = 2
}
}
22 changes: 22 additions & 0 deletions src/BlazorTable/Components/Column.razor.cs
Original file line number Diff line number Diff line change
@@ -48,6 +48,12 @@ public string Title
[Parameter]
public bool Filterable { get; set; }

/// <summary>
/// Column can be hidden
/// </summary>
[Parameter]
public bool Hideable { get; set; }

/// <summary>
/// Normal Item Template
/// </summary>
@@ -142,6 +148,22 @@ public string Title
/// </summary>
public bool FilterOpen { get; private set; }

private bool _visible = true;

/// <summary>
/// Column visibility
/// True if current column is visible else false.
/// </summary>
public bool Visible
{
get { return _visible; }
set
{
_visible = value;
Table.Refresh();
}
}

/// <summary>
/// Column Data Type
/// </summary>
171 changes: 113 additions & 58 deletions src/BlazorTable/Components/Table.razor
Original file line number Diff line number Diff line change
@@ -8,11 +8,51 @@

<div class="table-responsive">
<table aria-readonly="@(IsEditMode ? "false" : "true")" role="grid" class="@TableClass" @attributes="UnknownParameters">
@if (ShowSearchBar)
@if (ShowSearchBar || Columns.Exists(column => !column.Visible))
{
<thead role="search">
<tr>
<th colspan="@columnCount"><input type="text" class="form-control form-control-sm float-right" style="width:33%" value="@GlobalSearch" @onchange="@(x => { GlobalSearch = x.Value.ToString(); Update(); })" placeholder="@Localization["GlobalSearch"]" /></th>
<th colspan="@columnCount">
@if (ShowSearchBar)
{
<th colspan="@columnCount"><input type="text" class="form-control form-control-sm float-right" style="width:33%" value="@GlobalSearch" @onchange="@(x => { GlobalSearch = x.Value.ToString(); Update(); })" placeholder="@Localization["GlobalSearch"]" /></th>
}
@if (Columns.Exists(column => !column.Visible))
{
<div class="float-right" @onclick="@(_ => VisibilityMenuOpen = !VisibilityMenuOpen)">
<a href="javascript:;" @ref="VisibilityMenuIconRef">
<span aria-hidden="true">
<img src="_content/BlazorTable/images/plus.png" />
</span>
</a>
</div>
<Popover IsOpen="@VisibilityMenuOpen" Placement="Placement.Bottom" DismissOnNextClick="false" Reference="VisibilityMenuIconRef">
<h3 class="popover-header">Column Visibility</h3>
<div class="popover-body">
<table class="table table-sm table-borderless">
@foreach (IColumn<TableItem> column in Columns.Where(column => !column.Visible))
{
<tr>
<td>
@(column.Title)
</td>
<td>
<div class="float-right" @onclick="@(_ => { column.Visible = true; if (!Columns.Exists(column => !column.Visible)) VisibilityMenuOpen = false;})">
<span aria-hidden="true">
<img src="_content/BlazorTable/images/plus.png" />
</span>
</div>
</td>
</tr>
}
</table>
<button class="btn btn-sm btn-danger" @onclick="@(_ => VisibilityMenuOpen = false)">
Close
</button>
</div>
</Popover>
}
</th>
</tr>
</thead>
}
@@ -25,56 +65,68 @@
}
@foreach (IColumn<TableItem> column in Columns)
{
<th scope="col" style="@(!string.IsNullOrEmpty(column.Width) ? $"width:{column.Width};" : "") user-select: none"
@ondrop="@(() => HandleDrop(column))"
@ondragstart="@(() => HandleDragStart(column))"
ondragover="event.preventDefault();"
draggable="@(ColumnReorder.ToString())"
@key="column"
aria-sort="@column.AriaSort"
class="@(column.Class)">
@if (column.Visible)
{
<th scope="col" style="@(!string.IsNullOrEmpty(column.Width) ? $"width:{column.Width};" : "") user-select: none"
@ondrop="@(() => HandleDrop(column))"
@ondragstart="@(() => HandleDragStart(column))"
ondragover="event.preventDefault();"
draggable="@(ColumnReorder.ToString())"
@key="column"
aria-sort="@column.AriaSort"
class="@(column.Class)">

<div @onclick="@(() => column.SortBy())">
<span>@column.Title</span>
<div @onclick="@(() => column.SortBy())">
<span>@column.Title</span>

@if (column.SortColumn)
{
if (column.SortDescending)
{ <span aria-hidden="true"><img src="_content/BlazorTable/images/sort-desc.png" /></span> }
else
{ <span aria-hidden="true"><img src="_content/BlazorTable/images/sort-asc.png" /></span> }
}
@if (column.SortColumn)
{
if (column.SortDescending)
{ <span aria-hidden="true"><img src="_content/BlazorTable/images/sort-desc.png" /></span> }
else
{ <span aria-hidden="true"><img src="_content/BlazorTable/images/sort-asc.png" /></span> }
}

@if (column.Filterable)
{
<div class="float-right" @onclick="@((x) => column.ToggleFilter())" @onclick:stopPropagation>
<a href="javascript:;" @ref="column.FilterRef" aria-expanded="@(column.FilterOpen ? "true" : "false")" style="text-decoration: none" aria-label="@(column.Filter == null ? "unfiltered" : "filtered")">
<span aria-hidden="true" style="@(column.Filter == null ? "opacity: 0.2;" : string.Empty)">
<img src="_content/BlazorTable/images/filter.png" />
@if (column.Filterable)
{
<div class="float-right" @onclick="@(_ => column.ToggleFilter())" @onclick:stopPropagation>
<a href="javascript:;" @ref="column.FilterRef" aria-expanded="@(column.FilterOpen ? "true" : "false")" style="text-decoration: none" aria-label="@(column.Filter == null ? "unfiltered" : "filtered")">
<span aria-hidden="true" style="@(column.Filter == null ? "opacity: 0.2;" : string.Empty)">
<img src="_content/BlazorTable/images/filter.png" />
</span>
</a>
</div>
<CascadingValue Value="column" Name="Column">
<Popover Reference="@column.FilterRef" IsOpen="@column.FilterOpen" Placement="Placement.Bottom" DismissOnNextClick="false">
<h3 class="popover-header">Filter</h3>
<div class="popover-body">
<FilterManager TableItem="TableItem">
<StringFilter TableItem="TableItem" />
<BooleanFilter TableItem="TableItem" />
<NumberFilter TableItem="TableItem" />
<DateFilter TableItem="TableItem" />
<EnumFilter TableItem="TableItem" />
@if (column.CustomIFilters != null)
{
@column.CustomIFilters(column)
}
</FilterManager>
</div>
</Popover>
</CascadingValue>
}

@if (column.Hideable)
{
<div class="float-right mr-1" @onclick="@(_ => column.Visible = false)" @onclick:stopPropagation>
<span aria-hidden="true">
<img src="_content/BlazorTable/images/minus.png" />
</span>
</a>
</div>
<CascadingValue Value="column" Name="Column">
<Popover Reference="@column.FilterRef" IsOpen="@column.FilterOpen" Placement="Placement.Bottom" DismissOnNextClick="false">
<h3 class="popover-header">Filter</h3>
<div class="popover-body">
<FilterManager TableItem="TableItem">
<StringFilter TableItem="TableItem" />
<BooleanFilter TableItem="TableItem" />
<NumberFilter TableItem="TableItem" />
<DateFilter TableItem="TableItem" />
<EnumFilter TableItem="TableItem" />
@if (column.CustomIFilters != null)
{
@column.CustomIFilters(column)
}
</FilterManager>
</div>
</Popover>
</CascadingValue>
}
</div>
</th>
</div>
}
</div>
</th>
}
}
</tr>
</thead>
@@ -107,17 +159,20 @@

@foreach (IColumn<TableItem> column in Columns)
{
<td @key="column"
style="@(column.Align > 0 ? $"text-align: {column.Align};" : "")"
class="@(column.Class)">
@if (column.Visible)
{
<td @key="column"
style="@(column.Align > 0 ? $"text-align: {column.Align};" : "")"
class="@(column.Class)">

@if (IsEditMode && column.EditTemplate != null)
@column.EditTemplate(item)
else if (column.Template == null)
@column.Render(item)
else
@column.Template(item)
</td>
@if (IsEditMode && column.EditTemplate != null)
@column.EditTemplate(item)
else if (column.Template == null)
@column.Render(item)
else
@column.Template(item)
</td>
}
}
</tr>

10 changes: 10 additions & 0 deletions src/BlazorTable/Components/Table.razor.cs
Original file line number Diff line number Diff line change
@@ -81,6 +81,16 @@ public partial class Table<TableItem> : ITable<TableItem>
[Inject]
private ILogger<ITable<TableItem>> Logger { get; set; }

/// <summary>
/// Ref to visibility menu icon for popover display
/// </summary>
private ElementReference VisibilityMenuIconRef { get; set; }

/// <summary>
/// True if visibility menu is open otherwise false
/// </summary>
private bool VisibilityMenuOpen { get; set; }

/// <summary>
/// Collection of filtered items
/// </summary>
11 changes: 11 additions & 0 deletions src/BlazorTable/Interfaces/IColumn.cs
Original file line number Diff line number Diff line change
@@ -35,6 +35,11 @@ public interface IColumn<TableItem>
/// </summary>
bool Filterable { get; set; }

/// <summary>
/// Column can be hidden
/// </summary>
bool Hideable { get; set; }

/// <summary>
/// Set the format for values if no template
/// </summary>
@@ -45,6 +50,12 @@ public interface IColumn<TableItem>
/// </summary>
bool FilterOpen { get; }

/// <summary>
/// Column visibility
/// True if current column is visible else false.
/// </summary>
bool Visible { get; set; }

/// <summary>
/// Opens/Closes the Filter Panel
/// </summary>