Skip to content
36 changes: 23 additions & 13 deletions blazorbootstrap/Components/Grid/Grid.razor
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,31 @@
}

<th class="@string.Join(" ", columnClassList)" style="@string.Join(";", columnStyleList)">
@if (column.Filterable)

@if (isColumnsLoading)
{
<GridColumnFilter EnumFilterSelectText="@EnumFilterSelectText"
FilterButtonColor="@column.FilterButtonColor"
FilterButtonCSSClass="@column.FilterButtonCSSClass"
FilterOperator="@column.FilterOperator"
FilterValue="@column.FilterValue"
FilterWidth="@column.FilterTextboxWidth"
FiltersTranslationProvider="GridFiltersTranslationProviderAsync!"
FixedHeader="@FixedHeader"
GridColumnFilterChanged="async args => await column.OnFilterChangedAsync(args, column)"
PropertyType="@column.GetPropertyType()"
PropertyTypeName="@column.GetPropertyTypeName()"
Unit="@column.FilterTextboxWidthUnit" />
<PlaceholderContainer Animation="PlaceholderAnimation.Glow">
<Placeholder Width="PlaceholderWidth.Col12" />
</PlaceholderContainer>
}
else
{
@if (column.Filterable)
{
<GridColumnFilter EnumFilterSelectText="@EnumFilterSelectText"
FilterButtonColor="@column.FilterButtonColor"
FilterButtonCSSClass="@column.FilterButtonCSSClass"
FilterOperator="@column.FilterOperator"
FilterValue="@column.FilterValue"
FilterWidth="@column.FilterTextboxWidth"
FiltersTranslationProvider="GridFiltersTranslationProviderAsync!"
FixedHeader="@FixedHeader"
GridColumnFilterChanged="async args => await column.OnFilterChangedAsync(args, column)"
PropertyType="@column.GetPropertyType()"
PropertyTypeName="@column.GetPropertyTypeName()"
Unit="@column.FilterTextboxWidthUnit" />
}
}
</th>
}
</tr>
Expand Down
58 changes: 34 additions & 24 deletions blazorbootstrap/Components/Grid/Grid.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public partial class Grid<TItem> : BlazorBootstrapComponentBase

private int? totalCount = null;

private bool pendingPageSizeChanging = false;

private bool isColumnsLoading = false;

#endregion

#region Methods
Expand All @@ -68,17 +72,25 @@ protected override void OnInitialized()
pageSize = PageSize;
selectedItems = SelectedItems!;

isColumnsLoading = true;

base.OnInitialized();
}

protected override Task OnParametersSetAsync()
protected override async Task OnParametersSetAsync()
{
if ((Data is null && DataProvider is null) || (Data is not null && DataProvider is not null))
throw new ArgumentException($"Grid requires either {nameof(Data)} or {nameof(DataProvider)}, but not both or neither.");

if (AllowPaging && PageSize < 0)
throw new ArgumentException($"{nameof(PageSize)} must be greater than zero.");

if (pendingPageSizeChanging)
{
PageSize = pageSize;
return;
}

if (isFirstRenderComplete)
{
// Perform a re-query only if the data source or something else has changed
Expand All @@ -93,8 +105,8 @@ protected override Task OnParametersSetAsync()
{
mustRefreshData = true;
pageSize = PageSize;
_ = ResetPageNumberAsync();
SaveGridSettingsAsync();
await ResetPageNumberAsync();
await SaveGridSettingsAsync();
}

//if (!mustRefreshData && selectedItems != SelectedItems)
Expand All @@ -106,10 +118,11 @@ protected override Task OnParametersSetAsync()
// We want to trigger the first data load when we've collected the initial set of columns
// because they might perform some action, like setting the default sort order.
// It would be wasteful to have to re-query immediately.
return columns.Count > 0 && mustRefreshData ? RefreshDataAsync(false) : Task.CompletedTask;
if (columns.Count > 0 && mustRefreshData)
await RefreshDataAsync(false);
}

return base.OnParametersSetAsync();
await base.OnParametersSetAsync();
}

/// <summary>
Expand Down Expand Up @@ -274,6 +287,9 @@ internal async Task RefreshDataAsync(bool firstRender = false, CancellationToken
await RefreshSelectionAsync();
}

if (firstRender)
isColumnsLoading = false;

requestInProgress = false;

await InvokeAsync(StateHasChanged);
Expand Down Expand Up @@ -461,18 +477,10 @@ private async Task LoadGridSettingsAsync()
if (settings.Filters is not null && settings.Filters.Any())
SetFilters(settings.Filters);

if (settings.PageNumber > 0)
{
if (settings.PageSize > 0 && settings.PageNumber < settings.PageSize)
{
gridCurrentState = new GridState<TItem>(settings.PageNumber, gridCurrentState.Sorting);
pageSize = settings.PageSize;
}
else
{
gridCurrentState = new GridState<TItem>(1, null);
pageSize = 10;
}
if ((settings.PageNumber > 0) && (settings.PageSize > 0))
{
gridCurrentState = new GridState<TItem>(settings.PageNumber, gridCurrentState.Sorting);
pageSize = settings.PageSize;
}
else
{
Expand All @@ -489,17 +497,21 @@ private async Task OnHeaderCheckboxChanged(ChangeEventArgs args)

private async Task OnPageChangedAsync(int newPageNumber)
{
pendingPageSizeChanging = true;
gridCurrentState = new GridState<TItem>(newPageNumber, gridCurrentState.Sorting);
await SaveGridSettingsAsync();
await RefreshDataAsync(false);
pendingPageSizeChanging = false;
}

private async Task OnPageSizeChangedAsync(int newPageSize)
{
pendingPageSizeChanging = true;
pageSize = PageSize = newPageSize;
await ResetPageNumberAsync();
await SaveGridSettingsAsync();
await RefreshDataAsync(false);
pendingPageSizeChanging = false;
}

private async Task OnRowCheckboxChanged(string id, TItem item, ChangeEventArgs args)
Expand Down Expand Up @@ -584,14 +596,14 @@ private async Task RowDoubleClick(TItem item, EventArgs args)
await OnRowDoubleClick.InvokeAsync(new GridRowEventArgs<TItem>(item));
}

private Task SaveGridSettingsAsync()
private async Task SaveGridSettingsAsync()
{
if (!GridSettingsChanged.HasDelegate)
return Task.CompletedTask;
return;

var settings = new GridSettings { PageNumber = AllowPaging ? gridCurrentState.PageIndex : 0, PageSize = AllowPaging ? pageSize : 0, Filters = AllowFiltering ? GetFilters() : null };

return GridSettingsChanged.InvokeAsync(settings);
await GridSettingsChanged.InvokeAsync(settings);
}

private async Task SelectAllItemsInternalAsync(bool selectAll)
Expand All @@ -615,11 +627,9 @@ private async Task SelectAllItemsInternalAsync(bool selectAll)
SelectedItems = selectedItems;
}

private Task SetCheckboxStateAsync(string id, CheckboxState checkboxState)
private async Task SetCheckboxStateAsync(string id, CheckboxState checkboxState)
{
queuedTasks.Enqueue(async () => await SafeInvokeVoidAsync("window.blazorBootstrap.grid.setSelectAllCheckboxState", id, (int)checkboxState));

return Task.CompletedTask;
queuedTasks.Enqueue(async () => await SafeInvokeVoidAsync("window.blazorBootstrap.grid.setSelectAllCheckboxState", id, (int)checkboxState));
}

/// <summary>
Expand Down
27 changes: 3 additions & 24 deletions blazorbootstrap/Components/Grid/GridColumnFilter.razor
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,7 @@
}
</ul>

@if (PropertyTypeName == StringConstants.PropertyTypeNameInt16
|| PropertyTypeName == StringConstants.PropertyTypeNameInt32
|| PropertyTypeName == StringConstants.PropertyTypeNameInt64
|| PropertyTypeName == StringConstants.PropertyTypeNameSingle // float
|| PropertyTypeName == StringConstants.PropertyTypeNameDecimal
|| PropertyTypeName == StringConstants.PropertyTypeNameDouble)
{
<input class="form-control" style="@filterStyle" type="number" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))">
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameDateOnly)
{
<input class="form-control" style="@filterStyle" type="date" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))" />
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameDateTime)
{
<input class="form-control" style="@filterStyle" type="datetime-local" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))" />
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameBoolean)
{
<input class="form-check-input" type="checkbox" value="@filterValue" @onchange="@(async args => await OnFilterValueChangedAsync(args))" />
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameEnum)
@if (PropertyTypeName == StringConstants.PropertyTypeNameEnum)
{
<Dropdown Color="DropdownColor.Light">
<DropdownToggleButton Class="px-1" Style="@filterStyle">
Expand All @@ -77,8 +56,8 @@
</DropdownMenu>
</Dropdown>
}
else // guid or string
else
{
<input class="form-control" style="@filterStyle" type="text" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))" />
@InputFilterTemplate
}
</div>
56 changes: 56 additions & 0 deletions blazorbootstrap/Components/Grid/GridColumnFilter.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,62 @@ or StringConstants.PropertyTypeNameDecimal
}
}

private RenderFragment InputFilterTemplate => builder =>
{
string inputType;
string inputClass;

switch (PropertyTypeName)
{
case StringConstants.PropertyTypeNameInt16:
case StringConstants.PropertyTypeNameInt32:
case StringConstants.PropertyTypeNameInt64:
case StringConstants.PropertyTypeNameSingle:
case StringConstants.PropertyTypeNameDecimal:
case StringConstants.PropertyTypeNameDouble:
inputType = "number";
inputClass = "form-control";
break;
case StringConstants.PropertyTypeNameDateOnly:
inputType = "date";
inputClass = "form-control";
break;
case StringConstants.PropertyTypeNameDateTime:
inputType = "datetime-local";
inputClass = "form-control";
break;
case StringConstants.PropertyTypeNameBoolean:
inputType = "checkbox";
inputClass = "form-check-input";
break;
default:
inputType = "text";
inputClass = "form-control";
break;
}

builder.OpenElement(100, "input");
builder.AddAttribute(101, "class", inputClass);

builder.AddAttribute(102, "type", inputType);
builder.AddAttribute(103, "value", filterValue);

if (PropertyTypeName == StringConstants.PropertyTypeNameBoolean)
{
if ((bool.TryParse(filterValue, out bool isChecked)) && isChecked)
builder.AddAttribute(104, "checked", "checked");

builder.AddAttribute(106, "onchange", async (ChangeEventArgs args) => await OnFilterValueChangedAsync(args));
}
else
{
builder.AddAttribute(105, "style", filterStyle);
builder.AddAttribute(106, "oninput", async (ChangeEventArgs args) => await OnFilterValueChangedAsync(args));
}

builder.CloseElement();
};

private async Task<IEnumerable<FilterOperatorInfo>> GetFilterOperatorsAsync(string propertyTypeName)
{
if (FiltersTranslationProvider is null)
Expand Down
2 changes: 1 addition & 1 deletion blazorbootstrap/Components/Sidebar/Sidebar.razor
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<div class="bb-sidebar-top-row navbar">
<div class="container-fluid ps-3">
<a class="navbar-brand d-flex align-items-center" href="@Href">
<a class="navbar-brand d-flex align-items-center" href="@Href" @onclick=HideNavMenuOnMobile>
@if (!string.IsNullOrWhiteSpace(ImageSrc))
{
<span class="navbar-brand-image me-2">
Expand Down
3 changes: 3 additions & 0 deletions blazorbootstrap/Components/Sidebar/Sidebar.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ public void ToggleSidebar()
internal void HideNavMenuOnMobile()
{
if (isMobile && !collapseNavMenu)
{
collapseNavMenu = true;
StateHasChanged();
}
}

private string GetNavMenuCssClass()
Expand Down
2 changes: 1 addition & 1 deletion blazorbootstrap/Components/Sidebar2/Sidebar2.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<aside @ref="@Element" id="@Id" class="@ClassNames" style="@StyleNames" role="navigation" @attributes="@AdditionalAttributes">
<div class="bb-sidebar2-top-row navbar">
<div class="container-fluid ps-3">
<a class="navbar-brand d-flex align-items-center" href="@Href">
<a class="navbar-brand d-flex align-items-center" href="@Href" @onclick=HideNavMenuOnMobile>
@if (!string.IsNullOrWhiteSpace(ImageSrc))
{
<span class="navbar-brand-image me-2">
Expand Down
3 changes: 3 additions & 0 deletions blazorbootstrap/Components/Sidebar2/Sidebar2.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ public void ToggleSidebar()
internal void HideNavMenuOnMobile()
{
if (isMobile && !collapseNavMenu)
{
collapseNavMenu = true;
StateHasChanged();
}
}

private string GetNavMenuCssClass()
Expand Down