You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Components/Components/Carousel/Carousel.razor.cs

350 lines
9.1 KiB

2 years ago
using System.Diagnostics.CodeAnalysis;
using Connected.Annotations;
using Connected.Extensions;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TData>, IAsyncDisposable
{
protected string Classname =>
new CssBuilder("mud-carousel")
.AddClass($"mud-carousel-{(BulletsColor ?? _currentColor).ToDescriptionString()}")
.AddClass(Class)
.Build();
protected string NavigationButtonsClassName =>
new CssBuilder()
.AddClass($"align-self-{ConvertPosition(ArrowsPosition).ToDescriptionString()}", !(NavigationButtonsClass ?? "").Contains("align-self-"))
.AddClass("mud-carousel-elements-rtl", RightToLeft)
.AddClass(NavigationButtonsClass)
.Build();
protected string BulletsButtonsClassName =>
new CssBuilder()
.AddClass(BulletsClass)
.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)
{
return position switch
{
Position.Top => Position.Start,
Position.Start => Position.Start,
Position.Bottom => Position.End,
Position.End => Position.End,
_ => position
};
}
[CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }
/// <summary>
/// Gets or Sets if 'Next' and 'Previous' arrows must be visible
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Behavior)]
public bool ShowArrows { get; set; } = true;
/// <summary>
/// Sets the position of the arrows. By default, the position is the Center position
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Appearance)]
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>
/// Sets the position of the bullets. By default, the position is the Bottom position
/// </summary>
[Category(CategoryTypes.Carousel.Appearance)]
[Parameter] public Position BulletsPosition { get; set; } = Position.Bottom;
/// <summary>
/// Gets or Sets the Bullets color.
/// If not set, the color is determined based on the <see cref="MudCarouselItem.Color"/> property of the active child.
/// </summary>
[Category(CategoryTypes.Carousel.Appearance)]
[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>
/// 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.
/// Deprecated, use BulletsColor instead.
/// </summary>
[Obsolete($"Use {nameof(BulletsColor)} instead", false)]
[Category(CategoryTypes.Carousel.Appearance)]
[ExcludeFromCodeCoverage]
[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>
/// Gets or Sets custom class(es) for 'Next' and 'Previous' arrows
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Appearance)]
public string NavigationButtonsClass { get; set; }
/// <summary>
/// Gets or Sets custom class(es) for Bullets buttons
/// </summary>
[Category(CategoryTypes.Carousel.Appearance)]
[Parameter] public string BulletsClass { get; set; }
/// <summary>
/// Gets or Sets custom class(es) for Delimiters buttons.
/// Deprecated, use BulletsClass instead.
/// </summary>
[Category(CategoryTypes.Carousel.Appearance)]
[Obsolete($"Use {nameof(BulletsClass)} instead", false)]
[ExcludeFromCodeCoverage]
[Parameter] public string DelimitersClass { get => BulletsClass; set => BulletsClass = value; }
/// <summary>
/// Custom previous navigation icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Appearance)]
public string PreviousIcon { get; set; } = Icons.Material.Filled.NavigateBefore;
/// <summary>
/// Custom selected bullet icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Appearance)]
public string CheckedIcon { get; set; } = Icons.Material.Filled.RadioButtonChecked;
/// <summary>
/// Custom unselected bullet icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Appearance)]
public string UncheckedIcon { get; set; } = Icons.Material.Filled.RadioButtonUnchecked;
/// <summary>
/// Custom next navigation icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Appearance)]
public string NextIcon { get; set; } = Icons.Material.Filled.NavigateNext;
/// <summary>
/// Gets or Sets the Template for the Left Arrow
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Appearance)]
public RenderFragment NextButtonTemplate { get; set; }
/// <summary>
/// Gets or Sets the Template for the Right Arrow
/// </summary>
[Parameter]
[Category(CategoryTypes.Carousel.Appearance)]
public RenderFragment PreviousButtonTemplate { get; set; }
/// <summary>
/// Gets or Sets the Template for Bullets
/// </summary>
[Category(CategoryTypes.Carousel.Appearance)]
[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>
/// Gets or Sets the Template for Delimiters.
/// Deprecated, use BulletsTemplate instead.
/// </summary>
[Category(CategoryTypes.Carousel.Appearance)]
[Obsolete($"Use {nameof(BulletTemplate)} instead", false)]
[ExcludeFromCodeCoverage]
[Parameter] public RenderFragment<bool> DelimiterTemplate { get => BulletTemplate; set => BulletTemplate = value; }
/// <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);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
_timer = new Timer(TimerElapsed, null, AutoCycle ? AutoCycleTime : Timeout.InfiniteTimeSpan, AutoCycleTime);
}
}
public async ValueTask DisposeAsync()
{
await DisposeAsync(true);
GC.SuppressFinalize(this);
}
protected virtual async ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
await StopTimerAsync();
var timer = _timer;
if (timer != null)
{
_timer = null;
await timer.DisposeAsync();
}
}
}
}