@syncfusion/ej2-navigations 30.2.5 → 31.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/ej2-navigations.min.js +2 -2
  2. package/dist/ej2-navigations.umd.min.js +2 -2
  3. package/dist/ej2-navigations.umd.min.js.map +1 -1
  4. package/dist/es6/ej2-navigations.es2015.js +1 -0
  5. package/dist/es6/ej2-navigations.es2015.js.map +1 -1
  6. package/dist/es6/ej2-navigations.es5.js +1 -0
  7. package/dist/es6/ej2-navigations.es5.js.map +1 -1
  8. package/dist/global/ej2-navigations.min.js +2 -2
  9. package/dist/global/ej2-navigations.min.js.map +1 -1
  10. package/dist/global/index.d.ts +1 -1
  11. package/dist/ts/accordion/accordion-model.d.ts +285 -0
  12. package/dist/ts/accordion/accordion.d.ts +458 -0
  13. package/dist/ts/accordion/accordion.ts +1580 -0
  14. package/dist/ts/accordion/index.d.ts +5 -0
  15. package/dist/ts/accordion/index.ts +5 -0
  16. package/dist/ts/appbar/appbar-model.d.ts +76 -0
  17. package/dist/ts/appbar/appbar.d.ts +115 -0
  18. package/dist/ts/appbar/appbar.ts +281 -0
  19. package/dist/ts/appbar/index.d.ts +3 -0
  20. package/dist/ts/appbar/index.ts +3 -0
  21. package/dist/ts/breadcrumb/breadcrumb-model.d.ts +170 -0
  22. package/dist/ts/breadcrumb/breadcrumb.d.ts +297 -0
  23. package/dist/ts/breadcrumb/breadcrumb.ts +959 -0
  24. package/dist/ts/breadcrumb/index.d.ts +5 -0
  25. package/dist/ts/breadcrumb/index.ts +5 -0
  26. package/dist/ts/carousel/carousel-model.d.ts +282 -0
  27. package/dist/ts/carousel/carousel.d.ts +439 -0
  28. package/dist/ts/carousel/carousel.ts +1633 -0
  29. package/dist/ts/carousel/index.d.ts +3 -0
  30. package/dist/ts/carousel/index.ts +3 -0
  31. package/dist/ts/common/h-scroll-model.d.ts +16 -0
  32. package/dist/ts/common/h-scroll.d.ts +105 -0
  33. package/dist/ts/common/h-scroll.ts +481 -0
  34. package/dist/ts/common/index.d.ts +9 -0
  35. package/dist/ts/common/index.ts +10 -0
  36. package/dist/ts/common/menu-base-model.d.ts +308 -0
  37. package/dist/ts/common/menu-base.d.ts +558 -0
  38. package/dist/ts/common/menu-base.ts +2736 -0
  39. package/dist/ts/common/menu-scroll.d.ts +29 -0
  40. package/dist/ts/common/menu-scroll.ts +105 -0
  41. package/dist/ts/common/v-scroll-model.d.ts +16 -0
  42. package/dist/ts/common/v-scroll.d.ts +106 -0
  43. package/dist/ts/common/v-scroll.ts +454 -0
  44. package/dist/ts/context-menu/context-menu-model.d.ts +47 -0
  45. package/dist/ts/context-menu/context-menu.d.ts +102 -0
  46. package/dist/ts/context-menu/context-menu.ts +165 -0
  47. package/dist/ts/context-menu/index.d.ts +5 -0
  48. package/dist/ts/context-menu/index.ts +5 -0
  49. package/dist/ts/index.d.ts +16 -0
  50. package/dist/ts/index.ts +16 -0
  51. package/dist/ts/menu/index.d.ts +5 -0
  52. package/dist/ts/menu/index.ts +5 -0
  53. package/dist/ts/menu/menu-model.d.ts +70 -0
  54. package/dist/ts/menu/menu.d.ts +127 -0
  55. package/dist/ts/menu/menu.ts +313 -0
  56. package/dist/ts/sidebar/index.d.ts +5 -0
  57. package/dist/ts/sidebar/index.ts +5 -0
  58. package/dist/ts/sidebar/sidebar-model.d.ts +200 -0
  59. package/dist/ts/sidebar/sidebar.d.ts +336 -0
  60. package/dist/ts/sidebar/sidebar.ts +907 -0
  61. package/dist/ts/stepper/index.d.ts +3 -0
  62. package/dist/ts/stepper/index.ts +3 -0
  63. package/dist/ts/stepper/stepper-model.d.ts +159 -0
  64. package/dist/ts/stepper/stepper.d.ts +381 -0
  65. package/dist/ts/stepper/stepper.ts +1350 -0
  66. package/dist/ts/stepper-base/index.d.ts +5 -0
  67. package/dist/ts/stepper-base/index.ts +6 -0
  68. package/dist/ts/stepper-base/stepper-base-model.d.ts +124 -0
  69. package/dist/ts/stepper-base/stepper-base.d.ts +187 -0
  70. package/dist/ts/stepper-base/stepper-base.ts +290 -0
  71. package/dist/ts/tab/index.d.ts +5 -0
  72. package/dist/ts/tab/index.ts +5 -0
  73. package/dist/ts/tab/tab-model.d.ts +408 -0
  74. package/dist/ts/tab/tab.d.ts +715 -0
  75. package/dist/ts/tab/tab.ts +2842 -0
  76. package/dist/ts/toolbar/index.d.ts +5 -0
  77. package/dist/ts/toolbar/index.ts +5 -0
  78. package/dist/ts/toolbar/toolbar-model.d.ts +294 -0
  79. package/dist/ts/toolbar/toolbar.d.ts +541 -0
  80. package/dist/ts/toolbar/toolbar.ts +2646 -0
  81. package/dist/ts/treeview/index.d.ts +5 -0
  82. package/dist/ts/treeview/index.ts +5 -0
  83. package/dist/ts/treeview/treeview-model.d.ts +637 -0
  84. package/dist/ts/treeview/treeview.d.ts +1518 -0
  85. package/dist/ts/treeview/treeview.ts +6780 -0
  86. package/package.json +70 -17
  87. package/src/context-menu/context-menu-model.d.ts +1 -1
  88. package/src/context-menu/context-menu.js +1 -1
  89. package/src/tab/tab.js +1 -0
  90. package/styles/accordion/_bootstrap-dark-definition.scss +1 -1
  91. package/styles/accordion/_bootstrap-definition.scss +1 -1
  92. package/styles/accordion/bootstrap-dark.css +1 -1
  93. package/styles/accordion/bootstrap.css +1 -1
  94. package/styles/bds-lite.css +1 -1
  95. package/styles/bds.css +1 -1
  96. package/styles/bootstrap-dark-lite.css +2 -2
  97. package/styles/bootstrap-dark.css +2 -2
  98. package/styles/bootstrap-lite.css +2 -2
  99. package/styles/bootstrap.css +2 -2
  100. package/styles/bootstrap4-lite.css +1 -1
  101. package/styles/bootstrap4.css +1 -1
  102. package/styles/bootstrap5-dark-lite.css +1 -1
  103. package/styles/bootstrap5-dark.css +1 -1
  104. package/styles/bootstrap5-lite.css +1 -1
  105. package/styles/bootstrap5.3-lite.css +1 -1
  106. package/styles/bootstrap5.3.css +9 -1
  107. package/styles/bootstrap5.css +1 -1
  108. package/styles/fabric-dark-lite.css +1 -1
  109. package/styles/fabric-dark.css +1 -1
  110. package/styles/fabric-lite.css +1 -1
  111. package/styles/fabric.css +1 -1
  112. package/styles/fluent-dark-lite.css +1 -1
  113. package/styles/fluent-dark.css +1 -1
  114. package/styles/fluent-lite.css +1 -1
  115. package/styles/fluent.css +1 -1
  116. package/styles/fluent2-lite.css +4 -1
  117. package/styles/fluent2.css +4 -1
  118. package/styles/h-scroll/_layout.scss +1 -1
  119. package/styles/h-scroll/bds.css +1 -1
  120. package/styles/h-scroll/bootstrap-dark.css +1 -1
  121. package/styles/h-scroll/bootstrap.css +1 -1
  122. package/styles/h-scroll/bootstrap4.css +1 -1
  123. package/styles/h-scroll/bootstrap5-dark.css +1 -1
  124. package/styles/h-scroll/bootstrap5.3.css +1 -1
  125. package/styles/h-scroll/bootstrap5.css +1 -1
  126. package/styles/h-scroll/fabric-dark.css +1 -1
  127. package/styles/h-scroll/fabric.css +1 -1
  128. package/styles/h-scroll/fluent-dark.css +1 -1
  129. package/styles/h-scroll/fluent.css +1 -1
  130. package/styles/h-scroll/fluent2.css +1 -1
  131. package/styles/h-scroll/highcontrast-light.css +1 -1
  132. package/styles/h-scroll/highcontrast.css +1 -1
  133. package/styles/h-scroll/material-dark.css +1 -1
  134. package/styles/h-scroll/material.css +1 -1
  135. package/styles/h-scroll/material3-dark.css +1 -1
  136. package/styles/h-scroll/material3.css +1 -1
  137. package/styles/h-scroll/tailwind-dark.css +1 -1
  138. package/styles/h-scroll/tailwind.css +1 -1
  139. package/styles/h-scroll/tailwind3.css +1 -1
  140. package/styles/highcontrast-light-lite.css +1 -1
  141. package/styles/highcontrast-light.css +1 -1
  142. package/styles/highcontrast-lite.css +1 -1
  143. package/styles/highcontrast.css +1 -1
  144. package/styles/material-dark-lite.css +1 -1
  145. package/styles/material-dark.css +1 -1
  146. package/styles/material-lite.css +1 -1
  147. package/styles/material.css +1 -1
  148. package/styles/material3-dark-lite.css +1 -1
  149. package/styles/material3-dark.css +1 -7
  150. package/styles/material3-lite.css +1 -1
  151. package/styles/material3.css +1 -7
  152. package/styles/tailwind-dark-lite.css +1 -1
  153. package/styles/tailwind-dark.css +1 -1
  154. package/styles/tailwind-lite.css +1 -1
  155. package/styles/tailwind.css +1 -1
  156. package/styles/tailwind3-lite.css +1 -1
  157. package/styles/tailwind3.css +1 -1
  158. package/styles/toolbar/_layout.scss +1 -1
  159. package/styles/treeview/_bigger.scss +2 -2
  160. package/styles/treeview/_bootstrap5.3-definition.scss +1 -0
  161. package/styles/treeview/_layout.scss +3 -0
  162. package/styles/treeview/bootstrap5.3.css +8 -0
  163. package/styles/treeview/fluent2.css +3 -0
  164. package/styles/treeview/material3-dark.css +0 -6
  165. package/styles/treeview/material3.css +0 -6
@@ -0,0 +1,1633 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Component, EventHandler, Collection, Property, Event, EmitType, formatUnit, INotifyPropertyChanged, NotifyPropertyChanges, Browser } from '@syncfusion/ej2-base';
3
+ import { ChildProperty, addClass, removeClass, setStyleAttribute, attributes, getUniqueID, compile, getInstance, L10n } from '@syncfusion/ej2-base';
4
+ import { append, closest, isNullOrUndefined, remove, classList, Touch, SwipeEventArgs, KeyboardEvents, KeyboardEventArgs, BaseEventArgs } from '@syncfusion/ej2-base';
5
+ import { Button } from '@syncfusion/ej2-buttons';
6
+ import { CarouselModel, CarouselItemModel } from './carousel-model';
7
+
8
+ // Constant variables
9
+ const CLS_CAROUSEL: string = 'e-carousel';
10
+ const CLS_ACTIVE: string = 'e-active';
11
+ const CLS_RTL: string = 'e-rtl';
12
+ const CLS_PARTIAL: string = 'e-partial';
13
+ const CLS_SWIPE: string = 'e-swipe';
14
+ const CLS_SLIDE_CONTAINER: string = 'e-carousel-slide-container';
15
+ const CLS_ITEMS: string = 'e-carousel-items';
16
+ const CLS_CLONED: string = 'e-cloned';
17
+ const CLS_ITEM: string = 'e-carousel-item';
18
+ const CLS_PREVIOUS: string = 'e-previous';
19
+ const CLS_NEXT: string = 'e-next';
20
+ const CLS_PREV_ICON: string = 'e-previous-icon';
21
+ const CLS_NEXT_ICON: string = 'e-next-icon';
22
+ const CLS_NAVIGATORS: string = 'e-carousel-navigators';
23
+ const CLS_INDICATORS: string = 'e-carousel-indicators';
24
+ const CLS_INDICATOR_BARS: string = 'e-indicator-bars';
25
+ const CLS_INDICATOR_BAR: string = 'e-indicator-bar';
26
+ const CLS_INDICATOR: string = 'e-indicator';
27
+ const CLS_ICON: string = 'e-icons';
28
+ const CLS_PLAY_PAUSE: string = 'e-play-pause';
29
+ const CLS_PLAY_ICON: string = 'e-play-icon';
30
+ const CLS_PAUSE_ICON: string = 'e-pause-icon';
31
+ const CLS_PREV_BUTTON: string = 'e-previous-button';
32
+ const CLS_NEXT_BUTTON: string = 'e-next-button';
33
+ const CLS_PLAY_BUTTON: string = 'e-play-button';
34
+ const CLS_FLAT: string = 'e-flat';
35
+ const CLS_ROUND: string = 'e-round';
36
+ const CLS_HOVER_ARROWS: string = 'e-hover-arrows';
37
+ const CLS_HOVER: string = 'e-carousel-hover';
38
+ const CLS_TEMPLATE: string = 'e-template';
39
+ const CLS_SLIDE_ANIMATION: string = 'e-carousel-slide-animation';
40
+ const CLS_FADE_ANIMATION: string = 'e-carousel-fade-animation';
41
+ const CLS_CUSTOM_ANIMATION: string = 'e-carousel-custom-animation';
42
+ const CLS_ANIMATION_NONE: string = 'e-carousel-animation-none';
43
+ const CLS_PREV_SLIDE: string = 'e-prev';
44
+ const CLS_NEXT_SLIDE: string = 'e-next';
45
+ const CLS_TRANSITION_START: string = 'e-transition-start';
46
+ const CLS_TRANSITION_END: string = 'e-transition-end';
47
+
48
+ /**
49
+ * Specifies the direction of previous/next button navigations in carousel.
50
+ * ```props
51
+ * Previous :- To determine the previous direction of carousel item transition.
52
+ * Next :- To determine the next direction of carousel item transition.
53
+ * ```
54
+ */
55
+ export type CarouselSlideDirection = 'Previous' | 'Next';
56
+
57
+ /**
58
+ * Specifies the state of navigation buttons displayed in carousel.
59
+ * ```props
60
+ * Hidden :- Navigation buttons are hidden.
61
+ * Visible :- Navigation buttons are visible.
62
+ * VisibleOnHover :- Navigation buttons are visible only when we hover the carousel.
63
+ * ```
64
+ */
65
+ export type CarouselButtonVisibility = 'Hidden' | 'Visible' | 'VisibleOnHover';
66
+
67
+ /**
68
+ * Specifies the animation effects of carousel slide.
69
+ * ```props
70
+ * None :- The carousel item transition happens without animation.
71
+ * Slide :- The carousel item transition happens with slide animation.
72
+ * Fade :- The Carousel item transition happens with fade animation.
73
+ * Custom :- The Carousel item transition happens with custom animation.
74
+ * ```
75
+ */
76
+ export type CarouselAnimationEffect = 'None' | 'Slide' | 'Fade' | 'Custom';
77
+
78
+ /**
79
+ * Specifies the type of indicators.
80
+ * ```props
81
+ * Default: - Displays the indicators with a bullet design.
82
+ * Dynamic: - Applies a dynamic animation design to the indicators.
83
+ * Fraction: - Displays the slides numerically as indicators.
84
+ * Progress: - Represents the slides using a progress bar design.
85
+ * ```
86
+ */
87
+ export type CarouselIndicatorsType = 'Default' | 'Dynamic' | 'Fraction' | 'Progress';
88
+
89
+ /**
90
+ * Specifies the action (touch & mouse) which enables the slide swiping action in carousel.
91
+ * * Touch - Enables or disables the swiping action in touch interaction.
92
+ * * Mouse - Enables or disables the swiping action in mouse interaction.
93
+ *
94
+ * @aspNumberEnum
95
+ */
96
+ export enum CarouselSwipeMode {
97
+ /** Enables or disables the swiping action in touch interaction. */
98
+ Touch = 1 << 0,
99
+ /** Enables or disables the swiping action in mouse interaction. */
100
+ Mouse = 1 << 1
101
+ }
102
+
103
+ /** An interface that holds details when changing the slide. */
104
+ export interface SlideChangingEventArgs extends BaseEventArgs {
105
+ /** Specifies the index of current slide. */
106
+ currentIndex: number;
107
+ /** Specifies the element of current slide. */
108
+ currentSlide: HTMLElement;
109
+ /** Specifies the index of slide to be changed. */
110
+ nextIndex: number;
111
+ /** Specifies the element of slide to be changed. */
112
+ nextSlide: HTMLElement;
113
+ /** Specifies whether the slide transition occur through swiping or not. */
114
+ isSwiped: boolean;
115
+ /** Specifies the slide direction in which transition occurs. */
116
+ slideDirection: CarouselSlideDirection;
117
+ /** Specifies whether the slide transition should occur or not. */
118
+ cancel: boolean;
119
+ }
120
+
121
+ /** An interface that holds details once slide change done. */
122
+ export interface SlideChangedEventArgs extends BaseEventArgs {
123
+ /** Specifies the index of current slide. */
124
+ currentIndex: number;
125
+ /** Specifies the element of current slide. */
126
+ currentSlide: HTMLElement;
127
+ /** Specifies the index of slide from which it changed. */
128
+ previousIndex: number;
129
+ /** Specifies the element of slide from which it changed. */
130
+ previousSlide: HTMLElement;
131
+ /** Specifies whether the slide transition done through swiping or not. */
132
+ isSwiped: boolean;
133
+ /** Specifies the slide direction in which transition occurred. */
134
+ slideDirection: CarouselSlideDirection;
135
+ }
136
+
137
+ /** Specifies the carousel individual item. */
138
+ export class CarouselItem extends ChildProperty<CarouselItem> {
139
+
140
+ /**
141
+ * Accepts single/multiple classes (separated by a space) to be used for individual carousel item customization.
142
+ *
143
+ * @default null
144
+ */
145
+ @Property()
146
+ public cssClass: string;
147
+
148
+ /**
149
+ * Accepts the interval duration in milliseconds for individual carousel item transition.
150
+ *
151
+ * @default null
152
+ */
153
+ @Property()
154
+ public interval: number;
155
+
156
+ /**
157
+ * Accepts the template for individual carousel item.
158
+ *
159
+ * @default null
160
+ * @angularType string | object
161
+ * @reactType string | function | JSX.Element
162
+ * @vueType string | function
163
+ * @aspType string
164
+ */
165
+ @Property()
166
+ public template: string | Function;
167
+
168
+ /**
169
+ * Accepts HTML attributes/custom attributes to add in individual carousel item.
170
+ *
171
+ * @default null
172
+ */
173
+ @Property()
174
+ public htmlAttributes: Record<string, string>;
175
+
176
+ }
177
+
178
+ @NotifyPropertyChanges
179
+ export class Carousel extends Component<HTMLElement> implements INotifyPropertyChanged {
180
+ private autoSlideInterval: any;
181
+ private slideItems: any[];
182
+ private touchModule: Touch;
183
+ private keyModule: KeyboardEvents;
184
+ private keyConfigs: Record<string, string>;
185
+ private slideChangedEventArgs: SlideChangedEventArgs;
186
+ private localeObj: L10n;
187
+ private prevPageX: number;
188
+ private initialTranslate: number;
189
+ private itemsContainer: HTMLElement;
190
+ private isSwipe: boolean = false;
191
+ private timeStampStart: number;
192
+
193
+ /**
194
+ * Allows defining the collection of carousel item to be displayed on the Carousel.
195
+ *
196
+ * @default []
197
+ */
198
+ @Collection<CarouselItemModel>([], CarouselItem)
199
+ public items: CarouselItemModel[];
200
+
201
+ /**
202
+ * Specifies the type of animation effects. The possible values for this property as follows
203
+ * * `None`: The carousel item transition happens without animation.
204
+ * * `Slide`: The carousel item transition happens with slide animation.
205
+ * * `Fade`: The Carousel item transition happens with fade animation.
206
+ * * `Custom`: The Carousel item transition happens with custom animation.
207
+ *
208
+ * @default 'Slide'
209
+ */
210
+ @Property('Slide')
211
+ public animationEffect: CarouselAnimationEffect;
212
+
213
+ /**
214
+ * Accepts the template for previous navigation button.
215
+ *
216
+ * @default null
217
+ * @angularType string | object
218
+ * @reactType string | function | JSX.Element
219
+ * @vueType string | function
220
+ * @aspType string
221
+ */
222
+ @Property()
223
+ public previousButtonTemplate: string | Function;
224
+
225
+ /**
226
+ * Accepts the template for next navigation button.
227
+ *
228
+ * @default null
229
+ * @angularType string | object
230
+ * @reactType string | function | JSX.Element
231
+ * @vueType string | function
232
+ * @aspType string
233
+ */
234
+ @Property()
235
+ public nextButtonTemplate: string | Function;
236
+
237
+ /**
238
+ * Accepts the template for indicator buttons.
239
+ *
240
+ * @default null
241
+ * @angularType string | object
242
+ * @reactType string | function | JSX.Element
243
+ * @vueType string | function
244
+ * @aspType string
245
+ */
246
+ @Property()
247
+ public indicatorsTemplate: string | Function;
248
+
249
+ /**
250
+ * Accepts the template for play/pause button.
251
+ *
252
+ * @default null
253
+ * @angularType string | object
254
+ * @reactType string | function | JSX.Element
255
+ * @vueType string | function
256
+ * @aspType string
257
+ */
258
+ @Property()
259
+ public playButtonTemplate: string | Function;
260
+
261
+ /**
262
+ * Accepts single/multiple classes (separated by a space) to be used for carousel customization.
263
+ *
264
+ * @default null
265
+ */
266
+ @Property()
267
+ public cssClass: string;
268
+
269
+ /**
270
+ * Specifies the datasource for the carousel items.
271
+ *
272
+ * @isdatamanager false
273
+ * @default []
274
+ */
275
+ @Property([])
276
+ public dataSource: Record<string, any>[];
277
+
278
+ /**
279
+ * Specifies the template option for carousel items.
280
+ *
281
+ * @default null
282
+ * @angularType string | object
283
+ * @reactType string | function | JSX.Element
284
+ * @vueType string | function
285
+ * @aspType string
286
+ */
287
+ @Property()
288
+ public itemTemplate: string | Function;
289
+
290
+ /**
291
+ * Specifies index of the current carousel item.
292
+ *
293
+ * @default 0
294
+ */
295
+ @Property(0)
296
+ public selectedIndex: number;
297
+
298
+ /**
299
+ * Specifies the width of the Carousel in pixels/number/percentage. The number value is considered as pixels.
300
+ *
301
+ * @default '100%'
302
+ */
303
+ @Property('100%')
304
+ public width: string | number;
305
+
306
+ /**
307
+ * Specifies the height of the Carousel in pixels/number/percentage. The number value is considered as pixels.
308
+ *
309
+ * @default '100%'
310
+ */
311
+ @Property('100%')
312
+ public height: string | number;
313
+
314
+ /**
315
+ * Specifies the interval duration in milliseconds for carousel item transition.
316
+ *
317
+ * @default 5000
318
+ */
319
+ @Property(5000)
320
+ public interval: number;
321
+
322
+ /**
323
+ * Defines whether the slide transition is automatic or manual.
324
+ *
325
+ * @default true
326
+ */
327
+ @Property(true)
328
+ public autoPlay: boolean;
329
+
330
+ /**
331
+ * Defines whether the slide transition gets pause on hover or not.
332
+ *
333
+ * @default true
334
+ */
335
+ @Property(true)
336
+ public pauseOnHover: boolean;
337
+
338
+ /**
339
+ * Defines whether the slide transitions loop end or not. When set to false, the transition stops at last slide.
340
+ *
341
+ * @default true
342
+ */
343
+ @Property(true)
344
+ public loop: boolean;
345
+
346
+ /**
347
+ * Defines whether to show play button or not.
348
+ *
349
+ * @default false
350
+ */
351
+ @Property(false)
352
+ public showPlayButton: boolean;
353
+
354
+ /**
355
+ * Defines whether to enable swipe action in touch devices or not.
356
+ *
357
+ * @default true
358
+ */
359
+ @Property(true)
360
+ public enableTouchSwipe: boolean;
361
+
362
+ /**
363
+ * Defines whether to enable keyboard actions or not.
364
+ *
365
+ * * @remarks
366
+ * If any form input component is placed on the carousel slide, interacting with it may cause
367
+ * the left/right arrow keys to navigate to other slides. Disabling keyboard interaction helps
368
+ * prevent this unintended navigation, leading to a smoother user experience.
369
+ *
370
+ * @default true
371
+ */
372
+ @Property(true)
373
+ public allowKeyboardInteraction: boolean;
374
+
375
+ /**
376
+ * Defines whether to show the indicator positions or not. The indicator positions allow to know the current slide position of the carousel component.
377
+ *
378
+ * @default true
379
+ */
380
+ @Property(true)
381
+ public showIndicators: boolean;
382
+
383
+ /**
384
+ * Specifies the type of indicators. The available values for this property are:
385
+ *
386
+ * * `Default`: Displays the indicators with a bullet design.
387
+ * * `Dynamic`: Applies a dynamic animation design to the indicators.
388
+ * * `Fraction`: Displays the slides numerically as indicators.
389
+ * * `Progress`: Represents the slides using a progress bar design.
390
+ *
391
+ * @default 'Default'
392
+ */
393
+ @Property('Default')
394
+ public indicatorsType: CarouselIndicatorsType;
395
+
396
+ /**
397
+ * Defines how to show the previous, next and play pause buttons visibility. The possible values for this property as follows
398
+ * * `Hidden`: Navigation buttons are hidden.
399
+ * * `Visible`: Navigation buttons are visible.
400
+ * * `VisibleOnHover`: Navigation buttons are visible only when we hover the carousel.
401
+ *
402
+ * @default 'Visible'
403
+ */
404
+ @Property('Visible')
405
+ public buttonsVisibility: CarouselButtonVisibility;
406
+
407
+ /**
408
+ * Enables active slide with partial previous/next slides.
409
+ *
410
+ * Slide animation only applicable if the partialVisible is enabled.
411
+ *
412
+ * @default false
413
+ */
414
+ @Property(false)
415
+ public partialVisible: boolean;
416
+
417
+ /**
418
+ * Specifies whether the slide transition should occur while performing swiping via touch/mouse.
419
+ * The slide swiping is enabled or disabled using bitwise operators. The swiping is disabled using ‘~’ bitwise operator.
420
+ * * Touch - Enables or disables the swiping action in touch interaction.
421
+ * * Mouse - Enables or disables the swiping action in mouse interaction.
422
+ *
423
+ * @default 'Touch'
424
+ * @aspNumberEnum
425
+ */
426
+ @Property(CarouselSwipeMode.Touch)
427
+ public swipeMode: CarouselSwipeMode;
428
+
429
+ /**
430
+ * Accepts HTML attributes/custom attributes to add in individual carousel item.
431
+ *
432
+ * @default null
433
+ */
434
+ @Property()
435
+ public htmlAttributes: Record<string, string>;
436
+
437
+ /**
438
+ * The event will be fired before the slide change.
439
+ *
440
+ * @event slideChanging
441
+ */
442
+ @Event()
443
+ public slideChanging: EmitType<SlideChangingEventArgs>;
444
+
445
+ /**
446
+ * The event will be fired after the slide changed.
447
+ *
448
+ * @event slideChanged
449
+ */
450
+ @Event()
451
+ public slideChanged: EmitType<SlideChangedEventArgs>;
452
+
453
+ /**
454
+ * Constructor for creating the Carousel widget
455
+ *
456
+ * @param {CarouselModel} options Accepts the carousel model properties to initiate the rendering
457
+ * @param {string | HTMLElement} element Accepts the DOM element reference
458
+ */
459
+ constructor(options?: CarouselModel, element?: string | HTMLElement) {
460
+ super(options, <HTMLElement | string>element);
461
+ }
462
+
463
+ protected getModuleName(): string {
464
+ return CLS_CAROUSEL.replace('e-', '');
465
+ }
466
+
467
+ protected getPersistData(): string {
468
+ return this.addOnPersist(['selectedIndex']);
469
+ }
470
+
471
+ protected preRender(): void {
472
+ this.keyConfigs = {
473
+ home: 'home',
474
+ end: 'end',
475
+ space: 'space',
476
+ moveLeft: 'leftarrow',
477
+ moveRight: 'rightarrow',
478
+ moveUp: 'uparrow',
479
+ moveDown: 'downarrow'
480
+ };
481
+ const defaultLocale: Record<string, any> = {
482
+ nextSlide: 'Next slide',
483
+ of: 'of',
484
+ pauseSlideTransition: 'Pause slide transition',
485
+ playSlideTransition: 'Play slide transition',
486
+ previousSlide: 'Previous slide',
487
+ slide: 'Slide',
488
+ slideShow: 'Slide show'
489
+ };
490
+ this.localeObj = new L10n(this.getModuleName(), defaultLocale, this.locale);
491
+ }
492
+
493
+ protected render(): void {
494
+ this.initialize();
495
+ this.renderSlides();
496
+ this.renderNavigators();
497
+ this.renderPlayButton();
498
+ this.renderIndicators();
499
+ this.applyAnimation();
500
+ this.wireEvents();
501
+ }
502
+
503
+ public onPropertyChanged(newProp: CarouselModel, oldProp: CarouselModel): void {
504
+ let target: Element;
505
+ let rtlElement: Element[];
506
+ for (const prop of Object.keys(newProp)) {
507
+ switch (prop) {
508
+ case 'animationEffect':
509
+ this.applyAnimation();
510
+ break;
511
+ case 'cssClass':
512
+ classList(this.element, [newProp.cssClass], [oldProp.cssClass]);
513
+ break;
514
+ case 'selectedIndex':
515
+ this.setActiveSlide(this.selectedIndex, oldProp.selectedIndex > this.selectedIndex ? 'Previous' : 'Next');
516
+ this.autoSlide();
517
+ break;
518
+ case 'htmlAttributes':
519
+ if (!isNullOrUndefined(this.htmlAttributes)) {
520
+ this.setHtmlAttributes(this.htmlAttributes, this.element);
521
+ }
522
+ break;
523
+ case 'enableTouchSwipe':
524
+ if (this.element.querySelector(`.${CLS_ITEMS}`)) {
525
+ this.renderTouchActions();
526
+ }
527
+ break;
528
+ case 'loop':
529
+ if (this.loop && isNullOrUndefined(this.autoSlideInterval)) {
530
+ this.applySlideInterval();
531
+ }
532
+ this.handleNavigatorsActions(this.selectedIndex);
533
+ if (this.partialVisible || !(this.swipeMode === (~CarouselSwipeMode.Touch & ~CarouselSwipeMode.Mouse))) {
534
+ this.reRenderSlides();
535
+ }
536
+ break;
537
+ case 'allowKeyboardInteraction':
538
+ if (this.keyModule) {
539
+ this.keyModule.destroy();
540
+ this.keyModule = null;
541
+ }
542
+ if (newProp.allowKeyboardInteraction) {
543
+ this.renderKeyboardActions();
544
+ }
545
+ break;
546
+ case 'enableRtl':
547
+ rtlElement = [].slice.call(this.element.querySelectorAll(`.${CLS_PREV_BUTTON},
548
+ .${CLS_NEXT_BUTTON}, .${CLS_PLAY_BUTTON}`));
549
+ rtlElement.push(this.element);
550
+ if (this.enableRtl) {
551
+ addClass(rtlElement, CLS_RTL);
552
+ } else {
553
+ removeClass(rtlElement, CLS_RTL);
554
+ }
555
+ if (this.partialVisible || !(this.swipeMode === (~CarouselSwipeMode.Touch & ~CarouselSwipeMode.Mouse))) {
556
+ const cloneCount: number = this.loop ? this.getNumOfItems() : 0;
557
+ const slideWidth: number = this.itemsContainer.firstElementChild.clientWidth;
558
+ this.itemsContainer.style.transform = this.getTranslateX(slideWidth, this.selectedIndex + cloneCount);
559
+ }
560
+ break;
561
+ case 'buttonsVisibility':
562
+ target = this.element.querySelector(`.${CLS_NAVIGATORS}`);
563
+ if (target) {
564
+ switch (this.buttonsVisibility) {
565
+ case 'Hidden':
566
+ this.resetTemplates(['previousButtonTemplate', 'nextButtonTemplate']);
567
+ remove(target);
568
+ break;
569
+ case 'VisibleOnHover':
570
+ addClass([].slice.call(target.childNodes), CLS_HOVER_ARROWS);
571
+ break;
572
+ case 'Visible':
573
+ removeClass([].slice.call(target.childNodes), CLS_HOVER_ARROWS);
574
+ break;
575
+ }
576
+ } else {
577
+ this.renderNavigators();
578
+ this.renderPlayButton();
579
+ }
580
+ break;
581
+ case 'width':
582
+ setStyleAttribute(this.element, { 'width': formatUnit(this.width) });
583
+ break;
584
+ case 'height':
585
+ setStyleAttribute(this.element, { 'height': formatUnit(this.height) });
586
+ break;
587
+ case 'autoPlay':
588
+ if (this.showPlayButton && isNullOrUndefined(this.playButtonTemplate)) {
589
+ this.playButtonClickHandler(null, true);
590
+ }
591
+ this.autoSlide();
592
+ break;
593
+ case 'interval':
594
+ this.autoSlide();
595
+ break;
596
+ case 'showIndicators':
597
+ case 'indicatorsType':
598
+ target = this.element.querySelector(`.${CLS_INDICATORS}`);
599
+ if (target) {
600
+ this.resetTemplates(['indicatorsTemplate']);
601
+ remove(target);
602
+ }
603
+ this.renderIndicators();
604
+ break;
605
+ case 'showPlayButton':
606
+ target = this.element.querySelector(`.${CLS_PLAY_PAUSE}`);
607
+ if (!this.showPlayButton && target) {
608
+ remove(target);
609
+ this.resetTemplates(['playButtonTemplate']);
610
+ }
611
+ this.renderPlayButton();
612
+ break;
613
+ case 'items':
614
+ case 'dataSource': {
615
+ const selectedData: Record<string, any>[] | CarouselItem[] = prop === 'dataSource' ? this.dataSource : this.items;
616
+ if (!isNullOrUndefined(selectedData) && selectedData.length > 0 && this.selectedIndex >= selectedData.length) {
617
+ this.setActiveSlide(selectedData.length - 1, 'Previous');
618
+ this.autoSlide();
619
+ }
620
+ this.reRenderSlides();
621
+ this.reRenderIndicators();
622
+ break;
623
+ }
624
+ case 'partialVisible':
625
+ if (this.partialVisible) {
626
+ addClass([this.element], CLS_PARTIAL);
627
+ } else {
628
+ removeClass([this.element], CLS_PARTIAL);
629
+ }
630
+ this.reRenderSlides();
631
+ break;
632
+ case 'swipeMode':
633
+ EventHandler.remove(this.element, 'mousedown touchstart', this.swipeStart);
634
+ EventHandler.remove(this.element, 'mousemove touchmove', this.swiping);
635
+ EventHandler.remove(this.element, 'mouseup touchend', this.swipStop);
636
+ this.swipeModehandlers();
637
+ this.reRenderSlides();
638
+ break;
639
+ }
640
+ }
641
+ }
642
+
643
+ private reRenderSlides(): void {
644
+ const target: Element = this.element.querySelector(`.${CLS_ITEMS}`);
645
+ if (target) {
646
+ this.resetTemplates(['itemTemplate']);
647
+ remove(target);
648
+ }
649
+ this.renderSlides();
650
+ }
651
+
652
+ private reRenderIndicators(): void {
653
+ const target: Element = this.element.querySelector(`.${CLS_INDICATORS}`);
654
+ if (target) {
655
+ this.resetTemplates(['indicatorsTemplate']);
656
+ remove(target);
657
+ }
658
+ this.renderIndicators();
659
+ }
660
+
661
+ private initialize(): void {
662
+ const carouselClasses: string[] = [];
663
+ carouselClasses.push(CLS_CAROUSEL);
664
+ if (this.cssClass) {
665
+ carouselClasses.push(this.cssClass);
666
+ }
667
+ if (this.enableRtl) {
668
+ carouselClasses.push(CLS_RTL);
669
+ }
670
+ if (this.partialVisible) {
671
+ carouselClasses.push(CLS_PARTIAL);
672
+ }
673
+ if (!(this.swipeMode === (~CarouselSwipeMode.Touch & ~CarouselSwipeMode.Mouse))) {
674
+ carouselClasses.push(CLS_SWIPE);
675
+ }
676
+ addClass([this.element], carouselClasses);
677
+ setStyleAttribute(this.element, { 'width': formatUnit(this.width), 'height': formatUnit(this.height) });
678
+ attributes(this.element, { 'role': 'group', 'aria-roledescription': 'carousel', 'aria-label': this.localeObj.getConstant('slideShow') });
679
+ if (!isNullOrUndefined(this.htmlAttributes)) {
680
+ this.setHtmlAttributes(this.htmlAttributes, this.element);
681
+ }
682
+ }
683
+
684
+ private renderSlides(): void {
685
+ let slideContainer: HTMLElement = this.element.querySelector('.' + CLS_SLIDE_CONTAINER);
686
+ if (!slideContainer) {
687
+ slideContainer = this.createElement('div', { className: CLS_SLIDE_CONTAINER, attrs: { 'tabindex': '0', 'role': 'tabpanel' } });
688
+ this.element.appendChild(slideContainer);
689
+ }
690
+ this.itemsContainer = this.createElement('div', { className: CLS_ITEMS, attrs: { 'aria-live': this.autoPlay ? 'off' : 'polite' } });
691
+ slideContainer.appendChild(this.itemsContainer);
692
+ const numOfItems: number = this.getNumOfItems();
693
+ if (numOfItems > 0 && this.loop) {
694
+ if (this.items.length > 0) {
695
+ this.items.slice(-numOfItems).forEach((item: CarouselItemModel, index: number) => {
696
+ this.renderSlide(item, item.template, index, this.itemsContainer, true);
697
+ });
698
+ }
699
+ else if (!isNullOrUndefined(this.dataSource) && this.dataSource.length > 0) {
700
+ this.dataSource.slice(-numOfItems).forEach((item: Record<string, any>, index: number) => {
701
+ this.renderSlide(item, this.itemTemplate, index, this.itemsContainer, true);
702
+ });
703
+ }
704
+ }
705
+ if (this.items.length > 0) {
706
+ this.slideItems = this.items as Record<string, any>[];
707
+ this.items.forEach((item: CarouselItemModel, index: number) => {
708
+ this.renderSlide(item, item.template, index, this.itemsContainer);
709
+ });
710
+ } else if (!isNullOrUndefined(this.dataSource) && this.dataSource.length > 0) {
711
+ this.slideItems = this.dataSource;
712
+ this.dataSource.forEach((item: Record<string, any>, index: number) => {
713
+ this.renderSlide(item, this.itemTemplate, index, this.itemsContainer);
714
+ });
715
+ }
716
+ if (numOfItems > 0 && this.loop) {
717
+ if (this.items.length > 0) {
718
+ this.items.slice(0, numOfItems).forEach((item: CarouselItemModel, index: number) => {
719
+ this.renderSlide(item, item.template, index, this.itemsContainer, true);
720
+ });
721
+ }
722
+ else if (!isNullOrUndefined(this.dataSource) && this.dataSource.length > 0) {
723
+ this.dataSource.slice(0, numOfItems).forEach((item: Record<string, any>, index: number) => {
724
+ this.renderSlide(item, this.itemTemplate, index, this.itemsContainer, true);
725
+ });
726
+ }
727
+ }
728
+ this.renderTemplates();
729
+ this.itemsContainer.style.setProperty('--carousel-items-count', `${this.itemsContainer.children.length}`);
730
+ const slideWidth: number = isNullOrUndefined(this.itemsContainer.firstElementChild) ? 0 :
731
+ this.itemsContainer.firstElementChild.clientWidth;
732
+ this.itemsContainer.style.transitionProperty = 'none';
733
+ const cloneCount: number = this.loop ? numOfItems : 0;
734
+ this.itemsContainer.style.transform = this.getTranslateX(slideWidth, this.selectedIndex + cloneCount);
735
+ this.autoSlide();
736
+ this.renderTouchActions();
737
+ this.renderKeyboardActions();
738
+ }
739
+
740
+ private getTranslateX(slideWidth: number, count: number = 1): string {
741
+ return this.enableRtl ? `translateX(${(slideWidth) * (count)}px)` :
742
+ `translateX(${-(slideWidth) * (count)}px)`;
743
+ }
744
+
745
+ private renderSlide(item: Record<string, any>, itemTemplate: string | Function, index: number, container: HTMLElement,
746
+ isClone: boolean = false): void {
747
+ const itemEle: HTMLElement = this.createElement('div', {
748
+ id: getUniqueID('carousel_item'),
749
+ className: `${CLS_ITEM} ${item.cssClass ? item.cssClass : ''} ${this.selectedIndex === index && !isClone ? CLS_ACTIVE : ''}`,
750
+ attrs: {
751
+ 'aria-hidden': this.selectedIndex === index && !isClone ? 'false' : 'true', 'data-index': index.toString(),
752
+ 'role': 'group', 'aria-roledescription': 'slide'
753
+ }
754
+ });
755
+ if (isClone) {
756
+ itemEle.classList.add(CLS_CLONED);
757
+ }
758
+ if (!(this.selectedIndex === index && !isClone)) {
759
+ itemEle.setAttribute('inert', 'true');
760
+ }
761
+ if (!isNullOrUndefined(item.htmlAttributes)) {
762
+ this.setHtmlAttributes(item.htmlAttributes, itemEle);
763
+ }
764
+ const templateId: string = this.element.id + '_template';
765
+ const template: HTMLElement[] = this.templateParser(itemTemplate)(item, this, 'itemTemplate', templateId, false);
766
+ append(template, itemEle);
767
+ container.appendChild(itemEle);
768
+ }
769
+
770
+ private renderNavigators(): void {
771
+ if (this.buttonsVisibility === 'Hidden') {
772
+ return;
773
+ }
774
+ const navigators: HTMLElement = this.createElement('div', { className: CLS_NAVIGATORS });
775
+ const itemsContainer: HTMLElement = this.element.querySelector(`.${CLS_SLIDE_CONTAINER}`) as HTMLElement;
776
+ itemsContainer.insertAdjacentElement('afterend', navigators);
777
+ if (!isNullOrUndefined(this.slideItems) && this.slideItems.length > 1) {
778
+ this.renderNavigatorButton('Previous');
779
+ this.renderNavigatorButton('Next');
780
+ }
781
+ this.renderTemplates();
782
+ }
783
+
784
+ private renderNavigatorButton(direction: CarouselSlideDirection): void {
785
+ const buttonContainer: HTMLElement = this.createElement('div', {
786
+ className: (direction === 'Previous' ? CLS_PREVIOUS : CLS_NEXT) + ' ' + (this.buttonsVisibility === 'VisibleOnHover' ? CLS_HOVER_ARROWS : '')
787
+ });
788
+ if (direction === 'Previous' && this.previousButtonTemplate) {
789
+ addClass([buttonContainer], CLS_TEMPLATE);
790
+ const templateId: string = this.element.id + '_previousButtonTemplate';
791
+ const template: HTMLElement[] = this.templateParser(this.previousButtonTemplate)({ type: 'Previous' }, this, 'previousButtonTemplate', templateId, false);
792
+ append(template, buttonContainer);
793
+ } else if (direction === 'Next' && this.nextButtonTemplate) {
794
+ addClass([buttonContainer], CLS_TEMPLATE);
795
+ const templateId: string = this.element.id + '_nextButtonTemplate';
796
+ const template: HTMLElement[] = this.templateParser(this.nextButtonTemplate)({ type: 'Next' }, this, 'nextButtonTemplate', templateId, false);
797
+ append(template, buttonContainer);
798
+ } else {
799
+ const button: HTMLElement = this.createElement('button', {
800
+ attrs: { 'aria-label': this.localeObj.getConstant(direction === 'Previous' ? 'previousSlide' : 'nextSlide'), 'type': 'button' }
801
+ });
802
+ const buttonObj: Button = new Button({
803
+ cssClass: CLS_FLAT + ' ' + CLS_ROUND + ' ' + (direction === 'Previous' ? CLS_PREV_BUTTON : CLS_NEXT_BUTTON),
804
+ iconCss: CLS_ICON + ' ' + (direction === 'Previous' ? CLS_PREV_ICON : CLS_NEXT_ICON),
805
+ enableRtl: this.enableRtl,
806
+ disabled: !this.loop && this.selectedIndex === (direction === 'Previous' ? 0 : this.slideItems.length - 1)
807
+ });
808
+ buttonObj.appendTo(button);
809
+ buttonContainer.appendChild(button);
810
+ }
811
+ this.element.querySelector('.' + CLS_NAVIGATORS).appendChild(buttonContainer);
812
+ EventHandler.add(buttonContainer, 'click', this.navigatorClickHandler, this);
813
+ }
814
+
815
+ private renderPlayButton(): void {
816
+ if (isNullOrUndefined(this.slideItems) || this.buttonsVisibility === 'Hidden' || !this.showPlayButton || this.slideItems.length <= 1) {
817
+ return;
818
+ }
819
+ const playPauseWrap: HTMLElement = this.createElement('div', {
820
+ className: CLS_PLAY_PAUSE + ' ' + (this.buttonsVisibility === 'VisibleOnHover' ? CLS_HOVER_ARROWS : '')
821
+ });
822
+ if (this.playButtonTemplate) {
823
+ addClass([playPauseWrap], CLS_TEMPLATE);
824
+ const templateId: string = this.element.id + '_playButtonTemplate';
825
+ const template: HTMLElement[] = this.templateParser(this.playButtonTemplate)({}, this, 'playButtonTemplate', templateId, false);
826
+ append(template, playPauseWrap);
827
+ } else {
828
+ const playButton: HTMLElement = this.createElement('button', {
829
+ attrs: { 'aria-label': this.localeObj.getConstant(this.autoPlay ? 'pauseSlideTransition' : 'playSlideTransition'), 'type': 'button' }
830
+ });
831
+ const isLastSlide: boolean = this.selectedIndex === this.slideItems.length - 1 && !this.loop;
832
+ const buttonObj: Button = new Button({
833
+ cssClass: CLS_FLAT + ' ' + CLS_ROUND + ' ' + CLS_PLAY_BUTTON,
834
+ iconCss: CLS_ICON + ' ' + (this.autoPlay && !isLastSlide ? CLS_PAUSE_ICON : CLS_PLAY_ICON),
835
+ isToggle: true,
836
+ enableRtl: this.enableRtl
837
+ });
838
+ if (isLastSlide) {
839
+ this.setProperties({ autoPlay: false }, true);
840
+ playButton.setAttribute('aria-label', this.localeObj.getConstant('playSlideTransition'));
841
+ this.itemsContainer.setAttribute('aria-live', 'polite');
842
+ }
843
+ buttonObj.appendTo(playButton);
844
+ playPauseWrap.appendChild(playButton);
845
+ }
846
+ const navigators: Element = this.element.querySelector(`.${CLS_NAVIGATORS}`);
847
+ navigators.insertBefore(playPauseWrap, navigators.lastElementChild);
848
+ this.renderTemplates();
849
+ EventHandler.add(playPauseWrap, 'click', this.playButtonClickHandler, this);
850
+ }
851
+
852
+ private renderIndicators(): void {
853
+ if (!this.showIndicators || isNullOrUndefined(this.indicatorsType)) {
854
+ return;
855
+ }
856
+ let indicatorClass: string = 'e-default';
857
+ if (!this.indicatorsTemplate) {
858
+ indicatorClass = `e-${this.indicatorsType.toLowerCase()}`;
859
+ }
860
+ const indicatorWrap: HTMLElement = this.createElement('div', { className: `${CLS_INDICATORS} ${indicatorClass}` });
861
+ const indicatorBars: HTMLElement = this.createElement('div', { className: CLS_INDICATOR_BARS });
862
+ indicatorWrap.appendChild(indicatorBars);
863
+ let progress: HTMLElement;
864
+ if (this.slideItems) {
865
+ switch (this.indicatorsType) {
866
+ case 'Fraction':
867
+ if (this.indicatorsTemplate) {
868
+ this.renderIndicatorTemplate(indicatorBars, this.selectedIndex + 1);
869
+ } else {
870
+ indicatorBars.innerText = `${this.selectedIndex + 1} / ${this.slideItems.length}`;
871
+ }
872
+ break;
873
+ case 'Progress':
874
+ if (this.indicatorsTemplate) {
875
+ this.renderIndicatorTemplate(indicatorBars, this.selectedIndex + 1);
876
+ }
877
+ else {
878
+ progress = this.createElement('div', { className: CLS_INDICATOR_BAR });
879
+ progress.style.setProperty('--carousel-items-current', `${this.selectedIndex + 1}`);
880
+ progress.style.setProperty('--carousel-items-count', `${this.slideItems.length}`);
881
+ indicatorBars.appendChild(progress);
882
+ }
883
+ break;
884
+ case 'Default':
885
+ case 'Dynamic':
886
+ this.slideItems.forEach((item: Record<string, any>, index: number) => {
887
+ const indicatorBar: HTMLElement = this.createElement('div', {
888
+ className: CLS_INDICATOR_BAR + ' ' + (this.selectedIndex === index ? CLS_ACTIVE : this.selectedIndex - 1 === index ? CLS_PREV_SLIDE : this.selectedIndex + 1 === index ? CLS_NEXT_SLIDE : ''),
889
+ attrs: { 'data-index': index.toString(), 'aria-current': this.selectedIndex === index ? 'true' : 'false' }
890
+ });
891
+ indicatorBar.style.setProperty('--carousel-items-current', `${this.selectedIndex}`);
892
+ if (this.indicatorsTemplate) {
893
+ this.renderIndicatorTemplate(indicatorBar, index);
894
+ } else if (this.indicatorsType === 'Default') {
895
+ const indicator: HTMLElement = this.createElement('button', { className: CLS_INDICATOR, attrs: { 'type': 'button', 'aria-label': this.localeObj.getConstant('slide') + ' ' + (index + 1) + ' ' + this.localeObj.getConstant('of') + ' ' + this.slideItems.length } });
896
+ indicatorBar.appendChild(indicator);
897
+ indicator.appendChild(this.createElement('div', {}));
898
+ const buttonObj: Button = new Button({ cssClass: 'e-flat e-small' });
899
+ buttonObj.appendTo(indicator);
900
+ }
901
+ indicatorBars.appendChild(indicatorBar);
902
+ if (this.indicatorsType === 'Default') {
903
+ EventHandler.add(indicatorBar, 'click', this.indicatorClickHandler, this);
904
+ }
905
+ });
906
+ break;
907
+ }
908
+ }
909
+ this.element.appendChild(indicatorWrap);
910
+ }
911
+
912
+ private renderIndicatorTemplate(indicatorBar: HTMLElement, index: number = 0): void {
913
+ addClass([indicatorBar], CLS_TEMPLATE);
914
+ const templateId: string = this.element.id + '_indicatorsTemplate';
915
+ const template: HTMLElement[] = this.templateParser(this.indicatorsTemplate)({ index: index, selectedIndex: this.selectedIndex }, this, 'indicatorsTemplate', templateId, false);
916
+ append(template, indicatorBar);
917
+ }
918
+
919
+ private renderKeyboardActions(): void {
920
+ if (!this.allowKeyboardInteraction) {
921
+ return;
922
+ }
923
+ this.keyModule = new KeyboardEvents(this.element, { keyAction: this.keyHandler.bind(this), keyConfigs: this.keyConfigs });
924
+ }
925
+
926
+ private renderTouchActions(): void {
927
+ this.touchModule = new Touch(this.element, { swipe: this.swipeHandler.bind(this) });
928
+ }
929
+
930
+ private applyAnimation(): void {
931
+ removeClass([this.element], [CLS_CUSTOM_ANIMATION, CLS_FADE_ANIMATION, CLS_SLIDE_ANIMATION, CLS_ANIMATION_NONE]);
932
+ switch (this.animationEffect) {
933
+ case 'Slide':
934
+ addClass([this.element], CLS_SLIDE_ANIMATION);
935
+ break;
936
+ case 'Fade':
937
+ addClass([this.element], CLS_FADE_ANIMATION);
938
+ break;
939
+ case 'None':
940
+ addClass([this.element], CLS_ANIMATION_NONE);
941
+ break;
942
+ case 'Custom':
943
+ addClass([this.element], CLS_CUSTOM_ANIMATION);
944
+ break;
945
+ }
946
+ }
947
+
948
+ private autoSlide(): void {
949
+ if (isNullOrUndefined(this.slideItems) || this.slideItems.length <= 1) {
950
+ return;
951
+ }
952
+ this.resetSlideInterval();
953
+ this.applySlideInterval();
954
+ }
955
+
956
+ private autoSlideChange(): void {
957
+ const activeSlide: HTMLElement | null = this.element.querySelector(`.${CLS_ITEM}.${CLS_ACTIVE}`)
958
+ || this.element.querySelector(`.${CLS_INDICATORS} .${CLS_ACTIVE}`) as HTMLElement;
959
+ if (isNullOrUndefined(activeSlide)) { return; }
960
+ const activeIndex: number = parseInt(activeSlide.dataset.index, 10);
961
+ if (!this.loop && activeIndex === this.slideItems.length - 1) {
962
+ this.resetSlideInterval();
963
+ } else {
964
+ const index: number = (activeIndex + 1) % this.slideItems.length;
965
+ if (!this.element.classList.contains(CLS_HOVER)) {
966
+ this.setActiveSlide(index, 'Next');
967
+ }
968
+ this.autoSlide();
969
+ }
970
+ }
971
+
972
+ private applySlideInterval(): void {
973
+ if (!this.autoPlay || this.element.classList.contains(CLS_HOVER)) {
974
+ return;
975
+ }
976
+ let itemInterval: number = this.interval;
977
+ if (this.items.length > 0 && !isNullOrUndefined(this.items[this.selectedIndex || 0].interval)) {
978
+ itemInterval = this.items[this.selectedIndex || 0].interval;
979
+ }
980
+ this.autoSlideInterval = setInterval(() => this.autoSlideChange(), itemInterval);
981
+ }
982
+
983
+ private resetSlideInterval(): void {
984
+ clearInterval(this.autoSlideInterval);
985
+ this.autoSlideInterval = null;
986
+ }
987
+
988
+ private getSlideIndex(direction: CarouselSlideDirection): number {
989
+ let currentIndex: number = this.selectedIndex || 0;
990
+ if (direction === 'Previous') {
991
+ currentIndex--;
992
+ if (currentIndex < 0) {
993
+ currentIndex = this.slideItems.length - 1;
994
+ }
995
+ } else {
996
+ currentIndex++;
997
+ if (currentIndex === this.slideItems.length) {
998
+ currentIndex = 0;
999
+ }
1000
+ }
1001
+ return currentIndex;
1002
+ }
1003
+
1004
+ private setActiveSlide(currentIndex: number, direction: CarouselSlideDirection, isSwiped: boolean = false): void {
1005
+ if (this.element.querySelectorAll(`.${CLS_ITEM}.${CLS_PREV_SLIDE},.${CLS_ITEM}.${CLS_NEXT_SLIDE}`).length > 0) {
1006
+ return;
1007
+ }
1008
+ currentIndex = isNullOrUndefined(currentIndex) ? 0 : currentIndex;
1009
+ const allSlides: HTMLElement[] = [].slice.call(this.element.querySelectorAll(`.${CLS_ITEM}:not(.e-cloned)`));
1010
+ const activeSlide: HTMLElement = this.element.querySelector(`.${CLS_ITEM}.${CLS_ACTIVE}`);
1011
+ if (isNullOrUndefined(activeSlide) && this.showIndicators) {
1012
+ const activeIndicator: HTMLElement = this.element.querySelector(`.${CLS_INDICATOR_BAR}.${CLS_ACTIVE}`) as HTMLElement;
1013
+ const activeIndex: number = parseInt(activeIndicator.dataset.index, 10);
1014
+ addClass([allSlides[parseInt(activeIndex.toString(), 10)]], CLS_ACTIVE);
1015
+ return;
1016
+ } else if (isNullOrUndefined(activeSlide)) {
1017
+ addClass([allSlides[parseInt(currentIndex.toString(), 10)]], CLS_ACTIVE);
1018
+ return;
1019
+ }
1020
+ const activeIndex: number = parseInt(activeSlide.dataset.index, 10);
1021
+ const currentSlide: HTMLElement = allSlides[parseInt(currentIndex.toString(), 10)];
1022
+ const eventArgs: SlideChangingEventArgs = {
1023
+ currentIndex: activeIndex,
1024
+ nextIndex: currentIndex,
1025
+ currentSlide: activeSlide,
1026
+ nextSlide: currentSlide,
1027
+ slideDirection: direction,
1028
+ isSwiped: isSwiped,
1029
+ cancel: false
1030
+ };
1031
+ this.trigger('slideChanging', eventArgs, (args: SlideChangingEventArgs) => {
1032
+ if (args.cancel) {
1033
+ return;
1034
+ }
1035
+ this.setProperties({ selectedIndex: currentIndex }, true);
1036
+ attributes(args.currentSlide, { 'aria-hidden': 'true' });
1037
+ args.currentSlide.setAttribute('inert', 'true');
1038
+ attributes(args.nextSlide, { 'aria-hidden': 'false' });
1039
+ args.nextSlide.removeAttribute('inert');
1040
+ this.refreshIndicators(activeIndex, currentIndex);
1041
+ this.slideChangedEventArgs = {
1042
+ currentIndex: args.nextIndex,
1043
+ previousIndex: args.currentIndex,
1044
+ currentSlide: args.nextSlide,
1045
+ previousSlide: args.currentSlide,
1046
+ slideDirection: direction,
1047
+ isSwiped: isSwiped
1048
+ };
1049
+ const slideWidth: number = allSlides[parseInt(currentIndex.toString(), 10)].clientWidth;
1050
+ const numOfItems: number = this.getNumOfItems();
1051
+ if (!this.isSwipe) {
1052
+ this.itemsContainer.style.transitionDuration = '0.6s';
1053
+ }
1054
+ this.isSwipe = false;
1055
+ if ((this.animationEffect === 'Fade')) {
1056
+ this.itemsContainer.classList.add('e-fade-in-out');
1057
+ } else {
1058
+ this.itemsContainer.style.transitionProperty = 'transform';
1059
+ }
1060
+ if (this.loop) {
1061
+ if (this.slideChangedEventArgs.currentIndex === 0 && this.slideChangedEventArgs.slideDirection === 'Next') {
1062
+ this.itemsContainer.style.transform = this.getTranslateX(slideWidth, allSlides.length + numOfItems);
1063
+ } else if (this.slideChangedEventArgs.currentIndex === this.slideItems.length - 1 && this.slideChangedEventArgs.slideDirection === 'Previous') {
1064
+ this.itemsContainer.style.transform = this.partialVisible ? this.getTranslateX(slideWidth) : 'translateX(0px)';
1065
+ } else {
1066
+ this.itemsContainer.style.transform = this.getTranslateX(slideWidth, currentIndex + numOfItems);
1067
+ }
1068
+ } else {
1069
+ this.itemsContainer.style.transform = this.getTranslateX(slideWidth, currentIndex);
1070
+ }
1071
+ if (this.animationEffect === 'Slide') {
1072
+ if (direction === 'Previous') {
1073
+ addClass([args.nextSlide], CLS_PREV_SLIDE);
1074
+ args.nextSlide.setAttribute('data-slide-height', args.nextSlide.offsetHeight.toString());
1075
+ addClass([args.currentSlide, args.nextSlide], CLS_TRANSITION_END);
1076
+ } else {
1077
+ addClass([args.nextSlide], CLS_NEXT_SLIDE);
1078
+ args.nextSlide.setAttribute('data-slide-height', args.nextSlide.offsetHeight.toString());
1079
+ addClass([args.currentSlide, args.nextSlide], CLS_TRANSITION_START);
1080
+ }
1081
+ } else if (this.animationEffect === 'Fade') {
1082
+ removeClass([args.currentSlide], CLS_ACTIVE);
1083
+ addClass([args.nextSlide], CLS_ACTIVE);
1084
+ } else if (this.animationEffect === 'Custom') {
1085
+ if (direction === 'Previous') {
1086
+ addClass([args.nextSlide], CLS_NEXT_SLIDE);
1087
+ addClass([args.currentSlide], CLS_PREV_SLIDE);
1088
+ } else {
1089
+ addClass([args.currentSlide], CLS_PREV_SLIDE);
1090
+ addClass([args.nextSlide], CLS_NEXT_SLIDE);
1091
+ }
1092
+ } else {
1093
+ this.onTransitionEnd();
1094
+ }
1095
+ this.handleNavigatorsActions(currentIndex);
1096
+ });
1097
+ }
1098
+
1099
+ private onTransitionEnd(): void {
1100
+ removeClass(this.element.querySelectorAll(`.${CLS_ITEMS}`), 'e-fade-in-out');
1101
+ const numOfItems: number = this.getNumOfItems();
1102
+ if (this.slideChangedEventArgs) {
1103
+ this.itemsContainer.style.transitionProperty = 'none';
1104
+ if (this.loop && (this.slideChangedEventArgs.currentIndex === 0 && this.slideChangedEventArgs.slideDirection === 'Next' ||
1105
+ this.slideChangedEventArgs.currentIndex === this.slideItems.length - 1 && this.slideChangedEventArgs.slideDirection === 'Previous')) {
1106
+ const slideWidth: number = this.slideChangedEventArgs.currentSlide.clientWidth;
1107
+ this.itemsContainer.style.transform = this.getTranslateX(slideWidth, this.slideChangedEventArgs.currentIndex + numOfItems);
1108
+ }
1109
+ addClass([this.slideChangedEventArgs.currentSlide], CLS_ACTIVE);
1110
+ removeClass([this.slideChangedEventArgs.previousSlide], CLS_ACTIVE);
1111
+ this.trigger('slideChanged', this.slideChangedEventArgs, () => {
1112
+ removeClass(this.element.querySelectorAll(`.${CLS_ITEM}`), [CLS_PREV_SLIDE, CLS_NEXT_SLIDE, CLS_TRANSITION_START, CLS_TRANSITION_END]);
1113
+ this.slideChangedEventArgs = null;
1114
+ });
1115
+ }
1116
+ }
1117
+
1118
+ private refreshIndicators(activeIndex: number, currentIndex: number): void {
1119
+ const slideIndicator: HTMLElement = this.element.querySelector(`.${CLS_INDICATOR_BARS}`);
1120
+ if (isNullOrUndefined(slideIndicator)) { return; }
1121
+ const indicators: HTMLElement[] = [].slice.call(slideIndicator.childNodes);
1122
+ switch (this.indicatorsType) {
1123
+ case 'Default':
1124
+ case 'Dynamic':
1125
+ attributes(indicators[parseInt(activeIndex.toString(), 10)], { 'aria-current': 'false' });
1126
+ attributes(indicators[parseInt(currentIndex.toString(), 10)], { 'aria-current': 'true' });
1127
+ removeClass(indicators, [CLS_ACTIVE, CLS_PREV_SLIDE, CLS_NEXT_SLIDE]);
1128
+ addClass([indicators[parseInt(currentIndex.toString(), 10)]], CLS_ACTIVE);
1129
+ if (indicators[currentIndex - 1]) {
1130
+ addClass([indicators[currentIndex - 1]], CLS_PREV_SLIDE);
1131
+ }
1132
+ if (indicators[currentIndex + 1]) {
1133
+ addClass([indicators[currentIndex + 1]], CLS_NEXT_SLIDE);
1134
+ }
1135
+ indicators.forEach((item: HTMLElement) => item.style.setProperty('--carousel-items-current', `${this.selectedIndex}`));
1136
+ break;
1137
+ case 'Fraction':
1138
+ if (this.indicatorsTemplate) {
1139
+ if (slideIndicator.children.length > 0) {
1140
+ slideIndicator.removeChild(slideIndicator.firstElementChild);
1141
+ }
1142
+ this.renderIndicatorTemplate(slideIndicator, currentIndex + 1);
1143
+ }
1144
+ else {
1145
+ slideIndicator.innerText = `${this.selectedIndex + 1} / ${this.slideItems.length}`;
1146
+ }
1147
+ break;
1148
+ case 'Progress':
1149
+ if (this.indicatorsTemplate) {
1150
+ if (slideIndicator.children.length > 0) {
1151
+ slideIndicator.removeChild(slideIndicator.firstElementChild);
1152
+ }
1153
+ this.renderIndicatorTemplate(slideIndicator, currentIndex + 1);
1154
+ }
1155
+ else {
1156
+ (slideIndicator.firstElementChild as HTMLElement).style.setProperty('--carousel-items-current', `${this.selectedIndex + 1}`);
1157
+ }
1158
+ break;
1159
+ }
1160
+ }
1161
+
1162
+ private setHtmlAttributes(attribute: Record<string, string>, element: HTMLElement): void {
1163
+ const keys: string[] = Object.keys(attribute);
1164
+ for (const key of keys) {
1165
+ if (key === 'class') {
1166
+ addClass([element], attribute[`${key}`]);
1167
+ } else {
1168
+ element.setAttribute(key, attribute[`${key}`]);
1169
+ }
1170
+ }
1171
+ }
1172
+
1173
+ private templateParser(template: string | Function): Function {
1174
+ if (template) {
1175
+ try {
1176
+ if (typeof template !== 'function' && document.querySelectorAll(template).length) {
1177
+ return compile(document.querySelector(template).innerHTML.trim());
1178
+ } else {
1179
+ return compile(template);
1180
+ }
1181
+ } catch (error) {
1182
+ return compile(template);
1183
+ }
1184
+ }
1185
+ return undefined;
1186
+ }
1187
+
1188
+ private getNavigatorState(target: HTMLElement, isPrevious: boolean): boolean {
1189
+ const button: HTMLElement = target.querySelector(`.${isPrevious ? CLS_PREV_BUTTON : CLS_NEXT_BUTTON}`) as HTMLElement;
1190
+ if (button) {
1191
+ const buttonObj: Button = getInstance(button, Button) as Button;
1192
+ return buttonObj.disabled;
1193
+ }
1194
+ return false;
1195
+ }
1196
+
1197
+ private navigatorClickHandler(e: Event): void {
1198
+ const target: HTMLElement = e.currentTarget as HTMLElement;
1199
+ const isDisabled: boolean = this.getNavigatorState(target, target.classList.contains(CLS_PREVIOUS));
1200
+ if (isDisabled) { return; }
1201
+ const direction: CarouselSlideDirection = target.classList.contains(CLS_PREVIOUS) ? 'Previous' : 'Next';
1202
+ this.setActiveSlide(this.getSlideIndex(direction), direction);
1203
+ this.autoSlide();
1204
+ }
1205
+
1206
+ private indicatorClickHandler(e: Event): void {
1207
+ const target: HTMLElement = closest(e.target as Element, `.${CLS_INDICATOR_BAR}`) as HTMLElement;
1208
+ const index: number = parseInt(target.dataset.index, 10);
1209
+ if (this.selectedIndex !== index) {
1210
+ this.setActiveSlide(index, this.selectedIndex > index ? 'Previous' : 'Next');
1211
+ this.autoSlide();
1212
+ }
1213
+ }
1214
+
1215
+ private playButtonClickHandler(e: Event, isPropertyChange: boolean = false): void {
1216
+ const playButton: HTMLElement = this.element.querySelector(`.${CLS_PLAY_BUTTON}`) as HTMLElement;
1217
+ if (playButton) {
1218
+ const buttonObj: Button = getInstance(playButton, Button) as Button;
1219
+ if (!isPropertyChange) {
1220
+ this.setProperties({ autoPlay: !this.autoPlay }, true);
1221
+ }
1222
+ playButton.setAttribute('aria-label', this.localeObj.getConstant(this.autoPlay ? 'pauseSlideTransition' : 'playSlideTransition'));
1223
+ buttonObj.iconCss = CLS_ICON + ' ' + (this.autoPlay ? CLS_PAUSE_ICON : CLS_PLAY_ICON);
1224
+ buttonObj.dataBind();
1225
+ this.itemsContainer.setAttribute('aria-live', this.autoPlay ? 'off' : 'polite');
1226
+ if (this.autoPlay && !this.loop && this.selectedIndex === this.slideItems.length - 1) {
1227
+ this.setActiveSlide(0, 'Next');
1228
+ }
1229
+ this.autoSlide();
1230
+ }
1231
+ }
1232
+
1233
+ private keyHandler(e: KeyboardEventArgs): void {
1234
+ if (!this.allowKeyboardInteraction) { return; }
1235
+ let direction: CarouselSlideDirection;
1236
+ let slideIndex: number;
1237
+ let isSlideTransition: boolean = false;
1238
+ const target: HTMLElement = e.target as HTMLElement;
1239
+ e.preventDefault();
1240
+ switch (e.action) {
1241
+ case 'space':
1242
+ if (this.showIndicators && target.classList.contains(CLS_INDICATOR)) {
1243
+ target.click();
1244
+ } else if (target.classList.contains(CLS_CAROUSEL) || target.classList.contains(CLS_PLAY_BUTTON)) {
1245
+ this.playButtonClickHandler(e);
1246
+ } else if (target.classList.contains(CLS_NEXT_BUTTON)) {
1247
+ this.next();
1248
+ } else if (target.classList.contains(CLS_PREV_BUTTON)) {
1249
+ this.prev();
1250
+ }
1251
+ break;
1252
+ case 'end':
1253
+ slideIndex = this.slideItems.length - 1;
1254
+ direction = 'Next';
1255
+ isSlideTransition = true;
1256
+ break;
1257
+ case 'home':
1258
+ slideIndex = 0;
1259
+ direction = 'Previous';
1260
+ isSlideTransition = true;
1261
+ break;
1262
+ case 'moveUp':
1263
+ case 'moveLeft':
1264
+ case 'moveDown':
1265
+ case 'moveRight':
1266
+ if (this.showIndicators && isNullOrUndefined(this.indicatorsTemplate)) {
1267
+ this.element.focus();
1268
+ }
1269
+ direction = (e.action === 'moveUp' || e.action === 'moveLeft') ? 'Previous' : 'Next';
1270
+ slideIndex = this.getSlideIndex(direction);
1271
+ isSlideTransition = !this.isSuspendSlideTransition(slideIndex, direction);
1272
+ break;
1273
+ }
1274
+ if (isSlideTransition) {
1275
+ this.setActiveSlide(slideIndex, direction);
1276
+ this.autoSlide();
1277
+ isSlideTransition = false;
1278
+ }
1279
+ }
1280
+
1281
+ private swipeHandler(e: SwipeEventArgs): void {
1282
+ if (this.element.classList.contains(CLS_HOVER) || isNullOrUndefined(this.slideItems) || this.slideItems.length <= 1) {
1283
+ return;
1284
+ }
1285
+ if (this.swipeMode === (~CarouselSwipeMode.Touch & ~CarouselSwipeMode.Mouse)) {
1286
+ return;
1287
+ }
1288
+ const eventType: string = e.startEvents ? e.startEvents.toString() : null;
1289
+ if (eventType && ((this.swipeMode === CarouselSwipeMode.Mouse && eventType.includes('Touch')) ||
1290
+ (this.swipeMode === CarouselSwipeMode.Touch && eventType.includes('Mouse')))) {
1291
+ return;
1292
+ }
1293
+ const direction: CarouselSlideDirection = (e.swipeDirection === 'Right') ? 'Previous' : 'Next';
1294
+ const slideIndex: number = this.getSlideIndex(direction);
1295
+ if (!this.isSuspendSlideTransition(slideIndex, direction)) {
1296
+ this.setActiveSlide(slideIndex, direction, true);
1297
+ this.autoSlide();
1298
+ }
1299
+ }
1300
+
1301
+ private isSuspendSlideTransition(index: number, direction: CarouselSlideDirection): boolean {
1302
+ return !this.loop && (direction === 'Next' && index === 0 || direction === 'Previous' && index === this.slideItems.length - 1);
1303
+ }
1304
+
1305
+ private handleNavigatorsActions(index: number): void {
1306
+ if (this.buttonsVisibility === 'Hidden') {
1307
+ return;
1308
+ }
1309
+ if (this.showPlayButton) {
1310
+ const playButton: HTMLElement = this.element.querySelector(`.${CLS_PLAY_BUTTON}`) as HTMLElement;
1311
+ const isLastSlide: boolean = this.selectedIndex === this.slideItems.length - 1 && !this.loop;
1312
+ let isButtonUpdate: boolean = isNullOrUndefined(this.playButtonTemplate) && playButton && isLastSlide;
1313
+ if (isNullOrUndefined(this.playButtonTemplate) && playButton && !isLastSlide) {
1314
+ isButtonUpdate = !playButton.classList.contains(CLS_ACTIVE);
1315
+ }
1316
+ if (isButtonUpdate) {
1317
+ this.setProperties({ autoPlay: !isLastSlide }, true);
1318
+ playButton.setAttribute('aria-label', this.localeObj.getConstant(this.autoPlay ? 'pauseSlideTransition' : 'playSlideTransition'));
1319
+ this.itemsContainer.setAttribute('aria-live', this.autoPlay ? 'off' : 'polite');
1320
+ const buttonObj: Button = getInstance(playButton, Button) as Button;
1321
+ buttonObj.iconCss = CLS_ICON + ' ' + (this.autoPlay ? CLS_PAUSE_ICON : CLS_PLAY_ICON);
1322
+ buttonObj.dataBind();
1323
+ }
1324
+ }
1325
+ const prevButton: HTMLElement = this.element.querySelector(`.${CLS_PREV_BUTTON}`) as HTMLElement;
1326
+ if (prevButton && isNullOrUndefined(this.previousButtonTemplate)) {
1327
+ const buttonObj: Button = getInstance(prevButton, Button) as Button;
1328
+ buttonObj.disabled = !this.loop && index === 0;
1329
+ buttonObj.dataBind();
1330
+ }
1331
+ const nextButton: HTMLElement = this.element.querySelector(`.${CLS_NEXT_BUTTON}`) as HTMLElement;
1332
+ if (nextButton && isNullOrUndefined(this.nextButtonTemplate)) {
1333
+ const buttonObj: Button = getInstance(nextButton, Button) as Button;
1334
+ buttonObj.disabled = !this.loop && index === this.slideItems.length - 1;
1335
+ buttonObj.dataBind();
1336
+ }
1337
+ }
1338
+
1339
+ private onHoverActions(e: Event): void {
1340
+ const navigator: HTMLElement = this.element.querySelector(`.${CLS_NAVIGATORS}`);
1341
+ switch (e.type) {
1342
+ case 'mouseenter':
1343
+ if (this.buttonsVisibility === 'VisibleOnHover' && navigator) {
1344
+ removeClass([].slice.call(navigator.childNodes), CLS_HOVER_ARROWS);
1345
+ }
1346
+ if (this.pauseOnHover) {
1347
+ addClass([this.element], CLS_HOVER);
1348
+ }
1349
+ break;
1350
+ case 'mouseleave':
1351
+ if (this.buttonsVisibility === 'VisibleOnHover' && navigator) {
1352
+ addClass([].slice.call(navigator.childNodes), CLS_HOVER_ARROWS);
1353
+ }
1354
+ removeClass([this.element], CLS_HOVER);
1355
+ if (this.isSwipe) {
1356
+ this.swipStop();
1357
+ }
1358
+ break;
1359
+ }
1360
+ this.autoSlide();
1361
+ }
1362
+
1363
+ private onFocusActions(e: Event): void {
1364
+ switch (e.type) {
1365
+ case 'focusin':
1366
+ addClass([this.element], CLS_HOVER);
1367
+ break;
1368
+ case 'focusout':
1369
+ removeClass([this.element], CLS_HOVER);
1370
+ break;
1371
+ }
1372
+ this.autoSlide();
1373
+ }
1374
+
1375
+ private destroyButtons(): void {
1376
+ const buttonCollections: HTMLElement[] = [].slice.call(this.element.querySelectorAll('.e-control.e-btn'));
1377
+ for (const button of buttonCollections) {
1378
+ const instance: Button = getInstance(button, Button) as Button;
1379
+ if (instance) {
1380
+ instance.destroy();
1381
+ }
1382
+ }
1383
+ }
1384
+
1385
+ private getNumOfItems(): number {
1386
+ return this.partialVisible ? 2 : 1;
1387
+ }
1388
+
1389
+ private getTranslateValue(element: HTMLElement | Element): number {
1390
+ const style: CSSStyleDeclaration = getComputedStyle(element);
1391
+ return (<Record<string, any> & Window><unknown>window).WebKitCSSMatrix ?
1392
+ new WebKitCSSMatrix(style.webkitTransform).m41 : 0;
1393
+ }
1394
+
1395
+ private swipeStart(e: MouseEvent & TouchEvent): void {
1396
+ if (!this.timeStampStart) {
1397
+ this.timeStampStart = Date.now();
1398
+ }
1399
+ e.preventDefault();
1400
+ this.isSwipe = false;
1401
+ this.itemsContainer.classList.add('e-swipe-start');
1402
+ this.prevPageX = e.touches ? e.touches[0].pageX : e.pageX;
1403
+ this.initialTranslate = this.getTranslateValue(this.itemsContainer);
1404
+ }
1405
+
1406
+ private swiping(e: MouseEvent & TouchEvent): void {
1407
+ if (!this.itemsContainer.classList.contains('e-swipe-start')) {
1408
+ return;
1409
+ }
1410
+ this.isSwipe = true;
1411
+ e.preventDefault();
1412
+ const pageX: number = e.touches ? e.touches[0].pageX : e.pageX;
1413
+ const positionDiff: number = this.prevPageX - (pageX);
1414
+ if (!this.loop && (
1415
+ (this.enableRtl && ((this.selectedIndex === 0 && positionDiff > 0) ||
1416
+ (this.selectedIndex === this.itemsContainer.childElementCount - 1 && positionDiff < 0))) ||
1417
+ (!this.enableRtl && ((this.selectedIndex === 0 && positionDiff < 0) ||
1418
+ (this.selectedIndex === this.itemsContainer.childElementCount - 1 && positionDiff > 0)))
1419
+ )) {
1420
+ return;
1421
+ }
1422
+ this.itemsContainer.style.transform = `translateX(${this.initialTranslate + (this.enableRtl ? positionDiff : -positionDiff)}px)`;
1423
+ }
1424
+
1425
+ private swipStop(): void {
1426
+ const time: number = Date.now() - this.timeStampStart;
1427
+ let distanceX: number = this.getTranslateValue(this.itemsContainer) - this.initialTranslate;
1428
+ distanceX = distanceX < 0 ? distanceX * -1 : distanceX;
1429
+ if (this.isSwipe) {
1430
+ const offsetDist: number = distanceX * (Browser.isDevice ? 6 : 1.66);
1431
+ this.itemsContainer.style.transitionDuration = (((Browser.isDevice ? distanceX : offsetDist) / time) / 10) + 's';
1432
+ }
1433
+ const slideWidth: number = this.itemsContainer.firstElementChild.clientWidth;
1434
+ const threshold: number = slideWidth / 2;
1435
+ this.itemsContainer.classList.remove('e-swipe-start');
1436
+ const value: number = this.getTranslateValue(this.itemsContainer);
1437
+ if (value - this.initialTranslate < -threshold) {
1438
+ this.swipeNavigation(!this.enableRtl);
1439
+ }
1440
+ else if (value - this.initialTranslate > threshold) {
1441
+ this.swipeNavigation(this.enableRtl);
1442
+ }
1443
+ else {
1444
+ this.itemsContainer.style.transform = `translateX(${this.initialTranslate}px)`;
1445
+ if (this.animationEffect === 'Fade') {
1446
+ this.itemsContainer.classList.add('e-fade-in-out');
1447
+ }
1448
+ }
1449
+ }
1450
+
1451
+ private swipeNavigation(isRtl: boolean): void {
1452
+ if (isRtl) {
1453
+ this.next();
1454
+ } else {
1455
+ this.prev();
1456
+ }
1457
+ }
1458
+
1459
+ private swipeModehandlers(): void {
1460
+ if ((this.swipeMode & CarouselSwipeMode.Touch) === CarouselSwipeMode.Touch) {
1461
+ EventHandler.add(this.itemsContainer, 'touchstart', this.swipeStart, this);
1462
+ EventHandler.add(this.itemsContainer, 'touchmove', this.swiping, this);
1463
+ EventHandler.add(this.itemsContainer, 'touchend', this.swipStop, this);
1464
+ }
1465
+ if ((this.swipeMode & CarouselSwipeMode.Mouse) === CarouselSwipeMode.Mouse) {
1466
+ EventHandler.add(this.itemsContainer, 'mousedown', this.swipeStart, this);
1467
+ EventHandler.add(this.itemsContainer, 'mousemove', this.swiping, this);
1468
+ EventHandler.add(this.itemsContainer, 'mouseup', this.swipStop, this);
1469
+ }
1470
+ if ((this.swipeMode === 0) && (this.swipeMode & CarouselSwipeMode.Mouse & CarouselSwipeMode.Touch) ===
1471
+ (CarouselSwipeMode.Mouse & CarouselSwipeMode.Touch)) {
1472
+ EventHandler.add(this.itemsContainer, 'mousedown touchstart', this.swipeStart, this);
1473
+ EventHandler.add(this.itemsContainer, 'mousemove touchmove', this.swiping, this);
1474
+ EventHandler.add(this.itemsContainer, 'mouseup touchend', this.swipStop, this);
1475
+ }
1476
+ }
1477
+
1478
+ private resizeHandler(): void {
1479
+ if (this.itemsContainer && this.itemsContainer.firstElementChild) {
1480
+ const numOfItems: number = this.getNumOfItems();
1481
+ const slideWidth: number = this.itemsContainer.firstElementChild.clientWidth;
1482
+ if (this.loop) {
1483
+ this.itemsContainer.style.transform = this.getTranslateX(slideWidth, this.selectedIndex + numOfItems);
1484
+ }
1485
+ else {
1486
+ this.itemsContainer.style.transform = this.getTranslateX(slideWidth, this.selectedIndex);
1487
+ }
1488
+ }
1489
+ }
1490
+
1491
+ private wireEvents(): void {
1492
+ if (this.animationEffect !== 'Custom') {
1493
+ this.swipeModehandlers();
1494
+ }
1495
+ EventHandler.add(this.element, 'focusin focusout', this.onFocusActions, this);
1496
+ EventHandler.add(this.element, 'mouseenter mouseleave', this.onHoverActions, this);
1497
+ EventHandler.add(this.element.firstElementChild, 'animationend', this.onTransitionEnd, this);
1498
+ EventHandler.add(this.element.firstElementChild, 'transitionend', this.onTransitionEnd, this);
1499
+ EventHandler.add(<HTMLElement & Window><unknown>window, 'resize', this.resizeHandler, this);
1500
+ }
1501
+
1502
+ private unWireEvents(): void {
1503
+ const indicators: HTMLElement[] = [].slice.call(this.element.querySelectorAll(`.${CLS_INDICATOR_BAR}`));
1504
+ indicators.forEach((indicator: Element) => {
1505
+ EventHandler.remove(indicator, 'click', this.indicatorClickHandler);
1506
+ });
1507
+ const navigators: HTMLElement[] = [].slice.call(this.element.querySelectorAll(`.${CLS_PREVIOUS},.${CLS_NEXT}`));
1508
+ navigators.forEach((navigator: HTMLElement) => {
1509
+ EventHandler.remove(navigator, 'click', this.navigatorClickHandler);
1510
+ });
1511
+ const playIcon: Element = this.element.querySelector(`.${CLS_PLAY_PAUSE}`);
1512
+ if (playIcon) {
1513
+ EventHandler.remove(playIcon, 'click', this.playButtonClickHandler);
1514
+ }
1515
+ EventHandler.remove(this.element.firstElementChild, 'animationend', this.onTransitionEnd);
1516
+ EventHandler.remove(this.element.firstElementChild, 'transitionend', this.onTransitionEnd);
1517
+ EventHandler.clearEvents(this.element);
1518
+ EventHandler.clearEvents(this.itemsContainer);
1519
+ EventHandler.remove(<HTMLElement & Window><unknown>window, 'resize', this.resizeHandler);
1520
+ }
1521
+
1522
+ /**
1523
+ * Method to transit from the current slide to the previous slide.
1524
+ *
1525
+ * @returns {void}
1526
+ */
1527
+ public prev(): void {
1528
+ if (!this.loop && this.selectedIndex === 0) {
1529
+ return;
1530
+ }
1531
+ const index: number = (this.selectedIndex === 0) ? this.slideItems.length - 1 : this.selectedIndex - 1;
1532
+ this.setActiveSlide(index, 'Previous');
1533
+ this.autoSlide();
1534
+ }
1535
+
1536
+ /**
1537
+ * Method to transit from the current slide to the next slide.
1538
+ *
1539
+ * @returns {void}
1540
+ */
1541
+ public next(): void {
1542
+ if (!this.loop && this.selectedIndex === this.slideItems.length - 1) {
1543
+ return;
1544
+ }
1545
+ const index: number = (this.selectedIndex === this.slideItems.length - 1) ? 0 : this.selectedIndex + 1;
1546
+ this.setActiveSlide(index, 'Next');
1547
+ this.autoSlide();
1548
+ }
1549
+
1550
+ /**
1551
+ * Method to play the slides programmatically.
1552
+ *
1553
+ * @returns {void}
1554
+ */
1555
+ public play(): void {
1556
+ const playIcon: Element = this.element.querySelector(`.${CLS_PLAY_ICON}`);
1557
+ if (this.showPlayButton && playIcon) {
1558
+ classList(playIcon, [CLS_PAUSE_ICON], [CLS_PLAY_ICON]);
1559
+ const playButton: HTMLElement = this.element.querySelector(`.${CLS_PLAY_BUTTON}`) as HTMLElement;
1560
+ playButton.setAttribute('aria-label', this.localeObj.getConstant('pauseSlideTransition'));
1561
+ }
1562
+ this.setProperties({ autoPlay: true }, true);
1563
+ this.itemsContainer.setAttribute('aria-live', 'off');
1564
+ this.applySlideInterval();
1565
+ }
1566
+
1567
+ /**
1568
+ * Method to pause the slides programmatically.
1569
+ *
1570
+ * @returns {void}
1571
+ */
1572
+ public pause(): void {
1573
+ const pauseIcon: Element = this.element.querySelector(`.${CLS_PAUSE_ICON}`);
1574
+ if (this.showPlayButton && pauseIcon) {
1575
+ const playButton: HTMLElement = this.element.querySelector(`.${CLS_PLAY_BUTTON}`) as HTMLElement;
1576
+ playButton.setAttribute('aria-label', this.localeObj.getConstant('playSlideTransition'));
1577
+ classList(pauseIcon, [CLS_PLAY_ICON], [CLS_PAUSE_ICON]);
1578
+ }
1579
+ this.setProperties({ autoPlay: false }, true);
1580
+ this.itemsContainer.setAttribute('aria-live', 'off');
1581
+ this.resetSlideInterval();
1582
+ }
1583
+
1584
+ /**
1585
+ * Method to render react and angular templates
1586
+ *
1587
+ * @returns {void}
1588
+ * @private
1589
+ */
1590
+ private renderTemplates(): void {
1591
+ if ((this as any).isAngular || (this as any).isReact) {
1592
+ this.renderReactTemplates();
1593
+ }
1594
+ }
1595
+
1596
+ /**
1597
+ * Method to reset react and angular templates
1598
+ *
1599
+ * @param {string[]} templates Accepts the template ID
1600
+ * @returns {void}
1601
+ * @private
1602
+ */
1603
+ private resetTemplates(templates?: string[]): void {
1604
+ if ((this as any).isAngular || (this as any).isReact) {
1605
+ this.clearTemplate(templates);
1606
+ }
1607
+ }
1608
+
1609
+ /**
1610
+ * Method for destroy the carousel component.
1611
+ *
1612
+ * @returns {void}
1613
+ */
1614
+ public destroy(): void {
1615
+ this.resetTemplates();
1616
+ if (this.touchModule) {
1617
+ this.touchModule.destroy();
1618
+ this.touchModule = null;
1619
+ }
1620
+ if (this.keyModule) {
1621
+ this.keyModule.destroy();
1622
+ this.keyModule = null;
1623
+ }
1624
+ this.resetSlideInterval();
1625
+ this.destroyButtons();
1626
+ this.unWireEvents();
1627
+ [].slice.call(this.element.children).forEach((ele: HTMLElement) => { this.element.removeChild(ele); });
1628
+ removeClass([this.element], [CLS_CAROUSEL, this.cssClass, CLS_RTL, CLS_SWIPE]);
1629
+ ['role', 'style'].forEach((attr: string): void => { this.element.removeAttribute(attr); });
1630
+ this.itemsContainer = null;
1631
+ super.destroy();
1632
+ }
1633
+ }