2024-08-25 14:37:41 +08:00
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
2020-05-06 17:33:11 +08:00
using System.Collections.Generic ;
2020-09-01 13:49:31 +08:00
using System.Text.Json ;
2020-05-06 17:33:11 +08:00
using System.Threading ;
using System.Threading.Tasks ;
2020-05-29 00:33:49 +08:00
using AntDesign.JsInterop ;
2020-05-06 17:33:11 +08:00
using Microsoft.AspNetCore.Components ;
2020-05-29 00:33:49 +08:00
namespace AntDesign
2020-05-06 17:33:11 +08:00
{
2024-08-25 14:37:41 +08:00
/ * *
< summary >
< para > A carousel component . Scales with its container . < / para >
< h2 > When To Use < / h2 >
< list type = "bullet" >
< item > When there is a group of content on the same level . < / item >
< item > When there is insufficient content space , it can be used to save space in the form of a revolving door . < / item >
< item > Commonly used for a group of pictures / cards . < / item >
< / list >
< / summary >
< seealso cref = "CarouselSlick" / >
* /
[Documentation(DocumentationCategory.Components, DocumentationType.DataDisplay, "https://gw.alipayobjects.com/zos/antfincdn/%24C9tmj978R/Carousel.svg")]
2020-06-07 00:20:28 +08:00
public partial class Carousel : AntDomComponentBase
2020-05-06 17:33:11 +08:00
{
private const string PrefixCls = "ant-carousel" ;
2021-03-12 17:02:11 +08:00
2020-12-26 22:04:00 +08:00
private string TrackStyle
{
get
{
if ( Effect = = CarouselEffect . ScrollX & & IsHorizontal )
{
return $"width: {TotalWidth}px; opacity: 1; transform: translate3d(-{SlickWidth * (IndexOfSlick(ActiveSlick) + 1)}px, 0px, 0px);transition: -webkit-transform 500ms ease 0s;" ;
}
else if ( Effect = = CarouselEffect . ScrollX & & ! IsHorizontal )
{
return $"height: {TotalHeight}px; opacity: 1; transform: translate3d(0px, -{SlickHeight * (IndexOfSlick(ActiveSlick) + 1)}px, 0px);transition: -webkit-transform 500ms ease 0s;" ;
}
else if ( Effect = = CarouselEffect . Fade & & IsHorizontal )
{
return $"width: {TotalWidth}px; opacity: 1;" ;
}
else if ( Effect = = CarouselEffect . Fade & & ! IsHorizontal )
{
return $"height: {TotalHeight}px; opacity: 1;" ;
}
return string . Empty ;
}
}
2021-03-12 17:02:11 +08:00
2020-12-26 22:04:00 +08:00
internal string SlickClonedStyle = > $"width: {SlickWidth}px;" ;
2021-03-12 17:02:11 +08:00
private string SlickListStyle = > IsHorizontal ? string . Empty : $"height: {SlickHeight}px" ;
2020-12-26 22:04:00 +08:00
internal int SlickWidth { get ; set ; } = - 1 ;
private int SlickHeight { get ; set ; } = - 1 ;
private int TotalWidth = > SlickWidth * ( _slicks . Count * 2 + 1 ) ;
private int TotalHeight = > SlickHeight * ( _slicks . Count * 2 + 1 ) ;
2020-06-07 00:20:28 +08:00
private List < CarouselSlick > _slicks = new List < CarouselSlick > ( ) ;
2020-12-26 22:04:00 +08:00
internal CarouselSlick ActiveSlick { get ; set ; }
2020-05-06 17:33:11 +08:00
private Timer _timer ;
2020-06-07 00:20:28 +08:00
private ClassMapper SlickSliderClassMapper { get ; } = new ClassMapper ( ) ;
2020-06-11 23:24:58 +08:00
private bool IsHorizontal = > DotPosition = = CarouselDotPosition . Top | | DotPosition = = CarouselDotPosition . Bottom ;
2020-05-06 17:33:11 +08:00
#region Parameters
2024-08-25 14:37:41 +08:00
/// <summary>
/// Content of the carousel. Typically <see cref="CarouselSlick"/> elements
/// </summary>
2020-05-06 17:33:11 +08:00
[Parameter]
public RenderFragment ChildContent { get ; set ; }
/// <summary>
2020-06-07 00:20:28 +08:00
/// The position of the dots, which can be one of Top, Bottom, Left or Right, <see cref="CarouselDotPosition"/>
2020-05-06 17:33:11 +08:00
/// </summary>
[Parameter]
2020-06-07 00:20:28 +08:00
public string DotPosition { get ; set ; } = CarouselDotPosition . Bottom ;
2020-05-06 17:33:11 +08:00
/// <summary>
/// Whether to scroll automatically
/// </summary>
[Parameter]
public TimeSpan Autoplay { get ; set ; } = TimeSpan . Zero ;
/// <summary>
2020-06-07 00:20:28 +08:00
/// Transition effect, <see cref="CarouselEffect"/>
2020-05-06 17:33:11 +08:00
/// </summary>
[Parameter]
2020-06-07 00:20:28 +08:00
public string Effect { get ; set ; } = CarouselEffect . ScrollX ;
2020-05-06 17:33:11 +08:00
2024-08-03 01:16:12 +08:00
[Parameter]
public bool Dots { get ; set ; }
[Parameter]
public string DotsClass { get ; set ; }
2020-05-06 17:33:11 +08:00
#endregion Parameters
2024-08-25 14:37:41 +08:00
[Inject]
public IDomEventListener DomEventListener { get ; set ; }
2020-09-01 13:49:31 +08:00
2020-12-26 22:04:00 +08:00
public void Next ( ) = > GoTo ( _slicks . IndexOf ( ActiveSlick ) + 1 ) ;
2020-08-06 13:45:42 +08:00
2020-12-26 22:04:00 +08:00
public void Previous ( ) = > GoTo ( _slicks . IndexOf ( ActiveSlick ) - 1 ) ;
2020-08-06 13:45:42 +08:00
public void GoTo ( int index )
{
if ( index > = _slicks . Count )
{
index = 0 ;
}
else if ( index < 0 )
{
index = _slicks . Count - 1 ;
}
2020-12-26 22:04:00 +08:00
Activate ( _slicks [ index ] ) ;
2020-08-06 13:45:42 +08:00
}
2020-06-07 00:20:28 +08:00
private void SetClass ( )
{
2020-06-11 23:24:58 +08:00
SlickSliderClassMapper . Clear ( )
. Add ( "slick-slider slick-initialized" )
. If ( "slick-vertical" , ( ) = > ! IsHorizontal ) ;
2020-06-07 00:20:28 +08:00
ClassMapper . Clear ( )
. Add ( PrefixCls )
2021-03-12 17:02:11 +08:00
. If ( $"{PrefixCls}-vertical" , ( ) = > ! IsHorizontal )
2021-08-13 23:26:58 +08:00
. If ( $"{PrefixCls}-rtl" , ( ) = > RTL ) ;
2020-06-07 00:20:28 +08:00
}
2020-05-06 17:33:11 +08:00
protected override void OnParametersSet ( )
{
base . OnParametersSet ( ) ;
2020-06-11 23:24:58 +08:00
SetClass ( ) ;
2020-06-07 00:20:28 +08:00
if ( Effect ! = CarouselEffect . ScrollX & & Effect ! = CarouselEffect . Fade )
2020-05-06 17:33:11 +08:00
{
2020-06-07 00:20:28 +08:00
throw new ArgumentOutOfRangeException ( $"{nameof(Effect)} must be one of {nameof(CarouselEffect)}.{nameof(CarouselEffect.ScrollX)} or {nameof(CarouselEffect)}.{nameof(CarouselEffect.Fade)}." ) ;
2020-05-06 17:33:11 +08:00
}
_timer ? . Dispose ( ) ;
if ( Autoplay ! = TimeSpan . Zero )
{
_timer = new Timer ( AutoplaySlick , null , ( int ) Autoplay . TotalMilliseconds , ( int ) Autoplay . TotalMilliseconds ) ;
}
}
2022-05-15 12:45:43 +08:00
protected override async Task OnAfterRenderAsync ( bool firstRender )
2020-05-06 17:33:11 +08:00
{
await base . OnAfterRenderAsync ( firstRender ) ;
2020-09-01 13:49:31 +08:00
if ( firstRender )
{
Resize ( ) ;
2021-09-09 12:56:11 +08:00
DomEventListener . AddShared < JsonElement > ( "window" , "resize" , Resize ) ;
2020-09-01 13:49:31 +08:00
}
}
2020-05-06 17:33:11 +08:00
2020-09-01 13:49:31 +08:00
private async void Resize ( JsonElement e = default )
{
2021-01-21 17:20:10 +08:00
DomRect listRect = await JsInvokeAsync < DomRect > ( JSInteropConstants . GetBoundingClientRect , Ref ) ;
2021-04-17 22:14:01 +08:00
if ( ( SlickWidth ! = ( int ) listRect . Width & & IsHorizontal )
| | ( SlickHeight ! = ( int ) listRect . Height & & ! IsHorizontal )
2020-12-26 22:04:00 +08:00
| | IsHorizontal & & ! string . IsNullOrEmpty ( SlickListStyle )
| | ! IsHorizontal & & string . IsNullOrEmpty ( SlickListStyle ) )
2020-05-06 17:33:11 +08:00
{
2021-04-17 22:14:01 +08:00
SlickWidth = ( int ) listRect . Width ;
SlickHeight = ( int ) listRect . Height ;
2020-05-06 17:33:11 +08:00
StateHasChanged ( ) ;
}
}
2020-07-21 23:48:43 +08:00
internal void RemoveSlick ( CarouselSlick slick )
{
2020-12-26 22:04:00 +08:00
var slicks = new List < CarouselSlick > ( _slicks ? ? new List < CarouselSlick > ( ) ) ;
slicks . Remove ( slick ) ;
_slicks = slicks ;
InvokeAsync ( async ( ) = >
{
await InvokeAsync ( StateHasChanged ) . ConfigureAwait ( false ) ;
} ) ;
2020-07-21 23:48:43 +08:00
}
2020-06-07 00:20:28 +08:00
internal void AddSlick ( CarouselSlick slick )
2020-05-06 17:33:11 +08:00
{
2020-12-26 22:04:00 +08:00
var slicks = new List < CarouselSlick > ( _slicks ? ? new List < CarouselSlick > ( ) ) ;
slicks . Add ( slick ) ;
_slicks = slicks ;
if ( ActiveSlick = = null )
2020-05-06 17:33:11 +08:00
{
2020-12-26 22:04:00 +08:00
Activate ( _slicks [ 0 ] ) ;
2020-05-06 17:33:11 +08:00
}
2020-12-26 22:04:00 +08:00
InvokeStateHasChanged ( ) ;
2020-05-06 17:33:11 +08:00
}
2020-12-26 22:04:00 +08:00
internal int IndexOfSlick ( CarouselSlick slick )
2020-05-06 17:33:11 +08:00
{
2020-12-26 22:04:00 +08:00
return _slicks . IndexOf ( slick ) ;
}
2020-05-06 17:33:11 +08:00
2020-12-26 22:04:00 +08:00
private void Activate ( CarouselSlick slick )
{
this . ActiveSlick = slick ;
}
private async void AutoplaySlick ( object state )
{
var index = IndexOfSlick ( ActiveSlick ) + 1 ;
2020-05-06 17:33:11 +08:00
if ( index = = _slicks . Count )
{
index = 0 ;
}
2020-12-26 22:04:00 +08:00
if ( _slicks = = null | | _slicks . Count = = 0 )
2020-05-06 17:33:11 +08:00
{
2020-12-26 22:04:00 +08:00
return ;
2020-07-21 23:48:43 +08:00
}
2020-12-26 22:04:00 +08:00
this . Activate ( _slicks [ index ] ) ;
2020-05-06 17:33:11 +08:00
// The current thread is not associated with the Dispatcher.
// Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state
await InvokeAsync ( ( ) = > StateHasChanged ( ) ) ;
2020-12-26 22:04:00 +08:00
if ( index = = 0 & & Effect = = CarouselEffect . ScrollX )
2020-05-06 17:33:11 +08:00
{
2020-07-31 01:12:54 +08:00
await Task . Delay ( ( int ) Autoplay . TotalMilliseconds / 2 ) ;
2020-05-06 17:33:11 +08:00
}
await InvokeAsync ( ( ) = > StateHasChanged ( ) ) ;
}
2020-09-01 13:49:31 +08:00
protected override void Dispose ( bool disposing )
{
2022-05-15 12:45:43 +08:00
DomEventListener ? . Dispose ( ) ;
2020-09-01 13:49:31 +08:00
_slicks . Clear ( ) ;
base . Dispose ( disposing ) ;
}
2020-05-06 17:33:11 +08:00
}
}