using System.Diagnostics.CodeAnalysis; using System.Windows.Input; using Connected.Annotations; using Connected.Utilities; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; namespace Connected.Components; // note: the MudTable code is split. Everything that has nothing to do with the type parameter of MudTable is here in MudTableBase public abstract class TableBase : UIComponent { internal object _editingItem = null; internal bool IsEditing => _editingItem != null; private int _currentPage = 0; private int? _rowsPerPage; private bool _isFirstRendered = false; protected string Classname => new CssBuilder("mud-table") .AddClass("mud-xs-table", Breakpoint == Breakpoint.Xs) .AddClass("mud-sm-table", Breakpoint == Breakpoint.Sm) .AddClass("mud-md-table", Breakpoint == Breakpoint.Md) .AddClass("mud-lg-table", Breakpoint is Breakpoint.Lg or Breakpoint.Always) .AddClass("mud-xl-table", Breakpoint is Breakpoint.Xl or Breakpoint.Always) .AddClass("mud-xxl-table", Breakpoint is Breakpoint.Xxl or Breakpoint.Always) .AddClass("mud-table-dense", Dense) .AddClass("mud-table-hover", Hover) .AddClass("mud-table-bordered", Bordered) .AddClass("mud-table-striped", Striped) .AddClass("mud-table-outlined", Outlined) .AddClass("mud-table-square", Square) .AddClass("mud-table-sticky-header", FixedHeader) .AddClass("mud-table-sticky-footer", FixedFooter) .AddClass($"mud-elevation-{Elevation}", !Outlined) .AddClass(Class) .Build(); protected string HeadClassname => new CssBuilder("mud-table-head") .AddClass(HeaderClass).Build(); protected string FootClassname => new CssBuilder("mud-table-foot") .AddClass(FooterClass).Build(); /// /// When editing a row and this is true, the editing row must be saved/cancelled before a new row will be selected. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public bool IsEditRowSwitchingBlocked { get; set; } = false; /// /// The higher the number, the heavier the drop-shadow. 0 for no shadow. /// [Parameter] [Category(CategoryTypes.Table.Appearance)] public int Elevation { set; get; } = 1; /// /// Set true to disable rounded corners /// [Parameter] [Category(CategoryTypes.Table.Appearance)] public bool Square { get; set; } /// /// If true, table will be outlined. /// [Parameter] [Category(CategoryTypes.Table.Appearance)] public bool Outlined { get; set; } /// /// If true, table's cells will have left/right borders. /// [Parameter] [Category(CategoryTypes.Table.Appearance)] public bool Bordered { get; set; } /// /// Set true for rows with a narrow height /// [Parameter] [Category(CategoryTypes.Table.Appearance)] public bool Dense { get; set; } /// /// Set true to see rows hover on mouse-over. /// [Parameter] [Category(CategoryTypes.Table.Appearance)] public bool Hover { get; set; } /// /// If true, striped table rows will be used. /// [Parameter] [Category(CategoryTypes.Table.Appearance)] public bool Striped { get; set; } /// /// At what breakpoint the table should switch to mobile layout. Takes None, Xs, Sm, Md, Lg and Xl the default behavior is breaking on Xs. /// [Parameter] [Category(CategoryTypes.Table.Behavior)] public Breakpoint Breakpoint { get; set; } = Breakpoint.Xs; /// /// When true, the header will stay in place when the table is scrolled. Note: set Height to make the table scrollable. /// [Parameter] [Category(CategoryTypes.Table.Header)] public bool FixedHeader { get; set; } /// /// When true, the footer will be visible is not scrolled to the bottom. Note: set Height to make the table scrollable. /// [Parameter] [Category(CategoryTypes.Table.Footer)] public bool FixedFooter { get; set; } /// /// Setting a height will allow to scroll the table. If not set, it will try to grow in height. You can set this to any CSS value that the /// attribute 'height' accepts, i.e. 500px. /// [Parameter] [Category(CategoryTypes.Table.Appearance)] public string Height { get; set; } /// /// If table is in smalldevice mode and uses any kind of sorting the text applied here will be the sort selects label. /// [Parameter] [Category(CategoryTypes.Table.Sorting)] public string SortLabel { get; set; } /// /// If true allows table to be in an unsorted state through column clicks (i.e. first click sorts "Ascending", second "Descending", third "None"). /// If false only "Ascending" and "Descending" states are allowed (i.e. there always should be a column to sort). /// [Parameter] [Category(CategoryTypes.Table.Sorting)] public bool AllowUnsorted { get; set; } = true; /// /// If the table has more items than this number, it will break the rows into pages of said size. /// Note: requires a MudTablePager in PagerContent. /// [Parameter] [Category(CategoryTypes.Table.Pagination)] public int RowsPerPage { get => _rowsPerPage ?? 10; set { if (_rowsPerPage is null || _rowsPerPage != value) SetRowsPerPage(value); } } /// /// Rows Per Page two-way bindable parameter /// [Parameter] public EventCallback RowsPerPageChanged { get; set; } /// /// The page index of the currently displayed page (Zero based). Usually called by MudTablePager. /// Note: requires a MudTablePager in PagerContent. /// [Parameter] [Category(CategoryTypes.Table.Pagination)] public int CurrentPage { get => _currentPage; set { if (_currentPage == value) return; _currentPage = value; InvokeAsync(StateHasChanged); if (_isFirstRendered) InvokeServerLoadFunc(); } } /// /// Set to true to enable selection of multiple rows with check boxes. /// [Parameter] [Category(CategoryTypes.Table.Selecting)] public bool MultiSelection { get; set; } /// /// Optional. Add any kind of toolbar to this render fragment. /// [Parameter] [Category(CategoryTypes.Table.Behavior)] public RenderFragment ToolBarContent { get; set; } /// /// Show a loading animation, if true. /// [Parameter] [Category(CategoryTypes.Table.Data)] public bool Loading { get; set; } /// /// The color of the loading progress if used. It supports the theme colors. /// [Parameter] [Category(CategoryTypes.Table.Data)] public ThemeColor LoadingProgressColor { get; set; } = ThemeColor.Info; /// /// Add MudTh cells here to define the table header. If is set, add one or more MudTHeadRow instead. /// [Parameter] [Category(CategoryTypes.Table.Header)] public RenderFragment HeaderContent { get; set; } /// /// Specify if the header has multiple rows. In that case, you need to provide the MudTHeadRow tags. /// [Parameter] [Category(CategoryTypes.Table.Header)] public bool CustomHeader { get; set; } /// /// Add a class to the thead tag /// [Parameter] [Category(CategoryTypes.Table.Header)] public string HeaderClass { get; set; } /// /// Add MudTd cells here to define the table footer. If is set, add one or more MudTFootRow instead. /// [Parameter] [Category(CategoryTypes.Table.Footer)] public RenderFragment FooterContent { get; set; } /// /// Specify if the footer has multiple rows. In that case, you need to provide the MudTFootRow tags. /// [Parameter] [Category(CategoryTypes.Table.Footer)] public bool CustomFooter { get; set; } /// /// Add a class to the tfoot tag /// [Parameter] [Category(CategoryTypes.Table.Footer)] public string FooterClass { get; set; } /// /// Specifies a group of one or more columns in a table for formatting. /// Ex: /// table /// colgroup /// col span="2" style="background-color:red" /// col style="background-color:yellow" /// colgroup /// header /// body /// table /// [Parameter] [Category(CategoryTypes.Table.Behavior)] public RenderFragment ColGroup { get; set; } //[Parameter] public RenderFragment RowTemplate { get; set; } <-- see MudTable.razor /// /// Add MudTablePager here to enable breaking the rows in to multiple pages. /// [Parameter] [Category(CategoryTypes.Table.Pagination)] public RenderFragment PagerContent { get; set; } /// /// Locks Inline Edit mode, if true. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public bool ReadOnly { get; set; } = false; /// /// Button commit edit click event. /// [Parameter] public EventCallback OnCommitEditClick { get; set; } /// /// Button cancel edit click event. /// [Parameter] public EventCallback OnCancelEditClick { get; set; } /// /// Event is called before the item is modified in inline editing. /// [Parameter] public EventCallback OnPreviewEditClick { get; set; } /// /// Command executed when the user clicks on the CommitEdit Button. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public ICommand CommitEditCommand { get; set; } /// /// Command parameter for the CommitEdit Button. By default, will be the row level item model, if you won't set anything else. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public object CommitEditCommandParameter { get; set; } /// /// Tooltip for the CommitEdit Button. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public string CommitEditTooltip { get; set; } /// /// Tooltip for the CancelEdit Button. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public string CancelEditTooltip { get; set; } /// /// Sets the Icon of the CommitEdit Button. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public string CommitEditIcon { get; set; } = Icons.Material.Filled.Done; /// /// Sets the Icon of the CancelEdit Button. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public string CancelEditIcon { get; set; } = Icons.Material.Filled.Cancel; /// /// Define if Cancel button is present or not for inline editing. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public bool CanCancelEdit { get; set; } /// /// Set the positon of the CommitEdit and CancelEdit button, if IsEditable is true. Defaults to the end of the row /// [Parameter] [Category(CategoryTypes.Table.Editing)] public TableApplyButtonPosition ApplyButtonPosition { get; set; } = TableApplyButtonPosition.End; /// /// Set the positon of the StartEdit button, if IsEditable is true. Defaults to the end of the row /// [Parameter] [Category(CategoryTypes.Table.Editing)] public TableEditButtonPosition EditButtonPosition { get; set; } = TableEditButtonPosition.End; /// /// Defines how a table row edit will be triggered /// [Parameter] [Category(CategoryTypes.Table.Editing)] public TableEditTrigger EditTrigger { get; set; } = TableEditTrigger.RowClick; /// /// Defines the edit button that will be rendered when EditTrigger.EditButton /// [Parameter] [Category(CategoryTypes.Table.Editing)] public RenderFragment EditButtonContent { get; set; } /// /// The method is called before the item is modified in inline editing. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public Action RowEditPreview { get; set; } /// /// The method is called when the edition of the item has been committed in inline editing. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public Action RowEditCommit { get; set; } /// /// The method is called when the edition of the item has been canceled in inline editing. /// [Parameter] [Category(CategoryTypes.Table.Editing)] public Action RowEditCancel { get; set; } /// /// Number of items. Used only with ServerData="true" /// [Parameter] [Category(CategoryTypes.Table.Data)] public int TotalItems { get; set; } /// /// CSS class for the table rows. Note, many CSS settings are overridden by MudTd though /// [Parameter] [Category(CategoryTypes.Table.Rows)] public string RowClass { get; set; } /// /// CSS styles for the table rows. Note, many CSS settings are overridden by MudTd though /// [Parameter] [Category(CategoryTypes.Table.Rows)] public string RowStyle { get; set; } /// /// If true, the results are displayed in a Virtualize component, allowing a boost in rendering speed. /// [Parameter] [Category(CategoryTypes.Table.Behavior)] public bool Virtualize { get; set; } #region --> Obsolete Forwarders for Backwards-Compatiblilty /// /// Alignment of the table cell text when breakpoint is smaller than /// [ExcludeFromCodeCoverage] [Obsolete("This property is not needed anymore, the cells width/alignment is done automatically.", true)] [Parameter] public bool RightAlignSmall { get; set; } = true; #endregion public abstract TableContext TableContext { get; } protected override Task OnAfterRenderAsync(bool firstRender) { if (firstRender) _isFirstRendered = true; return base.OnAfterRenderAsync(firstRender); } public void NavigateTo(Page page) { switch (page) { case Page.First: CurrentPage = 0; break; case Page.Last: CurrentPage = Math.Max(0, NumPages - 1); break; case Page.Next: CurrentPage = Math.Min(NumPages - 1, CurrentPage + 1); break; case Page.Previous: CurrentPage = Math.Max(0, CurrentPage - 1); break; } } /// /// Navigate to page with specified index. /// /// The index of the page number. public void NavigateTo(int pageIndex) { CurrentPage = Math.Min(Math.Max(0, pageIndex), NumPages - 1); } public void SetRowsPerPage(int size) { if (_rowsPerPage == size) return; _rowsPerPage = size; CurrentPage = 0; StateHasChanged(); RowsPerPageChanged.InvokeAsync(_rowsPerPage.Value); if (_isFirstRendered) InvokeServerLoadFunc(); } protected abstract int NumPages { get; } public abstract int GetFilteredItemsCount(); public abstract void SetSelectedItem(object item); public abstract void SetEditingItem(object item); internal async Task OnCommitEditHandler(MouseEventArgs ev, object item) { await OnCommitEditClick.InvokeAsync(ev); if (CommitEditCommand?.CanExecute(CommitEditCommandParameter) ?? false) { var parameter = CommitEditCommandParameter; if (parameter == null) parameter = item; CommitEditCommand.Execute(parameter); } } internal Task OnPreviewEditHandler(object item) { return OnPreviewEditClick.InvokeAsync(item); } internal Task OnCancelEditHandler(MouseEventArgs ev) { return OnCancelEditClick.InvokeAsync(ev); } protected string TableStyle => new StyleBuilder() .AddStyle($"height", Height, !string.IsNullOrWhiteSpace(Height)) .Build(); internal abstract bool HasServerData { get; } internal abstract Task InvokeServerLoadFunc(); internal abstract void FireRowClickEvent(MouseEventArgs args, Tr tr, object item); internal abstract void OnHeaderCheckboxClicked(bool value); internal abstract bool IsEditable { get; } public abstract bool ContainsItem(object item); public abstract void UpdateSelection(); public IForm Validator { get; set; } = new TableRowValidator(); }