using Connected.Annotations; using Connected.Extensions; using Connected.Services; using Connected.Utilities; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; namespace Connected.Components; public partial class Radio : UIComponent, IDisposable { [CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; } protected string Classname => new CssBuilder("mud-radio") .AddClass($"mud-disabled", Disabled) .AddClass($"mud-radio-content-placement-{ConvertPlacement(Placement).ToDescriptionString()}") .AddClass(Class) .Build(); protected string ButtonClassname => new CssBuilder("mud-button-root mud-icon-button") .AddClass($"mud-ripple mud-ripple-radio", !DisableRipple && !Disabled) .AddClass($"mud-{Color.ToDescriptionString()}-text hover:mud-{Color.ToDescriptionString()}-hover", UnCheckedColor == null || (UnCheckedColor != null && Checked == true)) .AddClass($"mud-{UnCheckedColor?.ToDescriptionString()}-text hover:mud-{UnCheckedColor?.ToDescriptionString()}-hover", UnCheckedColor != null && Checked == false) .AddClass($"mud-radio-dense", Dense) .AddClass($"mud-disabled", Disabled) .AddClass($"mud-checked", Checked) .AddClass("mud-error-text", RadioGroup?.HasErrors) .Build(); protected string RadioIconsClassNames => new CssBuilder("mud-radio-icons") .AddClass($"mud-checked", Checked) .Build(); protected string IconClassName => new CssBuilder("mud-icon-root mud-svg-icon") .AddClass($"mud-icon-size-{Size.ToDescriptionString()}") .Build(); protected string CheckedIconClassName => new CssBuilder("mud-icon-root mud-svg-icon mud-radio-icon-checked") .AddClass($"mud-icon-size-{Size.ToDescriptionString()}") .Build(); protected string ChildSpanClassName => new CssBuilder("mud-radio-content mud-typography mud-typography-body1") .AddClass("mud-error-text", RadioGroup.HasErrors) .Build(); private IRadioGroup _parent; /// /// The parent Radio Group /// [CascadingParameter] internal IRadioGroup ParentGroup { get => _parent; set { _parent = value; if (_parent == null) return; _parent.CheckGenericTypeMatch(this); //MudRadioGroup?.Add(this); } } internal RadioGroup RadioGroup => (RadioGroup)ParentGroup; private Placement ConvertPlacement(Placement placement) { return placement switch { Placement.Left => RightToLeft ? Placement.End : Placement.Start, Placement.Right => RightToLeft ? Placement.Start : Placement.End, _ => placement }; } /// /// The color of the component. It supports the theme colors. /// [Parameter] [Category(CategoryTypes.Radio.Appearance)] public ThemeColor Color { get; set; } = ThemeColor.Default; /// /// The base color of the component in its none active/unchecked state. It supports the theme colors. /// [Parameter] [Category(CategoryTypes.Radio.Appearance)] public ThemeColor? UnCheckedColor { get; set; } = null; /// /// The position of the child content. /// [Parameter] [Category(CategoryTypes.Radio.Behavior)] public Placement Placement { get; set; } = Placement.End; /// /// The value to associate to the button. /// [Parameter] [Category(CategoryTypes.Radio.Behavior)] public T Option { get; set; } /// /// If true, compact padding will be applied. /// [Parameter] [Category(CategoryTypes.Radio.Appearance)] public bool Dense { get; set; } /// /// The Size of the component. /// [Parameter] [Category(CategoryTypes.Radio.Appearance)] public Size Size { get; set; } = Size.Medium; /// /// If true, disables ripple effect. /// [Parameter] [Category(CategoryTypes.Radio.Appearance)] public bool DisableRipple { get; set; } /// /// If true, the button will be disabled. /// [Parameter] [Category(CategoryTypes.Radio.Behavior)] public bool Disabled { get; set; } /// /// Child content of component. /// [Parameter] [Category(CategoryTypes.Radio.Behavior)] public RenderFragment ChildContent { get; set; } internal bool Checked { get; private set; } internal void SetChecked(bool value) { if (Checked != value) { Checked = value; StateHasChanged(); } } public void Select() { RadioGroup?.SetSelectedRadioAsync(this).AndForget(); } internal Task OnClick() { if (RadioGroup != null) return RadioGroup.SetSelectedRadioAsync(this); return Task.CompletedTask; } protected internal void HandleKeyDown(KeyboardEventArgs obj) { if (Disabled) return; switch (obj.Key) { case "Enter": case "NumpadEnter": case " ": Select(); break; case "Backspace": RadioGroup.Reset(); break; } } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); if (RadioGroup != null) await RadioGroup.RegisterRadioAsync(this); } public void Dispose() { RadioGroup?.UnregisterRadio(this); _keyInterceptor?.Dispose(); } private IKeyInterceptor _keyInterceptor; [Inject] private IKeyInterceptorFactory _keyInterceptorFactory { get; set; } private string _elementId = "radio" + Guid.NewGuid().ToString().Substring(0, 8); protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { _keyInterceptor = _keyInterceptorFactory.Create(); await _keyInterceptor.Connect(_elementId, new KeyInterceptorOptions() { //EnableLogging = true, TargetClass = "mud-button-root", Keys = { new KeyOptions { Key=" ", PreventDown = "key+none", PreventUp = "key+none" }, // prevent scrolling page new KeyOptions { Key="Enter", PreventDown = "key+none" }, new KeyOptions { Key="NumpadEnter", PreventDown = "key+none" }, new KeyOptions { Key="Backspace", PreventDown = "key+none" }, }, }); } await base.OnAfterRenderAsync(firstRender); } }