Progress - Categorizing variables and methods
This commit is contained in:
parent
46c396e33f
commit
c3b267dfc4
@ -126,7 +126,7 @@ public static class CategoryTypes
|
|||||||
public const string Common = "Common";
|
public const string Common = "Common";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Used in: <see cref="ButtonBase"/>, all components inheriting from it, and <see cref="MudToggleIconButton"/>.</summary>
|
/// <summary>Used in: <see cref="ButtonBase"/>, all components inheriting from it, and <see cref="ToggleIconButton"/>.</summary>
|
||||||
public static class Button
|
public static class Button
|
||||||
{
|
{
|
||||||
public const string Behavior = "Behavior";
|
public const string Behavior = "Behavior";
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
using Connected.Extensions;
|
using Connected.Utilities;
|
||||||
using Connected.Utilities;
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using System.Security;
|
|
||||||
|
|
||||||
namespace Connected.Components;
|
namespace Connected.Components;
|
||||||
|
|
||||||
public partial class AppBar : UIComponent
|
public partial class AppBar : UIComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Event callbacks
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content placeholders
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of the component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling properties
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The classlist determining the toolbar render.
|
/// The classlist determining the toolbar render.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -18,6 +31,12 @@ public partial class AppBar : UIComponent
|
|||||||
.AddClass(ToolbarClassList);
|
.AddClass(ToolbarClassList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Class names for the nested toolbar. In case of several, separate by spaces.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? ToolbarClassList { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The classlist determining the header render.
|
/// The classlist determining the header render.
|
||||||
@ -32,34 +51,36 @@ public partial class AppBar : UIComponent
|
|||||||
.AddClass(HeaderClassList);
|
.AddClass(HeaderClassList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of the component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment? ChildContent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, appbar will be fixed.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool Fixed { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class names for the AppBar header. In case of several, separate by spaces.
|
/// Class names for the AppBar header. In case of several, separate by spaces.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string? HeaderClassList { get; set; }
|
public string? HeaderClassList { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Class names for the nested toolbar. In case of several, separate by spaces.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? ToolbarClassList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines the vertical alignment of the AppBar.
|
/// Determines the vertical alignment of the AppBar.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public VerticalAlignment VerticalAlignment { get; set; }
|
public VerticalAlignment VerticalAlignment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, appbar will be fixed.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool Fixed { get; set; } = true;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,12 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Autocomplete<T> : InputBase<T>, IDisposable
|
public partial class Autocomplete<T> : InputBase<T>, IDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Variables
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
IScrollManager ScrollManager { get; set; }
|
||||||
|
|
||||||
private Func<T, string>? _toStringFunc;
|
private Func<T, string>? _toStringFunc;
|
||||||
private Task _currentSearchTask;
|
private Task _currentSearchTask;
|
||||||
private CancellationTokenSource _cancellationTokenSrc;
|
private CancellationTokenSource _cancellationTokenSrc;
|
||||||
@ -26,77 +32,22 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly string _componentId = Guid.NewGuid().ToString();
|
private readonly string _componentId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
public Autocomplete()
|
#endregion
|
||||||
{
|
|
||||||
Adornment = Adornment.End;
|
|
||||||
IconSize = Size.Medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Inject]
|
#region Event callbacks
|
||||||
IScrollManager ScrollManager { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// User class names for the popover, separated by space
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string PopoverClass { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Set the anchor origin point to determen where the popover will open from.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public Origin AnchorOrigin { get; set; } = Origin.BottomCenter;
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the transform origin point for the popover.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public Origin TransformOrigin { get; set; } = Origin.TopCenter;
|
|
||||||
/// <summary>
|
|
||||||
/// If true, compact vertical padding will be applied to all Autocomplete items.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool Dense { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// The Open Autocomplete Glyph
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string OpenIcon { get; set; } = Icons.Material.Filled.ArrowDropDown;
|
|
||||||
/// <summary>
|
|
||||||
/// The Close Autocomplete Glyph
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string CloseIcon { get; set; } = Icons.Material.Filled.ArrowDropUp;
|
|
||||||
/// <summary>
|
|
||||||
/// The maximum height of the Autocomplete when it is open.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public int MaxHeight { get; set; } = 300;
|
|
||||||
/// <summary>
|
|
||||||
/// Defines how values are displayed in the drop-down list
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public Func<T, string>? ToStringFunc
|
|
||||||
{
|
|
||||||
get => _toStringFunc;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_toStringFunc == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_toStringFunc = value;
|
/// <summary>
|
||||||
|
/// Function to be invoked when checking whether an item should be disabled or not
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<T, bool> ItemDisabledFunc { get; set; }
|
||||||
|
|
||||||
SetConverter(new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to show the progress indicator.
|
/// If true, the currently selected item from the drop-down (if it is open) is selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowProgressIndicator { get; set; } = false;
|
public bool SelectValueOnTab { get; set; } = false;
|
||||||
/// <summary>
|
|
||||||
/// The color of the progress indicator.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public ThemeColor ProgressIndicatorColor { get; set; } = ThemeColor.Default;
|
|
||||||
private bool IsLoading => _currentSearchTask is not null && !_currentSearchTask.IsCompleted;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Func that returns a list of items matching the typed text. Provides a cancellation token that
|
/// 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.
|
/// is marked as cancelled when the user changes the search text or selects a value from the list.
|
||||||
@ -109,145 +60,95 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<string, Task<IEnumerable<T>>> SearchFunc { get; set; }
|
public Func<string, Task<IEnumerable<T>>> SearchFunc { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Maximum items to display, defaults to 10.
|
|
||||||
/// A null value will display all items.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public int? MaxItems { get; set; } = 10;
|
|
||||||
/// <summary>
|
|
||||||
/// Minimum characters to initiate a search
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public int MinCharacters { get; set; } = 0;
|
|
||||||
/// <summary>
|
|
||||||
/// Reset value if user deletes the text
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool ResetValueOnEmptyText { get; set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// If true, clicking the text field will select (highlight) its contents.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool SelectOnClick { get; set; } = true;
|
|
||||||
/// <summary>
|
|
||||||
/// Debounce interval in milliseconds.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public int DebounceInterval { get; set; } = 100;
|
|
||||||
/// <summary>
|
|
||||||
/// Optional presentation template for unselected items
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment<T> ItemTemplate { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Optional presentation template for the selected item
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment<T> ItemSelectedTemplate { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Optional presentation template for disabled item
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment<T> ItemDisabledTemplate { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Optional presentation template for when more items were returned from the Search function than the MaxItems limit
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment MoreItemsTemplate { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Optional presentation template for when no items were returned from the Search function
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment NoItemsTemplate { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Optional template for progress indicator
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ProgressIndicatorTemplate { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Optional template for showing progress indicator inside the popover
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ProgressIndicatorInPopoverTemplate { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool CoerceText { get; set; } = true;
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool CoerceValue { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Function to be invoked when checking whether an item should be disabled or not
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public Func<T, bool> ItemDisabledFunc { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the open state of the drop-down.
|
|
||||||
/// </summary>
|
|
||||||
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();
|
// <summary>
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// An event triggered when the state of IsOpen has changed
|
/// An event triggered when the state of IsOpen has changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<bool> IsOpenChanged { get; set; }
|
public EventCallback<bool> IsOpenChanged { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// If true, the currently selected item from the drop-down (if it is open) is selected.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool SelectValueOnTab { get; set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// Show clear button.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool Clearable { get; set; } = false;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Button click event for clear button. Called after text and value has been cleared.
|
/// Button click event for clear button. Called after text and value has been cleared.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
|
public EventCallback<MouseEventArgs> OnClearButtonClick { get; set; }
|
||||||
|
|
||||||
private string CurrentIcon => !string.IsNullOrWhiteSpace(AdornmentIcon) ? AdornmentIcon : _isOpen ? CloseIcon : OpenIcon;
|
internal virtual async Task OnInputKeyUp(KeyboardEventArgs args)
|
||||||
|
|
||||||
protected string ClassList()
|
|
||||||
{
|
{
|
||||||
return new CssBuilder("select")
|
switch (args.Key)
|
||||||
.Build();
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string AutocompleteClassList()
|
break;
|
||||||
|
case "ArrowUp":
|
||||||
|
if (args.AltKey)
|
||||||
|
await ChangeMenu(false);
|
||||||
|
else if (!IsOpen)
|
||||||
|
await ToggleMenu();
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return new CssBuilder("select")
|
var decrement = _selectedListItemIndex - _enabledItemIndices.ElementAtOrDefault(_enabledItemIndices.IndexOf(_selectedListItemIndex) - 1);
|
||||||
.AddClass("autocomplete")
|
await SelectNextItem(-(decrement < 0 ? 1 : decrement));
|
||||||
.AddClass("width-full", FullWidth)
|
|
||||||
.AddClass("autocomplete--with-progress", ShowProgressIndicator && IsLoading)
|
|
||||||
.Build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string CircularProgressClassList()
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal virtual async Task OnInputKeyDown(KeyboardEventArgs args)
|
||||||
{
|
{
|
||||||
return new CssBuilder("progress-indicator-circular")
|
switch (args.Key)
|
||||||
.AddClass("progress-indicator-circular--with-adornment", Adornment == Adornment.End)
|
{
|
||||||
.Build();
|
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)
|
public async Task SelectOption(T value)
|
||||||
@ -309,20 +210,7 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
protected override Task UpdateTextPropertyAsync(bool updateValue)
|
||||||
{
|
{
|
||||||
@ -464,83 +352,6 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
|
|||||||
return "null";
|
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)
|
private ValueTask SelectNextItem(int increment)
|
||||||
{
|
{
|
||||||
if (increment == 0 || _items is null || !_items.Any() || !_enabledItemIndices.Any())
|
if (increment == 0 || _items is null || !_items.Any() || !_enabledItemIndices.Any())
|
||||||
@ -629,21 +440,7 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
|
|||||||
return SetValueAsync(value, updateText: false);
|
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);
|
|
||||||
}
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Focus the input in the Autocomplete component.
|
/// Focus the input in the Autocomplete component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -689,4 +486,247 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
|
|||||||
{
|
{
|
||||||
await SelectOption(item);
|
await SelectOption(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content placeholders
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show clear button.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool Clearable { get; set; } = false;
|
||||||
|
private string CurrentIcon => !string.IsNullOrWhiteSpace(AdornmentIcon) ? AdornmentIcon : _isOpen ? CloseIcon : OpenIcon;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the open state of the drop-down.
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User class names for the popover, separated by space
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string PopoverClass { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set the anchor origin point to determen where the popover will open from.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Origin AnchorOrigin { get; set; } = Origin.BottomCenter;
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the transform origin point for the popover.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Origin TransformOrigin { get; set; } = Origin.TopCenter;
|
||||||
|
/// <summary>
|
||||||
|
/// If true, compact vertical padding will be applied to all Autocomplete items.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool Dense { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The Open Autocomplete Glyph
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string OpenIcon { get; set; } = Icons.Material.Filled.ArrowDropDown;
|
||||||
|
/// <summary>
|
||||||
|
/// The Close Autocomplete Glyph
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string CloseIcon { get; set; } = Icons.Material.Filled.ArrowDropUp;
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum height of the Autocomplete when it is open.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int MaxHeight { get; set; } = 300;
|
||||||
|
/// <summary>
|
||||||
|
/// Defines how values are displayed in the drop-down list
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<T, string>? ToStringFunc
|
||||||
|
{
|
||||||
|
get => _toStringFunc;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_toStringFunc == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_toStringFunc = value;
|
||||||
|
|
||||||
|
SetConverter(new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to show the progress indicator.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowProgressIndicator { get; set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the progress indicator.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public ThemeColor ProgressIndicatorColor { get; set; } = ThemeColor.Default;
|
||||||
|
private bool IsLoading => _currentSearchTask is not null && !_currentSearchTask.IsCompleted;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum items to display, defaults to 10.
|
||||||
|
/// A null value will display all items.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int? MaxItems { get; set; } = 10;
|
||||||
|
/// <summary>
|
||||||
|
/// Minimum characters to initiate a search
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int MinCharacters { get; set; } = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Reset value if user deletes the text
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ResetValueOnEmptyText { get; set; } = false;
|
||||||
|
/// <summary>
|
||||||
|
/// If true, clicking the text field will select (highlight) its contents.
|
||||||
|
/// </summary>
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Debounce interval in milliseconds.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int DebounceInterval { get; set; } = 100;
|
||||||
|
/// <summary>
|
||||||
|
/// Optional presentation template for unselected items
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<T> ItemTemplate { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Optional presentation template for the selected item
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<T> ItemSelectedTemplate { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Optional presentation template for disabled item
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<T> ItemDisabledTemplate { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Optional presentation template for when more items were returned from the Search function than the MaxItems limit
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment MoreItemsTemplate { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Optional presentation template for when no items were returned from the Search function
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment NoItemsTemplate { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Optional template for progress indicator
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ProgressIndicatorTemplate { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Optional template for showing progress indicator inside the popover
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ProgressIndicatorInPopoverTemplate { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool CoerceText { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool CoerceValue { get; set; }
|
||||||
|
|
||||||
|
#endregion Styling
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,43 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
partial class Avatar : UIComponent, IDisposable
|
partial class Avatar : UIComponent, IDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Variables
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
internal void ForceRedraw() => StateHasChanged();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of the component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Link to image, if set a image will be displayed instead of text.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? Image { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If set (and Image is also set), will add an alt property to the img element
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? ImageAltText { get; set; }
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected AvatarGroup? AvatarGroup { get; set; }
|
protected AvatarGroup? AvatarGroup { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
|
|
||||||
protected CssBuilder CompiledClassList
|
protected CssBuilder CompiledClassList
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -41,18 +75,6 @@ partial class Avatar : UIComponent, IDisposable
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string? ClassList { get; set; }
|
public string? ClassList { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If set (and Image is also set), will add an alt property to the img element
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? ImageAltText { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of the component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment? ChildContent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The color of the component. It supports the theme colors.
|
/// The color of the component. It supports the theme colors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -65,12 +87,6 @@ partial class Avatar : UIComponent, IDisposable
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public int Elevation { set; get; } = 0;
|
public int Elevation { set; get; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Link to image, if set a image will be displayed instead of text.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? Image { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the avatar appearance
|
/// Sets the avatar appearance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -88,6 +104,10 @@ partial class Avatar : UIComponent, IDisposable
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public Variant Variant { get; set; } = Variant.Filled;
|
public Variant Variant { get; set; } = Variant.Filled;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
base.OnInitialized();
|
base.OnInitialized();
|
||||||
@ -97,5 +117,6 @@ partial class Avatar : UIComponent, IDisposable
|
|||||||
|
|
||||||
public void Dispose() => AvatarGroup?.RemoveAvatar(this);
|
public void Dispose() => AvatarGroup?.RemoveAvatar(this);
|
||||||
|
|
||||||
internal void ForceRedraw() => StateHasChanged();
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,57 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
partial class AvatarGroup : UIComponent
|
partial class AvatarGroup : UIComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Variables
|
||||||
|
|
||||||
private bool _childrenNeedUpdates = false;
|
private bool _childrenNeedUpdates = false;
|
||||||
private int _spacing = 3;
|
private int _spacing = 3;
|
||||||
private List<Avatar> _avatars = new();
|
private List<Avatar> _avatars = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event callbacks
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of the component.
|
||||||
|
/// </summary>
|
||||||
|
[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
|
private CssBuilder CompiledClassList
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -21,6 +68,11 @@ partial class AvatarGroup : UIComponent
|
|||||||
.AddClass(ClassList);
|
.AddClass(ClassList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// A space separated list of class names, added on top of the default class list.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? ClassList { get; set; }
|
||||||
|
|
||||||
private CssBuilder CompiledMaxAvatarClassList
|
private CssBuilder CompiledMaxAvatarClassList
|
||||||
{
|
{
|
||||||
@ -31,30 +83,12 @@ partial class AvatarGroup : UIComponent
|
|||||||
.AddClass(MaxAvatarClass);
|
.AddClass(MaxAvatarClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Spacing between avatars where 0 is none and 16 max.
|
/// Custom class/classes for MaxAvatar
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int Spacing
|
[Category(CategoryTypes.AvatarGroup.Appearance)]
|
||||||
{
|
public string MaxAvatarClass { get; set; }
|
||||||
get => _spacing;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != _spacing)
|
|
||||||
{
|
|
||||||
_spacing = value;
|
|
||||||
_childrenNeedUpdates = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A space separated list of class names, added on top of the default class list.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? ClassList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Outlines the grouped avatars to distinguish them, useful when avatars are the same color or uses images.
|
/// Outlines the grouped avatars to distinguish them, useful when avatars are the same color or uses images.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -132,44 +166,25 @@ partial class AvatarGroup : UIComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom class/classes for MaxAvatar
|
/// Spacing between avatars where 0 is none and 16 max.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.AvatarGroup.Appearance)]
|
public int Spacing
|
||||||
public string MaxAvatarClass { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of the component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.AvatarGroup.Behavior)]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
internal void AddAvatar(Avatar avatar)
|
|
||||||
{
|
{
|
||||||
_avatars.Add(avatar);
|
get => _spacing;
|
||||||
StateHasChanged();
|
set
|
||||||
|
{
|
||||||
|
if (value != _spacing)
|
||||||
|
{
|
||||||
|
_spacing = value;
|
||||||
|
_childrenNeedUpdates = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveAvatar(Avatar avatar)
|
#endregion
|
||||||
{
|
|
||||||
_avatars.Remove(avatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
@ -184,4 +199,7 @@ partial class AvatarGroup : UIComponent
|
|||||||
_childrenNeedUpdates = false;
|
_childrenNeedUpdates = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
using System;
|
namespace Connected.Components;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Connected.Components;
|
|
||||||
public enum AvatarKind
|
public enum AvatarKind
|
||||||
{
|
{
|
||||||
Undefined = 0,
|
Undefined = 0,
|
||||||
|
@ -9,6 +9,72 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Badge : UIComponent
|
public partial class Badge : UIComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Variables
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Button click event if set.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal Task HandleBadgeClick(MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (OnClick.HasDelegate)
|
||||||
|
return OnClick.InvokeAsync(e);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
|
private string _content;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the Glyph to use in the badge.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Badge.Behavior)]
|
||||||
|
public string Icon { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max value to show when content is integer type.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Badge.Behavior)]
|
||||||
|
public int Max { get; set; } = 99;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Content you want inside the badge. Supported types are string and integer.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Badge.Behavior)]
|
||||||
|
public object Content { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of component, the content that the badge will apply to.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Badge.Behavior)]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Badge class names, separated by space.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Badge.Appearance)]
|
||||||
|
public string BadgeClass { get; set; }
|
||||||
|
|
||||||
protected string CompiledClassList =>
|
protected string CompiledClassList =>
|
||||||
new CssBuilder("badge-root")
|
new CssBuilder("badge-root")
|
||||||
.AddClass(AdditionalClassList)
|
.AddClass(AdditionalClassList)
|
||||||
@ -59,27 +125,6 @@ public partial class Badge : UIComponent
|
|||||||
[Category(CategoryTypes.Badge.Appearance)]
|
[Category(CategoryTypes.Badge.Appearance)]
|
||||||
public ThemeColor Color { get; set; } = ThemeColor.Default;
|
public ThemeColor Color { get; set; } = ThemeColor.Default;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Aligns the badge to bottom.
|
|
||||||
/// </summary>
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
[Obsolete("Use Origin instead.", true)]
|
|
||||||
[Parameter] public bool Bottom { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Aligns the badge to left.
|
|
||||||
/// </summary>
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
[Obsolete("Use Origin instead.", true)]
|
|
||||||
[Parameter] public bool Left { get => Start; set { Start = value; } }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Aligns the badge to the start (Left in LTR and right in RTL).
|
|
||||||
/// </summary>
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
[Obsolete("Use Origin instead.", true)]
|
|
||||||
[Parameter] public bool Start { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reduces the size of the badge and hide any of its content.
|
/// Reduces the size of the badge and hide any of its content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -101,55 +146,9 @@ public partial class Badge : UIComponent
|
|||||||
[Category(CategoryTypes.Badge.Appearance)]
|
[Category(CategoryTypes.Badge.Appearance)]
|
||||||
public bool Bordered { get; set; }
|
public bool Bordered { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
#endregion
|
||||||
/// Sets the Glyph to use in the badge.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Badge.Behavior)]
|
|
||||||
public string Icon { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
#region Lifecycle
|
||||||
/// Max value to show when content is integer type.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Badge.Behavior)]
|
|
||||||
public int Max { get; set; } = 99;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Content you want inside the badge. Supported types are string and integer.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Badge.Behavior)]
|
|
||||||
public object Content { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Badge class names, separated by space.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Badge.Appearance)]
|
|
||||||
public string BadgeClass { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of component, the content that the badge will apply to.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Badge.Behavior)]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Button click event if set.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
|
|
||||||
|
|
||||||
private string _content;
|
|
||||||
|
|
||||||
internal Task HandleBadgeClick(MouseEventArgs e)
|
|
||||||
{
|
|
||||||
if (OnClick.HasDelegate)
|
|
||||||
return OnClick.InvokeAsync(e);
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
@ -173,4 +172,6 @@ public partial class Badge : UIComponent
|
|||||||
_content = null;
|
_content = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,20 @@ using Microsoft.AspNetCore.Components;
|
|||||||
namespace Connected.Components;
|
namespace Connected.Components;
|
||||||
public partial class BreadcrumbLink : UIComponent
|
public partial class BreadcrumbLink : UIComponent
|
||||||
{
|
{
|
||||||
|
#region Content
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public BreadcrumbItem Item { get; set; }
|
public BreadcrumbItem Item { get; set; }
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public Breadcrumbs Parent { get; set; }
|
public Breadcrumbs Parent { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Style
|
||||||
|
|
||||||
private string Classname => new CssBuilder("breadcrumb-item")
|
private string Classname => new CssBuilder("breadcrumb-item")
|
||||||
.AddClass("disabled", Item?.Disabled)
|
.AddClass("disabled", Item?.Disabled)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
namespace Connected.Components;
|
namespace Connected.Components;
|
||||||
public partial class BreadcrumbSeparator : UIComponent
|
public partial class BreadcrumbSeparator : UIComponent
|
||||||
{
|
{
|
||||||
|
#region Content
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public Breadcrumbs Parent { get; set; }
|
public Breadcrumbs Parent { get; set; }
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,22 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Breadcrumbs : UIComponent
|
public partial class Breadcrumbs : UIComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
internal void Expand()
|
||||||
|
{
|
||||||
|
if (!Collapsed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Collapsed = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of breadcrumb items/links.
|
/// A list of breadcrumb items/links.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -20,6 +36,30 @@ public partial class Breadcrumbs : UIComponent
|
|||||||
[Category(CategoryTypes.Breadcrumbs.Appearance)]
|
[Category(CategoryTypes.Breadcrumbs.Appearance)]
|
||||||
public string Separator { get; set; } = "/";
|
public string Separator { get; set; } = "/";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom expander icon.
|
||||||
|
/// </summary>
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies a RenderFragment to use as the separator.
|
/// Specifies a RenderFragment to use as the separator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -34,6 +74,8 @@ public partial class Breadcrumbs : UIComponent
|
|||||||
[Category(CategoryTypes.Breadcrumbs.Behavior)]
|
[Category(CategoryTypes.Breadcrumbs.Behavior)]
|
||||||
public RenderFragment<BreadcrumbItem> ItemTemplate { get; set; }
|
public RenderFragment<BreadcrumbItem> ItemTemplate { get; set; }
|
||||||
|
|
||||||
|
public bool Collapsed { get; private set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controls when (and if) the breadcrumbs will automatically collapse.
|
/// Controls when (and if) the breadcrumbs will automatically collapse.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -41,32 +83,6 @@ public partial class Breadcrumbs : UIComponent
|
|||||||
[Category(CategoryTypes.Breadcrumbs.Behavior)]
|
[Category(CategoryTypes.Breadcrumbs.Behavior)]
|
||||||
public byte? MaxItems { get; set; }
|
public byte? MaxItems { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
#endregion
|
||||||
/// Custom expander icon.
|
|
||||||
/// </summary>
|
|
||||||
[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();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Expand()
|
|
||||||
{
|
|
||||||
if (!Collapsed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Collapsed = false;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,37 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class BreakpointProvider : UIComponent, IAsyncDisposable
|
public partial class BreakpointProvider : UIComponent, IAsyncDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Variables
|
||||||
private Guid _breakPointListenerSubscriptionId;
|
private Guid _breakPointListenerSubscriptionId;
|
||||||
|
|
||||||
public Breakpoint Breakpoint { get; private set; } = Breakpoint.Always;
|
public Breakpoint Breakpoint { get; private set; } = Breakpoint.Always;
|
||||||
|
|
||||||
[Parameter] public EventCallback<Breakpoint> OnBreakpointChanged { get; set; }
|
|
||||||
|
|
||||||
[Inject] public IBreakpointService Service { 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<Breakpoint> OnBreakpointChanged { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.BreakpointProvider.Behavior)]
|
[Category(CategoryTypes.BreakpointProvider.Behavior)]
|
||||||
public RenderFragment ChildContent { get; set; }
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
await base.OnAfterRenderAsync(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);
|
public async ValueTask DisposeAsync() => await Service.Unsubscribe(_breakPointListenerSubscriptionId);
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,27 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Button : ButtonBase
|
public partial class Button : ButtonBase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
|
/// <summary>
|
||||||
|
/// A space separated list of class names, added on top of the default class list.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? ClassList { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The variant to use.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Variant Variant { get; set; } = Variant.Text;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains the default container classlist and the user defined classes.
|
/// Contains the default container classlist and the user defined classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -17,25 +38,6 @@ public partial class Button : ButtonBase
|
|||||||
.AddClass(ClassList);
|
.AddClass(ClassList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Styling properties
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment? ChildContent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A space separated list of class names, added on top of the default class list.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? ClassList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The variant to use.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public Variant Variant { get; set; } = Variant.Text;
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,9 +36,13 @@ public abstract class ButtonBase : UIComponent
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected IActivatable? Activateable { get; set; }
|
protected IActivatable? Activateable { get; set; }
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Styling properties
|
/// <summary>
|
||||||
|
/// The HTML element that will be rendered in the root by the component
|
||||||
|
/// By default, is a button.
|
||||||
|
/// </summary>
|
||||||
|
protected string HtmlTag => ButtonType.ToString().ToLower();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Behavior properties
|
#region Behavior properties
|
||||||
@ -65,13 +69,6 @@ public abstract class ButtonBase : UIComponent
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Disabled { get; set; }
|
public bool Disabled { get; set; }
|
||||||
#endregion
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The HTML element that will be rendered in the root by the component
|
|
||||||
/// By default, is a button.
|
|
||||||
/// </summary>
|
|
||||||
protected string HtmlTag => ButtonType.ToString().ToLower();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether the internal click propagation should be disabled,
|
/// Indicates whether the internal click propagation should be disabled,
|
||||||
@ -79,5 +76,6 @@ public abstract class ButtonBase : UIComponent
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool PreventOnClickPropagation => string.Compare(HtmlTag, "button", true) == 0;
|
protected bool PreventOnClickPropagation => string.Compare(HtmlTag, "button", true) == 0;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,38 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Fab : ButtonBase
|
public partial class Fab : ButtonBase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
/// <summary>
|
||||||
|
/// If applied Glyph will be added at the start of the component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Button.Behavior)]
|
||||||
|
public string StartIcon { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If applied Glyph will be added at the end of the component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Button.Behavior)]
|
||||||
|
public string EndIcon { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If applied the text will be added to the component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Button.Behavior)]
|
||||||
|
public string Label { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GlyphTitle of the icon used for accessibility.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Button.Behavior)]
|
||||||
|
public string Title { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
protected string Classname =>
|
protected string Classname =>
|
||||||
new CssBuilder("button-root mud-fab")
|
new CssBuilder("button-root mud-fab")
|
||||||
.AddClass($"fab-extended", !string.IsNullOrEmpty(Label))
|
.AddClass($"fab-extended", !string.IsNullOrEmpty(Label))
|
||||||
@ -28,25 +60,6 @@ public partial class Fab : ButtonBase
|
|||||||
[Category(CategoryTypes.Button.Appearance)]
|
[Category(CategoryTypes.Button.Appearance)]
|
||||||
public Size Size { get; set; } = Size.Large;
|
public Size Size { get; set; } = Size.Large;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If applied Glyph will be added at the start of the component.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("This property is obsolete. Use StartIcon instead.")][Parameter] public string Icon { get => StartIcon; set => StartIcon = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If applied Glyph will be added at the start of the component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Button.Behavior)]
|
|
||||||
public string StartIcon { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If applied Glyph will be added at the end of the component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Button.Behavior)]
|
|
||||||
public string EndIcon { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The color of the icon. It supports the theme colors.
|
/// The color of the icon. It supports the theme colors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -60,18 +73,6 @@ public partial class Fab : ButtonBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.Button.Appearance)]
|
[Category(CategoryTypes.Button.Appearance)]
|
||||||
public Size IconSize { get; set; } = Size.Medium;
|
public Size IconSize { get; set; } = Size.Medium;
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If applied the text will be added to the component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Button.Behavior)]
|
|
||||||
public string Label { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GlyphTitle of the icon used for accessibility.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Button.Behavior)]
|
|
||||||
public string Title { get; set; }
|
|
||||||
}
|
}
|
||||||
|
@ -5,28 +5,7 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class IconButton : ButtonBase
|
public partial class IconButton : ButtonBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
#region Content
|
||||||
/// Contains the default container classlist and the user defined classes.
|
|
||||||
/// </summary>
|
|
||||||
private CssBuilder CompiledClassList
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new CssBuilder("button-root glyph-button")
|
|
||||||
.AddClass(ClassList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of component, only shows if Glyph is null or Empty.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment? ChildContent { get; set; }
|
|
||||||
|
|
||||||
#region EventCallbacks
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Content placeholders
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Icon that will be used in the component.
|
/// The Icon that will be used in the component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -38,9 +17,16 @@ public partial class IconButton : ButtonBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string? IconTitle { get; set; }
|
public string? IconTitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of component, only shows if Glyph is null or Empty.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Styling properties
|
#region Styling
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A space separated list of class names, added on top of the default class list.
|
/// A space separated list of class names, added on top of the default class list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -52,5 +38,17 @@ public partial class IconButton : ButtonBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Variant Variant { get; set; } = Variant.Text;
|
public Variant Variant { get; set; } = Variant.Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the default container classlist and the user defined classes.
|
||||||
|
/// </summary>
|
||||||
|
private CssBuilder CompiledClassList
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new CssBuilder("button-root glyph-button")
|
||||||
|
.AddClass(ClassList);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
ClassList="@ClassList"
|
ClassList="@ClassList"
|
||||||
Clicked="Toggle"
|
Clicked="Toggle"
|
||||||
Disabled="Disabled"
|
Disabled="Disabled"
|
||||||
Icon="@(Toggled ? ToggledGlyph : Glyph)"
|
Icon="@(Toggled ? ToggledIcon : Icon)"
|
||||||
IconTitle="@(Toggled && ToggledGlyphTitle != null ? ToggledGlyphTitle : GlyphTitle)"
|
IconTitle="@(Toggled && ToggledIconTitle != null ? ToggledIconTitle : IconTitle)"
|
||||||
Variant="Variant"
|
Variant="Variant"
|
||||||
@attributes="CustomAttributes"
|
@attributes="CustomAttributes"
|
||||||
/>
|
/>
|
||||||
|
@ -4,67 +4,12 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class ToggleIconButton : UIComponent
|
public partial class ToggleIconButton : UIComponent
|
||||||
{
|
{
|
||||||
#region EventCallbacks
|
#region Events
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fires whenever toggled is changed.
|
/// Fires whenever toggled is changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<bool> ToggledChanged { get; set; }
|
public EventCallback<bool> ToggledChanged { get; set; }
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Content placeholders
|
|
||||||
/// <summary>
|
|
||||||
/// The glyph that will be used in the untoggled state.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? Glyph { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GlyphTitle of the icon used for accessibility.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? GlyphTitle { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The glyph that will be used in the toggled state.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? ToggledGlyph { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GlyphTitle used in toggled state, if different.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? ToggledGlyphTitle { get; set; }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Styling properties
|
|
||||||
/// <summary>
|
|
||||||
/// A space separated list of class names, added on top of the default class list.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string? ClassList { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The variant to use.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public Variant Variant { get; set; } = Variant.Text;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Behavior properties
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the button will be disabled.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool Disabled { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The button toggled state.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public bool Toggled { get; set; }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public async Task Toggle()
|
public async Task Toggle()
|
||||||
{
|
{
|
||||||
@ -86,4 +31,59 @@ public partial class ToggleIconButton : UIComponent
|
|||||||
await ToggledChanged.InvokeAsync(Toggled);
|
await ToggledChanged.InvokeAsync(Toggled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
/// <summary>
|
||||||
|
/// The glyph that will be used in the untoggled state.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? Icon { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GlyphTitle of the icon used for accessibility.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? IconTitle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The glyph that will be used in the toggled state.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? ToggledIcon { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GlyphTitle used in toggled state, if different.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? ToggledIconTitle { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
|
/// <summary>
|
||||||
|
/// A space separated list of class names, added on top of the default class list.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? ClassList { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The variant to use.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Variant Variant { get; set; } = Variant.Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the button will be disabled.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The button toggled state.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool Toggled { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -7,37 +7,17 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class ButtonGroup : UIComponent
|
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();
|
|
||||||
|
|
||||||
|
|
||||||
[CascadingParameter(Name = "RightToLeft")]
|
|
||||||
public bool RightToLeft { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the button group will override the styles of the individual buttons.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ButtonGroup.Appearance)]
|
|
||||||
public bool OverrideStyles { get; set; } = true;
|
|
||||||
|
|
||||||
|
#region Content
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Child content of component.
|
/// Child content of component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.ButtonGroup.Behavior)]
|
[Category(CategoryTypes.ButtonGroup.Behavior)]
|
||||||
public RenderFragment ChildContent { get; set; }
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, the button group will be displayed vertically.
|
/// If true, the button group will be displayed vertically.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -72,4 +52,30 @@ public partial class ButtonGroup : UIComponent
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.ButtonGroup.Appearance)]
|
[Category(CategoryTypes.ButtonGroup.Appearance)]
|
||||||
public Variant Variant { get; set; } = Variant.Text;
|
public Variant Variant { get; set; } = Variant.Text;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
||||||
|
[CascadingParameter(Name = "RightToLeft")]
|
||||||
|
public bool RightToLeft { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the button group will override the styles of the individual buttons.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ButtonGroup.Appearance)]
|
||||||
|
public bool OverrideStyles { get; set; } = true;
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,16 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Card : UIComponent
|
public partial class Card : UIComponent
|
||||||
{
|
{
|
||||||
|
#region Content
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of the component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Card.Behavior)]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
protected string Classname =>
|
protected string Classname =>
|
||||||
new CssBuilder("card")
|
new CssBuilder("card")
|
||||||
.AddClass(AdditionalClassList)
|
.AddClass(AdditionalClassList)
|
||||||
@ -31,11 +41,6 @@ public partial class Card : UIComponent
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.Card.Appearance)]
|
[Category(CategoryTypes.Card.Appearance)]
|
||||||
public bool Outlined { get; set; }
|
public bool Outlined { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of the component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Card.Behavior)]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,22 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class CardActions : UIComponent
|
public partial class CardActions : UIComponent
|
||||||
{
|
{
|
||||||
|
#region Style
|
||||||
protected string Classname =>
|
protected string Classname =>
|
||||||
new CssBuilder("card-actions")
|
new CssBuilder("card-actions")
|
||||||
.AddClass(AdditionalClassList)
|
.AddClass(AdditionalClassList)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Child content of the component.
|
/// Child content of the component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.Card.Behavior)]
|
[Category(CategoryTypes.Card.Behavior)]
|
||||||
public RenderFragment ChildContent { get; set; }
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,22 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class CardContent : UIComponent
|
public partial class CardContent : UIComponent
|
||||||
{
|
{
|
||||||
|
#region Style
|
||||||
protected string Classname =>
|
protected string Classname =>
|
||||||
new CssBuilder("card-content")
|
new CssBuilder("card-content")
|
||||||
.AddClass(AdditionalClassList)
|
.AddClass(AdditionalClassList)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Child content of the component.
|
/// Child content of the component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.Card.Behavior)]
|
[Category(CategoryTypes.Card.Behavior)]
|
||||||
public RenderFragment ChildContent { get; set; }
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class CardHeader : UIComponent
|
public partial class CardHeader : UIComponent
|
||||||
{
|
{
|
||||||
|
#region Style
|
||||||
protected string Classname =>
|
protected string Classname =>
|
||||||
new CssBuilder("card-header")
|
new CssBuilder("card-header")
|
||||||
.AddClass(AdditionalClassList)
|
.AddClass(AdditionalClassList)
|
||||||
@ -32,10 +33,16 @@ public partial class CardHeader : UIComponent
|
|||||||
[Category(CategoryTypes.Card.Behavior)]
|
[Category(CategoryTypes.Card.Behavior)]
|
||||||
public RenderFragment CardHeaderActions { get; set; }
|
public RenderFragment CardHeaderActions { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optional child content
|
/// Optional child content
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
[Category(CategoryTypes.Card.Behavior)]
|
[Category(CategoryTypes.Card.Behavior)]
|
||||||
public RenderFragment ChildContent { get; set; }
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class CardMedia : UIComponent
|
public partial class CardMedia : UIComponent
|
||||||
{
|
{
|
||||||
|
#region Style
|
||||||
protected string StyleString =>
|
protected string StyleString =>
|
||||||
StyleBuilder.Default($"background-image:url(\"{Image}\");height: {Height}px;")
|
StyleBuilder.Default($"background-image:url(\"{Image}\");height: {Height}px;")
|
||||||
.Build();
|
.Build();
|
||||||
@ -15,6 +16,17 @@ public partial class CardMedia : UIComponent
|
|||||||
.AddClass(AdditionalClassList)
|
.AddClass(AdditionalClassList)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the height of the image in px.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Card.Behavior)]
|
||||||
|
public int Height { get; set; } = 300;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GlyphTitle of the image used for accessibility.
|
/// GlyphTitle of the image used for accessibility.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -29,10 +41,6 @@ public partial class CardMedia : UIComponent
|
|||||||
[Category(CategoryTypes.Card.Behavior)]
|
[Category(CategoryTypes.Card.Behavior)]
|
||||||
public string Image { get; set; }
|
public string Image { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
#endregion
|
||||||
/// Specifies the height of the image in px.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Card.Behavior)]
|
|
||||||
public int Height { get; set; } = 300;
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,102 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TData>, IAsyncDisposable
|
public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TData>, 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
|
||||||
|
/// <summary>
|
||||||
|
/// Called when selected Index changed on base class
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides Selection changes by horizontal swipe gesture
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Immediately starts the AutoCycle timer
|
||||||
|
/// </summary>
|
||||||
|
private ValueTask StartTimerAsync()
|
||||||
|
{
|
||||||
|
if (AutoCycle)
|
||||||
|
_timer?.Change(AutoCycleTime, TimeSpan.Zero);
|
||||||
|
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Immediately stops the AutoCycle timer
|
||||||
|
/// </summary>
|
||||||
|
private ValueTask StopTimerAsync()
|
||||||
|
{
|
||||||
|
_timer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||||
|
return ValueTask.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops and restart the AutoCycle timer
|
||||||
|
/// </summary>
|
||||||
|
private async ValueTask ResetTimerAsync()
|
||||||
|
{
|
||||||
|
await StopTimerAsync();
|
||||||
|
await StartTimerAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the SelectedIndex to a next one (or restart on 0)
|
||||||
|
/// </summary>
|
||||||
|
private async ValueTask TimerTickAsync()
|
||||||
|
{
|
||||||
|
await InvokeAsync(Next);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
protected string Classname =>
|
protected string Classname =>
|
||||||
new CssBuilder("carousel")
|
new CssBuilder("carousel")
|
||||||
.AddClass($"carousel-{(BulletsColor ?? _currentColor).ToDescription()}")
|
.AddClass($"carousel-{(BulletsColor ?? _currentColor).ToDescription()}")
|
||||||
@ -26,12 +122,6 @@ public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TD
|
|||||||
.AddClass(BulletsClass)
|
.AddClass(BulletsClass)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
private static Position ConvertPosition(Position position)
|
private static Position ConvertPosition(Position position)
|
||||||
{
|
{
|
||||||
return position switch
|
return position switch
|
||||||
@ -61,12 +151,6 @@ public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TD
|
|||||||
[Category(CategoryTypes.Carousel.Appearance)]
|
[Category(CategoryTypes.Carousel.Appearance)]
|
||||||
public Position ArrowsPosition { get; set; } = Position.Center;
|
public Position ArrowsPosition { get; set; } = Position.Center;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or Sets if bar with Bullets must be visible
|
|
||||||
/// </summary>
|
|
||||||
[Category(CategoryTypes.Carousel.Behavior)]
|
|
||||||
[Parameter] public bool ShowBullets { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the position of the bullets. By default, the position is the Bottom position
|
/// Sets the position of the bullets. By default, the position is the Bottom position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -80,16 +164,6 @@ public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TD
|
|||||||
[Category(CategoryTypes.Carousel.Appearance)]
|
[Category(CategoryTypes.Carousel.Appearance)]
|
||||||
[Parameter] public ThemeColor? BulletsColor { get; set; }
|
[Parameter] public ThemeColor? BulletsColor { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or Sets if bottom bar with Delimiters must be visible.
|
|
||||||
/// Deprecated, use ShowBullets instead.
|
|
||||||
/// </summary>
|
|
||||||
[Category(CategoryTypes.Carousel.Behavior)]
|
|
||||||
[Obsolete($"Use {nameof(ShowBullets)} instead", false)]
|
|
||||||
[ExcludeFromCodeCoverage]
|
|
||||||
[Parameter] public bool ShowDelimiters { get => ShowBullets; set => ShowBullets = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or Sets the Delimiters color.
|
/// Gets or Sets the Delimiters color.
|
||||||
/// If not set, the color is determined based on the <see cref="MudCarouselItem.Color"/> property of the active child.
|
/// If not set, the color is determined based on the <see cref="MudCarouselItem.Color"/> property of the active child.
|
||||||
@ -100,48 +174,6 @@ public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TD
|
|||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
[Parameter] public ThemeColor? DelimitersColor { get => BulletsColor; set => BulletsColor = value; }
|
[Parameter] public ThemeColor? DelimitersColor { get => BulletsColor; set => BulletsColor = value; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or Sets automatic cycle on item collection.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Carousel.Behavior)]
|
|
||||||
public bool AutoCycle
|
|
||||||
{
|
|
||||||
get => _autoCycle;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_autoCycle = value;
|
|
||||||
|
|
||||||
if (_autoCycle)
|
|
||||||
InvokeAsync(async () => await ResetTimerAsync());
|
|
||||||
|
|
||||||
else
|
|
||||||
InvokeAsync(async () => await StopTimerAsync());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or Sets the Auto Cycle time
|
|
||||||
/// </summary>
|
|
||||||
[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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or Sets custom class(es) for 'Next' and 'Previous' arrows
|
/// Gets or Sets custom class(es) for 'Next' and 'Previous' arrows
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -214,13 +246,6 @@ public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TD
|
|||||||
[Category(CategoryTypes.Carousel.Appearance)]
|
[Category(CategoryTypes.Carousel.Appearance)]
|
||||||
[Parameter] public RenderFragment<bool> BulletTemplate { get; set; }
|
[Parameter] public RenderFragment<bool> BulletTemplate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or Sets if swipe gestures are allowed for touch devices.
|
|
||||||
/// </summary>
|
|
||||||
[Category(CategoryTypes.Carousel.Behavior)]
|
|
||||||
[Parameter]
|
|
||||||
public bool EnableSwipeGesture { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or Sets the Template for Delimiters.
|
/// Gets or Sets the Template for Delimiters.
|
||||||
/// Deprecated, use BulletsTemplate instead.
|
/// Deprecated, use BulletsTemplate instead.
|
||||||
@ -230,91 +255,78 @@ public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TD
|
|||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
[Parameter] public RenderFragment<bool> DelimiterTemplate { get => BulletTemplate; set => BulletTemplate = value; }
|
[Parameter] public RenderFragment<bool> DelimiterTemplate { get => BulletTemplate; set => BulletTemplate = value; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Behavior
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or Sets if bar with Bullets must be visible
|
||||||
|
/// </summary>
|
||||||
|
[Category(CategoryTypes.Carousel.Behavior)]
|
||||||
|
[Parameter] public bool ShowBullets { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when selected Index changed on base class
|
/// Gets or Sets if bottom bar with Delimiters must be visible.
|
||||||
|
/// Deprecated, use ShowBullets instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void SelectionChanged()
|
[Category(CategoryTypes.Carousel.Behavior)]
|
||||||
|
[Obsolete($"Use {nameof(ShowBullets)} instead", false)]
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
|
[Parameter] public bool ShowDelimiters { get => ShowBullets; set => ShowBullets = value; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or Sets automatic cycle on item collection.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Carousel.Behavior)]
|
||||||
|
public bool AutoCycle
|
||||||
{
|
{
|
||||||
|
get => _autoCycle;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_autoCycle = value;
|
||||||
|
|
||||||
|
if (_autoCycle)
|
||||||
InvokeAsync(async () => await ResetTimerAsync());
|
InvokeAsync(async () => await ResetTimerAsync());
|
||||||
|
|
||||||
_currentColor = SelectedContainer?.Color ?? ThemeColor.Inherit;
|
else
|
||||||
}
|
InvokeAsync(async () => await StopTimerAsync());
|
||||||
|
|
||||||
//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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides Selection changes by horizontal swipe gesture
|
/// Gets or Sets the Auto Cycle time
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnSwipe(SwipeDirection direction)
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Carousel.Behavior)]
|
||||||
|
public TimeSpan AutoCycleTime
|
||||||
{
|
{
|
||||||
if (!EnableSwipeGesture)
|
get => _cycleTimeout;
|
||||||
|
set
|
||||||
{
|
{
|
||||||
return;
|
_cycleTimeout = value;
|
||||||
}
|
|
||||||
|
|
||||||
switch (direction)
|
if (_autoCycle == true)
|
||||||
{
|
InvokeAsync(async () => await ResetTimerAsync());
|
||||||
case SwipeDirection.LeftToRight:
|
|
||||||
if (RightToLeft) Next();
|
|
||||||
else Previous();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SwipeDirection.RightToLeft:
|
else
|
||||||
if (RightToLeft) Previous();
|
InvokeAsync(async () => await StopTimerAsync());
|
||||||
else Next();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Immediately starts the AutoCycle timer
|
|
||||||
/// </summary>
|
|
||||||
private ValueTask StartTimerAsync()
|
|
||||||
{
|
|
||||||
if (AutoCycle)
|
|
||||||
_timer?.Change(AutoCycleTime, TimeSpan.Zero);
|
|
||||||
|
|
||||||
return ValueTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Immediately stops the AutoCycle timer
|
|
||||||
/// </summary>
|
|
||||||
private ValueTask StopTimerAsync()
|
|
||||||
{
|
|
||||||
_timer?.Change(Timeout.Infinite, Timeout.Infinite);
|
|
||||||
return ValueTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops and restart the AutoCycle timer
|
|
||||||
/// </summary>
|
|
||||||
private async ValueTask ResetTimerAsync()
|
|
||||||
{
|
|
||||||
await StopTimerAsync();
|
|
||||||
await StartTimerAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the SelectedIndex to a next one (or restart on 0)
|
/// Gets or Sets if swipe gestures are allowed for touch devices.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async ValueTask TimerTickAsync()
|
[Category(CategoryTypes.Carousel.Behavior)]
|
||||||
{
|
[Parameter]
|
||||||
await InvokeAsync(Next);
|
public bool EnableSwipeGesture { get; set; } = true;
|
||||||
}
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
@ -346,4 +358,6 @@ public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,19 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class CarouselItem : UIComponent, IDisposable
|
public partial class CarouselItem : UIComponent, IDisposable
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
|
private bool _disposed = false;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Carousel.Behavior)]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
[CascadingParameter] protected internal ItemsControlBase<CarouselItem> Parent { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
protected string Classname =>
|
protected string Classname =>
|
||||||
new CssBuilder("carousel-item")
|
new CssBuilder("carousel-item")
|
||||||
.AddClass($"carousel-item-{Color.ToDescription()}")
|
.AddClass($"carousel-item-{Color.ToDescription()}")
|
||||||
@ -35,14 +48,6 @@ public partial class CarouselItem : UIComponent, IDisposable
|
|||||||
.AddClass(AdditionalClassList)
|
.AddClass(AdditionalClassList)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Carousel.Behavior)]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
[CascadingParameter] protected internal ItemsControlBase<CarouselItem> Parent { get; set; }
|
|
||||||
|
|
||||||
[CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The color of the component. It supports the theme colors.
|
/// The color of the component. It supports the theme colors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -71,21 +76,24 @@ public partial class CarouselItem : UIComponent, IDisposable
|
|||||||
[Category(CategoryTypes.Carousel.Appearance)]
|
[Category(CategoryTypes.Carousel.Appearance)]
|
||||||
public string CustomTransitionExit { get; set; }
|
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));
|
public bool IsVisible => Parent != null && (Parent.LastContainer == this || Parent.SelectedIndex == Parent.Items.IndexOf(this));
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
protected override Task OnInitializedAsync()
|
protected override Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
Parent?.AddItem(this);
|
Parent?.AddItem(this);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _disposed = false;
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
Parent?.Items.Remove(this);
|
Parent?.Items.Remove(this);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,99 +8,12 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Chart : UIComponent
|
public partial class Chart : UIComponent
|
||||||
{
|
{
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Behavior)]
|
|
||||||
public double[] InputData { get; set; } = Array.Empty<double>();
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Behavior)]
|
|
||||||
public string[] InputLabels { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Behavior)]
|
|
||||||
public string[] XAxisLabels { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Behavior)]
|
|
||||||
public List<ChartSeries> ChartSeries { get; set; } = new();
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Appearance)]
|
|
||||||
public ChartOptions ChartOptions { get; set; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RenderFragment for costumization inside the chart's svg.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Appearance)]
|
|
||||||
public RenderFragment CustomGraphics { get; set; }
|
|
||||||
|
|
||||||
protected string Classname =>
|
|
||||||
new CssBuilder("chart")
|
|
||||||
.AddClass($"chart-legend-{ConvertLegendPosition(LegendPosition).ToDescription()}")
|
|
||||||
.AddClass(AdditionalClassList)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
[CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Type of the chart.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Behavior)]
|
|
||||||
public ChartType ChartType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Width of the chart, end with % or px.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Appearance)]
|
|
||||||
public string Width { get; set; } = "80%";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Height of the chart, end with % or px.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Appearance)]
|
|
||||||
public string Height { get; set; } = "80%";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The placement direction of the legend if used.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#region Variables
|
||||||
private int _selectedIndex;
|
private int _selectedIndex;
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
#region Events
|
||||||
/// Selected index of a portion of the chart.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chart.Behavior)]
|
|
||||||
public int SelectedIndex
|
|
||||||
{
|
|
||||||
get => _selectedIndex;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != _selectedIndex)
|
|
||||||
{
|
|
||||||
_selectedIndex = value;
|
|
||||||
SelectedIndexChanged.InvokeAsync(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selected index of a portion of the chart.
|
/// Selected index of a portion of the chart.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -124,6 +37,102 @@ public partial class Chart : UIComponent
|
|||||||
|
|
||||||
return d.ToString(format);
|
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();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RenderFragment for costumization inside the chart's svg.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Appearance)]
|
||||||
|
public RenderFragment CustomGraphics { get; set; }
|
||||||
|
|
||||||
|
protected string Classname =>
|
||||||
|
new CssBuilder("chart")
|
||||||
|
.AddClass($"chart-legend-{ConvertLegendPosition(LegendPosition).ToDescription()}")
|
||||||
|
.AddClass(AdditionalClassList)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
[CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Width of the chart, end with % or px.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Appearance)]
|
||||||
|
public string Width { get; set; } = "80%";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Height of the chart, end with % or px.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Appearance)]
|
||||||
|
public string Height { get; set; } = "80%";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The placement direction of the legend if used.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Appearance)]
|
||||||
|
public Position LegendPosition { get; set; } = Position.Bottom;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Behavior
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Behavior)]
|
||||||
|
public double[] InputData { get; set; } = Array.Empty<double>();
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Behavior)]
|
||||||
|
public string[] InputLabels { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Behavior)]
|
||||||
|
public string[] XAxisLabels { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Behavior)]
|
||||||
|
public List<ChartSeries> ChartSeries { get; set; } = new();
|
||||||
|
/// <summary>
|
||||||
|
/// The Type of the chart.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Behavior)]
|
||||||
|
public ChartType ChartType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selected index of a portion of the chart.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chart.Behavior)]
|
||||||
|
public int SelectedIndex
|
||||||
|
{
|
||||||
|
get => _selectedIndex;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != _selectedIndex)
|
||||||
|
{
|
||||||
|
_selectedIndex = value;
|
||||||
|
SelectedIndexChanged.InvokeAsync(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
partial class Bar : Chart
|
partial class Bar : Chart
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
[CascadingParameter] public Chart ChartParent { get; set; }
|
[CascadingParameter] public Chart ChartParent { get; set; }
|
||||||
|
|
||||||
private List<SvgPath> _horizontalLines = new();
|
private List<SvgPath> _horizontalLines = new();
|
||||||
@ -16,7 +17,9 @@ partial class Bar : Chart
|
|||||||
private List<ChartSeries> _series = new();
|
private List<ChartSeries> _series = new();
|
||||||
|
|
||||||
private List<SvgPath> _bars = new();
|
private List<SvgPath> _bars = new();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
@ -147,4 +150,6 @@ partial class Bar : Chart
|
|||||||
_legends.Add(legend);
|
_legends.Add(legend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
partial class Donut : Chart
|
partial class Donut : Chart
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
[CascadingParameter] public Chart ChartParent { get; set; }
|
[CascadingParameter] public Chart ChartParent { get; set; }
|
||||||
|
|
||||||
private List<SvgCircle> _circles = new();
|
private List<SvgCircle> _circles = new();
|
||||||
@ -12,6 +13,10 @@ partial class Donut : Chart
|
|||||||
protected string ParentWidth => ChartParent?.Width;
|
protected string ParentWidth => ChartParent?.Width;
|
||||||
protected string ParentHeight => ChartParent?.Height;
|
protected string ParentHeight => ChartParent?.Height;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
_circles.Clear();
|
_circles.Clear();
|
||||||
@ -57,4 +62,6 @@ partial class Donut : Chart
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
partial class Line : Chart
|
partial class Line : Chart
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
|
|
||||||
private const int MaxHorizontalGridLines = 100;
|
private const int MaxHorizontalGridLines = 100;
|
||||||
|
|
||||||
[CascadingParameter] public Chart ChartParent { get; set; }
|
[CascadingParameter] public Chart ChartParent { get; set; }
|
||||||
@ -19,6 +21,10 @@ partial class Line : Chart
|
|||||||
|
|
||||||
private List<SvgPath> _chartLines = new();
|
private List<SvgPath> _chartLines = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet();
|
base.OnParametersSet();
|
||||||
@ -228,4 +234,6 @@ partial class Line : Chart
|
|||||||
_legends.Add(legend);
|
_legends.Add(legend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,16 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
partial class Pie : Chart
|
partial class Pie : Chart
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
[CascadingParameter] public Chart ChartParent { get; set; }
|
[CascadingParameter] public Chart ChartParent { get; set; }
|
||||||
|
|
||||||
private List<SvgPath> _paths = new();
|
private List<SvgPath> _paths = new();
|
||||||
private List<SvgLegend> _legends = new();
|
private List<SvgLegend> _legends = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
_paths.Clear();
|
_paths.Clear();
|
||||||
@ -52,4 +57,5 @@ partial class Pie : Chart
|
|||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -9,134 +9,13 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class CheckBox<T> : BooleanInput<T>
|
public partial class CheckBox<T> : BooleanInput<T>
|
||||||
{
|
{
|
||||||
protected string Classname =>
|
#region Variables
|
||||||
new CssBuilder("input-control-boolean-input")
|
private IKeyInterceptor _keyInterceptor;
|
||||||
.AddClass(AdditionalClassList)
|
[Inject] private IKeyInterceptorFactory _keyInterceptorFactory { get; set; }
|
||||||
.Build();
|
private string _elementId = "checkbox" + Guid.NewGuid().ToString().Substring(0, 8);
|
||||||
|
#endregion
|
||||||
protected string LabelClassname =>
|
|
||||||
new CssBuilder("checkbox")
|
|
||||||
.AddClass($"disabled", Disabled)
|
|
||||||
.AddClass($"readonly", ReadOnly)
|
|
||||||
.AddClass(LabelPosition == LabelPosition.End ? "ltr" : "rtl", true)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
protected string CheckBoxClassname =>
|
|
||||||
new CssBuilder("button-root mud-icon-button")
|
|
||||||
.AddClass($"{Color.ToDescription()}-text hover:mud-{Color.ToDescription()}-hover", UnCheckedColor == null || (UnCheckedColor != null && BoolValue == true))
|
|
||||||
.AddClass($"{UnCheckedColor?.ToDescription()}-text hover:mud-{UnCheckedColor?.ToDescription()}-hover", UnCheckedColor != null && BoolValue == false)
|
|
||||||
.AddClass($"checkbox-dense", Dense)
|
|
||||||
.AddClass($"ripple mud-ripple-checkbox", !DisableRipple && !ReadOnly && !Disabled)
|
|
||||||
.AddClass($"disabled", Disabled)
|
|
||||||
.AddClass($"readonly", ReadOnly)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The color of the component. It supports the theme colors.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Appearance)]
|
|
||||||
public ThemeColor Color { get; set; } = ThemeColor.Default;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The base color of the component in its none active/unchecked state. It supports the theme colors.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Radio.Appearance)]
|
|
||||||
public ThemeColor? UnCheckedColor { get; set; } = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The text/label will be displayed next to the checkbox if set.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Behavior)]
|
|
||||||
public string Label { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The position of the text/label.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Behavior)]
|
|
||||||
public LabelPosition LabelPosition { get; set; } = LabelPosition.End;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the checkbox can be controlled with the keyboard.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Behavior)]
|
|
||||||
public bool KeyboardEnabled { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, disables ripple effect.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Appearance)]
|
|
||||||
public bool DisableRipple { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, compact padding will be applied.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Appearance)]
|
|
||||||
public bool Dense { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Size of the component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Appearance)]
|
|
||||||
public Size Size { get; set; } = Size.Medium;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Behavior)]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom checked icon, leave null for default.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Appearance)]
|
|
||||||
public string CheckedIcon { get; set; } = Icons.Material.Filled.CheckBox;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom unchecked icon, leave null for default.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Appearance)]
|
|
||||||
public string UncheckedIcon { get; set; } = Icons.Material.Filled.CheckBoxOutlineBlank;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom indeterminate icon, leave null for default.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Appearance)]
|
|
||||||
public string IndeterminateIcon { get; set; } = Icons.Material.Filled.IndeterminateCheckBox;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Define if the checkbox can cycle again through indeterminate status.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Validation)]
|
|
||||||
public bool TriState { get; set; }
|
|
||||||
|
|
||||||
private string GetIcon()
|
|
||||||
{
|
|
||||||
if (BoolValue == true)
|
|
||||||
{
|
|
||||||
return CheckedIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BoolValue == false)
|
|
||||||
{
|
|
||||||
return UncheckedIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IndeterminateIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#region Events
|
||||||
protected override Task OnChange(ChangeEventArgs args)
|
protected override Task OnChange(ChangeEventArgs args)
|
||||||
{
|
{
|
||||||
Modified = true;
|
Modified = true;
|
||||||
@ -202,12 +81,144 @@ public partial class CheckBox<T> : BooleanInput<T>
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
private IKeyInterceptor _keyInterceptor;
|
#region Content
|
||||||
[Inject] private IKeyInterceptorFactory _keyInterceptorFactory { get; set; }
|
/// <summary>
|
||||||
|
/// Child content of component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Behavior)]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
private string _elementId = "checkbox" + Guid.NewGuid().ToString().Substring(0, 8);
|
#region Styling
|
||||||
|
protected string Classname =>
|
||||||
|
new CssBuilder("input-control-boolean-input")
|
||||||
|
.AddClass(AdditionalClassList)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
protected string LabelClassname =>
|
||||||
|
new CssBuilder("checkbox")
|
||||||
|
.AddClass($"disabled", Disabled)
|
||||||
|
.AddClass($"readonly", ReadOnly)
|
||||||
|
.AddClass(LabelPosition == LabelPosition.End ? "ltr" : "rtl", true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
protected string CheckBoxClassname =>
|
||||||
|
new CssBuilder("button-root mud-icon-button")
|
||||||
|
.AddClass($"{Color.ToDescription()}-text hover:mud-{Color.ToDescription()}-hover", UnCheckedColor == null || (UnCheckedColor != null && BoolValue == true))
|
||||||
|
.AddClass($"{UnCheckedColor?.ToDescription()}-text hover:mud-{UnCheckedColor?.ToDescription()}-hover", UnCheckedColor != null && BoolValue == false)
|
||||||
|
.AddClass($"checkbox-dense", Dense)
|
||||||
|
.AddClass($"ripple mud-ripple-checkbox", !DisableRipple && !ReadOnly && !Disabled)
|
||||||
|
.AddClass($"disabled", Disabled)
|
||||||
|
.AddClass($"readonly", ReadOnly)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the component. It supports the theme colors.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Appearance)]
|
||||||
|
public ThemeColor Color { get; set; } = ThemeColor.Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base color of the component in its none active/unchecked state. It supports the theme colors.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Radio.Appearance)]
|
||||||
|
public ThemeColor? UnCheckedColor { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, disables ripple effect.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Appearance)]
|
||||||
|
public bool DisableRipple { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, compact padding will be applied.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Appearance)]
|
||||||
|
public bool Dense { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Size of the component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Appearance)]
|
||||||
|
public Size Size { get; set; } = Size.Medium;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom checked icon, leave null for default.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Appearance)]
|
||||||
|
public string CheckedIcon { get; set; } = Icons.Material.Filled.CheckBox;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom unchecked icon, leave null for default.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Appearance)]
|
||||||
|
public string UncheckedIcon { get; set; } = Icons.Material.Filled.CheckBoxOutlineBlank;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom indeterminate icon, leave null for default.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Appearance)]
|
||||||
|
public string IndeterminateIcon { get; set; } = Icons.Material.Filled.IndeterminateCheckBox;
|
||||||
|
|
||||||
|
private string GetIcon()
|
||||||
|
{
|
||||||
|
if (BoolValue == true)
|
||||||
|
{
|
||||||
|
return CheckedIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BoolValue == false)
|
||||||
|
{
|
||||||
|
return UncheckedIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IndeterminateIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Behavior
|
||||||
|
/// <summary>
|
||||||
|
/// The text/label will be displayed next to the checkbox if set.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Behavior)]
|
||||||
|
public string Label { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The position of the text/label.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Behavior)]
|
||||||
|
public LabelPosition LabelPosition { get; set; } = LabelPosition.End;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the checkbox can be controlled with the keyboard.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Behavior)]
|
||||||
|
public bool KeyboardEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Define if the checkbox can cycle again through indeterminate status.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Validation)]
|
||||||
|
public bool TriState { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
base.OnInitialized();
|
base.OnInitialized();
|
||||||
@ -251,4 +262,6 @@ public partial class CheckBox<T> : BooleanInput<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,101 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Chip : UIComponent, IDisposable
|
public partial class Chip : UIComponent, IDisposable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Variables
|
||||||
private bool _isSelected;
|
private bool _isSelected;
|
||||||
[Inject] public NavigationManager UriHelper { get; set; }
|
[Inject] public NavigationManager UriHelper { get; set; }
|
||||||
|
|
||||||
[Inject] public IJsApiService JsApiService { 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 <a> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chip click event, if set the chip focus, hover and click effects are applied.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chip delete event, if set the delete icon will be visible.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<Chip> OnClose { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
[CascadingParameter] ChipSet ChipSet { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If set to a URL, clicking the button will open the referenced document. Use Target to specify where
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.ClickAction)]
|
||||||
|
public string Href { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The target attribute specifies where to open the link, if Href is specified. Possible values: _blank | _self | _parent | _top | <i>framename</i>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.ClickAction)]
|
||||||
|
public string Target { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command executed when the user clicks on an element.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.ClickAction)]
|
||||||
|
public ICommand Command { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command parameter.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.ClickAction)]
|
||||||
|
public object CommandParameter { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
protected string Classname =>
|
protected string Classname =>
|
||||||
new CssBuilder("chip")
|
new CssBuilder("chip")
|
||||||
.AddClass($"chip-{GetVariant().ToDescription()}")
|
.AddClass($"chip-{GetVariant().ToDescription()}")
|
||||||
@ -57,8 +147,6 @@ public partial class Chip : UIComponent, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CascadingParameter] ChipSet ChipSet { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The color of the component.
|
/// The color of the component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -87,13 +175,6 @@ public partial class Chip : UIComponent, IDisposable
|
|||||||
[Category(CategoryTypes.Chip.Appearance)]
|
[Category(CategoryTypes.Chip.Appearance)]
|
||||||
public ThemeColor SelectedColor { get; set; } = ThemeColor.Inherit;
|
public ThemeColor SelectedColor { get; set; } = ThemeColor.Inherit;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Avatar Glyph, Overrides the regular Glyph if set.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.Behavior)]
|
|
||||||
public string Avatar { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Avatar CSS Class, appends to Chips default avatar classes.
|
/// Avatar CSS Class, appends to Chips default avatar classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -108,20 +189,6 @@ public partial class Chip : UIComponent, IDisposable
|
|||||||
[Category(CategoryTypes.Chip.Appearance)]
|
[Category(CategoryTypes.Chip.Appearance)]
|
||||||
public bool Label { get; set; }
|
public bool Label { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the chip will be displayed in disabled state and no events possible.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.Behavior)]
|
|
||||||
public bool Disabled { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the Glyph to use.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.Behavior)]
|
|
||||||
public string Icon { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom checked icon.
|
/// Custom checked icon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -150,6 +217,92 @@ public partial class Chip : UIComponent, IDisposable
|
|||||||
[Category(CategoryTypes.Chip.Appearance)]
|
[Category(CategoryTypes.Chip.Appearance)]
|
||||||
public bool DisableRipple { get; set; }
|
public bool DisableRipple { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Behavior
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set by MudChipSet
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isSelected == value)
|
||||||
|
return;
|
||||||
|
_isSelected = value;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If false, this chip has not been seen before
|
||||||
|
/// </summary>
|
||||||
|
public bool DefaultProcessed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set by MudChipSet
|
||||||
|
/// </summary>
|
||||||
|
public bool IsChecked
|
||||||
|
{
|
||||||
|
get => _isSelected && ChipSet?.Filter == true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, force browser to redirect outside component router-space.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.ClickAction)]
|
||||||
|
public bool ForceLoad { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, this chip is selected by default if used in a ChipSet.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.Behavior)]
|
||||||
|
public bool? Default { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A string you want to associate with the chip. If the ChildContent is not set this will be shown as chip text.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.Behavior)]
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A value that should be managed in the SelectedValues collection.
|
||||||
|
/// Note: do not change the value during the chip's lifetime
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.Behavior)]
|
||||||
|
public object Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Avatar Glyph, Overrides the regular Glyph if set.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.Behavior)]
|
||||||
|
public string Avatar { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the chip will be displayed in disabled state and no events possible.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.Behavior)]
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the Glyph to use.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.Chip.Behavior)]
|
||||||
|
public string Icon { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Child content of component.
|
/// Child content of component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -168,101 +321,9 @@ public partial class Chip : UIComponent, IDisposable
|
|||||||
get => Href;
|
get => Href;
|
||||||
set => Href = value;
|
set => Href = value;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
#region Lifecycle
|
||||||
/// If set to a URL, clicking the button will open the referenced document. Use Target to specify where
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.ClickAction)]
|
|
||||||
public string Href { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The target attribute specifies where to open the link, if Href is specified. Possible values: _blank | _self | _parent | _top | <i>framename</i>
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.ClickAction)]
|
|
||||||
public string Target { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A string you want to associate with the chip. If the ChildContent is not set this will be shown as chip text.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.Behavior)]
|
|
||||||
public string Text { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A value that should be managed in the SelectedValues collection.
|
|
||||||
/// Note: do not change the value during the chip's lifetime
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.Behavior)]
|
|
||||||
public object Value { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, force browser to redirect outside component router-space.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.ClickAction)]
|
|
||||||
public bool ForceLoad { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, this chip is selected by default if used in a ChipSet.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.Behavior)]
|
|
||||||
public bool? Default { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Command executed when the user clicks on an element.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.ClickAction)]
|
|
||||||
public ICommand Command { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Command parameter.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.Chip.ClickAction)]
|
|
||||||
public object CommandParameter { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Chip click event, if set the chip focus, hover and click effects are applied.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Chip delete event, if set the delete icon will be visible.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter] public EventCallback<Chip> OnClose { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set by MudChipSet
|
|
||||||
/// </summary>
|
|
||||||
public bool IsChecked
|
|
||||||
{
|
|
||||||
get => _isSelected && ChipSet?.Filter == true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If false, this chip has not been seen before
|
|
||||||
/// </summary>
|
|
||||||
public bool DefaultProcessed { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set by MudChipSet
|
|
||||||
/// </summary>
|
|
||||||
public bool IsSelected
|
|
||||||
{
|
|
||||||
get => _isSelected;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_isSelected == value)
|
|
||||||
return;
|
|
||||||
_isSelected = value;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
@ -271,53 +332,12 @@ public partial class Chip : UIComponent, IDisposable
|
|||||||
Value = this;
|
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 <a> 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()
|
protected override Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
ChipSet?.Add(this);
|
ChipSet?.Add(this);
|
||||||
return base.OnInitializedAsync();
|
return base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ForceRerender() => StateHasChanged();
|
|
||||||
|
|
||||||
//Exclude because we don't test to catching exception yet
|
//Exclude because we don't test to catching exception yet
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -331,5 +351,5 @@ public partial class Chip : UIComponent, IDisposable
|
|||||||
/* ignore! */
|
/* ignore! */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -6,158 +6,21 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class ChipSet : UIComponent, IDisposable
|
public partial class ChipSet : UIComponent, IDisposable
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
|
private IEqualityComparer<object> _comparer;
|
||||||
|
private HashSet<object> _selectedValues;
|
||||||
|
private HashSet<object> _initialSelectedValues;
|
||||||
|
private HashSet<Chip> _chips = new();
|
||||||
|
private bool _filter;
|
||||||
|
private bool _disposed;
|
||||||
|
#endregion
|
||||||
|
|
||||||
protected string Classname =>
|
#region Events
|
||||||
new CssBuilder("chipset")
|
|
||||||
.AddClass(AdditionalClassList)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Behavior)]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allows to select more than one chip.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Behavior)]
|
|
||||||
public bool MultiSelection { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Will not allow to deselect the selected chip in single selection mode.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Behavior)]
|
|
||||||
public bool Mandatory { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Will make all chips closable.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Behavior)]
|
|
||||||
public bool AllClosable { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Will show a check-mark for the selected components.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Appearance)]
|
|
||||||
public bool Filter
|
|
||||||
{
|
|
||||||
get => _filter;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_filter == value)
|
|
||||||
return;
|
|
||||||
_filter = value;
|
|
||||||
StateHasChanged();
|
|
||||||
foreach (var chip in _chips)
|
|
||||||
chip.ForceRerender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Will make all chips read only.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Behavior)]
|
|
||||||
public bool ReadOnly { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The currently selected chip in Choice mode
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Behavior)]
|
|
||||||
public Chip SelectedChip
|
|
||||||
{
|
|
||||||
get { return _chips.OfType<Chip>().FirstOrDefault(x => x.IsSelected); }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
foreach (var chip in _chips)
|
|
||||||
{
|
|
||||||
chip.IsSelected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var chip in _chips)
|
|
||||||
{
|
|
||||||
chip.IsSelected = (chip == value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the selected chip changes, in Choice mode
|
/// Called when the selected chip changes, in Choice mode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<Chip> SelectedChipChanged { get; set; }
|
public EventCallback<Chip> SelectedChipChanged { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The currently selected chips in Filter mode
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Behavior)]
|
|
||||||
public Chip[] SelectedChips
|
|
||||||
{
|
|
||||||
get { return _chips.OfType<Chip>().Where(x => x.IsSelected).ToArray(); }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null || value.Length == 0)
|
|
||||||
{
|
|
||||||
foreach (var chip in _chips)
|
|
||||||
{
|
|
||||||
chip.IsSelected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var selected = new HashSet<Chip>(value);
|
|
||||||
foreach (var chip in _chips)
|
|
||||||
{
|
|
||||||
chip.IsSelected = selected.Contains(chip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
base.OnInitialized();
|
|
||||||
if (_selectedValues == null)
|
|
||||||
_selectedValues = new HashSet<object>(_comparer);
|
|
||||||
_initialSelectedValues = new HashSet<object>(_selectedValues, _comparer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEqualityComparer<object> _comparer;
|
|
||||||
private HashSet<object> _selectedValues;
|
|
||||||
private HashSet<object> _initialSelectedValues;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The Comparer to use for comparing selected values internally.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.ChipSet.Behavior)]
|
|
||||||
public IEqualityComparer<object> Comparer
|
|
||||||
{
|
|
||||||
get => _comparer;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_comparer = value;
|
|
||||||
// Apply comparer and refresh selected values
|
|
||||||
_selectedValues = new HashSet<object>(_selectedValues, _comparer);
|
|
||||||
SelectedValues = _selectedValues;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the selection changed, in Filter mode
|
/// Called when the selection changed, in Filter mode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -186,7 +49,6 @@ public partial class ChipSet : UIComponent, IDisposable
|
|||||||
/// Called whenever the selection changed
|
/// Called whenever the selection changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public EventCallback<ICollection<object>> SelectedValuesChanged { get; set; }
|
[Parameter] public EventCallback<ICollection<object>> SelectedValuesChanged { get; set; }
|
||||||
|
|
||||||
internal Task SetSelectedValues(object[] values)
|
internal Task SetSelectedValues(object[] values)
|
||||||
{
|
{
|
||||||
HashSet<object> newValues = null;
|
HashSet<object> newValues = null;
|
||||||
@ -217,7 +79,6 @@ public partial class ChipSet : UIComponent, IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<Chip> OnClose { get; set; }
|
public EventCallback<Chip> OnClose { get; set; }
|
||||||
|
|
||||||
internal Task Add(Chip chip)
|
internal Task Add(Chip chip)
|
||||||
{
|
{
|
||||||
_chips.Add(chip);
|
_chips.Add(chip);
|
||||||
@ -257,9 +118,6 @@ public partial class ChipSet : UIComponent, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashSet<Chip> _chips = new();
|
|
||||||
private bool _filter;
|
|
||||||
|
|
||||||
internal Task OnChipClicked(Chip chip)
|
internal Task OnChipClicked(Chip chip)
|
||||||
{
|
{
|
||||||
var wasSelected = chip.IsSelected;
|
var wasSelected = chip.IsSelected;
|
||||||
@ -307,12 +165,7 @@ public partial class ChipSet : UIComponent, IDisposable
|
|||||||
OnClose.InvokeAsync(chip);
|
OnClose.InvokeAsync(chip);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async void OnAfterRender(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
await SelectDefaultChips();
|
|
||||||
base.OnAfterRender(firstRender);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SelectDefaultChips()
|
private async Task SelectDefaultChips()
|
||||||
{
|
{
|
||||||
@ -332,11 +185,165 @@ public partial class ChipSet : UIComponent, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
private bool _disposed;
|
#region Content
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Behavior)]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The currently selected chip in Choice mode
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Behavior)]
|
||||||
|
public Chip SelectedChip
|
||||||
|
{
|
||||||
|
get { return _chips.OfType<Chip>().FirstOrDefault(x => x.IsSelected); }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
foreach (var chip in _chips)
|
||||||
|
{
|
||||||
|
chip.IsSelected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var chip in _chips)
|
||||||
|
{
|
||||||
|
chip.IsSelected = (chip == value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The currently selected chips in Filter mode
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Behavior)]
|
||||||
|
public Chip[] SelectedChips
|
||||||
|
{
|
||||||
|
get { return _chips.OfType<Chip>().Where(x => x.IsSelected).ToArray(); }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null || value.Length == 0)
|
||||||
|
{
|
||||||
|
foreach (var chip in _chips)
|
||||||
|
{
|
||||||
|
chip.IsSelected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var selected = new HashSet<Chip>(value);
|
||||||
|
foreach (var chip in _chips)
|
||||||
|
{
|
||||||
|
chip.IsSelected = selected.Contains(chip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
|
protected string Classname =>
|
||||||
|
new CssBuilder("chipset")
|
||||||
|
.AddClass(AdditionalClassList)
|
||||||
|
.Build();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Behavior
|
||||||
|
/// <summary>
|
||||||
|
/// The Comparer to use for comparing selected values internally.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Behavior)]
|
||||||
|
public IEqualityComparer<object> Comparer
|
||||||
|
{
|
||||||
|
get => _comparer;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_comparer = value;
|
||||||
|
// Apply comparer and refresh selected values
|
||||||
|
_selectedValues = new HashSet<object>(_selectedValues, _comparer);
|
||||||
|
SelectedValues = _selectedValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Will make all chips read only.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Behavior)]
|
||||||
|
public bool ReadOnly { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows to select more than one chip.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Behavior)]
|
||||||
|
public bool MultiSelection { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will not allow to deselect the selected chip in single selection mode.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Behavior)]
|
||||||
|
public bool Mandatory { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will make all chips closable.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Behavior)]
|
||||||
|
public bool AllClosable { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will show a check-mark for the selected components.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.ChipSet.Appearance)]
|
||||||
|
public bool Filter
|
||||||
|
{
|
||||||
|
get => _filter;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_filter == value)
|
||||||
|
return;
|
||||||
|
_filter = value;
|
||||||
|
StateHasChanged();
|
||||||
|
foreach (var chip in _chips)
|
||||||
|
chip.ForceRerender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
if (_selectedValues == null)
|
||||||
|
_selectedValues = new HashSet<object>(_comparer);
|
||||||
|
_initialSelectedValues = new HashSet<object>(_selectedValues, _comparer);
|
||||||
|
}
|
||||||
|
protected override async void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
await SelectDefaultChips();
|
||||||
|
base.OnAfterRender(firstRender);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class Collapse : UIComponent
|
public partial class Collapse : UIComponent
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
internal enum CollapseState
|
internal enum CollapseState
|
||||||
{
|
{
|
||||||
Entering, Entered, Exiting, Exited
|
Entering, Entered, Exiting, Exited
|
||||||
@ -16,7 +17,58 @@ public partial class Collapse : UIComponent
|
|||||||
private bool _expanded, _isRendered, _updateHeight;
|
private bool _expanded, _isRendered, _updateHeight;
|
||||||
private ElementReference _wrapper;
|
private ElementReference _wrapper;
|
||||||
internal CollapseState _state = CollapseState.Exited;
|
internal CollapseState _state = CollapseState.Exited;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
[Parameter] public EventCallback OnAnimationEnd { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public EventCallback<bool> ExpandedChanged { get; 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void AnimationEnd()
|
||||||
|
{
|
||||||
|
if (_state == CollapseState.Entering)
|
||||||
|
{
|
||||||
|
_state = CollapseState.Entered;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
else if (_state == CollapseState.Exiting)
|
||||||
|
{
|
||||||
|
_state = CollapseState.Exited;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
OnAnimationEnd.InvokeAsync(Expanded);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
/// <summary>
|
||||||
|
/// Child content of component.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
protected string Stylename =>
|
protected string Stylename =>
|
||||||
new StyleBuilder()
|
new StyleBuilder()
|
||||||
.AddStyle("max-height", MaxHeight.ToPx(), MaxHeight != null)
|
.AddStyle("max-height", MaxHeight.ToPx(), MaxHeight != null)
|
||||||
@ -66,15 +118,6 @@ public partial class Collapse : UIComponent
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public int? MaxHeight { get; set; }
|
[Parameter] public int? MaxHeight { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Child content of component.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
[Parameter] public EventCallback OnAnimationEnd { get; set; }
|
|
||||||
|
|
||||||
[Parameter] public EventCallback<bool> ExpandedChanged { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Modified Animation duration that scales with height parameter.
|
/// 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.
|
/// 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 { }
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task UpdateHeight()
|
#endregion
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
@ -127,19 +156,6 @@ public partial class Collapse : UIComponent
|
|||||||
}
|
}
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -13,16 +13,7 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class ColorPicker : Picker<Color>, IAsyncDisposable
|
public partial class ColorPicker : Picker<Color>, IAsyncDisposable
|
||||||
{
|
{
|
||||||
public ColorPicker() : base(new DefaultConverter<Color>())
|
#region Variables
|
||||||
{
|
|
||||||
AdornmentIcon = Icons.Material.Outlined.Palette;
|
|
||||||
DisableToolbar = true;
|
|
||||||
Value = "#594ae2"; //MudBlazor Blue
|
|
||||||
Text = GetColorTextValue();
|
|
||||||
AdornmentAriaLabel = "Open Color Picker";
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Fields
|
|
||||||
|
|
||||||
private static Dictionary<int, (Func<int, int> r, Func<int, int> g, Func<int, int> b, string dominantColorPart)> _rgbToHueMapper = new()
|
private static Dictionary<int, (Func<int, int> r, Func<int, int> g, Func<int, int> b, string dominantColorPart)> _rgbToHueMapper = new()
|
||||||
{
|
{
|
||||||
@ -50,150 +41,12 @@ public partial class ColorPicker : Picker<Color>, IAsyncDisposable
|
|||||||
private readonly Guid _id = Guid.NewGuid();
|
private readonly Guid _id = Guid.NewGuid();
|
||||||
private Guid _throttledMouseOverEventId;
|
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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, Alpha options will not be displayed and color output will be RGB, HSL or HEX and not RGBA, HSLA or HEXA.
|
|
||||||
/// </summary>
|
|
||||||
[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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the color field will not be displayed.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public bool DisableColorField { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the switch to change color mode will not be displayed.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public bool DisableModeSwitch { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, textfield inputs and color mode switch will not be displayed.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public bool DisableInputs { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, hue and alpha sliders will not be displayed.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public bool DisableSliders { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true, the preview color box will not be displayed, note that the preview color functions as a button as well for collection colors.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public bool DisablePreview { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The initial mode (RGB, HSL or HEX) the picker should open. Defaults to RGB
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public ColorPickerMode ColorPickerMode { get; set; } = ColorPickerMode.RGB;
|
|
||||||
|
|
||||||
private ColorPickerView _colorPickerView = ColorPickerView.Spectrum;
|
private ColorPickerView _colorPickerView = ColorPickerView.Spectrum;
|
||||||
private ColorPickerView _activeColorPickerView = ColorPickerView.Spectrum;
|
private ColorPickerView _activeColorPickerView = ColorPickerView.Spectrum;
|
||||||
|
|
||||||
/// <summary>
|
private bool _disableAlpha = false;
|
||||||
/// The initial view of the picker. Views can be changed if toolbar is enabled.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public ColorPickerView ColorPickerView
|
|
||||||
{
|
|
||||||
get => _colorPickerView;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != _colorPickerView)
|
|
||||||
{
|
|
||||||
_colorPickerView = value;
|
|
||||||
ChangeView(value).AndForget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
private IEventListener _throttledEventManager;
|
||||||
/// If true, binding changes occurred also when HSL values changed without a corresponding RGB change
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Behavior)]
|
|
||||||
public bool UpdateBindingIfOnlyHSLChanged { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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
|
|
||||||
/// </summary>
|
|
||||||
[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<Color> ValueChanged { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MudColor list of predefined colors. The first five colors will show up as the quick colors on preview dot click.
|
/// MudColor list of predefined colors. The first five colors will show up as the quick colors on preview dot click.
|
||||||
@ -224,52 +77,140 @@ public partial class ColorPicker : Picker<Color>, IAsyncDisposable
|
|||||||
"#d2effd","#d6e1fc","#d6c9fa","#e9cbfb","#f3d4df","#f9dcd9","#fae3d8","#fcecd7","#fdf2d8","#fefce0","#f7fade","#e3edd6"
|
"#d2effd","#d6e1fc","#d6c9fa","#e9cbfb","#f3d4df","#f9dcd9","#fae3d8","#fcecd7","#fdf2d8","#fefce0","#f7fade","#e3edd6"
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
[Inject] IEventListenerFactory ThrottledEventManagerFactory { get; set; }
|
||||||
/// 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
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public bool DisableDragEffect { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom close icon.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
|
||||||
public string CloseIcon { get; set; } = Icons.Material.Filled.Close;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom spectrum icon.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
|
||||||
public string SpectrumIcon { get; set; } = Icons.Material.Filled.Tune;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom grid icon.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
|
||||||
public string GridIcon { get; set; } = Icons.Material.Filled.Apps;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom palette icon.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
|
||||||
public string PaletteIcon { get; set; } = Icons.Material.Filled.Palette;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom import/export icont.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
|
||||||
public string ImportExportIcon { get; set; } = Icons.Material.Filled.ImportExport;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
private EventCallback<MouseEventArgs> GetEventCallback() => EventCallback.Factory.Create<MouseEventArgs>(this, () => Close());
|
||||||
|
private EventCallback<MouseEventArgs> GetSelectPaletteColorCallback(Color color) => new EventCallbackFactory().Create(this, (MouseEventArgs e) => SelectPaletteColor(color));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the R (red) component of the color picker
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value between 0 (no red) or 255 (max red)</param>
|
||||||
|
public void SetR(int value) => Value = Value.SetR(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the G (green) component of the color picker
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value between 0 (no green) or 255 (max green)</param>
|
||||||
|
public void SetG(int value) => Value = Value.SetG(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the B (blue) component of the color picker
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value between 0 (no blue) or 255 (max blue)</param>
|
||||||
|
public void SetB(int value) => Value = Value.SetB(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the H (hue) component of the color picker
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value between 0 and 360 (degrees)</param>
|
||||||
|
public void SetH(double value) => Value = Value.SetH(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the S (saturation) component of the color picker
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value between 0.0 (no saturation) and 1.0 (max saturation)</param>
|
||||||
|
public void SetS(double value) => Value = Value.SetS(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the L (Lightness) component of the color picker
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value between 0.0 (no light, black) and 1.0 (max light, white)</param>
|
||||||
|
public void SetL(double value) => Value = Value.SetL(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the Alpha (transparency) component of the color picker
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value between 0.0 (full transparent) and 1.0 (solid) </param>
|
||||||
|
public void SetAlpha(double value) => Value = Value.SetAlpha(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the Alpha (transparency) component of the color picker
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A value between 0 (full transparent) and 1 (solid) </param>
|
||||||
|
public void SetAlpha(int value) => Value = Value.SetAlpha(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the color of the picker based on the string input
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Accepting different formats for a color representation such as rbg, rgba, #</param>
|
||||||
|
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<Color> ValueChanged { get; set; }
|
||||||
|
|
||||||
private void ToggleCollection()
|
private void ToggleCollection()
|
||||||
{
|
{
|
||||||
_collectionOpen = !_collectionOpen;
|
_collectionOpen = !_collectionOpen;
|
||||||
@ -391,156 +332,210 @@ public partial class ColorPicker : Picker<Color>, IAsyncDisposable
|
|||||||
_selectorX = relation * _maxX;
|
_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
|
#endregion
|
||||||
|
|
||||||
#region updating inputs
|
#region Styling
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the R (red) component of the color picker
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">A value between 0 (no red) or 255 (max red)</param>
|
|
||||||
public void SetR(int value) => Value = Value.SetR(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the G (green) component of the color picker
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">A value between 0 (no green) or 255 (max green)</param>
|
|
||||||
public void SetG(int value) => Value = Value.SetG(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the B (blue) component of the color picker
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">A value between 0 (no blue) or 255 (max blue)</param>
|
|
||||||
public void SetB(int value) => Value = Value.SetB(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the H (hue) component of the color picker
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">A value between 0 and 360 (degrees)</param>
|
|
||||||
public void SetH(double value) => Value = Value.SetH(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the S (saturation) component of the color picker
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">A value between 0.0 (no saturation) and 1.0 (max saturation)</param>
|
|
||||||
public void SetS(double value) => Value = Value.SetS(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the L (Lightness) component of the color picker
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">A value between 0.0 (no light, black) and 1.0 (max light, white)</param>
|
|
||||||
public void SetL(double value) => Value = Value.SetL(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the Alpha (transparency) component of the color picker
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">A value between 0.0 (full transparent) and 1.0 (solid) </param>
|
|
||||||
public void SetAlpha(double value) => Value = Value.SetAlpha(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the Alpha (transparency) component of the color picker
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">A value between 0 (full transparent) and 1 (solid) </param>
|
|
||||||
public void SetAlpha(int value) => Value = Value.SetAlpha(value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the color of the picker based on the string input
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input">Accepting different formats for a color representation such as rbg, rgba, #</param>
|
|
||||||
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 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 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 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 int GetHexColorInputMaxLength() => DisableAlpha ? 7 : 9;
|
||||||
|
|
||||||
private EventCallback<MouseEventArgs> GetEventCallback() => EventCallback.Factory.Create<MouseEventArgs>(this, () => Close());
|
/// <summary>
|
||||||
private bool IsAnyControlVisible() => !(DisablePreview && DisableSliders && DisableInputs);
|
/// When set to true, no mouse move events in the spectrum mode will be captured, so the selector circle won't fellow the mouse.
|
||||||
private EventCallback<MouseEventArgs> GetSelectPaletteColorCallback(Color color) => new EventCallbackFactory().Create(this, (MouseEventArgs e) => SelectPaletteColor(color));
|
/// Under some conditions like long latency the visual representation might not reflect the user behaviour anymore. So, it can be disabled
|
||||||
|
/// Enabled by default
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public bool DisableDragEffect { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom close icon.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
||||||
|
public string CloseIcon { get; set; } = Icons.Material.Filled.Close;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom spectrum icon.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
||||||
|
public string SpectrumIcon { get; set; } = Icons.Material.Filled.Tune;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom grid icon.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
||||||
|
public string GridIcon { get; set; } = Icons.Material.Filled.Apps;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom palette icon.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
||||||
|
public string PaletteIcon { get; set; } = Icons.Material.Filled.Palette;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom import/export icont.
|
||||||
|
/// </summary>
|
||||||
|
[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; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, Alpha options will not be displayed and color output will be RGB, HSL or HEX and not RGBA, HSLA or HEXA.
|
||||||
|
/// </summary>
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the color field will not be displayed.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public bool DisableColorField { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the switch to change color mode will not be displayed.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public bool DisableModeSwitch { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, textfield inputs and color mode switch will not be displayed.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public bool DisableInputs { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, hue and alpha sliders will not be displayed.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public bool DisableSliders { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the preview color box will not be displayed, note that the preview color functions as a button as well for collection colors.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public bool DisablePreview { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The initial mode (RGB, HSL or HEX) the picker should open. Defaults to RGB
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public ColorPickerMode ColorPickerMode { get; set; } = ColorPickerMode.RGB;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The initial view of the picker. Views can be changed if toolbar is enabled.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public ColorPickerView ColorPickerView
|
||||||
|
{
|
||||||
|
get => _colorPickerView;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != _colorPickerView)
|
||||||
|
{
|
||||||
|
_colorPickerView = value;
|
||||||
|
ChangeView(value).AndForget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, binding changes occurred also when HSL values changed without a corresponding RGB change
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Behavior)]
|
||||||
|
public bool UpdateBindingIfOnlyHSLChanged { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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
|
||||||
|
/// </summary>
|
||||||
|
[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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
|
#endregion
|
||||||
|
|
||||||
#region life cycle hooks
|
#region Lifecycle
|
||||||
|
|
||||||
|
public ColorPicker() : base(new DefaultConverter<Color>())
|
||||||
|
{
|
||||||
|
AdornmentIcon = Icons.Material.Outlined.Palette;
|
||||||
|
DisableToolbar = true;
|
||||||
|
Value = "#594ae2"; //MudBlazor Blue
|
||||||
|
Text = GetColorTextValue();
|
||||||
|
AdornmentAriaLabel = "Open Color Picker";
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
@ -594,4 +589,5 @@ public partial class ColorPicker : Picker<Color>, IAsyncDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,6 @@ namespace Connected.Components;
|
|||||||
public partial class Container : UIComponent
|
public partial class Container : UIComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
#region Event callbacks
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Content placeholders
|
#region Content placeholders
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -8,223 +8,100 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public abstract partial class DatePickerBase : Picker<DateTime?>
|
public abstract partial class DatePickerBase : Picker<DateTime?>
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
private bool _dateFormatTouched;
|
private bool _dateFormatTouched;
|
||||||
|
|
||||||
protected DatePickerBase() : base(new DefaultConverter<DateTime?>
|
|
||||||
{
|
|
||||||
Format = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern,
|
|
||||||
Culture = CultureInfo.CurrentCulture
|
|
||||||
})
|
|
||||||
{
|
|
||||||
AdornmentAriaLabel = "Open Date Picker";
|
|
||||||
}
|
|
||||||
|
|
||||||
[Inject] protected IScrollManager ScrollManager { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Max selectable date.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Validation)]
|
|
||||||
public DateTime? MaxDate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Min selectable date.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Validation)]
|
|
||||||
public DateTime? MinDate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// First view to show in the MudDatePicker.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public OpenTo OpenTo { get; set; } = OpenTo.Date;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// String Format for selected date view
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Behavior)]
|
|
||||||
public string DateFormat
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (Converter as DefaultConverter<DateTime?>)?.Format;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Converter is DefaultConverter<DateTime?> defaultConverter)
|
|
||||||
{
|
|
||||||
defaultConverter.Format = value;
|
|
||||||
_dateFormatTouched = true;
|
|
||||||
}
|
|
||||||
DateFormatChanged(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Date format value change hook for descendants.
|
|
||||||
/// </summary>
|
|
||||||
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<DateTime?> defaultConverter)
|
|
||||||
defaultConverter.Format = value.DateTimeFormat.ShortDatePattern;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines on which day the week starts. Depends on the value of Culture.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public DayOfWeek? FirstDayOfWeek { get; set; } = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public DateTime? PickerMonth
|
|
||||||
{
|
|
||||||
get => _picker_month;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == _picker_month)
|
|
||||||
return;
|
|
||||||
_picker_month = value;
|
|
||||||
InvokeAsync(StateHasChanged);
|
|
||||||
PickerMonthChanged.InvokeAsync(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private DateTime? _picker_month;
|
private DateTime? _picker_month;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fired when the date changes.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter] public EventCallback<DateTime?> PickerMonthChanged { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public int ClosingDelay { get; set; } = 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Number of months to display in the calendar
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public int DisplayMonths { get; set; } = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maximum number of months in one row
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
|
||||||
public int? MaxMonthColumns { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start month when opening the picker.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public DateTime? StartMonth { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Display week numbers according to the Culture parameter. If no culture is defined, CultureInfo.CurrentCulture will be used.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public bool ShowWeekNumbers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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".
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public string TitleDateFormat { get; set; } = "ddd, dd MMM";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If AutoClose is set to true and PickerActions are defined, selecting a day will close the MudDatePicker.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public bool AutoClose { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Function to determine whether a date is disabled
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Validation)]
|
|
||||||
public Func<DateTime, bool> IsDateDisabledFunc
|
|
||||||
{
|
|
||||||
get => _isDateDisabledFunc;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_isDateDisabledFunc = value ?? (_ => false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private Func<DateTime, bool> _isDateDisabledFunc = _ => false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Function to conditionally apply new classes to specific days
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.Appearance)]
|
|
||||||
public Func<DateTime, string> AdditionalDateClassesFunc { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom previous icon.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
|
||||||
public string PreviousIcon { get; set; } = Icons.Material.Filled.ChevronLeft;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom next icon.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
|
||||||
public string NextIcon { get; set; } = Icons.Material.Filled.ChevronRight;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set a predefined fix year - no year can be selected
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public int? FixYear { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Set a predefined fix month - no month can be selected
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public int? FixMonth { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Set a predefined fix day - no day can be selected
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
|
||||||
public int? FixDay { get; set; }
|
|
||||||
|
|
||||||
protected virtual bool IsRange { get; } = false;
|
protected virtual bool IsRange { get; } = false;
|
||||||
|
|
||||||
protected OpenTo CurrentView;
|
protected OpenTo CurrentView;
|
||||||
|
/// <summary>
|
||||||
|
/// We need a random id for the year items in the year list so we can scroll to the item safely in every DatePicker.
|
||||||
|
/// </summary>
|
||||||
|
private string _componentId = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is set to true to scroll to the actual year after the next render
|
||||||
|
/// </summary>
|
||||||
|
private bool _scrollToYearAfterRender = false;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
private Typo GetMonthTypo(DateTime month)
|
||||||
|
{
|
||||||
|
if (GetMonthStart(0) == month)
|
||||||
|
return Typo.h5;
|
||||||
|
return Typo.subtitle1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract DateTime GetCalendarStartOfMonth();
|
||||||
|
|
||||||
|
private int GetCalendarDayOfMonth(DateTime date)
|
||||||
|
{
|
||||||
|
return Culture.Calendar.GetDayOfMonth(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts gregorian year into whatever year it is in the provided culture
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="year">Gregorian year</param>
|
||||||
|
/// <returns>Year according to culture</returns>
|
||||||
|
protected abstract int GetCalendarYear(int year);
|
||||||
|
public async void ScrollToYear()
|
||||||
|
{
|
||||||
|
_scrollToYearAfterRender = false;
|
||||||
|
var id = $"{_componentId}{GetMonthStart(0).Year}";
|
||||||
|
await ScrollManager.ScrollToYearAsync(id);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetMinYear()
|
||||||
|
{
|
||||||
|
if (MinDate.HasValue)
|
||||||
|
return MinDate.Value.Year;
|
||||||
|
return DateTime.Today.Year - 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetMaxYear()
|
||||||
|
{
|
||||||
|
if (MaxDate.HasValue)
|
||||||
|
return MaxDate.Value.Year;
|
||||||
|
return DateTime.Today.Year + 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private Typo GetYearTypo(int year)
|
||||||
|
{
|
||||||
|
if (year == GetMonthStart(0).Year)
|
||||||
|
return Typo.h5;
|
||||||
|
return Typo.subtitle1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFormattedDateClick()
|
||||||
|
{
|
||||||
|
// todo: raise an event the user can handle
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private IEnumerable<DateTime> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
protected override void OnPickerOpened()
|
||||||
{
|
{
|
||||||
base.OnPickerOpened();
|
base.OnPickerOpened();
|
||||||
@ -461,39 +338,73 @@ public abstract partial class DatePickerBase : Picker<DateTime?>
|
|||||||
_scrollToYearAfterRender = true;
|
_scrollToYearAfterRender = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Date format value change hook for descendants.
|
||||||
|
/// </summary>
|
||||||
|
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<DateTime?> defaultConverter)
|
||||||
|
defaultConverter.Format = value.DateTimeFormat.ShortDatePattern;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when the date changes.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<DateTime?> PickerMonthChanged { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
[Inject] protected IScrollManager ScrollManager { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Defines on which day the week starts. Depends on the value of Culture.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public DayOfWeek? FirstDayOfWeek { get; set; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// We need a random id for the year items in the year list so we can scroll to the item safely in every DatePicker.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string _componentId = Guid.NewGuid().ToString();
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public DateTime? PickerMonth
|
||||||
|
{
|
||||||
|
get => _picker_month;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _picker_month)
|
||||||
|
return;
|
||||||
|
_picker_month = value;
|
||||||
|
InvokeAsync(StateHasChanged);
|
||||||
|
PickerMonthChanged.InvokeAsync(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Styling
|
||||||
|
private string GetMonthClasses(DateTime month)
|
||||||
|
{
|
||||||
|
if (GetMonthStart(0) == month)
|
||||||
|
return $"picker-month-selected mud-{Color.ToDescription()}-text";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is set to true to scroll to the actual year after the next render
|
/// Function to conditionally apply new classes to specific days
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool _scrollToYearAfterRender = false;
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Appearance)]
|
||||||
public async void ScrollToYear()
|
public Func<DateTime, string> AdditionalDateClassesFunc { get; set; }
|
||||||
{
|
|
||||||
_scrollToYearAfterRender = false;
|
|
||||||
var id = $"{_componentId}{GetMonthStart(0).Year}";
|
|
||||||
await ScrollManager.ScrollToYearAsync(id);
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetMinYear()
|
|
||||||
{
|
|
||||||
if (MinDate.HasValue)
|
|
||||||
return MinDate.Value.Year;
|
|
||||||
return DateTime.Today.Year - 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetMaxYear()
|
|
||||||
{
|
|
||||||
if (MaxDate.HasValue)
|
|
||||||
return MaxDate.Value.Year;
|
|
||||||
return DateTime.Today.Year + 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetYearClasses(int year)
|
private string GetYearClasses(int year)
|
||||||
{
|
{
|
||||||
if (year == GetMonthStart(0).Year)
|
if (year == GetMonthStart(0).Year)
|
||||||
@ -508,57 +419,162 @@ public abstract partial class DatePickerBase : Picker<DateTime?>
|
|||||||
.AddClass($"picker-calendar-header-last", month == DisplayMonths - 1)
|
.AddClass($"picker-calendar-header-last", month == DisplayMonths - 1)
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Custom previous icon.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
||||||
|
public string PreviousIcon { get; set; } = Icons.Material.Filled.ChevronLeft;
|
||||||
|
|
||||||
private Typo GetYearTypo(int year)
|
/// <summary>
|
||||||
|
/// Custom next icon.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
||||||
|
public string NextIcon { get; set; } = Icons.Material.Filled.ChevronRight;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String Format for selected date view
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Behavior)]
|
||||||
|
public string DateFormat
|
||||||
{
|
{
|
||||||
if (year == GetMonthStart(0).Year)
|
get
|
||||||
return Typo.h5;
|
|
||||||
return Typo.subtitle1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnFormattedDateClick()
|
|
||||||
{
|
{
|
||||||
// todo: raise an event the user can handle
|
return (Converter as DefaultConverter<DateTime?>)?.Format;
|
||||||
}
|
}
|
||||||
|
set
|
||||||
|
|
||||||
private IEnumerable<DateTime> GetAllMonths()
|
|
||||||
{
|
{
|
||||||
var current = GetMonthStart(0);
|
if (Converter is DefaultConverter<DateTime?> defaultConverter)
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetAbbreviatedMonthName(DateTime month)
|
|
||||||
{
|
{
|
||||||
var calendarMonth = Culture.Calendar.GetMonth(month);
|
defaultConverter.Format = value;
|
||||||
return Culture.DateTimeFormat.AbbreviatedMonthNames[calendarMonth - 1];
|
_dateFormatTouched = true;
|
||||||
}
|
}
|
||||||
|
DateFormatChanged(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
private string GetMonthName(DateTime month)
|
#region Behavior
|
||||||
|
/// <summary>
|
||||||
|
/// Max selectable date.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Validation)]
|
||||||
|
public DateTime? MaxDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Min selectable date.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Validation)]
|
||||||
|
public DateTime? MinDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// First view to show in the DatePicker.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public OpenTo OpenTo { get; set; } = OpenTo.Date;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a predefined fix year - no year can be selected
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public int? FixYear { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set a predefined fix month - no month can be selected
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public int? FixMonth { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Set a predefined fix day - no day can be selected
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public int? FixDay { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public int ClosingDelay { get; set; } = 100;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of months to display in the calendar
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public int DisplayMonths { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum number of months in one row
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerAppearance)]
|
||||||
|
public int? MaxMonthColumns { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start month when opening the picker.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public DateTime? StartMonth { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Display week numbers according to the Culture parameter. If no culture is defined, CultureInfo.CurrentCulture will be used.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public bool ShowWeekNumbers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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".
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public string TitleDateFormat { get; set; } = "ddd, dd MMM";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If AutoClose is set to true and PickerActions are defined, selecting a day will close the MudDatePicker.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.PickerBehavior)]
|
||||||
|
public bool AutoClose { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Function to determine whether a date is disabled
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[Category(CategoryTypes.FormComponent.Validation)]
|
||||||
|
public Func<DateTime, bool> IsDateDisabledFunc
|
||||||
{
|
{
|
||||||
var calendarMonth = Culture.Calendar.GetMonth(month);
|
get => _isDateDisabledFunc;
|
||||||
return Culture.DateTimeFormat.MonthNames[calendarMonth - 1];
|
set
|
||||||
}
|
|
||||||
|
|
||||||
private string GetMonthClasses(DateTime month)
|
|
||||||
{
|
{
|
||||||
if (GetMonthStart(0) == month)
|
_isDateDisabledFunc = value ?? (_ => false);
|
||||||
return $"picker-month-selected mud-{Color.ToDescription()}-text";
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
private Func<DateTime, bool> _isDateDisabledFunc = _ => false;
|
||||||
|
#endregion
|
||||||
|
|
||||||
private Typo GetMonthTypo(DateTime month)
|
#region Lifecycle
|
||||||
|
|
||||||
|
protected DatePickerBase() : base(new DefaultConverter<DateTime?>
|
||||||
{
|
{
|
||||||
if (GetMonthStart(0) == month)
|
Format = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern,
|
||||||
return Typo.h5;
|
Culture = CultureInfo.CurrentCulture
|
||||||
return Typo.subtitle1;
|
})
|
||||||
|
{
|
||||||
|
AdornmentAriaLabel = "Open Date Picker";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
base.OnInitialized();
|
base.OnInitialized();
|
||||||
@ -584,17 +600,6 @@ public abstract partial class DatePickerBase : Picker<DateTime?>
|
|||||||
ScrollToYear();
|
ScrollToYear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract DateTime GetCalendarStartOfMonth();
|
#endregion
|
||||||
|
|
||||||
private int GetCalendarDayOfMonth(DateTime date)
|
|
||||||
{
|
|
||||||
return Culture.Calendar.GetDayOfMonth(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts gregorian year into whatever year it is in the provided culture
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="year">Gregorian year</param>
|
|
||||||
/// <returns>Year according to culture</returns>
|
|
||||||
protected abstract int GetCalendarYear(int year);
|
|
||||||
}
|
}
|
||||||
|
@ -7,23 +7,12 @@ namespace Connected.Components;
|
|||||||
|
|
||||||
public partial class DateRangePicker : DatePickerBase
|
public partial class DateRangePicker : DatePickerBase
|
||||||
{
|
{
|
||||||
|
#region Variables
|
||||||
private DateTime? _firstDate = null, _secondDate;
|
private DateTime? _firstDate = null, _secondDate;
|
||||||
private DateRange _dateRange;
|
private DateRange _dateRange;
|
||||||
private Range<string> _rangeText;
|
private Range<string> _rangeText;
|
||||||
|
|
||||||
protected override bool IsRange => true;
|
protected override bool IsRange => true;
|
||||||
|
private RangeInput<string> _rangeInput;
|
||||||
public DateRangePicker()
|
|
||||||
{
|
|
||||||
DisplayMonths = 2;
|
|
||||||
AdornmentAriaLabel = "Open Date Range Picker";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fired when the DateFormat changes.
|
|
||||||
/// </summary>
|
|
||||||
[Parameter] public EventCallback<DateRange> DateRangeChanged { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently selected range (two-way bindable). If null, then nothing was selected.
|
/// The currently selected range (two-way bindable). If null, then nothing was selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -34,7 +23,73 @@ public partial class DateRangePicker : DatePickerBase
|
|||||||
get => _dateRange;
|
get => _dateRange;
|
||||||
set => SetDateRangeAsync(value, true).AndForget();
|
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);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when the DateFormat changes.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<DateRange> DateRangeChanged { get; set; }
|
||||||
protected async Task SetDateRangeAsync(DateRange range, bool updateValue)
|
protected async Task SetDateRangeAsync(DateRange range, bool updateValue)
|
||||||
{
|
{
|
||||||
if (_dateRange != range)
|
if (_dateRange != range)
|
||||||
@ -75,6 +130,9 @@ public partial class DateRangePicker : DatePickerBase
|
|||||||
BeginValidate();
|
BeginValidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Content
|
||||||
|
|
||||||
private Range<string> RangeText
|
private Range<string> RangeText
|
||||||
{
|
{
|
||||||
@ -90,82 +148,9 @@ public partial class DateRangePicker : DatePickerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RangeInput<string> _rangeInput;
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Focuses the start date of MudDateRangePicker
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public ValueTask FocusStartAsync() => _rangeInput.FocusStartAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Selects the start date of MudDateRangePicker
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public ValueTask SelectStartAsync() => _rangeInput.SelectStartAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Selects the specified range of the start date text
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pos1">Start position of the selection</param>
|
|
||||||
/// <param name="pos2">End position of the selection</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public ValueTask SelectRangeStartAsync(int pos1, int pos2) => _rangeInput.SelectRangeStartAsync(pos1, pos2);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Focuses the end date of MudDateRangePicker
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public ValueTask FocusEndAsync() => _rangeInput.FocusEndAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Selects the end date of MudDateRangePicker
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public ValueTask SelectEndAsync() => _rangeInput.SelectEndAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Selects the specified range of the end date text
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pos1">Start position of the selection</param>
|
|
||||||
/// <param name="pos2">End position of the selection</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#region Styling
|
||||||
protected override string GetDayClasses(int month, DateTime day)
|
protected override string GetDayClasses(int month, DateTime day)
|
||||||
{
|
{
|
||||||
var b = new CssBuilder("day");
|
var b = new CssBuilder("day");
|
||||||
@ -228,54 +213,9 @@ public partial class DateRangePicker : DatePickerBase
|
|||||||
|
|
||||||
return b.Build();
|
return b.Build();
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
protected override async void OnDayClicked(DateTime dateTime)
|
#region Behavior
|
||||||
{
|
|
||||||
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()
|
protected override string GetTitleDateString()
|
||||||
{
|
{
|
||||||
@ -300,4 +240,72 @@ public partial class DateRangePicker : DatePickerBase
|
|||||||
var calenderYear = Culture.Calendar.GetYear(date);
|
var calenderYear = Culture.Calendar.GetYear(date);
|
||||||
return calenderYear - diff;
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Focuses the start date of MudDateRangePicker
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ValueTask FocusStartAsync() => _rangeInput.FocusStartAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects the start date of MudDateRangePicker
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ValueTask SelectStartAsync() => _rangeInput.SelectStartAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects the specified range of the start date text
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pos1">Start position of the selection</param>
|
||||||
|
/// <param name="pos2">End position of the selection</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ValueTask SelectRangeStartAsync(int pos1, int pos2) => _rangeInput.SelectRangeStartAsync(pos1, pos2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Focuses the end date of MudDateRangePicker
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ValueTask FocusEndAsync() => _rangeInput.FocusEndAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects the end date of MudDateRangePicker
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ValueTask SelectEndAsync() => _rangeInput.SelectEndAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects the specified range of the end date text
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pos1">Start position of the selection</param>
|
||||||
|
/// <param name="pos2">End position of the selection</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
Variant="@Variant"
|
Variant="@Variant"
|
||||||
HelperText="@HelperText"
|
HelperText="@HelperText"
|
||||||
HelperTextOnFocus="@HelperTextOnFocus"
|
HelperTextOnFocus="@HelperTextOnFocus"
|
||||||
CounterText="@CounterText"
|
CounterText="@GetCounterText()"
|
||||||
FullWidth="@FullWidth"
|
FullWidth="@FullWidth"
|
||||||
class="@CompiledHelperContainerClassList.Build()"
|
class="@CompiledHelperContainerClassList.Build()"
|
||||||
Error="@HasErrors"
|
Error="@HasErrors"
|
||||||
|
@ -198,6 +198,11 @@ public partial class Input<T> : InputBase<T>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public bool HideSpinButtons { get; set; } = true;
|
[Parameter] public bool HideSpinButtons { get; set; } = true;
|
||||||
|
|
||||||
|
internal override InputType GetInputType()
|
||||||
|
{
|
||||||
|
return InputType;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Show clear button.
|
/// Show clear button.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -230,7 +235,7 @@ public partial class Input<T> : InputBase<T>
|
|||||||
#region Input field class
|
#region Input field class
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string InputClass { get; set; } = string.Empty;
|
public string Class { get; set; } = string.Empty;
|
||||||
protected CssBuilder CompiledInputClass
|
protected CssBuilder CompiledInputClass
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -240,7 +245,7 @@ public partial class Input<T> : InputBase<T>
|
|||||||
.AddClass($"input-root-{Variant.ToDescription()}")
|
.AddClass($"input-root-{Variant.ToDescription()}")
|
||||||
.AddClass($"input-root-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
|
.AddClass($"input-root-adorned-{Adornment.ToDescription()}", Adornment != Adornment.None)
|
||||||
.AddClass($"input-root-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
|
.AddClass($"input-root-margin-{Margin.ToDescription()}", when: () => Margin != Margin.None)
|
||||||
.AddClass(InputClass);
|
.AddClass(Class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@ -315,7 +320,17 @@ public partial class Input<T> : InputBase<T>
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current character counter, displayed below the text field.
|
/// The current character counter, displayed below the text field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public string CounterText { get; set; }
|
[Parameter] public bool ShowCharacterCounter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current character counter, displayed below the text field.
|
||||||
|
/// </summary>
|
||||||
|
public string GetCounterText()
|
||||||
|
{
|
||||||
|
if (ShowCharacterCounter)
|
||||||
|
return Text.Length.ToString();
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private System.Timers.Timer _timer;
|
private System.Timers.Timer _timer;
|
||||||
|
|
||||||
@ -381,7 +396,7 @@ public partial class Input<T> : InputBase<T>
|
|||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public double Step { get; set; } = 1;
|
public double Step { get; set; } = 1;
|
||||||
internal override InputType GetInputType() => InputType;
|
//internal override InputType GetInputType() => InputType;
|
||||||
protected string InputTypeString => InputType.ToDescription();
|
protected string InputTypeString => InputType.ToDescription();
|
||||||
public ElementReference ElementReference { get; private set; }
|
public ElementReference ElementReference { get; private set; }
|
||||||
private ElementReference _elementReference1;
|
private ElementReference _elementReference1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user