private TableNode FooterContainer(string cssClass) { TableNode container = new TableNode("div"); container.Element.AddCssClass("float-right"); container.Element.AddCssClass(cssClass); return(container); }
private TableNode CreateAndAppend(string tag, TableNode parent, IEnumerable <string> cssClasses = null) { TableNode node = new TableNode(tag); parent.InnerContent.Add(node); if (cssClasses != null) { this.AddCssClasses(cssClasses, node.Element); } return(node); }
private void FilteringLinkTemplate(TableNode container) { if (_config.Columns.Any(c => c.Value.Filtering.Threshold > 0)) { TableNode filterLink = this.CreateAndAppend("a", container); TableNode linkTemplate = this.CreateAndAppend("input", container); string url = $"{_config.Update.Url}?{this.CommonQueryAttrs()}{this.SortQueryAttrs()}{this.PagingQueryAttrs()}"; this.SetupAjaxAttrs(filterLink.Element); filterLink.Element.Attributes.Add("id", "FilterLink"); linkTemplate.Element.Attributes.Add("id", "FilterLinkTemplate"); linkTemplate.Element.Attributes.Add("type", "hidden"); linkTemplate.Element.Attributes.Add("value", url); } }
private TableNode NavCtrl(TableNode container, string iconLib, string icon, string navClass, bool enabled = true) { TableNode nav = this.CreateAndAppend("a", container, new [] { "btn", "btn-default", navClass }); if (!enabled) { nav.Element.AddCssClass("disabled"); } if (iconLib != null) { this.CreateAndAppend("span", nav, new [] { iconLib, icon }); } return(nav); }
private TableNode Table() { TableNode table = new TableNode("table"); Element = table.Element; this.BaseConfig((ConfigBase)_config, "table", "table-"); //this.AddAttribute("id", _config.Id, table.Element); //this.AddAttribute("name", _config.Name, table.Element); //table.Element.AddCssClass("table"); table.Element.AddCssClass(_config.Dark ? "table-dark" : null); table.Element.AddCssClass(_config.Striped ? "table-striped" : null); table.Element.AddCssClass(_config.Bordered ? "table-bordered" : null); table.Element.AddCssClass(_config.HoverState ? "table-hover" : null); table.Element.AddCssClass(_config.Small ? "table-sm" : null); //this.AddCssClasses(_config.CssClasses, table.Element); return(table); }
private void ManualFiltering(ColumnConfig config, PropertyInfo propInfo, TableNode filter) { TableNode input = this.CreateAndAppend("input", filter); input.Element.Attributes.Add("type", "text"); input.Element.Attributes.Add("data-filter-prop", propInfo.Name); input.Element.Attributes.Add("data-filter-threshold", config.Filtering.Threshold.ToString()); input.Element.AddCssClass("form-control"); this.AddCssClasses(config.Filtering.CssClasses, input.Element); if (_tableState.Filters.ContainsKey(propInfo.Name)) { input.Element.Attributes.Add("value", _tableState.Filters[propInfo.Name].Value); } if (propInfo.Name == _tableState.CurrentFilter) { // Filter input should be focused. input.Element.Attributes.Add("data-filter-focus", string.Empty); } }
public TableNode(string element, TableNode inner) : this(new TagBuilder(element)) { InnerContent.Add(inner); }
private void Footer(TableNode table) { if (_config.Paging.PageSize > 0 || !string.IsNullOrEmpty(_config.Footer.Text)) { List <TableNode> footerContent = new List <TableNode>(); int entityCount = _model.Entities.Count(); int pageCount = _config.Paging.PageSize > 0 ? entityCount / _config.Paging.PageSize + (entityCount % _config.Paging.PageSize > 0 ? 1 : 0) : 0; if (_config.Paging.PageInfo) { TableNode container = this.FooterContainer("NavInfoContainer"); this.CreateAndAppend("span", container).Element.InnerHtml.Append($"{_tableState.Page}/{pageCount}"); footerContent.Add(container); } // Paging. if (_config.Paging.PageSize > 0 && entityCount > _config.Paging.PageSize) { TableNode firstContainer = this.FooterContainer("NavBtnContainer"); TableNode prevContainer = this.FooterContainer("NavBtnContainer"); TableNode nextContainer = this.FooterContainer("NavBtnContainer"); TableNode lastContainer = this.FooterContainer("NavBtnContainer"); TableNode first = this.NavCtrl(firstContainer, _config.Paging.IconLib, _config.Paging.FirstCssClass, "NavFirst", _tableState.Page > 1); TableNode prev = this.NavCtrl(prevContainer, _config.Paging.IconLib, _config.Paging.PreviousCssClass, "NavPrevious", _tableState.Page > 1); TableNode next = this.NavCtrl(nextContainer, _config.Paging.IconLib, _config.Paging.NextCssClass, "NavNext", pageCount > _tableState.Page); TableNode last = this.NavCtrl(lastContainer, _config.Paging.IconLib, _config.Paging.LastCssClass, "NavLast", pageCount > _tableState.Page); this.SetupAjaxAttrs(first.Element, this.PagingLinkQueryAttrs(1)); this.SetupAjaxAttrs(prev.Element, this.PagingLinkQueryAttrs(_tableState.Page - 1)); this.SetupAjaxAttrs(next.Element, this.PagingLinkQueryAttrs(_tableState.Page + 1)); this.SetupAjaxAttrs(last.Element, this.PagingLinkQueryAttrs(pageCount)); if (_config.Paging.DirectPageAccess) { TableNode container = this.FooterContainer("NavAccessContainer"); TableNode pages = this.CreateAndAppend("select", container); TableNode pageSelector = this.CreateAndAppend("a", container); string pageSelectorId = Guid.NewGuid().ToString(); pages.Element.Attributes.Add("data-pageselector-id", $"#{pageSelectorId}"); for (int pageNumber = 1; pageNumber <= pageCount; pageNumber++) { TableNode page = this.CreateAndAppend("option", pages); page.Element.InnerHtml.Append(pageNumber.ToString()); page.Element.Attributes.Add("value", $"{_config.Update.Url}?{this.CommonQueryAttrs()}{this.FilterQueryAttrs()}{this.PagingLinkQueryAttrs(pageNumber)}"); if (pageNumber == _tableState.Page) { page.Element.Attributes.Add("selected", ""); } } pageSelector.Element.Attributes.Add("id", pageSelectorId); this.SetupAjaxAttrs(pageSelector.Element); footerContent.Add(container); } footerContent.Add(lastContainer); footerContent.Add(nextContainer); footerContent.Add(prevContainer); footerContent.Add(firstContainer); } // Text. if (!string.IsNullOrEmpty(_config.Footer.Text)) { TableNode container = new TableNode("div"); TableNode footerText = this.CreateAndAppend("span", container); container.Element.AddCssClass("FooterTextContainer"); footerText.Element.AddCssClass("FooterText"); footerText.Element.InnerHtml.Append(_config.Footer.Text); footerContent.Add(container); } if (footerContent.Any()) { TableNode footer = this.CreateAndAppend("tfoot", table); TableNode row = this.CreateAndAppend("tr", footer); TableNode content = this.CreateAndAppend("td", row); int colCount = _entity.GetType().GetProperties() .Count(pi => !_config.Columns.ContainsKey(pi.Name) || _config.Columns[pi.Name].Visible); content.Element.Attributes.Add("colspan", colCount.ToString()); this.AddContextualState(content.Element, _config.Footer.State, "table-"); this.AddCssClasses(_config.Footer.CssClasses, content.Element); content.InnerContent.AddRange(footerContent); } } }
private void PrepopulatedFiltering(ColumnConfig config, PropertyInfo propInfo, TableNode filter, IEnumerable <string> filterValues) { TableNode dropDown = this.CreateAndAppend("div", filter); TagBuilder dropDownBtn = new TagBuilder("button"); TagBuilder dropDownMenu = new TagBuilder("ul"); TagBuilder dropDownCaret = new TagBuilder("span"); dropDown.Element.AddCssClass("dropdown"); dropDown.Element.InnerHtml.AppendHtml(dropDownBtn); this.AddCssClasses(config.Filtering.CssClasses, dropDownMenu); dropDownBtn.AddCssClass("btn"); dropDownBtn.AddCssClass("btn-default"); dropDownBtn.AddCssClass("dropdown-toggle"); dropDownBtn.Attributes.Add("type", "button"); dropDownBtn.Attributes.Add("data-toggle", "dropdown"); dropDownBtn.Attributes.Add("aria-haspopup", "true"); dropDownBtn.Attributes.Add("aria-expanded", "false"); if (_tableState.Filters.ContainsKey(propInfo.Name)) { // Currently selected filter value. dropDownBtn.InnerHtml.Append(_tableState.Filters[propInfo.Name].Value); } dropDownBtn.InnerHtml.AppendHtml(dropDownCaret); dropDown.Element.InnerHtml.AppendHtml(dropDownMenu); dropDownMenu.AddCssClass("dropdown-menu"); dropDownCaret.AddCssClass("caret"); this.AddFilterSelection(dropDownMenu, propInfo.Name, " ", true); foreach (var filterValue in filterValues) { this.AddFilterSelection(dropDownMenu, propInfo.Name, filterValue); } }
private void Header(TableNode table) { if (_config.Columns.Values.Any(c => !string.IsNullOrEmpty(c.Header))) { TableNode header = this.CreateAndAppend("thead", table); TableNode headerRow = this.CreateAndAppend("tr", header); TableNode filterRow = _config.Columns.Any(c => c.Value.Filtering.Threshold > 0 || c.Value.Filtering.Prepopulated) ? this.CreateAndAppend("tr", header) : null; IDictionary <string, IEnumerable <string> > filterValues = new Dictionary <string, IEnumerable <string> >(); // For each column configured for prepopulated filtering, retrieve the possible // filter values. this.IterateProperties(_entity, (propInfo, config) => { if (config?.Filtering.Prepopulated != null) { List <object> values = _model.Entities .Select(ExpressionHelper.PropertyExpr <T>(propInfo.Name)).Distinct().ToList(); filterValues.Add(propInfo.Name, values.Select(v => v.ToString()).OrderBy(v => v)); } }); // Columns. this.IterateProperties(_entity, (propInfo, config) => { TableNode headerCol = this.CreateAndAppend("th", headerRow); TableNode filter = filterRow != null ? this.CreateAndAppend("td", filterRow) : null; if (config != null) { headerCol.Element.InnerHtml.Append(config.Header); this.AddCssClasses(config.CssClasses, headerCol.Element); // Sorting. if (config.SortState.HasValue) { TableNode sortAsc = this.CreateAndAppend("a", headerCol, new [] { "SortIcon" }); TableNode sortDesc = this.CreateAndAppend("a", headerCol, new [] { "SortIcon" }); TableNode ascIcon = this.CreateAndAppend("span", sortAsc); TableNode descIcon = this.CreateAndAppend("span", sortDesc); ascIcon.Element.AddCssClass(_config.Sorting.IconLib); ascIcon.Element.AddCssClass(_config.Sorting.AscendingCssClass); descIcon.Element.AddCssClass(_config.Sorting.IconLib); descIcon.Element.AddCssClass(_config.Sorting.DescendingCssClass); this.SetupAjaxAttrs(sortAsc.Element, $"&sort={propInfo.Name}&asc=True{this.PagingQueryAttrs()}"); this.SetupAjaxAttrs(sortDesc.Element, $"&sort={propInfo.Name}&asc=False{this.PagingQueryAttrs()}"); if (propInfo.Name == _tableState.SortProp || (config.SortState != SortState.None && _tableState.SortProp == null)) { bool ascending = propInfo.Name == _tableState.SortProp ? _tableState.AscSort : config.SortState == SortState.Ascending; sortAsc.Element.AddCssClass(ascending ? "ActiveSort" : null); sortDesc.Element.AddCssClass(!ascending ? "ActiveSort" : null); } } // Filtering. if (config.Filtering.Threshold > 0) { this.ManualFiltering(config, propInfo, filter); } else if (config.Filtering.Prepopulated) { this.PrepopulatedFiltering(config, propInfo, filter, filterValues[propInfo.Name]); } } }); } }
private void Body(TableNode table) { TableNode body = this.CreateAndAppend("tbody", table); IList <RowConfig> rows = _config.Rows; if (!rows.Any()) { IQueryable <T> entities = _model.ProcessedEntities; if (!_model.Processed) { KeyValuePair <string, ColumnConfig> initialFilterColumn = _config.Columns .FirstOrDefault(c => c.Value.Filtering.Initial != null); KeyValuePair <string, ColumnConfig> initialSortColumn = _config.Columns .FirstOrDefault(c => c.Value.SortState.HasValue); // Initial rendering of the table, apply initial filteringm sorting and paging. if (initialFilterColumn.Key != null) { Expression <Func <T, bool> > whereExpr = ExpressionHelper.EqualsExpr <T>(initialFilterColumn.Key, initialFilterColumn.Value.Filtering.Initial); entities = entities.Where(whereExpr); } entities = _config.Paging.PageSize > 0 ? entities.Take(_config.Paging.PageSize) : entities; if (initialSortColumn.Key != null) { var sortExpr = ExpressionHelper.PropertyExpr <T>(initialSortColumn.Key); entities = initialSortColumn.Value.SortState == SortState.Ascending ? entities.OrderBy(sortExpr) : entities.OrderByDescending(sortExpr); } } // No row configuration has been performed. // Create rows from the entities. foreach (var row in entities.Select(e => new RowConfig(e))) { rows.Add(row); } } // Rows. foreach (RowConfig rowConfig in rows) { TableNode row = this.CreateAndAppend("tr", body); this.AddContextualState(row.Element, rowConfig.State, "table-"); this.AddCssClasses(rowConfig.CssClasses, row.Element); if (rowConfig.NavigationUrl != null) { row.Element.Attributes.Add("style", "cursor: pointer"); row.Element.Attributes.Add("onclick", $"window.location.href = '{rowConfig.NavigationUrl}'"); } else if (!string.IsNullOrEmpty(_config.RowClick) || !string.IsNullOrEmpty(rowConfig.RowClick)) { string jsCall = !string.IsNullOrEmpty(rowConfig.RowClick) ? rowConfig.RowClick : $"{_config.RowClick}(this)"; row.Element.Attributes.Add("style", "cursor: pointer"); row.Element.Attributes.Add("onclick", jsCall); } // Cells. this.IterateProperties(rowConfig.Entity, (property, columnConfig) => { CellConfig cellConfig = rowConfig.CellConfigs.ContainsKey(property.Name) ? rowConfig.CellConfigs[property.Name] : null; TableNode cell = this.CreateAndAppend("td", row); string cellValue = property.GetValue(rowConfig.Entity)?.ToString(); if (cellValue != null) { if (columnConfig != null && columnConfig.Filtering.Prepopulated && columnConfig.Filtering.Links) { TagBuilder filterLink = new TagBuilder("a"); filterLink.AddCssClass("FilterLink"); filterLink.InnerHtml.Append(cellValue?.ToString()); filterLink.Attributes.Add("href", "#"); this.SetupAjaxAttrs(filterLink, $"&filter[]={property.Name}&filter[]={cellValue}&filter[]={true}", property.Name); cell.Element.InnerHtml.AppendHtml(filterLink); } else { cell.Element.InnerHtml.Append(cellValue); } } if (cellConfig != null) { this.AddContextualState(cell.Element, cellConfig.State, "table-"); this.AddCssClasses(cellConfig.CssClasses, cell.Element); } }); } }