var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var IgcCarouselComponent_1;
import { ContextProvider } from '@lit/context';
import { LitElement, html, nothing } from 'lit';
import { property, queryAll, queryAssignedElements, state, } from 'lit/decorators.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { styleMap } from 'lit/directives/style-map.js';
import { themes } from '../../theming/theming-decorator.js';
import IgcButtonComponent from '../button/button.js';
import { carouselContext } from '../common/context.js';
import { addKeyboardFocusRing } from '../common/controllers/focus-ring.js';
import { addGesturesController, } from '../common/controllers/gestures.js';
import { addKeybindings, arrowLeft, arrowRight, endKey, homeKey, } from '../common/controllers/key-bindings.js';
import { createMutationController, } from '../common/controllers/mutation-observer.js';
import { watch } from '../common/decorators/watch.js';
import { registerComponent } from '../common/definitions/register.js';
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
import { asNumber, createCounter, findElementFromEventPath, first, formatString, isLTR, last, partNameMap, wrap, } from '../common/util.js';
import IgcIconComponent from '../icon/icon.js';
import IgcCarouselIndicatorContainerComponent from './carousel-indicator-container.js';
import IgcCarouselIndicatorComponent from './carousel-indicator.js';
import IgcCarouselSlideComponent from './carousel-slide.js';
import { styles } from './themes/carousel.base.css.js';
import { all } from './themes/container.js';
import { styles as shared } from './themes/shared/carousel.common.css.js';
let IgcCarouselComponent = IgcCarouselComponent_1 = class IgcCarouselComponent extends EventEmitterMixin(LitElement) {
    static register() {
        registerComponent(IgcCarouselComponent_1, IgcCarouselIndicatorComponent, IgcCarouselIndicatorContainerComponent, IgcCarouselSlideComponent, IgcIconComponent, IgcButtonComponent);
    }
    get hasProjectedIndicators() {
        return this._projectedIndicators.length > 0;
    }
    get showIndicatorsLabel() {
        return this.total > this.maximumIndicatorsCount;
    }
    get nextIndex() {
        return wrap(0, this.total - 1, this.current + 1);
    }
    get prevIndex() {
        return wrap(0, this.total - 1, this.current - 1);
    }
    _observerCallback({ changes: { added, attributes }, }) {
        const activeSlides = this.slides.filter((slide) => slide.active);
        if (activeSlides.length <= 1) {
            return;
        }
        const idx = this.slides.indexOf(added.length ? last(added).node : last(attributes));
        for (const [i, slide] of this.slides.entries()) {
            if (slide.active && i !== idx) {
                slide.active = false;
            }
        }
        this.activateSlide(this.slides[idx]);
    }
    get total() {
        return this.slides.length;
    }
    get current() {
        return Math.max(0, this.slides.indexOf(this._activeSlide));
    }
    get isPlaying() {
        return this._playing;
    }
    get isPaused() {
        return this._paused;
    }
    contextChanged() {
        this._context.setValue(this, true);
    }
    intervalChange() {
        if (!this.isPlaying) {
            this._playing = true;
        }
        this.restartInterval();
    }
    constructor() {
        super();
        this._carouselId = `igc-carousel-${IgcCarouselComponent_1.increment()}`;
        this._carouselKeyboardInteractionFocus = addKeyboardFocusRing(this);
        this._hasKeyboardInteractionOnIndicators = false;
        this._hasMouseStop = false;
        this._context = new ContextProvider(this, {
            context: carouselContext,
            initialValue: this,
        });
        this._carouselSlidesContainerRef = createRef();
        this._indicatorsContainerRef = createRef();
        this._prevButtonRef = createRef();
        this._nextButtonRef = createRef();
        this._playing = false;
        this._paused = false;
        this.disableLoop = false;
        this.disablePauseOnInteraction = false;
        this.hideNavigation = false;
        this.hideIndicators = false;
        this.vertical = false;
        this.indicatorsOrientation = 'end';
        this.indicatorsLabelFormat = 'Slide {0}';
        this.slidesLabelFormat = '{0} of {1}';
        this.maximumIndicatorsCount = 10;
        this.animationType = 'slide';
        this._internals = this.attachInternals();
        this._internals.role = 'region';
        this._internals.ariaRoleDescription = 'carousel';
        this.addEventListener('pointerdown', this.handlePointerDown);
        this.addEventListener('pointerenter', this.handlePointerEnter);
        this.addEventListener('pointerleave', this.handlePointerLeave);
        addGesturesController(this, {
            ref: this._carouselSlidesContainerRef,
            touchOnly: true,
        })
            .set('swipe-left', this.handleHorizontalSwipe)
            .set('swipe-right', this.handleHorizontalSwipe)
            .set('swipe-up', this.handleVerticalSwipe)
            .set('swipe-down', this.handleVerticalSwipe);
        addKeybindings(this, {
            ref: this._indicatorsContainerRef,
            bindingDefaults: { preventDefault: true },
        })
            .set(arrowLeft, this.handleArrowLeft)
            .set(arrowRight, this.handleArrowRight)
            .set(homeKey, this.handleHomeKey)
            .set(endKey, this.handleEndKey);
        addKeybindings(this, {
            ref: this._prevButtonRef,
            bindingDefaults: { preventDefault: true },
        }).setActivateHandler(this.handleNavigationInteractionPrevious);
        addKeybindings(this, {
            ref: this._nextButtonRef,
            bindingDefaults: { preventDefault: true },
        }).setActivateHandler(this.handleNavigationInteractionNext);
        createMutationController(this, {
            callback: this._observerCallback,
            filter: [IgcCarouselSlideComponent.tagName],
            config: {
                attributeFilter: ['active'],
                childList: true,
                subtree: true,
            },
        });
    }
    handleSlotChange() {
        if (this.total) {
            this.activateSlide(this.slides.findLast((slide) => slide.active) ?? first(this.slides));
        }
    }
    handleIndicatorSlotChange() {
        this.requestUpdate();
    }
    handlePointerDown() {
        if (this._carouselKeyboardInteractionFocus.focused) {
            this._carouselKeyboardInteractionFocus.reset();
        }
    }
    handlePointerEnter() {
        this._hasMouseStop = true;
        if (this._carouselKeyboardInteractionFocus.focused) {
            return;
        }
        this.handlePauseOnInteraction();
    }
    handlePointerLeave() {
        this._hasMouseStop = false;
        if (this._carouselKeyboardInteractionFocus.focused) {
            return;
        }
        this.handlePauseOnInteraction();
    }
    handleFocusIn() {
        if (this._carouselKeyboardInteractionFocus.focused || this._hasMouseStop) {
            return;
        }
        this.handlePauseOnInteraction();
    }
    handleFocusOut(event) {
        const node = event.relatedTarget;
        if (this.contains(node) || this.renderRoot.contains(node)) {
            return;
        }
        if (this._carouselKeyboardInteractionFocus.focused) {
            this._carouselKeyboardInteractionFocus.reset();
            if (!this._hasMouseStop) {
                this.handlePauseOnInteraction();
            }
        }
    }
    handlePauseOnInteraction() {
        if (!this.interval || this.disablePauseOnInteraction)
            return;
        if (this.isPlaying) {
            this.pause();
            this.emitEvent('igcPaused');
        }
        else {
            this.play();
            this.emitEvent('igcPlaying');
        }
    }
    async handleArrowLeft() {
        this._hasKeyboardInteractionOnIndicators = true;
        this.handleInteraction(isLTR(this) ? this.prev : this.next);
    }
    async handleArrowRight() {
        this._hasKeyboardInteractionOnIndicators = true;
        this.handleInteraction(isLTR(this) ? this.next : this.prev);
    }
    async handleHomeKey() {
        this._hasKeyboardInteractionOnIndicators = true;
        this.handleInteraction(() => this.select(isLTR(this) ? first(this.slides) : last(this.slides)));
    }
    async handleEndKey() {
        this._hasKeyboardInteractionOnIndicators = true;
        this.handleInteraction(() => this.select(isLTR(this) ? last(this.slides) : first(this.slides)));
    }
    handleVerticalSwipe({ data: { direction } }) {
        if (this.vertical) {
            this.handleInteraction(direction === 'up' ? this.next : this.prev);
        }
    }
    handleHorizontalSwipe({ data: { direction } }) {
        if (!this.vertical) {
            this.handleInteraction(async () => {
                if (isLTR(this)) {
                    direction === 'left' ? await this.next() : await this.prev();
                }
                else {
                    direction === 'left' ? await this.prev() : await this.next();
                }
            });
        }
    }
    async handleIndicatorClick(event) {
        const indicator = findElementFromEventPath(IgcCarouselIndicatorComponent.tagName, event);
        const index = this.hasProjectedIndicators
            ? this._projectedIndicators.indexOf(indicator)
            : Array.from(this._defaultIndicators).indexOf(indicator);
        this.handleInteraction(() => this.select(this.slides[index], index > this.current ? 'next' : 'prev'));
    }
    handleNavigationInteractionNext() {
        this.handleInteraction(this.next);
    }
    handleNavigationInteractionPrevious() {
        this.handleInteraction(this.prev);
    }
    async handleInteraction(callback) {
        if (this.interval) {
            this.resetInterval();
        }
        await callback.call(this);
        this.emitEvent('igcSlideChanged', { detail: this.current });
        if (this.interval) {
            this.restartInterval();
        }
    }
    activateSlide(slide) {
        if (this._activeSlide) {
            this._activeSlide.active = false;
        }
        this._activeSlide = slide;
        this._activeSlide.active = true;
        if (this._hasKeyboardInteractionOnIndicators) {
            this.hasProjectedIndicators
                ? this._projectedIndicators[this.current].focus()
                : this._defaultIndicators[this.current].focus();
            this._hasKeyboardInteractionOnIndicators = false;
        }
    }
    updateProjectedIndicators() {
        for (const [idx, slide] of this.slides.entries()) {
            const indicator = this._projectedIndicators[idx];
            indicator.active = slide.active;
            indicator.index = idx;
            this.setAttribute('aria-controls', slide.id);
        }
    }
    resetInterval() {
        if (this._lastInterval) {
            clearInterval(this._lastInterval);
            this._lastInterval = null;
        }
    }
    restartInterval() {
        this.resetInterval();
        if (asNumber(this.interval) > 0) {
            this._lastInterval = setInterval(() => {
                if (this.isPlaying && this.total) {
                    this.next();
                    this.emitEvent('igcSlideChanged', { detail: this.current });
                }
                else {
                    this.pause();
                }
            }, this.interval);
        }
    }
    async animateSlides(nextSlide, currentSlide, dir) {
        if (dir === 'next') {
            currentSlide.previous = true;
            currentSlide.toggleAnimation('out');
            this.activateSlide(nextSlide);
            await nextSlide.toggleAnimation('in');
            currentSlide.previous = false;
        }
        else {
            currentSlide.previous = true;
            currentSlide.toggleAnimation('in', 'reverse');
            this.activateSlide(nextSlide);
            await nextSlide.toggleAnimation('out', 'reverse');
            currentSlide.previous = false;
        }
    }
    play() {
        if (!this.isPlaying) {
            this._paused = false;
            this._playing = true;
            this.restartInterval();
        }
    }
    pause() {
        if (this.isPlaying) {
            this._playing = false;
            this._paused = true;
            this.resetInterval();
        }
    }
    async next() {
        if (this.disableLoop && this.nextIndex === 0) {
            this.pause();
            return false;
        }
        return await this.select(this.slides[this.nextIndex], 'next');
    }
    async prev() {
        if (this.disableLoop && this.prevIndex === this.total - 1) {
            this.pause();
            return false;
        }
        return await this.select(this.slides[this.prevIndex], 'prev');
    }
    async select(slideOrIndex, animationDirection) {
        let index;
        let slide;
        if (typeof slideOrIndex === 'number') {
            index = slideOrIndex;
            slide = this.slides.at(index);
        }
        else {
            slide = slideOrIndex;
            index = this.slides.indexOf(slide);
        }
        if (index === this.current || index === -1 || !slide) {
            return false;
        }
        const dir = animationDirection ?? (index > this.current ? 'next' : 'prev');
        await this.animateSlides(slide, this._activeSlide, dir);
        return true;
    }
    navigationTemplate() {
        return html `
      <igc-button
        ${ref(this._prevButtonRef)}
        type="button"
        part="navigation previous"
        aria-label="Previous slide"
        aria-controls=${this._carouselId}
        ?disabled=${this.disableLoop && this.current === 0}
        @click=${this.handleNavigationInteractionPrevious}
      >
        <slot name="previous-button">
          <igc-icon
            name="carousel_prev"
            collection="default"
            aria-hidden="true"
          ></igc-icon>
        </slot>
      </igc-button>
      <igc-button
        ${ref(this._nextButtonRef)}
        type="button"
        part="navigation next"
        aria-label="Next slide"
        aria-controls=${this._carouselId}
        ?disabled=${this.disableLoop && this.current === this.total - 1}
        @click=${this.handleNavigationInteractionNext}
      >
        <slot name="next-button">
          <igc-icon
            name="carousel_next"
            collection="default"
            aria-hidden="true"
          ></igc-icon>
        </slot>
      </igc-button>
    `;
    }
    *renderIndicators() {
        for (const [i, slide] of this.slides.entries()) {
            const forward = slide.active ? 'visible' : 'hidden';
            const backward = slide.active ? 'hidden' : 'visible';
            yield html `
        <igc-carousel-indicator
          exportparts="indicator, active, inactive"
          .active=${slide.active}
          .index=${i}
        >
          <div
            part="dot"
            style=${styleMap({ visibility: backward, zIndex: 1 })}
          ></div>
          <div
            part="dot active"
            slot="active"
            style=${styleMap({ visibility: forward })}
          ></div>
        </igc-carousel-indicator>
      `;
        }
    }
    indicatorTemplate() {
        const parts = partNameMap({
            indicators: true,
            start: this.indicatorsOrientation === 'start',
        });
        return html `
      <igc-carousel-indicator-container>
        <div ${ref(this._indicatorsContainerRef)} role="tablist" part=${parts}>
          <slot
            name="indicator"
            @slotchange=${this.handleIndicatorSlotChange}
            @click=${this.handleIndicatorClick}
          >
            ${this.hasProjectedIndicators
            ? this.updateProjectedIndicators()
            : this.renderIndicators()}
          </slot>
        </div>
      </igc-carousel-indicator-container>
    `;
    }
    labelTemplate() {
        const parts = partNameMap({
            label: true,
            indicators: true,
            start: this.indicatorsOrientation === 'start',
        });
        const value = formatString(this.slidesLabelFormat, this.current + 1, this.total);
        return html `
      <div part=${parts}>
        <span>${value}</span>
      </div>
    `;
    }
    render() {
        return html `
      <section @focusin=${this.handleFocusIn} @focusout=${this.handleFocusOut}>
        ${this.hideNavigation ? nothing : this.navigationTemplate()}
        ${this.hideIndicators || this.showIndicatorsLabel
            ? nothing
            : this.indicatorTemplate()}
        ${!this.hideIndicators && this.showIndicatorsLabel
            ? this.labelTemplate()
            : nothing}
        <div
          ${ref(this._carouselSlidesContainerRef)}
          id=${this._carouselId}
          aria-live=${this.interval && this.isPlaying ? 'off' : 'polite'}
        >
          <slot @slotchange=${this.handleSlotChange}></slot>
        </div>
      </section>
    `;
    }
};
IgcCarouselComponent.styles = [styles, shared];
IgcCarouselComponent.tagName = 'igc-carousel';
IgcCarouselComponent.increment = createCounter();
__decorate([
    queryAll(IgcCarouselIndicatorComponent.tagName)
], IgcCarouselComponent.prototype, "_defaultIndicators", void 0);
__decorate([
    queryAssignedElements({
        selector: IgcCarouselIndicatorComponent.tagName,
        slot: 'indicator',
    })
], IgcCarouselComponent.prototype, "_projectedIndicators", void 0);
__decorate([
    state()
], IgcCarouselComponent.prototype, "_activeSlide", void 0);
__decorate([
    state()
], IgcCarouselComponent.prototype, "_playing", void 0);
__decorate([
    state()
], IgcCarouselComponent.prototype, "_paused", void 0);
__decorate([
    property({ type: Boolean, reflect: true, attribute: 'disable-loop' })
], IgcCarouselComponent.prototype, "disableLoop", void 0);
__decorate([
    property({
        type: Boolean,
        reflect: true,
        attribute: 'disable-pause-on-interaction',
    })
], IgcCarouselComponent.prototype, "disablePauseOnInteraction", void 0);
__decorate([
    property({ type: Boolean, reflect: true, attribute: 'hide-navigation' })
], IgcCarouselComponent.prototype, "hideNavigation", void 0);
__decorate([
    property({ type: Boolean, reflect: true, attribute: 'hide-indicators' })
], IgcCarouselComponent.prototype, "hideIndicators", void 0);
__decorate([
    property({ type: Boolean, reflect: true })
], IgcCarouselComponent.prototype, "vertical", void 0);
__decorate([
    property({ reflect: false, attribute: 'indicators-orientation' })
], IgcCarouselComponent.prototype, "indicatorsOrientation", void 0);
__decorate([
    property({ attribute: 'indicators-label-format' })
], IgcCarouselComponent.prototype, "indicatorsLabelFormat", void 0);
__decorate([
    property({ attribute: 'slides-label-format' })
], IgcCarouselComponent.prototype, "slidesLabelFormat", void 0);
__decorate([
    property({ type: Number, reflect: false })
], IgcCarouselComponent.prototype, "interval", void 0);
__decorate([
    property({
        type: Number,
        reflect: false,
        attribute: 'maximum-indicators-count',
    })
], IgcCarouselComponent.prototype, "maximumIndicatorsCount", void 0);
__decorate([
    property({ attribute: 'animation-type' })
], IgcCarouselComponent.prototype, "animationType", void 0);
__decorate([
    queryAssignedElements({ selector: IgcCarouselSlideComponent.tagName })
], IgcCarouselComponent.prototype, "slides", void 0);
__decorate([
    watch('animationType'),
    watch('slidesLabelFormat'),
    watch('indicatorsLabelFormat')
], IgcCarouselComponent.prototype, "contextChanged", null);
__decorate([
    watch('interval')
], IgcCarouselComponent.prototype, "intervalChange", null);
IgcCarouselComponent = IgcCarouselComponent_1 = __decorate([
    themes(all)
], IgcCarouselComponent);
export default IgcCarouselComponent;
//# sourceMappingURL=carousel.js.map