1
1
import { ExportNs } from '../../../esl-utils/environment/export-ns' ;
2
- import { bind , listen , memoize , ready } from '../../../esl-utils/decorators' ;
2
+ import { listen , memoize , ready } from '../../../esl-utils/decorators' ;
3
+ import { parseTime } from '../../../esl-utils/misc/format' ;
4
+ import { CSSClassUtils } from '../../../esl-utils/dom/class' ;
5
+ import { ESLTraversingQuery } from '../../../esl-traversing-query/core' ;
3
6
4
7
import { ESLCarouselPlugin } from '../esl-carousel.plugin' ;
5
8
import { ESLCarouselSlideEvent } from '../../core/esl-carousel.events' ;
6
- import { parseTime } from '../../../ esl-utils/misc/format ' ;
9
+ import { ESLCarouselAutoplayEvent } from './ esl-carousel.autoplay.event ' ;
7
10
8
11
export interface ESLCarouselAutoplayConfig {
9
12
/** Timeout to send next command to the host carousel */
10
- timeout : string | number ;
13
+ duration : string | number ;
11
14
/** Navigation command to send to the host carousel. Default: 'slide:next' */
12
15
command : string ;
16
+ /** Selector for control to toggle plugin state */
17
+ control ?: string ;
18
+ /** Class to toggle on control element, when autoplay is active */
19
+ controlCls ?: string ;
20
+ /** Class to toggle on container element, when autoplay is active */
21
+ containerCls ?: string ;
13
22
}
14
23
15
24
/**
@@ -22,68 +31,122 @@ export interface ESLCarouselAutoplayConfig {
22
31
export class ESLCarouselAutoplayMixin extends ESLCarouselPlugin < ESLCarouselAutoplayConfig > {
23
32
public static override is = 'esl-carousel-autoplay' ;
24
33
public static override DEFAULT_CONFIG : ESLCarouselAutoplayConfig = {
25
- timeout : 5000 ,
34
+ duration : 5000 ,
26
35
command : 'slide:next'
27
36
} ;
28
- public static override DEFAULT_CONFIG_KEY = 'timeout ' ;
37
+ public static override DEFAULT_CONFIG_KEY : keyof ESLCarouselAutoplayConfig = 'duration ' ;
29
38
30
- private _timeout : number | null = null ;
39
+ private _enabled : boolean = true ;
40
+ private _duration : number | null = null ;
31
41
42
+ /** True if the autoplay timer is currently active */
32
43
public get active ( ) : boolean {
33
- return ! ! this . _timeout ;
44
+ return ! ! this . _duration ;
34
45
}
35
- public get timeout ( ) : number {
36
- return parseTime ( this . config . timeout ) ;
46
+
47
+ /** True if the autoplay plugin is enabled */
48
+ public get enabled ( ) : boolean {
49
+ return this . _enabled ;
50
+ }
51
+ protected set enabled ( value : boolean ) {
52
+ this . _enabled = value ;
53
+ CSSClassUtils . toggle ( this . $controls , this . config . controlCls , this . _enabled ) ;
54
+ const { $container} = this . $host ;
55
+ $container && CSSClassUtils . toggle ( $container , this . config . containerCls , this . _enabled ) ;
56
+ }
57
+
58
+ /** The duration of the autoplay timer in milliseconds */
59
+ public get duration ( ) : number {
60
+ return parseTime ( this . config . duration ) ;
61
+ }
62
+
63
+ /** A list of control elements to toggle plugin state */
64
+ @memoize ( )
65
+ public get $controls ( ) : HTMLElement [ ] {
66
+ const sel = this . config . control ;
67
+ return sel ? ESLTraversingQuery . all ( sel , this . $host ) as HTMLElement [ ] : [ ] ;
37
68
}
38
69
39
70
@ready
40
71
protected override connectedCallback ( ) : void {
41
- if ( super . connectedCallback ( ) ) {
42
- this . start ( ) ;
43
- }
72
+ super . connectedCallback ( ) ;
73
+ this . start ( ) ;
44
74
}
45
75
46
76
protected override disconnectedCallback ( ) : void {
47
77
super . disconnectedCallback ( ) ;
48
78
this . stop ( ) ;
49
79
}
50
80
81
+ @listen ( { inherit : true } )
51
82
protected override onConfigChange ( ) : void {
83
+ super . onConfigChange ( ) ;
84
+ memoize . clear ( this , [ '$controls' , 'duration' ] ) ;
85
+ // Full restart during config change
86
+ this . stop ( ) ;
52
87
this . start ( ) ;
53
88
}
54
89
55
- /** Activates the timer to send commands */
90
+ /** Activates and restarts the autoplay carousel timer */
56
91
public start ( ) : void {
57
- this . stop ( ) ;
58
- const { timeout} = this ;
59
- if ( timeout <= 0 || isNaN ( + timeout ) ) return ;
60
- this . _timeout = window . setTimeout ( this . _onInterval , timeout ) ;
92
+ const { duration} = this ;
93
+ this . enabled = + duration > 0 ;
94
+ if ( this . enabled ) this . _onCycle ( ) ;
61
95
}
62
96
63
- /** Deactivates the timer to send commands */
64
- public stop ( ) : void {
65
- this . _timeout && window . clearTimeout ( this . _timeout ) ;
66
- this . _timeout = null ;
97
+ /**
98
+ * Deactivates the autoplay carousel timer.
99
+ * @param system - If true, the plugin will be suspended but not disabled.
100
+ */
101
+ public stop ( system = false ) : void {
102
+ if ( ! this . active && ! this . enabled ) return ;
103
+ if ( ! system ) this . enabled = false ;
104
+ this . _duration && window . clearTimeout ( this . _duration ) ;
105
+ this . _duration = null ;
106
+ ESLCarouselAutoplayEvent . dispatch ( this ) ;
67
107
}
68
108
69
- /** Handles next timer interval */
70
- @bind
71
- protected _onInterval ( ) : void {
72
- this . $host ?. goTo ( this . config . command ) ;
73
- this . _timeout = window . setTimeout ( this . _onInterval , this . timeout ) ;
109
+ /**
110
+ * Starts a new autoplay cycle.
111
+ * Produces cycle self call after a timeout with enabled command execution.
112
+ */
113
+ protected async _onCycle ( exec ?: boolean ) : Promise < void > {
114
+ this . _duration && window . clearTimeout ( this . _duration ) ;
115
+ this . _duration = null ;
116
+ if ( exec ) await this . $host ?. goTo ( this . config . command ) ;
117
+ if ( ! this . enabled || this . active ) return ;
118
+ const { duration} = this ;
119
+ this . _duration = window . setTimeout ( ( ) => this . _onCycle ( true ) , duration ) ;
120
+ ESLCarouselAutoplayEvent . dispatch ( this ) ;
74
121
}
75
122
76
- /** Handles auxiliary events to pause/resume timer */
77
- @listen ( `mouseout mouseover focusin focusout ${ ESLCarouselSlideEvent . AFTER } ` )
123
+ /** Handles click on control element to toggle plugin state */
124
+ @listen ( {
125
+ event : 'click' ,
126
+ target : ( $this : ESLCarouselAutoplayMixin ) => $this . $controls ,
127
+ condition : ( $this : ESLCarouselAutoplayMixin ) => ! ! $this . $controls . length
128
+ } )
129
+ protected _onToggle ( e : Event ) : void {
130
+ this . enabled ? this . stop ( ) : this . start ( ) ;
131
+ e . preventDefault ( ) ;
132
+ }
133
+
134
+ /** Handles auxiliary events that represent user interaction to pause/resume timer */
135
+ @listen ( 'mouseleave mouseenter focusin focusout' )
78
136
protected _onInteract ( e : Event ) : void {
79
- // Slide change can only delay the timer, but not start it
80
- if ( e . type === ESLCarouselSlideEvent . AFTER && ! this . active ) return ;
81
- if ( [ 'mouseover' , 'focusin' ] . includes ( e . type ) ) {
82
- this . stop ( ) ;
137
+ if ( ! this . enabled ) return ;
138
+ if ( [ 'mouseenter' , 'focusin' ] . includes ( e . type ) ) {
139
+ this . stop ( true ) ;
83
140
} else {
84
141
this . start ( ) ;
85
142
}
86
143
}
144
+
145
+ /** Handles carousel slide change event to restart the timer */
146
+ @listen ( ESLCarouselSlideEvent . AFTER )
147
+ protected _onSlideChange ( ) : void {
148
+ if ( this . active ) this . start ( ) ;
149
+ }
87
150
}
88
151
89
152
declare global {
0 commit comments