using System.Diagnostics.CodeAnalysis; 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 Chip : UIComponent, IDisposable { private bool _isSelected; [Inject] public NavigationManager UriHelper { get; set; } [Inject] public IJsApiService JsApiService { get; set; } protected string Classname => new CssBuilder("mud-chip") .AddClass($"mud-chip-{GetVariant().ToDescriptionString()}") .AddClass($"mud-chip-size-{Size.ToDescriptionString()}") .AddClass($"mud-chip-color-{GetColor().ToDescriptionString()}") .AddClass("mud-clickable", !ChipSet?.ReadOnly ?? OnClick.HasDelegate) .AddClass("mud-ripple", !ChipSet?.ReadOnly ?? OnClick.HasDelegate && !DisableRipple) .AddClass("mud-chip-label", Label) .AddClass("mud-disabled", Disabled) .AddClass("mud-chip-selected", IsSelected) .AddClass(Class) .Build(); //Cannot test the get variant (last line) [ExcludeFromCodeCoverage] private Variant GetVariant() { return Variant switch { Variant.Text => IsSelected ? Variant.Filled : Variant.Text, Variant.Filled => IsSelected ? Variant.Text : Variant.Filled, Variant.Outlined => Variant.Outlined, _ => Variant }; } private ThemeColor GetColor() { if (IsSelected && SelectedColor != ThemeColor.Inherit) { return SelectedColor; } else if (IsSelected && SelectedColor == ThemeColor.Inherit) { return Color; } else { return Color; } } [CascadingParameter] ChipSet ChipSet { get; set; } /// /// The color of the component. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public ThemeColor Color { get; set; } = ThemeColor.Default; /// /// The size of the button. small is equivalent to the dense button styling. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public Size Size { get; set; } = Size.Medium; /// /// The variant to use. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public Variant Variant { get; set; } = Variant.Filled; /// /// The selected color to use when selected, only works together with ChipSet, Color.Inherit for default value. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public ThemeColor SelectedColor { get; set; } = ThemeColor.Inherit; /// /// Avatar Icon, Overrides the regular Icon if set. /// [Parameter] [Category(CategoryTypes.Chip.Behavior)] public string Avatar { get; set; } /// /// Avatar CSS Class, appends to Chips default avatar classes. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public string AvatarClass { get; set; } /// /// Removes circle edges and applies theme default. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public bool Label { get; set; } /// /// If true, the chip will be displayed in disabled state and no events possible. /// [Parameter] [Category(CategoryTypes.Chip.Behavior)] public bool Disabled { get; set; } /// /// Sets the Icon to use. /// [Parameter] [Category(CategoryTypes.Chip.Behavior)] public string Icon { get; set; } /// /// Custom checked icon. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public string CheckedIcon { get; set; } = Icons.Material.Filled.Check; /// /// The color of the icon. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public ThemeColor IconColor { get; set; } = ThemeColor.Inherit; /// /// Overrides the default close icon, only shown if OnClose is set. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public string CloseIcon { get; set; } /// /// If true, disables ripple effect, ripple effect is only applied to clickable chips. /// [Parameter] [Category(CategoryTypes.Chip.Appearance)] public bool DisableRipple { get; set; } /// /// Child content of component. /// [Parameter] [Category(CategoryTypes.Chip.Behavior)] public RenderFragment ChildContent { get; set; } /// /// If set to a URL, clicking the button will open the referenced document. Use Target to specify where (Obsolete replaced by Href) /// [Obsolete("Use Href Instead.", false)] [Parameter] [Category(CategoryTypes.Chip.ClickAction)] public string Link { get => Href; set => Href = value; } /// /// If set to a URL, clicking the button will open the referenced document. Use Target to specify where /// [Parameter] [Category(CategoryTypes.Chip.ClickAction)] public string Href { get; set; } /// /// The target attribute specifies where to open the link, if Href is specified. Possible values: _blank | _self | _parent | _top | framename /// [Parameter] [Category(CategoryTypes.Chip.ClickAction)] public string Target { get; set; } /// /// A string you want to associate with the chip. If the ChildContent is not set this will be shown as chip text. /// [Parameter] [Category(CategoryTypes.Chip.Behavior)] public string Text { get; set; } /// /// A value that should be managed in the SelectedValues collection. /// Note: do not change the value during the chip's lifetime /// [Parameter] [Category(CategoryTypes.Chip.Behavior)] public object Value { get; set; } /// /// If true, force browser to redirect outside component router-space. /// [Parameter] [Category(CategoryTypes.Chip.ClickAction)] public bool ForceLoad { get; set; } /// /// If true, this chip is selected by default if used in a ChipSet. /// [Parameter] [Category(CategoryTypes.Chip.Behavior)] public bool? Default { get; set; } /// /// Command executed when the user clicks on an element. /// [Parameter] [Category(CategoryTypes.Chip.ClickAction)] public ICommand Command { get; set; } /// /// Command parameter. /// [Parameter] [Category(CategoryTypes.Chip.ClickAction)] public object CommandParameter { get; set; } /// /// Chip click event, if set the chip focus, hover and click effects are applied. /// [Parameter] public EventCallback OnClick { get; set; } /// /// Chip delete event, if set the delete icon will be visible. /// [Parameter] public EventCallback OnClose { get; set; } /// /// Set by MudChipSet /// public bool IsChecked { get => _isSelected && ChipSet?.Filter == true; } /// /// If false, this chip has not been seen before /// public bool DefaultProcessed { get; set; } /// /// Set by MudChipSet /// public bool IsSelected { get => _isSelected; set { if (_isSelected == value) return; _isSelected = value; StateHasChanged(); } } protected override void OnInitialized() { base.OnInitialized(); if (Value == null) Value = this; } protected internal async Task OnClickHandler(MouseEventArgs ev) { if (ChipSet?.ReadOnly == true) { return; } if (ChipSet != null) { _ = ChipSet.OnChipClicked(this); } if (Href != null) { // TODO: use MudElement to render and this code can be removed. we know that it has potential problems on iOS if (string.IsNullOrWhiteSpace(Target)) UriHelper.NavigateTo(Href, ForceLoad); else await JsApiService.Open(Href, Target); } else { await OnClick.InvokeAsync(ev); if (Command?.CanExecute(CommandParameter) ?? false) { Command.Execute(CommandParameter); } } } protected async Task OnCloseHandler(MouseEventArgs ev) { if (ChipSet?.ReadOnly == true) { return; } await OnClose.InvokeAsync(this); ChipSet?.OnChipDeleted(this); StateHasChanged(); } protected override Task OnInitializedAsync() { ChipSet?.Add(this); return base.OnInitializedAsync(); } internal void ForceRerender() => StateHasChanged(); //Exclude because we don't test to catching exception yet [ExcludeFromCodeCoverage] public void Dispose() { try { ChipSet?.Remove(this); } catch (Exception) { /* ignore! */ } } }