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

Commit b9ce343

Browse files
Merge pull request #250 from NilsPur/master
Detail Template, Column Reordering, Custom Rows
2 parents 98c94aa + 6dc63aa commit b9ce343

File tree

12 files changed

+309
-50
lines changed

12 files changed

+309
-50
lines changed

src/BlazorTable.Sample.Server/wwwroot/css/site.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ app {
4848

4949
.sidebar {
5050
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
51+
overflow: auto;
5152
}
5253

5354
.sidebar .top-row {

src/BlazorTable.Sample.Shared/NavMenu.razor

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@
7777
<span class="oi oi-eye" aria-hidden="true"></span> Column Visibility
7878
</NavLink>
7979
</li>
80+
<li class="nav-item px-3">
81+
<NavLink class="nav-link" href="CustomRow" Match="NavLinkMatch.All">
82+
<span class="oi oi-align-center" aria-hidden="true"></span> Custom Row
83+
</NavLink>
84+
</li>
8085
</ul>
8186
</div>
8287

src/BlazorTable.Sample.Shared/Pages/Detail.razor

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,28 @@
66

77
<p>The <code>DetailTemplate</code> can be used to display additional details below an item.</p>
88
<br />
9-
<Table TableItem="PersonData" Items="data" PageSize="15" ColumnReorder="true">
9+
<div class="btn-toolbar mb-3">
10+
<div class="btn-group mr-2">
11+
<button class="btn btn-primary" @onclick="_ => Try(() => table.ToggleAllDetailsView(open: true))">Open All</button>
12+
<button class="btn btn-danger" @onclick="_ => Try(() => table.ToggleAllDetailsView(open: false))">Close All</button>
13+
</div>
14+
<div class="input-group mr-1">
15+
<div class="input-group-prepend">
16+
<div class="input-group-text">Row Number:</div>
17+
</div>
18+
<input type="number" @bind="rowNumber" class="form-control">
19+
</div>
20+
<div class="btn-group">
21+
<button class="btn btn-primary" @onclick="_ => Try(() => table.ToggleDetailView(rowNumber, open: true))">Open</button>
22+
<button class="btn btn-danger" @onclick="_ => Try(() => table.ToggleDetailView(rowNumber, open: false))">Close</button>
23+
</div>
24+
</div>
25+
<br />
26+
@if (errorMessage != "")
27+
{
28+
<label>@errorMessage</label>
29+
}
30+
<Table @ref="table" TableItem="PersonData" Items="data" PageSize="15" ColumnReorder="true">
1031
<Column TableItem="PersonData" Title="Id" Field="@(x => x.id)" Sortable="true" Filterable="true" Width="10%" DefaultSortColumn="true" />
1132
<Column TableItem="PersonData" Title="Full Name" Field="@(x => x.full_name)" Sortable="true" Filterable="true" Width="20%" />
1233
<Column TableItem="PersonData" Title="Email" Field="@(x => x.email)" Sortable="true" Filterable="true" Width="20%">
@@ -46,8 +67,14 @@
4667
[Inject]
4768
private HttpClient Http { get; set; }
4869

70+
private ITable<PersonData> table;
71+
4972
private PersonData[] data;
5073

74+
private int rowNumber;
75+
76+
private string errorMessage = "";
77+
5178
protected override async Task OnInitializedAsync()
5279
{
5380
data = await Http.GetFromJsonAsync<PersonData[]>("sample-data/MOCK_DATA.json");
@@ -71,4 +98,17 @@
7198
MasterCard = 1,
7299
Visa = 2
73100
}
101+
102+
private void Try(Action action)
103+
{
104+
try
105+
{
106+
errorMessage = "";
107+
action();
108+
}
109+
catch (Exception e)
110+
{
111+
errorMessage = e.Message;
112+
}
113+
}
74114
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
@page "/CustomRow"
2+
@inject HttpClient Http
3+
@using BlazorTable
4+
@using System.ComponentModel
5+
6+
<h1>Row Template - Custom Row</h1>
7+
8+
<p></p>
9+
10+
<p>Use</p>
11+
12+
<Table @ref="table" TableItem="PersonData" Items="data" PageSize="15" ColumnReorder="true" ShowSearchBar="true">
13+
<Column TableItem="PersonData" Title="Id" Field="@(x => x.id)" Sortable="true" Filterable="true" Width="10%" DefaultSortColumn="true" />
14+
<Column TableItem="PersonData" Title="Full Name" Field="@(x => x.full_name)" Sortable="true" Filterable="true" Width="20%" />
15+
<Column TableItem="PersonData" Title="Email" Field="@(x => x.email)" Sortable="true" Filterable="true" Width="20%">
16+
<Template>
17+
<a href="mailto:@context.email">@context.email</a>
18+
</Template>
19+
</Column>
20+
<Column TableItem="PersonData" Title="Paid" Field="@(x => x.paid)" Sortable="true" Filterable="true" Width="10%">
21+
<Template>
22+
@context.paid.ToString()
23+
</Template>
24+
</Column>
25+
<Column TableItem="PersonData" Title="Price" Field="@(x => x.price)" Sortable="true" Filterable="true" Width="10%" Format="C" Align="Align.Right" />
26+
<Column TableItem="PersonData" Title="Created Date" Field="@(x => x.created_date)" Sortable="true" Filterable="true" Width="10%">
27+
<Template>
28+
@(context.created_date.HasValue ? context.created_date.Value.ToShortDateString() : string.Empty)
29+
</Template>
30+
</Column>
31+
<Column TableItem="PersonData" Title="Enum" Field="@(x => x.cc_type)" Sortable="true" Filterable="true" Width="10%">
32+
<Template>
33+
@context.cc_type
34+
</Template>
35+
</Column>
36+
<CustomRow TableItem="PersonData" IsActiveForItem="x => (x.id % 4) == 0">
37+
<tr>
38+
<td colspan="@(table.Columns.Count)">
39+
<div class="text-center w-100"><b>Custom Row</b></div>
40+
</td>
41+
</tr>
42+
</CustomRow>
43+
<Pager ShowPageNumber="true" ShowTotalCount="true" />
44+
</Table>
45+
46+
@code
47+
{
48+
[Inject]
49+
private HttpClient httpClient { get; set; }
50+
51+
private ITable<PersonData> table;
52+
53+
private PersonData[] data;
54+
55+
private int i = 0;
56+
57+
protected override async Task OnInitializedAsync()
58+
{
59+
data = await httpClient.GetFromJsonAsync<PersonData[]>("sample-data/MOCK_DATA.json");
60+
}
61+
62+
public class PersonData
63+
{
64+
public int? id { get; set; }
65+
public string full_name { get; set; }
66+
public string email { get; set; }
67+
public bool? paid { get; set; }
68+
public decimal? price { get; set; }
69+
public CreditCard? cc_type { get; set; }
70+
public DateTime? created_date { get; set; }
71+
}
72+
73+
public enum CreditCard
74+
{
75+
none = 0,
76+
[Description("MasterCard")]
77+
MasterCard = 1,
78+
Visa = 2
79+
}
80+
}

src/BlazorTable.Sample.Shared/Pages/ToggleColumnVisibility.razor

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
<h1>Toggle Column Visibility</h1>
77

8+
<p>Users can change the visibility of a column if its "Hideable" attribute is set to true.</p>
9+
810
<button class="btn btn-primary mb-2" @onclick="@(_ => showSearchBar = !showSearchBar)">Toggle Search Bar</button>
911

10-
<Table TableItem="PersonData" Items="data" PageSize="15" ColumnReorder="true" ShowSearchBar="showSearchBar">
12+
<Table @ref="table" TableItem="PersonData" Items="data" PageSize="15" ColumnReorder="true" ShowSearchBar="showSearchBar">
1113
<Column Hideable="true" TableItem="PersonData" Title="Id" Field="@(x => x.id)" Sortable="true" Filterable="true" Width="10%" DefaultSortColumn="true" />
1214
<Column Hideable="true" TableItem="PersonData" Title="Full Name" Field="@(x => x.full_name)" Sortable="true" Filterable="true" Width="20%" />
1315
<Column Hideable="true" TableItem="PersonData" Title="Email" Field="@(x => x.email)" Sortable="true" Filterable="true" Width="20%">
@@ -35,17 +37,26 @@
3537
</Table>
3638

3739
@code
38-
{
40+
{
3941
[Inject]
4042
private HttpClient httpClient { get; set; }
4143

44+
private ITable<PersonData> table;
45+
4246
private PersonData[] data;
4347

4448
private bool showSearchBar;
4549

4650
protected override async Task OnInitializedAsync()
4751
{
4852
data = await httpClient.GetFromJsonAsync<PersonData[]>("sample-data/MOCK_DATA.json");
53+
54+
Random random = new Random(123);
55+
56+
foreach (IColumn<PersonData> column in table.Columns)
57+
{
58+
column.Visible = random.Next(2) == 1 ? true : false; // column visibility
59+
}
4960
}
5061

5162
public class PersonData

src/BlazorTable.Sample.Wasm/wwwroot/css/site.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ app {
4848

4949
.sidebar {
5050
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
51+
overflow: auto;
5152
}
5253

5354
.sidebar .top-row {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@namespace BlazorTable
2+
@typeparam TableItem
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Microsoft.AspNetCore.Components;
2+
using System;
3+
4+
namespace BlazorTable
5+
{
6+
/// <summary>
7+
/// A custom row template
8+
/// </summary>
9+
/// <typeparam name="TableItem"></typeparam>
10+
public partial class CustomRow<TableItem> : ComponentBase
11+
{
12+
/// <summary>
13+
/// Parent Table
14+
/// </summary>
15+
[CascadingParameter(Name = "Table")]
16+
public ITable<TableItem> Table { get; set; }
17+
18+
/// <summary>
19+
/// Function that defines if the custom row is active for the TableItem inserted as parameter
20+
/// </summary>
21+
[Parameter]
22+
public Func<TableItem, bool> IsActiveForItem { get; set; }
23+
24+
/// <summary>
25+
/// Content to show
26+
/// </summary>
27+
[Parameter]
28+
public RenderFragment<TableItem> ChildContent { get; set; }
29+
30+
/// <summary>
31+
/// When initialized, add custom row to table
32+
/// </summary>
33+
protected override void OnInitialized()
34+
{
35+
Table.AddCustomRow(this);
36+
}
37+
}
38+
}

src/BlazorTable/Components/Table.razor

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@
7777
@if (column.Visible)
7878
{
7979
<th scope="col" style="@(!string.IsNullOrEmpty(column.Width) ? $"width:{column.Width};" : "") user-select: none"
80-
@ondrop="@(() => HandleDrop(column))"
81-
@ondragstart="@(() => HandleDragStart(column))"
80+
@ondrop="@(_ => HandleDrop(column))"
81+
@ondragstart="@(_ => HandleDragStart(column))"
8282
ondragover="event.preventDefault();"
8383
draggable="@(ColumnReorder.ToString())"
8484
@key="column"
@@ -145,57 +145,76 @@
145145
if (FilteredItems.Any())
146146
{
147147
int i = 0;
148+
bool detailViewActive = _detailTemplate != null;
148149

149150
foreach (TableItem item in FilteredItems)
150151
{
151-
<tr @key="item" aria-selected="@(SelectedItems.Contains(item) ? "true" : null)" class="@RowClass(item) @(SelectedItems.Contains(item) ? "table-active" : "")" @onclick="(() => OnRowClickHandler(item))">
152+
bool rowDisplayed = false;
152153

153-
@{
154-
int locali = i;
155-
bool isOpen = detailsViewOpen.ContainsKey(locali) && detailsViewOpen[locali];
156-
}
157-
@if (_detailTemplate != null)
154+
foreach (CustomRow<TableItem> customRow in CustomRows)
155+
{
156+
if (customRow.IsActiveForItem != null && customRow.IsActiveForItem(item))
158157
{
159-
<td>
160-
<a href="javascript:;" style="text-decoration: none" @onclick="@(() => { detailsViewOpen[locali] = !isOpen; StateHasChanged(); })" title="@Localization["TableDetailsView"]" aria-expanded="@(isOpen ? "true" : "false")">
161-
@if (isOpen)
162-
{<span aria-hidden="true"><img src="_content/BlazorTable/images/minus.png" /></span>}
163-
else
164-
{<span aria-hidden="true"><img src="_content/BlazorTable/images/plus.png" /></span>}
165-
</a>
166-
</td>
158+
@customRow.ChildContent(item)
159+
rowDisplayed = true;
160+
break;
167161
}
162+
}
168163

169-
@foreach (IColumn<TableItem> column in Columns)
170-
{
171-
@if (column.Visible)
172-
{
173-
<td @key="column"
174-
style="@(column.Align > 0 ? $"text-align: {column.Align};" : "")"
175-
class="@(column.Class)">
164+
if (!rowDisplayed)
165+
{
166+
<tr @key="item" aria-selected="@(SelectedItems.Contains(item) ? "true" : null)" class="@RowClass(item) @(SelectedItems.Contains(item) ? "table-active" : "")" @onclick="(() => OnRowClickHandler(item))">
176167

177-
@if (IsEditMode && column.EditTemplate != null)
178-
@column.EditTemplate(item)
179-
else if (column.Template == null)
180-
@column.Render(item)
181-
else
182-
@column.Template(item)
168+
@{
169+
int locali = i;
170+
if (!detailsViewOpen.ContainsKey(locali))
171+
{
172+
detailsViewOpen[locali] = false;
173+
}
174+
}
175+
@if (detailViewActive)
176+
{
177+
<td>
178+
<a href="javascript:;" style="text-decoration: none" @onclick="@(() => { detailsViewOpen[locali] = !detailsViewOpen[locali]; StateHasChanged(); })" title="@Localization["TableDetailsView"]" aria-expanded="@(detailsViewOpen[locali] ? "true" : "false")">
179+
@if (detailsViewOpen[locali])
180+
{<span aria-hidden="true"><img src="_content/BlazorTable/images/minus.png" /></span>}
181+
else
182+
{<span aria-hidden="true"><img src="_content/BlazorTable/images/plus.png" /></span>}
183+
</a>
183184
</td>
184185
}
185-
}
186-
</tr>
187186

188-
if (_detailTemplate != null && isOpen)
189-
{
190-
<tr>
191-
<td></td>
192-
<td colspan="@(Columns.Count)">
193-
@_detailTemplate(item)
194-
</td>
187+
@foreach (IColumn<TableItem> column in Columns)
188+
{
189+
@if (column.Visible)
190+
{
191+
<td @key="column"
192+
style="@(column.Align > 0 ? $"text-align: {column.Align};" : "")"
193+
class="@(column.Class)">
194+
195+
@if (IsEditMode && column.EditTemplate != null)
196+
@column.EditTemplate(item)
197+
else if (column.Template == null)
198+
@column.Render(item)
199+
else
200+
@column.Template(item)
201+
</td>
202+
}
203+
}
195204
</tr>
205+
206+
if (detailViewActive && detailsViewOpen[i])
207+
{
208+
<tr>
209+
<td></td>
210+
<td colspan="@(Columns.Count)">
211+
@_detailTemplate(item)
212+
</td>
213+
</tr>
214+
}
196215
}
197216

198-
{ i = locali + 1; }
217+
{ i++; }
199218
}
200219
}
201220
else if (_emptyDataTemplate != null)

0 commit comments

Comments
 (0)