diff --git a/src/Connected.Components/Annotations/CategoryAttribute.cs b/src/Connected.Components/Annotations/CategoryAttribute.cs
index 063d1d3..ef7b589 100644
--- a/src/Connected.Components/Annotations/CategoryAttribute.cs
+++ b/src/Connected.Components/Annotations/CategoryAttribute.cs
@@ -126,7 +126,7 @@ public static class CategoryTypes
public const string Common = "Common";
}
- /// Used in: , all components inheriting from it, and .
+ /// Used in: , all components inheriting from it, and .
public static class Button
{
public const string Behavior = "Behavior";
diff --git a/src/Connected.Components/Components/AppBar/AppBar.razor.cs b/src/Connected.Components/Components/AppBar/AppBar.razor.cs
index 416b35a..1465e43 100644
--- a/src/Connected.Components/Components/AppBar/AppBar.razor.cs
+++ b/src/Connected.Components/Components/AppBar/AppBar.razor.cs
@@ -1,12 +1,25 @@
-using Connected.Extensions;
-using Connected.Utilities;
+using Connected.Utilities;
using Microsoft.AspNetCore.Components;
-using System.Security;
namespace Connected.Components;
public partial class AppBar : UIComponent
{
+
+ #region Event callbacks
+ #endregion
+
+ #region Content placeholders
+
+ ///
+ /// Child content of the component.
+ ///
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+
+ #endregion
+
+ #region Styling properties
///
/// The classlist determining the toolbar render.
///
@@ -18,6 +31,12 @@ public partial class AppBar : UIComponent
.AddClass(ToolbarClassList);
}
}
+ ///
+ /// Class names for the nested toolbar. In case of several, separate by spaces.
+ ///
+ [Parameter]
+ public string? ToolbarClassList { get; set; }
+
///
/// The classlist determining the header render.
@@ -32,19 +51,6 @@ public partial class AppBar : UIComponent
.AddClass(HeaderClassList);
}
}
-
- ///
- /// Child content of the component.
- ///
- [Parameter]
- public RenderFragment? ChildContent { get; set; }
-
- ///
- /// If true, appbar will be fixed.
- ///
- [Parameter]
- public bool Fixed { get; set; } = true;
-
///
/// Class names for the AppBar header. In case of several, separate by spaces.
///
@@ -52,14 +58,29 @@ public partial class AppBar : UIComponent
public string? HeaderClassList { get; set; }
///
- /// Class names for the nested toolbar. In case of several, separate by spaces.
+ /// Determines the vertical alignment of the AppBar.
///
[Parameter]
- public string? ToolbarClassList { get; set; }
+ public VerticalAlignment VerticalAlignment { get; set; }
///
- /// Determines the vertical alignment of the AppBar.
+ /// If true, appbar will be fixed.
///
[Parameter]
- public VerticalAlignment VerticalAlignment { get; set; }
+ public bool Fixed { get; set; } = true;
+
+ #endregion
+
+ #region Lifecycle
+ #endregion
+
+
+
+
+
+
+
+
+
+
}
diff --git a/src/Connected.Components/Components/Autocomplete/Autocomplete.razor.cs b/src/Connected.Components/Components/Autocomplete/Autocomplete.razor.cs
index 9d3eea9..f943830 100644
--- a/src/Connected.Components/Components/Autocomplete/Autocomplete.razor.cs
+++ b/src/Connected.Components/Components/Autocomplete/Autocomplete.razor.cs
@@ -6,687 +6,727 @@ namespace Connected.Components;
public partial class Autocomplete : InputBase, IDisposable
{
- private Func? _toStringFunc;
- private Task _currentSearchTask;
- private CancellationTokenSource _cancellationTokenSrc;
- private bool _isOpen;
- private Timer _timer;
- private T[] _items;
- private int _selectedListItemIndex = 0;
- private IList _enabledItemIndices = new List();
- private int _itemsReturned; //the number of items returned by the search function
- int _elementKey = 0;
- ///
- /// This boolean will keep track if the clear function is called too keep the set text function to be called.
- ///
- private bool _isCleared;
- private Input _elementReference;
- ///
- /// We need a random id for the year items in the year list so we can scroll to the item safely in every DatePicker.
- ///
- private readonly string _componentId = Guid.NewGuid().ToString();
-
- public Autocomplete()
- {
- Adornment = Adornment.End;
- IconSize = Size.Medium;
- }
-
- [Inject]
- IScrollManager ScrollManager { get; set; }
- ///
- /// User class names for the popover, separated by space
- ///
- [Parameter]
- public string PopoverClass { get; set; }
- ///
- /// Set the anchor origin point to determen where the popover will open from.
- ///
- [Parameter]
- public Origin AnchorOrigin { get; set; } = Origin.BottomCenter;
- ///
- /// Sets the transform origin point for the popover.
- ///
- [Parameter]
- public Origin TransformOrigin { get; set; } = Origin.TopCenter;
- ///
- /// If true, compact vertical padding will be applied to all Autocomplete items.
- ///
- [Parameter]
- public bool Dense { get; set; }
- ///
- /// The Open Autocomplete Glyph
- ///
- [Parameter]
- public string OpenIcon { get; set; } = Icons.Material.Filled.ArrowDropDown;
- ///
- /// The Close Autocomplete Glyph
- ///
- [Parameter]
- public string CloseIcon { get; set; } = Icons.Material.Filled.ArrowDropUp;
- ///
- /// The maximum height of the Autocomplete when it is open.
- ///
- [Parameter]
- public int MaxHeight { get; set; } = 300;
- ///
- /// Defines how values are displayed in the drop-down list
- ///
- [Parameter]
- public Func? ToStringFunc
- {
- get => _toStringFunc;
- set
- {
- if (_toStringFunc == value)
- return;
-
- _toStringFunc = value;
-
- SetConverter(new LambdaConverter(_toStringFunc ?? (x => x?.ToString()), null));
- }
- }
- ///
- /// Whether to show the progress indicator.
- ///
- [Parameter]
- public bool ShowProgressIndicator { get; set; } = false;
- ///
- /// The color of the progress indicator.
- ///
- [Parameter]
- public ThemeColor ProgressIndicatorColor { get; set; } = ThemeColor.Default;
- private bool IsLoading => _currentSearchTask is not null && !_currentSearchTask.IsCompleted;
- ///
- /// Func that returns a list of items matching the typed text. Provides a cancellation token that
- /// is marked as cancelled when the user changes the search text or selects a value from the list.
- /// This can be used to cancel expensive asynchronous work occuring within the SearchFunc itself.
- ///
- [Parameter]
- public Func>> SearchFuncWithCancel { get; set; }
- ///
- /// The SearchFunc returns a list of items matching the typed text
- ///
- [Parameter]
- public Func>> SearchFunc { get; set; }
- ///
- /// Maximum items to display, defaults to 10.
- /// A null value will display all items.
- ///
- [Parameter]
- public int? MaxItems { get; set; } = 10;
- ///
- /// Minimum characters to initiate a search
- ///
- [Parameter]
- public int MinCharacters { get; set; } = 0;
- ///
- /// Reset value if user deletes the text
- ///
- [Parameter]
- public bool ResetValueOnEmptyText { get; set; } = false;
- ///
- /// If true, clicking the text field will select (highlight) its contents.
- ///
- [Parameter]
- public bool SelectOnClick { get; set; } = true;
- ///
- /// Debounce interval in milliseconds.
- ///
- [Parameter]
- public int DebounceInterval { get; set; } = 100;
- ///
- /// Optional presentation template for unselected items
- ///
- [Parameter]
- public RenderFragment ItemTemplate { get; set; }
- ///
- /// Optional presentation template for the selected item
- ///
- [Parameter]
- public RenderFragment ItemSelectedTemplate { get; set; }
- ///
- /// Optional presentation template for disabled item
- ///
- [Parameter]
- public RenderFragment ItemDisabledTemplate { get; set; }
- ///
- /// Optional presentation template for when more items were returned from the Search function than the MaxItems limit
- ///
- [Parameter]
- public RenderFragment MoreItemsTemplate { get; set; }
- ///
- /// Optional presentation template for when no items were returned from the Search function
- ///
- [Parameter]
- public RenderFragment NoItemsTemplate { get; set; }
- ///
- /// Optional template for progress indicator
- ///
- [Parameter]
- public RenderFragment ProgressIndicatorTemplate { get; set; }
- ///
- /// Optional template for showing progress indicator inside the popover
- ///
- [Parameter]
- public RenderFragment ProgressIndicatorInPopoverTemplate { get; set; }
- ///
- /// On drop-down close override Text with selected Value. This makes it clear to the user
- /// which list value is currently selected and disallows incomplete values in Text.
- ///
- [Parameter]
- public bool CoerceText { get; set; } = true;
- ///
- /// If user input is not found by the search func and CoerceValue is set to true the user input
- /// will be applied to the Value which allows to validate it and display an error message.
- ///
- [Parameter]
- public bool CoerceValue { get; set; }
- ///
- /// Function to be invoked when checking whether an item should be disabled or not
- ///
- [Parameter]
- public Func ItemDisabledFunc { get; set; }
- ///
- /// Returns the open state of the drop-down.
- ///
- public bool IsOpen
- {
- get => _isOpen;
- // Note: the setter is protected because it was needed by a user who derived his own autocomplete from this class.
- // Note: setting IsOpen will not open or close it. Use ToggleMenu() for that.
- protected set
- {
- if (value == _isOpen)
- return;
-
- _isOpen = value;
-
- IsOpenChanged.InvokeAsync(_isOpen).AndForget();
- }
- }
- ///
- /// An event triggered when the state of IsOpen has changed
- ///
- [Parameter]
- public EventCallback IsOpenChanged { get; set; }
- ///
- /// If true, the currently selected item from the drop-down (if it is open) is selected.
- ///
- [Parameter]
- public bool SelectValueOnTab { get; set; } = false;
- ///
- /// Show clear button.
- ///
- [Parameter]
- public bool Clearable { get; set; } = false;
- ///
- /// Button click event for clear button. Called after text and value has been cleared.
- ///
- [Parameter]
- public EventCallback OnClearButtonClick { get; set; }
-
- private string CurrentIcon => !string.IsNullOrWhiteSpace(AdornmentIcon) ? AdornmentIcon : _isOpen ? CloseIcon : OpenIcon;
-
- protected string ClassList()
- {
- return new CssBuilder("select")
- .Build();
- }
-
- protected string AutocompleteClassList()
- {
- return new CssBuilder("select")
- .AddClass("autocomplete")
- .AddClass("width-full", FullWidth)
- .AddClass("autocomplete--with-progress", ShowProgressIndicator && IsLoading)
- .Build();
- }
-
- protected string CircularProgressClassList()
- {
- return new CssBuilder("progress-indicator-circular")
- .AddClass("progress-indicator-circular--with-adornment", Adornment == Adornment.End)
- .Build();
- }
-
- public async Task SelectOption(T value)
- {
- await SetValueAsync(value);
-
- if (_items is not null)
- _selectedListItemIndex = Array.IndexOf(_items, value);
-
- var optionText = GetItemString(value);
-
- if (!_isCleared)
- await SetTextAsync(optionText, false);
-
- _timer?.Dispose();
-
- IsOpen = false;
-
- BeginValidate();
-
- if (!_isCleared)
- _elementReference?.SetText(optionText);
-
- _elementReference?.FocusAsync().AndForget();
-
- StateHasChanged();
- }
- ///
- /// Toggle the menu (if not disabled or not readonly, and is opened).
- ///
- public async Task ToggleMenu()
- {
- if ((Disabled || ReadOnly) && !IsOpen)
- return;
-
- await ChangeMenu(!IsOpen);
- }
-
- private async Task ChangeMenu(bool open)
- {
- if (open)
- {
- if (SelectOnClick)
- await _elementReference.SelectAsync();
-
- await OnSearchAsync();
- }
- else
- {
- _timer?.Dispose();
-
- RestoreScrollPosition();
-
- await CoerceTextToValue();
-
- IsOpen = false;
-
- StateHasChanged();
- }
- }
-
- protected override void OnInitialized()
- {
- var text = GetItemString(Value);
-
- if (!string.IsNullOrWhiteSpace(text))
- Text = text;
- }
-
- protected override void OnAfterRender(bool firstRender)
- {
- _isCleared = false;
-
- base.OnAfterRender(firstRender);
- }
-
- protected override Task UpdateTextPropertyAsync(bool updateValue)
- {
- _timer?.Dispose();
- // This keeps the text from being set when clear() was called
- if (_isCleared)
- return Task.CompletedTask;
-
- return base.UpdateTextPropertyAsync(updateValue);
- }
-
- protected override async Task UpdateValuePropertyAsync(bool updateText)
- {
- _timer?.Dispose();
-
- if (ResetValueOnEmptyText && string.IsNullOrWhiteSpace(Text))
- await SetValueAsync(default, updateText);
-
- if (DebounceInterval <= 0)
- await OnSearchAsync();
- else
- _timer = new Timer(OnTimerComplete, null, DebounceInterval, Timeout.Infinite);
- }
-
- private void OnTimerComplete(object stateInfo)
- {
- InvokeAsync(OnSearchAsync);
- }
- private void CancelToken()
- {
- try
- {
- _cancellationTokenSrc?.Cancel();
- }
- catch
- {
- }
-
- _cancellationTokenSrc = new CancellationTokenSource();
- }
- ///
- /// This async method needs to return a task and be awaited in order for
- /// unit tests that trigger this method to work correctly.
- ///
- private async Task OnSearchAsync()
- {
- if (MinCharacters > 0 && (string.IsNullOrWhiteSpace(Text) || Text.Length < MinCharacters))
- {
- IsOpen = false;
- StateHasChanged();
- return;
- }
-
- IEnumerable searchedItems = Array.Empty();
- CancelToken();
- try
- {
- if (ProgressIndicatorInPopoverTemplate is not null)
- IsOpen = true;
-
- var searchTask = SearchFuncWithCancel is not null ? SearchFuncWithCancel(Text, _cancellationTokenSrc.Token) : SearchFunc(Text);
-
- _currentSearchTask = searchTask;
+ #region Variables
+
+ [Inject]
+ IScrollManager ScrollManager { get; set; }
+
+ private Func? _toStringFunc;
+ private Task _currentSearchTask;
+ private CancellationTokenSource _cancellationTokenSrc;
+ private bool _isOpen;
+ private Timer _timer;
+ private T[] _items;
+ private int _selectedListItemIndex = 0;
+ private IList _enabledItemIndices = new List();
+ private int _itemsReturned; //the number of items returned by the search function
+ int _elementKey = 0;
+ ///
+ /// This boolean will keep track if the clear function is called too keep the set text function to be called.
+ ///
+ private bool _isCleared;
+ private Input _elementReference;
+ ///
+ /// We need a random id for the year items in the year list so we can scroll to the item safely in every DatePicker.
+ ///
+ private readonly string _componentId = Guid.NewGuid().ToString();
+
+ #endregion
+
+ #region Event callbacks
+
+ ///
+ /// Function to be invoked when checking whether an item should be disabled or not
+ ///
+ [Parameter]
+ public Func ItemDisabledFunc { get; set; }
+
+ ///
+ /// If true, the currently selected item from the drop-down (if it is open) is selected.
+ ///
+ [Parameter]
+ public bool SelectValueOnTab { get; set; } = false;
+
+ ///
+ /// Func that returns a list of items matching the typed text. Provides a cancellation token that
+ /// is marked as cancelled when the user changes the search text or selects a value from the list.
+ /// This can be used to cancel expensive asynchronous work occuring within the SearchFunc itself.
+ ///
+ [Parameter]
+ public Func>> SearchFuncWithCancel { get; set; }
+ ///
+ /// The SearchFunc returns a list of items matching the typed text
+ ///
+ [Parameter]
+ public Func>> SearchFunc { get; set; }
+
+
+ //
+ /// An event triggered when the state of IsOpen has changed
+ ///
+ [Parameter]
+ public EventCallback IsOpenChanged { get; set; }
+
+ ///
+ /// Button click event for clear button. Called after text and value has been cleared.
+ ///
+ [Parameter]
+ public EventCallback OnClearButtonClick { get; set; }
+
+ internal virtual async Task OnInputKeyUp(KeyboardEventArgs args)
+ {
+ switch (args.Key)
+ {
+ case "Enter":
+ case "NumpadEnter":
+ if (!IsOpen)
+ await ToggleMenu();
+ else
+ await OnEnterKey();
+
+ break;
+ case "ArrowDown":
+ if (!IsOpen)
+ await ToggleMenu();
+ else
+ {
+ var increment = _enabledItemIndices.ElementAtOrDefault(_enabledItemIndices.IndexOf(_selectedListItemIndex) + 1) - _selectedListItemIndex;
- StateHasChanged();
+ await SelectNextItem(increment < 0 ? 1 : increment);
+ }
- searchedItems = await searchTask ?? Array.Empty();
- }
- catch (TaskCanceledException)
- {
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception e)
- {
- Console.WriteLine($"The search function failed to return results: {e.Message}");
- }
+ break;
+ case "ArrowUp":
+ if (args.AltKey)
+ await ChangeMenu(false);
+ else if (!IsOpen)
+ await ToggleMenu();
+ else
+ {
+ var decrement = _selectedListItemIndex - _enabledItemIndices.ElementAtOrDefault(_enabledItemIndices.IndexOf(_selectedListItemIndex) - 1);
+ await SelectNextItem(-(decrement < 0 ? 1 : decrement));
+ }
- _itemsReturned = searchedItems.Count();
+ break;
+ case "Escape":
+ await ChangeMenu(false);
- if (MaxItems.HasValue)
- searchedItems = searchedItems.Take(MaxItems.Value);
+ break;
+ case "Tab":
+ await Task.Delay(1);
- _items = searchedItems.ToArray();
- _enabledItemIndices = _items.Select((item, idx) => (item, idx)).Where(tuple => ItemDisabledFunc?.Invoke(tuple.item) != true).Select(tuple => tuple.idx).ToList();
- _selectedListItemIndex = _enabledItemIndices.Any() ? _enabledItemIndices.First() : -1;
+ if (!IsOpen)
+ return;
- IsOpen = true;
+ if (SelectValueOnTab)
+ await OnEnterKey();
+ else
+ await ToggleMenu();
- if (_items?.Length == 0)
- {
- await CoerceValueToText();
+ break;
+ case "Backspace":
+ if (args.CtrlKey && args.ShiftKey)
+ Reset();
+ break;
+ }
- StateHasChanged();
+ base.InvokeKeyUp(args);
+ }
+ internal virtual async Task OnInputKeyDown(KeyboardEventArgs args)
+ {
+ switch (args.Key)
+ {
+ case "Tab":
+ // NOTE: We need to catch Tab in Keydown because a tab will move focus to the next element and thus
+ // in OnInputKeyUp we'd never get the tab key
+ if (!IsOpen)
+ return;
+ if (SelectValueOnTab)
+ await OnEnterKey();
+ else
+ IsOpen = false;
+ break;
+ }
+ }
+
+ public async Task SelectOption(T value)
+ {
+ await SetValueAsync(value);
+
+ if (_items is not null)
+ _selectedListItemIndex = Array.IndexOf(_items, value);
+
+ var optionText = GetItemString(value);
+
+ if (!_isCleared)
+ await SetTextAsync(optionText, false);
+
+ _timer?.Dispose();
+
+ IsOpen = false;
+
+ BeginValidate();
+
+ if (!_isCleared)
+ _elementReference?.SetText(optionText);
+
+ _elementReference?.FocusAsync().AndForget();
+
+ StateHasChanged();
+ }
+ ///
+ /// Toggle the menu (if not disabled or not readonly, and is opened).
+ ///
+ public async Task ToggleMenu()
+ {
+ if ((Disabled || ReadOnly) && !IsOpen)
+ return;
+
+ await ChangeMenu(!IsOpen);
+ }
+
+ private async Task ChangeMenu(bool open)
+ {
+ if (open)
+ {
+ if (SelectOnClick)
+ await _elementReference.SelectAsync();
+
+ await OnSearchAsync();
+ }
+ else
+ {
+ _timer?.Dispose();
+
+ RestoreScrollPosition();
+
+ await CoerceTextToValue();
+
+ IsOpen = false;
+
+ StateHasChanged();
+ }
+ }
+
+
+
+ protected override Task UpdateTextPropertyAsync(bool updateValue)
+ {
+ _timer?.Dispose();
+ // This keeps the text from being set when clear() was called
+ if (_isCleared)
+ return Task.CompletedTask;
+
+ return base.UpdateTextPropertyAsync(updateValue);
+ }
+
+ protected override async Task UpdateValuePropertyAsync(bool updateText)
+ {
+ _timer?.Dispose();
+
+ if (ResetValueOnEmptyText && string.IsNullOrWhiteSpace(Text))
+ await SetValueAsync(default, updateText);
+
+ if (DebounceInterval <= 0)
+ await OnSearchAsync();
+ else
+ _timer = new Timer(OnTimerComplete, null, DebounceInterval, Timeout.Infinite);
+ }
+
+ private void OnTimerComplete(object stateInfo)
+ {
+ InvokeAsync(OnSearchAsync);
+ }
+ private void CancelToken()
+ {
+ try
+ {
+ _cancellationTokenSrc?.Cancel();
+ }
+ catch
+ {
+ }
+
+ _cancellationTokenSrc = new CancellationTokenSource();
+ }
+ ///
+ /// This async method needs to return a task and be awaited in order for
+ /// unit tests that trigger this method to work correctly.
+ ///
+ private async Task OnSearchAsync()
+ {
+ if (MinCharacters > 0 && (string.IsNullOrWhiteSpace(Text) || Text.Length < MinCharacters))
+ {
+ IsOpen = false;
+ StateHasChanged();
+ return;
+ }
+
+ IEnumerable searchedItems = Array.Empty();
+ CancelToken();
+
+ try
+ {
+ if (ProgressIndicatorInPopoverTemplate is not null)
+ IsOpen = true;
+
+ var searchTask = SearchFuncWithCancel is not null ? SearchFuncWithCancel(Text, _cancellationTokenSrc.Token) : SearchFunc(Text);
+
+ _currentSearchTask = searchTask;
+
+ StateHasChanged();
+
+ searchedItems = await searchTask ?? Array.Empty();
+ }
+ catch (TaskCanceledException)
+ {
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"The search function failed to return results: {e.Message}");
+ }
+
+ _itemsReturned = searchedItems.Count();
+
+ if (MaxItems.HasValue)
+ searchedItems = searchedItems.Take(MaxItems.Value);
+
+ _items = searchedItems.ToArray();
+ _enabledItemIndices = _items.Select((item, idx) => (item, idx)).Where(tuple => ItemDisabledFunc?.Invoke(tuple.item) != true).Select(tuple => tuple.idx).ToList();
+ _selectedListItemIndex = _enabledItemIndices.Any() ? _enabledItemIndices.First() : -1;
+
+ IsOpen = true;
+
+ if (_items?.Length == 0)
+ {
+ await CoerceValueToText();
+
+ StateHasChanged();
+
+ return;
+ }
+
+ StateHasChanged();
+ }
+
+ ///
+ /// Clears the autocomplete's text
+ ///
+ public async Task Clear()
+ {
+ _isCleared = true;
+ IsOpen = false;
+
+ await SetTextAsync(string.Empty, updateValue: false);
+ await CoerceValueToText();
+
+ if (_elementReference is not null)
+ await _elementReference.SetText(string.Empty);
+
+ _timer?.Dispose();
+
+ StateHasChanged();
+ }
+
+ protected override async void ResetValue()
+ {
+ await Clear();
+ base.ResetValue();
+ }
+
+ private string GetItemString(T item)
+ {
+ if (item is null)
+ return string.Empty;
+ try
+ {
+ return Converter.Convert(item);
+ }
+ catch (NullReferenceException) { }
+
+ return "null";
+ }
+
+ private ValueTask SelectNextItem(int increment)
+ {
+ if (increment == 0 || _items is null || !_items.Any() || !_enabledItemIndices.Any())
+ return ValueTask.CompletedTask;
+ // if we are at the end, or the beginning we just do an rollover
+ _selectedListItemIndex = Math.Clamp(value: (10 * _items.Length + _selectedListItemIndex + increment) % _items.Length, min: 0, max: _items.Length - 1);
+
+ return ScrollToListItem(_selectedListItemIndex);
+ }
+ ///
+ /// Scroll to a specific item index in the Autocomplete list of items.
+ ///
+ /// the index to scroll to
+ public ValueTask ScrollToListItem(int index)
+ {
+ var id = GetListItemId(index);
+ //id of the scrolled element
+ return ScrollManager.ScrollToListItemAsync(id);
+ }
+ /*
+ * This restores the scroll position after closing the menu and element being 0
+ */
+ private void RestoreScrollPosition()
+ {
+ if (_selectedListItemIndex != 0)
+ return;
+
+ ScrollManager.ScrollToListItemAsync(GetListItemId(0));
+ }
+
+ private string GetListItemId(in int index)
+ {
+ return $"{_componentId}_item{index}";
+ }
+
+ internal Task OnEnterKey()
+ {
+ if (!IsOpen)
+ return Task.CompletedTask;
+
+ if (_items is null || !_items.Any())
+ return Task.CompletedTask;
+
+ if (_selectedListItemIndex >= 0 && _selectedListItemIndex < _items.Length)
+ return SelectOption(_items[_selectedListItemIndex]);
+
+ return Task.CompletedTask;
+ }
+
+ private Task OnInputBlurred(FocusEventArgs args)
+ {
+ OnBlur.InvokeAsync(args);
+
+ return Task.CompletedTask;
+ // we should not validate on blur in autocomplete, because the user needs to click out of the input to select a value,
+ // resulting in a premature validation. thus, don't call base
+ //base.OnBlurred(args);
+ }
+
+ private Task CoerceTextToValue()
+ {
+ if (!CoerceText)
+ return Task.CompletedTask;
+
+ _timer?.Dispose();
+
+ var text = Value is null ? null : GetItemString(Value);
+ /*
+ * Don't update the value to prevent the popover from opening again after coercion
+ */
+ if (text != Text)
+ return SetTextAsync(text, updateValue: false);
+
+ return Task.CompletedTask;
+ }
+
+ private Task CoerceValueToText()
+ {
+ if (!CoerceValue)
+ return Task.CompletedTask;
+
+ _timer?.Dispose();
+
+ var value = Converter.ConvertBack(Text);
+
+ return SetValueAsync(value, updateText: false);
+ }
+
+
+ ///
+ /// Focus the input in the Autocomplete component.
+ ///
+ public override ValueTask FocusAsync()
+ {
+ return _elementReference.FocusAsync();
+ }
+ ///
+ /// Blur from the input in the Autocomplete component.
+ ///
+ public override ValueTask BlurAsync()
+ {
+ return _elementReference.BlurAsync();
+ }
+
+ ///
+ /// Select all text within the Autocomplete input.
+ ///
+ public override ValueTask SelectAsync()
+ {
+ return _elementReference.SelectAsync();
+ }
+
+ ///
+ /// Select all text within the Autocomplete input and aligns its start and end points to the text content of the current input.
+ ///
+ public override ValueTask SelectRangeAsync(int pos1, int pos2)
+ {
+ return _elementReference.SelectRangeAsync(pos1, pos2);
+ }
+
+ private async Task OnTextChanged(string text)
+ {
+ await TextChanged.InvokeAsync();
+
+ if (text is null)
+ return;
+
+ await SetTextAsync(text, true);
+ }
+
+ private async Task ListItemOnClick(T item)
+ {
+ await SelectOption(item);
+ }
+
+ #endregion
+
+ #region Content placeholders
+
+
+
+ #endregion
+
+ #region Styling properties
+
+ ///
+ /// Show clear button.
+ ///
+ [Parameter]
+ public bool Clearable { get; set; } = false;
+ private string CurrentIcon => !string.IsNullOrWhiteSpace(AdornmentIcon) ? AdornmentIcon : _isOpen ? CloseIcon : OpenIcon;
+
+ ///
+ /// Returns the open state of the drop-down.
+ ///
+ public bool IsOpen
+ {
+ get => _isOpen;
+ // Note: the setter is protected because it was needed by a user who derived his own autocomplete from this class.
+ // Note: setting IsOpen will not open or close it. Use ToggleMenu() for that.
+ protected set
+ {
+ if (value == _isOpen)
return;
- }
-
- StateHasChanged();
- }
-
- ///
- /// Clears the autocomplete's text
- ///
- public async Task Clear()
- {
- _isCleared = true;
- IsOpen = false;
-
- await SetTextAsync(string.Empty, updateValue: false);
- await CoerceValueToText();
-
- if (_elementReference is not null)
- await _elementReference.SetText(string.Empty);
-
- _timer?.Dispose();
-
- StateHasChanged();
- }
-
- protected override async void ResetValue()
- {
- await Clear();
- base.ResetValue();
- }
-
- private string GetItemString(T item)
- {
- if (item is null)
- return string.Empty;
- try
- {
- return Converter.Convert(item);
- }
- catch (NullReferenceException) { }
-
- return "null";
- }
-
- internal virtual async Task OnInputKeyDown(KeyboardEventArgs args)
- {
- switch (args.Key)
- {
- case "Tab":
- // NOTE: We need to catch Tab in Keydown because a tab will move focus to the next element and thus
- // in OnInputKeyUp we'd never get the tab key
- if (!IsOpen)
- return;
- if (SelectValueOnTab)
- await OnEnterKey();
- else
- IsOpen = false;
- break;
- }
- }
-
- internal virtual async Task OnInputKeyUp(KeyboardEventArgs args)
- {
- switch (args.Key)
- {
- case "Enter":
- case "NumpadEnter":
- if (!IsOpen)
- await ToggleMenu();
- else
- await OnEnterKey();
-
- break;
- case "ArrowDown":
- if (!IsOpen)
- await ToggleMenu();
- else
- {
- var increment = _enabledItemIndices.ElementAtOrDefault(_enabledItemIndices.IndexOf(_selectedListItemIndex) + 1) - _selectedListItemIndex;
-
- await SelectNextItem(increment < 0 ? 1 : increment);
- }
-
- break;
- case "ArrowUp":
- if (args.AltKey)
- await ChangeMenu(false);
- else if (!IsOpen)
- await ToggleMenu();
- else
- {
- var decrement = _selectedListItemIndex - _enabledItemIndices.ElementAtOrDefault(_enabledItemIndices.IndexOf(_selectedListItemIndex) - 1);
- await SelectNextItem(-(decrement < 0 ? 1 : decrement));
- }
-
- break;
- case "Escape":
- await ChangeMenu(false);
-
- break;
- case "Tab":
- await Task.Delay(1);
-
- if (!IsOpen)
- return;
-
- if (SelectValueOnTab)
- await OnEnterKey();
- else
- await ToggleMenu();
-
- break;
- case "Backspace":
- if (args.CtrlKey && args.ShiftKey)
- Reset();
- break;
- }
-
- base.InvokeKeyUp(args);
- }
-
- private ValueTask SelectNextItem(int increment)
- {
- if (increment == 0 || _items is null || !_items.Any() || !_enabledItemIndices.Any())
- return ValueTask.CompletedTask;
- // if we are at the end, or the beginning we just do an rollover
- _selectedListItemIndex = Math.Clamp(value: (10 * _items.Length + _selectedListItemIndex + increment) % _items.Length, min: 0, max: _items.Length - 1);
-
- return ScrollToListItem(_selectedListItemIndex);
- }
- ///
- /// Scroll to a specific item index in the Autocomplete list of items.
- ///
- /// the index to scroll to
- public ValueTask ScrollToListItem(int index)
- {
- var id = GetListItemId(index);
- //id of the scrolled element
- return ScrollManager.ScrollToListItemAsync(id);
- }
- /*
- * This restores the scroll position after closing the menu and element being 0
- */
- private void RestoreScrollPosition()
- {
- if (_selectedListItemIndex != 0)
- return;
-
- ScrollManager.ScrollToListItemAsync(GetListItemId(0));
- }
-
- private string GetListItemId(in int index)
- {
- return $"{_componentId}_item{index}";
- }
-
- internal Task OnEnterKey()
- {
- if (!IsOpen)
- return Task.CompletedTask;
-
- if (_items is null || !_items.Any())
- return Task.CompletedTask;
- if (_selectedListItemIndex >= 0 && _selectedListItemIndex < _items.Length)
- return SelectOption(_items[_selectedListItemIndex]);
-
- return Task.CompletedTask;
- }
-
- private Task OnInputBlurred(FocusEventArgs args)
- {
- OnBlur.InvokeAsync(args);
-
- return Task.CompletedTask;
- // we should not validate on blur in autocomplete, because the user needs to click out of the input to select a value,
- // resulting in a premature validation. thus, don't call base
- //base.OnBlurred(args);
- }
-
- private Task CoerceTextToValue()
- {
- if (!CoerceText)
- return Task.CompletedTask;
-
- _timer?.Dispose();
-
- var text = Value is null ? null : GetItemString(Value);
- /*
- * Don't update the value to prevent the popover from opening again after coercion
- */
- if (text != Text)
- return SetTextAsync(text, updateValue: false);
-
- return Task.CompletedTask;
- }
-
- private Task CoerceValueToText()
- {
- if (!CoerceValue)
- return Task.CompletedTask;
-
- _timer?.Dispose();
-
- var value = Converter.ConvertBack(Text);
-
- return SetValueAsync(value, updateText: false);
- }
-
- protected override void Dispose(bool disposing)
- {
- _timer?.Dispose();
-
- if (_cancellationTokenSrc is not null)
- {
- try
- {
- _cancellationTokenSrc.Dispose();
- }
- catch { }
- }
-
- base.Dispose(disposing);
- }
- ///
- /// Focus the input in the Autocomplete component.
- ///
- public override ValueTask FocusAsync()
- {
- return _elementReference.FocusAsync();
- }
- ///
- /// Blur from the input in the Autocomplete component.
- ///
- public override ValueTask BlurAsync()
- {
- return _elementReference.BlurAsync();
- }
-
- ///
- /// Select all text within the Autocomplete input.
- ///
- public override ValueTask SelectAsync()
- {
- return _elementReference.SelectAsync();
- }
-
- ///
- /// Select all text within the Autocomplete input and aligns its start and end points to the text content of the current input.
- ///
- public override ValueTask SelectRangeAsync(int pos1, int pos2)
- {
- return _elementReference.SelectRangeAsync(pos1, pos2);
- }
-
- private async Task OnTextChanged(string text)
- {
- await TextChanged.InvokeAsync();
-
- if (text is null)
+ _isOpen = value;
+
+ IsOpenChanged.InvokeAsync(_isOpen).AndForget();
+ }
+ }
+
+ ///
+ /// User class names for the popover, separated by space
+ ///
+ [Parameter]
+ public string PopoverClass { get; set; }
+ ///
+ /// Set the anchor origin point to determen where the popover will open from.
+ ///
+ [Parameter]
+ public Origin AnchorOrigin { get; set; } = Origin.BottomCenter;
+ ///
+ /// Sets the transform origin point for the popover.
+ ///
+ [Parameter]
+ public Origin TransformOrigin { get; set; } = Origin.TopCenter;
+ ///
+ /// If true, compact vertical padding will be applied to all Autocomplete items.
+ ///
+ [Parameter]
+ public bool Dense { get; set; }
+ ///
+ /// The Open Autocomplete Glyph
+ ///
+ [Parameter]
+ public string OpenIcon { get; set; } = Icons.Material.Filled.ArrowDropDown;
+ ///
+ /// The Close Autocomplete Glyph
+ ///
+ [Parameter]
+ public string CloseIcon { get; set; } = Icons.Material.Filled.ArrowDropUp;
+ ///
+ /// The maximum height of the Autocomplete when it is open.
+ ///
+ [Parameter]
+ public int MaxHeight { get; set; } = 300;
+ ///
+ /// Defines how values are displayed in the drop-down list
+ ///
+ [Parameter]
+ public Func? ToStringFunc
+ {
+ get => _toStringFunc;
+ set
+ {
+ if (_toStringFunc == value)
return;
- await SetTextAsync(text, true);
- }
+ _toStringFunc = value;
+
+ SetConverter(new LambdaConverter(_toStringFunc ?? (x => x?.ToString()), null));
+ }
+ }
+ ///
+ /// Whether to show the progress indicator.
+ ///
+ [Parameter]
+ public bool ShowProgressIndicator { get; set; } = false;
+ ///
+ /// The color of the progress indicator.
+ ///
+ [Parameter]
+ public ThemeColor ProgressIndicatorColor { get; set; } = ThemeColor.Default;
+ private bool IsLoading => _currentSearchTask is not null && !_currentSearchTask.IsCompleted;
+
+ ///
+ /// Maximum items to display, defaults to 10.
+ /// A null value will display all items.
+ ///
+ [Parameter]
+ public int? MaxItems { get; set; } = 10;
+ ///
+ /// Minimum characters to initiate a search
+ ///
+ [Parameter]
+ public int MinCharacters { get; set; } = 0;
+ ///
+ /// Reset value if user deletes the text
+ ///
+ [Parameter]
+ public bool ResetValueOnEmptyText { get; set; } = false;
+ ///
+ /// If true, clicking the text field will select (highlight) its contents.
+ ///
+ [Parameter]
+ public bool SelectOnClick { get; set; } = true;
+
+ protected string ClassList()
+ {
+ return new CssBuilder("select")
+ .Build();
+ }
+
+ protected string AutocompleteClassList()
+ {
+ return new CssBuilder("select")
+ .AddClass("autocomplete")
+ .AddClass("width-full", FullWidth)
+ .AddClass("autocomplete--with-progress", ShowProgressIndicator && IsLoading)
+ .Build();
+ }
+
+ protected string CircularProgressClassList()
+ {
+ return new CssBuilder("progress-indicator-circular")
+ .AddClass("progress-indicator-circular--with-adornment", Adornment == Adornment.End)
+ .Build();
+ }
+
+ ///
+ /// Debounce interval in milliseconds.
+ ///
+ [Parameter]
+ public int DebounceInterval { get; set; } = 100;
+ ///
+ /// Optional presentation template for unselected items
+ ///
+ [Parameter]
+ public RenderFragment ItemTemplate { get; set; }
+ ///
+ /// Optional presentation template for the selected item
+ ///
+ [Parameter]
+ public RenderFragment ItemSelectedTemplate { get; set; }
+ ///
+ /// Optional presentation template for disabled item
+ ///
+ [Parameter]
+ public RenderFragment ItemDisabledTemplate { get; set; }
+ ///
+ /// Optional presentation template for when more items were returned from the Search function than the MaxItems limit
+ ///
+ [Parameter]
+ public RenderFragment MoreItemsTemplate { get; set; }
+ ///
+ /// Optional presentation template for when no items were returned from the Search function
+ ///
+ [Parameter]
+ public RenderFragment NoItemsTemplate { get; set; }
+ ///
+ /// Optional template for progress indicator
+ ///
+ [Parameter]
+ public RenderFragment ProgressIndicatorTemplate { get; set; }
+ ///
+ /// Optional template for showing progress indicator inside the popover
+ ///
+ [Parameter]
+ public RenderFragment ProgressIndicatorInPopoverTemplate { get; set; }
+ ///
+ /// On drop-down close override Text with selected Value. This makes it clear to the user
+ /// which list value is currently selected and disallows incomplete values in Text.
+ ///
+ [Parameter]
+ public bool CoerceText { get; set; } = true;
+ ///
+ /// If user input is not found by the search func and CoerceValue is set to true the user input
+ /// will be applied to the Value which allows to validate it and display an error message.
+ ///
+ [Parameter]
+ public bool CoerceValue { get; set; }
+
+ #endregion Styling
+
+ #region Lifecycle
+ ///
+ /// Constructor
+ ///
+ public Autocomplete()
+ {
+ Adornment = Adornment.End;
+ IconSize = Size.Medium;
+ }
+
+ protected override void OnInitialized()
+ {
+ var text = GetItemString(Value);
+
+ if (!string.IsNullOrWhiteSpace(text))
+ Text = text;
+ }
+
+ protected override void OnAfterRender(bool firstRender)
+ {
+ _isCleared = false;
+
+ base.OnAfterRender(firstRender);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ _timer?.Dispose();
+
+ if (_cancellationTokenSrc is not null)
+ {
+ try
+ {
+ _cancellationTokenSrc.Dispose();
+ }
+ catch { }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ #endregion
- private async Task ListItemOnClick(T item)
- {
- await SelectOption(item);
- }
}
diff --git a/src/Connected.Components/Components/Avatar/Avatar.razor.cs b/src/Connected.Components/Components/Avatar/Avatar.razor.cs
index d42775c..7137688 100644
--- a/src/Connected.Components/Components/Avatar/Avatar.razor.cs
+++ b/src/Connected.Components/Components/Avatar/Avatar.razor.cs
@@ -7,9 +7,43 @@ namespace Connected.Components;
partial class Avatar : UIComponent, IDisposable
{
+
+ #region Variables
+ #endregion
+
+ #region Events
+
+ internal void ForceRedraw() => StateHasChanged();
+
+ #endregion
+
+ #region Content
+
+ ///
+ /// Child content of the component.
+ ///
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+
+ ///
+ /// Link to image, if set a image will be displayed instead of text.
+ ///
+ [Parameter]
+ public string? Image { get; set; }
+
+ ///
+ /// If set (and Image is also set), will add an alt property to the img element
+ ///
+ [Parameter]
+ public string? ImageAltText { get; set; }
+
[CascadingParameter]
protected AvatarGroup? AvatarGroup { get; set; }
+ #endregion
+
+ #region Styling
+
protected CssBuilder CompiledClassList
{
get
@@ -41,18 +75,6 @@ partial class Avatar : UIComponent, IDisposable
[Parameter]
public string? ClassList { get; set; }
- ///
- /// If set (and Image is also set), will add an alt property to the img element
- ///
- [Parameter]
- public string? ImageAltText { get; set; }
-
- ///
- /// Child content of the component.
- ///
- [Parameter]
- public RenderFragment? ChildContent { get; set; }
-
///
/// The color of the component. It supports the theme colors.
///
@@ -65,12 +87,6 @@ partial class Avatar : UIComponent, IDisposable
[Parameter]
public int Elevation { set; get; } = 0;
- ///
- /// Link to image, if set a image will be displayed instead of text.
- ///
- [Parameter]
- public string? Image { get; set; }
-
///
/// Sets the avatar appearance
///
@@ -88,6 +104,10 @@ partial class Avatar : UIComponent, IDisposable
[Parameter]
public Variant Variant { get; set; } = Variant.Filled;
+ #endregion
+
+ #region Lifecycle
+
protected override void OnInitialized()
{
base.OnInitialized();
@@ -97,5 +117,6 @@ partial class Avatar : UIComponent, IDisposable
public void Dispose() => AvatarGroup?.RemoveAvatar(this);
- internal void ForceRedraw() => StateHasChanged();
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Avatar/AvatarGroup.razor.cs b/src/Connected.Components/Components/Avatar/AvatarGroup.razor.cs
index 48e1fd4..0cdef1b 100644
--- a/src/Connected.Components/Components/Avatar/AvatarGroup.razor.cs
+++ b/src/Connected.Components/Components/Avatar/AvatarGroup.razor.cs
@@ -7,10 +7,57 @@ namespace Connected.Components;
partial class AvatarGroup : UIComponent
{
+
+ #region Variables
+
private bool _childrenNeedUpdates = false;
private int _spacing = 3;
private List _avatars = new();
+ #endregion
+
+ #region Event callbacks
+ #endregion
+
+ #region Content
+
+ ///
+ /// Child content of the component.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.AvatarGroup.Behavior)]
+ public RenderFragment ChildContent { get; set; }
+
+ internal void AddAvatar(Avatar avatar)
+ {
+ _avatars.Add(avatar);
+ StateHasChanged();
+ }
+
+ internal void RemoveAvatar(Avatar avatar)
+ {
+ _avatars.Remove(avatar);
+ }
+
+ internal bool MaxGroupReached(Avatar avatar)
+ {
+ if (_avatars.IndexOf(avatar) < Max)
+ return true;
+ else
+ return false;
+ }
+
+ #endregion
+
+ #region Styling properties
+
+
+ internal CssBuilder GetAvatarSpacing() => new CssBuilder()
+ .AddClass($"ms-n{Spacing}");
+
+ internal StyleBuilder GetAvatarZindex(Avatar avatar) => new StyleBuilder()
+ .AddStyle("z-index", $"{_avatars.Count - _avatars.IndexOf(avatar)}");
+
private CssBuilder CompiledClassList
{
get
@@ -21,6 +68,11 @@ partial class AvatarGroup : UIComponent
.AddClass(ClassList);
}
}
+ ///
+ /// A space separated list of class names, added on top of the default class list.
+ ///
+ [Parameter]
+ public string? ClassList { get; set; }
private CssBuilder CompiledMaxAvatarClassList
{
@@ -31,30 +83,12 @@ partial class AvatarGroup : UIComponent
.AddClass(MaxAvatarClass);
}
}
-
///
- /// Spacing between avatars where 0 is none and 16 max.
- ///
- [Parameter]
- public int Spacing
- {
- get => _spacing;
- set
- {
- if (value != _spacing)
- {
- _spacing = value;
- _childrenNeedUpdates = true;
- }
- }
- }
-
- ///
- /// A space separated list of class names, added on top of the default class list.
+ /// Custom class/classes for MaxAvatar
///
[Parameter]
- public string? ClassList { get; set; }
-
+ [Category(CategoryTypes.AvatarGroup.Appearance)]
+ public string MaxAvatarClass { get; set; }
///
/// Outlines the grouped avatars to distinguish them, useful when avatars are the same color or uses images.
///
@@ -132,44 +166,25 @@ partial class AvatarGroup : UIComponent
}
///
- /// Custom class/classes for MaxAvatar
- ///
- [Parameter]
- [Category(CategoryTypes.AvatarGroup.Appearance)]
- public string MaxAvatarClass { get; set; }
-
- ///
- /// Child content of the component.
+ /// Spacing between avatars where 0 is none and 16 max.
///
[Parameter]
- [Category(CategoryTypes.AvatarGroup.Behavior)]
- public RenderFragment ChildContent { get; set; }
-
- internal void AddAvatar(Avatar avatar)
- {
- _avatars.Add(avatar);
- StateHasChanged();
- }
-
- internal void RemoveAvatar(Avatar avatar)
+ public int Spacing
{
- _avatars.Remove(avatar);
+ get => _spacing;
+ set
+ {
+ if (value != _spacing)
+ {
+ _spacing = value;
+ _childrenNeedUpdates = true;
+ }
+ }
}
- internal CssBuilder GetAvatarSpacing() => new CssBuilder()
- .AddClass($"ms-n{Spacing}");
-
- internal StyleBuilder GetAvatarZindex(Avatar avatar) => new StyleBuilder()
- .AddStyle("z-index", $"{_avatars.Count - _avatars.IndexOf(avatar)}");
-
- internal bool MaxGroupReached(Avatar avatar)
- {
- if (_avatars.IndexOf(avatar) < Max)
- return true;
- else
- return false;
- }
+ #endregion
+ #region Lifecycle
protected override void OnParametersSet()
{
base.OnParametersSet();
@@ -184,4 +199,7 @@ partial class AvatarGroup : UIComponent
_childrenNeedUpdates = false;
}
}
+
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Avatar/AvatarKind.cs b/src/Connected.Components/Components/Avatar/AvatarKind.cs
index e0d3195..2be8175 100644
--- a/src/Connected.Components/Components/Avatar/AvatarKind.cs
+++ b/src/Connected.Components/Components/Avatar/AvatarKind.cs
@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Connected.Components;
+namespace Connected.Components;
public enum AvatarKind
{
Undefined = 0,
diff --git a/src/Connected.Components/Components/Badge/Badge.razor.cs b/src/Connected.Components/Components/Badge/Badge.razor.cs
index 7ee9c51..92ce88d 100644
--- a/src/Connected.Components/Components/Badge/Badge.razor.cs
+++ b/src/Connected.Components/Components/Badge/Badge.razor.cs
@@ -9,6 +9,72 @@ namespace Connected.Components;
public partial class Badge : UIComponent
{
+
+ #region Variables
+ #endregion
+
+ #region Events
+
+ ///
+ /// Button click event if set.
+ ///
+ [Parameter] public EventCallback OnClick { get; set; }
+
+
+
+ internal Task HandleBadgeClick(MouseEventArgs e)
+ {
+ if (OnClick.HasDelegate)
+ return OnClick.InvokeAsync(e);
+
+ return Task.CompletedTask;
+ }
+
+ #endregion
+
+ #region Content
+
+ private string _content;
+
+ ///
+ /// Sets the Glyph to use in the badge.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.Badge.Behavior)]
+ public string Icon { get; set; }
+
+ ///
+ /// Max value to show when content is integer type.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.Badge.Behavior)]
+ public int Max { get; set; } = 99;
+
+ ///
+ /// Content you want inside the badge. Supported types are string and integer.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.Badge.Behavior)]
+ public object Content { get; set; }
+
+ ///
+ /// Child content of component, the content that the badge will apply to.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.Badge.Behavior)]
+ public RenderFragment ChildContent { get; set; }
+
+ #endregion
+
+ #region Styling
+
+ ///
+ /// Badge class names, separated by space.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.Badge.Appearance)]
+ public string BadgeClass { get; set; }
+
protected string CompiledClassList =>
new CssBuilder("badge-root")
.AddClass(AdditionalClassList)
@@ -59,27 +125,6 @@ public partial class Badge : UIComponent
[Category(CategoryTypes.Badge.Appearance)]
public ThemeColor Color { get; set; } = ThemeColor.Default;
- ///
- /// Aligns the badge to bottom.
- ///
- [ExcludeFromCodeCoverage]
- [Obsolete("Use Origin instead.", true)]
- [Parameter] public bool Bottom { get; set; }
-
- ///
- /// Aligns the badge to left.
- ///
- [ExcludeFromCodeCoverage]
- [Obsolete("Use Origin instead.", true)]
- [Parameter] public bool Left { get => Start; set { Start = value; } }
-
- ///
- /// Aligns the badge to the start (Left in LTR and right in RTL).
- ///
- [ExcludeFromCodeCoverage]
- [Obsolete("Use Origin instead.", true)]
- [Parameter] public bool Start { get; set; }
-
///
/// Reduces the size of the badge and hide any of its content.
///
@@ -101,55 +146,9 @@ public partial class Badge : UIComponent
[Category(CategoryTypes.Badge.Appearance)]
public bool Bordered { get; set; }
- ///
- /// Sets the Glyph to use in the badge.
- ///
- [Parameter]
- [Category(CategoryTypes.Badge.Behavior)]
- public string Icon { get; set; }
-
- ///
- /// Max value to show when content is integer type.
- ///
- [Parameter]
- [Category(CategoryTypes.Badge.Behavior)]
- public int Max { get; set; } = 99;
-
- ///
- /// Content you want inside the badge. Supported types are string and integer.
- ///
- [Parameter]
- [Category(CategoryTypes.Badge.Behavior)]
- public object Content { get; set; }
-
- ///
- /// Badge class names, separated by space.
- ///
- [Parameter]
- [Category(CategoryTypes.Badge.Appearance)]
- public string BadgeClass { get; set; }
+ #endregion
- ///
- /// Child content of component, the content that the badge will apply to.
- ///
- [Parameter]
- [Category(CategoryTypes.Badge.Behavior)]
- public RenderFragment ChildContent { get; set; }
-
- ///
- /// Button click event if set.
- ///
- [Parameter] public EventCallback OnClick { get; set; }
-
- private string _content;
-
- internal Task HandleBadgeClick(MouseEventArgs e)
- {
- if (OnClick.HasDelegate)
- return OnClick.InvokeAsync(e);
-
- return Task.CompletedTask;
- }
+ #region Lifecycle
protected override void OnParametersSet()
{
@@ -173,4 +172,6 @@ public partial class Badge : UIComponent
_content = null;
}
}
+
+ #endregion
}
diff --git a/src/Connected.Components/Components/Breadcrumbs/BreadcrumbLink.razor.cs b/src/Connected.Components/Components/Breadcrumbs/BreadcrumbLink.razor.cs
index 0f1bb03..1d94ce8 100644
--- a/src/Connected.Components/Components/Breadcrumbs/BreadcrumbLink.razor.cs
+++ b/src/Connected.Components/Components/Breadcrumbs/BreadcrumbLink.razor.cs
@@ -4,13 +4,20 @@ using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class BreadcrumbLink : UIComponent
{
+ #region Content
[Parameter]
public BreadcrumbItem Item { get; set; }
[CascadingParameter]
public Breadcrumbs Parent { get; set; }
+ #endregion
+
+ #region Style
+
private string Classname => new CssBuilder("breadcrumb-item")
.AddClass("disabled", Item?.Disabled)
.Build();
+
+ #endregion
}
diff --git a/src/Connected.Components/Components/Breadcrumbs/BreadcrumbSeparator.razor.cs b/src/Connected.Components/Components/Breadcrumbs/BreadcrumbSeparator.razor.cs
index a9cca7b..b402d2c 100644
--- a/src/Connected.Components/Components/Breadcrumbs/BreadcrumbSeparator.razor.cs
+++ b/src/Connected.Components/Components/Breadcrumbs/BreadcrumbSeparator.razor.cs
@@ -3,6 +3,8 @@
namespace Connected.Components;
public partial class BreadcrumbSeparator : UIComponent
{
+ #region Content
[CascadingParameter]
public Breadcrumbs Parent { get; set; }
+ #endregion
}
diff --git a/src/Connected.Components/Components/Breadcrumbs/Breadcrumbs.razor.cs b/src/Connected.Components/Components/Breadcrumbs/Breadcrumbs.razor.cs
index e0b5a99..b96b818 100644
--- a/src/Connected.Components/Components/Breadcrumbs/Breadcrumbs.razor.cs
+++ b/src/Connected.Components/Components/Breadcrumbs/Breadcrumbs.razor.cs
@@ -6,6 +6,22 @@ namespace Connected.Components;
public partial class Breadcrumbs : UIComponent
{
+
+ #region Events
+
+ internal void Expand()
+ {
+ if (!Collapsed)
+ return;
+
+ Collapsed = false;
+ StateHasChanged();
+ }
+
+ #endregion
+
+ #region Content
+
///
/// A list of breadcrumb items/links.
///
@@ -20,6 +36,30 @@ public partial class Breadcrumbs : UIComponent
[Category(CategoryTypes.Breadcrumbs.Appearance)]
public string Separator { get; set; } = "/";
+ ///
+ /// Custom expander icon.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.Breadcrumbs.Appearance)]
+ public string ExpanderIcon { get; set; } = Icons.Material.Filled.SettingsEthernet;
+
+ #endregion
+
+ #region Styling
+ [Parameter]
+ public string ClassList { get; set; } = string.Empty;
+ private string Classname => new CssBuilder("breadcrumbs")
+ .AddClass("typography-body1")
+ .AddClass(ClassList)
+ .Build();
+
+ internal static string GetItemClassname(BreadcrumbItem item)
+ {
+ return new CssBuilder("breadcrumb-item")
+ .AddClass("disabled", item.Disabled)
+ .Build();
+ }
+
///
/// Specifies a RenderFragment to use as the separator.
///
@@ -34,6 +74,8 @@ public partial class Breadcrumbs : UIComponent
[Category(CategoryTypes.Breadcrumbs.Behavior)]
public RenderFragment ItemTemplate { get; set; }
+ public bool Collapsed { get; private set; } = true;
+
///
/// Controls when (and if) the breadcrumbs will automatically collapse.
///
@@ -41,32 +83,6 @@ public partial class Breadcrumbs : UIComponent
[Category(CategoryTypes.Breadcrumbs.Behavior)]
public byte? MaxItems { get; set; }
- ///
- /// Custom expander icon.
- ///
- [Parameter]
- [Category(CategoryTypes.Breadcrumbs.Appearance)]
- public string ExpanderIcon { get; set; } = Icons.Material.Filled.SettingsEthernet;
-
- public bool Collapsed { get; private set; } = true;
- private string Classname => new CssBuilder("breadcrumbs")
- .AddClass("typography-body1")
- .AddClass(AdditionalClassList)
- .Build();
-
- internal static string GetItemClassname(BreadcrumbItem item)
- {
- return new CssBuilder("breadcrumb-item")
- .AddClass("disabled", item.Disabled)
- .Build();
- }
+ #endregion
- internal void Expand()
- {
- if (!Collapsed)
- return;
-
- Collapsed = false;
- StateHasChanged();
- }
}
diff --git a/src/Connected.Components/Components/BreakpointProvider/BreakpointProvider.razor.cs b/src/Connected.Components/Components/BreakpointProvider/BreakpointProvider.razor.cs
index 3dcca7d..9108251 100644
--- a/src/Connected.Components/Components/BreakpointProvider/BreakpointProvider.razor.cs
+++ b/src/Connected.Components/Components/BreakpointProvider/BreakpointProvider.razor.cs
@@ -6,18 +6,37 @@ namespace Connected.Components;
public partial class BreakpointProvider : UIComponent, IAsyncDisposable
{
- private Guid _breakPointListenerSubscriptionId;
+ #region Variables
+ private Guid _breakPointListenerSubscriptionId;
+
public Breakpoint Breakpoint { get; private set; } = Breakpoint.Always;
- [Parameter] public EventCallback OnBreakpointChanged { get; set; }
-
[Inject] public IBreakpointService Service { get; set; }
+ #endregion
+
+ #region Events
+ private void SetBreakpointCallback(Breakpoint breakpoint)
+ {
+ InvokeAsync(() =>
+ {
+ Breakpoint = breakpoint;
+ OnBreakpointChanged.InvokeAsync(breakpoint);
+ StateHasChanged();
+ }).AndForget();
+ }
+
+ [Parameter] public EventCallback OnBreakpointChanged { get; set; }
+ #endregion
+
+ #region Content
[Parameter]
[Category(CategoryTypes.BreakpointProvider.Behavior)]
public RenderFragment ChildContent { get; set; }
+ #endregion
+ #region Lifecycle
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
@@ -32,15 +51,9 @@ public partial class BreakpointProvider : UIComponent, IAsyncDisposable
}
}
- private void SetBreakpointCallback(Breakpoint breakpoint)
- {
- InvokeAsync(() =>
- {
- Breakpoint = breakpoint;
- OnBreakpointChanged.InvokeAsync(breakpoint);
- StateHasChanged();
- }).AndForget();
- }
+
public async ValueTask DisposeAsync() => await Service.Unsubscribe(_breakPointListenerSubscriptionId);
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Button/Button.razor.cs b/src/Connected.Components/Components/Button/Button.razor.cs
index e650ba4..b1b835a 100644
--- a/src/Connected.Components/Components/Button/Button.razor.cs
+++ b/src/Connected.Components/Components/Button/Button.razor.cs
@@ -5,26 +5,16 @@ namespace Connected.Components;
public partial class Button : ButtonBase
{
- ///
- /// Contains the default container classlist and the user defined classes.
- ///
- private CssBuilder CompiledClassList
- {
- get
- {
- return new CssBuilder("button-root mud-button")
- .AddClass($"button-{Variant}")
- .AddClass(ClassList);
- }
- }
- #region Styling properties
+ #region Content
///
/// Child content of component.
///
[Parameter]
public RenderFragment? ChildContent { get; set; }
+ #endregion
+ #region Styling
///
/// A space separated list of class names, added on top of the default class list.
///
@@ -36,6 +26,18 @@ public partial class Button : ButtonBase
///
[Parameter]
public Variant Variant { get; set; } = Variant.Text;
+ ///
+ /// Contains the default container classlist and the user defined classes.
+ ///
+ private CssBuilder CompiledClassList
+ {
+ get
+ {
+ return new CssBuilder("button-root mud-button")
+ .AddClass($"button-{Variant}")
+ .AddClass(ClassList);
+ }
+ }
#endregion
}
diff --git a/src/Connected.Components/Components/Button/ButtonBase.cs b/src/Connected.Components/Components/Button/ButtonBase.cs
index c6e54ec..029852a 100644
--- a/src/Connected.Components/Components/Button/ButtonBase.cs
+++ b/src/Connected.Components/Components/Button/ButtonBase.cs
@@ -36,9 +36,13 @@ public abstract class ButtonBase : UIComponent
///
[CascadingParameter]
protected IActivatable? Activateable { get; set; }
- #endregion
- #region Styling properties
+ ///
+ /// The HTML element that will be rendered in the root by the component
+ /// By default, is a button.
+ ///
+ protected string HtmlTag => ButtonType.ToString().ToLower();
+
#endregion
#region Behavior properties
@@ -65,13 +69,6 @@ public abstract class ButtonBase : UIComponent
///
[Parameter]
public bool Disabled { get; set; }
- #endregion
-
- ///
- /// The HTML element that will be rendered in the root by the component
- /// By default, is a button.
- ///
- protected string HtmlTag => ButtonType.ToString().ToLower();
///
/// Indicates whether the internal click propagation should be disabled,
@@ -79,5 +76,6 @@ public abstract class ButtonBase : UIComponent
///
protected bool PreventOnClickPropagation => string.Compare(HtmlTag, "button", true) == 0;
-
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Button/Fab.razor.cs b/src/Connected.Components/Components/Button/Fab.razor.cs
index ce3e08c..3ad2a22 100644
--- a/src/Connected.Components/Components/Button/Fab.razor.cs
+++ b/src/Connected.Components/Components/Button/Fab.razor.cs
@@ -7,71 +7,72 @@ namespace Connected.Components;
public partial class Fab : ButtonBase
{
- protected string Classname =>
- new CssBuilder("button-root mud-fab")
- .AddClass($"fab-extended", !string.IsNullOrEmpty(Label))
- .AddClass($"fab-{Color.ToDescription()}")
- .AddClass($"fab-size-{Size.ToDescription()}")
- .Build();
+ #region Content
///
- /// The color of the component. It supports the theme colors.
+ /// If applied Glyph will be added at the start of the component.
///
[Parameter]
- [Category(CategoryTypes.Button.Appearance)]
- public ThemeColor Color { get; set; } = ThemeColor.Default;
+ [Category(CategoryTypes.Button.Behavior)]
+ public string StartIcon { get; set; }
///
- /// The Size of the component.
+ /// If applied Glyph will be added at the end of the component.
///
[Parameter]
- [Category(CategoryTypes.Button.Appearance)]
- public Size Size { get; set; } = Size.Large;
-
- ///
- /// If applied Glyph will be added at the start of the component.
- ///
- [Obsolete("This property is obsolete. Use StartIcon instead.")][Parameter] public string Icon { get => StartIcon; set => StartIcon = value; }
+ [Category(CategoryTypes.Button.Behavior)]
+ public string EndIcon { get; set; }
///
- /// If applied Glyph will be added at the start of the component.
+ /// If applied the text will be added to the component.
///
[Parameter]
[Category(CategoryTypes.Button.Behavior)]
- public string StartIcon { get; set; }
+ public string Label { get; set; }
///
- /// If applied Glyph will be added at the end of the component.
+ /// GlyphTitle of the icon used for accessibility.
///
[Parameter]
[Category(CategoryTypes.Button.Behavior)]
- public string EndIcon { get; set; }
+ public string Title { get; set; }
+ #endregion
+
+ #region Styling
+ protected string Classname =>
+ new CssBuilder("button-root mud-fab")
+ .AddClass($"fab-extended", !string.IsNullOrEmpty(Label))
+ .AddClass($"fab-{Color.ToDescription()}")
+ .AddClass($"fab-size-{Size.ToDescription()}")
+ .Build();
///
- /// The color of the icon. It supports the theme colors.
+ /// The color of the component. It supports the theme colors.
///
[Parameter]
[Category(CategoryTypes.Button.Appearance)]
- public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;
+ public ThemeColor Color { get; set; } = ThemeColor.Default;
///
- /// The size of the icon.
+ /// The Size of the component.
///
[Parameter]
[Category(CategoryTypes.Button.Appearance)]
- public Size IconSize { get; set; } = Size.Medium;
+ public Size Size { get; set; } = Size.Large;
///
- /// If applied the text will be added to the component.
+ /// The color of the icon. It supports the theme colors.
///
[Parameter]
- [Category(CategoryTypes.Button.Behavior)]
- public string Label { get; set; }
+ [Category(CategoryTypes.Button.Appearance)]
+ public ThemeColor IconColor { get; set; } = ThemeColor.Inherit;
///
- /// GlyphTitle of the icon used for accessibility.
+ /// The size of the icon.
///
[Parameter]
- [Category(CategoryTypes.Button.Behavior)]
- public string Title { get; set; }
+ [Category(CategoryTypes.Button.Appearance)]
+ public Size IconSize { get; set; } = Size.Medium;
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Button/IconButton.razor.cs b/src/Connected.Components/Components/Button/IconButton.razor.cs
index 2755723..047e8c7 100644
--- a/src/Connected.Components/Components/Button/IconButton.razor.cs
+++ b/src/Connected.Components/Components/Button/IconButton.razor.cs
@@ -5,28 +5,7 @@ namespace Connected.Components;
public partial class IconButton : ButtonBase
{
- ///
- /// Contains the default container classlist and the user defined classes.
- ///
- private CssBuilder CompiledClassList
- {
- get
- {
- return new CssBuilder("button-root glyph-button")
- .AddClass(ClassList);
- }
- }
-
- ///
- /// Child content of component, only shows if Glyph is null or Empty.
- ///
- [Parameter]
- public RenderFragment? ChildContent { get; set; }
-
- #region EventCallbacks
- #endregion
-
- #region Content placeholders
+ #region Content
///
/// The Icon that will be used in the component.
///
@@ -38,9 +17,16 @@ public partial class IconButton : ButtonBase
///
[Parameter]
public string? IconTitle { get; set; }
+
+ ///
+ /// Child content of component, only shows if Glyph is null or Empty.
+ ///
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+
#endregion
- #region Styling properties
+ #region Styling
///
/// A space separated list of class names, added on top of the default class list.
///
@@ -52,5 +38,17 @@ public partial class IconButton : ButtonBase
///
[Parameter]
public Variant Variant { get; set; } = Variant.Text;
+
+ ///
+ /// Contains the default container classlist and the user defined classes.
+ ///
+ private CssBuilder CompiledClassList
+ {
+ get
+ {
+ return new CssBuilder("button-root glyph-button")
+ .AddClass(ClassList);
+ }
+ }
#endregion
}
diff --git a/src/Connected.Components/Components/Button/ToggleIconButton.razor b/src/Connected.Components/Components/Button/ToggleIconButton.razor
index 1bd2f6c..004062b 100644
--- a/src/Connected.Components/Components/Button/ToggleIconButton.razor
+++ b/src/Connected.Components/Components/Button/ToggleIconButton.razor
@@ -6,8 +6,8 @@
ClassList="@ClassList"
Clicked="Toggle"
Disabled="Disabled"
- Icon="@(Toggled ? ToggledGlyph : Glyph)"
- IconTitle="@(Toggled && ToggledGlyphTitle != null ? ToggledGlyphTitle : GlyphTitle)"
+ Icon="@(Toggled ? ToggledIcon : Icon)"
+ IconTitle="@(Toggled && ToggledIconTitle != null ? ToggledIconTitle : IconTitle)"
Variant="Variant"
@attributes="CustomAttributes"
/>
diff --git a/src/Connected.Components/Components/Button/ToggleIconButton.razor.cs b/src/Connected.Components/Components/Button/ToggleIconButton.razor.cs
index 0d42b0b..53371a7 100644
--- a/src/Connected.Components/Components/Button/ToggleIconButton.razor.cs
+++ b/src/Connected.Components/Components/Button/ToggleIconButton.razor.cs
@@ -4,41 +4,63 @@ namespace Connected.Components;
public partial class ToggleIconButton : UIComponent
{
- #region EventCallbacks
+ #region Events
///
/// Fires whenever toggled is changed.
///
[Parameter]
public EventCallback ToggledChanged { get; set; }
+
+ public async Task Toggle()
+ {
+ await SetToggledAsync(!Toggled);
+ }
+
+ protected internal async Task SetToggledAsync(bool toggled)
+ {
+ if (Disabled)
+ return;
+
+ if (Toggled != toggled)
+ {
+ Toggled = toggled;
+
+ if (!ToggledChanged.HasDelegate)
+ return;
+
+ await ToggledChanged.InvokeAsync(Toggled);
+ }
+ }
+
#endregion
- #region Content placeholders
+ #region Content
///
/// The glyph that will be used in the untoggled state.
///
[Parameter]
- public string? Glyph { get; set; }
+ public string? Icon { get; set; }
///
/// GlyphTitle of the icon used for accessibility.
///
[Parameter]
- public string? GlyphTitle { get; set; }
+ public string? IconTitle { get; set; }
///
/// The glyph that will be used in the toggled state.
///
[Parameter]
- public string? ToggledGlyph { get; set; }
+ public string? ToggledIcon { get; set; }
///
/// GlyphTitle used in toggled state, if different.
///
[Parameter]
- public string? ToggledGlyphTitle { get; set; }
+ public string? ToggledIconTitle { get; set; }
#endregion
- #region Styling properties
+ #region Styling
///
/// A space separated list of class names, added on top of the default class list.
///
@@ -50,9 +72,7 @@ public partial class ToggleIconButton : UIComponent
///
[Parameter]
public Variant Variant { get; set; } = Variant.Text;
- #endregion
- #region Behavior properties
///
/// If true, the button will be disabled.
///
@@ -64,26 +84,6 @@ public partial class ToggleIconButton : UIComponent
///
[Parameter]
public bool Toggled { get; set; }
- #endregion
-
- public async Task Toggle()
- {
- await SetToggledAsync(!Toggled);
- }
-
- protected internal async Task SetToggledAsync(bool toggled)
- {
- if (Disabled)
- return;
- if (Toggled != toggled)
- {
- Toggled = toggled;
-
- if (!ToggledChanged.HasDelegate)
- return;
-
- await ToggledChanged.InvokeAsync(Toggled);
- }
- }
+ #endregion
}
diff --git a/src/Connected.Components/Components/ButtonGroup/ButtonGroup.razor.cs b/src/Connected.Components/Components/ButtonGroup/ButtonGroup.razor.cs
index 4b2a6e5..2f80de2 100644
--- a/src/Connected.Components/Components/ButtonGroup/ButtonGroup.razor.cs
+++ b/src/Connected.Components/Components/ButtonGroup/ButtonGroup.razor.cs
@@ -7,69 +7,75 @@ namespace Connected.Components;
public partial class ButtonGroup : UIComponent
{
- protected string Classname =>
- new CssBuilder("button-group-root")
- .AddClass($"button-group-override-styles", OverrideStyles)
- .AddClass($"button-group-{Variant.ToDescription()}")
- .AddClass($"button-group-{Variant.ToDescription()}-{Color.ToDescription()}")
- .AddClass($"button-group-{Variant.ToDescription()}-size-{Size.ToDescription()}")
- .AddClass($"button-group-vertical", VerticalAlign)
- .AddClass($"button-group-horizontal", !VerticalAlign)
- .AddClass($"button-group-disable-elevation", DisableElevation)
- .AddClass($"button-group-rtl", RightToLeft)
- .AddClass(AdditionalClassList)
- .Build();
+ #region Content
+ ///
+ /// Child content of component.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.ButtonGroup.Behavior)]
+ public RenderFragment ChildContent { get; set; }
+ #endregion
- [CascadingParameter(Name = "RightToLeft")]
- public bool RightToLeft { get; set; }
+ #region Styling
+ ///
+ /// If true, the button group will be displayed vertically.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.ButtonGroup.Appearance)]
+ public bool VerticalAlign { get; set; } = false;
- ///
- /// If true, the button group will override the styles of the individual buttons.
- ///
- [Parameter]
- [Category(CategoryTypes.ButtonGroup.Appearance)]
- public bool OverrideStyles { get; set; } = true;
+ ///
+ /// If true, no drop-shadow will be used.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.ButtonGroup.Appearance)]
+ public bool DisableElevation { get; set; } = false;
- ///
- /// Child content of component.
- ///
- [Parameter]
- [Category(CategoryTypes.ButtonGroup.Behavior)]
- public RenderFragment ChildContent { get; set; }
+ ///
+ /// The color of the component. It supports the theme colors.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.ButtonGroup.Appearance)]
+ public ThemeColor Color { get; set; } = ThemeColor.Default;
- ///
- /// If true, the button group will be displayed vertically.
- ///
- [Parameter]
- [Category(CategoryTypes.ButtonGroup.Appearance)]
- public bool VerticalAlign { get; set; } = false;
+ ///
+ /// The size of the component.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.ButtonGroup.Appearance)]
+ public Size Size { get; set; } = Size.Medium;
- ///
- /// If true, no drop-shadow will be used.
- ///
- [Parameter]
- [Category(CategoryTypes.ButtonGroup.Appearance)]
- public bool DisableElevation { get; set; } = false;
+ ///
+ /// The variant to use.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.ButtonGroup.Appearance)]
+ public Variant Variant { get; set; } = Variant.Text;
- ///
- /// The color of the component. It supports the theme colors.
- ///
- [Parameter]
- [Category(CategoryTypes.ButtonGroup.Appearance)]
- public ThemeColor Color { get; set; } = ThemeColor.Default;
+ protected string Classname =>
+ new CssBuilder("button-group-root")
+ .AddClass($"button-group-override-styles", OverrideStyles)
+ .AddClass($"button-group-{Variant.ToDescription()}")
+ .AddClass($"button-group-{Variant.ToDescription()}-{Color.ToDescription()}")
+ .AddClass($"button-group-{Variant.ToDescription()}-size-{Size.ToDescription()}")
+ .AddClass($"button-group-vertical", VerticalAlign)
+ .AddClass($"button-group-horizontal", !VerticalAlign)
+ .AddClass($"button-group-disable-elevation", DisableElevation)
+ .AddClass($"button-group-rtl", RightToLeft)
+ .AddClass(AdditionalClassList)
+ .Build();
- ///
- /// The size of the component.
- ///
- [Parameter]
- [Category(CategoryTypes.ButtonGroup.Appearance)]
- public Size Size { get; set; } = Size.Medium;
- ///
- /// The variant to use.
- ///
- [Parameter]
- [Category(CategoryTypes.ButtonGroup.Appearance)]
- public Variant Variant { get; set; } = Variant.Text;
+ [CascadingParameter(Name = "RightToLeft")]
+ public bool RightToLeft { get; set; }
+
+ ///
+ /// If true, the button group will override the styles of the individual buttons.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.ButtonGroup.Appearance)]
+ public bool OverrideStyles { get; set; } = true;
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Card/Card.razor.cs b/src/Connected.Components/Components/Card/Card.razor.cs
index 0b954b0..c9e1baa 100644
--- a/src/Connected.Components/Components/Card/Card.razor.cs
+++ b/src/Connected.Components/Components/Card/Card.razor.cs
@@ -6,6 +6,16 @@ namespace Connected.Components;
public partial class Card : UIComponent
{
+ #region Content
+ ///
+ /// Child content of the component.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.Card.Behavior)]
+ public RenderFragment ChildContent { get; set; }
+ #endregion
+
+ #region Styling
protected string Classname =>
new CssBuilder("card")
.AddClass(AdditionalClassList)
@@ -31,11 +41,6 @@ public partial class Card : UIComponent
[Parameter]
[Category(CategoryTypes.Card.Appearance)]
public bool Outlined { get; set; }
+ #endregion
- ///
- /// Child content of the component.
- ///
- [Parameter]
- [Category(CategoryTypes.Card.Behavior)]
- public RenderFragment ChildContent { get; set; }
}
diff --git a/src/Connected.Components/Components/Card/CardActions.razor.cs b/src/Connected.Components/Components/Card/CardActions.razor.cs
index 3c74bf1..8979a02 100644
--- a/src/Connected.Components/Components/Card/CardActions.razor.cs
+++ b/src/Connected.Components/Components/Card/CardActions.razor.cs
@@ -6,15 +6,22 @@ namespace Connected.Components;
public partial class CardActions : UIComponent
{
+ #region Style
protected string Classname =>
new CssBuilder("card-actions")
.AddClass(AdditionalClassList)
.Build();
+ #endregion
+
+ #region Content
+
///
/// Child content of the component.
///
[Parameter]
[Category(CategoryTypes.Card.Behavior)]
public RenderFragment ChildContent { get; set; }
+
+ #endregion
}
diff --git a/src/Connected.Components/Components/Card/CardContent.razor.cs b/src/Connected.Components/Components/Card/CardContent.razor.cs
index 28c0f76..7c8cbca 100644
--- a/src/Connected.Components/Components/Card/CardContent.razor.cs
+++ b/src/Connected.Components/Components/Card/CardContent.razor.cs
@@ -6,15 +6,22 @@ namespace Connected.Components;
public partial class CardContent : UIComponent
{
+ #region Style
protected string Classname =>
new CssBuilder("card-content")
.AddClass(AdditionalClassList)
.Build();
+ #endregion
+
+ #region Content
+
///
/// Child content of the component.
///
[Parameter]
[Category(CategoryTypes.Card.Behavior)]
public RenderFragment ChildContent { get; set; }
+
+ #endregion
}
diff --git a/src/Connected.Components/Components/Card/CardHeader.razor.cs b/src/Connected.Components/Components/Card/CardHeader.razor.cs
index 79f0065..f826226 100644
--- a/src/Connected.Components/Components/Card/CardHeader.razor.cs
+++ b/src/Connected.Components/Components/Card/CardHeader.razor.cs
@@ -6,7 +6,8 @@ namespace Connected.Components;
public partial class CardHeader : UIComponent
{
- protected string Classname =>
+ #region Style
+ protected string Classname =>
new CssBuilder("card-header")
.AddClass(AdditionalClassList)
.Build();
@@ -32,10 +33,16 @@ public partial class CardHeader : UIComponent
[Category(CategoryTypes.Card.Behavior)]
public RenderFragment CardHeaderActions { get; set; }
- ///
- /// Optional child content
- ///
- [Parameter]
+ #endregion
+
+ #region Content
+
+ ///
+ /// Optional child content
+ ///
+ [Parameter]
[Category(CategoryTypes.Card.Behavior)]
public RenderFragment ChildContent { get; set; }
+
+ #endregion
}
diff --git a/src/Connected.Components/Components/Card/CardMedia.razor.cs b/src/Connected.Components/Components/Card/CardMedia.razor.cs
index 3009eab..88793be 100644
--- a/src/Connected.Components/Components/Card/CardMedia.razor.cs
+++ b/src/Connected.Components/Components/Card/CardMedia.razor.cs
@@ -6,6 +6,7 @@ namespace Connected.Components;
public partial class CardMedia : UIComponent
{
+ #region Style
protected string StyleString =>
StyleBuilder.Default($"background-image:url(\"{Image}\");height: {Height}px;")
.Build();
@@ -16,23 +17,30 @@ public partial class CardMedia : UIComponent
.Build();
///
- /// GlyphTitle of the image used for accessibility.
+ /// Specifies the height of the image in px.
///
[Parameter]
[Category(CategoryTypes.Card.Behavior)]
- public string Title { get; set; }
+ public int Height { get; set; } = 300;
+
+ #endregion
+
+ #region Content
///
- /// Specifies the path to the image.
+ /// GlyphTitle of the image used for accessibility.
///
[Parameter]
[Category(CategoryTypes.Card.Behavior)]
- public string Image { get; set; }
+ public string Title { get; set; }
///
- /// Specifies the height of the image in px.
+ /// Specifies the path to the image.
///
[Parameter]
[Category(CategoryTypes.Card.Behavior)]
- public int Height { get; set; } = 300;
+ public string Image { get; set; }
+
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Carousel/Carousel.razor.cs b/src/Connected.Components/Components/Carousel/Carousel.razor.cs
index 5bac57e..0d1646b 100644
--- a/src/Connected.Components/Components/Carousel/Carousel.razor.cs
+++ b/src/Connected.Components/Components/Carousel/Carousel.razor.cs
@@ -8,6 +8,102 @@ namespace Connected.Components;
public partial class Carousel : BindableItemsControlBase, IAsyncDisposable
{
+ #region Variables
+
+ private Timer _timer;
+ private bool _autoCycle = true;
+ private ThemeColor _currentColor = ThemeColor.Inherit;
+ private TimeSpan _cycleTimeout = TimeSpan.FromSeconds(5);
+ private void TimerElapsed(object stateInfo) => InvokeAsync(async () => await TimerTickAsync());
+
+ #endregion
+
+ #region Events
+ ///
+ /// Called when selected Index changed on base class
+ ///
+ protected override void SelectionChanged()
+ {
+ InvokeAsync(async () => await ResetTimerAsync());
+
+ _currentColor = SelectedContainer?.Color ?? ThemeColor.Inherit;
+ }
+
+ //When an item is added, it automatically checks the color
+ public override void AddItem(CarouselItem item)
+ {
+ Items.Add(item);
+ if (Items.Count - 1 == SelectedIndex)
+ {
+ _currentColor = item.Color;
+ StateHasChanged();
+ }
+ }
+
+ ///
+ /// Provides Selection changes by horizontal swipe gesture
+ ///
+ private void OnSwipe(SwipeDirection direction)
+ {
+ if (!EnableSwipeGesture)
+ {
+ return;
+ }
+
+ switch (direction)
+ {
+ case SwipeDirection.LeftToRight:
+ if (RightToLeft) Next();
+ else Previous();
+ break;
+
+ case SwipeDirection.RightToLeft:
+ if (RightToLeft) Previous();
+ else Next();
+ break;
+ }
+ }
+
+ ///
+ /// Immediately starts the AutoCycle timer
+ ///
+ private ValueTask StartTimerAsync()
+ {
+ if (AutoCycle)
+ _timer?.Change(AutoCycleTime, TimeSpan.Zero);
+
+ return ValueTask.CompletedTask;
+ }
+
+ ///
+ /// Immediately stops the AutoCycle timer
+ ///
+ private ValueTask StopTimerAsync()
+ {
+ _timer?.Change(Timeout.Infinite, Timeout.Infinite);
+ return ValueTask.CompletedTask;
+ }
+
+ ///
+ /// Stops and restart the AutoCycle timer
+ ///
+ private async ValueTask ResetTimerAsync()
+ {
+ await StopTimerAsync();
+ await StartTimerAsync();
+ }
+
+
+ ///
+ /// Changes the SelectedIndex to a next one (or restart on 0)
+ ///
+ private async ValueTask TimerTickAsync()
+ {
+ await InvokeAsync(Next);
+ }
+ #endregion
+
+ #region Styling
protected string Classname =>
new CssBuilder("carousel")
.AddClass($"carousel-{(BulletsColor ?? _currentColor).ToDescription()}")
@@ -26,12 +122,6 @@ public partial class Carousel : BindableItemsControlBase InvokeAsync(async () => await TimerTickAsync());
-
private static Position ConvertPosition(Position position)
{
return position switch
@@ -61,12 +151,6 @@ public partial class Carousel : BindableItemsControlBase
- /// Gets or Sets if bar with Bullets must be visible
- ///
- [Category(CategoryTypes.Carousel.Behavior)]
- [Parameter] public bool ShowBullets { get; set; } = true;
-
///
/// Sets the position of the bullets. By default, the position is the Bottom position
///
@@ -80,16 +164,6 @@ public partial class Carousel : BindableItemsControlBase
- /// Gets or Sets if bottom bar with Delimiters must be visible.
- /// Deprecated, use ShowBullets instead.
- ///
- [Category(CategoryTypes.Carousel.Behavior)]
- [Obsolete($"Use {nameof(ShowBullets)} instead", false)]
- [ExcludeFromCodeCoverage]
- [Parameter] public bool ShowDelimiters { get => ShowBullets; set => ShowBullets = value; }
-
///
/// Gets or Sets the Delimiters color.
/// If not set, the color is determined based on the property of the active child.
@@ -100,48 +174,6 @@ public partial class Carousel : BindableItemsControlBase BulletsColor; set => BulletsColor = value; }
- ///
- /// Gets or Sets automatic cycle on item collection.
- ///
- [Parameter]
- [Category(CategoryTypes.Carousel.Behavior)]
- public bool AutoCycle
- {
- get => _autoCycle;
- set
- {
- _autoCycle = value;
-
- if (_autoCycle)
- InvokeAsync(async () => await ResetTimerAsync());
-
- else
- InvokeAsync(async () => await StopTimerAsync());
- }
- }
-
-
- ///
- /// Gets or Sets the Auto Cycle time
- ///
- [Parameter]
- [Category(CategoryTypes.Carousel.Behavior)]
- public TimeSpan AutoCycleTime
- {
- get => _cycleTimeout;
- set
- {
- _cycleTimeout = value;
-
- if (_autoCycle == true)
- InvokeAsync(async () => await ResetTimerAsync());
-
- else
- InvokeAsync(async () => await StopTimerAsync());
- }
- }
-
-
///
/// Gets or Sets custom class(es) for 'Next' and 'Previous' arrows
///
@@ -214,13 +246,6 @@ public partial class Carousel : BindableItemsControlBase BulletTemplate { get; set; }
- ///
- /// Gets or Sets if swipe gestures are allowed for touch devices.
- ///
- [Category(CategoryTypes.Carousel.Behavior)]
- [Parameter]
- public bool EnableSwipeGesture { get; set; } = true;
-
///
/// Gets or Sets the Template for Delimiters.
/// Deprecated, use BulletsTemplate instead.
@@ -230,91 +255,78 @@ public partial class Carousel : BindableItemsControlBase DelimiterTemplate { get => BulletTemplate; set => BulletTemplate = value; }
+ #endregion
+ #region Behavior
///
- /// Called when selected Index changed on base class
+ /// Gets or Sets if bar with Bullets must be visible
///
- protected override void SelectionChanged()
- {
- InvokeAsync(async () => await ResetTimerAsync());
-
- _currentColor = SelectedContainer?.Color ?? ThemeColor.Inherit;
- }
+ [Category(CategoryTypes.Carousel.Behavior)]
+ [Parameter] public bool ShowBullets { get; set; } = true;
- //When an item is added, it automatically checks the color
- public override void AddItem(CarouselItem item)
- {
- Items.Add(item);
- if (Items.Count - 1 == SelectedIndex)
- {
- _currentColor = item.Color;
- StateHasChanged();
- }
- }
+ ///
+ /// Gets or Sets if bottom bar with Delimiters must be visible.
+ /// Deprecated, use ShowBullets instead.
+ ///
+ [Category(CategoryTypes.Carousel.Behavior)]
+ [Obsolete($"Use {nameof(ShowBullets)} instead", false)]
+ [ExcludeFromCodeCoverage]
+ [Parameter] public bool ShowDelimiters { get => ShowBullets; set => ShowBullets = value; }
///
- /// Provides Selection changes by horizontal swipe gesture
+ /// Gets or Sets automatic cycle on item collection.
///
- private void OnSwipe(SwipeDirection direction)
+ [Parameter]
+ [Category(CategoryTypes.Carousel.Behavior)]
+ public bool AutoCycle
{
- if (!EnableSwipeGesture)
+ get => _autoCycle;
+ set
{
- return;
- }
+ _autoCycle = value;
- switch (direction)
- {
- case SwipeDirection.LeftToRight:
- if (RightToLeft) Next();
- else Previous();
- break;
+ if (_autoCycle)
+ InvokeAsync(async () => await ResetTimerAsync());
- case SwipeDirection.RightToLeft:
- if (RightToLeft) Previous();
- else Next();
- break;
+ else
+ InvokeAsync(async () => await StopTimerAsync());
}
}
+
///
- /// Immediately starts the AutoCycle timer
+ /// Gets or Sets the Auto Cycle time
///
- private ValueTask StartTimerAsync()
+ [Parameter]
+ [Category(CategoryTypes.Carousel.Behavior)]
+ public TimeSpan AutoCycleTime
{
- if (AutoCycle)
- _timer?.Change(AutoCycleTime, TimeSpan.Zero);
+ get => _cycleTimeout;
+ set
+ {
+ _cycleTimeout = value;
- return ValueTask.CompletedTask;
- }
+ if (_autoCycle == true)
+ InvokeAsync(async () => await ResetTimerAsync());
- ///
- /// Immediately stops the AutoCycle timer
- ///
- private ValueTask StopTimerAsync()
- {
- _timer?.Change(Timeout.Infinite, Timeout.Infinite);
- return ValueTask.CompletedTask;
+ else
+ InvokeAsync(async () => await StopTimerAsync());
+ }
}
- ///
- /// Stops and restart the AutoCycle timer
- ///
- private async ValueTask ResetTimerAsync()
- {
- await StopTimerAsync();
- await StartTimerAsync();
- }
+
///
- /// Changes the SelectedIndex to a next one (or restart on 0)
+ /// Gets or Sets if swipe gestures are allowed for touch devices.
///
- private async ValueTask TimerTickAsync()
- {
- await InvokeAsync(Next);
- }
+ [Category(CategoryTypes.Carousel.Behavior)]
+ [Parameter]
+ public bool EnableSwipeGesture { get; set; } = true;
+ #endregion
+ #region Lifecycle
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
@@ -346,4 +358,6 @@ public partial class Carousel : BindableItemsControlBase Parent { get; set; }
+ #endregion
+
+ #region Styling
protected string Classname =>
new CssBuilder("carousel-item")
.AddClass($"carousel-item-{Color.ToDescription()}")
@@ -35,14 +48,6 @@ public partial class CarouselItem : UIComponent, IDisposable
.AddClass(AdditionalClassList)
.Build();
- [Parameter]
- [Category(CategoryTypes.Carousel.Behavior)]
- public RenderFragment ChildContent { get; set; }
-
- [CascadingParameter] protected internal ItemsControlBase Parent { get; set; }
-
- [CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
-
///
/// The color of the component. It supports the theme colors.
///
@@ -71,21 +76,24 @@ public partial class CarouselItem : UIComponent, IDisposable
[Category(CategoryTypes.Carousel.Appearance)]
public string CustomTransitionExit { get; set; }
+ [CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
public bool IsVisible => Parent != null && (Parent.LastContainer == this || Parent.SelectedIndex == Parent.Items.IndexOf(this));
+ #endregion
-
+ #region Lifecycle
protected override Task OnInitializedAsync()
{
Parent?.AddItem(this);
return Task.CompletedTask;
}
- private bool _disposed = false;
+
public void Dispose()
{
_disposed = true;
Parent?.Items.Remove(this);
}
+ #endregion
}
diff --git a/src/Connected.Components/Components/Chart/Chart.razor.cs b/src/Connected.Components/Components/Chart/Chart.razor.cs
index e5ba7df..f76d160 100644
--- a/src/Connected.Components/Components/Chart/Chart.razor.cs
+++ b/src/Connected.Components/Components/Chart/Chart.razor.cs
@@ -8,22 +8,47 @@ namespace Connected.Components;
public partial class Chart : UIComponent
{
- [Parameter]
- [Category(CategoryTypes.Chart.Behavior)]
- public double[] InputData { get; set; } = Array.Empty();
- [Parameter]
- [Category(CategoryTypes.Chart.Behavior)]
- public string[] InputLabels { get; set; } = Array.Empty();
+ #region Variables
+ private int _selectedIndex;
+ #endregion
- [Parameter]
- [Category(CategoryTypes.Chart.Behavior)]
- public string[] XAxisLabels { get; set; } = Array.Empty();
+ #region Events
+ ///
+ /// Selected index of a portion of the chart.
+ ///
+ [Parameter] public EventCallback SelectedIndexChanged { get; set; }
- [Parameter]
- [Category(CategoryTypes.Chart.Behavior)]
- public List ChartSeries { get; set; } = new();
+ ///
+ /// Scales the input data to the range between 0 and 1
+ ///
+ protected double[] GetNormalizedData()
+ {
+ if (InputData == null)
+ return Array.Empty();
+ var total = InputData.Sum();
+ return InputData.Select(x => Math.Abs(x) / total).ToArray();
+ }
+ protected string ToS(double d, string format = null)
+ {
+ if (string.IsNullOrEmpty(format))
+ return d.ToString(CultureInfo.InvariantCulture);
+
+ return d.ToString(format);
+ }
+ private Position ConvertLegendPosition(Position position)
+ {
+ return position switch
+ {
+ Position.Start => RightToLeft ? Position.Right : Position.Left,
+ Position.End => RightToLeft ? Position.Left : Position.Right,
+ _ => position
+ };
+ }
+ #endregion
+
+ #region Styling
[Parameter]
[Category(CategoryTypes.Chart.Appearance)]
public ChartOptions ChartOptions { get; set; } = new();
@@ -43,13 +68,6 @@ public partial class Chart : UIComponent
[CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
- ///
- /// The Type of the chart.
- ///
- [Parameter]
- [Category(CategoryTypes.Chart.Behavior)]
- public ChartType ChartType { get; set; }
-
///
/// The Width of the chart, end with % or px.
///
@@ -71,17 +89,30 @@ public partial class Chart : UIComponent
[Category(CategoryTypes.Chart.Appearance)]
public Position LegendPosition { get; set; } = Position.Bottom;
- private Position ConvertLegendPosition(Position position)
- {
- return position switch
- {
- Position.Start => RightToLeft ? Position.Right : Position.Left,
- Position.End => RightToLeft ? Position.Left : Position.Right,
- _ => position
- };
- }
+ #endregion
- private int _selectedIndex;
+ #region Behavior
+ [Parameter]
+ [Category(CategoryTypes.Chart.Behavior)]
+ public double[] InputData { get; set; } = Array.Empty();
+
+ [Parameter]
+ [Category(CategoryTypes.Chart.Behavior)]
+ public string[] InputLabels { get; set; } = Array.Empty();
+
+ [Parameter]
+ [Category(CategoryTypes.Chart.Behavior)]
+ public string[] XAxisLabels { get; set; } = Array.Empty();
+
+ [Parameter]
+ [Category(CategoryTypes.Chart.Behavior)]
+ public List ChartSeries { get; set; } = new();
+ ///
+ /// The Type of the chart.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.Chart.Behavior)]
+ public ChartType ChartType { get; set; }
///
/// Selected index of a portion of the chart.
@@ -101,29 +132,7 @@ public partial class Chart : UIComponent
}
}
- ///
- /// Selected index of a portion of the chart.
- ///
- [Parameter] public EventCallback SelectedIndexChanged { get; set; }
-
- ///
- /// Scales the input data to the range between 0 and 1
- ///
- protected double[] GetNormalizedData()
- {
- if (InputData == null)
- return Array.Empty();
- var total = InputData.Sum();
- return InputData.Select(x => Math.Abs(x) / total).ToArray();
- }
-
- protected string ToS(double d, string format = null)
- {
- if (string.IsNullOrEmpty(format))
- return d.ToString(CultureInfo.InvariantCulture);
-
- return d.ToString(format);
- }
+ #endregion
}
diff --git a/src/Connected.Components/Components/Chart/Charts/Bar.razor.cs b/src/Connected.Components/Components/Chart/Charts/Bar.razor.cs
index 7a49a3f..d1b097a 100644
--- a/src/Connected.Components/Components/Chart/Charts/Bar.razor.cs
+++ b/src/Connected.Components/Components/Chart/Charts/Bar.razor.cs
@@ -4,6 +4,7 @@ namespace Connected.Components;
partial class Bar : Chart
{
+ #region Variables
[CascadingParameter] public Chart ChartParent { get; set; }
private List _horizontalLines = new();
@@ -16,7 +17,9 @@ partial class Bar : Chart
private List _series = new();
private List _bars = new();
+ #endregion
+ #region Lifecycle
protected override void OnParametersSet()
{
base.OnParametersSet();
@@ -147,4 +150,6 @@ partial class Bar : Chart
_legends.Add(legend);
}
}
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Chart/Charts/Donut.razor.cs b/src/Connected.Components/Components/Chart/Charts/Donut.razor.cs
index 44299f6..83c6807 100644
--- a/src/Connected.Components/Components/Chart/Charts/Donut.razor.cs
+++ b/src/Connected.Components/Components/Chart/Charts/Donut.razor.cs
@@ -4,6 +4,7 @@ namespace Connected.Components;
partial class Donut : Chart
{
+ #region Variables
[CascadingParameter] public Chart ChartParent { get; set; }
private List _circles = new();
@@ -12,6 +13,10 @@ partial class Donut : Chart
protected string ParentWidth => ChartParent?.Width;
protected string ParentHeight => ChartParent?.Height;
+ #endregion
+
+ #region Lifecycle
+
protected override void OnParametersSet()
{
_circles.Clear();
@@ -57,4 +62,6 @@ partial class Donut : Chart
}
}
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Chart/Charts/Line.razor.cs b/src/Connected.Components/Components/Chart/Charts/Line.razor.cs
index 1833cba..4d6962c 100644
--- a/src/Connected.Components/Components/Chart/Charts/Line.razor.cs
+++ b/src/Connected.Components/Components/Chart/Charts/Line.razor.cs
@@ -4,6 +4,8 @@ namespace Connected.Components;
partial class Line : Chart
{
+ #region Variables
+
private const int MaxHorizontalGridLines = 100;
[CascadingParameter] public Chart ChartParent { get; set; }
@@ -19,6 +21,10 @@ partial class Line : Chart
private List _chartLines = new();
+ #endregion
+
+ #region Lifecycle
+
protected override void OnParametersSet()
{
base.OnParametersSet();
@@ -228,4 +234,6 @@ partial class Line : Chart
_legends.Add(legend);
}
}
+
+ #endregion
}
diff --git a/src/Connected.Components/Components/Chart/Charts/Pie.razor.cs b/src/Connected.Components/Components/Chart/Charts/Pie.razor.cs
index 665d684..e0e9c6d 100644
--- a/src/Connected.Components/Components/Chart/Charts/Pie.razor.cs
+++ b/src/Connected.Components/Components/Chart/Charts/Pie.razor.cs
@@ -4,11 +4,16 @@ namespace Connected.Components;
partial class Pie : Chart
{
+ #region Variables
[CascadingParameter] public Chart ChartParent { get; set; }
private List _paths = new();
private List _legends = new();
+ #endregion
+
+ #region Lifecycle
+
protected override void OnParametersSet()
{
_paths.Clear();
@@ -52,4 +57,5 @@ partial class Pie : Chart
counter += 1;
}
}
+ #endregion
}
diff --git a/src/Connected.Components/Components/CheckBox/CheckBox.razor.cs b/src/Connected.Components/Components/CheckBox/CheckBox.razor.cs
index 28b7899..3e67b39 100644
--- a/src/Connected.Components/Components/CheckBox/CheckBox.razor.cs
+++ b/src/Connected.Components/Components/CheckBox/CheckBox.razor.cs
@@ -9,6 +9,90 @@ namespace Connected.Components;
public partial class CheckBox : BooleanInput
{
+ #region Variables
+ private IKeyInterceptor _keyInterceptor;
+ [Inject] private IKeyInterceptorFactory _keyInterceptorFactory { get; set; }
+ private string _elementId = "checkbox" + Guid.NewGuid().ToString().Substring(0, 8);
+ #endregion
+
+ #region Events
+ protected override Task OnChange(ChangeEventArgs args)
+ {
+ Modified = true;
+
+ // Apply only when TriState parameter is set to true and T is bool?
+ if (TriState && typeof(T) == typeof(bool?))
+ {
+ // The cycle is forced with the following steps: true, false, indeterminate, true, false, indeterminate...
+ if (!((bool?)(object)_value).HasValue)
+ {
+ return SetBoolValueAsync(true);
+ }
+ else
+ {
+ return ((bool?)(object)_value).Value ? SetBoolValueAsync(false) : SetBoolValueAsync(default);
+ }
+ }
+ else
+ {
+ return SetBoolValueAsync((bool?)args.Value);
+ }
+ }
+
+ protected void HandleKeyDown(KeyboardEventArgs obj)
+ {
+ if (Disabled || ReadOnly || !KeyboardEnabled)
+ return;
+ switch (obj.Key)
+ {
+ case "Delete":
+ SetBoolValueAsync(false);
+ break;
+ case "Enter":
+ case "NumpadEnter":
+ SetBoolValueAsync(true);
+ break;
+ case "Backspace":
+ if (TriState)
+ {
+ SetBoolValueAsync(null);
+ }
+ break;
+ case " ":
+ if (BoolValue == null)
+ {
+ SetBoolValueAsync(true);
+ }
+ else if (BoolValue == true)
+ {
+ SetBoolValueAsync(false);
+ }
+ else if (BoolValue == false)
+ {
+ if (TriState == true)
+ {
+ SetBoolValueAsync(null);
+ }
+ else
+ {
+ SetBoolValueAsync(true);
+ }
+ }
+ break;
+ }
+ }
+ #endregion
+
+ #region Content
+ ///
+ /// Child content of component.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Behavior)]
+ public RenderFragment ChildContent { get; set; }
+ #endregion
+
+ #region Styling
protected string Classname =>
new CssBuilder("input-control-boolean-input")
.AddClass(AdditionalClassList)
@@ -45,27 +129,6 @@ public partial class CheckBox : BooleanInput
[Category(CategoryTypes.Radio.Appearance)]
public ThemeColor? UnCheckedColor { get; set; } = null;
- ///
- /// The text/label will be displayed next to the checkbox if set.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Behavior)]
- public string Label { get; set; }
-
- ///
- /// The position of the text/label.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Behavior)]
- public LabelPosition LabelPosition { get; set; } = LabelPosition.End;
-
- ///
- /// If true, the checkbox can be controlled with the keyboard.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Behavior)]
- public bool KeyboardEnabled { get; set; } = true;
-
///
/// If true, disables ripple effect.
///
@@ -87,13 +150,6 @@ public partial class CheckBox : BooleanInput
[Category(CategoryTypes.FormComponent.Appearance)]
public Size Size { get; set; } = Size.Medium;
- ///
- /// Child content of component.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Behavior)]
- public RenderFragment ChildContent { get; set; }
-
///
/// Custom checked icon, leave null for default.
///
@@ -115,13 +171,6 @@ public partial class CheckBox : BooleanInput
[Category(CategoryTypes.FormComponent.Appearance)]
public string IndeterminateIcon { get; set; } = Icons.Material.Filled.IndeterminateCheckBox;
- ///
- /// Define if the checkbox can cycle again through indeterminate status.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Validation)]
- public bool TriState { get; set; }
-
private string GetIcon()
{
if (BoolValue == true)
@@ -137,77 +186,39 @@ public partial class CheckBox : BooleanInput
return IndeterminateIcon;
}
- protected override Task OnChange(ChangeEventArgs args)
- {
- Modified = true;
+ #endregion
- // Apply only when TriState parameter is set to true and T is bool?
- if (TriState && typeof(T) == typeof(bool?))
- {
- // The cycle is forced with the following steps: true, false, indeterminate, true, false, indeterminate...
- if (!((bool?)(object)_value).HasValue)
- {
- return SetBoolValueAsync(true);
- }
- else
- {
- return ((bool?)(object)_value).Value ? SetBoolValueAsync(false) : SetBoolValueAsync(default);
- }
- }
- else
- {
- return SetBoolValueAsync((bool?)args.Value);
- }
- }
+ #region Behavior
+ ///
+ /// The text/label will be displayed next to the checkbox if set.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Behavior)]
+ public string Label { get; set; }
- protected void HandleKeyDown(KeyboardEventArgs obj)
- {
- if (Disabled || ReadOnly || !KeyboardEnabled)
- return;
- switch (obj.Key)
- {
- case "Delete":
- SetBoolValueAsync(false);
- break;
- case "Enter":
- case "NumpadEnter":
- SetBoolValueAsync(true);
- break;
- case "Backspace":
- if (TriState)
- {
- SetBoolValueAsync(null);
- }
- break;
- case " ":
- if (BoolValue == null)
- {
- SetBoolValueAsync(true);
- }
- else if (BoolValue == true)
- {
- SetBoolValueAsync(false);
- }
- else if (BoolValue == false)
- {
- if (TriState == true)
- {
- SetBoolValueAsync(null);
- }
- else
- {
- SetBoolValueAsync(true);
- }
- }
- break;
- }
- }
+ ///
+ /// The position of the text/label.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Behavior)]
+ public LabelPosition LabelPosition { get; set; } = LabelPosition.End;
- private IKeyInterceptor _keyInterceptor;
- [Inject] private IKeyInterceptorFactory _keyInterceptorFactory { get; set; }
+ ///
+ /// If true, the checkbox can be controlled with the keyboard.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Behavior)]
+ public bool KeyboardEnabled { get; set; } = true;
- private string _elementId = "checkbox" + Guid.NewGuid().ToString().Substring(0, 8);
+ ///
+ /// Define if the checkbox can cycle again through indeterminate status.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Validation)]
+ public bool TriState { get; set; }
+ #endregion
+ #region Lifecycle
protected override void OnInitialized()
{
base.OnInitialized();
@@ -251,4 +262,6 @@ public partial class CheckBox : BooleanInput
}
}
}
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Chip/Chip.razor.cs b/src/Connected.Components/Components/Chip/Chip.razor.cs
index 8520042..6f10742 100644
--- a/src/Connected.Components/Components/Chip/Chip.razor.cs
+++ b/src/Connected.Components/Components/Chip/Chip.razor.cs
@@ -10,11 +10,101 @@ namespace Connected.Components;
public partial class Chip : UIComponent, IDisposable
{
+
+ #region Variables
private bool _isSelected;
[Inject] public NavigationManager UriHelper { get; set; }
[Inject] public IJsApiService JsApiService { get; set; }
+ #endregion
+
+ #region Events
+
+ internal void ForceRerender() => StateHasChanged();
+
+ 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();
+ }
+
+
+ ///
+ /// 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; }
+ #endregion
+
+ #region Content
+ [CascadingParameter] ChipSet ChipSet { get; set; }
+
+ ///
+ /// 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; }
+
+ ///
+ /// 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; }
+ #endregion
+
+ #region Styling
protected string Classname =>
new CssBuilder("chip")
.AddClass($"chip-{GetVariant().ToDescription()}")
@@ -57,8 +147,6 @@ public partial class Chip : UIComponent, IDisposable
}
}
- [CascadingParameter] ChipSet ChipSet { get; set; }
-
///
/// The color of the component.
///
@@ -87,13 +175,6 @@ public partial class Chip : UIComponent, IDisposable
[Category(CategoryTypes.Chip.Appearance)]
public ThemeColor SelectedColor { get; set; } = ThemeColor.Inherit;
- ///
- /// Avatar Glyph, Overrides the regular Glyph if set.
- ///
- [Parameter]
- [Category(CategoryTypes.Chip.Behavior)]
- public string Avatar { get; set; }
-
///
/// Avatar CSS Class, appends to Chips default avatar classes.
///
@@ -108,20 +189,6 @@ public partial class Chip : UIComponent, IDisposable
[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 Glyph to use.
- ///
- [Parameter]
- [Category(CategoryTypes.Chip.Behavior)]
- public string Icon { get; set; }
-
///
/// Custom checked icon.
///
@@ -150,38 +217,51 @@ public partial class Chip : UIComponent, IDisposable
[Category(CategoryTypes.Chip.Appearance)]
public bool DisableRipple { get; set; }
+ #endregion
+
+ #region Behavior
+
///
- /// Child content of component.
+ /// Set by MudChipSet
///
- [Parameter]
- [Category(CategoryTypes.Chip.Behavior)]
- public RenderFragment ChildContent { get; set; }
+ public bool IsSelected
+ {
+ get => _isSelected;
+ set
+ {
+ if (_isSelected == value)
+ return;
+ _isSelected = value;
+ StateHasChanged();
+ }
+ }
///
- /// If set to a URL, clicking the button will open the referenced document. Use Target to specify where (Obsolete replaced by Href)
+ /// If false, this chip has not been seen before
///
- [Obsolete("Use Href Instead.", false)]
- [Parameter]
- [Category(CategoryTypes.Chip.ClickAction)]
- public string Link
+ public bool DefaultProcessed { get; set; }
+
+ ///
+ /// Set by MudChipSet
+ ///
+ public bool IsChecked
{
- get => Href;
- set => Href = value;
+ get => _isSelected && ChipSet?.Filter == true;
}
///
- /// If set to a URL, clicking the button will open the referenced document. Use Target to specify where
+ /// If true, force browser to redirect outside component router-space.
///
[Parameter]
[Category(CategoryTypes.Chip.ClickAction)]
- public string Href { get; set; }
+ public bool ForceLoad { get; set; }
///
- /// The target attribute specifies where to open the link, if Href is specified. Possible values: _blank | _self | _parent | _top | framename
+ /// If true, this chip is selected by default if used in a ChipSet.
///
[Parameter]
- [Category(CategoryTypes.Chip.ClickAction)]
- public string Target { get; set; }
+ [Category(CategoryTypes.Chip.Behavior)]
+ public bool? Default { get; set; }
///
/// A string you want to associate with the chip. If the ChildContent is not set this will be shown as chip text.
@@ -199,70 +279,51 @@ public partial class Chip : UIComponent, IDisposable
public object Value { get; set; }
///
- /// If true, force browser to redirect outside component router-space.
+ /// Avatar Glyph, Overrides the regular Glyph if set.
///
[Parameter]
- [Category(CategoryTypes.Chip.ClickAction)]
- public bool ForceLoad { get; set; }
+ [Category(CategoryTypes.Chip.Behavior)]
+ public string Avatar { get; set; }
+
+
///
- /// If true, this chip is selected by default if used in a ChipSet.
+ /// If true, the chip will be displayed in disabled state and no events possible.
///
[Parameter]
[Category(CategoryTypes.Chip.Behavior)]
- public bool? Default { get; set; }
+ public bool Disabled { get; set; }
///
- /// Command executed when the user clicks on an element.
+ /// Sets the Glyph to use.
///
[Parameter]
- [Category(CategoryTypes.Chip.ClickAction)]
- public ICommand Command { get; set; }
+ [Category(CategoryTypes.Chip.Behavior)]
+ public string Icon { 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.
+ /// Child content of component.
///
- [Parameter] public EventCallback OnClose { get; set; }
+ [Parameter]
+ [Category(CategoryTypes.Chip.Behavior)]
+ public RenderFragment ChildContent { get; set; }
///
- /// Set by MudChipSet
+ /// If set to a URL, clicking the button will open the referenced document. Use Target to specify where (Obsolete replaced by Href)
///
- public bool IsChecked
+ [Obsolete("Use Href Instead.", false)]
+ [Parameter]
+ [Category(CategoryTypes.Chip.ClickAction)]
+ public string Link
{
- get => _isSelected && ChipSet?.Filter == true;
+ get => Href;
+ set => Href = value;
}
+ #endregion
- ///
- /// 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();
- }
- }
+ #region Lifecycle
protected override void OnInitialized()
{
@@ -271,53 +332,12 @@ public partial class Chip : UIComponent, IDisposable
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()
@@ -331,5 +351,5 @@ public partial class Chip : UIComponent, IDisposable
/* ignore! */
}
}
-
+ #endregion
}
diff --git a/src/Connected.Components/Components/ChipSet/ChipSet.razor.cs b/src/Connected.Components/Components/ChipSet/ChipSet.razor.cs
index 6b5556f..32b112a 100644
--- a/src/Connected.Components/Components/ChipSet/ChipSet.razor.cs
+++ b/src/Connected.Components/Components/ChipSet/ChipSet.razor.cs
@@ -6,337 +6,344 @@ namespace Connected.Components;
public partial class ChipSet : UIComponent, IDisposable
{
+ #region Variables
+ private IEqualityComparer
[Parameter] public int? MaxHeight { get; set; }
- ///
- /// Child content of component.
- ///
- [Parameter] public RenderFragment ChildContent { get; set; }
-
- [Parameter] public EventCallback OnAnimationEnd { get; set; }
-
- [Parameter] public EventCallback ExpandedChanged { get; set; }
-
///
/// Modified Animation duration that scales with height parameter.
/// Basic implementation for now but should be a math formula to allow it to scale between 0.1s and 1s for the effect to be consistently smooth.
@@ -95,23 +138,9 @@ public partial class Collapse : UIComponent
set { }
}
- internal async Task UpdateHeight()
- {
- try
- {
- _height = (await _wrapper.MudGetBoundingClientRectAsync())?.Height ?? 0;
- }
- catch (Exception ex) when (ex is JSDisconnectedException || ex is TaskCanceledException)
- {
- _height = 0;
- }
-
- if (MaxHeight != null && _height > MaxHeight)
- {
- _height = MaxHeight.Value;
- }
- }
+ #endregion
+ #region Lifecycle
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
@@ -127,19 +156,6 @@ public partial class Collapse : UIComponent
}
await base.OnAfterRenderAsync(firstRender);
}
+ #endregion
- public void AnimationEnd()
- {
- if (_state == CollapseState.Entering)
- {
- _state = CollapseState.Entered;
- StateHasChanged();
- }
- else if (_state == CollapseState.Exiting)
- {
- _state = CollapseState.Exited;
- StateHasChanged();
- }
- OnAnimationEnd.InvokeAsync(Expanded);
- }
-}
+}
\ No newline at end of file
diff --git a/src/Connected.Components/Components/ColorPicker/ColorPicker.razor.cs b/src/Connected.Components/Components/ColorPicker/ColorPicker.razor.cs
index 2f4aae0..e3cac1e 100644
--- a/src/Connected.Components/Components/ColorPicker/ColorPicker.razor.cs
+++ b/src/Connected.Components/Components/ColorPicker/ColorPicker.razor.cs
@@ -13,585 +13,581 @@ namespace Connected.Components;
public partial class ColorPicker : Picker, IAsyncDisposable
{
- public ColorPicker() : base(new DefaultConverter())
- {
- AdornmentIcon = Icons.Material.Outlined.Palette;
- DisableToolbar = true;
- Value = "#594ae2"; //MudBlazor Blue
- Text = GetColorTextValue();
- AdornmentAriaLabel = "Open Color Picker";
- }
-
- #region Fields
-
- private static Dictionary r, Func g, Func b, string dominantColorPart)> _rgbToHueMapper = new()
- {
- { 0, ((x) => 255, x => x, x => 0, "rb") },
- { 1, ((x) => 255 - x, x => 255, x => 0, "gb") },
- { 2, ((x) => 0, x => 255, x => x, "gr") },
- { 3, ((x) => 0, x => 255 - x, x => 255, "br") },
- { 4, ((x) => x, x => 0, x => 255, "bg") },
- { 5, ((x) => 255, x => 0, x => 255 - x, "rg") },
- };
-
- private const double _maxY = 250;
- private const double _maxX = 312;
- private const double _selctorSize = 26.0;
-
- private double _selectorX;
- private double _selectorY;
- private bool _skipFeedback = false;
-
- private Color _baseColor;
- private Color _color;
-
- private bool _collectionOpen;
-
- private readonly Guid _id = Guid.NewGuid();
- private Guid _throttledMouseOverEventId;
-
- private IEventListener _throttledEventManager;
- [Inject] IEventListenerFactory ThrottledEventManagerFactory { get; set; }
-
- #endregion
-
- #region Parameters
-
- [CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
-
- private bool _disableAlpha = false;
-
- ///
- /// If true, Alpha options will not be displayed and color output will be RGB, HSL or HEX and not RGBA, HSLA or HEXA.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool DisableAlpha
- {
- get => _disableAlpha;
- set
- {
- if (value != _disableAlpha)
- {
- _disableAlpha = value;
-
- if (value == true)
- {
- Value = Value.SetAlpha(1.0);
- }
-
- Text = GetColorTextValue();
- }
- }
- }
-
- ///
- /// If true, the color field will not be displayed.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool DisableColorField { get; set; } = false;
-
- ///
- /// If true, the switch to change color mode will not be displayed.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool DisableModeSwitch { get; set; } = false;
-
- ///
- /// If true, textfield inputs and color mode switch will not be displayed.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool DisableInputs { get; set; } = false;
-
- ///
- /// If true, hue and alpha sliders will not be displayed.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool DisableSliders { get; set; } = false;
-
- ///
- /// If true, the preview color box will not be displayed, note that the preview color functions as a button as well for collection colors.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool DisablePreview { get; set; } = false;
-
- ///
- /// The initial mode (RGB, HSL or HEX) the picker should open. Defaults to RGB
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public ColorPickerMode ColorPickerMode { get; set; } = ColorPickerMode.RGB;
-
- private ColorPickerView _colorPickerView = ColorPickerView.Spectrum;
- private ColorPickerView _activeColorPickerView = ColorPickerView.Spectrum;
-
- ///
- /// The initial view of the picker. Views can be changed if toolbar is enabled.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public ColorPickerView ColorPickerView
- {
- get => _colorPickerView;
- set
- {
- if (value != _colorPickerView)
- {
- _colorPickerView = value;
- ChangeView(value).AndForget();
- }
- }
- }
-
- ///
- /// If true, binding changes occurred also when HSL values changed without a corresponding RGB change
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Behavior)]
- public bool UpdateBindingIfOnlyHSLChanged { get; set; } = false;
-
- ///
- /// A two-way bindable property representing the selected value. MudColor is a utility class that can be used to get the value as RGB, HSL, hex or other value
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Data)]
- public Color Value
- {
- get => _color;
- set
- {
- if (value == null) { return; }
-
- var rgbChanged = value != _color;
- var hslChanged = _color == null ? false : value.HslChanged(_color);
- _color = value;
-
- if (rgbChanged)
- {
- if (_skipFeedback == false)
- {
- UpdateBaseColor();
- UpdateColorSelectorBasedOnRgb();
- }
-
- SetTextAsync(GetColorTextValue(), false).AndForget();
- ValueChanged.InvokeAsync(value).AndForget();
- FieldChanged(value);
- }
-
- if (rgbChanged == false && UpdateBindingIfOnlyHSLChanged && hslChanged == true)
- {
- SetTextAsync(GetColorTextValue(), false).AndForget();
- ValueChanged.InvokeAsync(value).AndForget();
- FieldChanged(value);
- }
- }
- }
-
- [Parameter] public EventCallback ValueChanged { get; set; }
-
- ///
- /// MudColor list of predefined colors. The first five colors will show up as the quick colors on preview dot click.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public IEnumerable Palette { get; set; } = new Color[]
- { "#424242", "#2196f3", "#00c853", "#ff9800", "#f44336",
- "#f6f9fb", "#9df1fa", "#bdffcf", "#fff0a3", "#ffd254",
- "#e6e9eb", "#27dbf5", "#7ef7a0", "#ffe273", "#ffb31f",
- "#c9cccf", "#13b8e8", "#14dc71", "#fdd22f", "#ff9102",
- "#858791", "#0989c2", "#1bbd66", "#ebb323", "#fe6800",
- "#585b62", "#17698e", "#17a258", "#d9980d", "#dc3f11",
- "#353940", "#113b53", "#127942", "#bf7d11", "#aa0000"
- };
-
- private IEnumerable _gridList = new Color[]
- {
- "#FFFFFF","#ebebeb","#d6d6d6","#c2c2c2","#adadad","#999999","#858586","#707070","#5c5c5c","#474747","#333333","#000000",
- "#133648","#071d53","#0f0638","#2a093b","#370c1b","#541107","#532009","#53350d","#523e0f","#65611b","#505518","#2b3d16",
- "#1e4c63","#0f2e76","#180b4e","#3f1256","#4e1629","#781e0e","#722f10","#734c16","#73591a","#8c8629","#707625","#3f5623",
- "#2e6c8c","#1841a3","#280c72","#591e77","#6f223d","#a62c17","#a0451a","#a06b23","#9f7d28","#c3bc3c","#9da436","#587934",
- "#3c8ab0","#2155ce","#331c8e","#702898","#8d2e4f","#d03a20","#ca5a24","#c8862e","#c99f35","#f3ec4e","#c6d047","#729b44",
- "#479fd3","#2660f5","#4725ab","#8c33b5","#aa395d","#eb512e","#ed732e","#f3ae3d","#f5c944","#fefb67","#ddeb5c","#86b953",
- "#59c4f7","#4e85f6","#5733e2","#af43eb","#d44a7a","#ed6c59","#ef8c56","#f3b757","#f6cd5b","#fef881","#e6ee7a","#a3d16e",
- "#78d3f8","#7fa6f8","#7e52f5","#c45ff6","#de789d","#f09286","#f2a984","#f6c983","#f9da85","#fef9a1","#ebf29b","#badc94",
- "#a5e1fa","#adc5fa","#ab8df7","#d696f8","#e8a7bf","#f4b8b1","#f6c7af","#f9daae","#fae5af","#fefbc0","#f3f7be","#d2e7ba",
- "#d2effd","#d6e1fc","#d6c9fa","#e9cbfb","#f3d4df","#f9dcd9","#fae3d8","#fcecd7","#fdf2d8","#fefce0","#f7fade","#e3edd6"
- };
-
- ///
- /// When set to true, no mouse move events in the spectrum mode will be captured, so the selector circle won't fellow the mouse.
- /// Under some conditions like long latency the visual representation might not reflect the user behaviour anymore. So, it can be disabled
- /// Enabled by default
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool DisableDragEffect { get; set; } = false;
-
- ///
- /// Custom close icon.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerAppearance)]
- public string CloseIcon { get; set; } = Icons.Material.Filled.Close;
-
- ///
- /// Custom spectrum icon.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerAppearance)]
- public string SpectrumIcon { get; set; } = Icons.Material.Filled.Tune;
-
- ///
- /// Custom grid icon.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerAppearance)]
- public string GridIcon { get; set; } = Icons.Material.Filled.Apps;
-
- ///
- /// Custom palette icon.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerAppearance)]
- public string PaletteIcon { get; set; } = Icons.Material.Filled.Palette;
-
- ///
- /// Custom import/export icont.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerAppearance)]
- public string ImportExportIcon { get; set; } = Icons.Material.Filled.ImportExport;
-
- #endregion
-
- private void ToggleCollection()
- {
- _collectionOpen = !_collectionOpen;
- }
-
- private void SelectPaletteColor(Color color)
- {
- Value = color;
- _collectionOpen = false;
-
- if (
- IsAnyControlVisible() == false || _activeColorPickerView is ColorPickerView.GridCompact or ColorPickerView.Palette)
- {
- Close();
- }
- }
-
- public void ChangeMode() =>
- ColorPickerMode = ColorPickerMode switch
- {
- ColorPickerMode.RGB => ColorPickerMode.HSL,
- ColorPickerMode.HSL => ColorPickerMode.HEX,
- ColorPickerMode.HEX => ColorPickerMode.RGB,
- _ => ColorPickerMode.RGB,
- };
-
- public async Task ChangeView(ColorPickerView value)
- {
-
- var oldValue = _activeColorPickerView;
-
- _activeColorPickerView = value;
- Text = GetColorTextValue();
-
- if (oldValue == ColorPickerView.Spectrum)
- {
- await RemoveMouseOverEvent();
- }
-
- if (value == ColorPickerView.Spectrum)
- {
- _attachedMouseEvent = true;
- }
- }
-
- private void UpdateBaseColorSlider(int value)
- {
- var diff = Math.Abs(value - (int)Value.H);
- if (diff == 0) { return; }
-
- Value = Value.SetH(value);
- }
-
- private void UpdateBaseColor()
- {
- var index = (int)_color.H / 60;
- if (index == 6)
- {
- index = 5;
- }
-
- var valueInDeg = (int)_color.H - (index * 60);
- var value = (int)(MathExtensions.Map(0, 60, 0, 255, valueInDeg));
- var section = _rgbToHueMapper[index];
-
- _baseColor = new(section.r(value), section.g(value), section.b(value), 255);
- }
-
- private void UpdateColorBaseOnSelection()
- {
- var x = _selectorX / _maxX;
-
- var r_x = 255 - (int)((255 - _baseColor.R) * x);
- var g_x = 255 - (int)((255 - _baseColor.G) * x);
- var b_x = 255 - (int)((255 - _baseColor.B) * x);
-
- var y = 1.0 - _selectorY / _maxY;
-
- var r = r_x * y;
- var g = g_x * y;
- var b = b_x * y;
-
- _skipFeedback = true;
- //in this mode, H is expected to be stable, so copy H value
- Value = new Color((byte)r, (byte)g, (byte)b, _color);
- _skipFeedback = false;
- }
-
- private void UpdateColorSelectorBasedOnRgb()
- {
- var hueValue = (int)MathExtensions.Map(0, 360, 0, 6 * 255, _color.H);
- var index = hueValue / 255;
- if (index == 6)
- {
- index = 5;
- }
-
- var section = _rgbToHueMapper[index];
-
- var colorValues = section.dominantColorPart switch
- {
- "rb" => (_color.R, _color.B),
- "rg" => (_color.R, _color.G),
- "gb" => (_color.G, _color.B),
- "gr" => (_color.G, _color.R),
- "br" => (_color.B, _color.R),
- "bg" => (_color.B, _color.G),
- _ => (255, 255)
- };
-
- var primaryDiff = 255 - colorValues.Item1;
- var primaryDiffDelta = colorValues.Item1 / 255.0;
-
- _selectorY = MathExtensions.Map(0, 255, 0, _maxY, primaryDiff);
-
- var secondaryColorX = colorValues.Item2 * (1.0 / primaryDiffDelta);
- var relation = (255 - secondaryColorX) / 255.0;
-
- _selectorX = relation * _maxX;
- }
-
- #region mouse interactions
-
- private void HandleColorOverlayClicked()
- {
- UpdateColorBaseOnSelection();
-
- if (IsAnyControlVisible() == false)
- {
- Close();
- }
- }
-
- private void OnSelectorClicked(MouseEventArgs e)
- {
- SetSelectorBasedOnMouseEvents(e, false);
- HandleColorOverlayClicked();
- }
-
- private void OnColorOverlayClick(MouseEventArgs e)
- {
- SetSelectorBasedOnMouseEvents(e, true);
- HandleColorOverlayClicked();
- }
-
- private void OnMouseOver(MouseEventArgs e)
- {
- if (e.Buttons == 1)
- {
- SetSelectorBasedOnMouseEvents(e, true);
- UpdateColorBaseOnSelection();
- }
- }
-
- private void SetSelectorBasedOnMouseEvents(MouseEventArgs e, bool offsetIsAbsolute)
- {
- _selectorX = (offsetIsAbsolute == true ? e.OffsetX : (e.OffsetX - _selctorSize / 2.0) + _selectorX).EnsureRange(_maxX);
- _selectorY = (offsetIsAbsolute == true ? e.OffsetY : (e.OffsetY - _selctorSize / 2.0) + _selectorY).EnsureRange(_maxY);
- }
-
- #endregion
-
- #region updating inputs
-
- ///
- /// Set the R (red) component of the color picker
- ///
- /// A value between 0 (no red) or 255 (max red)
- public void SetR(int value) => Value = Value.SetR(value);
-
- ///
- /// Set the G (green) component of the color picker
- ///
- /// A value between 0 (no green) or 255 (max green)
- public void SetG(int value) => Value = Value.SetG(value);
-
- ///
- /// Set the B (blue) component of the color picker
- ///
- /// A value between 0 (no blue) or 255 (max blue)
- public void SetB(int value) => Value = Value.SetB(value);
-
- ///
- /// Set the H (hue) component of the color picker
- ///
- /// A value between 0 and 360 (degrees)
- public void SetH(double value) => Value = Value.SetH(value);
-
- ///
- /// Set the S (saturation) component of the color picker
- ///
- /// A value between 0.0 (no saturation) and 1.0 (max saturation)
- public void SetS(double value) => Value = Value.SetS(value);
-
- ///
- /// Set the L (Lightness) component of the color picker
- ///
- /// A value between 0.0 (no light, black) and 1.0 (max light, white)
- public void SetL(double value) => Value = Value.SetL(value);
-
- ///
- /// Set the Alpha (transparency) component of the color picker
- ///
- /// A value between 0.0 (full transparent) and 1.0 (solid)
- public void SetAlpha(double value) => Value = Value.SetAlpha(value);
-
- ///
- /// Set the Alpha (transparency) component of the color picker
- ///
- /// A value between 0 (full transparent) and 1 (solid)
- public void SetAlpha(int value) => Value = Value.SetAlpha(value);
-
- ///
- /// Set the color of the picker based on the string input
- ///
- /// Accepting different formats for a color representation such as rbg, rgba, #
- public void SetInputString(string input)
- {
- Color color;
- try
- {
- color = new Color(input);
- }
- catch (Exception)
- {
- return;
- }
-
- Value = color;
- }
-
- protected override Task StringValueChanged(string value)
- {
- SetInputString(value);
- return Task.CompletedTask;
- }
-
- private bool _attachedMouseEvent = false;
-
- protected override void OnPickerOpened()
- {
- base.OnPickerOpened();
- _attachedMouseEvent = true;
- StateHasChanged();
- }
-
- protected override void OnPickerClosed()
- {
- base.OnPickerClosed();
- RemoveMouseOverEvent().AndForget();
- }
-
- #endregion
-
- #region helper
-
- private string GetSelectorLocation() => $"translate({Math.Round(_selectorX, 2).ToString(CultureInfo.InvariantCulture)}px, {Math.Round(_selectorY, 2).ToString(CultureInfo.InvariantCulture)}px);";
- private string GetColorTextValue() => (DisableAlpha == true || _activeColorPickerView is ColorPickerView.Palette or ColorPickerView.GridCompact) ? _color.ToString(ColorOutputFormats.Hex) : _color.ToString(ColorOutputFormats.HexA);
- private int GetHexColorInputMaxLength() => DisableAlpha ? 7 : 9;
-
- private EventCallback GetEventCallback() => EventCallback.Factory.Create(this, () => Close());
- private bool IsAnyControlVisible() => !(DisablePreview && DisableSliders && DisableInputs);
- private EventCallback GetSelectPaletteColorCallback(Color color) => new EventCallbackFactory().Create(this, (MouseEventArgs e) => SelectPaletteColor(color));
-
- private ThemeColor GetButtonColor(ColorPickerView view) => _activeColorPickerView == view ? ThemeColor.Primary : ThemeColor.Inherit;
- private string GetColorDotClass(Color color) => new CssBuilder("picker-color-dot").AddClass("selected", color == Value).ToString();
- private string AlphaSliderStyle => new StyleBuilder().AddStyle($"background-image: linear-gradient(to {(RightToLeft ? "left" : "right")}, transparent, {_color.ToString(ColorOutputFormats.RGB)})").Build();
-
- #endregion
-
- #region life cycle hooks
-
- protected override async Task OnAfterRenderAsync(bool firstRender)
- {
- await base.OnAfterRenderAsync(firstRender);
-
- if (firstRender == true)
- {
- if (PickerVariant == PickerVariant.Static)
- {
- await AddMouseOverEvent();
- }
- }
-
- if (_attachedMouseEvent == true)
- {
- _attachedMouseEvent = false;
- await AddMouseOverEvent();
- }
- }
-
- private async Task AddMouseOverEvent()
- {
- if (DisableDragEffect == true) { return; }
-
- if (_throttledEventManager == null)
- {
- _throttledEventManager = ThrottledEventManagerFactory.Create();
- }
-
- _throttledMouseOverEventId = await
- _throttledEventManager.Subscribe("mousemove", _id.ToString(), "mudEventProjections.correctOffset", 10, async (x) =>
- {
- var e = x as MouseEventArgs;
- await InvokeAsync(() => OnMouseOver(e));
- StateHasChanged();
- });
- }
-
- private Task RemoveMouseOverEvent()
- {
- if (_throttledMouseOverEventId == default) { return Task.CompletedTask; }
-
- return _throttledEventManager.Unsubscribe(_throttledMouseOverEventId);
- }
-
- public async ValueTask DisposeAsync()
- {
- if (_throttledEventManager == null) { return; }
-
- await _throttledEventManager.DisposeAsync();
- }
-
- #endregion
+ #region Variables
+
+ private static Dictionary r, Func g, Func b, string dominantColorPart)> _rgbToHueMapper = new()
+ {
+ { 0, ((x) => 255, x => x, x => 0, "rb") },
+ { 1, ((x) => 255 - x, x => 255, x => 0, "gb") },
+ { 2, ((x) => 0, x => 255, x => x, "gr") },
+ { 3, ((x) => 0, x => 255 - x, x => 255, "br") },
+ { 4, ((x) => x, x => 0, x => 255, "bg") },
+ { 5, ((x) => 255, x => 0, x => 255 - x, "rg") },
+ };
+
+ private const double _maxY = 250;
+ private const double _maxX = 312;
+ private const double _selctorSize = 26.0;
+
+ private double _selectorX;
+ private double _selectorY;
+ private bool _skipFeedback = false;
+
+ private Color _baseColor;
+ private Color _color;
+
+ private bool _collectionOpen;
+
+ private readonly Guid _id = Guid.NewGuid();
+ private Guid _throttledMouseOverEventId;
+
+ private ColorPickerView _colorPickerView = ColorPickerView.Spectrum;
+ private ColorPickerView _activeColorPickerView = ColorPickerView.Spectrum;
+
+ private bool _disableAlpha = false;
+
+ private IEventListener _throttledEventManager;
+
+ ///
+ /// MudColor list of predefined colors. The first five colors will show up as the quick colors on preview dot click.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public IEnumerable Palette { get; set; } = new Color[]
+ { "#424242", "#2196f3", "#00c853", "#ff9800", "#f44336",
+ "#f6f9fb", "#9df1fa", "#bdffcf", "#fff0a3", "#ffd254",
+ "#e6e9eb", "#27dbf5", "#7ef7a0", "#ffe273", "#ffb31f",
+ "#c9cccf", "#13b8e8", "#14dc71", "#fdd22f", "#ff9102",
+ "#858791", "#0989c2", "#1bbd66", "#ebb323", "#fe6800",
+ "#585b62", "#17698e", "#17a258", "#d9980d", "#dc3f11",
+ "#353940", "#113b53", "#127942", "#bf7d11", "#aa0000"
+ };
+
+ private IEnumerable _gridList = new Color[]
+ {
+ "#FFFFFF","#ebebeb","#d6d6d6","#c2c2c2","#adadad","#999999","#858586","#707070","#5c5c5c","#474747","#333333","#000000",
+ "#133648","#071d53","#0f0638","#2a093b","#370c1b","#541107","#532009","#53350d","#523e0f","#65611b","#505518","#2b3d16",
+ "#1e4c63","#0f2e76","#180b4e","#3f1256","#4e1629","#781e0e","#722f10","#734c16","#73591a","#8c8629","#707625","#3f5623",
+ "#2e6c8c","#1841a3","#280c72","#591e77","#6f223d","#a62c17","#a0451a","#a06b23","#9f7d28","#c3bc3c","#9da436","#587934",
+ "#3c8ab0","#2155ce","#331c8e","#702898","#8d2e4f","#d03a20","#ca5a24","#c8862e","#c99f35","#f3ec4e","#c6d047","#729b44",
+ "#479fd3","#2660f5","#4725ab","#8c33b5","#aa395d","#eb512e","#ed732e","#f3ae3d","#f5c944","#fefb67","#ddeb5c","#86b953",
+ "#59c4f7","#4e85f6","#5733e2","#af43eb","#d44a7a","#ed6c59","#ef8c56","#f3b757","#f6cd5b","#fef881","#e6ee7a","#a3d16e",
+ "#78d3f8","#7fa6f8","#7e52f5","#c45ff6","#de789d","#f09286","#f2a984","#f6c983","#f9da85","#fef9a1","#ebf29b","#badc94",
+ "#a5e1fa","#adc5fa","#ab8df7","#d696f8","#e8a7bf","#f4b8b1","#f6c7af","#f9daae","#fae5af","#fefbc0","#f3f7be","#d2e7ba",
+ "#d2effd","#d6e1fc","#d6c9fa","#e9cbfb","#f3d4df","#f9dcd9","#fae3d8","#fcecd7","#fdf2d8","#fefce0","#f7fade","#e3edd6"
+ };
+
+ [Inject] IEventListenerFactory ThrottledEventManagerFactory { get; set; }
+
+ #endregion
+
+ #region Events
+ private EventCallback GetEventCallback() => EventCallback.Factory.Create(this, () => Close());
+ private EventCallback GetSelectPaletteColorCallback(Color color) => new EventCallbackFactory().Create(this, (MouseEventArgs e) => SelectPaletteColor(color));
+
+ ///
+ /// Set the R (red) component of the color picker
+ ///
+ /// A value between 0 (no red) or 255 (max red)
+ public void SetR(int value) => Value = Value.SetR(value);
+
+ ///
+ /// Set the G (green) component of the color picker
+ ///
+ /// A value between 0 (no green) or 255 (max green)
+ public void SetG(int value) => Value = Value.SetG(value);
+
+ ///
+ /// Set the B (blue) component of the color picker
+ ///
+ /// A value between 0 (no blue) or 255 (max blue)
+ public void SetB(int value) => Value = Value.SetB(value);
+
+ ///
+ /// Set the H (hue) component of the color picker
+ ///
+ /// A value between 0 and 360 (degrees)
+ public void SetH(double value) => Value = Value.SetH(value);
+
+ ///
+ /// Set the S (saturation) component of the color picker
+ ///
+ /// A value between 0.0 (no saturation) and 1.0 (max saturation)
+ public void SetS(double value) => Value = Value.SetS(value);
+
+ ///
+ /// Set the L (Lightness) component of the color picker
+ ///
+ /// A value between 0.0 (no light, black) and 1.0 (max light, white)
+ public void SetL(double value) => Value = Value.SetL(value);
+
+ ///
+ /// Set the Alpha (transparency) component of the color picker
+ ///
+ /// A value between 0.0 (full transparent) and 1.0 (solid)
+ public void SetAlpha(double value) => Value = Value.SetAlpha(value);
+
+ ///
+ /// Set the Alpha (transparency) component of the color picker
+ ///
+ /// A value between 0 (full transparent) and 1 (solid)
+ public void SetAlpha(int value) => Value = Value.SetAlpha(value);
+
+ ///
+ /// Set the color of the picker based on the string input
+ ///
+ /// Accepting different formats for a color representation such as rbg, rgba, #
+ public void SetInputString(string input)
+ {
+ Color color;
+ try
+ {
+ color = new Color(input);
+ }
+ catch (Exception)
+ {
+ return;
+ }
+
+ Value = color;
+ }
+
+ protected override Task StringValueChanged(string value)
+ {
+ SetInputString(value);
+ return Task.CompletedTask;
+ }
+
+ private bool _attachedMouseEvent = false;
+
+ protected override void OnPickerOpened()
+ {
+ base.OnPickerOpened();
+ _attachedMouseEvent = true;
+ StateHasChanged();
+ }
+
+ protected override void OnPickerClosed()
+ {
+ base.OnPickerClosed();
+ RemoveMouseOverEvent().AndForget();
+ }
+ private void HandleColorOverlayClicked()
+ {
+ UpdateColorBaseOnSelection();
+
+ if (IsAnyControlVisible() == false)
+ {
+ Close();
+ }
+ }
+
+ private void OnSelectorClicked(MouseEventArgs e)
+ {
+ SetSelectorBasedOnMouseEvents(e, false);
+ HandleColorOverlayClicked();
+ }
+
+ private void OnColorOverlayClick(MouseEventArgs e)
+ {
+ SetSelectorBasedOnMouseEvents(e, true);
+ HandleColorOverlayClicked();
+ }
+
+ private void OnMouseOver(MouseEventArgs e)
+ {
+ if (e.Buttons == 1)
+ {
+ SetSelectorBasedOnMouseEvents(e, true);
+ UpdateColorBaseOnSelection();
+ }
+ }
+
+ private void SetSelectorBasedOnMouseEvents(MouseEventArgs e, bool offsetIsAbsolute)
+ {
+ _selectorX = (offsetIsAbsolute == true ? e.OffsetX : (e.OffsetX - _selctorSize / 2.0) + _selectorX).EnsureRange(_maxX);
+ _selectorY = (offsetIsAbsolute == true ? e.OffsetY : (e.OffsetY - _selctorSize / 2.0) + _selectorY).EnsureRange(_maxY);
+ }
+
+ [Parameter] public EventCallback ValueChanged { get; set; }
+
+ private void ToggleCollection()
+ {
+ _collectionOpen = !_collectionOpen;
+ }
+
+ private void SelectPaletteColor(Color color)
+ {
+ Value = color;
+ _collectionOpen = false;
+
+ if (
+ IsAnyControlVisible() == false || _activeColorPickerView is ColorPickerView.GridCompact or ColorPickerView.Palette)
+ {
+ Close();
+ }
+ }
+
+ public void ChangeMode() =>
+ ColorPickerMode = ColorPickerMode switch
+ {
+ ColorPickerMode.RGB => ColorPickerMode.HSL,
+ ColorPickerMode.HSL => ColorPickerMode.HEX,
+ ColorPickerMode.HEX => ColorPickerMode.RGB,
+ _ => ColorPickerMode.RGB,
+ };
+
+ public async Task ChangeView(ColorPickerView value)
+ {
+
+ var oldValue = _activeColorPickerView;
+
+ _activeColorPickerView = value;
+ Text = GetColorTextValue();
+
+ if (oldValue == ColorPickerView.Spectrum)
+ {
+ await RemoveMouseOverEvent();
+ }
+
+ if (value == ColorPickerView.Spectrum)
+ {
+ _attachedMouseEvent = true;
+ }
+ }
+
+ private void UpdateBaseColorSlider(int value)
+ {
+ var diff = Math.Abs(value - (int)Value.H);
+ if (diff == 0) { return; }
+
+ Value = Value.SetH(value);
+ }
+
+ private void UpdateBaseColor()
+ {
+ var index = (int)_color.H / 60;
+ if (index == 6)
+ {
+ index = 5;
+ }
+
+ var valueInDeg = (int)_color.H - (index * 60);
+ var value = (int)(MathExtensions.Map(0, 60, 0, 255, valueInDeg));
+ var section = _rgbToHueMapper[index];
+
+ _baseColor = new(section.r(value), section.g(value), section.b(value), 255);
+ }
+
+ private void UpdateColorBaseOnSelection()
+ {
+ var x = _selectorX / _maxX;
+
+ var r_x = 255 - (int)((255 - _baseColor.R) * x);
+ var g_x = 255 - (int)((255 - _baseColor.G) * x);
+ var b_x = 255 - (int)((255 - _baseColor.B) * x);
+
+ var y = 1.0 - _selectorY / _maxY;
+
+ var r = r_x * y;
+ var g = g_x * y;
+ var b = b_x * y;
+
+ _skipFeedback = true;
+ //in this mode, H is expected to be stable, so copy H value
+ Value = new Color((byte)r, (byte)g, (byte)b, _color);
+ _skipFeedback = false;
+ }
+
+ private void UpdateColorSelectorBasedOnRgb()
+ {
+ var hueValue = (int)MathExtensions.Map(0, 360, 0, 6 * 255, _color.H);
+ var index = hueValue / 255;
+ if (index == 6)
+ {
+ index = 5;
+ }
+
+ var section = _rgbToHueMapper[index];
+
+ var colorValues = section.dominantColorPart switch
+ {
+ "rb" => (_color.R, _color.B),
+ "rg" => (_color.R, _color.G),
+ "gb" => (_color.G, _color.B),
+ "gr" => (_color.G, _color.R),
+ "br" => (_color.B, _color.R),
+ "bg" => (_color.B, _color.G),
+ _ => (255, 255)
+ };
+
+ var primaryDiff = 255 - colorValues.Item1;
+ var primaryDiffDelta = colorValues.Item1 / 255.0;
+
+ _selectorY = MathExtensions.Map(0, 255, 0, _maxY, primaryDiff);
+
+ var secondaryColorX = colorValues.Item2 * (1.0 / primaryDiffDelta);
+ var relation = (255 - secondaryColorX) / 255.0;
+
+ _selectorX = relation * _maxX;
+ }
+
+ #endregion
+
+ #region Styling
+
+ private bool IsAnyControlVisible() => !(DisablePreview && DisableSliders && DisableInputs);
+ private ThemeColor GetButtonColor(ColorPickerView view) => _activeColorPickerView == view ? ThemeColor.Primary : ThemeColor.Inherit;
+ private string GetColorDotClass(Color color) => new CssBuilder("picker-color-dot").AddClass("selected", color == Value).ToString();
+ private string AlphaSliderStyle => new StyleBuilder().AddStyle($"background-image: linear-gradient(to {(RightToLeft ? "left" : "right")}, transparent, {_color.ToString(ColorOutputFormats.RGB)})").Build();
+ private string GetSelectorLocation() => $"translate({Math.Round(_selectorX, 2).ToString(CultureInfo.InvariantCulture)}px, {Math.Round(_selectorY, 2).ToString(CultureInfo.InvariantCulture)}px);";
+ private string GetColorTextValue() => (DisableAlpha == true || _activeColorPickerView is ColorPickerView.Palette or ColorPickerView.GridCompact) ? _color.ToString(ColorOutputFormats.Hex) : _color.ToString(ColorOutputFormats.HexA);
+ private int GetHexColorInputMaxLength() => DisableAlpha ? 7 : 9;
+
+ ///
+ /// When set to true, no mouse move events in the spectrum mode will be captured, so the selector circle won't fellow the mouse.
+ /// Under some conditions like long latency the visual representation might not reflect the user behaviour anymore. So, it can be disabled
+ /// Enabled by default
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool DisableDragEffect { get; set; } = false;
+
+ ///
+ /// Custom close icon.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerAppearance)]
+ public string CloseIcon { get; set; } = Icons.Material.Filled.Close;
+
+ ///
+ /// Custom spectrum icon.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerAppearance)]
+ public string SpectrumIcon { get; set; } = Icons.Material.Filled.Tune;
+
+ ///
+ /// Custom grid icon.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerAppearance)]
+ public string GridIcon { get; set; } = Icons.Material.Filled.Apps;
+
+ ///
+ /// Custom palette icon.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerAppearance)]
+ public string PaletteIcon { get; set; } = Icons.Material.Filled.Palette;
+
+ ///
+ /// Custom import/export icont.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerAppearance)]
+ public string ImportExportIcon { get; set; } = Icons.Material.Filled.ImportExport;
+ #endregion
+
+ #region Behavior
+ [CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
+
+ ///
+ /// If true, Alpha options will not be displayed and color output will be RGB, HSL or HEX and not RGBA, HSLA or HEXA.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool DisableAlpha
+ {
+ get => _disableAlpha;
+ set
+ {
+ if (value != _disableAlpha)
+ {
+ _disableAlpha = value;
+
+ if (value == true)
+ {
+ Value = Value.SetAlpha(1.0);
+ }
+
+ Text = GetColorTextValue();
+ }
+ }
+ }
+
+ ///
+ /// If true, the color field will not be displayed.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool DisableColorField { get; set; } = false;
+
+ ///
+ /// If true, the switch to change color mode will not be displayed.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool DisableModeSwitch { get; set; } = false;
+
+ ///
+ /// If true, textfield inputs and color mode switch will not be displayed.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool DisableInputs { get; set; } = false;
+
+ ///
+ /// If true, hue and alpha sliders will not be displayed.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool DisableSliders { get; set; } = false;
+
+ ///
+ /// If true, the preview color box will not be displayed, note that the preview color functions as a button as well for collection colors.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool DisablePreview { get; set; } = false;
+
+ ///
+ /// The initial mode (RGB, HSL or HEX) the picker should open. Defaults to RGB
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public ColorPickerMode ColorPickerMode { get; set; } = ColorPickerMode.RGB;
+
+
+
+ ///
+ /// The initial view of the picker. Views can be changed if toolbar is enabled.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public ColorPickerView ColorPickerView
+ {
+ get => _colorPickerView;
+ set
+ {
+ if (value != _colorPickerView)
+ {
+ _colorPickerView = value;
+ ChangeView(value).AndForget();
+ }
+ }
+ }
+
+ ///
+ /// If true, binding changes occurred also when HSL values changed without a corresponding RGB change
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Behavior)]
+ public bool UpdateBindingIfOnlyHSLChanged { get; set; } = false;
+
+ ///
+ /// A two-way bindable property representing the selected value. MudColor is a utility class that can be used to get the value as RGB, HSL, hex or other value
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Data)]
+ public Color Value
+ {
+ get => _color;
+ set
+ {
+ if (value == null) { return; }
+
+ var rgbChanged = value != _color;
+ var hslChanged = _color == null ? false : value.HslChanged(_color);
+ _color = value;
+
+ if (rgbChanged)
+ {
+ if (_skipFeedback == false)
+ {
+ UpdateBaseColor();
+ UpdateColorSelectorBasedOnRgb();
+ }
+
+ SetTextAsync(GetColorTextValue(), false).AndForget();
+ ValueChanged.InvokeAsync(value).AndForget();
+ FieldChanged(value);
+ }
+
+ if (rgbChanged == false && UpdateBindingIfOnlyHSLChanged && hslChanged == true)
+ {
+ SetTextAsync(GetColorTextValue(), false).AndForget();
+ ValueChanged.InvokeAsync(value).AndForget();
+ FieldChanged(value);
+ }
+ }
+ }
+
+
+ #endregion
+
+ #region Lifecycle
+
+ public ColorPicker() : base(new DefaultConverter())
+ {
+ AdornmentIcon = Icons.Material.Outlined.Palette;
+ DisableToolbar = true;
+ Value = "#594ae2"; //MudBlazor Blue
+ Text = GetColorTextValue();
+ AdornmentAriaLabel = "Open Color Picker";
+ }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ await base.OnAfterRenderAsync(firstRender);
+
+ if (firstRender == true)
+ {
+ if (PickerVariant == PickerVariant.Static)
+ {
+ await AddMouseOverEvent();
+ }
+ }
+
+ if (_attachedMouseEvent == true)
+ {
+ _attachedMouseEvent = false;
+ await AddMouseOverEvent();
+ }
+ }
+
+ private async Task AddMouseOverEvent()
+ {
+ if (DisableDragEffect == true) { return; }
+
+ if (_throttledEventManager == null)
+ {
+ _throttledEventManager = ThrottledEventManagerFactory.Create();
+ }
+
+ _throttledMouseOverEventId = await
+ _throttledEventManager.Subscribe("mousemove", _id.ToString(), "mudEventProjections.correctOffset", 10, async (x) =>
+ {
+ var e = x as MouseEventArgs;
+ await InvokeAsync(() => OnMouseOver(e));
+ StateHasChanged();
+ });
+ }
+
+ private Task RemoveMouseOverEvent()
+ {
+ if (_throttledMouseOverEventId == default) { return Task.CompletedTask; }
+
+ return _throttledEventManager.Unsubscribe(_throttledMouseOverEventId);
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ if (_throttledEventManager == null) { return; }
+
+ await _throttledEventManager.DisposeAsync();
+ }
+
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Container/Container.razor.cs b/src/Connected.Components/Components/Container/Container.razor.cs
index f1746b3..fcdfaf4 100644
--- a/src/Connected.Components/Components/Container/Container.razor.cs
+++ b/src/Connected.Components/Components/Container/Container.razor.cs
@@ -5,9 +5,6 @@ namespace Connected.Components;
public partial class Container : UIComponent
{
- #region Event callbacks
- #endregion
-
#region Content placeholders
///
diff --git a/src/Connected.Components/Components/DatePicker/DatePickerBase.razor.cs b/src/Connected.Components/Components/DatePicker/DatePickerBase.razor.cs
index 8062746..9bb9cea 100644
--- a/src/Connected.Components/Components/DatePicker/DatePickerBase.razor.cs
+++ b/src/Connected.Components/Components/DatePicker/DatePickerBase.razor.cs
@@ -8,223 +8,100 @@ namespace Connected.Components;
public abstract partial class DatePickerBase : Picker
{
+ #region Variables
private bool _dateFormatTouched;
-
- protected DatePickerBase() : base(new DefaultConverter
- {
- Format = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern,
- Culture = CultureInfo.CurrentCulture
- })
- {
- AdornmentAriaLabel = "Open Date Picker";
- }
-
- [Inject] protected IScrollManager ScrollManager { get; set; }
-
+ private DateTime? _picker_month;
+ protected virtual bool IsRange { get; } = false;
+ protected OpenTo CurrentView;
///
- /// Max selectable date.
+ /// We need a random id for the year items in the year list so we can scroll to the item safely in every DatePicker.
///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Validation)]
- public DateTime? MaxDate { get; set; }
+ private string _componentId = Guid.NewGuid().ToString();
///
- /// Min selectable date.
+ /// Is set to true to scroll to the actual year after the next render
///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Validation)]
- public DateTime? MinDate { get; set; }
+ private bool _scrollToYearAfterRender = false;
+ #endregion
- ///
- /// First view to show in the MudDatePicker.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public OpenTo OpenTo { get; set; } = OpenTo.Date;
+ #region Events
+ private Typo GetMonthTypo(DateTime month)
+ {
+ if (GetMonthStart(0) == month)
+ return Typo.h5;
+ return Typo.subtitle1;
+ }
- ///
- /// String Format for selected date view
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Behavior)]
- public string DateFormat
+ protected abstract DateTime GetCalendarStartOfMonth();
+
+ private int GetCalendarDayOfMonth(DateTime date)
{
- get
- {
- return (Converter as DefaultConverter)?.Format;
- }
- set
- {
- if (Converter is DefaultConverter defaultConverter)
- {
- defaultConverter.Format = value;
- _dateFormatTouched = true;
- }
- DateFormatChanged(value);
- }
+ return Culture.Calendar.GetDayOfMonth(date);
}
///
- /// Date format value change hook for descendants.
+ /// Converts gregorian year into whatever year it is in the provided culture
///
- protected virtual Task DateFormatChanged(string newFormat)
+ /// Gregorian year
+ /// Year according to culture
+ protected abstract int GetCalendarYear(int year);
+ public async void ScrollToYear()
{
- return Task.CompletedTask;
+ _scrollToYearAfterRender = false;
+ var id = $"{_componentId}{GetMonthStart(0).Year}";
+ await ScrollManager.ScrollToYearAsync(id);
+ StateHasChanged();
}
- protected override bool SetCulture(CultureInfo value)
+ private int GetMinYear()
{
- if (!base.SetCulture(value))
- return false;
-
- if (!_dateFormatTouched && Converter is DefaultConverter defaultConverter)
- defaultConverter.Format = value.DateTimeFormat.ShortDatePattern;
-
- return true;
+ if (MinDate.HasValue)
+ return MinDate.Value.Year;
+ return DateTime.Today.Year - 100;
}
- ///
- /// Defines on which day the week starts. Depends on the value of Culture.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public DayOfWeek? FirstDayOfWeek { get; set; } = null;
-
- ///
- /// The current month of the date picker (two-way bindable). This changes when the user browses through the calender.
- /// The month is represented as a DateTime which is always the first day of that month. You can also set this to define which month is initially shown. If not set, the current month is shown.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public DateTime? PickerMonth
+ private int GetMaxYear()
{
- get => _picker_month;
- set
- {
- if (value == _picker_month)
- return;
- _picker_month = value;
- InvokeAsync(StateHasChanged);
- PickerMonthChanged.InvokeAsync(value);
- }
+ if (MaxDate.HasValue)
+ return MaxDate.Value.Year;
+ return DateTime.Today.Year + 100;
}
- private DateTime? _picker_month;
- ///
- /// Fired when the date changes.
- ///
- [Parameter] public EventCallback PickerMonthChanged { get; set; }
- ///
- /// Sets the amount of time in milliseconds to wait before closing the picker. This helps the user see that the date was selected before the popover disappears.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public int ClosingDelay { get; set; } = 100;
-
- ///
- /// Number of months to display in the calendar
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public int DisplayMonths { get; set; } = 1;
-
- ///
- /// Maximum number of months in one row
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerAppearance)]
- public int? MaxMonthColumns { get; set; }
-
- ///
- /// Start month when opening the picker.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public DateTime? StartMonth { get; set; }
-
- ///
- /// Display week numbers according to the Culture parameter. If no culture is defined, CultureInfo.CurrentCulture will be used.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool ShowWeekNumbers { get; set; }
-
- ///
- /// Format of the selected date in the title. By default, this is "ddd, dd MMM" which abbreviates day and month names.
- /// For instance, display the long names like this "dddd, dd. MMMM".
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public string TitleDateFormat { get; set; } = "ddd, dd MMM";
-
- ///
- /// If AutoClose is set to true and PickerActions are defined, selecting a day will close the MudDatePicker.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public bool AutoClose { get; set; }
-
- ///
- /// Function to determine whether a date is disabled
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Validation)]
- public Func IsDateDisabledFunc
+ private Typo GetYearTypo(int year)
{
- get => _isDateDisabledFunc;
- set
- {
- _isDateDisabledFunc = value ?? (_ => false);
- }
+ if (year == GetMonthStart(0).Year)
+ return Typo.h5;
+ return Typo.subtitle1;
}
- private Func _isDateDisabledFunc = _ => false;
- ///
- /// Function to conditionally apply new classes to specific days
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Appearance)]
- public Func AdditionalDateClassesFunc { get; set; }
-
- ///
- /// Custom previous icon.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerAppearance)]
- public string PreviousIcon { get; set; } = Icons.Material.Filled.ChevronLeft;
-
- ///
- /// Custom next icon.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerAppearance)]
- public string NextIcon { get; set; } = Icons.Material.Filled.ChevronRight;
+ private void OnFormattedDateClick()
+ {
+ // todo: raise an event the user can handle
+ }
- ///
- /// Set a predefined fix year - no year can be selected
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public int? FixYear { get; set; }
- ///
- /// Set a predefined fix month - no month can be selected
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public int? FixMonth { get; set; }
- ///
- /// Set a predefined fix day - no day can be selected
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.PickerBehavior)]
- public int? FixDay { get; set; }
- protected virtual bool IsRange { get; } = false;
+ private IEnumerable GetAllMonths()
+ {
+ var current = GetMonthStart(0);
+ var calendarYear = Culture.Calendar.GetYear(current);
+ var firstOfCalendarYear = Culture.Calendar.ToDateTime(calendarYear, 1, 1, 0, 0, 0, 0);
+ for (var i = 0; i < Culture.Calendar.GetMonthsInYear(calendarYear); i++)
+ yield return Culture.Calendar.AddMonths(firstOfCalendarYear, i);
+ }
- protected OpenTo CurrentView;
+ private string GetAbbreviatedMonthName(DateTime month)
+ {
+ var calendarMonth = Culture.Calendar.GetMonth(month);
+ return Culture.DateTimeFormat.AbbreviatedMonthNames[calendarMonth - 1];
+ }
+ private string GetMonthName(DateTime month)
+ {
+ var calendarMonth = Culture.Calendar.GetMonth(month);
+ return Culture.DateTimeFormat.MonthNames[calendarMonth - 1];
+ }
protected override void OnPickerOpened()
{
base.OnPickerOpened();
@@ -461,39 +338,73 @@ public abstract partial class DatePickerBase : Picker
_scrollToYearAfterRender = true;
}
}
-
///
- /// We need a random id for the year items in the year list so we can scroll to the item safely in every DatePicker.
+ /// Date format value change hook for descendants.
///
- private string _componentId = Guid.NewGuid().ToString();
+ protected virtual Task DateFormatChanged(string newFormat)
+ {
+ return Task.CompletedTask;
+ }
+
+ protected override bool SetCulture(CultureInfo value)
+ {
+ if (!base.SetCulture(value))
+ return false;
+
+ if (!_dateFormatTouched && Converter is DefaultConverter defaultConverter)
+ defaultConverter.Format = value.DateTimeFormat.ShortDatePattern;
+ return true;
+ }
///
- /// Is set to true to scroll to the actual year after the next render
+ /// Fired when the date changes.
///
- private bool _scrollToYearAfterRender = false;
+ [Parameter] public EventCallback PickerMonthChanged { get; set; }
- public async void ScrollToYear()
- {
- _scrollToYearAfterRender = false;
- var id = $"{_componentId}{GetMonthStart(0).Year}";
- await ScrollManager.ScrollToYearAsync(id);
- StateHasChanged();
- }
+ #endregion
- private int GetMinYear()
+ #region Content
+ [Inject] protected IScrollManager ScrollManager { get; set; }
+ ///
+ /// Defines on which day the week starts. Depends on the value of Culture.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public DayOfWeek? FirstDayOfWeek { get; set; } = null;
+
+ ///
+ /// The current month of the date picker (two-way bindable). This changes when the user browses through the calender.
+ /// The month is represented as a DateTime which is always the first day of that month. You can also set this to define which month is initially shown. If not set, the current month is shown.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public DateTime? PickerMonth
{
- if (MinDate.HasValue)
- return MinDate.Value.Year;
- return DateTime.Today.Year - 100;
+ get => _picker_month;
+ set
+ {
+ if (value == _picker_month)
+ return;
+ _picker_month = value;
+ InvokeAsync(StateHasChanged);
+ PickerMonthChanged.InvokeAsync(value);
+ }
}
+ #endregion
- private int GetMaxYear()
+ #region Styling
+ private string GetMonthClasses(DateTime month)
{
- if (MaxDate.HasValue)
- return MaxDate.Value.Year;
- return DateTime.Today.Year + 100;
+ if (GetMonthStart(0) == month)
+ return $"picker-month-selected mud-{Color.ToDescription()}-text";
+ return null;
}
-
+ ///
+ /// Function to conditionally apply new classes to specific days
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Appearance)]
+ public Func AdditionalDateClassesFunc { get; set; }
private string GetYearClasses(int year)
{
if (year == GetMonthStart(0).Year)
@@ -508,56 +419,161 @@ public abstract partial class DatePickerBase : Picker
.AddClass($"picker-calendar-header-last", month == DisplayMonths - 1)
.Build();
}
+ ///
+ /// Custom previous icon.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerAppearance)]
+ public string PreviousIcon { get; set; } = Icons.Material.Filled.ChevronLeft;
- private Typo GetYearTypo(int year)
- {
- if (year == GetMonthStart(0).Year)
- return Typo.h5;
- return Typo.subtitle1;
- }
+ ///
+ /// Custom next icon.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerAppearance)]
+ public string NextIcon { get; set; } = Icons.Material.Filled.ChevronRight;
- private void OnFormattedDateClick()
+
+
+ ///
+ /// String Format for selected date view
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Behavior)]
+ public string DateFormat
{
- // todo: raise an event the user can handle
+ get
+ {
+ return (Converter as DefaultConverter)?.Format;
+ }
+ set
+ {
+ if (Converter is DefaultConverter defaultConverter)
+ {
+ defaultConverter.Format = value;
+ _dateFormatTouched = true;
+ }
+ DateFormatChanged(value);
+ }
}
+ #endregion
+ #region Behavior
+ ///
+ /// Max selectable date.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Validation)]
+ public DateTime? MaxDate { get; set; }
- private IEnumerable GetAllMonths()
- {
- var current = GetMonthStart(0);
- var calendarYear = Culture.Calendar.GetYear(current);
- var firstOfCalendarYear = Culture.Calendar.ToDateTime(calendarYear, 1, 1, 0, 0, 0, 0);
- for (var i = 0; i < Culture.Calendar.GetMonthsInYear(calendarYear); i++)
- yield return Culture.Calendar.AddMonths(firstOfCalendarYear, i);
- }
+ ///
+ /// Min selectable date.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Validation)]
+ public DateTime? MinDate { get; set; }
- private string GetAbbreviatedMonthName(DateTime month)
- {
- var calendarMonth = Culture.Calendar.GetMonth(month);
- return Culture.DateTimeFormat.AbbreviatedMonthNames[calendarMonth - 1];
- }
+ ///
+ /// First view to show in the DatePicker.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public OpenTo OpenTo { get; set; } = OpenTo.Date;
- private string GetMonthName(DateTime month)
- {
- var calendarMonth = Culture.Calendar.GetMonth(month);
- return Culture.DateTimeFormat.MonthNames[calendarMonth - 1];
- }
+ ///
+ /// Set a predefined fix year - no year can be selected
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public int? FixYear { get; set; }
+ ///
+ /// Set a predefined fix month - no month can be selected
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public int? FixMonth { get; set; }
+ ///
+ /// Set a predefined fix day - no day can be selected
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public int? FixDay { get; set; }
+ ///
+ /// Sets the amount of time in milliseconds to wait before closing the picker. This helps the user see that the date was selected before the popover disappears.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public int ClosingDelay { get; set; } = 100;
- private string GetMonthClasses(DateTime month)
- {
- if (GetMonthStart(0) == month)
- return $"picker-month-selected mud-{Color.ToDescription()}-text";
- return null;
- }
+ ///
+ /// Number of months to display in the calendar
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public int DisplayMonths { get; set; } = 1;
- private Typo GetMonthTypo(DateTime month)
+ ///
+ /// Maximum number of months in one row
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerAppearance)]
+ public int? MaxMonthColumns { get; set; }
+
+ ///
+ /// Start month when opening the picker.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public DateTime? StartMonth { get; set; }
+
+ ///
+ /// Display week numbers according to the Culture parameter. If no culture is defined, CultureInfo.CurrentCulture will be used.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool ShowWeekNumbers { get; set; }
+
+ ///
+ /// Format of the selected date in the title. By default, this is "ddd, dd MMM" which abbreviates day and month names.
+ /// For instance, display the long names like this "dddd, dd. MMMM".
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public string TitleDateFormat { get; set; } = "ddd, dd MMM";
+
+ ///
+ /// If AutoClose is set to true and PickerActions are defined, selecting a day will close the MudDatePicker.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.PickerBehavior)]
+ public bool AutoClose { get; set; }
+
+ ///
+ /// Function to determine whether a date is disabled
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Validation)]
+ public Func IsDateDisabledFunc
{
- if (GetMonthStart(0) == month)
- return Typo.h5;
- return Typo.subtitle1;
+ get => _isDateDisabledFunc;
+ set
+ {
+ _isDateDisabledFunc = value ?? (_ => false);
+ }
}
+ private Func _isDateDisabledFunc = _ => false;
+ #endregion
+ #region Lifecycle
+ protected DatePickerBase() : base(new DefaultConverter
+ {
+ Format = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern,
+ Culture = CultureInfo.CurrentCulture
+ })
+ {
+ AdornmentAriaLabel = "Open Date Picker";
+ }
protected override void OnInitialized()
{
@@ -584,17 +600,6 @@ public abstract partial class DatePickerBase : Picker
ScrollToYear();
}
- protected abstract DateTime GetCalendarStartOfMonth();
-
- private int GetCalendarDayOfMonth(DateTime date)
- {
- return Culture.Calendar.GetDayOfMonth(date);
- }
-
- ///
- /// Converts gregorian year into whatever year it is in the provided culture
- ///
- /// Gregorian year
- /// Year according to culture
- protected abstract int GetCalendarYear(int year);
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/DatePicker/DateRangePicker.razor.cs b/src/Connected.Components/Components/DatePicker/DateRangePicker.razor.cs
index ebf569e..af0723e 100644
--- a/src/Connected.Components/Components/DatePicker/DateRangePicker.razor.cs
+++ b/src/Connected.Components/Components/DatePicker/DateRangePicker.razor.cs
@@ -7,297 +7,305 @@ namespace Connected.Components;
public partial class DateRangePicker : DatePickerBase
{
- private DateTime? _firstDate = null, _secondDate;
- private DateRange _dateRange;
- private Range _rangeText;
-
- protected override bool IsRange => true;
-
- public DateRangePicker()
- {
- DisplayMonths = 2;
- AdornmentAriaLabel = "Open Date Range Picker";
- }
-
- ///
- /// Fired when the DateFormat changes.
- ///
- [Parameter] public EventCallback DateRangeChanged { get; set; }
-
- ///
- /// The currently selected range (two-way bindable). If null, then nothing was selected.
- ///
- [Parameter]
- [Category(CategoryTypes.FormComponent.Data)]
- public DateRange DateRange
- {
- get => _dateRange;
- set => SetDateRangeAsync(value, true).AndForget();
- }
-
- protected async Task SetDateRangeAsync(DateRange range, bool updateValue)
- {
- if (_dateRange != range)
- {
- var doesRangeContainDisabledDates = range?.Start != null && range?.End != null && Enumerable
- .Range(0, int.MaxValue)
- .Select(index => range.Start.Value.AddDays(index))
- .TakeWhile(date => date <= range.End.Value)
- .Any(date => IsDateDisabledFunc(date.Date));
-
- if (doesRangeContainDisabledDates)
- {
- _rangeText = null;
- await SetTextAsync(null, false);
- return;
- }
-
- _dateRange = range;
- _value = range?.End;
-
- if (updateValue)
- {
- if (_dateRange == null)
- {
- _rangeText = null;
- await SetTextAsync(null, false);
- }
- else
- {
- _rangeText = new Range(
- Converter.Convert(_dateRange.Start),
- Converter.Convert(_dateRange.End));
- await SetTextAsync(_dateRange.ToString(Converter), false);
- }
- }
-
- await DateRangeChanged.InvokeAsync(_dateRange);
- BeginValidate();
- }
- }
-
- private Range RangeText
- {
- get => _rangeText;
- set
- {
- if (_rangeText?.Equals(value) ?? value == null)
- return;
-
- Modified = true;
- _rangeText = value;
- SetDateRangeAsync(ParseDateRangeValue(value?.Start, value?.End), false).AndForget();
- }
- }
-
- private RangeInput _rangeInput;
-
- ///
- /// Focuses the start date of MudDateRangePicker
- ///
- ///
- public ValueTask FocusStartAsync() => _rangeInput.FocusStartAsync();
-
- ///
- /// Selects the start date of MudDateRangePicker
- ///
- ///
- public ValueTask SelectStartAsync() => _rangeInput.SelectStartAsync();
-
- ///
- /// Selects the specified range of the start date text
- ///
- /// Start position of the selection
- /// End position of the selection
- ///
- public ValueTask SelectRangeStartAsync(int pos1, int pos2) => _rangeInput.SelectRangeStartAsync(pos1, pos2);
-
- ///
- /// Focuses the end date of MudDateRangePicker
- ///
- ///
- public ValueTask FocusEndAsync() => _rangeInput.FocusEndAsync();
-
- ///
- /// Selects the end date of MudDateRangePicker
- ///
- ///
- public ValueTask SelectEndAsync() => _rangeInput.SelectEndAsync();
-
- ///
- /// Selects the specified range of the end date text
- ///
- /// Start position of the selection
- /// End position of the selection
- ///
- public ValueTask SelectRangeEndAsync(int pos1, int pos2) => _rangeInput.SelectRangeEndAsync(pos1, pos2);
-
- protected override Task DateFormatChanged(string newFormat)
- {
- Modified = true;
- return SetTextAsync(_dateRange?.ToString(Converter), false);
- }
-
- protected override Task StringValueChanged(string value)
- {
- Modified = true;
- // Update the daterange property (without updating back the Value property)
- return SetDateRangeAsync(ParseDateRangeValue(value), false);
- }
-
- protected override bool HasValue(DateTime? value)
- {
- return null != value && value.HasValue;
- }
-
- private DateRange ParseDateRangeValue(string value)
- {
- return DateRange.TryParse(value, Converter, out var dateRange) ? dateRange : null;
- }
-
- private DateRange ParseDateRangeValue(string start, string end)
- {
- return DateRange.TryParse(start, end, Converter, out var dateRange) ? dateRange : null;
- }
-
- protected override void OnPickerClosed()
- {
- _firstDate = null;
- base.OnPickerClosed();
- }
-
- protected override string GetDayClasses(int month, DateTime day)
- {
- var b = new CssBuilder("day");
- if (day < GetMonthStart(month) || day > GetMonthEnd(month))
- {
- return b.AddClass("hidden").Build();
- }
-
- if ((_firstDate != null && _secondDate != null && _firstDate < day && _secondDate > day) ||
- (_firstDate == null && _dateRange != null && _dateRange.Start < day && _dateRange.End > day))
- {
- return b
- .AddClass("range")
- .AddClass("range-between")
- .Build();
- }
-
- if ((_firstDate != null && day == _firstDate) ||
- (_firstDate == null && _dateRange != null && _dateRange.Start == day && DateRange.End != day))
- {
- return b.AddClass("selected")
- .AddClass("range")
- .AddClass("range-start-selected")
- .AddClass("range-selection", _firstDate != null)
- .AddClass($"theme-{Color.ToDescription()}")
- .Build();
- }
-
- if ((_firstDate != null && _secondDate != null && day == _secondDate) ||
- (_firstDate == null && _dateRange != null && _dateRange.Start != day && _dateRange.End == day))
- {
- return b.AddClass("selected")
- .AddClass("range")
- .AddClass("range-end-selected")
- .AddClass($"theme-{Color.ToDescription()}")
- .Build();
- }
-
- if (_firstDate == null && _dateRange != null && _dateRange.Start == _dateRange.End && _dateRange.Start == day)
- {
- return b.AddClass("selected").AddClass($"theme-{Color.ToDescription()}").Build();
- }
- else if (_firstDate != null && day > _firstDate)
- {
- return b.AddClass("range")
- .AddClass("range-selection", _secondDate == null)
- .AddClass($"range-selection-{Color.ToDescription()}", _firstDate != null)
- .Build();
- }
-
- if (day == DateTime.Today)
- {
- return b.AddClass("current")
- .AddClass("range", _firstDate != null && day > _firstDate)
- .AddClass("range-selection", _firstDate != null && day > _firstDate)
- .AddClass($"range-selection-{Color.ToDescription()}", _firstDate != null && day > _firstDate)
- .AddClass($"{Color.ToDescription()}-text")
- .Build();
- }
-
- return b.Build();
- }
-
- protected override async void OnDayClicked(DateTime dateTime)
- {
- if (_firstDate == null || _firstDate > dateTime || _secondDate != null)
- {
- _secondDate = null;
- _firstDate = dateTime;
- return;
- }
-
- _secondDate = dateTime;
- if (PickerActions == null || AutoClose)
- {
- Submit();
-
- if (PickerVariant != PickerVariant.Static)
- {
- await Task.Delay(ClosingDelay);
- Close(false);
- }
- }
- }
-
- protected override void OnOpened()
- {
- _secondDate = null;
- base.OnOpened();
- }
-
- protected internal override async void Submit()
- {
- if (ReadOnly)
- return;
- if (_firstDate == null || _secondDate == null)
- return;
-
- await SetDateRangeAsync(new DateRange(_firstDate, _secondDate), true);
-
- _firstDate = null;
- _secondDate = null;
- }
-
- public override void Clear(bool close = true)
- {
- DateRange = null;
- _firstDate = _secondDate = null;
- base.Clear();
- }
-
- protected override string GetTitleDateString()
- {
- if (_firstDate != null)
- return $"{FormatTitleDate(_firstDate)} - {FormatTitleDate(_secondDate)}";
-
- return DateRange?.Start != null
- ? $"{FormatTitleDate(DateRange.Start)} - {FormatTitleDate(DateRange.End)}"
- : "";
- }
-
- protected override DateTime GetCalendarStartOfMonth()
- {
- var date = StartMonth ?? DateRange?.Start ?? DateTime.Today;
- return date.StartOfMonth(Culture);
- }
-
- protected override int GetCalendarYear(int year)
- {
- var date = DateRange?.Start ?? DateTime.Today;
- var diff = date.Year - year;
- var calenderYear = Culture.Calendar.GetYear(date);
- return calenderYear - diff;
- }
+ #region Variables
+ private DateTime? _firstDate = null, _secondDate;
+ private DateRange _dateRange;
+ private Range _rangeText;
+ protected override bool IsRange => true;
+ private RangeInput _rangeInput;
+ ///
+ /// The currently selected range (two-way bindable). If null, then nothing was selected.
+ ///
+ [Parameter]
+ [Category(CategoryTypes.FormComponent.Data)]
+ public DateRange DateRange
+ {
+ get => _dateRange;
+ set => SetDateRangeAsync(value, true).AndForget();
+ }
+ #endregion
+
+ #region Events
+
+ protected override async void OnDayClicked(DateTime dateTime)
+ {
+ if (_firstDate == null || _firstDate > dateTime || _secondDate != null)
+ {
+ _secondDate = null;
+ _firstDate = dateTime;
+ return;
+ }
+
+ _secondDate = dateTime;
+ if (PickerActions == null || AutoClose)
+ {
+ Submit();
+
+ if (PickerVariant != PickerVariant.Static)
+ {
+ await Task.Delay(ClosingDelay);
+ Close(false);
+ }
+ }
+ }
+
+ protected override void OnOpened()
+ {
+ _secondDate = null;
+ base.OnOpened();
+ }
+
+ protected internal override async void Submit()
+ {
+ if (ReadOnly)
+ return;
+ if (_firstDate == null || _secondDate == null)
+ return;
+
+ await SetDateRangeAsync(new DateRange(_firstDate, _secondDate), true);
+
+ _firstDate = null;
+ _secondDate = null;
+ }
+
+ public override void Clear(bool close = true)
+ {
+ DateRange = null;
+ _firstDate = _secondDate = null;
+ base.Clear();
+ }
+ protected override Task DateFormatChanged(string newFormat)
+ {
+ Modified = true;
+ return SetTextAsync(_dateRange?.ToString(Converter), false);
+ }
+
+ protected override Task StringValueChanged(string value)
+ {
+ Modified = true;
+ // Update the daterange property (without updating back the Value property)
+ return SetDateRangeAsync(ParseDateRangeValue(value), false);
+ }
+ ///
+ /// Fired when the DateFormat changes.
+ ///
+ [Parameter] public EventCallback DateRangeChanged { get; set; }
+ protected async Task SetDateRangeAsync(DateRange range, bool updateValue)
+ {
+ if (_dateRange != range)
+ {
+ var doesRangeContainDisabledDates = range?.Start != null && range?.End != null && Enumerable
+ .Range(0, int.MaxValue)
+ .Select(index => range.Start.Value.AddDays(index))
+ .TakeWhile(date => date <= range.End.Value)
+ .Any(date => IsDateDisabledFunc(date.Date));
+
+ if (doesRangeContainDisabledDates)
+ {
+ _rangeText = null;
+ await SetTextAsync(null, false);
+ return;
+ }
+
+ _dateRange = range;
+ _value = range?.End;
+
+ if (updateValue)
+ {
+ if (_dateRange == null)
+ {
+ _rangeText = null;
+ await SetTextAsync(null, false);
+ }
+ else
+ {
+ _rangeText = new Range(
+ Converter.Convert(_dateRange.Start),
+ Converter.Convert(_dateRange.End));
+ await SetTextAsync(_dateRange.ToString(Converter), false);
+ }
+ }
+
+ await DateRangeChanged.InvokeAsync(_dateRange);
+ BeginValidate();
+ }
+ }
+ #endregion
+
+ #region Content
+
+ private Range RangeText
+ {
+ get => _rangeText;
+ set
+ {
+ if (_rangeText?.Equals(value) ?? value == null)
+ return;
+
+ Modified = true;
+ _rangeText = value;
+ SetDateRangeAsync(ParseDateRangeValue(value?.Start, value?.End), false).AndForget();
+ }
+ }
+
+ #endregion
+
+ #region Styling
+ protected override string GetDayClasses(int month, DateTime day)
+ {
+ var b = new CssBuilder("day");
+ if (day < GetMonthStart(month) || day > GetMonthEnd(month))
+ {
+ return b.AddClass("hidden").Build();
+ }
+
+ if ((_firstDate != null && _secondDate != null && _firstDate < day && _secondDate > day) ||
+ (_firstDate == null && _dateRange != null && _dateRange.Start < day && _dateRange.End > day))
+ {
+ return b
+ .AddClass("range")
+ .AddClass("range-between")
+ .Build();
+ }
+
+ if ((_firstDate != null && day == _firstDate) ||
+ (_firstDate == null && _dateRange != null && _dateRange.Start == day && DateRange.End != day))
+ {
+ return b.AddClass("selected")
+ .AddClass("range")
+ .AddClass("range-start-selected")
+ .AddClass("range-selection", _firstDate != null)
+ .AddClass($"theme-{Color.ToDescription()}")
+ .Build();
+ }
+
+ if ((_firstDate != null && _secondDate != null && day == _secondDate) ||
+ (_firstDate == null && _dateRange != null && _dateRange.Start != day && _dateRange.End == day))
+ {
+ return b.AddClass("selected")
+ .AddClass("range")
+ .AddClass("range-end-selected")
+ .AddClass($"theme-{Color.ToDescription()}")
+ .Build();
+ }
+
+ if (_firstDate == null && _dateRange != null && _dateRange.Start == _dateRange.End && _dateRange.Start == day)
+ {
+ return b.AddClass("selected").AddClass($"theme-{Color.ToDescription()}").Build();
+ }
+ else if (_firstDate != null && day > _firstDate)
+ {
+ return b.AddClass("range")
+ .AddClass("range-selection", _secondDate == null)
+ .AddClass($"range-selection-{Color.ToDescription()}", _firstDate != null)
+ .Build();
+ }
+
+ if (day == DateTime.Today)
+ {
+ return b.AddClass("current")
+ .AddClass("range", _firstDate != null && day > _firstDate)
+ .AddClass("range-selection", _firstDate != null && day > _firstDate)
+ .AddClass($"range-selection-{Color.ToDescription()}", _firstDate != null && day > _firstDate)
+ .AddClass($"{Color.ToDescription()}-text")
+ .Build();
+ }
+
+ return b.Build();
+ }
+ #endregion
+
+ #region Behavior
+
+ protected override string GetTitleDateString()
+ {
+ if (_firstDate != null)
+ return $"{FormatTitleDate(_firstDate)} - {FormatTitleDate(_secondDate)}";
+
+ return DateRange?.Start != null
+ ? $"{FormatTitleDate(DateRange.Start)} - {FormatTitleDate(DateRange.End)}"
+ : "";
+ }
+
+ protected override DateTime GetCalendarStartOfMonth()
+ {
+ var date = StartMonth ?? DateRange?.Start ?? DateTime.Today;
+ return date.StartOfMonth(Culture);
+ }
+
+ protected override int GetCalendarYear(int year)
+ {
+ var date = DateRange?.Start ?? DateTime.Today;
+ var diff = date.Year - year;
+ var calenderYear = Culture.Calendar.GetYear(date);
+ return calenderYear - diff;
+ }
+ protected override bool HasValue(DateTime? value)
+ {
+ return null != value && value.HasValue;
+ }
+
+ private DateRange ParseDateRangeValue(string value)
+ {
+ return DateRange.TryParse(value, Converter, out var dateRange) ? dateRange : null;
+ }
+
+ private DateRange ParseDateRangeValue(string start, string end)
+ {
+ return DateRange.TryParse(start, end, Converter, out var dateRange) ? dateRange : null;
+ }
+ ///
+ /// Focuses the start date of MudDateRangePicker
+ ///
+ ///
+ public ValueTask FocusStartAsync() => _rangeInput.FocusStartAsync();
+
+ ///
+ /// Selects the start date of MudDateRangePicker
+ ///
+ ///
+ public ValueTask SelectStartAsync() => _rangeInput.SelectStartAsync();
+
+ ///
+ /// Selects the specified range of the start date text
+ ///
+ /// Start position of the selection
+ /// End position of the selection
+ ///
+ public ValueTask SelectRangeStartAsync(int pos1, int pos2) => _rangeInput.SelectRangeStartAsync(pos1, pos2);
+
+ ///
+ /// Focuses the end date of MudDateRangePicker
+ ///
+ ///
+ public ValueTask FocusEndAsync() => _rangeInput.FocusEndAsync();
+
+ ///
+ /// Selects the end date of MudDateRangePicker
+ ///
+ ///
+ public ValueTask SelectEndAsync() => _rangeInput.SelectEndAsync();
+
+ ///
+ /// Selects the specified range of the end date text
+ ///
+ /// Start position of the selection
+ /// End position of the selection
+ ///
+ public ValueTask SelectRangeEndAsync(int pos1, int pos2) => _rangeInput.SelectRangeEndAsync(pos1, pos2);
+ #endregion
+
+ #region Lifecycle
+ public DateRangePicker()
+ {
+ DisplayMonths = 2;
+ AdornmentAriaLabel = "Open Date Range Picker";
+ }
+ protected override void OnPickerClosed()
+ {
+ _firstDate = null;
+ base.OnPickerClosed();
+ }
+ #endregion
+
}
diff --git a/src/Connected.Components/Components/Input/Input.razor b/src/Connected.Components/Components/Input/Input.razor
index e27444b..e0c8326 100644
--- a/src/Connected.Components/Components/Input/Input.razor
+++ b/src/Connected.Components/Components/Input/Input.razor
@@ -8,7 +8,7 @@
Variant="@Variant"
HelperText="@HelperText"
HelperTextOnFocus="@HelperTextOnFocus"
- CounterText="@CounterText"
+ CounterText="@GetCounterText()"
FullWidth="@FullWidth"
class="@CompiledHelperContainerClassList.Build()"
Error="@HasErrors"
diff --git a/src/Connected.Components/Components/Input/Input.razor.cs b/src/Connected.Components/Components/Input/Input.razor.cs
index 0c282bc..846623e 100644
--- a/src/Connected.Components/Components/Input/Input.razor.cs
+++ b/src/Connected.Components/Components/Input/Input.razor.cs
@@ -198,6 +198,11 @@ public partial class Input : InputBase
///
[Parameter] public bool HideSpinButtons { get; set; } = true;
+ internal override InputType GetInputType()
+ {
+ return InputType;
+ }
+
///
/// Show clear button.
///
@@ -230,7 +235,7 @@ public partial class Input : InputBase
#region Input field class
[Parameter]
- public string InputClass { get; set; } = string.Empty;
+ public string Class { get; set; } = string.Empty;
protected CssBuilder CompiledInputClass
{
get
@@ -240,7 +245,7 @@ public partial class Input : InputBase
.AddClass($"input-root-{Variant.ToDescription()}")
.AddClass($"input-root-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
.AddClass($"input-root-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
- .AddClass(InputClass);
+ .AddClass(Class);
}
}
#endregion
@@ -315,7 +320,17 @@ public partial class Input : InputBase
///
/// The current character counter, displayed below the text field.
///
- [Parameter] public string CounterText { get; set; }
+ [Parameter] public bool ShowCharacterCounter { get; set; }
+
+ ///
+ /// The current character counter, displayed below the text field.
+ ///
+ public string GetCounterText()
+ {
+ if (ShowCharacterCounter)
+ return Text.Length.ToString();
+ return string.Empty;
+ }
private System.Timers.Timer _timer;
@@ -381,7 +396,7 @@ public partial class Input : InputBase
[Parameter]
public double Step { get; set; } = 1;
- internal override InputType GetInputType() => InputType;
+ //internal override InputType GetInputType() => InputType;
protected string InputTypeString => InputType.ToDescription();
public ElementReference ElementReference { get; private set; }
private ElementReference _elementReference1;