import { h } from './index-BdVwMG9u.js';
import { B as BEM } from './bem-ByiN-QjD.js';
import { s as stopEventBubbling } from './form-input-DYvzaBlM.js';
import { i as isDescendant, k as debounce, o as addEventListener, p as removeEventListener, m as raf } from './helpers-BNj87EbG.js';
import { h as isTabKey } from './keyboard.helpers-BERg8BNc.js';

const convert = (value) => parseInt(value.slice(0, -2), 10);
const getComputedPadding = (element, defaultPadding = 16) => {
    if (typeof window === 'undefined' || element === undefined) {
        return { top: defaultPadding, right: defaultPadding, bottom: defaultPadding, left: defaultPadding };
    }
    const computedStyle = window.getComputedStyle(element);
    const top = convert(computedStyle.getPropertyValue('padding-top'));
    const right = convert(computedStyle.getPropertyValue('padding-right'));
    const bottom = convert(computedStyle.getPropertyValue('padding-bottom'));
    const left = convert(computedStyle.getPropertyValue('padding-left'));
    return { top, right, bottom, left };
};
const getComputedWidth = (element) => {
    if (typeof window === 'undefined') {
        return element.clientWidth;
    }
    const computedStyle = window.getComputedStyle(element);
    const boxSizing = computedStyle.getPropertyValue('box-sizing');
    const width = convert(computedStyle.getPropertyValue('width'));
    if (boxSizing === 'border-box') {
        return width;
    }
    const right = convert(computedStyle.getPropertyValue('padding-right'));
    const left = convert(computedStyle.getPropertyValue('padding-left'));
    return left + width + right;
};
const getWidthOfOverflowingChildren = (element) => {
    const children = Array.from(element.children);
    let maxWidth = 0;
    for (const child of children) {
        const childWidth = child.offsetLeft + child.offsetWidth;
        maxWidth = Math.max(maxWidth, childWidth);
    }
    return maxWidth;
};

const DotControl = ({ value, items, containerId, onControlChange }) => {
    const block = BEM.block('swiper');
    const controls = block.element('controls');
    const onChange = (ev) => {
        let selectedValue = ev.detail - 1;
        if (selectedValue < 0) {
            selectedValue = 0;
        }
        if (selectedValue >= items.length) {
            selectedValue = items.length - 1;
        }
        onControlChange(items[selectedValue]);
    };
    return (h("div", { class: Object.assign(Object.assign({}, controls.class()), controls.modifier('dots').class()) }, h("bal-pagination", { "aria-controls": containerId, interface: "small", value: value + 1, totalPages: items.length, onBalChange: onChange })));
};

const LargeControl = ({ isFirst, isLast, inverted, areControlsHidden, onNextClick, onPreviousClick, containerId, leftControlTitle, rightControlTitle, }) => {
    const block = BEM.block('swiper');
    const controls = block.element('controls');
    const button = controls.element('button');
    return (h("div", { "data-mutation": "false", class: Object.assign(Object.assign({}, controls.class()), controls.modifier('large').class()) }, h("bal-button", { class: Object.assign(Object.assign(Object.assign({}, button.class()), button.modifier('left').class()), button.modifier('hidden').class(isFirst && areControlsHidden)), "aria-controls": containerId, square: true, icon: "nav-go-left", rounded: true, inverted: inverted, onClick: () => onPreviousClick(), tabindex: -1, disabled: isFirst, "aria-hidden": isFirst && areControlsHidden ? 'true' : null, "data-testid": "bal-carousel-control-left", title: leftControlTitle }), h("bal-button", { class: Object.assign(Object.assign(Object.assign({}, button.class()), button.modifier('right').class()), button.modifier('hidden').class(isLast && areControlsHidden)), "aria-controls": containerId, square: true, icon: "nav-go-right", rounded: true, inverted: inverted, onClick: () => onNextClick(), tabindex: -1, disabled: isLast, "aria-hidden": isLast && areControlsHidden ? 'true' : null, "data-testid": "bal-carousel-control-right", title: rightControlTitle })));
};

const SmallControl = ({ isFirst, isLast, inverted, leftControlTitle, rightControlTitle, containerId, onNextClick, onPreviousClick, }) => {
    const block = BEM.block('swiper');
    const controls = block.element('controls');
    const button = controls.element('button');
    return (h("div", { class: Object.assign(Object.assign({}, controls.class()), controls.modifier('small').class()) }, h("bal-button", { class: Object.assign(Object.assign(Object.assign({}, button.class()), button.modifier('left').class()), button.modifier('hidden').class(isFirst)), "aria-controls": containerId, square: true, size: "small", icon: "nav-go-left", rounded: true, inverted: inverted, onClick: () => onPreviousClick(), disabled: isFirst, tabindex: "-1", "data-testid": "bal-carousel-control-left", title: leftControlTitle }), h("bal-button", { class: Object.assign(Object.assign(Object.assign({}, button.class()), button.modifier('right').class()), button.modifier('hidden').class(isLast)), "aria-controls": containerId, square: true, size: "small", icon: "nav-go-right", rounded: true, inverted: inverted, onClick: () => onNextClick(), disabled: isLast, tabindex: "-1", "data-testid": "bal-carousel-control-right", title: rightControlTitle })));
};

const i18nSwiperControlLabel = {
    de: {
        left: 'vorherige',
        right: 'nächste',
    },
    en: {
        left: 'previous',
        right: 'next',
    },
    fr: {
        left: 'précédent',
        right: 'suivant',
    },
    it: {
        left: 'precedente',
        right: 'successivo',
    },
    nl: {
        left: 'vorige',
        right: 'volgende',
    },
    es: {
        left: 'anterior',
        right: 'siguiente',
    },
    pl: {
        left: 'poprzedni',
        right: 'następny',
    },
    pt: {
        left: 'anterior',
        right: 'próximo',
    },
    sv: {
        left: 'föregående',
        right: 'nästa',
    },
    fi: {
        left: 'edellinen',
        right: 'seuraava',
    },
};

class SwiperUtil {
    constructor() {
        this.previousTransformValue = 0;
        this.active = true;
        this.id = `bal-swiper-${SwiperIds++}`;
        this.containerId = `${this.id}-container`;
        this.index = 0;
        this.steps = 1;
        this.noNeedForControls = true;
        this.isLastSlideVisible = false;
        this.itemsPerView = 1;
        this.controls = 'none';
        this.gapSpace = 'none';
        /**
         * EVENTS
         * ------------------------------------------------------
         */
        this.keyboardMode = false;
        this.shiftMode = false;
        this.isInsideContainer = false;
        this.focusByKey = false;
        this.pointerDown = () => {
            this.keyboardMode = false;
            this.shiftMode = false;
            this.focusByKey = false;
            this.isInsideContainer = false;
        };
        this.listenToKeyDown = (ev) => {
            this.keyboardMode = isTabKey(ev);
            this.shiftMode = ev.shiftKey;
            this.focusByKey = false;
            this.isInsideContainer = isDescendant(this.containerEl, ev.target);
            if (this.active) {
                if (this.controls !== 'dots' && this.controls !== 'tabs') {
                    if (this.isInsideContainer && this.keyboardMode) {
                        if (this.shiftMode && this.index > 0) {
                            this.focusByKey = true;
                            this.focusPreviousItem(ev);
                        }
                        else if (!this.shiftMode && this.index < this.lastIndex()) {
                            this.focusByKey = true;
                            this.focusNextItem(ev);
                        }
                    }
                }
            }
        };
        /**
         * PRIVATE METHODS
         * ------------------------------------------------------
         */
        this.updateFocus = (ev) => {
            if (this.active) {
                const focusByKey = this.focusByKey;
                const backwards = this.shiftMode;
                const isInsideContainer = this.isInsideContainer;
                // when the focus enters the component we focus
                // the last focused item
                if (this.controls !== 'dots' && this.controls !== 'tabs' && focusByKey && !isInsideContainer) {
                    if (backwards) {
                        this.focusItem(this.lastIndex());
                    }
                    else {
                        this.focusItem(0);
                    }
                }
            }
        };
        /**
         * CSS Classes
         * ------------------------------------------------------
         */
        this.cssBlock = BEM.block('swiper');
        this.cssSwiper = () => (Object.assign({}, this.cssBlock.class(this.active)));
        this.cssInnerSwiper = () => (Object.assign(Object.assign(Object.assign(Object.assign({}, this.cssBlock.element('inner').class(this.active)), this.cssBlock.element('inner').modifier(`items-per-view-${this.itemsPerView}`).class(this.active)), this.cssBlock.element('inner').modifier(`shadow-left`).class(this.hasShadowLeft())), this.cssBlock.element('inner').modifier(`shadow-right`).class(this.hasShadowRight())));
        this.cssSwiperContainer = () => (Object.assign(Object.assign({}, this.cssBlock.element('container').class(this.active)), this.cssBlock.element('container').modifier(`gap-${this.gapSpace}`).class(this.active)));
        /**
         * PUBLIC METHODS
         * ------------------------------------------------------
         */
        this.notifyChange = debounce(() => this.notifyChangeInternal(), 100);
        this.onControlChange = (selectedValue) => {
            if (selectedValue !== this.index) {
                const isForward = selectedValue > this.index;
                if (isForward) {
                    this.next(selectedValue - this.index);
                }
                else {
                    this.previous(this.index - selectedValue);
                }
            }
        };
        this.onPreviousButtonClick = () => this.previous();
        this.onNextButtonClick = () => this.next();
    }
    /**
     * LIFECYCLE
     * ------------------------------------------------------
     */
    connectedCallback(component) {
        this.component = component;
        addEventListener(window, 'keydown', this.listenToKeyDown);
        addEventListener(this.component.el, 'focusin', this.updateFocus);
        addEventListener(window, 'touchstart', this.pointerDown);
        addEventListener(window, 'mousedown', this.pointerDown);
    }
    disconnectedCallback() {
        removeEventListener(window, 'keydown', this.listenToKeyDown);
        removeEventListener(this.component.el, 'focusin', this.updateFocus);
        removeEventListener(window, 'touchstart', this.pointerDown);
        removeEventListener(window, 'mousedown', this.pointerDown);
    }
    /**
     * PUBLIC METHODS
     * ------------------------------------------------------
     */
    isActive() {
        return this.active;
    }
    async activate() {
        this.active = true;
    }
    async disable() {
        this.active = false;
    }
    async focusItem(index) {
        const slide = await this.goTo(index);
        if (slide && slide.el) {
            await this.setFocusToEl(slide.el);
        }
    }
    async focusNextItem(ev) {
        const slide = await this.next(1);
        if (slide && slide.el) {
            stopEventBubbling(ev);
            await this.setFocusToEl(slide.el);
        }
    }
    async focusPreviousItem(ev) {
        const slide = await this.previous(1);
        if (slide && slide.el) {
            stopEventBubbling(ev);
            await this.setFocusToEl(slide.el);
        }
    }
    async setFocusToEl(el) {
        if (el.setFocus) {
            await el.setFocus();
        }
    }
    isFirst() {
        return this.index === 0;
    }
    hasShadowLeft() {
        return this.hasShadow() && this.index !== 0 && this.active;
    }
    hasShadowRight() {
        return this.hasShadow() && !this.isLastSlideVisible && this.active;
    }
    isLast() {
        return this.isLastSlideVisible;
    }
    async previous(steps = this.steps) {
        let previousValue = this.index - steps;
        if (previousValue < 0) {
            previousValue = 0;
        }
        return this.goTo(previousValue);
    }
    async next(steps = this.steps) {
        const items = this.component.swiperGetAllChildrenElements();
        const length = items.length;
        let nextValue = this.index + steps;
        if (nextValue >= length) {
            nextValue = length - 1;
        }
        return this.goTo(nextValue);
    }
    async updateIndex(index = this.index) {
        if (this.index !== index) {
            this.goTo(index);
            this.index = index;
            this.component.swiperOnChange(this.index);
        }
    }
    async goTo(index = this.index) {
        if (this.active) {
            const activeSlide = await this.buildSlide(index);
            if (activeSlide) {
                const didAnimate = await this.animate(activeSlide.transformActive, true);
                if (didAnimate || this.index !== index) {
                    this.index = index;
                    this.component.swiperOnChange(this.index);
                }
            }
            return activeSlide;
        }
    }
    renderControls() {
        if (!this.active || this.noNeedForControls || !this.component.hasAnimated) {
            return '';
        }
        const leftControlTitle = i18nSwiperControlLabel[this.component.language].left;
        const rightControlTitle = i18nSwiperControlLabel[this.component.language].right;
        switch (this.controls) {
            case 'small':
                return (h(SmallControl, { isFirst: this.isFirst(), isLast: this.isLast(), inverted: this.component.inverted, leftControlTitle: leftControlTitle, rightControlTitle: rightControlTitle, containerId: this.containerId, onNextClick: () => this.onNextButtonClick(), onPreviousClick: () => this.onPreviousButtonClick() }));
            case 'large':
                return (h(LargeControl, { isFirst: this.isFirst(), isLast: this.isLast(), inverted: this.component.inverted, areControlsHidden: !this.component.isMobile, leftControlTitle: leftControlTitle, rightControlTitle: rightControlTitle, containerId: this.containerId, onNextClick: () => this.onNextButtonClick(), onPreviousClick: () => this.onPreviousButtonClick() }));
            case 'dots':
                return (h(DotControl, { value: this.index, items: this.getAllControlItems(), containerId: this.containerId, onControlChange: item => this.onControlChange(item.value) }));
            default:
                return '';
        }
    }
    getAllControlItems() {
        const items = this.component.swiperGetAllChildrenElements();
        return items.map((item, index) => ({ value: index, label: item.label }));
    }
    /**
     * PRIVATE METHODS
     * ------------------------------------------------------
     */
    lastIndex() {
        const items = this.component.swiperGetAllChildrenElements();
        return items.length - 1;
    }
    hasShadow() {
        return !this.component.inverted && (this.itemsPerView === 'auto' || this.itemsPerView > 1);
    }
    async notifyChangeInternal() {
        const activeSlide = await this.buildSlide(this.index);
        if (activeSlide) {
            this.animate(activeSlide.transformActive, false);
        }
    }
    async buildSlide(slideIndex) {
        const items = this.component.swiperGetAllChildrenElements();
        const index = slideIndex === undefined ? this.lastIndex() : slideIndex;
        if (items.length > index && index >= 0) {
            const gaps = {
                none: 0,
                normal: 16,
                medium: 24,
            };
            const gapSize = gaps[this.gapSpace];
            const transformNext = items
                .filter((_, n) => n < index + 1)
                .reduce((acc, item) => acc + getComputedWidth(item) + gapSize, 0);
            const transformActive = items
                .filter((_, n) => n < index)
                .reduce((acc, item) => acc + getComputedWidth(item) + gapSize, 0);
            return {
                el: items[index],
                transformNext,
                transformActive,
                isFirst: index === 0,
                isLast: index === this.lastIndex(),
                total: this.lastIndex() + 1,
                index,
            };
        }
        return undefined;
    }
    async animate(pixels = 0, animated = true) {
        return new Promise(resolve => {
            if (this.currentRaf !== undefined) {
                cancelAnimationFrame(this.currentRaf);
            }
            this.currentRaf = raf(async () => {
                if (this.containerEl && this.innerEl) {
                    const lastSlide = await this.buildSlide();
                    if (lastSlide) {
                        const totalWidth = lastSlide.transformNext || 0;
                        const overflowWindowWidth = this.innerEl.clientWidth || 0;
                        if (totalWidth === 0 || overflowWindowWidth === 0) {
                            return resolve(false);
                        }
                        // get max amount of pixel to move the items container to the left or right
                        let maxAmountOfPixel = totalWidth - overflowWindowWidth;
                        // -1 one is needed for example when we use items per view 3 with 33.333%
                        if (this.itemsPerView === 3) {
                            maxAmountOfPixel = totalWidth - overflowWindowWidth - 1;
                        }
                        // last item is visible if the overflow window moved more than the diff
                        const isLastSlideVisible = maxAmountOfPixel <= pixels;
                        const isFirst = pixels === 0 || maxAmountOfPixel <= 2;
                        if (isFirst) {
                            this.index = 0;
                        }
                        const hasSmallControls = this.controls === 'small';
                        const hasLargeControls = this.controls === 'large';
                        this.noNeedForControls = totalWidth <= overflowWindowWidth;
                        let transformValue = this.noNeedForControls ? 0 : isLastSlideVisible ? maxAmountOfPixel : pixels;
                        if (!isFirst &&
                            !this.noNeedForControls &&
                            (hasSmallControls || (hasLargeControls && !this.component.isMobile))) {
                            transformValue = transformValue - (isLastSlideVisible ? 0 : hasLargeControls ? 56 : 48);
                        }
                        this.containerEl.style.transitionDuration = animated ? '0.6s' : '0';
                        this.containerEl.style.transform = `translate3d(-${transformValue}px, 0px, 0px)`;
                        const didAnimate = transformValue !== this.previousTransformValue;
                        this.previousTransformValue = transformValue;
                        this.isLastSlideVisible = isLastSlideVisible;
                        if (this.borderEl) {
                            this.borderEl.style.transitionDuration = animated ? '0.6s' : '0';
                            this.borderEl.style.transform = `translate3d(${transformValue}px, 0px, 0px)`;
                        }
                        this.component.hasAnimated = true;
                        if (!didAnimate) {
                            return resolve(false);
                        }
                        return resolve(true);
                    }
                }
            });
        });
    }
}
let SwiperIds = 0;

const toKebabCase = (string) => {
    if (typeof string === 'string') {
        return string
            .replace(/([a-z])([0-9])/g, '$1-$2')
            .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
            .replace(/[\s_]+/g, '-')
            .toLowerCase();
    }
    return '';
};

export { SwiperUtil as S, getWidthOfOverflowingChildren as a, getComputedPadding as g, toKebabCase as t };
