mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-02-04 05:35:44 +00:00
391 lines
13 KiB
C#
391 lines
13 KiB
C#
using Microsoft.AspNetCore.Components;
|
|
using Microsoft.AspNetCore.Components.Web;
|
|
using Microsoft.JSInterop;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Timers;
|
|
|
|
namespace Radzen.Blazor
|
|
{
|
|
/// <summary>
|
|
/// A carousel/slideshow component for cycling through content items (images, cards, or custom content) with navigation and paging controls.
|
|
/// RadzenCarousel displays one item at a time with automatic or manual advancement and various navigation options.
|
|
/// Perfect for image galleries, product showcases, hero sections, or any content that benefits from sequential presentation.
|
|
/// Features automatic advancement with configurable interval, Previous/Next buttons with customizable icons and text, dot indicators or page numbers for direct item selection,
|
|
/// infinite loop for continuous cycling from last to first item, keyboard control (Arrow keys for navigation, Page Up/Down for first/last), swipe gestures on touch devices,
|
|
/// and customization of button styles, pager position (top/bottom/overlay), and navigation visibility.
|
|
/// Items are defined using RadzenCarouselItem components. Each item can contain images, text, or complex layouts. Use Auto property to enable automatic cycling, and Interval to control slide duration.
|
|
/// </summary>
|
|
/// <example>
|
|
/// Basic image carousel:
|
|
/// <code>
|
|
/// <RadzenCarousel Style="height: 400px;">
|
|
/// <Items>
|
|
/// <RadzenCarouselItem>
|
|
/// <RadzenImage Path="images/slide1.jpg" Style="width: 100%; height: 100%; object-fit: cover;" />
|
|
/// </RadzenCarouselItem>
|
|
/// <RadzenCarouselItem>
|
|
/// <RadzenImage Path="images/slide2.jpg" Style="width: 100%; height: 100%; object-fit: cover;" />
|
|
/// </RadzenCarouselItem>
|
|
/// </Items>
|
|
/// </RadzenCarousel>
|
|
/// </code>
|
|
/// Auto-play carousel with custom navigation:
|
|
/// <code>
|
|
/// <RadzenCarousel Auto="true" Interval="3000" AllowNavigation="true" PagerPosition="PagerPosition.Bottom">
|
|
/// <Items>
|
|
/// <RadzenCarouselItem>Slide 1 content</RadzenCarouselItem>
|
|
/// <RadzenCarouselItem>Slide 2 content</RadzenCarouselItem>
|
|
/// <RadzenCarouselItem>Slide 3 content</RadzenCarouselItem>
|
|
/// </Items>
|
|
/// </RadzenCarousel>
|
|
/// </code>
|
|
/// </example>
|
|
public partial class RadzenCarousel : RadzenComponent
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the items.
|
|
/// </summary>
|
|
/// <value>The items.</value>
|
|
[Parameter]
|
|
public RenderFragment? Items { get; set; }
|
|
|
|
internal List<RadzenCarouselItem> items = new List<RadzenCarouselItem>();
|
|
|
|
/// <summary>
|
|
/// Adds the item.
|
|
/// </summary>
|
|
/// <param name="item">The item.</param>
|
|
public void AddItem(RadzenCarouselItem item)
|
|
{
|
|
if (!items.Contains(item))
|
|
{
|
|
items.Add(item);
|
|
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the item.
|
|
/// </summary>
|
|
/// <param name="item">The item.</param>
|
|
public void RemoveItem(RadzenCarouselItem item)
|
|
{
|
|
if (items.Remove(item))
|
|
{
|
|
if (!disposed)
|
|
{
|
|
try { InvokeAsync(StateHasChanged); } catch { }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override string GetComponentCssClass()
|
|
{
|
|
return $"rz-carousel {(AllowNavigation ? "" : "rz-carousel-no-navigation")} {(PagerOverlay ? "rz-carousel-pager-overlay" : "")}".Trim();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Navigates to specific index.
|
|
/// </summary>
|
|
public async Task Navigate(int index)
|
|
{
|
|
if (Auto)
|
|
{
|
|
await Reset();
|
|
}
|
|
|
|
await GoTo(index);
|
|
}
|
|
|
|
async Task Prev()
|
|
{
|
|
await Navigate(selectedIndex == 0 ? items.Count - 1 : selectedIndex - 1);
|
|
}
|
|
|
|
async Task Next()
|
|
{
|
|
await Navigate(selectedIndex == items.Count - 1 ? 0 : selectedIndex + 1);
|
|
}
|
|
|
|
async Task GoTo(int index)
|
|
{
|
|
if (index >= 0 && index <= items.Count - 1 && selectedIndex != index)
|
|
{
|
|
selectedIndex = index;
|
|
await SelectedIndexChanged.InvokeAsync(selectedIndex);
|
|
await Change.InvokeAsync(selectedIndex);
|
|
if (JSRuntime != null)
|
|
{
|
|
await JSRuntime.InvokeVoidAsync("Radzen.scrollCarouselItem", items[selectedIndex].element);
|
|
}
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the auto-cycle timer.
|
|
/// </summary>
|
|
public void Stop()
|
|
{
|
|
timer?.Change(Timeout.Infinite, Timeout.Infinite);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts the auto-cycle timer.
|
|
/// </summary>
|
|
public void Start()
|
|
{
|
|
timer?.Change(TimeSpan.FromMilliseconds(Interval), TimeSpan.FromMilliseconds(Interval));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the auto-cycle timer.
|
|
/// </summary>
|
|
public async Task Reset()
|
|
{
|
|
Stop();
|
|
Start();
|
|
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the selected index.
|
|
/// </summary>
|
|
/// <value>The selected index.</value>
|
|
[Parameter]
|
|
public int SelectedIndex { get; set; }
|
|
|
|
private int selectedIndex;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the selected index changed callback.
|
|
/// </summary>
|
|
/// <value>The selected index changed callback.</value>
|
|
[Parameter]
|
|
public EventCallback<int> SelectedIndexChanged { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the change callback.
|
|
/// </summary>
|
|
/// <value>The change callback.</value>
|
|
[Parameter]
|
|
public EventCallback<int> Change { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public override async Task SetParametersAsync(ParameterView parameters)
|
|
{
|
|
var shouldUpdate = false;
|
|
if (parameters.DidParameterChange(nameof(SelectedIndex), SelectedIndex))
|
|
{
|
|
selectedIndex = parameters.GetValueOrDefault<int>(nameof(SelectedIndex));
|
|
shouldUpdate = true;
|
|
}
|
|
|
|
if (parameters.DidParameterChange(nameof(Auto), Auto) ||
|
|
parameters.DidParameterChange(nameof(Interval), Interval))
|
|
{
|
|
if (parameters.GetValueOrDefault<bool>(nameof(Auto)))
|
|
{
|
|
await Reset();
|
|
}
|
|
else
|
|
{
|
|
Stop();
|
|
}
|
|
}
|
|
|
|
await base.SetParametersAsync(parameters);
|
|
|
|
if (shouldUpdate)
|
|
{
|
|
if (JSRuntime != null)
|
|
{
|
|
await JSRuntime.InvokeVoidAsync("Radzen.scrollCarouselItem", items[selectedIndex].element);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether this <see cref="RadzenCarousel"/> cycle is automatic.
|
|
/// </summary>
|
|
/// <value><c>true</c> if cycle automatic; otherwise, <c>false</c>.</value>
|
|
[Parameter]
|
|
public bool Auto { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the auto-cycle interval in milliseconds.
|
|
/// </summary>
|
|
[Parameter]
|
|
public double Interval { get; set; } = 4000;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the pager position. Set to <c>PagerPosition.Bottom</c> by default.
|
|
/// </summary>
|
|
/// <value>The pager position.</value>
|
|
[Parameter]
|
|
public PagerPosition PagerPosition { get; set; } = PagerPosition.Bottom;
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether pager overlays the carousel items. Set to <c>true</c> by default.
|
|
/// </summary>
|
|
/// <value><c>true</c> if pager overlay is allowed; otherwise, <c>false</c>.</value>
|
|
[Parameter]
|
|
public bool PagerOverlay { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether paging is allowed. Set to <c>true</c> by default.
|
|
/// </summary>
|
|
/// <value><c>true</c> if paging is allowed; otherwise, <c>false</c>.</value>
|
|
[Parameter]
|
|
public bool AllowPaging { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Gets or sets a value indicating whether previous/next navigation is allowed. Set to <c>true</c> by default.
|
|
/// </summary>
|
|
/// <value><c>true</c> if previous/next navigation is allowed; otherwise, <c>false</c>.</value>
|
|
[Parameter]
|
|
public bool AllowNavigation { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the buttons style
|
|
/// </summary>
|
|
/// <value>The buttons style.</value>
|
|
[Parameter]
|
|
public ButtonStyle ButtonStyle { get; set; } = ButtonStyle.Base;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the design variant of the buttons.
|
|
/// </summary>
|
|
/// <value>The variant of the buttons.</value>
|
|
[Parameter]
|
|
public Variant ButtonVariant { get; set; } = Variant.Text;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the color shade of the buttons.
|
|
/// </summary>
|
|
/// <value>The color shade of the buttons.</value>
|
|
[Parameter]
|
|
public Shade ButtonShade { get; set; } = Shade.Lighter;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the buttons size.
|
|
/// </summary>
|
|
/// <value>The buttons size.</value>
|
|
[Parameter]
|
|
public ButtonSize ButtonSize { get; set; } = ButtonSize.Large;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the next button text.
|
|
/// </summary>
|
|
/// <value>The next button text.</value>
|
|
[Parameter]
|
|
public string NextText { get; set; } = "";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the previous button text.
|
|
/// </summary>
|
|
/// <value>The previous button text.</value>
|
|
[Parameter]
|
|
public string PrevText { get; set; } = "";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the next button icon.
|
|
/// </summary>
|
|
/// <value>The next button icon.</value>
|
|
[Parameter]
|
|
public string NextIcon { get; set; } = "arrow_forward_ios";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the previous button icon.
|
|
/// </summary>
|
|
/// <value>The previous button icon.</value>
|
|
[Parameter]
|
|
public string PrevIcon { get; set; } = "arrow_back_ios_new";
|
|
|
|
System.Threading.Timer? timer;
|
|
|
|
/// <inheritdoc />
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
await base.OnAfterRenderAsync(firstRender);
|
|
|
|
if (firstRender)
|
|
{
|
|
var ts = TimeSpan.FromMilliseconds(Interval);
|
|
timer = new System.Threading.Timer(state => InvokeAsync(Next),
|
|
null, Auto ? ts : Timeout.InfiniteTimeSpan, ts);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Dispose()
|
|
{
|
|
base.Dispose();
|
|
|
|
if (timer != null)
|
|
{
|
|
timer.Dispose();
|
|
timer = null;
|
|
}
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
double? x;
|
|
double? y;
|
|
|
|
void OnTouchStart(TouchEventArgs args)
|
|
{
|
|
x = args.Touches[0].ClientX;
|
|
y = args.Touches[0].ClientY;
|
|
}
|
|
|
|
async Task OnTouchEnd(TouchEventArgs args)
|
|
{
|
|
if (x == null || y == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var xDiff = x.Value - args.ChangedTouches[0].ClientX;
|
|
var yDiff = y.Value - args.ChangedTouches[0].ClientY;
|
|
|
|
if (Math.Abs(xDiff) < 100 && Math.Abs(yDiff) < 100)
|
|
{
|
|
x = null;
|
|
y = null;
|
|
return;
|
|
}
|
|
|
|
if (Math.Abs(xDiff) > Math.Abs(yDiff))
|
|
{
|
|
if (xDiff > 0)
|
|
{
|
|
await Next();
|
|
}
|
|
else
|
|
{
|
|
await Prev();
|
|
}
|
|
}
|
|
|
|
x = null;
|
|
y = null;
|
|
}
|
|
|
|
void OnTouchCancel(TouchEventArgs args)
|
|
{
|
|
x = null;
|
|
y = null;
|
|
}
|
|
}
|
|
}
|