diff --git a/Oqtane.Client/Modules/Controls/StaticPager.razor b/Oqtane.Client/Modules/Controls/StaticPager.razor new file mode 100644 index 00000000..8fb98c9c --- /dev/null +++ b/Oqtane.Client/Modules/Controls/StaticPager.razor @@ -0,0 +1,499 @@ +@namespace Oqtane.Modules.Controls +@inherits ModuleControlBase +@inject IStringLocalizerFactory LocalizerFactory +@inject IStringLocalizer SharedLocalizer +@typeparam TableItem + +@if (ItemList != null) +{ + @if (!string.IsNullOrEmpty(SearchProperties)) + { +
+ +
+ + + @SharedLocalizer["Reset"] +
+
+ } + + @if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems) + { + + } + @if (Format == "Table" && Row != null) + { +
+ + + @Header + + + @foreach (var item in ItemList) + { + @Row(item) + @if (Detail != null) + { + @Detail(item) + } + } + + + @Footer + +
+
+ } + @if (Format == "Grid" && Row != null) + { + int count = 0; + int rows = 0; + int cols = 0; + if (ItemList != null) + { + if (_columns == 0) + { + count = ItemList.Count(); + rows = 1; + cols = count; + } + else + { + count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns; + rows = count / _columns; + cols = _columns; + } + } +
+ @for (int row = 0; row < rows; row++) + { +
+ @for (int col = 0; col < cols; col++) + { + int index = (row * _columns) + col; + if (index < ItemList.Count()) + { +
@Row(ItemList.ElementAt(index))
+ } + else + { +
 
+ } + } +
+ } +
+ } + @if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems) + { + + } +} + +@code { + private IStringLocalizer Localizer; + private int _pages = 0; + private int _page = 1; + private int _maxItems = 10; + private int _displayPages = 5; + private int _startPage = 0; + private int _endPage = 0; + private int _columns = 0; + private string _search = ""; + + private IEnumerable AllItems; + + [Parameter] + public string Format { get; set; } // Table or Grid + + [Parameter] + public string Toolbar { get; set; } // Top, Bottom or Both + + [Parameter] + public RenderFragment Header { get; set; } = null; // only applicable to Table layouts + + [Parameter] + public RenderFragment Row { get; set; } = null; // required + + [Parameter] + public RenderFragment Footer { get; set; } = null; // only applicable to Table layouts + + [Parameter] + public RenderFragment Detail { get; set; } = null; // only applicable to Table layouts + + [Parameter] + public IEnumerable Items { get; set; } // the IEnumerable data source + + [Parameter] + public string PageSize { get; set; } // number of items to display on a page + + [Parameter] + public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior + + [Parameter] + public string CurrentPage { get; set; } // sets the initial page to display + + [Parameter] + public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection + + [Parameter] + public string Class { get; set; } // class for the containing element - ie. for Table or
for Grid + + [Parameter] + public string RowClass { get; set; } // class for row element - ie.
for Table or
for Grid + + [Parameter] + public string ColumnClass { get; set; } // class for column element - only applicable to Grid format + + [Parameter] + public Action OnPageChange { get; set; } // a method to be executed in the calling component when the page changes + + [Parameter] + public string SearchProperties { get; set; } // comma delimited list of property names to include in search + + [SupplyParameterFromForm(FormName = "PagerForm")] + public string _Search { get => ""; set => _search = value; } + + private IEnumerable ItemList { get; set; } + + protected override void OnInitialized() + { + Localizer = LocalizerFactory.Create(GetType().FullName); + } + + protected override void OnParametersSet() + { + if (string.IsNullOrEmpty(Format)) + { + Format = "Table"; + } + + if (string.IsNullOrEmpty(Toolbar)) + { + Toolbar = "Top"; + } + + if (string.IsNullOrEmpty(Class)) + { + if (Format == "Table") + { + Class = "table table-borderless"; + } + else + { + Class = "container-fluid"; + } + } + + if (string.IsNullOrEmpty(RowClass)) + { + if (Format == "Table") + { + RowClass = ""; + } + else + { + RowClass = "row"; + } + } + + if (string.IsNullOrEmpty(ColumnClass)) + { + if (Format == "Table") + { + ColumnClass = ""; + } + else + { + ColumnClass = "col"; + } + } + + if (PageState.QueryString.ContainsKey("search")) + { + _search = PageState.QueryString["search"]; + } + + if (!string.IsNullOrEmpty(SearchProperties)) + { + AllItems = Items; // only used in search + if (!string.IsNullOrEmpty(_search)) + { + Search(); + } + } + + if (!string.IsNullOrEmpty(PageSize)) + { + _maxItems = int.Parse(PageSize); + } + + if (!string.IsNullOrEmpty(Columns)) + { + _columns = int.Parse(Columns); + } + + if (!string.IsNullOrEmpty(DisplayPages)) + { + _displayPages = int.Parse(DisplayPages); + } + + if (PageState.QueryString.ContainsKey("page")) + { + _page = int.Parse(PageState.QueryString["page"]); + } + else + { + if (!string.IsNullOrEmpty(CurrentPage)) + { + _page = int.Parse(CurrentPage); + } + else + { + _page = 1; + } + } + if (_page < 1) _page = 1; + + _startPage = 0; + _endPage = 0; + + if (Items != null) + { + _pages = (int)Math.Ceiling(Items.Count() / (decimal)_maxItems); + if (_page > _pages) + { + _page = _pages; + } + SetPagerSize(); + } + } + + public void SetPagerSize() + { + _startPage = ((_page - 1) / _displayPages) * _displayPages + 1; + _endPage = _startPage + _displayPages - 1; + if (_endPage > _pages) + { + _endPage = _pages; + } + ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems); + StateHasChanged(); + OnPageChange?.Invoke(_page); + } + + public void UpdateList(int page) + { + _page = page; + SetPagerSize(); + } + + public void SkipPages(string direction) + { + switch (direction) + { + case "forward": + _page = _endPage + 1; + break; + case "back": + _page = _startPage - 1; + break; + } + + SetPagerSize(); + } + + public void NavigateToPage(string direction) + { + switch (direction) + { + case "next": + if (_page < _pages) + { + _page += 1; + } + break; + case "previous": + if (_page > 1) + { + _page -= 1; + } + break; + } + + UpdateList(_page); + } + + public void Search() + { + if (!string.IsNullOrEmpty(_search)) + { + Items = AllItems.Where(item => + { + var values = SearchProperties.Split(',') + .Select(itemType => GetPropertyValue(item, itemType)) + .Where(value => value != null) + .Select(value => value.ToString().ToLower()); + + return values.Any(value => value.Contains(_search.ToLower())); + }).ToList(); + } + else + { + Items = AllItems; + } + _pages = (int)Math.Ceiling(Items.Count() / (decimal)_maxItems); + UpdateList(1); + } + + private object GetPropertyValue(object obj, string propertyName) + { + var index = propertyName.IndexOf("."); + if (index != -1) + { + var propertyInfo = obj.GetType().GetProperty(propertyName.Substring(0, index)); + if (propertyInfo != null) + { + return GetPropertyValue(propertyInfo.GetValue(obj), propertyName.Substring(index + 1)); + } + return null; + } + else + { + var propertyInfo = obj.GetType().GetProperty(propertyName); + if (propertyInfo != null) + { + return propertyInfo.GetValue(obj); + } + return null; + } + } + + public void Reset() + { + _search = ""; + Items = AllItems; + _pages = (int)Math.Ceiling(Items.Count() / (decimal)_maxItems); + UpdateList(1); + } + + private string FormatSearchProperties() + { + var properties = new List(); + foreach (var property in SearchProperties.Split(',', StringSplitOptions.RemoveEmptyEntries)) + { + var index = property.LastIndexOf("."); + if (index != -1) + { + properties.Add(property.Substring(index + 1)); + } + else + { + properties.Add(property); + } + } + return string.Join(",", properties); + } + + private string PageUrl(int page, string search) + { + var parameters = new Dictionary(PageState.QueryString); + if (parameters.ContainsKey("page")) parameters.Remove("page"); + parameters.Add("page", page.ToString()); + if (parameters.ContainsKey("search")) parameters.Remove("search"); + if (!string.IsNullOrEmpty(search)) + { + parameters.Add("search", search); + } + return PageState.Route.AbsolutePath + Utilities.CreateQueryString(parameters); + } +} diff --git a/Oqtane.Client/Resources/Modules/Controls/StaticPager.resx b/Oqtane.Client/Resources/Modules/Controls/StaticPager.resx new file mode 100644 index 00000000..2dfdd092 --- /dev/null +++ b/Oqtane.Client/Resources/Modules/Controls/StaticPager.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Page {0} of {1} + + + Search: {0} + + \ No newline at end of file