@aurodesignsystem-dev/auro-slideshow 0.0.0-pr21.0 → 0.0.0-pr22.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.
@@ -2724,7 +2724,6 @@ let AuroElement$1 = class AuroElement extends i$2 {
2724
2724
  * @private
2725
2725
  */
2726
2726
  wrapper: {
2727
- type: HTMLElement,
2728
2727
  attribute: false,
2729
2728
  reflect: false
2730
2729
  }
@@ -3200,13 +3199,23 @@ class AuroButton extends AuroElement$1 {
3200
3199
  },
3201
3200
 
3202
3201
  /**
3203
- * Populates `tabIndex` to define the focusable sequence in keyboard navigation.
3202
+ * Populates `tabindex` to define the focusable sequence in keyboard navigation.
3204
3203
  */
3205
3204
  tIndex: {
3206
3205
  type: String,
3207
3206
  reflect: true
3208
3207
  },
3209
3208
 
3209
+ /**
3210
+ * Populates `tabindex` to define the focusable sequence in keyboard navigation.
3211
+ * Must be used with "." to ensure the host element does not retain a reference to the `tabindex` attribute.
3212
+ * Example: `<auro-button .tabindex="${this.disabled ? '-1' : '0'}"></auro-button>`
3213
+ */
3214
+ tabindex: {
3215
+ type: String,
3216
+ reflect: false
3217
+ },
3218
+
3210
3219
  /**
3211
3220
  * Sets title attribute. The information is most often shown as a tooltip text when the mouse moves over the element.
3212
3221
  */
@@ -3358,7 +3367,7 @@ class AuroButton extends AuroElement$1 {
3358
3367
  part="${part}"
3359
3368
  aria-label="${o(this.loading ? this.loadingText : this.currentAriaLabel || undefined)}"
3360
3369
  aria-labelledby="${o(this.loading ? undefined : this.currentAriaLabelledBy || undefined)}"
3361
- tabIndex="${o(this.tIndex)}"
3370
+ tabindex="${o(this.tIndex || this.tabindex)}"
3362
3371
  ?autofocus="${this.autofocus}"
3363
3372
  class=${e(classes)}
3364
3373
  ?disabled="${this.disabled || this.loading}"
@@ -3394,7 +3403,7 @@ class AuroButton extends AuroElement$1 {
3394
3403
  }
3395
3404
  }
3396
3405
 
3397
- var buttonVersion = "9.3.3";
3406
+ var buttonVersion = "11.2.1";
3398
3407
 
3399
3408
  var chevronLeft = {"svg":"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" aria-labelledby=\"chevron-left__desc\" class=\"ico_squareLarge\" role=\"img\" style=\"min-width:var(--auro-size-lg, var(--ds-size-300, 1.5rem));height:var(--auro-size-lg, var(--ds-size-300, 1.5rem));fill:currentColor\" viewBox=\"0 0 24 24\" part=\"svg\"><title/><desc id=\"chevron-left__desc\">Directional indicator; left.</desc><path d=\"m14.395 6.345.084.073a.75.75 0 0 1 .072.977l-.072.084-4.47 4.47 4.47 4.47a.75.75 0 0 1 .072.976l-.072.084a.75.75 0 0 1-.977.072l-.084-.072-4.823-4.823a1 1 0 0 1 0-1.415l4.823-4.823a.75.75 0 0 1 .977-.073\"/></svg>"};
3400
3409
 
@@ -3799,15 +3808,24 @@ class AuroIcon extends BaseIcon {
3799
3808
  }
3800
3809
  }
3801
3810
 
3802
- var iconVersion = "8.0.2";
3811
+ var iconVersion = "8.0.4";
3803
3812
 
3804
- var styleCss = i$5`:host{--border-size: 6px;--border-radius: 24px}.container{display:flex;width:100%;flex-direction:column;align-items:flex-start}.slideshow-wrapper{position:relative;width:100%;display:flex;justify-content:start;align-items:center;overflow:hidden;padding:var(--border-size)}.embla{max-width:100%;margin:0;--slide-size: 100%}.embla__container{display:flex}.embla__slide{transform:translateZ(0);flex:0 0 var(--slide-size);min-width:0;border-radius:var(--border-radius);margin-right:1rem;box-sizing:border-box;overflow:hidden}.embla__slide:focus-visible{outline:unset;box-shadow:0 0 0 2px var(--ds-basic-color-border-inverse, #ffffff),0 0 0 var(--border-size) var(--ds-advanced-color-state-focused, #01426a)}.embla__slide:not(.active):not(.in-view){filter:brightness(30%)}.scroll-prev,.scroll-next{position:absolute;display:none;top:50%;transform:translateY(-50%);z-index:10;--ds-auro-button-container-color: var(--ds-advanced-color-state-focused, #01426a);--ds-auro-button-container-image: var(--ds-advanced-color-state-focused, #01426a);color:var(--ds-advanced-color-shared-background, #ffffff)}.scroll-prev:hover,.scroll-next:hover{--ds-auro-button-container-color: var(--ds-advanced-color-button-primary-background-inverse-hover, #ebf3f9)}.scroll-prev{left:8px}.scroll-next{right:8px}.slideshow-wrapper:hover .scroll-prev,.slideshow-wrapper:hover .scroll-next{display:block}.pagination-container{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;width:100%;margin-top:40px;gap:14px}.embla__dots{display:flex;gap:16px}.embla__dot{width:12px;height:12px;border-radius:50%;border:none;background-color:var(--ds-advanced-color-button-primary-background-inactive, #cfe0ef);position:relative;cursor:pointer;transition:all .3s ease-in-out}.embla__dot:hover:not(.embla__dot--selected){background-color:var(--ds-advanced-color-button-primary-background-inactive-hover, #89b2d4)}.embla__dot:before{content:"";position:absolute;width:24px;height:24px;cursor:pointer;top:50%;left:50%;transform:translate(-50%,-50%)}.embla__dot--selected{width:52px;height:12px;border-radius:1.8rem;background-color:var(--ds-advanced-color-button-primary-background, #01426a)}.embla__dot--selected:before{width:64px}.embla__progress{border-radius:1.8rem;border:none;background-color:var(--ds-advanced-color-button-primary-background-inactive, #cfe0ef);position:relative;align-self:center;height:12px;width:56px;max-width:90%;overflow:hidden;cursor:pointer;justify-self:center;transition:all .3s ease-in-out}.embla__progress:hover{background-color:var(--ds-advanced-color-button-primary-background-inactive-hover, #89b2d4)}.embla__progress__bar{border-radius:inherit;background-color:var(--ds-advanced-color-button-primary-background, #01426a);position:absolute;width:100%;top:0;bottom:0;left:-100%;animation-name:autoplay-progress;animation-timing-function:linear;animation-iteration-count:1;animation-play-state:running}.embla__progress--paused .embla__progress__bar{animation-play-state:paused}@keyframes autoplay-progress{0%{transform:translateZ(0)}to{transform:translate3d(100%,0,0)}}
3813
+ var styleCss = i$5`:host{--border-size: 6px;--border-radius: 24px}.container{display:flex;width:100%;flex-direction:column;align-items:flex-start}.slideshow-wrapper{position:relative;display:flex;width:100%;overflow:hidden;align-items:center;justify-content:start;padding:var(--border-size)}.embla{max-width:100%;margin:0;--slide-size: 100%}.embla__container{display:flex}.embla__slide{transform:translateZ(0);min-width:0;flex:0 0 var(--slide-size);border-radius:var(--border-radius);box-sizing:border-box;margin-right:1rem;overflow:hidden}.embla__slide:focus-visible{outline:unset;box-shadow:0 0 0 2px var(--ds-basic-color-border-inverse, #ffffff),0 0 0 var(--border-size) var(--ds-advanced-color-state-focused, #01426a)}.embla__slide:not(.active):not(.in-view){filter:brightness(30%)}.scroll-prev,.scroll-next{position:absolute;display:none;top:50%;transform:translateY(-50%);z-index:10;--ds-auro-button-container-color: var(--ds-advanced-color-state-focused, #01426a);--ds-auro-button-container-image: var(--ds-advanced-color-state-focused, #01426a);color:var(--ds-advanced-color-shared-background, #ffffff)}.scroll-prev:hover,.scroll-next:hover{--ds-auro-button-container-color: var(--ds-advanced-color-button-primary-background-inverse-hover, #ebf3f9)}.scroll-prev{left:8px}.scroll-next{right:8px}.slideshow-wrapper:hover .scroll-prev,.slideshow-wrapper:hover .scroll-next{display:block}.pagination-container{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;width:100%;margin-top:40px;gap:14px}.embla__dots{display:flex;gap:16px}.embla__dot{width:12px;height:12px;border-radius:50%;border:none;background-color:var(--ds-advanced-color-button-primary-background-inactive, #cfe0ef);position:relative;cursor:pointer;transition:all .3s ease-in-out}.embla__dot:hover:not(.embla__dot--selected){background-color:var(--ds-advanced-color-button-primary-background-inactive-hover, #89b2d4)}.embla__dot:before{content:"";width:24px;height:24px;position:absolute;cursor:pointer;top:50%;left:50%;transform:translate(-50%,-50%)}.embla__dot--selected{width:52px;height:12px;border-radius:1.8rem;background-color:var(--ds-advanced-color-button-primary-background, #01426a)}.embla__dot--selected:before{width:64px}.embla__progress{width:52px;height:12px;border-radius:1.8rem;border:none;background-color:var(--ds-advanced-color-button-primary-background-inactive, #cfe0ef);position:relative;overflow:hidden;cursor:pointer;align-self:center;justify-self:center;transition:all .3s ease-in-out}.embla__progress:hover:not(.stopped){background-color:var(--ds-advanced-color-button-primary-background-inactive-hover, #89b2d4)}.embla__progress__bar{border-radius:inherit;background-color:var(--ds-advanced-color-button-primary-background, #01426a);position:absolute;width:100%;top:0;bottom:0;left:-100%;animation-name:autoplay-progress;animation-timing-function:linear;animation-iteration-count:1;animation-play-state:running}.embla__progress--paused .embla__progress__bar{animation-play-state:paused}.stopped{background-color:var(--ds-advanced-color-button-primary-background, #01426a)}@keyframes autoplay-progress{0%{transform:translateZ(0)}to{transform:translate3d(100%,0,0)}}
3805
3814
  `;
3806
3815
 
3807
3816
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
3808
3817
  // See LICENSE in the project root for license information.
3809
3818
 
3810
3819
 
3820
+ /**
3821
+ * The auro-slideshow component is a customizable slideshow that displays a series of slides
3822
+ * with several options such as autoplay, navigation controls, and pagination dots.
3823
+ *
3824
+ * @slot - Default slot for the slides. Each child element will be treated as a slide.
3825
+ * @csspart prev-button - Use to style the previous button control.
3826
+ * @csspart next-button - Use to style the next button control.
3827
+ * @csspart play-pause-button - Use to style the play/pause button control.
3828
+ */
3811
3829
  class AuroSlideshow extends i$2 {
3812
3830
  constructor() {
3813
3831
  super();
@@ -3964,6 +3982,14 @@ class AuroSlideshow extends i$2 {
3964
3982
  return this.shadowRoot.querySelector(".play-pause");
3965
3983
  }
3966
3984
 
3985
+ get _prevBtn() {
3986
+ return this.shadowRoot.querySelector(".scroll-prev");
3987
+ }
3988
+
3989
+ get _nextBtn() {
3990
+ return this.shadowRoot.querySelector(".scroll-next");
3991
+ }
3992
+
3967
3993
  get _dotsNode() {
3968
3994
  return this.shadowRoot.querySelector(".embla__dots");
3969
3995
  }
@@ -3972,7 +3998,54 @@ class AuroSlideshow extends i$2 {
3972
3998
  return this.shadowRoot.querySelector(".embla__progress");
3973
3999
  }
3974
4000
 
3975
- /** @private */
4001
+ // ========== PUBLIC METHODS =================
4002
+
4003
+ /**
4004
+ * Starts the slideshow playback.
4005
+ * @returns {void}
4006
+ */
4007
+ play() {
4008
+ if (this.autoplay) {
4009
+ this.embla?.plugins()?.autoplay.play();
4010
+ } else if (this.autoScroll) {
4011
+ this.embla?.plugins()?.autoScroll.play();
4012
+ }
4013
+ }
4014
+
4015
+ /**
4016
+ * Stops the slideshow playback.
4017
+ * @returns {void}
4018
+ */
4019
+ stop() {
4020
+ if (this.autoplay) {
4021
+ this.embla?.plugins()?.autoplay.stop();
4022
+ } else if (this.autoScroll) {
4023
+ this.embla?.plugins()?.autoScroll.stop();
4024
+ }
4025
+ }
4026
+
4027
+ /**
4028
+ * Scrolls to the previous slide.
4029
+ * @returns {void}
4030
+ */
4031
+ scrollPrev() {
4032
+ this.embla.scrollPrev();
4033
+ }
4034
+
4035
+ /**
4036
+ * Scrolls to the next slide.
4037
+ * @returns {void}
4038
+ */
4039
+ scrollNext() {
4040
+ this.embla.scrollNext();
4041
+ }
4042
+
4043
+ // ========== PRIVATE METHODS =================
4044
+
4045
+ /**
4046
+ * @private
4047
+ * Initializes the Embla carousel with the provided options and plugins.
4048
+ */
3976
4049
  initializeEmbla() {
3977
4050
  const emblaNode = this.shadowRoot.querySelector(".embla");
3978
4051
  const options = { loop: this.loop, align: "start" };
@@ -3989,6 +4062,7 @@ class AuroSlideshow extends i$2 {
3989
4062
  playOnInit: this.playOnInit,
3990
4063
  delay: this.delay,
3991
4064
  stopOnMouseEnter: true,
4065
+ stopOnLastSnap: !this.loop,
3992
4066
  };
3993
4067
 
3994
4068
  const autoscrollOptions = {
@@ -4000,17 +4074,17 @@ class AuroSlideshow extends i$2 {
4000
4074
 
4001
4075
  const plugins = [ClassNames(classNamesOptions)];
4002
4076
 
4003
- // Autoplay and AutoScroll cannot be used together.
4077
+ // Prevent both autoplay and autoScroll from being used simultaneously.
4004
4078
  if (this.autoplay && this.autoScroll) {
4005
4079
  console.warn(
4006
- "Autoplay and AutoScroll are not meant to be used together. Please select one.",
4080
+ "Autoplay and AutoScroll are not meant to be used together. AutoScroll has been disabled.",
4007
4081
  );
4008
4082
  this.autoScroll = false;
4009
4083
  }
4010
4084
  if (this.autoplay) {
4011
4085
  plugins.push(Autoplay(autoplayOptions));
4012
4086
  }
4013
- if (this.autoScroll) {
4087
+ if (this.autoScroll && !this.isTouchDevice()) {
4014
4088
  plugins.push(AutoScroll(autoscrollOptions));
4015
4089
  }
4016
4090
 
@@ -4021,43 +4095,57 @@ class AuroSlideshow extends i$2 {
4021
4095
  this.addDotBtnsAndClickHandlers(
4022
4096
  this.embla,
4023
4097
  this._dotsNode,
4024
- this.onNavButtonClick,
4098
+ this.stopAutoplayOnInteraction,
4025
4099
  );
4026
4100
  }
4027
4101
 
4102
+ if (this.navigation && !this.isTouchDevice()) {
4103
+ this.embla
4104
+ .on("select", this.toggleNavBtnsState)
4105
+ .on("init", this.toggleNavBtnsState)
4106
+ .on("reInit", this.toggleNavBtnsState);
4107
+ }
4108
+
4028
4109
  if (this.autoplay) {
4029
- this.addAutoPlayBtnListener(this.embla, this._playBtn);
4030
4110
  this.embla
4031
- .on("autoplay:stop", () => {
4032
- this.isPlaying = false;
4033
- })
4034
- .on("autoplay:play", () => {
4035
- this.isPlaying = true;
4036
- });
4111
+ .on("autoplay:stop", this.togglePlayButtonOnStop)
4112
+ .on("autoplay:play", this.togglePlayButtonOnPlay);
4037
4113
  }
4038
4114
 
4039
- if (this.autoScroll) {
4040
- this.addAutoScrollBtnListener(this.embla, this._playBtn);
4115
+ if (this.autoScroll && !this.isTouchDevice()) {
4041
4116
  this.embla
4042
- .on("autoScroll:stop", () => {
4043
- this.isPlaying = false;
4044
- })
4045
- .on("autoScroll:play", () => {
4046
- this.isPlaying = true;
4047
- });
4117
+ .on("autoScroll:stop", this.togglePlayButtonOnStop)
4118
+ .on("autoScroll:play", this.togglePlayButtonOnPlay);
4048
4119
  }
4049
4120
  }
4050
4121
 
4051
- firstUpdated() {
4052
- this.updateSlides();
4053
- this.initializeEmbla();
4054
-
4055
- // add event listener to embla instance to toggle tabindex on active slide whenever slide is changed
4056
- this.embla.on("select", this.toggleTabIndex);
4122
+ /**
4123
+ * @private
4124
+ * Gets slides from the slot, adds necessary classes and event listeners,
4125
+ * and updates the Embla instance with the new slides.
4126
+ */
4127
+ updateSlides() {
4128
+ if (this._slot) {
4129
+ this.slides = Array.from(this._slot.assignedElements());
4130
+
4131
+ this.slides.forEach((element, index) => {
4132
+ element.classList.add("embla__slide");
4133
+ element.addEventListener("keydown", this.handleKeydown);
4134
+ if (index === 0) {
4135
+ element.setAttribute("tabindex", "0");
4136
+ } else {
4137
+ element.setAttribute("tabindex", "-1");
4138
+ }
4139
+ });
4057
4140
 
4058
- this.isPlaying = this.playOnInit;
4141
+ // Attach slides to the Embla instance
4142
+ const emblaContainer = this.shadowRoot.querySelector(".embla__container");
4143
+ emblaContainer.replaceChildren(...this.slides);
4144
+ }
4059
4145
  }
4060
4146
 
4147
+ // ========== PRIVATE HELPER METHODS =================
4148
+
4061
4149
  /**
4062
4150
  * @private
4063
4151
  * Toggles the tabindex attribute on the active slide to allow keyboard navigation.
@@ -4072,130 +4160,137 @@ class AuroSlideshow extends i$2 {
4072
4160
 
4073
4161
  /**
4074
4162
  * @private
4075
- * Handles the slot change event to update slides and reinitialize Embla.
4163
+ * Toggles the icon and aria-label of the play button to stopped state.
4076
4164
  */
4077
- handleSlotChange() {
4078
- this.updateSlides();
4079
-
4080
- // Reinitialize Embla with new slides
4081
- if (this.embla) {
4082
- this.embla.reInit();
4083
- }
4084
- }
4165
+ togglePlayButtonOnStop = () => {
4166
+ this.isPlaying = false;
4167
+ this.playBtnLabel = this.playLabel;
4168
+ };
4085
4169
 
4086
4170
  /**
4087
4171
  * @private
4088
- * @param {KeyboardEvent} event - The keydown event triggered by the user.
4089
- * @returns {void}
4172
+ * Toggles the icon and aria-label of the play button to playing state.
4090
4173
  */
4091
- handleKeydown = (event) => {
4092
- const focusActiveSlide = () => {
4093
- setTimeout(() => {
4094
- const activeSlide = this.slides[this.embla.selectedScrollSnap()];
4095
- activeSlide.focus();
4096
- }, 200);
4097
- };
4098
- if (event.key === "ArrowLeft") {
4099
- event.preventDefault();
4100
- this.embla.scrollPrev();
4101
- focusActiveSlide();
4102
- } else if (event.key === "ArrowRight") {
4103
- event.preventDefault();
4104
- this.embla.scrollNext();
4105
- focusActiveSlide();
4106
- }
4174
+ togglePlayButtonOnPlay = () => {
4175
+ this.isPlaying = true;
4176
+ this.playBtnLabel = this.pauseLabel;
4107
4177
  };
4108
4178
 
4109
- /** @private */
4110
- updateSlides() {
4111
- if (!this._slot) {
4112
- return;
4179
+ /**
4180
+ * @private
4181
+ * Adds and removes disabled attribute if at the beginning or end of the slideshow and loop is off.
4182
+ */
4183
+ toggleNavBtnsState = () => {
4184
+ if (this.embla.canScrollPrev()) {
4185
+ this._prevBtn.removeAttribute("disabled");
4186
+ } else {
4187
+ this._prevBtn.setAttribute("disabled", "");
4113
4188
  }
4114
4189
 
4115
- this.slides = Array.from(this._slot.assignedElements());
4116
-
4117
- this.slides.forEach((element, index) => {
4118
- element.classList.add("embla__slide");
4119
- element.addEventListener("keydown", this.handleKeydown);
4120
- if (index === 0) {
4121
- element.setAttribute("tabindex", "0");
4122
- } else {
4123
- element.setAttribute("tabindex", "-1");
4124
- }
4125
- });
4126
-
4127
- // Attach slides to the Embla instance
4128
- const emblaContainer = this.shadowRoot.querySelector(".embla__container");
4129
- emblaContainer.replaceChildren(...this.slides);
4130
- }
4190
+ if (this.embla.canScrollNext()) {
4191
+ this._nextBtn.removeAttribute("disabled");
4192
+ } else {
4193
+ this._nextBtn.setAttribute("disabled", "");
4194
+ }
4195
+ };
4131
4196
 
4132
4197
  /**
4133
4198
  * @private
4134
- * Stops autoplay when the user interacts with the navigation buttons.
4199
+ * Stops autoplay when the user interacts with the navigation controls or pagination dots.
4135
4200
  */
4136
- onNavButtonClick = (emblaApi) => {
4201
+ stopAutoplayOnInteraction = (emblaApi) => {
4137
4202
  const autoplay = emblaApi?.plugins()?.autoplay;
4138
4203
  if (!autoplay) return;
4139
4204
 
4140
- const resetOrStop =
4141
- autoplay.options.stopOnInteraction === false
4142
- ? autoplay.reset
4143
- : autoplay.stop;
4144
-
4145
- resetOrStop();
4205
+ autoplay.stop();
4146
4206
  };
4147
4207
 
4148
4208
  /**
4149
4209
  * @private
4150
- * Creates event listeners for controlling autoplay or auto scroll.
4210
+ * Checks to see if the user is on a touch device.
4211
+ * @returns {boolean} True if touch device, false otherwise.
4151
4212
  */
4152
- createPlayButtonListeners = (type) => (emblaApi, playBtn) => {
4153
- const plugin = type === "autoplay" ? "autoplay" : "autoScroll";
4213
+ isTouchDevice() {
4214
+ return window.matchMedia("(pointer: coarse)").matches;
4215
+ }
4154
4216
 
4155
- const togglePlayBtnState = () => {
4156
- const control = emblaApi?.plugins()?.[plugin];
4157
- if (!control) return;
4217
+ // ========== EVENT HANDLERS =================
4158
4218
 
4159
- this.playBtnLabel = control.isPlaying()
4160
- ? this.playLabel
4161
- : this.pauseLabel; // these are reversed
4162
- };
4219
+ /**
4220
+ * @private
4221
+ * Handles the slot change event to update slides and initialize or reinitialize Embla.
4222
+ * If the slot is empty, it will not initialize Embla.
4223
+ */
4224
+ handleSlotChange() {
4225
+ this.updateSlides();
4163
4226
 
4164
- const onPlayBtnClick = () => {
4165
- const control = emblaApi?.plugins()?.[plugin];
4166
- if (!control) return;
4227
+ if (this.embla) {
4228
+ this.embla.reInit();
4229
+ } else {
4230
+ this.initializeEmbla();
4231
+ }
4167
4232
 
4168
- const playOrStop = control.isPlaying() ? control.stop : control.play;
4169
- playOrStop();
4170
- this.isPlaying = control.isPlaying();
4171
- };
4233
+ // add event listener to embla instance to toggle tabindex on active slide whenever slide is changed
4234
+ this.embla.on("select", this.toggleTabIndex);
4172
4235
 
4173
- playBtn?.addEventListener("click", onPlayBtnClick);
4174
- emblaApi
4175
- .on(`${plugin}:play`, togglePlayBtnState)
4176
- .on(`${plugin}:stop`, togglePlayBtnState)
4177
- .on("reInit", togglePlayBtnState);
4236
+ // Set isPlaying to true if play is triggered on init
4237
+ this.isPlaying = this.playOnInit;
4238
+ }
4178
4239
 
4179
- return () => {
4180
- playBtn?.removeEventListener("click", onPlayBtnClick);
4181
- emblaApi
4182
- .off(`${plugin}:play`, togglePlayBtnState)
4183
- .off(`${plugin}:stop`, togglePlayBtnState)
4184
- .off("reInit", togglePlayBtnState);
4185
- };
4186
- };
4240
+ /**
4241
+ * @private
4242
+ * @param {string} direction - The direction of the navigation ("prev" or "next").
4243
+ * Handles click events on the previous and next buttons.
4244
+ */
4245
+ handleNavClick(direction) {
4246
+ if (direction === "prev") {
4247
+ this.scrollPrev();
4248
+ }
4249
+ if (direction === "next") {
4250
+ this.scrollNext();
4251
+ }
4252
+ this.stopAutoplayOnInteraction(this.embla);
4253
+ }
4187
4254
 
4188
4255
  /**
4189
4256
  * @private
4190
- * Adds event listeners to the play button to control autoplay.
4257
+ * @param {KeyboardEvent} event - The keydown event triggered by the user.
4258
+ * Allows users to navigate through the slideshow using left/right arrow keys.
4191
4259
  */
4192
- addAutoPlayBtnListener = this.createPlayButtonListeners("autoplay");
4260
+ handleKeydown = (event) => {
4261
+ const focusActiveSlide = () => {
4262
+ // Timeout added for UX optimization
4263
+ setTimeout(() => {
4264
+ const activeSlide = this.slides[this.embla.selectedScrollSnap()];
4265
+ activeSlide.focus();
4266
+ }, 200);
4267
+ };
4268
+ if (event.key === "ArrowLeft") {
4269
+ event.preventDefault();
4270
+ this.scrollPrev();
4271
+ focusActiveSlide();
4272
+ } else if (event.key === "ArrowRight") {
4273
+ event.preventDefault();
4274
+ this.scrollNext();
4275
+ focusActiveSlide();
4276
+ }
4277
+ };
4193
4278
 
4194
4279
  /**
4195
4280
  * @private
4196
- * Adds event listeners to the play button to control auto scroll.
4281
+ * Handles click events on the play button to toggle autoplay or autoScroll.
4197
4282
  */
4198
- addAutoScrollBtnListener = this.createPlayButtonListeners("autoScroll");
4283
+ handlePlayClick() {
4284
+ if (this.isPlaying) {
4285
+ this.stop();
4286
+ this.togglePlayButtonOnStop();
4287
+ } else {
4288
+ this.play();
4289
+ this.togglePlayButtonOnPlay();
4290
+ }
4291
+ }
4292
+
4293
+ // ========== DOTS AND PROGRESS BAR METHODS =================
4199
4294
 
4200
4295
  /**
4201
4296
  * @private
@@ -4205,12 +4300,12 @@ class AuroSlideshow extends i$2 {
4205
4300
  let dotNodes = [];
4206
4301
 
4207
4302
  const addDotBtnsWithClickHandlers = () => {
4208
- // Create new dot buttons using Lit's render
4209
4303
  const dots = emblaApi.scrollSnapList().map((_, index) => {
4210
4304
  const button = document.createElement("button");
4211
4305
  button.className = "embla__dot";
4212
4306
  button.type = "button";
4213
4307
  button.tabIndex = -1;
4308
+ button.setAttribute("aria-label", `Go to slide ${index + 1}`); // TODO: localization
4214
4309
  button.addEventListener(
4215
4310
  "click",
4216
4311
  () => {
@@ -4222,11 +4317,18 @@ class AuroSlideshow extends i$2 {
4222
4317
  return button;
4223
4318
  });
4224
4319
 
4225
- // Use a single DOM operation to update dots
4226
4320
  dotsNode.replaceChildren(...dots);
4227
4321
  dotNodes = dots;
4228
4322
  };
4229
4323
 
4324
+ // 'stopped' class adds fill color to progress dot when not playing
4325
+ const toggleProgressStopped = () => {
4326
+ const selected = emblaApi.selectedScrollSnap();
4327
+ if (dotNodes[selected]) {
4328
+ dotNodes[selected].classList.toggle("stopped", !this.isPlaying);
4329
+ }
4330
+ };
4331
+
4230
4332
  const toggleDotBtnsActive = () => {
4231
4333
  const previous = emblaApi.previousScrollSnap();
4232
4334
  const selected = emblaApi.selectedScrollSnap();
@@ -4243,9 +4345,16 @@ class AuroSlideshow extends i$2 {
4243
4345
  if (dotNodes[selected]) {
4244
4346
  dotNodes[selected].className = "embla__progress";
4245
4347
  dotNodes[selected].replaceChildren(progressBar);
4348
+ if (!this.isPlaying) {
4349
+ dotNodes[selected].classList.add("stopped");
4350
+ }
4246
4351
  }
4247
4352
 
4248
4353
  this.addAutoplayProgressListeners(this.embla, this._progressNode);
4354
+
4355
+ emblaApi
4356
+ .on("autoplay:play", toggleProgressStopped) // Removes fill color when autoplay starts
4357
+ .on("autoplay:stop", toggleProgressStopped); // Adds fill color when autoplay stops (temp solution until paused state is implemented)
4249
4358
  } else {
4250
4359
  if (dotNodes[previous]) {
4251
4360
  dotNodes[previous].classList.remove("embla__dot--selected");
@@ -4270,6 +4379,8 @@ class AuroSlideshow extends i$2 {
4270
4379
 
4271
4380
  /**
4272
4381
  * @private
4382
+ * Adds autoplay progress listeners to the progress bar.
4383
+ * This function updates the progress bar animation based on the autoplay timer.
4273
4384
  */
4274
4385
  addAutoplayProgressListeners = (emblaApi, progressNode) => {
4275
4386
  const progressBarNode = progressNode.querySelector(".embla__progress__bar");
@@ -4318,13 +4429,34 @@ class AuroSlideshow extends i$2 {
4318
4429
  };
4319
4430
  };
4320
4431
 
4432
+ // ========== LIFECYCLE METHODS =================
4433
+
4321
4434
  disconnectedCallback() {
4322
4435
  super.disconnectedCallback();
4323
4436
 
4324
- this.embla.off("select", this.toggleTabIndex);
4325
-
4326
4437
  // Clean up event listeners and Embla instance
4327
4438
  if (this.embla) {
4439
+ this.embla.off("select", this.toggleTabIndex);
4440
+
4441
+ if (this.navigation && !this.isTouchDevice()) {
4442
+ this.embla
4443
+ .off("select", this.toggleNavBtnsState)
4444
+ .off("init", this.toggleNavBtnsState)
4445
+ .off("reInit", this.toggleNavBtnsState);
4446
+ }
4447
+
4448
+ if (this.autoplay) {
4449
+ this.embla
4450
+ .off("autoplay:stop", this.togglePlayButtonOnStop)
4451
+ .off("autoplay:play", this.togglePlayButtonOnPlay);
4452
+ }
4453
+
4454
+ if (this.autoScroll && !this.isTouchDevice()) {
4455
+ this.embla
4456
+ .off("autoScroll:stop", this.togglePlayButtonOnStop)
4457
+ .off("autoScroll:play", this.togglePlayButtonOnPlay);
4458
+ }
4459
+
4328
4460
  this.embla.destroy();
4329
4461
  this.embla = null;
4330
4462
  }
@@ -4336,6 +4468,8 @@ class AuroSlideshow extends i$2 {
4336
4468
  this.slides = [];
4337
4469
  }
4338
4470
 
4471
+ // ========== RENDER METHODS =================
4472
+
4339
4473
  /**
4340
4474
  * Internal function to generate the HTML for the icon to use.
4341
4475
  * @private
@@ -4364,7 +4498,8 @@ class AuroSlideshow extends i$2 {
4364
4498
  shape="circle"
4365
4499
  onDark
4366
4500
  size="lg"
4367
- @click=${() => this.embla?.scrollPrev()}>
4501
+ @click=${() => this.handleNavClick("prev")}
4502
+ part="prev-button">
4368
4503
  ${this.generateIconHtml(chevronLeft.svg)}
4369
4504
  </${this.buttonTag}>
4370
4505
  <${this.buttonTag}
@@ -4373,7 +4508,8 @@ class AuroSlideshow extends i$2 {
4373
4508
  shape="circle"
4374
4509
  onDark
4375
4510
  size="lg"
4376
- @click=${() => this.embla?.scrollNext()}>
4511
+ @click=${() => this.handleNavClick("next")}
4512
+ part="next-button">
4377
4513
  ${this.generateIconHtml(chevronRight.svg)}
4378
4514
  </${this.buttonTag}>`;
4379
4515
  }
@@ -4385,6 +4521,8 @@ class AuroSlideshow extends i$2 {
4385
4521
  aria-label="${this.playBtnLabel}"
4386
4522
  class="play-pause"
4387
4523
  shape="circle"
4524
+ @click=${() => this.handlePlayClick()}
4525
+ part="play-pause-button"
4388
4526
  >
4389
4527
  ${this.generateIconHtml(play.svg, this.isPlaying)}
4390
4528
  ${this.generateIconHtml(pause.svg, !this.isPlaying)}
@@ -4401,7 +4539,7 @@ class AuroSlideshow extends i$2 {
4401
4539
  renderPaginationContainer() {
4402
4540
  return u`
4403
4541
  <div class="pagination-container">
4404
- ${this.autoplay || this.autoScroll ? this.renderPlayButton() : E}
4542
+ ${this.autoplay || (this.autoScroll && !this.isTouchDevice()) ? this.renderPlayButton() : E}
4405
4543
  ${this.pagination ? u`<div class="embla__dots"></div>` : E}
4406
4544
  </div>
4407
4545
  `;
@@ -4411,14 +4549,20 @@ class AuroSlideshow extends i$2 {
4411
4549
  return u`
4412
4550
  <div class="container">
4413
4551
  <div class="slideshow-wrapper">
4414
- ${this.navigation ? this.renderNavigationControls() : E}
4552
+ ${this.navigation && !this.isTouchDevice() ? this.renderNavigationControls() : E}
4415
4553
  <div class="embla">
4416
4554
  <div class="embla__container">
4417
4555
  <slot @slotchange=${this.handleSlotChange}></slot>
4418
4556
  </div>
4419
4557
  </div>
4420
4558
  </div>
4421
- ${this.pagination || this.autoplay || this.autoScroll ? this.renderPaginationContainer() : E}
4559
+ ${
4560
+ this.pagination ||
4561
+ this.autoplay ||
4562
+ (this.autoScroll && !this.isTouchDevice())
4563
+ ? this.renderPaginationContainer()
4564
+ : E
4565
+ }
4422
4566
  </div>
4423
4567
  `;
4424
4568
  }