@kiva/kv-components 4.3.3 → 4.5.0

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,29 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [4.5.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@4.4.0...@kiva/kv-components@4.5.0) (2025-01-03)
7
+
8
+
9
+ ### Features
10
+
11
+ * add autoplay and fade plugin options for carousels ([b85b0c2](https://github.com/kiva/kv-ui-elements/commit/b85b0c20fbff52663c823a984db53fbb41baf0a4))
12
+
13
+
14
+
15
+
16
+
17
+ # [4.4.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@4.3.3...@kiva/kv-components@4.4.0) (2025-01-02)
18
+
19
+
20
+ ### Features
21
+
22
+ * add browser API mocks to test embla carousel ([e4e8bd1](https://github.com/kiva/kv-ui-elements/commit/e4e8bd11812faf75ed12d7258cdaf7f024f86c45))
23
+ * update embla to latest version ([6d37f77](https://github.com/kiva/kv-ui-elements/commit/6d37f7734072e595e6648592050b55027be537a5))
24
+
25
+
26
+
27
+
28
+
6
29
  ## [4.3.3](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@4.3.2...@kiva/kv-components@4.3.3) (2024-12-20)
7
30
 
8
31
 
@@ -180,6 +180,22 @@ export default {
180
180
  return {};
181
181
  },
182
182
  },
183
+ /**
184
+ * Options for the autoplay plugin - // https://www.embla-carousel.com/plugins/autoplay/#options
185
+ * */
186
+ autoplayOptions: {
187
+ type: Object,
188
+ default() {
189
+ return {};
190
+ },
191
+ },
192
+ /**
193
+ * Enable fade plugin - // https://www.embla-carousel.com/plugins/fade/
194
+ * */
195
+ fadeEnabled: {
196
+ type: Boolean,
197
+ default: false,
198
+ },
183
199
  /**
184
200
  * The type of logic to implement when deciding how many slides
185
201
  * to scroll when pressing the next/prev button
@@ -233,6 +249,7 @@ export default {
233
249
  goToSlide,
234
250
  handleUserInteraction,
235
251
  isAriaHidden,
252
+ isAutoplaying,
236
253
  nextIndex,
237
254
  onCarouselContainerClick,
238
255
  previousIndex,
@@ -242,6 +259,7 @@ export default {
242
259
  slideIndicatorCount,
243
260
  slideIndicatorListLength,
244
261
  slides,
262
+ toggleAutoPlay,
245
263
  } = carouselUtil(props, { emit, slots });
246
264
 
247
265
  return {
@@ -251,6 +269,7 @@ export default {
251
269
  goToSlide,
252
270
  handleUserInteraction,
253
271
  isAriaHidden,
272
+ isAutoplaying,
254
273
  mdiArrowLeft,
255
274
  mdiArrowRight,
256
275
  mdiChevronLeft,
@@ -264,6 +283,7 @@ export default {
264
283
  slideIndicatorCount,
265
284
  slideIndicatorListLength,
266
285
  slides,
286
+ toggleAutoPlay,
267
287
  };
268
288
  },
269
289
  };
@@ -107,6 +107,22 @@ export default {
107
107
  default: 'auto',
108
108
  validator: (value) => ['visible', 'auto'].indexOf(value) !== -1,
109
109
  },
110
+ /**
111
+ * Options for the autoplay plugin - // https://www.embla-carousel.com/plugins/autoplay/#options
112
+ * */
113
+ autoplayOptions: {
114
+ type: Object,
115
+ default() {
116
+ return {};
117
+ },
118
+ },
119
+ /**
120
+ * Enable fade plugin - // https://www.embla-carousel.com/plugins/fade/
121
+ * */
122
+ fadeEnabled: {
123
+ type: Boolean,
124
+ default: false,
125
+ },
110
126
  },
111
127
  emits: [
112
128
  'change',
@@ -120,6 +136,7 @@ export default {
120
136
  goToSlide,
121
137
  handleUserInteraction,
122
138
  isAriaHidden,
139
+ isAutoplaying,
123
140
  nextIndex,
124
141
  onCarouselContainerClick,
125
142
  previousIndex,
@@ -128,6 +145,7 @@ export default {
128
145
  rootEl,
129
146
  slideIndicatorCount,
130
147
  slides,
148
+ toggleAutoPlay,
131
149
  } = carouselUtil(props, { emit, slots }, { axis: 'y' });
132
150
 
133
151
  return {
@@ -137,6 +155,7 @@ export default {
137
155
  goToSlide,
138
156
  handleUserInteraction,
139
157
  isAriaHidden,
158
+ isAutoplaying,
140
159
  mdiChevronDown,
141
160
  mdiChevronUp,
142
161
  nextIndex,
@@ -147,6 +166,7 @@ export default {
147
166
  rootEl,
148
167
  slideIndicatorCount,
149
168
  slides,
169
+ toggleAutoPlay,
150
170
  };
151
171
  },
152
172
  };
@@ -424,3 +424,81 @@ export const ThreeDimensional = () => ({
424
424
  </kv-carousel>
425
425
  `,
426
426
  });
427
+
428
+ export const AutoPlayAutomatically = () => ({
429
+ components: {
430
+ KvCarousel,
431
+ },
432
+ template: `
433
+ <kv-carousel
434
+ style="max-width: 400px;"
435
+ :embla-options="{ loop: false }"
436
+ :autoplay-options="{ playOnInit: true, delay: 3000 }"
437
+ >
438
+ ${defaultCarouselSlides}
439
+ </kv-carousel>
440
+ `,
441
+ });
442
+
443
+ export const AutoPlayButton = () => ({
444
+ components: {
445
+ KvCarousel,
446
+ },
447
+ data() {
448
+ return {
449
+ isPlaying: false,
450
+ };
451
+ },
452
+ mounted() {
453
+ this.$nextTick(() => {
454
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
455
+ });
456
+ },
457
+ template: `
458
+ <div>
459
+ <kv-carousel
460
+ ref="sampleCarousel"
461
+ style="max-width: 400px;"
462
+ :embla-options="{ loop: false }"
463
+ :autoplay-options="{ playOnInit: true, delay: 3000 }"
464
+ @interact-carousel="carouselInteraction"
465
+ >
466
+ ${defaultCarouselSlides}
467
+ </kv-carousel>
468
+ <a href="#" @click.native.prevent="toggleAutoPlay()" role="toggleAutoPlayButton">Toggle AutoPlay</a>
469
+ <br/>
470
+ <p>AutoPlay is: {{ isPlaying ? 'ON' : 'OFF' }}</p>
471
+ </div>
472
+ `,
473
+ methods: {
474
+ toggleAutoPlay() {
475
+ this.$refs.sampleCarousel.toggleAutoPlay();
476
+ },
477
+ carouselInteraction() {
478
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
479
+ },
480
+ },
481
+ });
482
+
483
+ export const Fade = () => ({
484
+ components: {
485
+ KvCarousel,
486
+ },
487
+ mounted() {
488
+ this.$nextTick(() => {
489
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
490
+ });
491
+ },
492
+ template: `
493
+ <div>
494
+ <kv-carousel
495
+ ref="sampleCarousel"
496
+ style="max-width: 400px;"
497
+ :embla-options="{ loop: false }"
498
+ :fade-enabled="true"
499
+ >
500
+ ${defaultCarouselSlides}
501
+ </kv-carousel>
502
+ </div>
503
+ `,
504
+ });
@@ -166,3 +166,81 @@ export const CustomStartIndex = () => ({
166
166
  </kv-vertical-carousel>
167
167
  `,
168
168
  });
169
+
170
+ export const AutoPlayAutomatically = () => ({
171
+ components: {
172
+ KvVerticalCarousel,
173
+ },
174
+ template: `
175
+ <kv-vertical-carousel
176
+ style="max-width: 400px;"
177
+ :embla-options="{ loop: false }"
178
+ :autoplay-options="{ playOnInit: true, delay: 3000 }"
179
+ >
180
+ ${defaultCarouselSlides}
181
+ </kv-vertical-carousel>
182
+ `,
183
+ });
184
+
185
+ export const AutoPlayButton = () => ({
186
+ components: {
187
+ KvVerticalCarousel,
188
+ },
189
+ data() {
190
+ return {
191
+ isPlaying: false,
192
+ };
193
+ },
194
+ mounted() {
195
+ this.$nextTick(() => {
196
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
197
+ });
198
+ },
199
+ template: `
200
+ <div>
201
+ <kv-vertical-carousel
202
+ ref="sampleCarousel"
203
+ style="max-width: 400px;"
204
+ :embla-options="{ loop: false }"
205
+ :autoplay-options="{ playOnInit: true, delay: 3000 }"
206
+ @interact-carousel="carouselInteraction"
207
+ >
208
+ ${defaultCarouselSlides}
209
+ </kv-vertical-carousel>
210
+ <a href="#" @click.native.prevent="toggleAutoPlay()" role="toggleAutoPlayButton">Toggle AutoPlay</a>
211
+ <br/>
212
+ <p>AutoPlay is: {{ isPlaying ? 'ON' : 'OFF' }}</p>
213
+ </div>
214
+ `,
215
+ methods: {
216
+ toggleAutoPlay() {
217
+ this.$refs.sampleCarousel.toggleAutoPlay();
218
+ },
219
+ carouselInteraction() {
220
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
221
+ },
222
+ },
223
+ });
224
+
225
+ export const Fade = () => ({
226
+ components: {
227
+ KvVerticalCarousel,
228
+ },
229
+ mounted() {
230
+ this.$nextTick(() => {
231
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
232
+ });
233
+ },
234
+ template: `
235
+ <div>
236
+ <kv-vertical-carousel
237
+ ref="sampleCarousel"
238
+ style="max-width: 400px;"
239
+ :embla-options="{ loop: false }"
240
+ :fade-enabled="true"
241
+ >
242
+ ${defaultCarouselSlides}
243
+ </kv-vertical-carousel>
244
+ </div>
245
+ `,
246
+ });
@@ -34,6 +34,8 @@ __export(carousels_exports, {
34
34
  module.exports = __toCommonJS(carousels_exports);
35
35
  var import_vue_demi = require("vue-demi");
36
36
  var import_embla_carousel = __toESM(require("embla-carousel"), 1);
37
+ var import_embla_carousel_autoplay = __toESM(require("embla-carousel-autoplay"), 1);
38
+ var import_embla_carousel_fade = __toESM(require("embla-carousel-fade"), 1);
37
39
 
38
40
  // utils/throttle.js
39
41
  function throttle(func, timeFrame) {
@@ -51,6 +53,8 @@ function throttle(func, timeFrame) {
51
53
  function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
52
54
  var _a;
53
55
  const {
56
+ autoplayOptions,
57
+ fadeEnabled,
54
58
  emblaOptions,
55
59
  slidesToScroll
56
60
  } = (0, import_vue_demi.toRefs)(props);
@@ -88,6 +92,22 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
88
92
  }
89
93
  emit("interact-carousel", interactionType);
90
94
  };
95
+ const toggleAutoPlay = () => {
96
+ var _a2, _b;
97
+ const autoplay = (_b = (_a2 = embla.value) == null ? void 0 : _a2.plugins()) == null ? void 0 : _b.autoplay;
98
+ if (!autoplay)
99
+ return;
100
+ const playOrStop = autoplay.isPlaying() ? autoplay.stop : autoplay.play;
101
+ playOrStop();
102
+ handleUserInteraction(null, "autoplay");
103
+ };
104
+ const isAutoplaying = () => {
105
+ var _a2, _b;
106
+ const autoplay = (_b = (_a2 = embla.value) == null ? void 0 : _a2.plugins()) == null ? void 0 : _b.autoplay;
107
+ if (!autoplay)
108
+ return false;
109
+ return autoplay.isPlaying();
110
+ };
91
111
  const slideIndicatorListLength = () => {
92
112
  const indicator = embla.value ? embla.value.scrollSnapList().length : 0;
93
113
  slideIndicatorCount.value = indicator;
@@ -124,6 +144,13 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
124
144
  };
125
145
  (0, import_vue_demi.onMounted)(async () => {
126
146
  var _a2;
147
+ const combinedPluginOptions = [];
148
+ if (Object.keys(autoplayOptions.value).length !== 0) {
149
+ combinedPluginOptions.push((0, import_embla_carousel_autoplay.default)(autoplayOptions.value));
150
+ }
151
+ if (fadeEnabled.value) {
152
+ combinedPluginOptions.push((0, import_embla_carousel_fade.default)());
153
+ }
127
154
  embla.value = (0, import_embla_carousel.default)(rootEl.value, {
128
155
  loop: true,
129
156
  containScroll: "trimSnaps",
@@ -131,7 +158,7 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
131
158
  align: "start",
132
159
  ...extraEmblaOptions,
133
160
  ...emblaOptions.value
134
- });
161
+ }, combinedPluginOptions);
135
162
  if (slidesToScroll.value === "visible") {
136
163
  reInitVisible();
137
164
  embla.value.on(
@@ -175,7 +202,9 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
175
202
  reInitVisible,
176
203
  onCarouselContainerClick,
177
204
  isAriaHidden,
178
- slideIndicatorListLength
205
+ isAutoplaying,
206
+ slideIndicatorListLength,
207
+ toggleAutoPlay
179
208
  };
180
209
  }
181
210
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  carouselUtil
3
- } from "./chunk-AZPWOFD5.js";
3
+ } from "./chunk-P6FXCNYP.js";
4
4
  import "./chunk-HV3AUBFT.js";
5
5
  import "./chunk-YCNMJ4YV.js";
6
6
  export {
@@ -12,9 +12,13 @@ import {
12
12
  nextTick
13
13
  } from "vue-demi";
14
14
  import EmblaCarousel from "embla-carousel";
15
+ import Autoplay from "embla-carousel-autoplay";
16
+ import Fade from "embla-carousel-fade";
15
17
  function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
16
18
  var _a;
17
19
  const {
20
+ autoplayOptions,
21
+ fadeEnabled,
18
22
  emblaOptions,
19
23
  slidesToScroll
20
24
  } = toRefs(props);
@@ -52,6 +56,22 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
52
56
  }
53
57
  emit("interact-carousel", interactionType);
54
58
  };
59
+ const toggleAutoPlay = () => {
60
+ var _a2, _b;
61
+ const autoplay = (_b = (_a2 = embla.value) == null ? void 0 : _a2.plugins()) == null ? void 0 : _b.autoplay;
62
+ if (!autoplay)
63
+ return;
64
+ const playOrStop = autoplay.isPlaying() ? autoplay.stop : autoplay.play;
65
+ playOrStop();
66
+ handleUserInteraction(null, "autoplay");
67
+ };
68
+ const isAutoplaying = () => {
69
+ var _a2, _b;
70
+ const autoplay = (_b = (_a2 = embla.value) == null ? void 0 : _a2.plugins()) == null ? void 0 : _b.autoplay;
71
+ if (!autoplay)
72
+ return false;
73
+ return autoplay.isPlaying();
74
+ };
55
75
  const slideIndicatorListLength = () => {
56
76
  const indicator = embla.value ? embla.value.scrollSnapList().length : 0;
57
77
  slideIndicatorCount.value = indicator;
@@ -88,6 +108,13 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
88
108
  };
89
109
  onMounted(async () => {
90
110
  var _a2;
111
+ const combinedPluginOptions = [];
112
+ if (Object.keys(autoplayOptions.value).length !== 0) {
113
+ combinedPluginOptions.push(Autoplay(autoplayOptions.value));
114
+ }
115
+ if (fadeEnabled.value) {
116
+ combinedPluginOptions.push(Fade());
117
+ }
91
118
  embla.value = EmblaCarousel(rootEl.value, {
92
119
  loop: true,
93
120
  containScroll: "trimSnaps",
@@ -95,7 +122,7 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
95
122
  align: "start",
96
123
  ...extraEmblaOptions,
97
124
  ...emblaOptions.value
98
- });
125
+ }, combinedPluginOptions);
99
126
  if (slidesToScroll.value === "visible") {
100
127
  reInitVisible();
101
128
  embla.value.on(
@@ -139,7 +166,9 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
139
166
  reInitVisible,
140
167
  onCarouselContainerClick,
141
168
  isAriaHidden,
142
- slideIndicatorListLength
169
+ isAutoplaying,
170
+ slideIndicatorListLength,
171
+ toggleAutoPlay
143
172
  };
144
173
  }
145
174
 
@@ -142,6 +142,8 @@ function useAttrs({ attrs, listeners }, ownEvents = []) {
142
142
  // utils/carousels.js
143
143
  var import_vue_demi = require("vue-demi");
144
144
  var import_embla_carousel = __toESM(require("embla-carousel"), 1);
145
+ var import_embla_carousel_autoplay = __toESM(require("embla-carousel-autoplay"), 1);
146
+ var import_embla_carousel_fade = __toESM(require("embla-carousel-fade"), 1);
145
147
 
146
148
  // utils/throttle.js
147
149
  function throttle(func, timeFrame) {
@@ -159,6 +161,8 @@ function throttle(func, timeFrame) {
159
161
  function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
160
162
  var _a;
161
163
  const {
164
+ autoplayOptions,
165
+ fadeEnabled,
162
166
  emblaOptions,
163
167
  slidesToScroll
164
168
  } = (0, import_vue_demi.toRefs)(props);
@@ -196,6 +200,22 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
196
200
  }
197
201
  emit("interact-carousel", interactionType);
198
202
  };
203
+ const toggleAutoPlay = () => {
204
+ var _a2, _b;
205
+ const autoplay = (_b = (_a2 = embla.value) == null ? void 0 : _a2.plugins()) == null ? void 0 : _b.autoplay;
206
+ if (!autoplay)
207
+ return;
208
+ const playOrStop = autoplay.isPlaying() ? autoplay.stop : autoplay.play;
209
+ playOrStop();
210
+ handleUserInteraction(null, "autoplay");
211
+ };
212
+ const isAutoplaying = () => {
213
+ var _a2, _b;
214
+ const autoplay = (_b = (_a2 = embla.value) == null ? void 0 : _a2.plugins()) == null ? void 0 : _b.autoplay;
215
+ if (!autoplay)
216
+ return false;
217
+ return autoplay.isPlaying();
218
+ };
199
219
  const slideIndicatorListLength = () => {
200
220
  const indicator = embla.value ? embla.value.scrollSnapList().length : 0;
201
221
  slideIndicatorCount.value = indicator;
@@ -232,6 +252,13 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
232
252
  };
233
253
  (0, import_vue_demi.onMounted)(async () => {
234
254
  var _a2;
255
+ const combinedPluginOptions = [];
256
+ if (Object.keys(autoplayOptions.value).length !== 0) {
257
+ combinedPluginOptions.push((0, import_embla_carousel_autoplay.default)(autoplayOptions.value));
258
+ }
259
+ if (fadeEnabled.value) {
260
+ combinedPluginOptions.push((0, import_embla_carousel_fade.default)());
261
+ }
235
262
  embla.value = (0, import_embla_carousel.default)(rootEl.value, {
236
263
  loop: true,
237
264
  containScroll: "trimSnaps",
@@ -239,7 +266,7 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
239
266
  align: "start",
240
267
  ...extraEmblaOptions,
241
268
  ...emblaOptions.value
242
- });
269
+ }, combinedPluginOptions);
243
270
  if (slidesToScroll.value === "visible") {
244
271
  reInitVisible();
245
272
  embla.value.on(
@@ -283,7 +310,9 @@ function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
283
310
  reInitVisible,
284
311
  onCarouselContainerClick,
285
312
  isAriaHidden,
286
- slideIndicatorListLength
313
+ isAutoplaying,
314
+ slideIndicatorListLength,
315
+ toggleAutoPlay
287
316
  };
288
317
  }
289
318
 
@@ -31,7 +31,7 @@ import {
31
31
  } from "./chunk-3HK4G4NT.js";
32
32
  import {
33
33
  carouselUtil
34
- } from "./chunk-AZPWOFD5.js";
34
+ } from "./chunk-P6FXCNYP.js";
35
35
  import {
36
36
  throttle
37
37
  } from "./chunk-HV3AUBFT.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kiva/kv-components",
3
- "version": "4.3.3",
3
+ "version": "4.5.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -70,7 +70,9 @@
70
70
  "aria-hidden": "^1.1.3",
71
71
  "change-case": "^4.1.2",
72
72
  "date-fns": "^2.30.0",
73
- "embla-carousel": "^4.5.3",
73
+ "embla-carousel": "^8.5.1",
74
+ "embla-carousel-autoplay": "^8.5.1",
75
+ "embla-carousel-fade": "^8.5.1",
74
76
  "flag-icons": "^7.2.3",
75
77
  "focus-trap": "^6.7.2",
76
78
  "moment": "^2.29.4",
@@ -96,5 +98,5 @@
96
98
  "peerDependencies": {
97
99
  "vue": ">=3.0.0"
98
100
  },
99
- "gitHead": "9947b8a5d8bcfe549c154a92448e12f7ce5f3dc5"
101
+ "gitHead": "0facec90197844ded4496934e85b9014d4949237"
100
102
  }
@@ -33,6 +33,35 @@ const defaultCarouselSlides = `
33
33
  </template>
34
34
  `;
35
35
 
36
+ // Mocking IntersectionObserver and ResizeObserver and matchMedia
37
+ // required for embla carousel
38
+ Object.defineProperty(window, 'IntersectionObserver', {
39
+ writable: true,
40
+ value: jest.fn().mockImplementation(() => ({
41
+ observe: jest.fn(),
42
+ unobserve: jest.fn(),
43
+ disconnect: jest.fn(),
44
+ })),
45
+ });
46
+
47
+ Object.defineProperty(window, 'matchMedia', {
48
+ writable: true,
49
+ value: jest.fn().mockImplementation((query) => ({
50
+ matches: [].includes(query),
51
+ addEventListener: jest.fn(),
52
+ removeEventListener: jest.fn(),
53
+ })),
54
+ });
55
+
56
+ Object.defineProperty(window, 'ResizeObserver', {
57
+ writable: true,
58
+ value: jest.fn().mockImplementation(() => ({
59
+ observe: jest.fn(),
60
+ unobserve: jest.fn(),
61
+ disconnect: jest.fn(),
62
+ })),
63
+ });
64
+
36
65
  describe('KvCarousel', () => {
37
66
  it('has no automated accessibility violations', async () => {
38
67
  const TestComponent = {
@@ -8,10 +8,14 @@ import {
8
8
  nextTick,
9
9
  } from 'vue-demi';
10
10
  import EmblaCarousel from 'embla-carousel';
11
+ import Autoplay from 'embla-carousel-autoplay';
12
+ import Fade from 'embla-carousel-fade';
11
13
  import { throttle } from './throttle';
12
14
 
13
15
  export function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
14
16
  const {
17
+ autoplayOptions,
18
+ fadeEnabled,
15
19
  emblaOptions,
16
20
  slidesToScroll,
17
21
  } = toRefs(props);
@@ -53,6 +57,7 @@ export function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
53
57
  const goToSlide = (index) => {
54
58
  embla.value.scrollTo(index);
55
59
  };
60
+
56
61
  const handleUserInteraction = async (index, interactionType) => {
57
62
  if (index !== null && typeof index !== 'undefined') {
58
63
  await nextTick(); // wait for embla.
@@ -66,7 +71,29 @@ export function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
66
71
  */
67
72
  emit('interact-carousel', interactionType);
68
73
  };
74
+ /**
75
+ * Start/Stop autoplay
76
+ *
77
+ * @public This is a public method
78
+ */
79
+ const toggleAutoPlay = () => {
80
+ const autoplay = embla.value?.plugins()?.autoplay;
81
+ if (!autoplay) return;
82
+ const playOrStop = autoplay.isPlaying() ? autoplay.stop : autoplay.play;
83
+ playOrStop();
84
+ handleUserInteraction(null, 'autoplay');
85
+ };
69
86
 
87
+ /**
88
+ * Returns true if the carousel is autoplaying
89
+ *
90
+ * @public This is a public method
91
+ */
92
+ const isAutoplaying = () => {
93
+ const autoplay = embla.value?.plugins()?.autoplay;
94
+ if (!autoplay) return false;
95
+ return autoplay.isPlaying();
96
+ };
70
97
  /**
71
98
  * Returns number of slides in the carousel
72
99
  *
@@ -93,6 +120,7 @@ export function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
93
120
  });
94
121
  }
95
122
  };
123
+
96
124
  const reInit = () => {
97
125
  embla.value.reInit();
98
126
  if (slidesToScroll.value === 'visible') {
@@ -123,6 +151,13 @@ export function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
123
151
  };
124
152
 
125
153
  onMounted(async () => {
154
+ const combinedPluginOptions = [];
155
+ if (Object.keys(autoplayOptions.value).length !== 0) {
156
+ combinedPluginOptions.push(Autoplay(autoplayOptions.value));
157
+ }
158
+ if (fadeEnabled.value) {
159
+ combinedPluginOptions.push(Fade());
160
+ }
126
161
  embla.value = EmblaCarousel(rootEl.value, {
127
162
  loop: true,
128
163
  containScroll: 'trimSnaps',
@@ -130,7 +165,7 @@ export function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
130
165
  align: 'start',
131
166
  ...extraEmblaOptions,
132
167
  ...emblaOptions.value,
133
- });
168
+ }, combinedPluginOptions);
134
169
 
135
170
  if (slidesToScroll.value === 'visible') {
136
171
  reInitVisible();
@@ -185,6 +220,8 @@ export function carouselUtil(props, { emit, slots }, extraEmblaOptions) {
185
220
  reInitVisible,
186
221
  onCarouselContainerClick,
187
222
  isAriaHidden,
223
+ isAutoplaying,
188
224
  slideIndicatorListLength,
225
+ toggleAutoPlay,
189
226
  };
190
227
  }
@@ -180,6 +180,22 @@ export default {
180
180
  return {};
181
181
  },
182
182
  },
183
+ /**
184
+ * Options for the autoplay plugin - // https://www.embla-carousel.com/plugins/autoplay/#options
185
+ * */
186
+ autoplayOptions: {
187
+ type: Object,
188
+ default() {
189
+ return {};
190
+ },
191
+ },
192
+ /**
193
+ * Enable fade plugin - // https://www.embla-carousel.com/plugins/fade/
194
+ * */
195
+ fadeEnabled: {
196
+ type: Boolean,
197
+ default: false,
198
+ },
183
199
  /**
184
200
  * The type of logic to implement when deciding how many slides
185
201
  * to scroll when pressing the next/prev button
@@ -233,6 +249,7 @@ export default {
233
249
  goToSlide,
234
250
  handleUserInteraction,
235
251
  isAriaHidden,
252
+ isAutoplaying,
236
253
  nextIndex,
237
254
  onCarouselContainerClick,
238
255
  previousIndex,
@@ -242,6 +259,7 @@ export default {
242
259
  slideIndicatorCount,
243
260
  slideIndicatorListLength,
244
261
  slides,
262
+ toggleAutoPlay,
245
263
  } = carouselUtil(props, { emit, slots });
246
264
 
247
265
  return {
@@ -251,6 +269,7 @@ export default {
251
269
  goToSlide,
252
270
  handleUserInteraction,
253
271
  isAriaHidden,
272
+ isAutoplaying,
254
273
  mdiArrowLeft,
255
274
  mdiArrowRight,
256
275
  mdiChevronLeft,
@@ -264,6 +283,7 @@ export default {
264
283
  slideIndicatorCount,
265
284
  slideIndicatorListLength,
266
285
  slides,
286
+ toggleAutoPlay,
267
287
  };
268
288
  },
269
289
  };
@@ -107,6 +107,22 @@ export default {
107
107
  default: 'auto',
108
108
  validator: (value) => ['visible', 'auto'].indexOf(value) !== -1,
109
109
  },
110
+ /**
111
+ * Options for the autoplay plugin - // https://www.embla-carousel.com/plugins/autoplay/#options
112
+ * */
113
+ autoplayOptions: {
114
+ type: Object,
115
+ default() {
116
+ return {};
117
+ },
118
+ },
119
+ /**
120
+ * Enable fade plugin - // https://www.embla-carousel.com/plugins/fade/
121
+ * */
122
+ fadeEnabled: {
123
+ type: Boolean,
124
+ default: false,
125
+ },
110
126
  },
111
127
  emits: [
112
128
  'change',
@@ -120,6 +136,7 @@ export default {
120
136
  goToSlide,
121
137
  handleUserInteraction,
122
138
  isAriaHidden,
139
+ isAutoplaying,
123
140
  nextIndex,
124
141
  onCarouselContainerClick,
125
142
  previousIndex,
@@ -128,6 +145,7 @@ export default {
128
145
  rootEl,
129
146
  slideIndicatorCount,
130
147
  slides,
148
+ toggleAutoPlay,
131
149
  } = carouselUtil(props, { emit, slots }, { axis: 'y' });
132
150
 
133
151
  return {
@@ -137,6 +155,7 @@ export default {
137
155
  goToSlide,
138
156
  handleUserInteraction,
139
157
  isAriaHidden,
158
+ isAutoplaying,
140
159
  mdiChevronDown,
141
160
  mdiChevronUp,
142
161
  nextIndex,
@@ -147,6 +166,7 @@ export default {
147
166
  rootEl,
148
167
  slideIndicatorCount,
149
168
  slides,
169
+ toggleAutoPlay,
150
170
  };
151
171
  },
152
172
  };
@@ -424,3 +424,81 @@ export const ThreeDimensional = () => ({
424
424
  </kv-carousel>
425
425
  `,
426
426
  });
427
+
428
+ export const AutoPlayAutomatically = () => ({
429
+ components: {
430
+ KvCarousel,
431
+ },
432
+ template: `
433
+ <kv-carousel
434
+ style="max-width: 400px;"
435
+ :embla-options="{ loop: false }"
436
+ :autoplay-options="{ playOnInit: true, delay: 3000 }"
437
+ >
438
+ ${defaultCarouselSlides}
439
+ </kv-carousel>
440
+ `,
441
+ });
442
+
443
+ export const AutoPlayButton = () => ({
444
+ components: {
445
+ KvCarousel,
446
+ },
447
+ data() {
448
+ return {
449
+ isPlaying: false,
450
+ };
451
+ },
452
+ mounted() {
453
+ this.$nextTick(() => {
454
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
455
+ });
456
+ },
457
+ template: `
458
+ <div>
459
+ <kv-carousel
460
+ ref="sampleCarousel"
461
+ style="max-width: 400px;"
462
+ :embla-options="{ loop: false }"
463
+ :autoplay-options="{ playOnInit: true, delay: 3000 }"
464
+ @interact-carousel="carouselInteraction"
465
+ >
466
+ ${defaultCarouselSlides}
467
+ </kv-carousel>
468
+ <a href="#" @click.native.prevent="toggleAutoPlay()" role="toggleAutoPlayButton">Toggle AutoPlay</a>
469
+ <br/>
470
+ <p>AutoPlay is: {{ isPlaying ? 'ON' : 'OFF' }}</p>
471
+ </div>
472
+ `,
473
+ methods: {
474
+ toggleAutoPlay() {
475
+ this.$refs.sampleCarousel.toggleAutoPlay();
476
+ },
477
+ carouselInteraction() {
478
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
479
+ },
480
+ },
481
+ });
482
+
483
+ export const Fade = () => ({
484
+ components: {
485
+ KvCarousel,
486
+ },
487
+ mounted() {
488
+ this.$nextTick(() => {
489
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
490
+ });
491
+ },
492
+ template: `
493
+ <div>
494
+ <kv-carousel
495
+ ref="sampleCarousel"
496
+ style="max-width: 400px;"
497
+ :embla-options="{ loop: false }"
498
+ :fade-enabled="true"
499
+ >
500
+ ${defaultCarouselSlides}
501
+ </kv-carousel>
502
+ </div>
503
+ `,
504
+ });
@@ -166,3 +166,81 @@ export const CustomStartIndex = () => ({
166
166
  </kv-vertical-carousel>
167
167
  `,
168
168
  });
169
+
170
+ export const AutoPlayAutomatically = () => ({
171
+ components: {
172
+ KvVerticalCarousel,
173
+ },
174
+ template: `
175
+ <kv-vertical-carousel
176
+ style="max-width: 400px;"
177
+ :embla-options="{ loop: false }"
178
+ :autoplay-options="{ playOnInit: true, delay: 3000 }"
179
+ >
180
+ ${defaultCarouselSlides}
181
+ </kv-vertical-carousel>
182
+ `,
183
+ });
184
+
185
+ export const AutoPlayButton = () => ({
186
+ components: {
187
+ KvVerticalCarousel,
188
+ },
189
+ data() {
190
+ return {
191
+ isPlaying: false,
192
+ };
193
+ },
194
+ mounted() {
195
+ this.$nextTick(() => {
196
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
197
+ });
198
+ },
199
+ template: `
200
+ <div>
201
+ <kv-vertical-carousel
202
+ ref="sampleCarousel"
203
+ style="max-width: 400px;"
204
+ :embla-options="{ loop: false }"
205
+ :autoplay-options="{ playOnInit: true, delay: 3000 }"
206
+ @interact-carousel="carouselInteraction"
207
+ >
208
+ ${defaultCarouselSlides}
209
+ </kv-vertical-carousel>
210
+ <a href="#" @click.native.prevent="toggleAutoPlay()" role="toggleAutoPlayButton">Toggle AutoPlay</a>
211
+ <br/>
212
+ <p>AutoPlay is: {{ isPlaying ? 'ON' : 'OFF' }}</p>
213
+ </div>
214
+ `,
215
+ methods: {
216
+ toggleAutoPlay() {
217
+ this.$refs.sampleCarousel.toggleAutoPlay();
218
+ },
219
+ carouselInteraction() {
220
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
221
+ },
222
+ },
223
+ });
224
+
225
+ export const Fade = () => ({
226
+ components: {
227
+ KvVerticalCarousel,
228
+ },
229
+ mounted() {
230
+ this.$nextTick(() => {
231
+ this.isPlaying = this.$refs.sampleCarousel.isAutoplaying();
232
+ });
233
+ },
234
+ template: `
235
+ <div>
236
+ <kv-vertical-carousel
237
+ ref="sampleCarousel"
238
+ style="max-width: 400px;"
239
+ :embla-options="{ loop: false }"
240
+ :fade-enabled="true"
241
+ >
242
+ ${defaultCarouselSlides}
243
+ </kv-vertical-carousel>
244
+ </div>
245
+ `,
246
+ });