using System.Windows.Input; using Connected.Annotations; using Connected.Extensions; using Connected.Utilities; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; namespace Connected.Components; public partial class ListItem : UIComponent, IDisposable { protected string Classname => new CssBuilder("mud-list-item") .AddClass("mud-list-item-dense", (Dense ?? List?.Dense) ?? false) .AddClass("mud-list-item-gutters", !DisableGutters && !(List?.DisableGutters == true)) .AddClass("mud-list-item-clickable", List?.Clickable) .AddClass("mud-ripple", List?.Clickable == true && !DisableRipple && !Disabled) .AddClass($"mud-selected-item mud-{List?.Color.ToDescriptionString()}-text mud-{List?.Color.ToDescriptionString()}-hover", _selected && !Disabled) .AddClass("mud-list-item-disabled", Disabled) .AddClass(Class) .Build(); [Inject] protected NavigationManager UriHelper { get; set; } [CascadingParameter] protected List List { get; set; } private bool _onClickHandlerPreventDefault = false; /// /// The text to display /// [Parameter] [Category(CategoryTypes.List.Behavior)] public string Text { get; set; } [Parameter] [Category(CategoryTypes.List.Selecting)] public object Value { get; set; } /// /// Avatar to use if set. /// [Parameter] [Category(CategoryTypes.List.Behavior)] public string Avatar { get; set; } /// /// Link to a URL when clicked. /// [Parameter] [Category(CategoryTypes.List.ClickAction)] public string Href { get; set; } /// /// If true, force browser to redirect outside component router-space. /// [Parameter] [Category(CategoryTypes.List.ClickAction)] public bool ForceLoad { get; set; } /// /// Avatar CSS Class to apply if Avatar is set. /// [Parameter] [Category(CategoryTypes.List.Appearance)] public string AvatarClass { get; set; } private bool _disabled; /// /// If true, will disable the list item if it has onclick. /// The value can be overridden by the parent list. /// [Parameter] [Category(CategoryTypes.List.Behavior)] public bool Disabled { get => _disabled || (List?.Disabled ?? false); set => _disabled = value; } /// /// If true, disables ripple effect. /// [Parameter] [Category(CategoryTypes.List.Appearance)] public bool DisableRipple { get; set; } /// /// Icon to use if set. /// [Parameter] [Category(CategoryTypes.List.Behavior)] public string Icon { get; set; } /// /// The color of the icon. /// [Parameter] [Category(CategoryTypes.List.Appearance)] public ThemeColor IconColor { get; set; } = ThemeColor.Inherit; /// /// Sets the Icon Size. /// [Parameter] [Category(CategoryTypes.List.Appearance)] public Size IconSize { get; set; } = Size.Medium; /// /// The color of the adornment if used. It supports the theme colors. /// [Parameter] [Category(CategoryTypes.List.Expanding)] public ThemeColor AdornmentColor { get; set; } = ThemeColor.Default; /// /// Custom expand less icon. /// [Parameter] [Category(CategoryTypes.List.Expanding)] public string ExpandLessIcon { get; set; } = Icons.Material.Filled.ExpandLess; /// /// Custom expand more icon. /// [Parameter] [Category(CategoryTypes.List.Expanding)] public string ExpandMoreIcon { get; set; } = Icons.Material.Filled.ExpandMore; /// /// If true, the List Subheader will be indented. /// [Parameter] [Category(CategoryTypes.List.Appearance)] public bool Inset { get; set; } /// /// If true, compact vertical padding will be used. /// [Parameter] [Category(CategoryTypes.List.Appearance)] public bool? Dense { get; set; } /// /// If true, the left and right padding is removed. /// [Parameter] [Category(CategoryTypes.List.Appearance)] public bool DisableGutters { get; set; } /// /// Expand or collapse nested list. Two-way bindable. Note: if you directly set this to /// true or false (instead of using two-way binding) it will force the nested list's expansion state. /// [Parameter] [Category(CategoryTypes.List.Expanding)] public bool Expanded { get => _expanded; set { if (_expanded == value) return; _expanded = value; _ = ExpandedChanged.InvokeAsync(value); } } private bool _expanded; [Parameter] public EventCallback ExpandedChanged { get; set; } /// /// If true, expands the nested list on first display /// [Parameter] [Category(CategoryTypes.List.Expanding)] public bool InitiallyExpanded { get; set; } /// /// Command parameter. /// [Parameter] [Category(CategoryTypes.List.ClickAction)] public object CommandParameter { get; set; } /// /// Command executed when the user clicks on an element. /// [Parameter] [Category(CategoryTypes.List.ClickAction)] public ICommand Command { get; set; } /// /// Display content of this list item. If set, this overrides Text /// [Parameter] [Category(CategoryTypes.List.Behavior)] public RenderFragment ChildContent { get; set; } [Parameter] [Category(CategoryTypes.List.Behavior)] public bool OnClickHandlerPreventDefault { get => _onClickHandlerPreventDefault; set => _onClickHandlerPreventDefault = value; } /// /// Add child list items here to create a nested list. /// [Parameter] [Category(CategoryTypes.List.Behavior)] public RenderFragment NestedList { get; set; } /// /// List click event. /// [Parameter] public EventCallback OnClick { get; set; } protected void OnClickHandler(MouseEventArgs ev) { if (Disabled) return; if (!_onClickHandlerPreventDefault) { if (NestedList != null) { Expanded = !Expanded; } else if (Href != null) { List?.SetSelectedValue(this.Value); OnClick.InvokeAsync(ev); UriHelper.NavigateTo(Href, ForceLoad); } else { List?.SetSelectedValue(this.Value); OnClick.InvokeAsync(ev); if (Command?.CanExecute(CommandParameter) ?? false) { Command.Execute(CommandParameter); } } } else { OnClick.InvokeAsync(ev); } } protected override void OnInitialized() { _expanded = InitiallyExpanded; if (List != null) { List.Register(this); OnListParametersChanged(); List.ParametersChanged += OnListParametersChanged; } } private Typo _textTypo; private void OnListParametersChanged() { if ((Dense ?? List?.Dense) ?? false) { _textTypo = Typo.body2; } else if (!((Dense ?? List?.Dense) ?? false)) { _textTypo = Typo.body1; } StateHasChanged(); } private bool _selected; internal void SetSelected(bool selected) { if (Disabled) return; if (_selected == selected) return; _selected = selected; StateHasChanged(); } public void Dispose() { try { if (List == null) return; List.ParametersChanged -= OnListParametersChanged; List.Unregister(this); } catch (Exception) { /*ignore*/ } } }