/**
 * Vue 3 Carousel 0.8.1
 * (c) 2024
 * @license MIT
 */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) :
    typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.VueCarousel = {}, global.Vue));
})(this, (function (exports, vue) { 'use strict';

    const SNAP_ALIGN_OPTIONS = ['center', 'start', 'end', 'center-even', 'center-odd'];
    const BREAKPOINT_MODE_OPTIONS = ['viewport', 'carousel'];
    const DIR_OPTIONS = [
        'ltr',
        'left-to-right',
        'rtl',
        'right-to-left',
        'ttb',
        'top-to-bottom',
        'btt',
        'bottom-to-top',
    ];
    const I18N_DEFAULT_CONFIG = {
        ariaNextSlide: 'Navigate to next slide',
        ariaPreviousSlide: 'Navigate to previous slide',
        ariaNavigateToSlide: 'Navigate to slide {slideNumber}',
        ariaGallery: 'Gallery',
        itemXofY: 'Item {currentSlide} of {slidesCount}',
        iconArrowUp: 'Arrow pointing upwards',
        iconArrowDown: 'Arrow pointing downwards',
        iconArrowRight: 'Arrow pointing to the right',
        iconArrowLeft: 'Arrow pointing to the left',
    };
    const DEFAULT_CONFIG = {
        enabled: true,
        itemsToShow: 1,
        itemsToScroll: 1,
        modelValue: 0,
        transition: 300,
        autoplay: 0,
        gap: 0,
        height: 'auto',
        wrapAround: false,
        pauseAutoplayOnHover: false,
        mouseDrag: true,
        touchDrag: true,
        snapAlign: SNAP_ALIGN_OPTIONS[0],
        dir: DIR_OPTIONS[0],
        breakpointMode: BREAKPOINT_MODE_OPTIONS[0],
        breakpoints: undefined,
        i18n: I18N_DEFAULT_CONFIG,
    };

    const carouselProps = {
        // enable/disable the carousel component
        enabled: {
            default: DEFAULT_CONFIG.enabled,
            type: Boolean,
        },
        // count of items to showed per view
        itemsToShow: {
            default: DEFAULT_CONFIG.itemsToShow,
            type: Number,
        },
        // count of items to be scrolled
        itemsToScroll: {
            default: DEFAULT_CONFIG.itemsToScroll,
            type: Number,
        },
        // control infinite scrolling mode
        wrapAround: {
            default: DEFAULT_CONFIG.wrapAround,
            type: Boolean,
        },
        // control the gap between slides
        gap: {
            default: DEFAULT_CONFIG.gap,
            type: Number,
        },
        // control the gap between slides
        height: {
            default: DEFAULT_CONFIG.height,
            type: [Number, String],
        },
        // control snap position alignment
        snapAlign: {
            default: DEFAULT_CONFIG.snapAlign,
            validator(value) {
                return SNAP_ALIGN_OPTIONS.includes(value);
            },
        },
        // sliding transition time in ms
        transition: {
            default: DEFAULT_CONFIG.transition,
            type: Number,
        },
        // controls the breakpoint mode relative to the carousel container or the viewport
        breakpointMode: {
            default: DEFAULT_CONFIG.breakpointMode,
            validator(value) {
                return BREAKPOINT_MODE_OPTIONS.includes(value);
            },
        },
        // an object to store breakpoints
        breakpoints: {
            default: DEFAULT_CONFIG.breakpoints,
            type: Object,
        },
        // time to auto advance slides in ms
        autoplay: {
            default: DEFAULT_CONFIG.autoplay,
            type: Number,
        },
        // pause autoplay when mouse hover over the carousel
        pauseAutoplayOnHover: {
            default: DEFAULT_CONFIG.pauseAutoplayOnHover,
            type: Boolean,
        },
        // slide number number of initial slide
        modelValue: {
            default: undefined,
            type: Number,
        },
        // toggle mouse dragging.
        mouseDrag: {
            default: DEFAULT_CONFIG.mouseDrag,
            type: Boolean,
        },
        // toggle mouse dragging.
        touchDrag: {
            default: DEFAULT_CONFIG.touchDrag,
            type: Boolean,
        },
        // control snap position alignment
        dir: {
            default: DEFAULT_CONFIG.dir,
            validator(value) {
                // The value must match one of these strings
                return DIR_OPTIONS.includes(value);
            },
        },
        // aria-labels and additional text labels
        i18n: {
            default: DEFAULT_CONFIG.i18n,
            type: Object,
        },
    };

    /**
     * Determines the maximum slide index based on the configuration.
     *
     * @param {Args} args - The carousel configuration and slide count.
     * @returns {number} The maximum slide index.
     */
    function getMaxSlideIndex({ config, slidesCount }) {
        var _a;
        const { snapAlign = 'N/A', wrapAround, itemsToShow = 1 } = config;
        // If wrapAround is enabled, the max index is the last slide
        if (wrapAround) {
            return Math.max(slidesCount - 1, 0);
        }
        // Map snapAlign values to calculation logic
        const snapAlignCalculations = {
            start: Math.ceil(slidesCount - itemsToShow),
            end: Math.ceil(slidesCount - 1),
            center: slidesCount - Math.ceil((itemsToShow - 0.5) / 2),
            'center-odd': slidesCount - Math.ceil((itemsToShow - 0.5) / 2),
            'center-even': slidesCount - Math.ceil(itemsToShow / 2),
        };
        // Compute the max index based on snapAlign, or default to 0
        const calculateMaxIndex = (_a = snapAlignCalculations[snapAlign]) !== null && _a !== void 0 ? _a : 0;
        // Return the result ensuring it's non-negative
        return Math.max(calculateMaxIndex, 0);
    }

    /**
     * Determines the minimum slide index based on the configuration.
     *
     * @param {Args} args - The carousel configuration and slide count.
     * @returns {number} The minimum slide index.
     */
    function getMinSlideIndex({ config, slidesCount }) {
        var _a;
        const { snapAlign = 'N/A', wrapAround, itemsToShow = 1 } = config;
        // If wrapAround is enabled or itemsToShow exceeds slidesCount, the minimum index is always 0
        if (wrapAround || itemsToShow > slidesCount) {
            return 0;
        }
        // Map of snapAlign to offset calculations
        const snapAlignCalculations = {
            start: 0,
            end: Math.floor(itemsToShow - 1),
            center: Math.floor((itemsToShow - 1) / 2),
            'center-odd': Math.floor((itemsToShow - 1) / 2),
            'center-even': Math.floor((itemsToShow - 2) / 2),
        };
        // Return the calculated offset or default to 0 for invalid snapAlign values
        return (_a = snapAlignCalculations[snapAlign]) !== null && _a !== void 0 ? _a : 0;
    }

    function getNumberInRange({ val, max, min }) {
        if (max < min) {
            return val;
        }
        return Math.min(Math.max(val, min), max);
    }

    const calculateOffset = (snapAlign, itemsToShow) => {
        var _a;
        const offsetMap = {
            start: 0,
            center: (itemsToShow - 1) / 2,
            'center-odd': (itemsToShow - 1) / 2,
            'center-even': (itemsToShow - 2) / 2,
            end: itemsToShow - 1,
        };
        return (_a = offsetMap[snapAlign]) !== null && _a !== void 0 ? _a : 0; // Fallback to 0 for unknown snapAlign
    };
    function getScrolledIndex({ config, currentSlide, slidesCount }) {
        const { snapAlign = 'N/A', wrapAround, itemsToShow = 1 } = config;
        // Calculate the offset based on snapAlign
        const offset = calculateOffset(snapAlign, itemsToShow);
        // Compute the index with or without wrapAround
        if (!wrapAround) {
            return getNumberInRange({
                val: currentSlide - offset,
                max: slidesCount - itemsToShow,
                min: 0,
            });
        }
        return currentSlide - offset;
    }

    function getSlidesVNodes(vNode) {
        if (!vNode)
            return [];
        return vNode.reduce((acc, node) => {
            var _a;
            if (node.type === vue.Fragment) {
                return [...acc, ...getSlidesVNodes(node.children)];
            }
            if (((_a = node.type) === null || _a === void 0 ? void 0 : _a.name) === 'CarouselSlide') {
                return [...acc, node];
            }
            return acc;
        }, []);
    }

    function mapNumberToRange({ val, max, min = 0 }) {
        const mod = max - min + 1;
        return ((val - min) % mod + mod) % mod + min;
    }

    /**
     * return a throttle version of the function
     * Throttling
     *
     */
    // eslint-disable-next-line no-unused-vars
    function throttle(fn) {
        let isRunning = false;
        return function (...args) {
            if (!isRunning) {
                isRunning = true;
                requestAnimationFrame(() => {
                    fn.apply(this, args);
                    isRunning = false;
                });
            }
        };
    }

    /**
     * return a debounced version of the function
     * @param fn
     * @param delay
     */
    // eslint-disable-next-line no-unused-vars
    function debounce(fn, delay) {
        let timerId;
        return function (...args) {
            if (timerId) {
                clearTimeout(timerId);
            }
            timerId = setTimeout(() => {
                fn(...args);
                timerId = null;
            }, delay);
        };
    }

    function i18nFormatter(string = '', values = {}) {
        return Object.entries(values).reduce((acc, [key, value]) => acc.replace(`{${key}}`, String(value)), string);
    }

    var ARIAComponent = vue.defineComponent({
        name: 'ARIA',
        setup() {
            const config = vue.inject('config', vue.reactive(Object.assign({}, DEFAULT_CONFIG)));
            const currentSlide = vue.inject('currentSlide', vue.ref(0));
            const slidesCount = vue.inject('slidesCount', vue.ref(0));
            return () => vue.h('div', {
                class: ['carousel__liveregion', 'carousel__sr-only'],
                'aria-live': 'polite',
                'aria-atomic': 'true',
            }, i18nFormatter(config.i18n['itemXofY'], {
                currentSlide: currentSlide.value + 1,
                slidesCount: slidesCount.value,
            }));
        },
    });

    var Carousel = vue.defineComponent({
        name: 'Carousel',
        props: carouselProps,
        emits: [
            'init',
            'drag',
            'slide-start',
            'loop',
            'update:modelValue',
            'slide-end',
            'before-init',
        ],
        setup(props, { slots, emit, expose }) {
            var _a;
            const root = vue.ref(null);
            const viewport = vue.ref(null);
            const slides = vue.ref([]);
            const slideSize = vue.ref(0);
            const slidesCount = vue.ref(0);
            const fallbackConfig = vue.computed(() => (Object.assign(Object.assign(Object.assign({}, DEFAULT_CONFIG), props), { i18n: Object.assign(Object.assign({}, DEFAULT_CONFIG.i18n), props.i18n), breakpoints: undefined })));
            // current active config
            const config = vue.reactive(Object.assign({}, fallbackConfig.value));
            // slides
            const currentSlideIndex = vue.ref((_a = props.modelValue) !== null && _a !== void 0 ? _a : 0);
            const prevSlideIndex = vue.ref(0);
            const middleSlideIndex = vue.ref(0);
            const maxSlideIndex = vue.ref(0);
            const minSlideIndex = vue.ref(0);
            let autoplayTimer = null;
            let transitionTimer = null;
            let resizeObserver = null;
            const effectiveSlideSize = vue.computed(() => slideSize.value + config.gap);
            const normalizeDir = vue.computed(() => {
                const dir = config.dir || 'lrt';
                const dirMap = {
                    'left-to-right': 'ltr',
                    'right-to-left': 'rtl',
                    'top-to-bottom': 'ttb',
                    'bottom-to-top': 'btt',
                };
                return dirMap[dir] || dir;
            });
            const isVertical = vue.computed(() => ['ttb', 'btt'].includes(normalizeDir.value));
            vue.provide('config', config);
            vue.provide('slidesCount', slidesCount);
            vue.provide('currentSlide', currentSlideIndex);
            vue.provide('maxSlide', maxSlideIndex);
            vue.provide('minSlide', minSlideIndex);
            vue.provide('slideSize', slideSize);
            vue.provide('isVertical', isVertical);
            vue.provide('normalizeDir', normalizeDir);
            function updateBreakpointsConfig() {
                var _a;
                // Determine the width source based on the 'breakpointMode' config
                const widthSource = (config.breakpointMode === 'carousel'
                    ? (_a = root.value) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width
                    : window.innerWidth) || 0;
                const breakpointsArray = Object.keys(props.breakpoints || {})
                    .map((key) => Number(key))
                    .sort((a, b) => +b - +a);
                let newConfig = Object.assign({}, fallbackConfig.value);
                breakpointsArray.some((breakpoint) => {
                    var _a;
                    if (widthSource >= breakpoint) {
                        newConfig = Object.assign(Object.assign({}, newConfig), (_a = props.breakpoints) === null || _a === void 0 ? void 0 : _a[breakpoint]);
                        return true;
                    }
                    return false;
                });
                Object.assign(config, newConfig);
            }
            const handleResize = debounce(() => {
                updateBreakpointsConfig();
                updateSlidesData();
                updateSlideSize();
            }, 16);
            /**
             * Setup functions
             */
            function updateSlideSize() {
                if (!viewport.value)
                    return;
                const rect = viewport.value.getBoundingClientRect();
                // Calculate the total gap space
                const totalGap = (config.itemsToShow - 1) * config.gap;
                // Calculate size based on orientation
                if (isVertical.value) {
                    slideSize.value = (rect.height - totalGap) / config.itemsToShow;
                }
                else {
                    slideSize.value = (rect.width - totalGap) / config.itemsToShow;
                }
            }
            function updateSlidesData() {
                if (slidesCount.value <= 0)
                    return;
                middleSlideIndex.value = Math.ceil((slidesCount.value - 1) / 2);
                maxSlideIndex.value = getMaxSlideIndex({ config, slidesCount: slidesCount.value });
                minSlideIndex.value = getMinSlideIndex({ config, slidesCount: slidesCount.value });
                if (!config.wrapAround) {
                    currentSlideIndex.value = getNumberInRange({
                        val: currentSlideIndex.value,
                        max: maxSlideIndex.value,
                        min: minSlideIndex.value,
                    });
                }
            }
            vue.onMounted(() => {
                vue.nextTick(() => updateSlideSize());
                // Overcome some edge cases
                setTimeout(() => updateSlideSize(), 1000);
                updateBreakpointsConfig();
                initAutoplay();
                window.addEventListener('resize', handleResize, { passive: true });
                resizeObserver = new ResizeObserver(handleResize);
                if (root.value) {
                    resizeObserver.observe(root.value);
                }
                emit('init');
            });
            vue.onUnmounted(() => {
                if (transitionTimer) {
                    clearTimeout(transitionTimer);
                }
                if (autoplayTimer) {
                    clearInterval(autoplayTimer);
                }
                if (resizeObserver && root.value) {
                    resizeObserver.unobserve(root.value);
                    resizeObserver = null;
                }
                window.removeEventListener('resize', handleResize, {
                    passive: true,
                });
            });
            /**
             * Carousel Event listeners
             */
            let isTouch = false;
            const startPosition = { x: 0, y: 0 };
            const dragged = vue.reactive({ x: 0, y: 0 });
            const isHover = vue.ref(false);
            const isDragging = vue.ref(false);
            const handleMouseEnter = () => {
                isHover.value = true;
            };
            const handleMouseLeave = () => {
                isHover.value = false;
            };
            function handleDragStart(event) {
                // Prevent drag initiation on input elements or if already sliding
                const targetTagName = event.target.tagName;
                if (['INPUT', 'TEXTAREA', 'SELECT'].includes(targetTagName) || isSliding.value) {
                    return;
                }
                // Detect if the event is a touchstart or mousedown event
                isTouch = event.type === 'touchstart';
                // For mouse events, prevent default to avoid text selection
                if (!isTouch) {
                    event.preventDefault();
                    if (event.button !== 0) {
                        // Ignore non-left-click mouse events
                        return;
                    }
                }
                // Initialize start positions for the drag
                startPosition.x = isTouch ? event.touches[0].clientX : event.clientX;
                startPosition.y = isTouch ? event.touches[0].clientY : event.clientY;
                // Attach event listeners for dragging and drag end
                const moveEvent = isTouch ? 'touchmove' : 'mousemove';
                const endEvent = isTouch ? 'touchend' : 'mouseup';
                document.addEventListener(moveEvent, handleDragging, { passive: false });
                document.addEventListener(endEvent, handleDragEnd, { passive: true });
            }
            const handleDragging = throttle((event) => {
                isDragging.value = true;
                // Get the current position based on the interaction type (touch or mouse)
                const currentX = isTouch ? event.touches[0].clientX : event.clientX;
                const currentY = isTouch ? event.touches[0].clientY : event.clientY;
                // Calculate deltas for X and Y axes
                const deltaX = currentX - startPosition.x;
                const deltaY = currentY - startPosition.y;
                // Update dragged state reactively
                dragged.x = deltaX;
                dragged.y = deltaY;
                // Emit a drag event for further customization if needed
                emit('drag', { deltaX, deltaY });
            });
            function handleDragEnd() {
                // Determine the active axis and direction multiplier
                const dragAxis = isVertical.value ? 'y' : 'x';
                const directionMultiplier = ['rtl', 'btt'].includes(normalizeDir.value) ? -1 : 1;
                // Calculate dragged slides with a tolerance to account for incomplete drags
                const tolerance = Math.sign(dragged[dragAxis]) * 0.4; // Smooth out small drags
                const draggedSlides = Math.round(dragged[dragAxis] / effectiveSlideSize.value + tolerance) *
                    directionMultiplier;
                // Prevent accidental clicks when there is a slide drag
                if (draggedSlides && !isTouch) {
                    const preventClick = (e) => {
                        e.preventDefault();
                        window.removeEventListener('click', preventClick);
                    };
                    window.addEventListener('click', preventClick);
                }
                // Slide to the appropriate slide index
                const targetSlideIndex = currentSlideIndex.value - draggedSlides;
                slideTo(targetSlideIndex);
                // Reset drag state
                dragged.x = 0;
                dragged.y = 0;
                isDragging.value = false;
                const moveEvent = isTouch ? 'touchmove' : 'mousemove';
                const endEvent = isTouch ? 'touchend' : 'mouseup';
                document.removeEventListener(moveEvent, handleDragging);
                document.removeEventListener(endEvent, handleDragEnd);
            }
            /**
             * Autoplay
             */
            function initAutoplay() {
                if (!config.autoplay || config.autoplay <= 0) {
                    return;
                }
                autoplayTimer = setInterval(() => {
                    if (config.pauseAutoplayOnHover && isHover.value) {
                        return;
                    }
                    next();
                }, config.autoplay);
            }
            function resetAutoplay() {
                if (autoplayTimer) {
                    clearInterval(autoplayTimer);
                    autoplayTimer = null;
                }
                initAutoplay();
            }
            /**
             * Navigation function
             */
            const isSliding = vue.ref(false);
            function slideTo(slideIndex) {
                const currentVal = config.wrapAround
                    ? slideIndex
                    : getNumberInRange({
                        val: slideIndex,
                        max: maxSlideIndex.value,
                        min: minSlideIndex.value,
                    });
                if (currentSlideIndex.value === currentVal || isSliding.value) {
                    return;
                }
                emit('slide-start', {
                    slidingToIndex: slideIndex,
                    currentSlideIndex: currentSlideIndex.value,
                    prevSlideIndex: prevSlideIndex.value,
                    slidesCount: slidesCount.value,
                });
                isSliding.value = true;
                prevSlideIndex.value = currentSlideIndex.value;
                currentSlideIndex.value = currentVal;
                transitionTimer = setTimeout(() => {
                    if (config.wrapAround) {
                        const mappedNumber = mapNumberToRange({
                            val: currentVal,
                            max: maxSlideIndex.value,
                            min: 0,
                        });
                        if (mappedNumber !== currentSlideIndex.value) {
                            currentSlideIndex.value = mappedNumber;
                            emit('loop', {
                                currentSlideIndex: currentSlideIndex.value,
                                slidingToIndex: slideIndex,
                            });
                        }
                    }
                    emit('update:modelValue', currentSlideIndex.value);
                    emit('slide-end', {
                        currentSlideIndex: currentSlideIndex.value,
                        prevSlideIndex: prevSlideIndex.value,
                        slidesCount: slidesCount.value,
                    });
                    isSliding.value = false;
                    resetAutoplay();
                }, config.transition);
            }
            function next() {
                slideTo(currentSlideIndex.value + config.itemsToScroll);
            }
            function prev() {
                slideTo(currentSlideIndex.value - config.itemsToScroll);
            }
            const nav = { slideTo, next, prev };
            vue.provide('nav', nav);
            vue.provide('isSliding', isSliding);
            function restartCarousel() {
                updateBreakpointsConfig();
                updateSlidesData();
                updateSlideSize();
                resetAutoplay();
            }
            // Update the carousel on props change
            vue.watch(() => (Object.assign({}, props)), restartCarousel, { deep: true });
            // Handle changing v-model value
            vue.watch(() => props['modelValue'], (val) => {
                if (val === currentSlideIndex.value) {
                    return;
                }
                slideTo(Number(val));
            });
            // Handel when slides added/removed
            vue.watch(slidesCount, updateSlidesData);
            // Init carousel
            emit('before-init');
            const data = {
                config,
                slidesCount,
                slideSize,
                currentSlide: currentSlideIndex,
                maxSlide: maxSlideIndex,
                minSlide: minSlideIndex,
                middleSlide: middleSlideIndex,
            };
            expose({
                updateBreakpointsConfig,
                updateSlidesData,
                updateSlideSize,
                restartCarousel,
                slideTo,
                next,
                prev,
                nav,
                data,
            });
            /**
             * Track style
             */
            const trackTransform = vue.computed(() => {
                // Calculate the scrolled index with wrapping offset if applicable
                const scrolledIndex = getScrolledIndex({
                    config,
                    currentSlide: currentSlideIndex.value,
                    slidesCount: slidesCount.value,
                });
                const cloneOffset = config.wrapAround ? slidesCount.value : 0;
                // Determine direction multiplier for orientation
                const isReverseDirection = ['rtl', 'btt'].includes(normalizeDir.value);
                const directionMultiplier = isReverseDirection ? -1 : 1;
                // Calculate the total offset for slide transformation
                const totalOffset = (scrolledIndex + cloneOffset) * effectiveSlideSize.value * directionMultiplier;
                // Include user drag interaction offset
                const dragOffset = isVertical.value ? dragged.y : dragged.x;
                // Generate the appropriate CSS transformation
                const translateAxis = isVertical.value ? 'Y' : 'X';
                return `translate${translateAxis}(${dragOffset - totalOffset}px)`;
            });
            const slotSlides = slots.default || slots.slides;
            const slotAddons = slots.addons;
            const slotsProps = vue.reactive(data);
            return () => {
                if (!config.enabled) {
                    return vue.h('section', {
                        ref: root,
                        class: ['carousel', 'is-disabled'],
                    }, slotSlides === null || slotSlides === void 0 ? void 0 : slotSlides());
                }
                const slidesElements = getSlidesVNodes(slotSlides === null || slotSlides === void 0 ? void 0 : slotSlides(slotsProps));
                const addonsElements = (slotAddons === null || slotAddons === void 0 ? void 0 : slotAddons(slotsProps)) || [];
                slidesElements.forEach((el, index) => {
                    if (el.props) {
                        el.props.index = index;
                    }
                    else {
                        el.props = { index };
                    }
                });
                let output = slidesElements;
                if (config.wrapAround) {
                    const slidesBefore = slidesElements.map((el, index) => vue.cloneVNode(el, {
                        index: -slidesElements.length + index,
                        isClone: true,
                        key: `clone-before-${index}`,
                    }));
                    const slidesAfter = slidesElements.map((el, index) => vue.cloneVNode(el, {
                        index: slidesElements.length + index,
                        isClone: true,
                        key: `clone-after-${index}`,
                    }));
                    output = [...slidesBefore, ...slidesElements, ...slidesAfter];
                }
                slides.value = slidesElements;
                slidesCount.value = Math.max(slidesElements.length, 1);
                const trackEl = vue.h('ol', {
                    class: 'carousel__track',
                    style: {
                        transform: trackTransform.value,
                        transition: `${isSliding.value ? config.transition : 0}ms`,
                        gap: `${config.gap}px`,
                    },
                    onMousedownCapture: config.mouseDrag ? handleDragStart : null,
                    onTouchstartPassiveCapture: config.touchDrag ? handleDragStart : null,
                }, output);
                const viewPortEl = vue.h('div', { class: 'carousel__viewport', ref: viewport }, trackEl);
                return vue.h('section', {
                    ref: root,
                    class: [
                        'carousel',
                        `is-${normalizeDir.value}`,
                        {
                            'is-vertical': isVertical.value,
                            'is-sliding': isSliding.value,
                            'is-dragging': isDragging.value,
                            'is-hover': isHover.value,
                        },
                    ],
                    style: {
                        '--vc-trk-height': `${typeof config.height === 'number' ? `${config.height}px` : config.height}`,
                    },
                    dir: normalizeDir.value,
                    'aria-label': config.i18n['ariaGallery'],
                    tabindex: '0',
                    onMouseenter: handleMouseEnter,
                    onMouseleave: handleMouseLeave,
                }, [viewPortEl, addonsElements, vue.h(ARIAComponent)]);
            };
        },
    });

    var IconName;
    (function (IconName) {
        IconName["arrowUp"] = "arrowUp";
        IconName["arrowDown"] = "arrowDown";
        IconName["arrowRight"] = "arrowRight";
        IconName["arrowLeft"] = "arrowLeft";
    })(IconName || (IconName = {}));
    const icons = {
        arrowUp: 'M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z',
        arrowDown: 'M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z',
        arrowRight: 'M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z',
        arrowLeft: 'M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z',
    };

    function isIconName(candidate) {
        return candidate in IconName;
    }
    const Icon = (props) => {
        const config = vue.inject('config', vue.reactive(Object.assign({}, DEFAULT_CONFIG)));
        const iconName = String(props.name);
        const iconI18n = `icon${iconName.charAt(0).toUpperCase() + iconName.slice(1)}`;
        if (!iconName || typeof iconName !== 'string' || !isIconName(iconName)) {
            return;
        }
        const path = icons[iconName];
        const pathEl = vue.h('path', { d: path });
        const iconTitle = config.i18n[iconI18n] || props.title || iconName;
        const titleEl = vue.h('title', iconTitle);
        return vue.h('svg', {
            class: 'carousel__icon',
            viewBox: '0 0 24 24',
            role: 'img',
            'aria-label': iconTitle,
        }, [titleEl, pathEl]);
    };
    Icon.props = { name: String, title: String };

    const Navigation = (props, { slots, attrs }) => {
        const { next: slotNext, prev: slotPrev } = slots || {};
        const config = vue.inject('config', vue.reactive(Object.assign({}, DEFAULT_CONFIG)));
        const maxSlide = vue.inject('maxSlide', vue.ref(1));
        const minSlide = vue.inject('minSlide', vue.ref(1));
        const normalizeDir = vue.inject('normalizeDir', vue.ref('ltr'));
        const currentSlide = vue.inject('currentSlide', vue.ref(1));
        const nav = vue.inject('nav', {});
        const { wrapAround, i18n } = config;
        const getPrevIcon = () => {
            const directionIcons = {
                ltr: 'arrowLeft',
                rtl: 'arrowRight',
                ttb: 'arrowUp',
                btt: 'arrowDown',
            };
            return directionIcons[normalizeDir.value];
        };
        const getNextIcon = () => {
            const directionIcons = {
                ltr: 'arrowRight',
                rtl: 'arrowLeft',
                ttb: 'arrowDown',
                btt: 'arrowUp',
            };
            return directionIcons[normalizeDir.value];
        };
        const prevButton = vue.h('button', {
            type: 'button',
            class: [
                'carousel__prev',
                !wrapAround && currentSlide.value <= minSlide.value && 'carousel__prev--disabled',
                attrs === null || attrs === void 0 ? void 0 : attrs.class,
            ],
            'aria-label': i18n['ariaPreviousSlide'],
            title: i18n['ariaPreviousSlide'],
            onClick: nav.prev,
        }, (slotPrev === null || slotPrev === void 0 ? void 0 : slotPrev()) || vue.h(Icon, { name: getPrevIcon() }));
        const nextButton = vue.h('button', {
            type: 'button',
            class: [
                'carousel__next',
                !wrapAround && currentSlide.value >= maxSlide.value && 'carousel__next--disabled',
                attrs === null || attrs === void 0 ? void 0 : attrs.class,
            ],
            'aria-label': i18n['ariaNextSlide'],
            title: i18n['ariaNextSlide'],
            onClick: nav.next,
        }, (slotNext === null || slotNext === void 0 ? void 0 : slotNext()) || vue.h(Icon, { name: getNextIcon() }));
        return [prevButton, nextButton];
    };

    const Pagination = () => {
        const config = vue.inject('config', vue.reactive(Object.assign({}, DEFAULT_CONFIG)));
        const maxSlide = vue.inject('maxSlide', vue.ref(1));
        const minSlide = vue.inject('minSlide', vue.ref(1));
        const currentSlide = vue.inject('currentSlide', vue.ref(1));
        const nav = vue.inject('nav', {});
        const isActive = (slide) => mapNumberToRange({
            val: currentSlide.value,
            max: maxSlide.value,
            min: 0,
        }) === slide;
        const children = [];
        for (let slide = minSlide.value; slide < maxSlide.value + 1; slide++) {
            const buttonLabel = i18nFormatter(config.i18n['ariaNavigateToSlide'], {
                slideNumber: slide + 1,
            });
            const button = vue.h('button', {
                type: 'button',
                class: {
                    'carousel__pagination-button': true,
                    'carousel__pagination-button--active': isActive(slide),
                },
                'aria-label': buttonLabel,
                title: buttonLabel,
                onClick: () => nav.slideTo(slide),
            });
            const item = vue.h('li', { class: 'carousel__pagination-item', key: slide }, button);
            children.push(item);
        }
        return vue.h('ol', { class: 'carousel__pagination' }, children);
    };

    var Slide = vue.defineComponent({
        name: 'CarouselSlide',
        props: {
            index: {
                type: Number,
                default: 1,
            },
            isClone: {
                type: Boolean,
                default: false,
            },
        },
        setup(props, { slots }) {
            const config = vue.inject('config', vue.reactive(Object.assign({}, DEFAULT_CONFIG)));
            const currentSlide = vue.inject('currentSlide', vue.ref(0));
            const slidesToScroll = vue.inject('slidesToScroll', vue.ref(0));
            const isSliding = vue.inject('isSliding', vue.ref(false));
            const isVertical = vue.inject('isVertical', vue.ref(false));
            const slideSize = vue.inject('slideSize', vue.ref(0));
            const isActive = vue.computed(() => props.index === currentSlide.value);
            const isPrev = vue.computed(() => props.index === currentSlide.value - 1);
            const isNext = vue.computed(() => props.index === currentSlide.value + 1);
            const isVisible = vue.computed(() => {
                const min = Math.floor(slidesToScroll.value);
                const max = Math.ceil(slidesToScroll.value + config.itemsToShow - 1);
                return props.index >= min && props.index <= max;
            });
            const slideStyle = vue.computed(() => {
                const dimension = config.gap
                    ? `${slideSize.value}px`
                    : `${100 / config.itemsToShow}%`;
                return isVertical.value
                    ? { height: dimension, width: '' }
                    : { width: dimension, height: '' };
            });
            return () => {
                var _a, _b;
                if (!config.enabled) {
                    return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
                }
                return vue.h('li', {
                    style: slideStyle.value,
                    class: {
                        carousel__slide: true,
                        'carousel__slide--clone': props.isClone,
                        'carousel__slide--visible': isVisible.value,
                        'carousel__slide--active': isActive.value,
                        'carousel__slide--prev': isPrev.value,
                        'carousel__slide--next': isNext.value,
                        'carousel__slide--sliding': isSliding.value,
                    },
                    'aria-hidden': !isVisible.value,
                }, (_b = slots.default) === null || _b === void 0 ? void 0 : _b.call(slots, {
                    isActive: isActive.value,
                    isClone: props.isClone,
                    isPrev: isPrev.value,
                    isNext: isNext.value,
                    isSliding: isSliding.value,
                    isVisible: isVisible.value,
                }));
            };
        },
    });

    exports.Carousel = Carousel;
    exports.Icon = Icon;
    exports.Navigation = Navigation;
    exports.Pagination = Pagination;
    exports.Slide = Slide;

}));
