// #region Imports
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 __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Appearanceable } from '../../../Behaviors/Appearanceable';
import { Disableable } from '../../../Behaviors/Disableable';
import { Themeable } from '../../../Behaviors/Themeable';
import { Variantable } from '../../../Behaviors/Variantable';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { Key } from '../../../Types/Key';
import { FloatingActionButtonElement } from '../../Buttons/FloatingActionButton/FloatingActionButtonElement';
import { SelectorElement } from '../Abstracts/Selector/SelectorElement';
import { carouselElementTemplate } from './CarouselElementTemplate';
import { CarouselItemElement } from './CarouselItemElement';
import { carouselElementCosmopolitanStyle } from './Themes/CarouselElement.Cosmopolitan';
import { carouselElementJoyStyle } from './Themes/CarouselElement.Joy';
import { carouselElementMemphisStyle } from './Themes/CarouselElement.Memphis';
// #endregion
/**
 * Carousel - A content navigation component that displays items in a horizontally scrolling slideshow format.
 *
 * Enables users to browse through multiple content items (images, cards, media) in a space-efficient
 * carousel layout with navigation controls. Supports automatic cycling, manual navigation, and
 * touch/swipe interactions for engaging content presentation.
 *
 * @name CarouselElement
 * @element mosaik-carousel
 * @category Selectors
 *
 * @slot - Default content area for carousel items and navigation elements
 *
 * @csspart container - Main carousel container for content and controls
 * @csspart track - Scrollable track containing all carousel items
 * @csspart navigation - Navigation controls container (prev/next buttons, indicators)
 * @csspart indicators - Page indicators showing current position
 *
 * @cssprop {String} --carousel-track-gap - Spacing between carousel items
 * @cssprop {String} --carousel-item-width - Width of individual carousel items
 * @cssprop {String} --carousel-transition-duration - Animation duration for item transitions
 * @cssprop {Color} --carousel-navigation-color - Color for navigation controls
 * @cssprop {Color} --carousel-indicator-color - Color for page indicators
 * @cssprop {Color} --carousel-indicator-active-color - Color for active page indicator
 *
 * @keyhandle LeftArrow - Navigates to the previous item in the carousel
 * @keyhandle RightArrow - Navigates to the next item in the carousel
 * @keyhandle Home - Navigates to the first item in the carousel
 * @keyhandle End - Navigates to the last item in the carousel
 *
 * @dependency FloatingActionButtonElement - For navigation controls and action buttons
 * @dependency CarouselItemElement - For individual content items within the carousel
 *
 * @example
 * ```html
 * <!-- Image carousel with navigation -->
 * <mosaik-carousel auto-play="5000" show-indicators="true">
 *   <mosaik-carousel-item>
 *     <img src="slide1.jpg" alt="Product showcase 1" />
 *   </mosaik-carousel-item>
 *   <mosaik-carousel-item>
 *     <img src="slide2.jpg" alt="Product showcase 2" />
 *   </mosaik-carousel-item>
 *   <mosaik-carousel-item>
 *     <img src="slide3.jpg" alt="Product showcase 3" />
 *   </mosaik-carousel-item>
 * </mosaik-carousel>
 *
 * <!-- Card carousel for testimonials -->
 * <mosaik-carousel loop="true" items-per-view="2">
 *   <mosaik-carousel-item>
 *     <mosaik-card>
 *       <mosaik-text slot="header">"Excellent service!"</mosaik-text>
 *       <mosaik-text>- Customer A</mosaik-text>
 *     </mosaik-card>
 *   </mosaik-carousel-item>
 *   <mosaik-carousel-item>
 *     <mosaik-card>
 *       <mosaik-text slot="header">"Highly recommended"</mosaik-text>
 *       <mosaik-text>- Customer B</mosaik-text>
 *     </mosaik-card>
 *   </mosaik-carousel-item>
 * </mosaik-carousel>
 *
 * <!-- Product feature carousel -->
 * <mosaik-carousel show-navigation="true" transition="slide">
 *   <mosaik-carousel-item>
 *     <div class="feature-slide">
 *       <mosaik-icon data="shield-check"></mosaik-icon>
 *       <h3>Secure & Reliable</h3>
 *       <p>Enterprise-grade security for your data</p>
 *     </div>
 *   </mosaik-carousel-item>
 *   <mosaik-carousel-item>
 *     <div class="feature-slide">
 *       <mosaik-icon data="lightning-bolt"></mosaik-icon>
 *       <h3>Lightning Fast</h3>
 *       <p>Optimized performance for speed</p>
 *     </div>
 *   </mosaik-carousel-item>
 * </mosaik-carousel>
 * ```
 *
 * @public
 */
let CarouselElement = class CarouselElement extends Themeable(Disableable(Appearanceable(Variantable(SelectorElement)))) {
    // #region Fields
    _list = null;
    _position;
    _hasPrevious;
    _hasNext;
    _loop;
    _showNavigation;
    _showIndicator;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this._position = 0;
        this._hasPrevious = false;
        this._hasNext = false;
        this._loop = false;
        this._showNavigation = false;
        this._showIndicator = false;
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `{@link is}` property.
     * The `{@link is}` property represents natural name of this element.
     *
     * @public
     * @static
     * @override
     * @readonly
     */
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    static get is() {
        return 'mosaik-carousel';
    }
    /**
     * Gets or sets the `loop` property.
     *
     * @public
     * @attr
     */
    get loop() {
        return this._loop;
    }
    set loop(value) {
        if (this._loop !== value) {
            this._loop = value;
            this.requestUpdate('loop');
        }
    }
    /**
     * Gets or sets the `showNavigation` property.
     *
     * @public
     * @attr
     */
    get showNavigation() {
        return this._showNavigation;
    }
    set showNavigation(value) {
        if (this._showNavigation !== value) {
            this._showNavigation = value;
            this.requestUpdate('showNavigation');
        }
    }
    /**
     * Gets or sets the `showIndicator` property.
     *
     * @public
     * @attr
     */
    get showIndicator() {
        return this._showIndicator;
    }
    set showIndicator(value) {
        if (this._showIndicator !== value) {
            this._showIndicator = value;
            this.requestUpdate('showIndicator');
        }
    }
    /**
    * Gets or sets the `hasPrevious` property.
    *
    * @public
    */
    get hasPrevious() {
        return this._hasPrevious;
    }
    set hasPrevious(value) {
        if (this._hasPrevious !== value) {
            this._hasPrevious = value;
            this.requestUpdate('hasPrevious');
        }
    }
    /**
     * Gets or sets the `hasNext` property.
     *
     * @public
     * @attr
     */
    get hasNext() {
        return this._hasNext;
    }
    set hasNext(value) {
        if (this._hasNext !== value) {
            this._hasNext = value;
            this.requestUpdate('hasNext');
        }
    }
    /**
     * Gets the items of the Carousel.
     *
     * @remark
     * It is important to override the base class property to ensure that only
     * {@link CarouselItemElement} instances are returned.
     *
     * @public
     * @readonly
     * @ignore
     */
    get items() {
        return super.items;
    }
    // #endregion
    // #region Methods
    /**
     * @public
     * @override
     */
    connectedCallback() {
        super.connectedCallback();
        this.keyboard
            .add([Key.LeftArrow], () => this.selectPrevious())
            .add([Key.RightArrow], () => this.selectNext());
    }
    /**
     * @public
     * @override
     */
    select(item) {
        super.select(item);
        this.scrollToActiveItem();
    }
    /**
     * @public
     * @override
     */
    selectNext() {
        if (this.isOutOfRange(this.selectedIndex, 'next')) {
            this.selectFirst();
        }
        else {
            super.selectNext();
            this.scrollToActiveItem();
        }
    }
    /**
     * @public
     * @override
     */
    selectPrevious() {
        if (this.isOutOfRange(this.selectedIndex, 'prev')) {
            this.selectLast();
        }
        else {
            super.selectPrevious();
            this.scrollToActiveItem();
        }
    }
    /**
     * @public
     * @override
     */
    selectFirst() {
        super.selectFirst();
        this.scrollToActiveItem();
    }
    /**
     * @public
     * @override
     */
    selectLast() {
        super.selectLast();
        this.scrollToActiveItem();
    }
    /**
     * @protected
     * @override
     */
    onApplyTemplate() {
        super.onApplyTemplate();
        this._list = this.getTemplatePart('list');
    }
    /**
     * @protected
     * @override
     */
    onItemsChanged() {
        super.onItemsChanged();
        const items = this.items.filter((x) => x instanceof CarouselItemElement);
        // here we handle the special logic, when no item is initial isSelected.
        // we select the first item, if no item is selected.
        if (items.length && !items.some((x) => x.isSelected)) {
            items.at(0)?.select(true);
            // set the state of the navigation buttons
            this.hasPrevious = this._loop ? this._loop : false;
            this.hasNext = this._loop ? true : items.length > 1;
        }
    }
    /**
     * @private
     */
    scrollToActiveItem() {
        if (!this.isOutOfView(this.selectedItem?.index)) {
            return;
        }
        let targetItemIndex = this.selectedIndex;
        // Only shift the carousel by one if we're going forwards.
        // This looks better compared to moving the carousel by an entire page.
        if (this.selectedIndex > 0 && !this.isOutOfView(this.selectedIndex - 1)) {
            targetItemIndex = this.items.findIndex((_, i) => !this.isOutOfView(i)) + 1;
        }
        // // If loop is true and the current position is the last, go to first
        // if (this._loop && this.selectedIndex === this.items.length - 1) {
        //     targetItemIndex = 0;
        // }
        // // If loop is true and the current position is the first, go to last
        // if (this._loop && this.selectedIndex === 0) {
        //     targetItemIndex = this.items.length - 1;
        // }
        this._position = this.items[targetItemIndex].offsetLeft;
        if (this._list) {
            this._list.style.transform = `translateX(-${this._position}px)`;
        }
        this.hasPrevious = this._loop ? true : this.selectedIndex > 0;
        this.hasNext = this._loop ? this._loop : false;
        for (let i = this.items.length - 1; i > -1; i--) {
            if (this.isOutOfView(i, 'next')) {
                this.hasNext = this._loop ? true : true;
                break;
            }
        }
    }
    /**
     * Checks whether an item at a specific index is outside of the viewport.
     *
     * @private
     */
    isOutOfView(index, direction) {
        const { offsetWidth, offsetLeft } = this.items[index];
        return (!direction || direction === 'prev') && offsetLeft - this._position < 0
            ? true
            : (!direction || direction === 'next') && offsetWidth + offsetLeft - this._position > (this._list?.clientWidth ?? 0);
    }
    /**
     * Checks whether an index is out of range.
     *
     * @private
     */
    isOutOfRange(index, direction) {
        return (direction === 'prev') && index <= 0
            ? true
            : (direction === 'next') && index >= (this.items.length - 1);
    }
};
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CarouselElement.prototype, "loop", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CarouselElement.prototype, "showNavigation", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CarouselElement.prototype, "showIndicator", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CarouselElement.prototype, "hasPrevious", null);
__decorate([
    Attribute({
        type: Boolean,
        useDefault: true
    }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], CarouselElement.prototype, "hasNext", null);
CarouselElement = __decorate([
    Component({
        selector: 'mosaik-carousel',
        template: carouselElementTemplate,
        themes: {
            joy: carouselElementJoyStyle,
            memphis: carouselElementMemphisStyle,
            cosmopolitan: carouselElementCosmopolitanStyle
        },
        host: {
            tabIndex: '0'
        },
        imports: [
            FloatingActionButtonElement,
            CarouselItemElement
        ]
    }),
    __metadata("design:paramtypes", [])
], CarouselElement);
export { CarouselElement };
//# sourceMappingURL=CarouselElement.js.map