diff --git a/README.md b/README.md index 6e46cb0..f486691 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Blazor RadioButton is a basic yet customizable RadioButton component for Tailwindcss. +> **New in v1.1.0**: The library now targets .NET 9 (`net9.0` and `net9.0-browser`) so it can participate in Blazor's new Auto render mode without any extra setup. Drop it into server-side, WebAssembly, or Auto-rendered applications and it will light up interactivity wherever the app is running. + Without passing anything to it you will get a default radio color of `text-blue-500` and `focus:ring-blue-500`. If you would like to implement your own colour please add `radioButtonColor` into the theme section of your tailwind.config.js. ``` javascript @@ -65,8 +67,8 @@ Simply open up a component and add your content. - - + + @@ -79,7 +81,7 @@ If you would like to add custom content for the `````` component, ``` C# - + ``` @@ -92,7 +94,7 @@ or you can add your custom content inside the ````` ``` -**NOTE: You must specify the TValue for each `````` component or else the compile will fail, this is unfortunately a limitation of Blazor.** +Thanks to Blazor's cascading type inference, you only need to specify `TValue` on the ``—child `` components automatically receive the correct type. ### 3. (Optional) Creating a RadioButton component via a list diff --git a/src/TailBlazor.RadioButton/Extensions.cs b/src/TailBlazor.RadioButton/Extensions.cs index 52591c1..afae228 100644 --- a/src/TailBlazor.RadioButton/Extensions.cs +++ b/src/TailBlazor.RadioButton/Extensions.cs @@ -1,31 +1,41 @@ -using System; +using System; using System.Linq; using System.Reflection; -namespace TailBlazor.RadioButton +namespace TailBlazor.RadioButton; + +public static class StringExtensions { - public static class StringExtensions - { - /// - /// Check if the string is null or empty. - /// - /// the string - /// a ture/false depending of if string is empty - public static bool IsEmpty(this string s) => string.IsNullOrEmpty(s); - } + /// + /// Check if the string is null or empty. + /// + /// the string + /// true if the string is empty or whitespace, otherwise false. + public static bool IsEmpty(this string? s) => string.IsNullOrWhiteSpace(s); +} - public static class EnumExtensions +public static class EnumExtensions +{ + /// + /// Return the class attribute for an enum. + /// + /// the enum + /// a string based on the class attribute + public static string GetClass(this Enum enumValue) { - /// - /// Return the class attribute for an enum. - /// - /// the enum - /// a string based on the class attribute - public static string GetClass(this Enum enumValue) => - enumValue + ArgumentNullException.ThrowIfNull(enumValue); + + var member = enumValue .GetType() .GetMember(enumValue.ToString()) - .First() - .GetCustomAttribute().Value; + .FirstOrDefault(); + + if (member is null) + { + return string.Empty; + } + + var attribute = member.GetCustomAttribute(); + return attribute?.Value ?? string.Empty; } } diff --git a/src/TailBlazor.RadioButton/RadioButton.razor b/src/TailBlazor.RadioButton/RadioButton.razor index 522022e..48daa1f 100644 --- a/src/TailBlazor.RadioButton/RadioButton.razor +++ b/src/TailBlazor.RadioButton/RadioButton.razor @@ -1,14 +1,24 @@ -@typeparam TValue +@typeparam TValue +@attribute [CascadingTypeParameter(nameof(TValue))] -
- @if (ChildContent == null) +
+ @if (ChildContent is null) { - - + + @if (!Label.IsEmpty()) + { + + } } else { - @ChildContent(RadioButtonGroupContainer) + @ChildContent(ParentContainer) }
@@ -18,71 +28,69 @@ /// The parent container. /// [CascadingParameter] - protected RadioButtonGroupContainer RadioButtonGroupContainer { get; set; } + protected RadioButtonGroupContainer? RadioButtonGroupContainer { get; set; } /// /// The id of the radio input. /// [Parameter] - public string InputId { get; set; } + public string? InputId { get; set; } /// /// The class for the radio input. This will override any default classes. /// [Parameter] - public string InputClass { get; set; } + public string? InputClass { get; set; } /// /// The class for the label for the radio input. This will override any default classes. /// [Parameter] - public string LabelClass { get; set; } + public string? LabelClass { get; set; } /// /// The label text. /// [Parameter] - public string Label { get; set; } + public string? Label { get; set; } /// /// The value of the input. /// [Parameter] - public TValue Value { get; set; } + public TValue Value { get; set; } = default!; /// /// The custom content, if not using the pre-made content. /// [Parameter] - public RenderFragment> ChildContent { get; set; } + public RenderFragment>? ChildContent { get; set; } #endregion - #region private function(s) - /// - /// Returns the default classes, if InputClass is not empty then return the custom classes. - /// - /// a string of classes - string ReturnInputClass() => InputClass.IsEmpty() ? "focus:ring-blue-500 focus:ring-radioButtonColor h-4 w-4 text-blue-600 text-radioButtonColor border-gray-300" : InputClass; + #region private properties + string InputCssClass => InputClass.IsEmpty() ? "focus:ring-blue-500 focus:ring-radioButtonColor h-4 w-4 text-blue-600 text-radioButtonColor border-gray-300" : InputClass!; - /// - /// Returns the default classes, if LabelClass is not empty then return the custom classes. - /// - /// - string ReturnLabelClass() => LabelClass.IsEmpty() ? "ml-3 block text-sm font-medium text-gray-700" : LabelClass; + string LabelCssClass => LabelClass.IsEmpty() ? "ml-3 block text-sm font-medium text-gray-700" : LabelClass!; - /// - /// Returns specific margining depending on the orientation of the inputs. - /// - /// - string CheckIfHorizontal() => RadioButtonGroupContainer?.Orientation == RadioButtonOrientation.Horizontal ? "mr-3" : "mb-1"; + RadioButtonGroupContainer ParentContainer => RadioButtonGroupContainer ?? throw new InvalidOperationException("RadioButton must be placed inside a RadioButtonGroupContainer."); + string OrientationClass => ParentContainer.Orientation == RadioButtonOrientation.Horizontal ? "mr-3" : "mb-1"; + + string GroupName => ParentContainer.GroupName ?? string.Empty; + + string ValueAsString => Value is null ? string.Empty : Value.ToString() ?? string.Empty; + + bool IsChecked => ParentContainer.IsSelected(Value); + #endregion + + #region private function(s) /// /// Set the value in the parent so that we can bind the value. /// - /// the event arguments - void OnChangeHandler(ChangeEventArgs ev) + /// the event arguments + void OnChangeHandler(ChangeEventArgs _) { - RadioButtonGroupContainer?.SetCurrentValue(this.Value); - } + ParentContainer.SetCurrentValue(Value); + } #endregion } diff --git a/src/TailBlazor.RadioButton/RadioButtonGroup.razor b/src/TailBlazor.RadioButton/RadioButtonGroup.razor index 1e99652..a69bb07 100644 --- a/src/TailBlazor.RadioButton/RadioButtonGroup.razor +++ b/src/TailBlazor.RadioButton/RadioButtonGroup.razor @@ -1,4 +1,4 @@ -
+
@ChildContent
@@ -7,17 +7,17 @@ /// The id for the group. /// [Parameter] - public string Id { get; set; } + public string? Id { get; set; } /// /// The class for the group. /// [Parameter] - public string Class { get; set; } + public string? Class { get; set; } /// /// The child content of the group. /// [Parameter] - public RenderFragment ChildContent { get; set; } -} \ No newline at end of file + public RenderFragment? ChildContent { get; set; } +} diff --git a/src/TailBlazor.RadioButton/RadioButtonGroupContainer.razor b/src/TailBlazor.RadioButton/RadioButtonGroupContainer.razor index 847b835..6676ece 100644 --- a/src/TailBlazor.RadioButton/RadioButtonGroupContainer.razor +++ b/src/TailBlazor.RadioButton/RadioButtonGroupContainer.razor @@ -1,18 +1,18 @@ -@typeparam TValue +@typeparam TValue +@attribute [CascadingTypeParameter(nameof(TValue))]
- @if (Items != null && - Items?.Count() > 0) + @if (Items?.Any() == true) { - @foreach(RadioItem item in Items) + foreach (var item in Items) { - + } } else { - @ChildContent(this) + @ChildContent?.Invoke(this) }
@@ -21,7 +21,8 @@ #region private variable(s) // Defaults private RadioButtonOrientation _orientation = RadioButtonOrientation.Vertical; - private TValue _value; + private TValue _value = default!; + private bool _hasValue; #endregion #region parameter(s) @@ -34,23 +35,26 @@ get => _value; set { - if (_value?.Equals(value) ?? false) return; + if (_hasValue && EqualityComparer.Default.Equals(_value, value)) + { + return; + } _value = value; + _hasValue = true; - // set the value - if(_value != null) - ValueChanged.InvokeAsync(value); + if (ValueChanged.HasDelegate) + { + _ = ValueChanged.InvokeAsync(value); + } } } - //[Parameter] - //public TValue Value { get; set; } /// /// The class for the container div. This will override any default classes. /// [Parameter] - public string Class { get; set; } + public string? Class { get; set; } /// /// The orientation that we would like to set these radio buttons. @@ -62,7 +66,7 @@ get => _orientation; set { - if (Orientation != value) + if (_orientation != value) { _orientation = value; } @@ -73,13 +77,13 @@ /// The group name for all the radio buttons that get created. /// [Parameter] - public string GroupName { get; set; } + public string? GroupName { get; set; } /// /// A list of RadioItems so that we can automatically create the radio buttons. /// [Parameter] - public List> Items { get; set; } + public IEnumerable>? Items { get; set; } /// /// A callback function for any customization when a value changes. @@ -91,7 +95,7 @@ /// The child content. /// [Parameter] - public RenderFragment> ChildContent { get; set; } + public RenderFragment>? ChildContent { get; set; } #endregion #region public function(s) @@ -101,8 +105,16 @@ /// public void SetCurrentValue(TValue value) { - this.Value = value; + Value = value; } + + /// + /// Determines if the provided value matches the currently selected value. + /// + /// The value to compare. + /// True when the value is selected. + public bool IsSelected(TValue value) => + _hasValue && EqualityComparer.Default.Equals(_value, value); #endregion #region private function(s) @@ -110,6 +122,6 @@ /// Returns the default classes, if Class is not empty then return the custom classes. /// /// a string of classes - string ReturnClass() => Class.IsEmpty() ? "mt-4 space-y" : Class; + string ReturnClass() => Class.IsEmpty() ? "mt-4 space-y" : Class!; #endregion } diff --git a/src/TailBlazor.RadioButton/RadioButtonGroupLabel.razor b/src/TailBlazor.RadioButton/RadioButtonGroupLabel.razor index 4325a46..4a1baab 100644 --- a/src/TailBlazor.RadioButton/RadioButtonGroupLabel.razor +++ b/src/TailBlazor.RadioButton/RadioButtonGroupLabel.razor @@ -1,14 +1,15 @@ -
+
@if (!Label.IsEmpty()) { - @(Label) + @Label @if (!SubLabel.IsEmpty()) {

- @(SubLabel)) + @SubLabel

} - } else + } + else { @ChildContent } @@ -20,28 +21,28 @@ /// The class for the group label. This will override any default classes. /// [Parameter] - public string Class { get; set; } + public string? Class { get; set; } /// /// The label text. /// [Parameter] - public string Label { get; set; } + public string? Label { get; set; } /// /// The sub label text, goes underneath the main label. /// [Parameter] - public string SubLabel { get; set; } + public string? SubLabel { get; set; } [Parameter] - public RenderFragment ChildContent { get; set; } + public RenderFragment? ChildContent { get; set; } /// /// The class for the sub label. This will override any default classes. /// [Parameter] - public string SubLabelClass { get; set; } + public string? SubLabelClass { get; set; } #endregion #region private function(s) @@ -49,12 +50,12 @@ /// Returns the default classes, if Class is not empty then return the custom classes. /// /// a string of classes - string ReturnClass() => Class.IsEmpty() ? "text-base font-medium text-gray-900" : Class; + string ReturnClass() => Class.IsEmpty() ? "text-base font-medium text-gray-900" : Class!; /// /// Returns the default classes, if SubLabelClass is not empty then return the custom classes. /// /// a string of classes - string ReturnSubLabelClass() => SubLabelClass.IsEmpty() ? "text-sm text-gray-500" : SubLabelClass; + string ReturnSubLabelClass() => SubLabelClass.IsEmpty() ? "text-sm text-gray-500" : SubLabelClass!; #endregion } diff --git a/src/TailBlazor.RadioButton/RadioButtonOrientation.cs b/src/TailBlazor.RadioButton/RadioButtonOrientation.cs index b48930a..68a572c 100644 --- a/src/TailBlazor.RadioButton/RadioButtonOrientation.cs +++ b/src/TailBlazor.RadioButton/RadioButtonOrientation.cs @@ -1,22 +1,22 @@ -using System; +using System; -namespace TailBlazor.RadioButton +namespace TailBlazor.RadioButton; + +public enum RadioButtonOrientation { - public enum RadioButtonOrientation - { - [Class("flex flex-col")] - Vertical, - [Class("flex flex-row")] - Horizontal - } + [Class("flex flex-col")] + Vertical, + [Class("flex flex-row")] + Horizontal +} - public class ClassAttribute : Attribute +[AttributeUsage(AttributeTargets.Field)] +public sealed class ClassAttribute : Attribute +{ + public ClassAttribute(string value) { - public string Value { get; set; } - - public ClassAttribute(string value) - { - this.Value = value; - } + Value = value; } + + public string Value { get; } } diff --git a/src/TailBlazor.RadioButton/RadioItem.cs b/src/TailBlazor.RadioButton/RadioItem.cs index 27a1b53..d20cd8e 100644 --- a/src/TailBlazor.RadioButton/RadioItem.cs +++ b/src/TailBlazor.RadioButton/RadioItem.cs @@ -1,13 +1,14 @@ -namespace TailBlazor.RadioButton +namespace TailBlazor.RadioButton; + +/// +/// A model that will hold details for the radio inputs, in case the user wants to use a list. +/// +/// the type for the Value param +public class RadioItem { - /// - /// A model that will hold details for the radio inputs, incase the user wants to use a list. - /// - /// the type for the Value param - public class RadioItem - { - public string Id { get; set; } - public string Text { get; set; } - public TValue Value { get; set; } - } + public string? Id { get; set; } + + public string? Text { get; set; } + + public required TValue Value { get; set; } } diff --git a/src/TailBlazor.RadioButton/TailBlazor.RadioButton.csproj b/src/TailBlazor.RadioButton/TailBlazor.RadioButton.csproj index d5e38c1..75afc83 100644 --- a/src/TailBlazor.RadioButton/TailBlazor.RadioButton.csproj +++ b/src/TailBlazor.RadioButton/TailBlazor.RadioButton.csproj @@ -1,9 +1,9 @@ - + - net5.0 + net9.0;net9.0-browser TailBlazor.RadioButton TailBlazor.RadioButton - 1.0.1 + 1.1.0 Rogerio Ribeiro true Blazor; TailBlazorcss; TailBlazor css; tailwind; tailwindcss; RadioButton; radio; Radio; Radio Button; @@ -17,14 +17,12 @@ true true logo.png + enable + enable - - - - - + diff --git a/src/TailBlazor.RadioButton/_Imports.razor b/src/TailBlazor.RadioButton/_Imports.razor index 7728512..e6ba56e 100644 --- a/src/TailBlazor.RadioButton/_Imports.razor +++ b/src/TailBlazor.RadioButton/_Imports.razor @@ -1 +1,4 @@ -@using Microsoft.AspNetCore.Components.Web +@using System.Collections.Generic +@using System.Linq +@using Microsoft.AspNetCore.Components +@using Microsoft.AspNetCore.Components.Web