diff --git a/src/BlazingApple.Components.Shared/BlazingApple.Components.Shared.csproj b/src/BlazingApple.Components.Shared/BlazingApple.Components.Shared.csproj
index 27574be..e462d15 100644
--- a/src/BlazingApple.Components.Shared/BlazingApple.Components.Shared.csproj
+++ b/src/BlazingApple.Components.Shared/BlazingApple.Components.Shared.csproj
@@ -9,10 +9,10 @@
https://github.com/BlazingApple/ComponentsGitHubtrue
- 2.5.4
- 2.5.5
- 2.5.5
- 2.5.5 - Target .NET 8
+ 2.5.6
+ 2.5.6
+ 2.5.6
+ 2.5.6 - Add new SpanType enumtruesnupkgtrue
@@ -27,4 +27,8 @@
\
+
+
+
+
diff --git a/src/BlazingApple.Components.Shared/Models/Time/SpanType.cs b/src/BlazingApple.Components.Shared/Models/Time/SpanType.cs
new file mode 100644
index 0000000..b3192ac
--- /dev/null
+++ b/src/BlazingApple.Components.Shared/Models/Time/SpanType.cs
@@ -0,0 +1,15 @@
+namespace BlazingApple.Components.Shared.Models.Time;
+
+/// The type of span
+///
+public enum SpanType
+{
+ /// Render the span in days
+ Days,
+ /// Render the span in weeks
+ Weeks,
+ /// Render the span in months
+ Months,
+ /// Render the span in years
+ Years,
+}
diff --git a/src/BlazingApple.Components/BlazingApple.Components/BlazingApple.Components.csproj b/src/BlazingApple.Components/BlazingApple.Components/BlazingApple.Components.csproj
index bf769c8..ad6f9d0 100644
--- a/src/BlazingApple.Components/BlazingApple.Components/BlazingApple.Components.csproj
+++ b/src/BlazingApple.Components/BlazingApple.Components/BlazingApple.Components.csproj
@@ -10,10 +10,10 @@
https://github.com/BlazingApple/ComponentsGitHubtrue
- 2.6
- 2.6
- 2.6
- 2.6 - Add toggle button
+ 2.7.0
+ 2.7.0
+ 2.7.0
+ 2.7.0 - New Input Components and Buttonstruesnupkgtrue
@@ -32,12 +32,12 @@
-
+
-
-
-
+
+
+
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/DropDown.razor b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/DropDown.razor
index 57de123..c4df41c 100644
--- a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/DropDown.razor
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/DropDown.razor
@@ -13,7 +13,15 @@
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSlider.razor b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSlider.razor
new file mode 100644
index 0000000..cbdf314
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSlider.razor
@@ -0,0 +1,10 @@
+@using System.Numerics
+@typeparam TNumber where TNumber : INumber
+
+
+
+ @if (ShowCurrentValue)
+ {
+ @Value
+ }
+
\ No newline at end of file
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSlider.razor.cs b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSlider.razor.cs
new file mode 100644
index 0000000..fa665e8
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSlider.razor.cs
@@ -0,0 +1,58 @@
+using System.Numerics;
+
+namespace BlazingApple.Components.HTMLElements;
+
+/// Slider control.
+/// A type of number
+public partial class InputSlider : ComponentBase
+ where TNumber : INumber
+{
+ private readonly Guid _uniqueId = Guid.NewGuid();
+
+ /// Captures/renders standard HTML attributes passed
+ [Parameter(CaptureUnmatchedValues = true)]
+ public IDictionary AdditionalAttributes { get; set; } = null!;
+
+ /// Label for the switch
+ [Parameter]
+ public string? Label { get; set; }
+
+ /// Whether or not to show the current value.
+ [Parameter]
+ public bool ShowCurrentValue { get; set; } = true;
+
+ /// Display text/title text to show on hover, if any.
+ [Parameter]
+ public string? TitleText { get; set; }
+
+ /// Child content to render, if any.
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+
+ /// A reference to the Filter object that a consumer passes in.
+ [Parameter]
+ public TNumber Value { get; set; } = default!;
+
+ /// Min value of the range.
+ [Parameter]
+ public TNumber? Min { get; set; }
+
+ /// Max value of the range.
+ [Parameter]
+ public TNumber? Max { get; set; }
+
+ /// Step of the range slider.
+ [Parameter]
+ public TNumber Step { get; set; } = TNumber.One;
+
+ /// Allows binding to the Value parameter.
+ [Parameter]
+ public EventCallback ValueChanged { get; set; }
+
+ private async Task ValueChangedInternal(ChangeEventArgs args)
+ {
+ Value = TNumber.Parse(args.Value!.ToString()!, System.Globalization.NumberStyles.Number, null);
+ if (ValueChanged.HasDelegate)
+ await ValueChanged.InvokeAsync(Value);
+ }
+}
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSwitch.razor b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSwitch.razor
new file mode 100644
index 0000000..4656af1
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSwitch.razor
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSwitch.razor.cs b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSwitch.razor.cs
new file mode 100644
index 0000000..7853bcb
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputSwitch.razor.cs
@@ -0,0 +1,35 @@
+namespace BlazingApple.Components.HTMLElements;
+
+/// Renders a checkbox as a switch.
+/// See https://getbootstrap.com/docs/5.0/forms/checks-radios/#switches
+public partial class InputSwitch : ComponentBase
+{
+ private readonly Guid _uniqueId = Guid.NewGuid();
+
+ /// Label for the switch
+ [Parameter]
+ public string? Label { get; set; }
+
+ /// Display text/title text to show on hover, if any.
+ [Parameter]
+ public string? TitleText { get; set; }
+
+ /// Child content to render, if any.
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+
+ /// A reference to the Filter object that a consumer passes in.
+ [Parameter]
+ public bool Value { get; set; } = default!;
+
+ /// Allows binding to the Value parameter.
+ [Parameter]
+ public EventCallback ValueChanged { get; set; }
+
+ private async Task ValueChangedInternal(ChangeEventArgs args)
+ {
+ Value = (bool)args.Value!;
+ if (ValueChanged.HasDelegate)
+ await ValueChanged.InvokeAsync(Value);
+ }
+}
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputTimeSpan.razor b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputTimeSpan.razor
new file mode 100644
index 0000000..68b4018
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputTimeSpan.razor
@@ -0,0 +1,23 @@
+@using BlazingApple.Components.Shared.Models.Time
+@using Humanizer
+
+
+
+ @if (!Units.HasValue)
+ {
+
+ @foreach (SpanType val in Enum.GetValues())
+ {
+
+
\ No newline at end of file
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputTimeSpan.razor.cs b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputTimeSpan.razor.cs
new file mode 100644
index 0000000..b38b1ca
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/InputTimeSpan.razor.cs
@@ -0,0 +1,164 @@
+using Syncfusion.Blazor.InPlaceEditor;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BlazingApple.Components.Shared.Models.Time;
+
+namespace BlazingApple.Components.HTMLElements;
+
+/// Allows setting a timespan, using only even numbers.
+public partial class InputTimeSpan : ComponentBase
+{
+ private readonly Guid _uniqueId = Guid.NewGuid();
+ private int _countOfSpan;
+ private int _maxForSpan;
+ private SpanType _spanType;
+
+ /// Captures/renders standard HTML attributes passed
+ [Parameter(CaptureUnmatchedValues = true)]
+ public IDictionary AdditionalAttributes { get; set; } = null!;
+
+ /// Label for the switch
+ [Parameter]
+ public string? Label { get; set; }
+
+ /// A reference to the Filter object that a consumer passes in.
+ [Parameter]
+ public TimeSpan? Value { get; set; } = default!;
+
+ /// Allows binding to the Value parameter.
+ [Parameter]
+ public EventCallback ValueChanged { get; set; }
+
+ /// Forces the units of the control.
+ [Parameter]
+ public SpanType? Units { get; set; }
+
+ /// The maximum span.
+ [Parameter]
+ public TimeSpan Max { get; set; } = new(365, 0, 0, 0);
+
+ ///
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ if (Units.HasValue)
+ {
+ _spanType = Units.Value;
+ _maxForSpan = ToCount(_spanType, (int)Math.Floor(Max.TotalDays));
+ return;
+ }
+
+ if (Max == default)
+ {
+ _spanType = SpanType.Days;
+ _maxForSpan = 30;
+ }
+
+ if (Max.TotalDays <= 21)
+ {
+ _spanType = SpanType.Days;
+ _maxForSpan = (int)Math.Floor(Max.TotalDays);
+ }
+ else if (Max.TotalDays is > 21 and <= 84)
+ {
+ // 84 days == 12 weeks
+ _spanType = SpanType.Weeks;
+ _maxForSpan = (int)Math.Floor(Max.TotalDays / 7);
+ }
+ else if (Max.TotalDays is > 84 and <= 900)
+ {
+ _spanType = SpanType.Months;
+ _maxForSpan = (int)Math.Floor(Max.TotalDays / 30);
+ }
+ else if (Max.TotalDays > 900)
+ {
+ _spanType = SpanType.Years;
+ _maxForSpan = (int)Math.Floor(Max.TotalDays / 365);
+ }
+ }
+
+ ///
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+ if (Value.HasValue)
+ _countOfSpan = ToCount(_spanType, (int)Math.Floor(Value.Value.TotalDays));
+ }
+
+ private static TimeSpan ToSpan(SpanType spanType, int count)
+ {
+ TimeSpan span = spanType switch
+ {
+ SpanType.Days => new TimeSpan(count, 0, 0, 0),
+ SpanType.Weeks => new TimeSpan(count * 7, 0, 0, 0),
+ SpanType.Months => new TimeSpan(count * 30, 0, 0, 0),
+ SpanType.Years => new TimeSpan(count * 365, 0, 0, 0),
+ _ => throw new ArgumentOutOfRangeException(nameof(spanType)),
+ };
+ return span;
+ }
+
+ private static int ToCount(SpanType spanType, int maxInDays)
+ {
+ int countForSpan = spanType switch
+ {
+ SpanType.Days => maxInDays,
+ SpanType.Weeks => maxInDays / 7,
+ SpanType.Months => maxInDays / 30,
+ SpanType.Years => maxInDays / 365,
+ _ => throw new ArgumentOutOfRangeException(nameof(spanType)),
+ };
+ return countForSpan;
+ }
+
+ private static int ToCountInDays(SpanType spanType, int countInSpan)
+ {
+ int countInDays = spanType switch
+ {
+ SpanType.Days => countInSpan,
+ SpanType.Weeks => countInSpan * 7,
+ SpanType.Months => countInSpan * 30,
+ SpanType.Years => countInSpan * 365,
+ _ => throw new ArgumentOutOfRangeException(nameof(spanType)),
+ };
+ return countInDays;
+ }
+
+ private static int ToCount(SpanType oldSpan, int countInOldSpan, SpanType newSpan)
+ {
+ int countInDays = ToCountInDays(oldSpan, countInOldSpan);
+ return ToCount(newSpan, countInDays);
+ }
+
+ private async Task UpdateValue()
+ {
+ Value = ToSpan(_spanType, _countOfSpan);
+ await ValueChangedInternal();
+ }
+
+ private async Task ValueChangedInternal()
+ {
+ if (ValueChanged.HasDelegate)
+ await ValueChanged.InvokeAsync(Value);
+ }
+
+ private async Task CountChanged(int count)
+ {
+ _countOfSpan = count;
+ await UpdateValue();
+ }
+
+ private async Task SpanTypeChanged(SpanType spanType)
+ {
+ _countOfSpan = ToCount(_spanType, _countOfSpan, spanType);
+ _spanType = spanType;
+
+ _maxForSpan = ToCount(_spanType, Convert.ToInt16(Math.Floor(Max.TotalDays)));
+
+ await UpdateValue();
+ }
+}
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickDate.razor b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickDate.razor
new file mode 100644
index 0000000..d986950
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickDate.razor
@@ -0,0 +1,11 @@
+@typeparam TValue
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickDate.razor.cs b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickDate.razor.cs
new file mode 100644
index 0000000..8386659
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickDate.razor.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BlazingApple.Components.HTMLElements;
+
+/// Allows for quick date selection, like tomorrow, next week, etc.
+/// Supported types are and DateTime?
+public partial class QuickDate : ComponentBase
+{
+ /// Any parameter provided to the component that doesn't match a parameter, will be provided here as a dictionary.
+ [Parameter(CaptureUnmatchedValues = true)]
+ public IReadOnlyDictionary? AdditionalAttributes { get; set; }
+
+ /// A reference to the Filter object that a consumer passes in.
+ [Parameter]
+ public TValue? Value { get; set; }
+
+ /// that allows binding to .
+ [Parameter]
+ public EventCallback ValueChanged { get; set; }
+
+ /// Used internally to trigger other event callbacks.
+ private TValue? BoundValue
+ {
+ get => Value;
+ set
+ {
+ Value = value;
+ ValueChanged.InvokeAsync(Value);
+ }
+ }
+
+ ///
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+
+ Type type = typeof(TValue);
+ if (type != typeof(DateTime?) && type != typeof(DateTime))
+ throw new ArgumentOutOfRangeException(nameof(TValue), $"Unsupported type passed to QuickDate, received {typeof(TValue)}");
+ }
+
+ private async Task OnQuickDateSelect(int daysFromNow)
+ {
+ Value = (TValue)(object)DateTime.Now.AddDays(daysFromNow);
+
+ if (ValueChanged.HasDelegate)
+ await ValueChanged.InvokeAsync(Value);
+ }
+}
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickNumber.razor b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickNumber.razor
new file mode 100644
index 0000000..a9f78c0
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickNumber.razor
@@ -0,0 +1,37 @@
+@typeparam TValue
+
+@if (ButtonsOnly)
+{
+
+
+
+
+ @if (SingularUnit is not null && Convert.ToDouble(BoundValue) == 1d)
+ {
+ @($"{BoundValue} {SingularUnit}")
+ }
+ else if (PluralUnit is not null && Convert.ToDouble(BoundValue) != 1d)
+ {
+ @($"{BoundValue} {PluralUnit}")
+ }
+ else
+ {
+ @BoundValue
+ }
+
+
+
+
+}
+else
+{
+
+
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickNumber.razor.cs b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickNumber.razor.cs
new file mode 100644
index 0000000..be4ca6c
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/QuickNumber.razor.cs
@@ -0,0 +1,99 @@
+using System.Numerics;
+
+namespace BlazingApple.Components.HTMLElements;
+
+/// Allows quickly selecting/alternating a number.
+/// The type of data bound to this.
+public partial class QuickNumber : ComponentBase
+ where TValue : INumber
+{
+ /// Any parameter provided to the component that doesn't match a parameter, will be provided here as a dictionary.
+ [Parameter(CaptureUnmatchedValues = true)]
+ public IReadOnlyDictionary? AdditionalAttributes { get; set; }
+
+ /// If true, the input field will be hidden.
+ [Parameter]
+ public bool ButtonsOnly { get; set; }
+
+ /// If true, the user will not be able to modify the value.
+ [Parameter]
+ public bool Disabled { get; set; }
+
+ /// The amount by which to increment/decrement by.
+ [Parameter]
+ public TValue IncrementBy { get; set; } = TValue.One;
+
+ /// The max value
+ [Parameter]
+ public TValue? MaxValue { get; set; }
+
+ /// The minimum value.
+ [Parameter]
+ public TValue? MinValue { get; set; }
+
+ /// The unit term for a nonzero instance of the unit (if the value is exactly one).
+ [Parameter]
+ public string? PluralUnit { get; set; }
+
+ /// The unit term for a single instance of the unit (if the value is exactly one).
+ [Parameter]
+ public string? SingularUnit { get; set; }
+
+ /// A reference to the Filter object that a consumer passes in.
+ [Parameter]
+ public TValue Value { get; set; } = default!;
+
+ /// Eventcallback that allows people to bind to the Value parameter passed in.
+ [Parameter]
+ public EventCallback ValueChanged { get; set; }
+
+ private TValue BoundValue
+ {
+ get => Value;
+ set
+ {
+ Value = value;
+ if (ValueChanged.HasDelegate)
+ ValueChanged.InvokeAsync(Value);
+ }
+ }
+
+ private bool CanPressButton(bool isIncrement)
+ {
+ if (Disabled)
+ return false;
+
+ if (isIncrement && MaxValue is null)
+ return true;
+ else if (MinValue is null)
+ return true;
+
+ TValue val;
+
+ if (isIncrement)
+ val = Value + IncrementBy;
+ else
+ val = Value - IncrementBy;
+
+ if (MinValue is not null && !isIncrement && val.CompareTo(MinValue) < 0)
+ return false;
+ else if (MaxValue is not null && MaxValue.CompareTo(0) != 0 && isIncrement && val.CompareTo(MaxValue) > 0)
+ return false;
+ else
+ return true;
+ }
+
+ private async Task OnQuickSelect(bool isIncrement)
+ {
+ if (Disabled)
+ return;
+
+ if (isIncrement)
+ Value += IncrementBy;
+ else
+ Value -= IncrementBy;
+
+ if (ValueChanged.HasDelegate)
+ await ValueChanged.InvokeAsync(Value);
+ }
+}
diff --git a/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/SplitButtonDropdown.razor b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/SplitButtonDropdown.razor
new file mode 100644
index 0000000..6b91c1e
--- /dev/null
+++ b/src/BlazingApple.Components/BlazingApple.Components/HTMLElements/SplitButtonDropdown.razor
@@ -0,0 +1,19 @@
+@if (_expanded)
+{
+