Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(components): add switch component #31

Merged
merged 7 commits into from
Jul 6, 2024
51 changes: 51 additions & 0 deletions src/LumexUI/Components/Bases/LumexBooleanInputBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Diagnostics.CodeAnalysis;

using Microsoft.AspNetCore.Components;

namespace LumexUI;

public abstract class LumexBooleanInputBase : LumexInputBase<bool>
{
/// <summary>
/// Gets or sets content to be rendered inside the input.
/// </summary>
[Parameter] public RenderFragment? ChildContent { get; set; }

/// <summary>
/// Gets the disabled state of the input.
/// Derived classes can override this to determine the input's disabled state.
/// </summary>
/// <returns>A <see cref="bool"/> value indicating whether the input is disabled.</returns>
protected internal virtual bool GetDisabledState() => Disabled;

/// <summary>
/// Gets the readonly state of the input.
/// Derived classes can override this to determine the input's readonly state.
/// </summary>
/// <returns>A <see cref="bool"/> value indicating whether the input is readonly.</returns>
protected internal virtual bool GetReadOnlyState() => ReadOnly;

/// <inheritdoc />
protected override bool TryParseValueFromString( string? value, [MaybeNullWhen( false )] out bool result )
{
throw new NotSupportedException(
$"This component does not parse string inputs. " +
$"Bind to the '{nameof( CurrentValue )}' property, not '{nameof( CurrentValueAsString )}'." );
}

/// <summary>
/// Handles the change event asynchronously.
/// Derived classes can override this to specify custom behavior when the input's value changes.
/// </summary>
/// <param name="args">The change event arguments.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected virtual Task OnChangeAsync( ChangeEventArgs args )
{
if( GetDisabledState() || GetReadOnlyState() )
{
return Task.CompletedTask;
}

return SetCurrentValueAsync( (bool)args.Value! );
}
}
8 changes: 0 additions & 8 deletions src/LumexUI/Components/Bases/LumexInputBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,6 @@ public abstract class LumexInputBase<TValue> : LumexComponentBase
/// </remarks>
[Parameter] public ThemeColor Color { get; set; }

/// <summary>
/// Gets or sets the border radius of the input.
/// </summary>
/// <remarks>
/// The default is <see cref="Radius.Medium"/>
/// </remarks>
[Parameter] public Radius Radius { get; set; } = Radius.Medium;

/// <summary>
/// Gets or sets the size of the input.
/// </summary>
Expand Down
18 changes: 10 additions & 8 deletions src/LumexUI/Components/Checkbox/LumexCheckbox.razor
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
@namespace LumexUI
@inherits LumexInputBase<bool>
@inherits LumexBooleanInputBase

<LumexComponent As="label"
Class="@RootClass"
Style="@RootStyle"
data-checked="@_checked"
tabindex="1"
data-checked="@CurrentValue"
data-disabled="@GetDisabledState()"
data-readonly="@GetReadOnlyState()"
tabindex="0"
@attributes="@AdditionalAttributes">
<span class="@Utils.VisuallyHidden">
<input type="checkbox"
value="@true"
checked="@_checked"
disabled="@_disabled"
checked="@CurrentValue"
disabled="@GetDisabledState()"
@ref="@Element"
@attributes="@AdditionalAttributes"
@onchange="@OnChangeAsync" />
Expand Down Expand Up @@ -40,19 +42,19 @@
private void RenderCheckIcon( RenderTreeBuilder __builder )
{
var style = ElementStyle.Empty()
.Add( "transition", "stroke-dashoffset 0.15s linear 0.15s", when: _checked )
.Add( "transition", "stroke-dashoffset 0.15s linear 0.15s", when: CurrentValue )
.ToString();

<svg class="@IconClass" viewBox="0 0 17 18">
<polyline fill="none"
points="1 9 7 14 15 4"
stroke="currentColor"
stroke-dasharray="22"
stroke-dashoffset="@(_checked ? 44 : 66)"
stroke-dashoffset="@(CurrentValue ? 44 : 66)"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
style="@style" />
</svg>
}
}
}
45 changes: 12 additions & 33 deletions src/LumexUI/Components/Checkbox/LumexCheckbox.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@

namespace LumexUI;

public partial class LumexCheckbox : LumexInputBase<bool>, ISlotComponent<CheckboxSlots>
public partial class LumexCheckbox : LumexBooleanInputBase, ISlotComponent<CheckboxSlots>
{
/// <summary>
/// Gets or sets content to be rendered inside the checkbox.
/// Gets or sets the border radius of the checkbox.
/// </summary>
[Parameter] public RenderFragment? ChildContent { get; set; }
/// <remarks>
/// The default is <see cref="Radius.Medium"/>
/// </remarks>
[Parameter] public Radius Radius { get; set; } = Radius.Medium;

/// <summary>
/// Gets or sets the icon to be used for indicating a checked state of the checkbox.
Expand Down Expand Up @@ -42,10 +45,6 @@ public partial class LumexCheckbox : LumexInputBase<bool>, ISlotComponent<Checkb

private readonly RenderFragment _renderCheckIcon;

private bool _checked;
private bool _disabled;
private bool _readonly;

/// <summary>
/// Initializes a new instance of the <see cref="LumexCheckbox"/>.
/// </summary>
Expand All @@ -59,7 +58,7 @@ public override async Task SetParametersAsync( ParameterView parameters )
{
await base.SetParametersAsync( parameters );

Color = parameters.TryGetValue<ThemeColor>( nameof( Color ), out var color )
Color = parameters.TryGetValue<ThemeColor>( nameof( Color ), out var color )
? color
: Context?.Owner.Color ?? ThemeColor.Primary;

Expand All @@ -78,30 +77,10 @@ public override async Task SetParametersAsync( ParameterView parameters )
}

/// <inheritdoc />
protected override void OnParametersSet()
{
_checked = CurrentValue;
_disabled = Disabled || ( Context?.Owner.Disabled ?? false );
_readonly = ReadOnly || ( Context?.Owner.ReadOnly ?? false );
}
protected internal override bool GetDisabledState() =>
Disabled || ( Context?.Owner.Disabled ?? false );

/// <inheritdoc />
protected override bool TryParseValueFromString( string? value, out bool result )
{
throw new NotSupportedException(
$"This component does not parse string inputs. " +
$"Bind to the '{nameof( CurrentValue )}' property, not '{nameof( CurrentValueAsString )}'." );
}

internal bool GetDisabledState() => _disabled;

private Task OnChangeAsync( ChangeEventArgs args )
{
if( _disabled || _readonly )
{
return Task.CompletedTask;
}

return SetCurrentValueAsync( (bool)args.Value! );
}
}
protected internal override bool GetReadOnlyState() =>
ReadOnly || ( Context?.Owner.ReadOnly ?? false );
}
47 changes: 47 additions & 0 deletions src/LumexUI/Components/Switch/LumexSwitch.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@namespace LumexUI
@inherits LumexBooleanInputBase

<LumexComponent As="label"
Class="@RootClass"
Style="@RootStyle"
data-checked="@CurrentValue"
data-disabled="@GetDisabledState()"
data-readonly="@GetReadOnlyState()"
tabindex="0"
@attributes="@AdditionalAttributes">
<span class="@Utils.VisuallyHidden">
<input type="checkbox"
value="@true"
checked="@CurrentValue"
disabled="@GetDisabledState()"
@ref="@Element"
@attributes="@AdditionalAttributes"
@onchange="@OnChangeAsync" />
</span>

<span class="@WrapperClass">
@if( !string.IsNullOrEmpty( StartIcon ) )
{
<LumexIcon Icon="@StartIcon" Size="@(new("100%"))" Class="@StartIconClass" />
}

<span class="@ThumbClass">
@if( !string.IsNullOrEmpty( ThumbIcon ) )
{
<LumexIcon Icon="@ThumbIcon" Size="@(new("100%"))" Class="@ThumbIconClass" />
}
</span>

@if( !string.IsNullOrEmpty( EndIcon ) )
{
<LumexIcon Icon="@EndIcon" Size="@(new("100%"))" Class="@EndIconClass" />
}
</span>

@if( ChildContent is not null )
{
<span class="@LabelClass">
@ChildContent
</span>
}
</LumexComponent>
59 changes: 59 additions & 0 deletions src/LumexUI/Components/Switch/LumexSwitch.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) LumexUI 2024
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

using LumexUI.Common;
using LumexUI.Styles;

using Microsoft.AspNetCore.Components;

namespace LumexUI;

public partial class LumexSwitch : LumexBooleanInputBase, ISlotComponent<SwitchSlots>
{
/// <summary>
/// Gets or sets the icon to be used for indicating a checked state of the switch.
/// </summary>
[Parameter] public string? ThumbIcon { get; set; }

/// <summary>
/// Gets or sets the icon to be rendered before the switch.
/// </summary>
[Parameter] public string? StartIcon { get; set; }

/// <summary>
/// Gets or sets the icon to be rendered after the switch.
/// </summary>
[Parameter] public string? EndIcon { get; set; }

/// <summary>
/// Gets or sets the CSS class names for the switch slots.
/// </summary>
[Parameter] public SwitchSlots? Classes { get; set; }

private protected override string? RootClass =>
TwMerge.Merge( Switch.GetStyles( this ) );

private string? WrapperClass =>
TwMerge.Merge( Switch.GetWrapperStyles( this ) );

private string? ThumbClass =>
TwMerge.Merge( Switch.GetThumbStyles( this ) );

private string? ThumbIconClass =>
TwMerge.Merge( Switch.GetThumbIconStyles( this ) );

private string? StartIconClass =>
TwMerge.Merge( Switch.GetStartIconStyles( this ) );

private string? EndIconClass =>
TwMerge.Merge( Switch.GetEndIconStyles( this ) );

private string? LabelClass =>
TwMerge.Merge( Switch.GetLabelStyles( this ) );

public LumexSwitch()
{
Color = ThemeColor.Primary;
}
}
17 changes: 17 additions & 0 deletions src/LumexUI/Components/Switch/SwitchSlots.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Diagnostics.CodeAnalysis;

using LumexUI.Common;

namespace LumexUI;

[ExcludeFromCodeCoverage]
public class SwitchSlots : ISlot
{
public string? Root { get; set; }
public string? Wrapper { get; set; }
public string? Thumb { get; set; }
public string? StartIcon { get; set; }
public string? EndIcon { get; set; }
public string? ThumbIcon { get; set; }
public string? Label { get; set; }
}
Loading