Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

vue3-carousel

Package Overview
Dependencies
Maintainers
0
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vue3-carousel - npm Package Compare versions

Comparing version 0.8.1 to 0.9.0

eslint.config.js

5

CHANGELOG.md

@@ -5,2 +5,7 @@ # Changelog

## [0.8.1](https://github.com/ismail9k/vue3-carousel/releases/tag/v0.8.1) - 2024-11-26
- fix: cannot set properties on null (setting 'index') issue by @ismail9k in #427
- Better support for typescript and add CarouselExposed type by @ismail9k in #429
## [0.8.0](https://github.com/ismail9k/vue3-carousel/releases/tag/v0.8.0) - 2024-11-24

@@ -7,0 +12,0 @@

1228

dist/carousel.d.ts

@@ -1,8 +0,47 @@

import * as vue from 'vue';
import { Ref, VNode } from 'vue';
import { Reactive, Ref, ShallowReactive, ComponentInternalInstance, ComputedRef } from 'vue';
interface Data$1 {
[key: string]: unknown
// Use a symbol for inject provide to avoid any kind of collision with another lib
// https://vuejs.org/guide/components/provide-inject#working-with-symbol-keys
declare const injectCarousel = Symbol('carousel') as InjectionKey<
InjectedCarousel | undefined
>
type Breakpoints = {
[key: number]: Partial<
Omit<CarouselConfig, 'breakpoints' | 'modelValue' | 'breakpointMode'>
>
}
type SnapAlign = (typeof SNAP_ALIGN_OPTIONS)[number]
type Dir = (typeof DIR_OPTIONS)[number]
type BreakpointMode = (typeof BREAKPOINT_MODE_OPTIONS)[number]
type NormalizedDir = (typeof NORMALIZED_DIR_OPTIONS)[number]
type NonNormalizedDir = keyof typeof DIR_MAP
type I18nKeys = keyof typeof I18N_DEFAULT_CONFIG
interface CarouselConfig {
enabled: boolean
itemsToShow: number
itemsToScroll: number
modelValue?: number
transition?: number
gap: number
autoplay?: number
snapAlign: SnapAlign
wrapAround?: boolean
pauseAutoplayOnHover?: boolean
mouseDrag?: boolean
touchDrag?: boolean
dir?: Dir
breakpointMode?: BreakpointMode
breakpoints?: Breakpoints
height: string | number
i18n: { [key in I18nKeys]?: string }
}
declare const SNAP_ALIGN_OPTIONS = ['center', 'start', 'end', 'center-even', 'center-odd']

@@ -32,37 +71,194 @@ declare const BREAKPOINT_MODE_OPTIONS = ['viewport', 'carousel']

type Breakpoints = { [key: number]: Partial<CarouselConfig> }
declare const DIR_MAP = {
'left-to-right': 'ltr',
'right-to-left': 'rtl',
'top-to-bottom': 'ttb',
'bottom-to-top': 'btt',
} as const
type SnapAlign = (typeof SNAP_ALIGN_OPTIONS)[number]
declare const NORMALIZED_DIR_OPTIONS = Object.values(DIR_MAP)
type Dir = (typeof DIR_OPTIONS)[number]
declare const DEFAULT_CONFIG: CarouselConfig = {
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,
}
type BreakpointMode = (typeof BREAKPOINT_MODE_OPTIONS)[number]
type I18nKeys = keyof typeof I18N_DEFAULT_CONFIG
interface CarouselConfig {
enabled: boolean
itemsToShow: number
itemsToScroll: number
modelValue?: number
transition?: number
gap: number
autoplay?: number
snapAlign: SnapAlign
wrapAround?: boolean
pauseAutoplayOnHover?: boolean
mouseDrag?: boolean
touchDrag?: boolean
dir?: Dir
breakpointMode?: string
breakpoints?: Breakpoints
height: string | number
i18n: { [key in I18nKeys]?: string }
interface SlideProps {
id: string
index: number
isClone?: boolean
}
declare const Slide = defineComponent({
name: 'CarouselSlide',
props: {
isClone: {
type: Boolean,
default: false,
},
id: {
type: String,
default: (props: { isClone?: boolean }) => (props.isClone ? undefined : useId()),
},
index: {
type: Number,
default: 0,
},
},
setup(props: SlideProps, { slots, expose }: SetupContext) {
const carousel = inject(injectCarousel)
provide(injectCarousel, undefined) // Don't provide for nested slides
if (!carousel) {
return null // Don't render, let vue warn about the missing provide
}
const index = ref(props.index)
watch(() => props.index, (i) => index.value = i)
const isActive: ComputedRef<boolean> = computed(
() => index.value === carousel.currentSlide
)
const isPrev: ComputedRef<boolean> = computed(
() => index.value === carousel.currentSlide - 1
)
const isNext: ComputedRef<boolean> = computed(
() => index.value === carousel.currentSlide + 1
)
const isVisible: ComputedRef<boolean> = computed(
() =>
index.value >= Math.floor(carousel.scrolledIndex) &&
index.value < Math.ceil(carousel.scrolledIndex) + carousel.config.itemsToShow
)
const slideStyle = computed(() => {
const dimension =
carousel.config.gap > 0 && carousel.config.itemsToShow > 1
? `calc(${100 / carousel.config.itemsToShow}% - ${
(carousel.config.gap * (carousel.config.itemsToShow - 1)) /
carousel.config.itemsToShow
}px)`
: `${100 / carousel.config.itemsToShow}%`
return carousel.isVertical ? { height: dimension } : { width: dimension }
})
const instance = getCurrentInstance()!
if (!props.isClone) {
carousel.registerSlide(
instance,
(resolvedIndex: number) => (index.value = resolvedIndex)
)
onUnmounted(() => {
carousel.unregisterSlide(instance)
})
} else {
const makeUnfocusable = (node: VNode) => {
;[
...(node?.el
? node.el.querySelectorAll(
'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
)
: []),
]
.filter((el) => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'))
.forEach((el) => el.setAttribute('tabindex', '-1'))
}
// Prevent cloned slides from being focusable
onMounted(() => {
makeUnfocusable(instance.vnode)
})
onUpdated(() => {
makeUnfocusable(instance.vnode)
})
}
expose({
id: props.id,
})
return () => {
if (!carousel.config.enabled) {
return slots.default?.()
}
return 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': carousel.isSliding,
},
onFocusin: () => {
// Prevent the viewport being scrolled by the focus
if (carousel.viewport) {
carousel.viewport.scrollLeft = 0
}
carousel.nav.slideTo(index.value)
},
id: props.isClone ? undefined : props.id,
'aria-hidden': props.isClone || undefined,
},
slots.default?.({
isActive: isActive.value,
isClone: props.isClone,
isPrev: isPrev.value,
isNext: isNext.value,
isSliding: carousel.isSliding,
isVisible: isVisible.value,
})
)
}
},
})
interface CarouselNav {
slideTo: (index: number) => void
next: () => void
prev: () => void
next: (skipTransition?: boolean) => void
prev: (skipTransition?: boolean) => void
}
type InjectedCarousel = Reactive<{
config: CarouselConfig
viewport: Ref<Element | null>
slides: ShallowReactive<Array<ComponentInternalInstance>>
slidesCount: ComputedRef<number>
currentSlide: Ref<number>
scrolledIndex: Ref<number>
maxSlide: ComputedRef<number>
minSlide: ComputedRef<number>
slideSize: Ref<number>
isVertical: ComputedRef<boolean>
normalizedDir: ComputedRef<NormalizedDir>
nav: CarouselNav
isSliding: Ref<boolean>
registerSlide: (
slide: ComponentInternalInstance,
indexCb: (idx: number) => void
) => void
unregisterSlide: (slide: ComponentInternalInstance) => void
}>
interface CarouselData {

@@ -86,112 +282,880 @@ config: CarouselConfig

nav: CarouselNav
data: CarouselData
data: Reactive<CarouselData>
}
declare const _default$1: vue.DefineComponent<{
enabled: boolean;
itemsToShow: number;
itemsToScroll: number;
modelValue?: number | undefined;
transition?: number | undefined;
gap: number;
autoplay?: number | undefined;
snapAlign: SnapAlign;
wrapAround?: boolean | undefined;
pauseAutoplayOnHover?: boolean | undefined;
mouseDrag?: boolean | undefined;
touchDrag?: boolean | undefined;
dir?: Dir | undefined;
breakpointMode?: string | undefined;
breakpoints?: Breakpoints | undefined;
height: string | number;
i18n: { [key in I18nKeys]?: string; };
}, () => VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, "drag" | "init" | "slide-start" | "loop" | "update:modelValue" | "slide-end" | "before-init", vue.PublicProps, Readonly<{
enabled: boolean;
itemsToShow: number;
itemsToScroll: number;
modelValue?: number | undefined;
transition?: number | undefined;
gap: number;
autoplay?: number | undefined;
snapAlign: SnapAlign;
wrapAround?: boolean | undefined;
pauseAutoplayOnHover?: boolean | undefined;
mouseDrag?: boolean | undefined;
touchDrag?: boolean | undefined;
dir?: Dir | undefined;
breakpointMode?: string | undefined;
breakpoints?: Breakpoints | undefined;
height: string | number;
i18n: { [key in I18nKeys]?: string; };
}> & Readonly<{}>, {
snapAlign: string;
enabled: boolean;
itemsToShow: number;
itemsToScroll: number;
modelValue: number;
transition: number;
gap: number;
autoplay: number;
wrapAround: boolean;
pauseAutoplayOnHover: boolean;
mouseDrag: boolean;
touchDrag: boolean;
dir: string;
breakpointMode: string;
breakpoints: Record<string, any>;
height: string | number;
i18n: Record<string, any>;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare const Carousel = defineComponent({
name: 'VueCarousel',
props: carouselProps,
emits: [
'init',
'drag',
'slide-start',
'loop',
'update:modelValue',
'slide-end',
'before-init',
],
setup(props: CarouselConfig, { slots, emit, expose }: SetupContext) {
const root: Ref<Element | null> = ref(null)
const viewport: Ref<Element | null> = ref(null)
const slides = shallowReactive<Array<ComponentInternalInstance>>([])
const slideSize: Ref<number> = ref(0)
const slidesCount = computed(() => slides.length)
interface Data {
[key: string]: unknown;
const fallbackConfig = computed(() => ({
...DEFAULT_CONFIG,
...props,
i18n: { ...DEFAULT_CONFIG.i18n, ...props.i18n },
breakpoints: undefined,
}))
// current active config
const config = reactive<CarouselConfig>({ ...fallbackConfig.value })
// slides
const currentSlideIndex = ref(props.modelValue ?? 0)
const prevSlideIndex = ref(0)
const middleSlideIndex = computed(() => Math.ceil((slidesCount.value - 1) / 2))
const maxSlideIndex = computed(() => {
return getMaxSlideIndex({ config, slidesCount: slidesCount.value })
})
const minSlideIndex = computed(() => {
return getMinSlideIndex({ config, slidesCount: slidesCount.value })
})
let autoplayTimer: ReturnType<typeof setInterval> | null = null
let transitionTimer: ReturnType<typeof setTimeout> | null = null
let resizeObserver: ResizeObserver | null = null
const effectiveSlideSize = computed(() => slideSize.value + config.gap)
const normalizedDir = computed<NormalizedDir>(() => {
const dir = config.dir || 'ltr'
return dir in DIR_MAP ? DIR_MAP[dir as NonNormalizedDir] : (dir as NormalizedDir)
})
const indexCbs: Array<(index: number) => void> = []
const registerSlide: InjectedCarousel['registerSlide'] = (slide, indexCb) => {
indexCb(slides.length)
slides.push(slide)
indexCbs.push(indexCb)
}
const unregisterSlide: InjectedCarousel['unregisterSlide'] = (slide) => {
const found = slides.indexOf(slide)
if (found >= 0) {
slides.splice(found, 1)
indexCbs.splice(found, 1)
// Update indexes after the one that was removed
indexCbs.slice(found).forEach((cb, index) => cb(found + index))
}
}
const isReversed = computed(() => ['rtl', 'btt'].includes(normalizedDir.value))
const isVertical = computed(() => ['ttb', 'btt'].includes(normalizedDir.value))
const clonedSlidesCount = computed(() => Math.ceil(config.itemsToShow) + 1)
function updateBreakpointsConfig(): void {
// Determine the width source based on the 'breakpointMode' config
const widthSource =
(fallbackConfig.value.breakpointMode === 'carousel'
? root.value?.getBoundingClientRect().width
: typeof window !== 'undefined'
? window.innerWidth
: 0) || 0
const breakpointsArray = Object.keys(props.breakpoints || {})
.map((key) => Number(key))
.sort((a, b) => +b - +a)
const newConfig: Partial<CarouselConfig> = {}
breakpointsArray.some((breakpoint) => {
if (widthSource >= breakpoint) {
Object.assign(newConfig, props.breakpoints![breakpoint])
if (newConfig.i18n) {
Object.assign(
newConfig.i18n,
fallbackConfig.value.i18n,
props.breakpoints![breakpoint].i18n
)
}
return true
}
return false
})
Object.assign(config, fallbackConfig.value, newConfig)
}
const handleResize = throttle(() => {
updateBreakpointsConfig()
updateSlidesData()
updateSlideSize()
})
const totalGap = computed(() => (config.itemsToShow - 1) * config.gap)
const transformElements = shallowReactive<Set<HTMLElement>>(new Set())
/**
* Setup functions
*/
function updateSlideSize(): void {
if (!viewport.value) return
let multiplierWidth = 1
transformElements.forEach((el) => {
const transformArr = getTransformValues(el)
if (transformArr.length === 6) {
multiplierWidth *= transformArr[0]
}
})
// Calculate size based on orientation
if (isVertical.value) {
if (config.height !== 'auto') {
const height =
typeof config.height === 'string' && isNaN(parseInt(config.height))
? viewport.value.getBoundingClientRect().height
: parseInt(config.height as string)
slideSize.value = (height - totalGap.value) / config.itemsToShow
}
} else {
const width = viewport.value.getBoundingClientRect().width
slideSize.value = (width / multiplierWidth - totalGap.value) / config.itemsToShow
}
}
function updateSlidesData(): void {
if (!config.wrapAround && slidesCount.value > 0) {
currentSlideIndex.value = getNumberInRange({
val: currentSlideIndex.value,
max: maxSlideIndex.value,
min: minSlideIndex.value,
})
}
}
watchEffect(() => updateSlidesData())
watchEffect(() => {
// Call updateSlideSize when viewport is ready and track deps
updateSlideSize()
})
let animationInterval: number
const setAnimationInterval = (event: AnimationEvent | TransitionEvent) => {
const target = event.target as HTMLElement
if (target) {
transformElements.add(target)
}
if (!animationInterval) {
const stepAnimation = () => {
animationInterval = requestAnimationFrame(() => {
updateSlideSize()
stepAnimation()
})
}
stepAnimation()
}
}
const finishAnimation = (event: AnimationEvent | TransitionEvent) => {
const target = event.target as HTMLElement
if (target) {
transformElements.delete(target)
}
if (animationInterval && transformElements.size === 0) {
cancelAnimationFrame(animationInterval)
updateSlideSize()
}
}
updateBreakpointsConfig()
onMounted((): void => {
if (fallbackConfig.value.breakpointMode === 'carousel') {
updateBreakpointsConfig()
}
initAutoplay()
if (document) {
document.addEventListener('animationstart', setAnimationInterval)
document.addEventListener('animationend', finishAnimation)
}
if (root.value) {
resizeObserver = new ResizeObserver(handleResize)
resizeObserver.observe(root.value)
}
emit('init')
})
onBeforeUnmount(() => {
// Empty the slides before they unregister for better performance
slides.splice(0, slides.length)
indexCbs.splice(0, indexCbs.length)
if (transitionTimer) {
clearTimeout(transitionTimer)
}
if (animationInterval) {
cancelAnimationFrame(animationInterval)
}
if (autoplayTimer) {
clearInterval(autoplayTimer)
}
if (resizeObserver) {
resizeObserver.disconnect()
resizeObserver = null
}
if (document) {
document.removeEventListener('keydown', handleArrowKeys)
document.removeEventListener('animationstart', setAnimationInterval)
document.removeEventListener('animationend', finishAnimation)
}
if (root.value) {
root.value.removeEventListener('transitionend', updateSlideSize)
root.value.removeEventListener('animationiteration', updateSlideSize)
}
})
/**
* Carousel Event listeners
*/
let isTouch = false
const startPosition = { x: 0, y: 0 }
const dragged = reactive({ x: 0, y: 0 })
const isHover = ref(false)
const isDragging = ref(false)
const handleMouseEnter = (): void => {
isHover.value = true
}
const handleMouseLeave = (): void => {
isHover.value = false
}
const handleArrowKeys = throttle((event: KeyboardEvent): void => {
if (event.ctrlKey) return
switch (event.key) {
case 'ArrowLeft':
case 'ArrowUp':
if (isVertical.value === event.key.endsWith('Up')) {
if (isReversed.value) {
nav.next(true)
} else {
nav.prev(true)
}
}
break
case 'ArrowRight':
case 'ArrowDown':
if (isVertical.value === event.key.endsWith('Down')) {
if (isReversed.value) {
nav.prev(true)
} else {
nav.next(true)
}
}
break
}
}, 200)
const handleFocus = (): void => {
document.addEventListener('keydown', handleArrowKeys)
}
const handleBlur = (): void => {
document.removeEventListener('keydown', handleArrowKeys)
}
function handleDragStart(event: MouseEvent | TouchEvent): void {
// Prevent drag initiation on input elements or if already sliding
const targetTagName = (event.target as HTMLElement).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 as MouseEvent).button !== 0) {
// Ignore non-left-click mouse events
return
}
}
// Initialize start positions for the drag
startPosition.x = 'touches' in event ? event.touches[0].clientX : event.clientX
startPosition.y = 'touches' in event ? 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: TouchEvent | MouseEvent): void => {
isDragging.value = true
// Get the current position based on the interaction type (touch or mouse)
const currentX = 'touches' in event ? event.touches[0].clientX : event.clientX
const currentY = 'touches' in event ? 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(): void {
handleDragging.cancel()
// Determine the active axis and direction multiplier
const dragAxis = isVertical.value ? 'y' : 'x'
const directionMultiplier = isReversed.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: MouseEvent) => {
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(): void {
if (!config.autoplay || config.autoplay <= 0) {
return
}
autoplayTimer = setInterval(() => {
if (config.pauseAutoplayOnHover && isHover.value) {
return
}
next()
}, config.autoplay)
}
function stopAutoplay(): void {
if (autoplayTimer) {
clearInterval(autoplayTimer)
autoplayTimer = null
}
}
function resetAutoplay(): void {
stopAutoplay()
initAutoplay()
}
/**
* Navigation function
*/
const isSliding = ref(false)
function slideTo(slideIndex: number, skipTransition = false): void {
const currentVal = config.wrapAround
? slideIndex
: getNumberInRange({
val: slideIndex,
max: maxSlideIndex.value,
min: minSlideIndex.value,
})
if (
currentSlideIndex.value === currentVal ||
(!skipTransition && isSliding.value)
) {
return
}
emit('slide-start', {
slidingToIndex: slideIndex,
currentSlideIndex: currentSlideIndex.value,
prevSlideIndex: prevSlideIndex.value,
slidesCount: slidesCount.value,
})
stopAutoplay()
isSliding.value = true
prevSlideIndex.value = currentSlideIndex.value
const mappedNumber = config.wrapAround
? mapNumberToRange({
val: currentVal,
max: maxSlideIndex.value,
min: 0,
})
: currentVal
currentSlideIndex.value = currentVal
if (mappedNumber !== currentVal) {
modelWatcher.pause()
}
emit('update:modelValue', mappedNumber)
transitionTimer = setTimeout((): void => {
if (config.wrapAround) {
if (mappedNumber !== currentVal) {
modelWatcher.resume()
currentSlideIndex.value = mappedNumber
emit('loop', {
currentSlideIndex: currentSlideIndex.value,
slidingToIndex: slideIndex,
})
}
}
emit('slide-end', {
currentSlideIndex: currentSlideIndex.value,
prevSlideIndex: prevSlideIndex.value,
slidesCount: slidesCount.value,
})
isSliding.value = false
resetAutoplay()
}, config.transition)
}
function next(skipTransition = false): void {
slideTo(currentSlideIndex.value + config.itemsToScroll, skipTransition)
}
function prev(skipTransition = false): void {
slideTo(currentSlideIndex.value - config.itemsToScroll, skipTransition)
}
const nav: CarouselNav = { slideTo, next, prev }
const scrolledIndex = computed(() =>
getScrolledIndex({
config,
currentSlide: currentSlideIndex.value,
slidesCount: slidesCount.value,
})
)
const provided: InjectedCarousel = reactive({
config,
slidesCount,
viewport,
slides,
scrolledIndex,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
slideSize,
isVertical,
normalizedDir,
nav,
isSliding,
registerSlide,
unregisterSlide,
})
provide(injectCarousel, provided)
/** @deprecated provides */
provide('config', config)
provide('slidesCount', slidesCount)
provide('currentSlide', currentSlideIndex)
provide('maxSlide', maxSlideIndex)
provide('minSlide', minSlideIndex)
provide('slideSize', slideSize)
provide('isVertical', isVertical)
provide('normalizeDir', normalizedDir)
provide('nav', nav)
provide('isSliding', isSliding)
function restartCarousel(): void {
updateBreakpointsConfig()
updateSlidesData()
updateSlideSize()
resetAutoplay()
}
// Update the carousel on props change
watch(
() => [fallbackConfig.value, props.breakpoints],
() => updateBreakpointsConfig(),
{ deep: true }
)
watch(
() => props.autoplay,
() => resetAutoplay()
)
// Handle changing v-model value
const modelWatcher = watch(
() => props.modelValue,
(val) => {
if (val === currentSlideIndex.value) {
return
}
slideTo(Number(val), true)
}
)
// Init carousel
emit('before-init')
const data = reactive<CarouselData>({
config,
slidesCount,
slideSize,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
middleSlide: middleSlideIndex,
})
expose<CarouselExposed>({
updateBreakpointsConfig,
updateSlidesData,
updateSlideSize,
restartCarousel,
slideTo,
next,
prev,
nav,
data,
})
const trackHeight = computed(() => {
if (isVertical.value && slideSize.value && config.height === 'auto') {
return `${slideSize.value * config.itemsToShow + totalGap.value}px`
}
return config.height !== 'auto'
? typeof config.height === 'number' ||
parseInt(config.height).toString() === config.height
? `${config.height}px`
: config.height
: undefined
})
/**
* Track style
*/
const trackTransform: ComputedRef<string> = computed(() => {
// Calculate the scrolled index with wrapping offset if applicable
const cloneOffset = config.wrapAround ? clonedSlidesCount.value : 0
// Determine direction multiplier for orientation
const directionMultiplier = isReversed.value ? -1 : 1
// Calculate the total offset for slide transformation
const totalOffset =
(scrolledIndex.value + 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)`
})
return () => {
const slotSlides = slots.default || slots.slides
const slotAddons = slots.addons
let output: VNode[] | Array<Array<VNode>> = slotSlides?.(data) || []
if (!config.enabled || !output.length) {
return h(
'section',
{
ref: root,
class: ['carousel', 'is-disabled'],
},
output
)
}
const addonsElements = slotAddons?.(data) || []
if (config.wrapAround) {
// Ensure scoped CSS tracks properly
const scopeId = output.length > 0 ? output[0].scopeId : null
pushScopeId(scopeId)
const toShow = clonedSlidesCount.value
const slidesBefore = createCloneSlides({ slides, position: 'before', toShow })
const slidesAfter = createCloneSlides({ slides, position: 'after', toShow })
popScopeId()
output = [...slidesBefore, ...output, ...slidesAfter]
}
const trackEl = h(
'ol',
{
class: 'carousel__track',
style: {
transform: trackTransform.value,
'transition-duration': isSliding.value ? `${config.transition}ms` : undefined,
gap: config.gap > 0 ? `${config.gap}px` : undefined,
},
onMousedownCapture: config.mouseDrag ? handleDragStart : null,
onTouchstartPassiveCapture: config.touchDrag ? handleDragStart : null,
},
output
)
const viewPortEl = h('div', { class: 'carousel__viewport', ref: viewport }, trackEl)
return h(
'section',
{
ref: root,
class: [
'carousel',
`is-${normalizedDir.value}`,
{
'is-vertical': isVertical.value,
'is-sliding': isSliding.value,
'is-dragging': isDragging.value,
'is-hover': isHover.value,
},
],
style: {
'--vc-trk-height': trackHeight.value,
},
dir: normalizedDir.value,
'aria-label': config.i18n['ariaGallery'],
tabindex: '0',
onFocus: handleFocus,
onBlur: handleBlur,
onMouseenter: handleMouseEnter,
onMouseleave: handleMouseLeave,
},
[viewPortEl, addonsElements, h(ARIAComponent)]
)
}
},
})
declare enum IconName {
arrowUp = 'arrowUp',
arrowDown = 'arrowDown',
arrowRight = 'arrowRight',
arrowLeft = 'arrowLeft',
}
declare const Icon: {
(props: Data): vue.VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}> | undefined;
props: {
name: StringConstructor;
title: StringConstructor;
};
};
type IconNameValue = `${IconName}`
declare const Navigation: (props: any, { slots, attrs }: any) => vue.VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}>[];
interface IconProps {
title?: string
name?: IconNameValue
}
declare const Pagination: () => VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}>;
declare 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',
}
declare const _default: vue.DefineComponent<vue.ExtractPropTypes<{
index: {
type: NumberConstructor;
default: number;
};
isClone: {
type: BooleanConstructor;
default: boolean;
};
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}> | vue.VNode<vue.RendererNode, vue.RendererElement, {
[key: string]: any;
}>[] | undefined, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
index: {
type: NumberConstructor;
default: number;
};
isClone: {
type: BooleanConstructor;
default: boolean;
};
}>> & Readonly<{}>, {
index: number;
isClone: boolean;
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
declare const Icon = defineComponent({
props: {
name: {
type: String as PropType<IconNameValue>,
required: true,
validator: validateIconName,
},
title: {
type: String,
default: (props: { name: IconNameValue }) =>
props.name ? DEFAULT_CONFIG.i18n[iconI18n(props.name)] : '',
},
},
setup(props: IconProps) {
const carousel = inject(injectCarousel, null)
export { type BreakpointMode, type Breakpoints, _default$1 as Carousel, type CarouselConfig, type CarouselData, type CarouselExposed, type CarouselMethods, type CarouselNav, type Data$1 as Data, type Dir, type I18nKeys, Icon, Navigation, Pagination, _default as Slide, type SnapAlign };
return () => {
const iconName = props.name
if (!iconName || !validateIconName(iconName)) return
const path = icons[iconName]
const pathEl = h('path', { d: path })
const iconTitle: string =
carousel?.config.i18n[iconI18n(iconName)] || props.title || iconName
const titleEl = h('title', iconTitle)
return h(
'svg',
{
class: 'carousel__icon',
viewBox: '0 0 24 24',
role: 'img',
'aria-label': iconTitle,
},
[titleEl, pathEl]
)
}
},
})
interface NavigationProps {
class: any
}
declare const Navigation = defineComponent({
name: 'CarouselNavigation',
setup(props: NavigationProps, { slots }) {
const carousel = inject(injectCarousel)
if (!carousel) {
return null // Don't render, let vue warn about the missing provide
}
const { next: slotNext, prev: slotPrev } = slots
const getPrevIcon = () => {
const directionIcons: Record<NormalizedDir, IconNameValue> = {
ltr: 'arrowLeft',
rtl: 'arrowRight',
ttb: 'arrowUp',
btt: 'arrowDown',
}
return directionIcons[carousel.normalizedDir]
}
const getNextIcon = () => {
const directionIcons: Record<NormalizedDir, IconNameValue> = {
ltr: 'arrowRight',
rtl: 'arrowLeft',
ttb: 'arrowDown',
btt: 'arrowUp',
}
return directionIcons[carousel.normalizedDir]
}
return () => {
const { wrapAround, i18n } = carousel.config
const prevButton = h(
'button',
{
type: 'button',
class: [
'carousel__prev',
!wrapAround &&
carousel.currentSlide <= carousel.minSlide &&
'carousel__prev--disabled',
props.class,
],
'aria-label': i18n['ariaPreviousSlide'],
title: i18n['ariaPreviousSlide'],
onClick: carousel.nav.prev,
},
slotPrev?.() || h(Icon, { name: getPrevIcon() })
)
const nextButton = h(
'button',
{
type: 'button',
class: [
'carousel__next',
!wrapAround &&
carousel.currentSlide >= carousel.maxSlide &&
'carousel__next--disabled',
props.class,
],
'aria-label': i18n['ariaNextSlide'],
title: i18n['ariaNextSlide'],
onClick: carousel.nav.next,
},
slotNext?.() || h(Icon, { name: getNextIcon() })
)
return [prevButton, nextButton]
}
},
})
interface PaginationProps {
disableOnClick?: boolean
key?: string
}
declare const Pagination = defineComponent({
name: 'CarouselPagination',
setup(props: PaginationProps) {
const carousel = inject(injectCarousel)
if (!carousel) {
return null // Don't render, let vue warn about the missing provide
}
const isActive = (slide: number): boolean =>
mapNumberToRange({
val: carousel.currentSlide,
max: carousel.maxSlide,
min: 0,
}) === slide
return () => {
const children: Array<VNode> = []
for (let slide = carousel.minSlide; slide <= carousel.maxSlide; slide++) {
const buttonLabel = i18nFormatter(carousel.config.i18n.ariaNavigateToSlide, {
slideNumber: slide + 1,
})
const active = isActive(slide)
const button = h('button', {
type: 'button',
class: {
'carousel__pagination-button': true,
'carousel__pagination-button--active': active,
},
'aria-label': buttonLabel,
'aria-pressed': active,
'aria-controls': carousel.slides[slide]?.exposed?.id,
title: buttonLabel,
onClick: props.disableOnClick ? undefined : () => carousel.nav.slideTo(slide),
})
const item = h('li', { class: 'carousel__pagination-item', key: slide }, button)
children.push(item)
}
return h('ol', { class: 'carousel__pagination' }, children)
}
},
})
export { BREAKPOINT_MODE_OPTIONS, type BreakpointMode, type Breakpoints, Carousel, type CarouselConfig, type CarouselData, type CarouselExposed, type CarouselMethods, type CarouselNav, DEFAULT_CONFIG, DIR_MAP, DIR_OPTIONS, type Dir, I18N_DEFAULT_CONFIG, type I18nKeys, Icon, IconName, type IconNameValue, type IconProps, type InjectedCarousel, NORMALIZED_DIR_OPTIONS, Navigation, type NavigationProps, type NonNormalizedDir, type NormalizedDir, Pagination, type PaginationProps, SNAP_ALIGN_OPTIONS, Slide, type SlideProps, type SnapAlign, icons, injectCarousel };

1151

dist/carousel.es.js
/**
* Vue 3 Carousel 0.8.1
* Vue 3 Carousel 0.9.0
* (c) 2024
* @license MIT
*/
import { Fragment, defineComponent, inject, reactive, ref, h, computed, provide, onMounted, nextTick, onUnmounted, watch, cloneVNode } from 'vue';
import { defineComponent, useId, inject, provide, ref, watch, computed, getCurrentInstance, onUnmounted, onMounted, onUpdated, h, cloneVNode, shallowReactive, reactive, watchEffect, onBeforeUnmount, pushScopeId, popScopeId } from 'vue';
// Use a symbol for inject provide to avoid any kind of collision with another lib
// https://vuejs.org/guide/components/provide-inject#working-with-symbol-keys
const injectCarousel$1 = Symbol('carousel');
/**
* Determines the minimum slide index based on the configuration.
*
* @param {GetMinSlideIndexArgs} args - The carousel configuration and slide count.
* @returns {number} The minimum slide index.
*/
function getMinSlideIndex({ config, slidesCount }) {
const { snapAlign = 'center', 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 snapAlign values to calculation logic
function snapAlignCalculations() {
switch (snapAlign) {
case 'end':
return Math.floor(itemsToShow - 1);
case 'center':
case 'center-odd':
return Math.floor((itemsToShow - 1) / 2);
case 'center-even':
return Math.floor((itemsToShow - 2) / 2);
}
return 0;
}
// Return the calculated offset or default to 0 for invalid snapAlign values
return Math.max(0, snapAlignCalculations());
}
/**
* 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 }) {
const { snapAlign = 'center', wrapAround, itemsToShow = 1 } = config;
// Map snapAlign values to calculation logic
function snapAlignCalculations() {
// If wrapAround is enabled, fallback to default which is the last slide
switch (wrapAround ? '' : snapAlign) {
case 'start':
return Math.ceil(slidesCount - itemsToShow);
case 'center':
case 'center-odd':
return slidesCount - Math.ceil((itemsToShow - 0.5) / 2);
case 'center-even':
return slidesCount - Math.ceil(itemsToShow / 2);
case 'end':
default:
return Math.ceil(slidesCount - 1);
}
}
// Return the result ensuring it's non-negative
return Math.max(snapAlignCalculations(), 0);
}
function getNumberInRange({ val, max, min }) {
if (max < min) {
return val;
}
return Math.min(Math.max(val, isNaN(min) ? val : min), isNaN(max) ? val : max);
}
function mapNumberToRange({ val, max, min = 0 }) {
const mod = max - min + 1;
return ((((val - min) % mod) + mod) % mod) + min;
}
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 = 'center', 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,
});
}
else {
return mapNumberToRange({
val: currentSlide - offset,
max: slidesCount + itemsToShow,
min: 0 - itemsToShow,
});
}
}
function i18nFormatter(string = '', values = {}) {
return Object.entries(values).reduce((acc, [key, value]) => acc.replace(`{${key}}`, String(value)), string);
}
/**
* Returns a throttled version of the function using requestAnimationFrame.
*
* @param fn - The function to throttle.
* @param ms - The number of milliseconds to wait for the throttled function to be called again
*/
function throttle(fn, ms = 0) {
let isThrottled = false;
let start = 0;
let frameId = null;
function throttled(...args) {
if (isThrottled)
return;
isThrottled = true;
const step = () => {
frameId = requestAnimationFrame((time) => {
const elapsed = time - start;
if (elapsed > ms) {
start = time;
fn(...args);
isThrottled = false;
}
else {
step();
}
});
};
step();
}
throttled.cancel = () => {
if (frameId) {
cancelAnimationFrame(frameId);
frameId = null;
isThrottled = false;
}
};
return throttled;
}
function getTransformValues(el) {
const { transform } = window.getComputedStyle(el);
//add sanity check
return transform
.split(/[(,)]/)
.slice(1, -1)
.map((v) => parseFloat(v));
}
const Slide = defineComponent({
name: 'CarouselSlide',
props: {
isClone: {
type: Boolean,
default: false,
},
id: {
type: String,
default: (props) => (props.isClone ? undefined : useId()),
},
index: {
type: Number,
default: 0,
},
},
setup(props, { slots, expose }) {
const carousel = inject(injectCarousel$1);
provide(injectCarousel$1, undefined); // Don't provide for nested slides
if (!carousel) {
return null; // Don't render, let vue warn about the missing provide
}
const index = ref(props.index);
watch(() => props.index, (i) => index.value = i);
const isActive = computed(() => index.value === carousel.currentSlide);
const isPrev = computed(() => index.value === carousel.currentSlide - 1);
const isNext = computed(() => index.value === carousel.currentSlide + 1);
const isVisible = computed(() => index.value >= Math.floor(carousel.scrolledIndex) &&
index.value < Math.ceil(carousel.scrolledIndex) + carousel.config.itemsToShow);
const slideStyle = computed(() => {
const dimension = carousel.config.gap > 0 && carousel.config.itemsToShow > 1
? `calc(${100 / carousel.config.itemsToShow}% - ${(carousel.config.gap * (carousel.config.itemsToShow - 1)) /
carousel.config.itemsToShow}px)`
: `${100 / carousel.config.itemsToShow}%`;
return carousel.isVertical ? { height: dimension } : { width: dimension };
});
const instance = getCurrentInstance();
if (!props.isClone) {
carousel.registerSlide(instance, (resolvedIndex) => (index.value = resolvedIndex));
onUnmounted(() => {
carousel.unregisterSlide(instance);
});
}
else {
const makeUnfocusable = (node) => {
[
...((node === null || node === void 0 ? void 0 : node.el)
? node.el.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])')
: []),
]
.filter((el) => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'))
.forEach((el) => el.setAttribute('tabindex', '-1'));
};
// Prevent cloned slides from being focusable
onMounted(() => {
makeUnfocusable(instance.vnode);
});
onUpdated(() => {
makeUnfocusable(instance.vnode);
});
}
expose({
id: props.id,
});
return () => {
var _a, _b;
if (!carousel.config.enabled) {
return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
}
return 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': carousel.isSliding,
},
onFocusin: () => {
// Prevent the viewport being scrolled by the focus
if (carousel.viewport) {
carousel.viewport.scrollLeft = 0;
}
carousel.nav.slideTo(index.value);
},
id: props.isClone ? undefined : props.id,
'aria-hidden': props.isClone || undefined,
}, (_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: carousel.isSliding,
isVisible: isVisible.value,
}));
};
},
});
function createCloneSlides({ slides, position, toShow }) {
const clones = [];
const isBefore = position === 'before';
const start = isBefore ? -toShow : 0;
const end = isBefore ? 0 : toShow;
for (let i = start; i < end; i++) {
const index = isBefore ? i : slides.length > 0 ? i + slides.length : i + 99999;
const props = {
index,
isClone: true,
key: `clone-${position}-${i}`,
};
clones.push(slides.length > 0
? cloneVNode(slides[(i + slides.length) % slides.length].vnode, props)
: h(Slide, props));
}
return clones;
}
const ARIA = defineComponent({
name: 'CarouselAria',
setup() {
const carousel = inject(injectCarousel$1);
if (!carousel) {
return;
}
return () => h('div', {
class: ['carousel__liveregion', 'carousel__sr-only'],
'aria-live': 'polite',
'aria-atomic': 'true',
}, i18nFormatter(carousel.config.i18n['itemXofY'], {
currentSlide: carousel.currentSlide + 1,
slidesCount: carousel.slidesCount,
}));
},
});
// Use a symbol for inject provide to avoid any kind of collision with another lib
// https://vuejs.org/guide/components/provide-inject#working-with-symbol-keys
const injectCarousel = Symbol('carousel');
const SNAP_ALIGN_OPTIONS = ['center', 'start', 'end', 'center-even', 'center-odd'];

@@ -31,2 +330,9 @@ const BREAKPOINT_MODE_OPTIONS = ['viewport', 'carousel'];

};
const DIR_MAP = {
'left-to-right': 'ltr',
'right-to-left': 'rtl',
'top-to-bottom': 'ttb',
'bottom-to-top': 'btt',
};
const NORMALIZED_DIR_OPTIONS = Object.values(DIR_MAP);
const DEFAULT_CONFIG = {

@@ -134,2 +440,3 @@ enabled: true,

dir: {
type: String,
default: DEFAULT_CONFIG.dir,

@@ -148,168 +455,4 @@ validator(value) {

/**
* 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 === 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 = defineComponent({
name: 'ARIA',
setup() {
const config = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
const currentSlide = inject('currentSlide', ref(0));
const slidesCount = inject('slidesCount', ref(0));
return () => 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 = defineComponent({
name: 'Carousel',
const Carousel = defineComponent({
name: 'VueCarousel',
props: carouselProps,

@@ -329,5 +472,5 @@ emits: [

const viewport = ref(null);
const slides = ref([]);
const slides = shallowReactive([]);
const slideSize = ref(0);
const slidesCount = ref(0);
const slidesCount = computed(() => slides.length);
const fallbackConfig = computed(() => (Object.assign(Object.assign(Object.assign({}, DEFAULT_CONFIG), props), { i18n: Object.assign(Object.assign({}, DEFAULT_CONFIG.i18n), props.i18n), breakpoints: undefined })));

@@ -339,5 +482,9 @@ // current active config

const prevSlideIndex = ref(0);
const middleSlideIndex = ref(0);
const maxSlideIndex = ref(0);
const minSlideIndex = ref(0);
const middleSlideIndex = computed(() => Math.ceil((slidesCount.value - 1) / 2));
const maxSlideIndex = computed(() => {
return getMaxSlideIndex({ config, slidesCount: slidesCount.value });
});
const minSlideIndex = computed(() => {
return getMinSlideIndex({ config, slidesCount: slidesCount.value });
});
let autoplayTimer = null;

@@ -347,35 +494,42 @@ let transitionTimer = null;

const effectiveSlideSize = computed(() => slideSize.value + config.gap);
const normalizeDir = 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 normalizedDir = computed(() => {
const dir = config.dir || 'ltr';
return dir in DIR_MAP ? DIR_MAP[dir] : dir;
});
const isVertical = computed(() => ['ttb', 'btt'].includes(normalizeDir.value));
provide('config', config);
provide('slidesCount', slidesCount);
provide('currentSlide', currentSlideIndex);
provide('maxSlide', maxSlideIndex);
provide('minSlide', minSlideIndex);
provide('slideSize', slideSize);
provide('isVertical', isVertical);
provide('normalizeDir', normalizeDir);
const indexCbs = [];
const registerSlide = (slide, indexCb) => {
indexCb(slides.length);
slides.push(slide);
indexCbs.push(indexCb);
};
const unregisterSlide = (slide) => {
const found = slides.indexOf(slide);
if (found >= 0) {
slides.splice(found, 1);
indexCbs.splice(found, 1);
// Update indexes after the one that was removed
indexCbs.slice(found).forEach((cb, index) => cb(found + index));
}
};
const isReversed = computed(() => ['rtl', 'btt'].includes(normalizedDir.value));
const isVertical = computed(() => ['ttb', 'btt'].includes(normalizedDir.value));
const clonedSlidesCount = computed(() => Math.ceil(config.itemsToShow) + 1);
function updateBreakpointsConfig() {
var _a;
// Determine the width source based on the 'breakpointMode' config
const widthSource = (config.breakpointMode === 'carousel'
const widthSource = (fallbackConfig.value.breakpointMode === 'carousel'
? (_a = root.value) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width
: window.innerWidth) || 0;
: typeof window !== 'undefined'
? window.innerWidth
: 0) || 0;
const breakpointsArray = Object.keys(props.breakpoints || {})
.map((key) => Number(key))
.sort((a, b) => +b - +a);
let newConfig = Object.assign({}, fallbackConfig.value);
const newConfig = {};
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]);
Object.assign(newConfig, props.breakpoints[breakpoint]);
if (newConfig.i18n) {
Object.assign(newConfig.i18n, fallbackConfig.value.i18n, props.breakpoints[breakpoint].i18n);
}
return true;

@@ -385,9 +539,11 @@ }

});
Object.assign(config, newConfig);
Object.assign(config, fallbackConfig.value, newConfig);
}
const handleResize = debounce(() => {
const handleResize = throttle(() => {
updateBreakpointsConfig();
updateSlidesData();
updateSlideSize();
}, 16);
});
const totalGap = computed(() => (config.itemsToShow - 1) * config.gap);
const transformElements = shallowReactive(new Set());
/**

@@ -399,20 +555,25 @@ * Setup functions

return;
const rect = viewport.value.getBoundingClientRect();
// Calculate the total gap space
const totalGap = (config.itemsToShow - 1) * config.gap;
let multiplierWidth = 1;
transformElements.forEach((el) => {
const transformArr = getTransformValues(el);
if (transformArr.length === 6) {
multiplierWidth *= transformArr[0];
}
});
// Calculate size based on orientation
if (isVertical.value) {
slideSize.value = (rect.height - totalGap) / config.itemsToShow;
if (config.height !== 'auto') {
const height = typeof config.height === 'string' && isNaN(parseInt(config.height))
? viewport.value.getBoundingClientRect().height
: parseInt(config.height);
slideSize.value = (height - totalGap.value) / config.itemsToShow;
}
}
else {
slideSize.value = (rect.width - totalGap) / config.itemsToShow;
const width = viewport.value.getBoundingClientRect().width;
slideSize.value = (width / multiplierWidth - totalGap.value) / 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) {
if (!config.wrapAround && slidesCount.value > 0) {
currentSlideIndex.value = getNumberInRange({

@@ -425,11 +586,45 @@ val: currentSlideIndex.value,

}
watchEffect(() => updateSlidesData());
watchEffect(() => {
// Call updateSlideSize when viewport is ready and track deps
updateSlideSize();
});
let animationInterval;
const setAnimationInterval = (event) => {
const target = event.target;
if (target) {
transformElements.add(target);
}
if (!animationInterval) {
const stepAnimation = () => {
animationInterval = requestAnimationFrame(() => {
updateSlideSize();
stepAnimation();
});
};
stepAnimation();
}
};
const finishAnimation = (event) => {
const target = event.target;
if (target) {
transformElements.delete(target);
}
if (animationInterval && transformElements.size === 0) {
cancelAnimationFrame(animationInterval);
updateSlideSize();
}
};
updateBreakpointsConfig();
onMounted(() => {
nextTick(() => updateSlideSize());
// Overcome some edge cases
setTimeout(() => updateSlideSize(), 1000);
updateBreakpointsConfig();
if (fallbackConfig.value.breakpointMode === 'carousel') {
updateBreakpointsConfig();
}
initAutoplay();
window.addEventListener('resize', handleResize, { passive: true });
resizeObserver = new ResizeObserver(handleResize);
if (document) {
document.addEventListener('animationstart', setAnimationInterval);
document.addEventListener('animationend', finishAnimation);
}
if (root.value) {
resizeObserver = new ResizeObserver(handleResize);
resizeObserver.observe(root.value);

@@ -439,16 +634,28 @@ }

});
onUnmounted(() => {
onBeforeUnmount(() => {
// Empty the slides before they unregister for better performance
slides.splice(0, slides.length);
indexCbs.splice(0, indexCbs.length);
if (transitionTimer) {
clearTimeout(transitionTimer);
}
if (animationInterval) {
cancelAnimationFrame(animationInterval);
}
if (autoplayTimer) {
clearInterval(autoplayTimer);
}
if (resizeObserver && root.value) {
resizeObserver.unobserve(root.value);
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
window.removeEventListener('resize', handleResize, {
passive: true,
});
if (document) {
document.removeEventListener('keydown', handleArrowKeys);
document.removeEventListener('animationstart', setAnimationInterval);
document.removeEventListener('animationend', finishAnimation);
}
if (root.value) {
root.value.removeEventListener('transitionend', updateSlideSize);
root.value.removeEventListener('animationiteration', updateSlideSize);
}
});

@@ -469,2 +676,36 @@ /**

};
const handleArrowKeys = throttle((event) => {
if (event.ctrlKey)
return;
switch (event.key) {
case 'ArrowLeft':
case 'ArrowUp':
if (isVertical.value === event.key.endsWith('Up')) {
if (isReversed.value) {
nav.next(true);
}
else {
nav.prev(true);
}
}
break;
case 'ArrowRight':
case 'ArrowDown':
if (isVertical.value === event.key.endsWith('Down')) {
if (isReversed.value) {
nav.prev(true);
}
else {
nav.next(true);
}
}
break;
}
}, 200);
const handleFocus = () => {
document.addEventListener('keydown', handleArrowKeys);
};
const handleBlur = () => {
document.removeEventListener('keydown', handleArrowKeys);
};
function handleDragStart(event) {

@@ -487,4 +728,4 @@ // Prevent drag initiation on input elements or if already sliding

// Initialize start positions for the drag
startPosition.x = isTouch ? event.touches[0].clientX : event.clientX;
startPosition.y = isTouch ? event.touches[0].clientY : event.clientY;
startPosition.x = 'touches' in event ? event.touches[0].clientX : event.clientX;
startPosition.y = 'touches' in event ? event.touches[0].clientY : event.clientY;
// Attach event listeners for dragging and drag end

@@ -499,4 +740,4 @@ const moveEvent = isTouch ? 'touchmove' : 'mousemove';

// 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;
const currentX = 'touches' in event ? event.touches[0].clientX : event.clientX;
const currentY = 'touches' in event ? event.touches[0].clientY : event.clientY;
// Calculate deltas for X and Y axes

@@ -512,5 +753,6 @@ const deltaX = currentX - startPosition.x;

function handleDragEnd() {
handleDragging.cancel();
// Determine the active axis and direction multiplier
const dragAxis = isVertical.value ? 'y' : 'x';
const directionMultiplier = ['rtl', 'btt'].includes(normalizeDir.value) ? -1 : 1;
const directionMultiplier = isReversed.value ? -1 : 1;
// Calculate dragged slides with a tolerance to account for incomplete drags

@@ -554,3 +796,3 @@ const tolerance = Math.sign(dragged[dragAxis]) * 0.4; // Smooth out small drags

}
function resetAutoplay() {
function stopAutoplay() {
if (autoplayTimer) {

@@ -560,2 +802,5 @@ clearInterval(autoplayTimer);

}
}
function resetAutoplay() {
stopAutoplay();
initAutoplay();

@@ -567,3 +812,3 @@ }

const isSliding = ref(false);
function slideTo(slideIndex) {
function slideTo(slideIndex, skipTransition = false) {
const currentVal = config.wrapAround

@@ -576,3 +821,4 @@ ? slideIndex

});
if (currentSlideIndex.value === currentVal || isSliding.value) {
if (currentSlideIndex.value === currentVal ||
(!skipTransition && isSliding.value)) {
return;

@@ -586,13 +832,21 @@ }

});
stopAutoplay();
isSliding.value = true;
prevSlideIndex.value = currentSlideIndex.value;
const mappedNumber = config.wrapAround
? mapNumberToRange({
val: currentVal,
max: maxSlideIndex.value,
min: 0,
})
: currentVal;
currentSlideIndex.value = currentVal;
if (mappedNumber !== currentVal) {
modelWatcher.pause();
}
emit('update:modelValue', mappedNumber);
transitionTimer = setTimeout(() => {
if (config.wrapAround) {
const mappedNumber = mapNumberToRange({
val: currentVal,
max: maxSlideIndex.value,
min: 0,
});
if (mappedNumber !== currentSlideIndex.value) {
if (mappedNumber !== currentVal) {
modelWatcher.resume();
currentSlideIndex.value = mappedNumber;

@@ -605,3 +859,2 @@ emit('loop', {

}
emit('update:modelValue', currentSlideIndex.value);
emit('slide-end', {

@@ -616,9 +869,41 @@ currentSlideIndex: currentSlideIndex.value,

}
function next() {
slideTo(currentSlideIndex.value + config.itemsToScroll);
function next(skipTransition = false) {
slideTo(currentSlideIndex.value + config.itemsToScroll, skipTransition);
}
function prev() {
slideTo(currentSlideIndex.value - config.itemsToScroll);
function prev(skipTransition = false) {
slideTo(currentSlideIndex.value - config.itemsToScroll, skipTransition);
}
const nav = { slideTo, next, prev };
const scrolledIndex = computed(() => getScrolledIndex({
config,
currentSlide: currentSlideIndex.value,
slidesCount: slidesCount.value,
}));
const provided = reactive({
config,
slidesCount,
viewport,
slides,
scrolledIndex,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
slideSize,
isVertical,
normalizedDir,
nav,
isSliding,
registerSlide,
unregisterSlide,
});
provide(injectCarousel$1, provided);
/** @deprecated provides */
provide('config', config);
provide('slidesCount', slidesCount);
provide('currentSlide', currentSlideIndex);
provide('maxSlide', maxSlideIndex);
provide('minSlide', minSlideIndex);
provide('slideSize', slideSize);
provide('isVertical', isVertical);
provide('normalizeDir', normalizedDir);
provide('nav', nav);

@@ -633,15 +918,14 @@ provide('isSliding', isSliding);

// Update the carousel on props change
watch(() => (Object.assign({}, props)), restartCarousel, { deep: true });
watch(() => [fallbackConfig.value, props.breakpoints], () => updateBreakpointsConfig(), { deep: true });
watch(() => props.autoplay, () => resetAutoplay());
// Handle changing v-model value
watch(() => props['modelValue'], (val) => {
const modelWatcher = watch(() => props.modelValue, (val) => {
if (val === currentSlideIndex.value) {
return;
}
slideTo(Number(val));
slideTo(Number(val), true);
});
// Handel when slides added/removed
watch(slidesCount, updateSlidesData);
// Init carousel
emit('before-init');
const data = {
const data = reactive({
config,

@@ -654,3 +938,3 @@ slidesCount,

middleSlide: middleSlideIndex,
};
});
expose({

@@ -667,2 +951,13 @@ updateBreakpointsConfig,

});
const trackHeight = computed(() => {
if (isVertical.value && slideSize.value && config.height === 'auto') {
return `${slideSize.value * config.itemsToShow + totalGap.value}px`;
}
return config.height !== 'auto'
? typeof config.height === 'number' ||
parseInt(config.height).toString() === config.height
? `${config.height}px`
: config.height
: undefined;
});
/**

@@ -673,13 +968,9 @@ * Track style

// 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;
const cloneOffset = config.wrapAround ? clonedSlidesCount.value : 0;
// Determine direction multiplier for orientation
const isReverseDirection = ['rtl', 'btt'].includes(normalizeDir.value);
const directionMultiplier = isReverseDirection ? -1 : 1;
const directionMultiplier = isReversed.value ? -1 : 1;
// Calculate the total offset for slide transformation
const totalOffset = (scrolledIndex + cloneOffset) * effectiveSlideSize.value * directionMultiplier;
const totalOffset = (scrolledIndex.value + cloneOffset) *
effectiveSlideSize.value *
directionMultiplier;
// Include user drag interaction offset

@@ -691,38 +982,23 @@ const dragOffset = isVertical.value ? dragged.y : dragged.x;

});
const slotSlides = slots.default || slots.slides;
const slotAddons = slots.addons;
const slotsProps = reactive(data);
return () => {
if (!config.enabled) {
const slotSlides = slots.default || slots.slides;
const slotAddons = slots.addons;
let output = (slotSlides === null || slotSlides === void 0 ? void 0 : slotSlides(data)) || [];
if (!config.enabled || !output.length) {
return h('section', {
ref: root,
class: ['carousel', 'is-disabled'],
}, slotSlides === null || slotSlides === void 0 ? void 0 : slotSlides());
}, output);
}
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;
const addonsElements = (slotAddons === null || slotAddons === void 0 ? void 0 : slotAddons(data)) || [];
if (config.wrapAround) {
const slidesBefore = slidesElements.map((el, index) => cloneVNode(el, {
index: -slidesElements.length + index,
isClone: true,
key: `clone-before-${index}`,
}));
const slidesAfter = slidesElements.map((el, index) => cloneVNode(el, {
index: slidesElements.length + index,
isClone: true,
key: `clone-after-${index}`,
}));
output = [...slidesBefore, ...slidesElements, ...slidesAfter];
// Ensure scoped CSS tracks properly
const scopeId = output.length > 0 ? output[0].scopeId : null;
pushScopeId(scopeId);
const toShow = clonedSlidesCount.value;
const slidesBefore = createCloneSlides({ slides, position: 'before', toShow });
const slidesAfter = createCloneSlides({ slides, position: 'after', toShow });
popScopeId();
output = [...slidesBefore, ...output, ...slidesAfter];
}
slides.value = slidesElements;
slidesCount.value = Math.max(slidesElements.length, 1);
const trackEl = h('ol', {

@@ -732,4 +1008,4 @@ class: 'carousel__track',

transform: trackTransform.value,
transition: `${isSliding.value ? config.transition : 0}ms`,
gap: `${config.gap}px`,
'transition-duration': isSliding.value ? `${config.transition}ms` : undefined,
gap: config.gap > 0 ? `${config.gap}px` : undefined,
},

@@ -744,3 +1020,3 @@ onMousedownCapture: config.mouseDrag ? handleDragStart : null,

'carousel',
`is-${normalizeDir.value}`,
`is-${normalizedDir.value}`,
{

@@ -754,10 +1030,12 @@ 'is-vertical': isVertical.value,

style: {
'--vc-trk-height': `${typeof config.height === 'number' ? `${config.height}px` : config.height}`,
'--vc-trk-height': trackHeight.value,
},
dir: normalizeDir.value,
dir: normalizedDir.value,
'aria-label': config.i18n['ariaGallery'],
tabindex: '0',
onFocus: handleFocus,
onBlur: handleBlur,
onMouseenter: handleMouseEnter,
onMouseleave: handleMouseLeave,
}, [viewPortEl, addonsElements, h(ARIAComponent)]);
}, [viewPortEl, addonsElements, h(ARIA)]);
};

@@ -774,2 +1052,10 @@ },

})(IconName || (IconName = {}));
function isIconName(candidate) {
return candidate in IconName;
}
const iconI18n = (name) => `icon${name.charAt(0).toUpperCase() + name.slice(1)}`;
const validateIconName = (value) => {
return value && isIconName(value);
};
const icons = {

@@ -781,170 +1067,129 @@ arrowUp: 'M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z',

};
function isIconName(candidate) {
return candidate in IconName;
}
const Icon = (props) => {
const config = inject('config', 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 = h('path', { d: path });
const iconTitle = config.i18n[iconI18n] || props.title || iconName;
const titleEl = h('title', iconTitle);
return 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 = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
const maxSlide = inject('maxSlide', ref(1));
const minSlide = inject('minSlide', ref(1));
const normalizeDir = inject('normalizeDir', ref('ltr'));
const currentSlide = inject('currentSlide', ref(1));
const nav = 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 = 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()) || h(Icon, { name: getPrevIcon() }));
const nextButton = 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()) || h(Icon, { name: getNextIcon() }));
return [prevButton, nextButton];
};
const Pagination = () => {
const config = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
const maxSlide = inject('maxSlide', ref(1));
const minSlide = inject('minSlide', ref(1));
const currentSlide = inject('currentSlide', ref(1));
const nav = 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 = 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 = h('li', { class: 'carousel__pagination-item', key: slide }, button);
children.push(item);
}
return h('ol', { class: 'carousel__pagination' }, children);
};
var Slide = defineComponent({
name: 'CarouselSlide',
const Icon = defineComponent({
props: {
index: {
type: Number,
default: 1,
name: {
type: String,
required: true,
validator: validateIconName,
},
isClone: {
type: Boolean,
default: false,
title: {
type: String,
default: (props) => props.name ? DEFAULT_CONFIG.i18n[iconI18n(props.name)] : '',
},
},
setup(props) {
const carousel = inject(injectCarousel$1, null);
return () => {
const iconName = props.name;
if (!iconName || !validateIconName(iconName))
return;
const path = icons[iconName];
const pathEl = h('path', { d: path });
const iconTitle = (carousel === null || carousel === void 0 ? void 0 : carousel.config.i18n[iconI18n(iconName)]) || props.title || iconName;
const titleEl = h('title', iconTitle);
return h('svg', {
class: 'carousel__icon',
viewBox: '0 0 24 24',
role: 'img',
'aria-label': iconTitle,
}, [titleEl, pathEl]);
};
},
});
const Navigation = defineComponent({
name: 'CarouselNavigation',
setup(props, { slots }) {
const config = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
const currentSlide = inject('currentSlide', ref(0));
const slidesToScroll = inject('slidesToScroll', ref(0));
const isSliding = inject('isSliding', ref(false));
const isVertical = inject('isVertical', ref(false));
const slideSize = inject('slideSize', ref(0));
const isActive = computed(() => props.index === currentSlide.value);
const isPrev = computed(() => props.index === currentSlide.value - 1);
const isNext = computed(() => props.index === currentSlide.value + 1);
const isVisible = 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 = computed(() => {
const dimension = config.gap
? `${slideSize.value}px`
: `${100 / config.itemsToShow}%`;
return isVertical.value
? { height: dimension, width: '' }
: { width: dimension, height: '' };
});
const carousel = inject(injectCarousel$1);
if (!carousel) {
return null; // Don't render, let vue warn about the missing provide
}
const { next: slotNext, prev: slotPrev } = slots;
const getPrevIcon = () => {
const directionIcons = {
ltr: 'arrowLeft',
rtl: 'arrowRight',
ttb: 'arrowUp',
btt: 'arrowDown',
};
return directionIcons[carousel.normalizedDir];
};
const getNextIcon = () => {
const directionIcons = {
ltr: 'arrowRight',
rtl: 'arrowLeft',
ttb: 'arrowDown',
btt: 'arrowUp',
};
return directionIcons[carousel.normalizedDir];
};
return () => {
const { wrapAround, i18n } = carousel.config;
const prevButton = h('button', {
type: 'button',
class: [
'carousel__prev',
!wrapAround &&
carousel.currentSlide <= carousel.minSlide &&
'carousel__prev--disabled',
props.class,
],
'aria-label': i18n['ariaPreviousSlide'],
title: i18n['ariaPreviousSlide'],
onClick: carousel.nav.prev,
}, (slotPrev === null || slotPrev === void 0 ? void 0 : slotPrev()) || h(Icon, { name: getPrevIcon() }));
const nextButton = h('button', {
type: 'button',
class: [
'carousel__next',
!wrapAround &&
carousel.currentSlide >= carousel.maxSlide &&
'carousel__next--disabled',
props.class,
],
'aria-label': i18n['ariaNextSlide'],
title: i18n['ariaNextSlide'],
onClick: carousel.nav.next,
}, (slotNext === null || slotNext === void 0 ? void 0 : slotNext()) || h(Icon, { name: getNextIcon() }));
return [prevButton, nextButton];
};
},
});
const Pagination = defineComponent({
name: 'CarouselPagination',
setup(props) {
const carousel = inject(injectCarousel$1);
if (!carousel) {
return null; // Don't render, let vue warn about the missing provide
}
const isActive = (slide) => mapNumberToRange({
val: carousel.currentSlide,
max: carousel.maxSlide,
min: 0,
}) === slide;
return () => {
var _a, _b;
if (!config.enabled) {
return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
const children = [];
for (let slide = carousel.minSlide; slide <= carousel.maxSlide; slide++) {
const buttonLabel = i18nFormatter(carousel.config.i18n.ariaNavigateToSlide, {
slideNumber: slide + 1,
});
const active = isActive(slide);
const button = h('button', {
type: 'button',
class: {
'carousel__pagination-button': true,
'carousel__pagination-button--active': active,
},
'aria-label': buttonLabel,
'aria-pressed': active,
'aria-controls': (_b = (_a = carousel.slides[slide]) === null || _a === void 0 ? void 0 : _a.exposed) === null || _b === void 0 ? void 0 : _b.id,
title: buttonLabel,
onClick: props.disableOnClick ? undefined : () => carousel.nav.slideTo(slide),
});
const item = h('li', { class: 'carousel__pagination-item', key: slide }, button);
children.push(item);
}
return 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,
}));
return h('ol', { class: 'carousel__pagination' }, children);
};

@@ -954,2 +1199,2 @@ },

export { Carousel, Icon, Navigation, Pagination, Slide };
export { BREAKPOINT_MODE_OPTIONS, Carousel, DEFAULT_CONFIG, DIR_MAP, DIR_OPTIONS, I18N_DEFAULT_CONFIG, Icon, NORMALIZED_DIR_OPTIONS, Navigation, Pagination, SNAP_ALIGN_OPTIONS, Slide, icons, injectCarousel };
/**
* Vue 3 Carousel 0.8.1
* Vue 3 Carousel 0.9.0
* (c) 2024
* @license MIT
*/
import{Fragment as e,defineComponent as t,inject as l,reactive as a,ref as n,h as i,computed as o,provide as r,onMounted as u,nextTick as s,onUnmounted as d,watch as c,cloneVNode as v}from"vue";const p=["center","start","end","center-even","center-odd"],m=["viewport","carousel"],g=["ltr","left-to-right","rtl","right-to-left","ttb","top-to-bottom","btt","bottom-to-top"],f={enabled:!0,itemsToShow:1,itemsToScroll:1,modelValue:0,transition:300,autoplay:0,gap:0,height:"auto",wrapAround:!1,pauseAutoplayOnHover:!1,mouseDrag:!0,touchDrag:!0,snapAlign:p[0],dir:g[0],breakpointMode:m[0],breakpoints:void 0,i18n:{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"}},h={enabled:{default:f.enabled,type:Boolean},itemsToShow:{default:f.itemsToShow,type:Number},itemsToScroll:{default:f.itemsToScroll,type:Number},wrapAround:{default:f.wrapAround,type:Boolean},gap:{default:f.gap,type:Number},height:{default:f.height,type:[Number,String]},snapAlign:{default:f.snapAlign,validator:e=>p.includes(e)},transition:{default:f.transition,type:Number},breakpointMode:{default:f.breakpointMode,validator:e=>m.includes(e)},breakpoints:{default:f.breakpoints,type:Object},autoplay:{default:f.autoplay,type:Number},pauseAutoplayOnHover:{default:f.pauseAutoplayOnHover,type:Boolean},modelValue:{default:void 0,type:Number},mouseDrag:{default:f.mouseDrag,type:Boolean},touchDrag:{default:f.touchDrag,type:Boolean},dir:{default:f.dir,validator:e=>g.includes(e)},i18n:{default:f.i18n,type:Object}};function b({val:e,max:t,min:l}){return t<l?e:Math.min(Math.max(e,l),t)}function w(t){return t?t.reduce(((t,l)=>{var a;return l.type===e?[...t,...w(l.children)]:"CarouselSlide"===(null===(a=l.type)||void 0===a?void 0:a.name)?[...t,l]:t}),[]):[]}function S({val:e,max:t,min:l=0}){const a=t-l+1;return((e-l)%a+a)%a+l}function x(e="",t={}){return Object.entries(t).reduce(((e,[t,l])=>e.replace(`{${t}}`,String(l))),e)}var y,A=t({name:"ARIA",setup(){const e=l("config",a(Object.assign({},f))),t=l("currentSlide",n(0)),o=l("slidesCount",n(0));return()=>i("div",{class:["carousel__liveregion","carousel__sr-only"],"aria-live":"polite","aria-atomic":"true"},x(e.i18n.itemXofY,{currentSlide:t.value+1,slidesCount:o.value}))}}),_=t({name:"Carousel",props:h,emits:["init","drag","slide-start","loop","update:modelValue","slide-end","before-init"],setup(e,{slots:t,emit:l,expose:p}){var m;const g=n(null),h=n(null),x=n([]),y=n(0),_=n(0),T=o((()=>Object.assign(Object.assign(Object.assign({},f),e),{i18n:Object.assign(Object.assign({},f.i18n),e.i18n),breakpoints:void 0}))),C=a(Object.assign({},T.value)),M=n(null!==(m=e.modelValue)&&void 0!==m?m:0),N=n(0),O=n(0),k=n(0),L=n(0);let j=null,D=null,z=null;const E=o((()=>y.value+C.gap)),I=o((()=>{const e=C.dir||"lrt";return{"left-to-right":"ltr","right-to-left":"rtl","top-to-bottom":"ttb","bottom-to-top":"btt"}[e]||e})),$=o((()=>["ttb","btt"].includes(I.value)));function R(){var t;const l=("carousel"===C.breakpointMode?null===(t=g.value)||void 0===t?void 0:t.getBoundingClientRect().width:window.innerWidth)||0,a=Object.keys(e.breakpoints||{}).map((e=>Number(e))).sort(((e,t)=>+t-+e));let n=Object.assign({},T.value);a.some((t=>{var a;return l>=t&&(n=Object.assign(Object.assign({},n),null===(a=e.breakpoints)||void 0===a?void 0:a[t]),!0)})),Object.assign(C,n)}r("config",C),r("slidesCount",_),r("currentSlide",M),r("maxSlide",k),r("minSlide",L),r("slideSize",y),r("isVertical",$),r("normalizeDir",I);const B=function(e,t){let l;return function(...a){l&&clearTimeout(l),l=setTimeout((()=>{e(...a),l=null}),t)}}((()=>{R(),X(),V()}),16);function V(){if(!h.value)return;const e=h.value.getBoundingClientRect(),t=(C.itemsToShow-1)*C.gap;$.value?y.value=(e.height-t)/C.itemsToShow:y.value=(e.width-t)/C.itemsToShow}function X(){_.value<=0||(O.value=Math.ceil((_.value-1)/2),k.value=function({config:e,slidesCount:t}){var l;const{snapAlign:a="N/A",wrapAround:n,itemsToShow:i=1}=e;if(n)return Math.max(t-1,0);const o=null!==(l={start:Math.ceil(t-i),end:Math.ceil(t-1),center:t-Math.ceil((i-.5)/2),"center-odd":t-Math.ceil((i-.5)/2),"center-even":t-Math.ceil(i/2)}[a])&&void 0!==l?l:0;return Math.max(o,0)}({config:C,slidesCount:_.value}),L.value=function({config:e,slidesCount:t}){var l;const{snapAlign:a="N/A",wrapAround:n,itemsToShow:i=1}=e;return n||i>t?0:null!==(l={start:0,end:Math.floor(i-1),center:Math.floor((i-1)/2),"center-odd":Math.floor((i-1)/2),"center-even":Math.floor((i-2)/2)}[a])&&void 0!==l?l:0}({config:C,slidesCount:_.value}),C.wrapAround||(M.value=b({val:M.value,max:k.value,min:L.value})))}u((()=>{s((()=>V())),setTimeout((()=>V()),1e3),R(),Q(),window.addEventListener("resize",B,{passive:!0}),z=new ResizeObserver(B),g.value&&z.observe(g.value),l("init")})),d((()=>{D&&clearTimeout(D),j&&clearInterval(j),z&&g.value&&(z.unobserve(g.value),z=null),window.removeEventListener("resize",B,{passive:!0})}));let U=!1;const Y={x:0,y:0},P=a({x:0,y:0}),H=n(!1),G=n(!1),q=()=>{H.value=!0},F=()=>{H.value=!1};function W(e){const t=e.target.tagName;if(["INPUT","TEXTAREA","SELECT"].includes(t)||ee.value)return;if(U="touchstart"===e.type,!U&&(e.preventDefault(),0!==e.button))return;Y.x=U?e.touches[0].clientX:e.clientX,Y.y=U?e.touches[0].clientY:e.clientY;const l=U?"touchmove":"mousemove",a=U?"touchend":"mouseup";document.addEventListener(l,J,{passive:!1}),document.addEventListener(a,K,{passive:!0})}const J=function(e){let t=!1;return function(...l){t||(t=!0,requestAnimationFrame((()=>{e.apply(this,l),t=!1})))}}((e=>{G.value=!0;const t=U?e.touches[0].clientX:e.clientX,a=U?e.touches[0].clientY:e.clientY,n=t-Y.x,i=a-Y.y;P.x=n,P.y=i,l("drag",{deltaX:n,deltaY:i})}));function K(){const e=$.value?"y":"x",t=["rtl","btt"].includes(I.value)?-1:1,l=.4*Math.sign(P[e]),a=Math.round(P[e]/E.value+l)*t;if(a&&!U){const e=t=>{t.preventDefault(),window.removeEventListener("click",e)};window.addEventListener("click",e)}te(M.value-a),P.x=0,P.y=0,G.value=!1;const n=U?"touchmove":"mousemove",i=U?"touchend":"mouseup";document.removeEventListener(n,J),document.removeEventListener(i,K)}function Q(){!C.autoplay||C.autoplay<=0||(j=setInterval((()=>{C.pauseAutoplayOnHover&&H.value||le()}),C.autoplay))}function Z(){j&&(clearInterval(j),j=null),Q()}const ee=n(!1);function te(e){const t=C.wrapAround?e:b({val:e,max:k.value,min:L.value});M.value===t||ee.value||(l("slide-start",{slidingToIndex:e,currentSlideIndex:M.value,prevSlideIndex:N.value,slidesCount:_.value}),ee.value=!0,N.value=M.value,M.value=t,D=setTimeout((()=>{if(C.wrapAround){const a=S({val:t,max:k.value,min:0});a!==M.value&&(M.value=a,l("loop",{currentSlideIndex:M.value,slidingToIndex:e}))}l("update:modelValue",M.value),l("slide-end",{currentSlideIndex:M.value,prevSlideIndex:N.value,slidesCount:_.value}),ee.value=!1,Z()}),C.transition))}function le(){te(M.value+C.itemsToScroll)}function ae(){te(M.value-C.itemsToScroll)}const ne={slideTo:te,next:le,prev:ae};function ie(){R(),X(),V(),Z()}r("nav",ne),r("isSliding",ee),c((()=>Object.assign({},e)),ie,{deep:!0}),c((()=>e.modelValue),(e=>{e!==M.value&&te(Number(e))})),c(_,X),l("before-init");const oe={config:C,slidesCount:_,slideSize:y,currentSlide:M,maxSlide:k,minSlide:L,middleSlide:O};p({updateBreakpointsConfig:R,updateSlidesData:X,updateSlideSize:V,restartCarousel:ie,slideTo:te,next:le,prev:ae,nav:ne,data:oe});const re=o((()=>{const e=function({config:e,currentSlide:t,slidesCount:l}){const{snapAlign:a="N/A",wrapAround:n,itemsToShow:i=1}=e,o=((e,t)=>{var l;return null!==(l={start:0,center:(t-1)/2,"center-odd":(t-1)/2,"center-even":(t-2)/2,end:t-1}[e])&&void 0!==l?l:0})(a,i);return n?t-o:b({val:t-o,max:l-i,min:0})}({config:C,currentSlide:M.value,slidesCount:_.value}),t=C.wrapAround?_.value:0,l=["rtl","btt"].includes(I.value)?-1:1,a=(e+t)*E.value*l,n=$.value?P.y:P.x;return`translate${$.value?"Y":"X"}(${n-a}px)`})),ue=t.default||t.slides,se=t.addons,de=a(oe);return()=>{if(!C.enabled)return i("section",{ref:g,class:["carousel","is-disabled"]},null==ue?void 0:ue());const e=w(null==ue?void 0:ue(de)),t=(null==se?void 0:se(de))||[];e.forEach(((e,t)=>{e.props?e.props.index=t:e.props={index:t}}));let l=e;if(C.wrapAround){const t=e.map(((t,l)=>v(t,{index:-e.length+l,isClone:!0,key:`clone-before-${l}`}))),a=e.map(((t,l)=>v(t,{index:e.length+l,isClone:!0,key:`clone-after-${l}`})));l=[...t,...e,...a]}x.value=e,_.value=Math.max(e.length,1);const a=i("ol",{class:"carousel__track",style:{transform:re.value,transition:`${ee.value?C.transition:0}ms`,gap:`${C.gap}px`},onMousedownCapture:C.mouseDrag?W:null,onTouchstartPassiveCapture:C.touchDrag?W:null},l),n=i("div",{class:"carousel__viewport",ref:h},a);return i("section",{ref:g,class:["carousel",`is-${I.value}`,{"is-vertical":$.value,"is-sliding":ee.value,"is-dragging":G.value,"is-hover":H.value}],style:{"--vc-trk-height":`${"number"==typeof C.height?`${C.height}px`:C.height}`},dir:I.value,"aria-label":C.i18n.ariaGallery,tabindex:"0",onMouseenter:q,onMouseleave:F},[n,t,i(A)])}}});!function(e){e.arrowUp="arrowUp",e.arrowDown="arrowDown",e.arrowRight="arrowRight",e.arrowLeft="arrowLeft"}(y||(y={}));const T={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"};const C=e=>{const t=l("config",a(Object.assign({},f))),n=String(e.name),o=`icon${n.charAt(0).toUpperCase()+n.slice(1)}`;if(!n||"string"!=typeof n||!(n in y))return;const r=i("path",{d:T[n]}),u=t.i18n[o]||e.title||n,s=i("title",u);return i("svg",{class:"carousel__icon",viewBox:"0 0 24 24",role:"img","aria-label":u},[s,r])};C.props={name:String,title:String};const M=(e,{slots:t,attrs:o})=>{const{next:r,prev:u}=t||{},s=l("config",a(Object.assign({},f))),d=l("maxSlide",n(1)),c=l("minSlide",n(1)),v=l("normalizeDir",n("ltr")),p=l("currentSlide",n(1)),m=l("nav",{}),{wrapAround:g,i18n:h}=s;return[i("button",{type:"button",class:["carousel__prev",!g&&p.value<=c.value&&"carousel__prev--disabled",null==o?void 0:o.class],"aria-label":h.ariaPreviousSlide,title:h.ariaPreviousSlide,onClick:m.prev},(null==u?void 0:u())||i(C,{name:{ltr:"arrowLeft",rtl:"arrowRight",ttb:"arrowUp",btt:"arrowDown"}[v.value]})),i("button",{type:"button",class:["carousel__next",!g&&p.value>=d.value&&"carousel__next--disabled",null==o?void 0:o.class],"aria-label":h.ariaNextSlide,title:h.ariaNextSlide,onClick:m.next},(null==r?void 0:r())||i(C,{name:{ltr:"arrowRight",rtl:"arrowLeft",ttb:"arrowDown",btt:"arrowUp"}[v.value]}))]},N=()=>{const e=l("config",a(Object.assign({},f))),t=l("maxSlide",n(1)),o=l("minSlide",n(1)),r=l("currentSlide",n(1)),u=l("nav",{}),s=e=>S({val:r.value,max:t.value,min:0})===e,d=[];for(let l=o.value;l<t.value+1;l++){const t=x(e.i18n.ariaNavigateToSlide,{slideNumber:l+1}),a=i("button",{type:"button",class:{"carousel__pagination-button":!0,"carousel__pagination-button--active":s(l)},"aria-label":t,title:t,onClick:()=>u.slideTo(l)}),n=i("li",{class:"carousel__pagination-item",key:l},a);d.push(n)}return i("ol",{class:"carousel__pagination"},d)};var O=t({name:"CarouselSlide",props:{index:{type:Number,default:1},isClone:{type:Boolean,default:!1}},setup(e,{slots:t}){const r=l("config",a(Object.assign({},f))),u=l("currentSlide",n(0)),s=l("slidesToScroll",n(0)),d=l("isSliding",n(!1)),c=l("isVertical",n(!1)),v=l("slideSize",n(0)),p=o((()=>e.index===u.value)),m=o((()=>e.index===u.value-1)),g=o((()=>e.index===u.value+1)),h=o((()=>{const t=Math.floor(s.value),l=Math.ceil(s.value+r.itemsToShow-1);return e.index>=t&&e.index<=l})),b=o((()=>{const e=r.gap?`${v.value}px`:100/r.itemsToShow+"%";return c.value?{height:e,width:""}:{width:e,height:""}}));return()=>{var l,a;return r.enabled?i("li",{style:b.value,class:{carousel__slide:!0,"carousel__slide--clone":e.isClone,"carousel__slide--visible":h.value,"carousel__slide--active":p.value,"carousel__slide--prev":m.value,"carousel__slide--next":g.value,"carousel__slide--sliding":d.value},"aria-hidden":!h.value},null===(a=t.default)||void 0===a?void 0:a.call(t,{isActive:p.value,isClone:e.isClone,isPrev:m.value,isNext:g.value,isSliding:d.value,isVisible:h.value})):null===(l=t.default)||void 0===l?void 0:l.call(t)}}});export{_ as Carousel,C as Icon,M as Navigation,N as Pagination,O as Slide};
import{defineComponent as e,useId as t,inject as n,provide as i,ref as o,watch as a,computed as l,getCurrentInstance as r,onUnmounted as s,onMounted as u,onUpdated as c,h as d,cloneVNode as v,shallowReactive as p,reactive as m,watchEffect as g,onBeforeUnmount as h,pushScopeId as f,popScopeId as w}from"vue";const b=Symbol("carousel");function S({val:e,max:t,min:n}){return t<n?e:Math.min(Math.max(e,isNaN(n)?e:n),isNaN(t)?e:t)}function x({val:e,max:t,min:n=0}){const i=t-n+1;return((e-n)%i+i)%i+n}function y(e="",t={}){return Object.entries(t).reduce(((e,[t,n])=>e.replace(`{${t}}`,String(n))),e)}function A(e,t=0){let n=!1,i=0,o=null;function a(...a){if(n)return;n=!0;const l=()=>{o=requestAnimationFrame((o=>{o-i>t?(i=o,e(...a),n=!1):l()}))};l()}return a.cancel=()=>{o&&(cancelAnimationFrame(o),o=null,n=!1)},a}const C=e({name:"CarouselSlide",props:{isClone:{type:Boolean,default:!1},id:{type:String,default:e=>e.isClone?void 0:t()},index:{type:Number,default:0}},setup(e,{slots:t,expose:v}){const p=n(b);if(i(b,void 0),!p)return null;const m=o(e.index);a((()=>e.index),(e=>m.value=e));const g=l((()=>m.value===p.currentSlide)),h=l((()=>m.value===p.currentSlide-1)),f=l((()=>m.value===p.currentSlide+1)),w=l((()=>m.value>=Math.floor(p.scrolledIndex)&&m.value<Math.ceil(p.scrolledIndex)+p.config.itemsToShow)),S=l((()=>{const e=p.config.gap>0&&p.config.itemsToShow>1?`calc(${100/p.config.itemsToShow}% - ${p.config.gap*(p.config.itemsToShow-1)/p.config.itemsToShow}px)`:100/p.config.itemsToShow+"%";return p.isVertical?{height:e}:{width:e}})),x=r();if(e.isClone){const e=e=>{[...(null==e?void 0:e.el)?e.el.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'):[]].filter((e=>!e.hasAttribute("disabled")&&!e.getAttribute("aria-hidden"))).forEach((e=>e.setAttribute("tabindex","-1")))};u((()=>{e(x.vnode)})),c((()=>{e(x.vnode)}))}else p.registerSlide(x,(e=>m.value=e)),s((()=>{p.unregisterSlide(x)}));return v({id:e.id}),()=>{var n,i;return p.config.enabled?d("li",{style:S.value,class:{carousel__slide:!0,"carousel__slide--clone":e.isClone,"carousel__slide--visible":w.value,"carousel__slide--active":g.value,"carousel__slide--prev":h.value,"carousel__slide--next":f.value,"carousel__slide--sliding":p.isSliding},onFocusin:()=>{p.viewport&&(p.viewport.scrollLeft=0),p.nav.slideTo(m.value)},id:e.isClone?void 0:e.id,"aria-hidden":e.isClone||void 0},null===(i=t.default)||void 0===i?void 0:i.call(t,{isActive:g.value,isClone:e.isClone,isPrev:h.value,isNext:f.value,isSliding:p.isSliding,isVisible:w.value})):null===(n=t.default)||void 0===n?void 0:n.call(t)}}});function _({slides:e,position:t,toShow:n}){const i=[],o="before"===t,a=o?0:n;for(let l=o?-n:0;l<a;l++){const n={index:o?l:e.length>0?l+e.length:l+99999,isClone:!0,key:`clone-${t}-${l}`};i.push(e.length>0?v(e[(l+e.length)%e.length].vnode,n):d(C,n))}return i}const T=e({name:"CarouselAria",setup(){const e=n(b);if(e)return()=>d("div",{class:["carousel__liveregion","carousel__sr-only"],"aria-live":"polite","aria-atomic":"true"},y(e.config.i18n.itemXofY,{currentSlide:e.currentSlide+1,slidesCount:e.slidesCount}))}}),k=Symbol("carousel"),L=["center","start","end","center-even","center-odd"],N=["viewport","carousel"],M=["ltr","left-to-right","rtl","right-to-left","ttb","top-to-bottom","btt","bottom-to-top"],D={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"},E={"left-to-right":"ltr","right-to-left":"rtl","top-to-bottom":"ttb","bottom-to-top":"btt"},O=Object.values(E),I={enabled:!0,itemsToShow:1,itemsToScroll:1,modelValue:0,transition:300,autoplay:0,gap:0,height:"auto",wrapAround:!1,pauseAutoplayOnHover:!1,mouseDrag:!0,touchDrag:!0,snapAlign:L[0],dir:M[0],breakpointMode:N[0],breakpoints:void 0,i18n:D},j=e({name:"VueCarousel",props:{enabled:{default:I.enabled,type:Boolean},itemsToShow:{default:I.itemsToShow,type:Number},itemsToScroll:{default:I.itemsToScroll,type:Number},wrapAround:{default:I.wrapAround,type:Boolean},gap:{default:I.gap,type:Number},height:{default:I.height,type:[Number,String]},snapAlign:{default:I.snapAlign,validator:e=>L.includes(e)},transition:{default:I.transition,type:Number},breakpointMode:{default:I.breakpointMode,validator:e=>N.includes(e)},breakpoints:{default:I.breakpoints,type:Object},autoplay:{default:I.autoplay,type:Number},pauseAutoplayOnHover:{default:I.pauseAutoplayOnHover,type:Boolean},modelValue:{default:void 0,type:Number},mouseDrag:{default:I.mouseDrag,type:Boolean},touchDrag:{default:I.touchDrag,type:Boolean},dir:{type:String,default:I.dir,validator:e=>M.includes(e)},i18n:{default:I.i18n,type:Object}},emits:["init","drag","slide-start","loop","update:modelValue","slide-end","before-init"],setup(e,{slots:t,emit:n,expose:r}){var s;const c=o(null),v=o(null),y=p([]),C=o(0),k=l((()=>y.length)),L=l((()=>Object.assign(Object.assign(Object.assign({},I),e),{i18n:Object.assign(Object.assign({},I.i18n),e.i18n),breakpoints:void 0}))),N=m(Object.assign({},L.value)),M=o(null!==(s=e.modelValue)&&void 0!==s?s:0),D=o(0),O=l((()=>Math.ceil((k.value-1)/2))),j=l((()=>function({config:e,slidesCount:t}){const{snapAlign:n="center",wrapAround:i,itemsToShow:o=1}=e;return Math.max(function(){switch(i?"":n){case"start":return Math.ceil(t-o);case"center":case"center-odd":return t-Math.ceil((o-.5)/2);case"center-even":return t-Math.ceil(o/2);default:return Math.ceil(t-1)}}(),0)}({config:N,slidesCount:k.value}))),z=l((()=>function({config:e,slidesCount:t}){const{snapAlign:n="center",wrapAround:i,itemsToShow:o=1}=e;return i||o>t?0:Math.max(0,function(){switch(n){case"end":return Math.floor(o-1);case"center":case"center-odd":return Math.floor((o-1)/2);case"center-even":return Math.floor((o-2)/2)}return 0}())}({config:N,slidesCount:k.value})));let $=null,B=null,R=null;const V=l((()=>C.value+N.gap)),U=l((()=>{const e=N.dir||"ltr";return e in E?E[e]:e})),X=[],F=l((()=>["rtl","btt"].includes(U.value))),Y=l((()=>["ttb","btt"].includes(U.value))),P=l((()=>Math.ceil(N.itemsToShow)+1));function q(){var t;const n=("carousel"===L.value.breakpointMode?null===(t=c.value)||void 0===t?void 0:t.getBoundingClientRect().width:"undefined"!=typeof window?window.innerWidth:0)||0,i=Object.keys(e.breakpoints||{}).map((e=>Number(e))).sort(((e,t)=>+t-+e)),o={};i.some((t=>n>=t&&(Object.assign(o,e.breakpoints[t]),o.i18n&&Object.assign(o.i18n,L.value.i18n,e.breakpoints[t].i18n),!0))),Object.assign(N,L.value,o)}const H=A((()=>{q(),J(),K()})),G=l((()=>(N.itemsToShow-1)*N.gap)),W=p(new Set);function K(){if(!v.value)return;let e=1;if(W.forEach((t=>{const n=function(e){const{transform:t}=window.getComputedStyle(e);return t.split(/[(,)]/).slice(1,-1).map((e=>parseFloat(e)))}(t);6===n.length&&(e*=n[0])})),Y.value){if("auto"!==N.height){const e="string"==typeof N.height&&isNaN(parseInt(N.height))?v.value.getBoundingClientRect().height:parseInt(N.height);C.value=(e-G.value)/N.itemsToShow}}else{const t=v.value.getBoundingClientRect().width;C.value=(t/e-G.value)/N.itemsToShow}}function J(){!N.wrapAround&&k.value>0&&(M.value=S({val:M.value,max:j.value,min:z.value}))}let Q;g((()=>J())),g((()=>{K()}));const Z=e=>{const t=e.target;if(t&&W.add(t),!Q){const e=()=>{Q=requestAnimationFrame((()=>{K(),e()}))};e()}},ee=e=>{const t=e.target;t&&W.delete(t),Q&&0===W.size&&(cancelAnimationFrame(Q),K())};q(),u((()=>{"carousel"===L.value.breakpointMode&&q(),me(),document&&(document.addEventListener("animationstart",Z),document.addEventListener("animationend",ee)),c.value&&(R=new ResizeObserver(H),R.observe(c.value)),n("init")})),h((()=>{y.splice(0,y.length),X.splice(0,X.length),B&&clearTimeout(B),Q&&cancelAnimationFrame(Q),$&&clearInterval($),R&&(R.disconnect(),R=null),document&&(document.removeEventListener("keydown",se),document.removeEventListener("animationstart",Z),document.removeEventListener("animationend",ee)),c.value&&(c.value.removeEventListener("transitionend",K),c.value.removeEventListener("animationiteration",K))}));let te=!1;const ne={x:0,y:0},ie=m({x:0,y:0}),oe=o(!1),ae=o(!1),le=()=>{oe.value=!0},re=()=>{oe.value=!1},se=A((e=>{if(!e.ctrlKey)switch(e.key){case"ArrowLeft":case"ArrowUp":Y.value===e.key.endsWith("Up")&&(F.value?xe.next(!0):xe.prev(!0));break;case"ArrowRight":case"ArrowDown":Y.value===e.key.endsWith("Down")&&(F.value?xe.prev(!0):xe.next(!0))}}),200),ue=()=>{document.addEventListener("keydown",se)},ce=()=>{document.removeEventListener("keydown",se)};function de(e){const t=e.target.tagName;if(["INPUT","TEXTAREA","SELECT"].includes(t)||fe.value)return;if(te="touchstart"===e.type,!te&&(e.preventDefault(),0!==e.button))return;ne.x="touches"in e?e.touches[0].clientX:e.clientX,ne.y="touches"in e?e.touches[0].clientY:e.clientY;const n=te?"touchmove":"mousemove",i=te?"touchend":"mouseup";document.addEventListener(n,ve,{passive:!1}),document.addEventListener(i,pe,{passive:!0})}const ve=A((e=>{ae.value=!0;const t="touches"in e?e.touches[0].clientX:e.clientX,i="touches"in e?e.touches[0].clientY:e.clientY,o=t-ne.x,a=i-ne.y;ie.x=o,ie.y=a,n("drag",{deltaX:o,deltaY:a})}));function pe(){ve.cancel();const e=Y.value?"y":"x",t=F.value?-1:1,n=.4*Math.sign(ie[e]),i=Math.round(ie[e]/V.value+n)*t;if(i&&!te){const e=t=>{t.preventDefault(),window.removeEventListener("click",e)};window.addEventListener("click",e)}we(M.value-i),ie.x=0,ie.y=0,ae.value=!1;const o=te?"touchmove":"mousemove",a=te?"touchend":"mouseup";document.removeEventListener(o,ve),document.removeEventListener(a,pe)}function me(){!N.autoplay||N.autoplay<=0||($=setInterval((()=>{N.pauseAutoplayOnHover&&oe.value||be()}),N.autoplay))}function ge(){$&&(clearInterval($),$=null)}function he(){ge(),me()}const fe=o(!1);function we(e,t=!1){const i=N.wrapAround?e:S({val:e,max:j.value,min:z.value});if(M.value===i||!t&&fe.value)return;n("slide-start",{slidingToIndex:e,currentSlideIndex:M.value,prevSlideIndex:D.value,slidesCount:k.value}),ge(),fe.value=!0,D.value=M.value;const o=N.wrapAround?x({val:i,max:j.value,min:0}):i;M.value=i,o!==i&&Ce.pause(),n("update:modelValue",o),B=setTimeout((()=>{N.wrapAround&&o!==i&&(Ce.resume(),M.value=o,n("loop",{currentSlideIndex:M.value,slidingToIndex:e})),n("slide-end",{currentSlideIndex:M.value,prevSlideIndex:D.value,slidesCount:k.value}),fe.value=!1,he()}),N.transition)}function be(e=!1){we(M.value+N.itemsToScroll,e)}function Se(e=!1){we(M.value-N.itemsToScroll,e)}const xe={slideTo:we,next:be,prev:Se},ye=l((()=>function({config:e,currentSlide:t,slidesCount:n}){const{snapAlign:i="center",wrapAround:o,itemsToShow:a=1}=e,l=((e,t)=>{var n;return null!==(n={start:0,center:(t-1)/2,"center-odd":(t-1)/2,"center-even":(t-2)/2,end:t-1}[e])&&void 0!==n?n:0})(i,a);return o?x({val:t-l,max:n+a,min:0-a}):S({val:t-l,max:n-a,min:0})}({config:N,currentSlide:M.value,slidesCount:k.value}))),Ae=m({config:N,slidesCount:k,viewport:v,slides:y,scrolledIndex:ye,currentSlide:M,maxSlide:j,minSlide:z,slideSize:C,isVertical:Y,normalizedDir:U,nav:xe,isSliding:fe,registerSlide:(e,t)=>{t(y.length),y.push(e),X.push(t)},unregisterSlide:e=>{const t=y.indexOf(e);t>=0&&(y.splice(t,1),X.splice(t,1),X.slice(t).forEach(((e,n)=>e(t+n))))}});i(b,Ae),i("config",N),i("slidesCount",k),i("currentSlide",M),i("maxSlide",j),i("minSlide",z),i("slideSize",C),i("isVertical",Y),i("normalizeDir",U),i("nav",xe),i("isSliding",fe),a((()=>[L.value,e.breakpoints]),(()=>q()),{deep:!0}),a((()=>e.autoplay),(()=>he()));const Ce=a((()=>e.modelValue),(e=>{e!==M.value&&we(Number(e),!0)}));n("before-init");const _e=m({config:N,slidesCount:k,slideSize:C,currentSlide:M,maxSlide:j,minSlide:z,middleSlide:O});r({updateBreakpointsConfig:q,updateSlidesData:J,updateSlideSize:K,restartCarousel:function(){q(),J(),K(),he()},slideTo:we,next:be,prev:Se,nav:xe,data:_e});const Te=l((()=>Y.value&&C.value&&"auto"===N.height?`${C.value*N.itemsToShow+G.value}px`:"auto"!==N.height?"number"==typeof N.height||parseInt(N.height).toString()===N.height?`${N.height}px`:N.height:void 0)),ke=l((()=>{const e=N.wrapAround?P.value:0,t=F.value?-1:1,n=(ye.value+e)*V.value*t,i=Y.value?ie.y:ie.x;return`translate${Y.value?"Y":"X"}(${i-n}px)`}));return()=>{const e=t.default||t.slides,n=t.addons;let i=(null==e?void 0:e(_e))||[];if(!N.enabled||!i.length)return d("section",{ref:c,class:["carousel","is-disabled"]},i);const o=(null==n?void 0:n(_e))||[];if(N.wrapAround){const e=i.length>0?i[0].scopeId:null;f(e);const t=P.value,n=_({slides:y,position:"before",toShow:t}),o=_({slides:y,position:"after",toShow:t});w(),i=[...n,...i,...o]}const a=d("ol",{class:"carousel__track",style:{transform:ke.value,"transition-duration":fe.value?`${N.transition}ms`:void 0,gap:N.gap>0?`${N.gap}px`:void 0},onMousedownCapture:N.mouseDrag?de:null,onTouchstartPassiveCapture:N.touchDrag?de:null},i),l=d("div",{class:"carousel__viewport",ref:v},a);return d("section",{ref:c,class:["carousel",`is-${U.value}`,{"is-vertical":Y.value,"is-sliding":fe.value,"is-dragging":ae.value,"is-hover":oe.value}],style:{"--vc-trk-height":Te.value},dir:U.value,"aria-label":N.i18n.ariaGallery,tabindex:"0",onFocus:ue,onBlur:ce,onMouseenter:le,onMouseleave:re},[l,o,d(T)])}}});var z;!function(e){e.arrowUp="arrowUp",e.arrowDown="arrowDown",e.arrowRight="arrowRight",e.arrowLeft="arrowLeft"}(z||(z={}));const $=e=>`icon${e.charAt(0).toUpperCase()+e.slice(1)}`,B=e=>e&&e in z,R={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"},V=e({props:{name:{type:String,required:!0,validator:B},title:{type:String,default:e=>e.name?I.i18n[$(e.name)]:""}},setup(e){const t=n(b,null);return()=>{const n=e.name;if(!n||!B(n))return;const i=d("path",{d:R[n]}),o=(null==t?void 0:t.config.i18n[$(n)])||e.title||n,a=d("title",o);return d("svg",{class:"carousel__icon",viewBox:"0 0 24 24",role:"img","aria-label":o},[a,i])}}}),U=e({name:"CarouselNavigation",setup(e,{slots:t}){const i=n(b);if(!i)return null;const{next:o,prev:a}=t;return()=>{const{wrapAround:t,i18n:n}=i.config;return[d("button",{type:"button",class:["carousel__prev",!t&&i.currentSlide<=i.minSlide&&"carousel__prev--disabled",e.class],"aria-label":n.ariaPreviousSlide,title:n.ariaPreviousSlide,onClick:i.nav.prev},(null==a?void 0:a())||d(V,{name:{ltr:"arrowLeft",rtl:"arrowRight",ttb:"arrowUp",btt:"arrowDown"}[i.normalizedDir]})),d("button",{type:"button",class:["carousel__next",!t&&i.currentSlide>=i.maxSlide&&"carousel__next--disabled",e.class],"aria-label":n.ariaNextSlide,title:n.ariaNextSlide,onClick:i.nav.next},(null==o?void 0:o())||d(V,{name:{ltr:"arrowRight",rtl:"arrowLeft",ttb:"arrowDown",btt:"arrowUp"}[i.normalizedDir]}))]}}}),X=e({name:"CarouselPagination",setup(e){const t=n(b);if(!t)return null;const i=e=>x({val:t.currentSlide,max:t.maxSlide,min:0})===e;return()=>{var n,o;const a=[];for(let l=t.minSlide;l<=t.maxSlide;l++){const r=y(t.config.i18n.ariaNavigateToSlide,{slideNumber:l+1}),s=i(l),u=d("button",{type:"button",class:{"carousel__pagination-button":!0,"carousel__pagination-button--active":s},"aria-label":r,"aria-pressed":s,"aria-controls":null===(o=null===(n=t.slides[l])||void 0===n?void 0:n.exposed)||void 0===o?void 0:o.id,title:r,onClick:e.disableOnClick?void 0:()=>t.nav.slideTo(l)}),c=d("li",{class:"carousel__pagination-item",key:l},u);a.push(c)}return d("ol",{class:"carousel__pagination"},a)}}});export{N as BREAKPOINT_MODE_OPTIONS,j as Carousel,I as DEFAULT_CONFIG,E as DIR_MAP,M as DIR_OPTIONS,D as I18N_DEFAULT_CONFIG,V as Icon,O as NORMALIZED_DIR_OPTIONS,U as Navigation,X as Pagination,L as SNAP_ALIGN_OPTIONS,C as Slide,R as icons,k as injectCarousel};
/**
* Vue 3 Carousel 0.8.1
* Vue 3 Carousel 0.9.0
* (c) 2024

@@ -12,2 +12,301 @@ * @license MIT

// Use a symbol for inject provide to avoid any kind of collision with another lib
// https://vuejs.org/guide/components/provide-inject#working-with-symbol-keys
const injectCarousel$1 = Symbol('carousel');
/**
* Determines the minimum slide index based on the configuration.
*
* @param {GetMinSlideIndexArgs} args - The carousel configuration and slide count.
* @returns {number} The minimum slide index.
*/
function getMinSlideIndex({ config, slidesCount }) {
const { snapAlign = 'center', 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 snapAlign values to calculation logic
function snapAlignCalculations() {
switch (snapAlign) {
case 'end':
return Math.floor(itemsToShow - 1);
case 'center':
case 'center-odd':
return Math.floor((itemsToShow - 1) / 2);
case 'center-even':
return Math.floor((itemsToShow - 2) / 2);
}
return 0;
}
// Return the calculated offset or default to 0 for invalid snapAlign values
return Math.max(0, snapAlignCalculations());
}
/**
* 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 }) {
const { snapAlign = 'center', wrapAround, itemsToShow = 1 } = config;
// Map snapAlign values to calculation logic
function snapAlignCalculations() {
// If wrapAround is enabled, fallback to default which is the last slide
switch (wrapAround ? '' : snapAlign) {
case 'start':
return Math.ceil(slidesCount - itemsToShow);
case 'center':
case 'center-odd':
return slidesCount - Math.ceil((itemsToShow - 0.5) / 2);
case 'center-even':
return slidesCount - Math.ceil(itemsToShow / 2);
case 'end':
default:
return Math.ceil(slidesCount - 1);
}
}
// Return the result ensuring it's non-negative
return Math.max(snapAlignCalculations(), 0);
}
function getNumberInRange({ val, max, min }) {
if (max < min) {
return val;
}
return Math.min(Math.max(val, isNaN(min) ? val : min), isNaN(max) ? val : max);
}
function mapNumberToRange({ val, max, min = 0 }) {
const mod = max - min + 1;
return ((((val - min) % mod) + mod) % mod) + min;
}
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 = 'center', 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,
});
}
else {
return mapNumberToRange({
val: currentSlide - offset,
max: slidesCount + itemsToShow,
min: 0 - itemsToShow,
});
}
}
function i18nFormatter(string = '', values = {}) {
return Object.entries(values).reduce((acc, [key, value]) => acc.replace(`{${key}}`, String(value)), string);
}
/**
* Returns a throttled version of the function using requestAnimationFrame.
*
* @param fn - The function to throttle.
* @param ms - The number of milliseconds to wait for the throttled function to be called again
*/
function throttle(fn, ms = 0) {
let isThrottled = false;
let start = 0;
let frameId = null;
function throttled(...args) {
if (isThrottled)
return;
isThrottled = true;
const step = () => {
frameId = requestAnimationFrame((time) => {
const elapsed = time - start;
if (elapsed > ms) {
start = time;
fn(...args);
isThrottled = false;
}
else {
step();
}
});
};
step();
}
throttled.cancel = () => {
if (frameId) {
cancelAnimationFrame(frameId);
frameId = null;
isThrottled = false;
}
};
return throttled;
}
function getTransformValues(el) {
const { transform } = window.getComputedStyle(el);
//add sanity check
return transform
.split(/[(,)]/)
.slice(1, -1)
.map((v) => parseFloat(v));
}
const Slide = vue.defineComponent({
name: 'CarouselSlide',
props: {
isClone: {
type: Boolean,
default: false,
},
id: {
type: String,
default: (props) => (props.isClone ? undefined : vue.useId()),
},
index: {
type: Number,
default: 0,
},
},
setup(props, { slots, expose }) {
const carousel = vue.inject(injectCarousel$1);
vue.provide(injectCarousel$1, undefined); // Don't provide for nested slides
if (!carousel) {
return null; // Don't render, let vue warn about the missing provide
}
const index = vue.ref(props.index);
vue.watch(() => props.index, (i) => index.value = i);
const isActive = vue.computed(() => index.value === carousel.currentSlide);
const isPrev = vue.computed(() => index.value === carousel.currentSlide - 1);
const isNext = vue.computed(() => index.value === carousel.currentSlide + 1);
const isVisible = vue.computed(() => index.value >= Math.floor(carousel.scrolledIndex) &&
index.value < Math.ceil(carousel.scrolledIndex) + carousel.config.itemsToShow);
const slideStyle = vue.computed(() => {
const dimension = carousel.config.gap > 0 && carousel.config.itemsToShow > 1
? `calc(${100 / carousel.config.itemsToShow}% - ${(carousel.config.gap * (carousel.config.itemsToShow - 1)) /
carousel.config.itemsToShow}px)`
: `${100 / carousel.config.itemsToShow}%`;
return carousel.isVertical ? { height: dimension } : { width: dimension };
});
const instance = vue.getCurrentInstance();
if (!props.isClone) {
carousel.registerSlide(instance, (resolvedIndex) => (index.value = resolvedIndex));
vue.onUnmounted(() => {
carousel.unregisterSlide(instance);
});
}
else {
const makeUnfocusable = (node) => {
[
...((node === null || node === void 0 ? void 0 : node.el)
? node.el.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])')
: []),
]
.filter((el) => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'))
.forEach((el) => el.setAttribute('tabindex', '-1'));
};
// Prevent cloned slides from being focusable
vue.onMounted(() => {
makeUnfocusable(instance.vnode);
});
vue.onUpdated(() => {
makeUnfocusable(instance.vnode);
});
}
expose({
id: props.id,
});
return () => {
var _a, _b;
if (!carousel.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': carousel.isSliding,
},
onFocusin: () => {
// Prevent the viewport being scrolled by the focus
if (carousel.viewport) {
carousel.viewport.scrollLeft = 0;
}
carousel.nav.slideTo(index.value);
},
id: props.isClone ? undefined : props.id,
'aria-hidden': props.isClone || undefined,
}, (_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: carousel.isSliding,
isVisible: isVisible.value,
}));
};
},
});
function createCloneSlides({ slides, position, toShow }) {
const clones = [];
const isBefore = position === 'before';
const start = isBefore ? -toShow : 0;
const end = isBefore ? 0 : toShow;
for (let i = start; i < end; i++) {
const index = isBefore ? i : slides.length > 0 ? i + slides.length : i + 99999;
const props = {
index,
isClone: true,
key: `clone-${position}-${i}`,
};
clones.push(slides.length > 0
? vue.cloneVNode(slides[(i + slides.length) % slides.length].vnode, props)
: vue.h(Slide, props));
}
return clones;
}
const ARIA = vue.defineComponent({
name: 'CarouselAria',
setup() {
const carousel = vue.inject(injectCarousel$1);
if (!carousel) {
return;
}
return () => vue.h('div', {
class: ['carousel__liveregion', 'carousel__sr-only'],
'aria-live': 'polite',
'aria-atomic': 'true',
}, i18nFormatter(carousel.config.i18n['itemXofY'], {
currentSlide: carousel.currentSlide + 1,
slidesCount: carousel.slidesCount,
}));
},
});
// Use a symbol for inject provide to avoid any kind of collision with another lib
// https://vuejs.org/guide/components/provide-inject#working-with-symbol-keys
const injectCarousel = Symbol('carousel');
const SNAP_ALIGN_OPTIONS = ['center', 'start', 'end', 'center-even', 'center-odd'];

@@ -36,2 +335,9 @@ const BREAKPOINT_MODE_OPTIONS = ['viewport', 'carousel'];

};
const DIR_MAP = {
'left-to-right': 'ltr',
'right-to-left': 'rtl',
'top-to-bottom': 'ttb',
'bottom-to-top': 'btt',
};
const NORMALIZED_DIR_OPTIONS = Object.values(DIR_MAP);
const DEFAULT_CONFIG = {

@@ -139,2 +445,3 @@ enabled: true,

dir: {
type: String,
default: DEFAULT_CONFIG.dir,

@@ -153,168 +460,4 @@ validator(value) {

/**
* 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',
const Carousel = vue.defineComponent({
name: 'VueCarousel',
props: carouselProps,

@@ -334,5 +477,5 @@ emits: [

const viewport = vue.ref(null);
const slides = vue.ref([]);
const slides = vue.shallowReactive([]);
const slideSize = vue.ref(0);
const slidesCount = vue.ref(0);
const slidesCount = vue.computed(() => slides.length);
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 })));

@@ -344,5 +487,9 @@ // current active config

const prevSlideIndex = vue.ref(0);
const middleSlideIndex = vue.ref(0);
const maxSlideIndex = vue.ref(0);
const minSlideIndex = vue.ref(0);
const middleSlideIndex = vue.computed(() => Math.ceil((slidesCount.value - 1) / 2));
const maxSlideIndex = vue.computed(() => {
return getMaxSlideIndex({ config, slidesCount: slidesCount.value });
});
const minSlideIndex = vue.computed(() => {
return getMinSlideIndex({ config, slidesCount: slidesCount.value });
});
let autoplayTimer = null;

@@ -352,35 +499,42 @@ let transitionTimer = 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 normalizedDir = vue.computed(() => {
const dir = config.dir || 'ltr';
return dir in DIR_MAP ? DIR_MAP[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);
const indexCbs = [];
const registerSlide = (slide, indexCb) => {
indexCb(slides.length);
slides.push(slide);
indexCbs.push(indexCb);
};
const unregisterSlide = (slide) => {
const found = slides.indexOf(slide);
if (found >= 0) {
slides.splice(found, 1);
indexCbs.splice(found, 1);
// Update indexes after the one that was removed
indexCbs.slice(found).forEach((cb, index) => cb(found + index));
}
};
const isReversed = vue.computed(() => ['rtl', 'btt'].includes(normalizedDir.value));
const isVertical = vue.computed(() => ['ttb', 'btt'].includes(normalizedDir.value));
const clonedSlidesCount = vue.computed(() => Math.ceil(config.itemsToShow) + 1);
function updateBreakpointsConfig() {
var _a;
// Determine the width source based on the 'breakpointMode' config
const widthSource = (config.breakpointMode === 'carousel'
const widthSource = (fallbackConfig.value.breakpointMode === 'carousel'
? (_a = root.value) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width
: window.innerWidth) || 0;
: typeof window !== 'undefined'
? window.innerWidth
: 0) || 0;
const breakpointsArray = Object.keys(props.breakpoints || {})
.map((key) => Number(key))
.sort((a, b) => +b - +a);
let newConfig = Object.assign({}, fallbackConfig.value);
const newConfig = {};
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]);
Object.assign(newConfig, props.breakpoints[breakpoint]);
if (newConfig.i18n) {
Object.assign(newConfig.i18n, fallbackConfig.value.i18n, props.breakpoints[breakpoint].i18n);
}
return true;

@@ -390,9 +544,11 @@ }

});
Object.assign(config, newConfig);
Object.assign(config, fallbackConfig.value, newConfig);
}
const handleResize = debounce(() => {
const handleResize = throttle(() => {
updateBreakpointsConfig();
updateSlidesData();
updateSlideSize();
}, 16);
});
const totalGap = vue.computed(() => (config.itemsToShow - 1) * config.gap);
const transformElements = vue.shallowReactive(new Set());
/**

@@ -404,20 +560,25 @@ * Setup functions

return;
const rect = viewport.value.getBoundingClientRect();
// Calculate the total gap space
const totalGap = (config.itemsToShow - 1) * config.gap;
let multiplierWidth = 1;
transformElements.forEach((el) => {
const transformArr = getTransformValues(el);
if (transformArr.length === 6) {
multiplierWidth *= transformArr[0];
}
});
// Calculate size based on orientation
if (isVertical.value) {
slideSize.value = (rect.height - totalGap) / config.itemsToShow;
if (config.height !== 'auto') {
const height = typeof config.height === 'string' && isNaN(parseInt(config.height))
? viewport.value.getBoundingClientRect().height
: parseInt(config.height);
slideSize.value = (height - totalGap.value) / config.itemsToShow;
}
}
else {
slideSize.value = (rect.width - totalGap) / config.itemsToShow;
const width = viewport.value.getBoundingClientRect().width;
slideSize.value = (width / multiplierWidth - totalGap.value) / 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) {
if (!config.wrapAround && slidesCount.value > 0) {
currentSlideIndex.value = getNumberInRange({

@@ -430,11 +591,45 @@ val: currentSlideIndex.value,

}
vue.watchEffect(() => updateSlidesData());
vue.watchEffect(() => {
// Call updateSlideSize when viewport is ready and track deps
updateSlideSize();
});
let animationInterval;
const setAnimationInterval = (event) => {
const target = event.target;
if (target) {
transformElements.add(target);
}
if (!animationInterval) {
const stepAnimation = () => {
animationInterval = requestAnimationFrame(() => {
updateSlideSize();
stepAnimation();
});
};
stepAnimation();
}
};
const finishAnimation = (event) => {
const target = event.target;
if (target) {
transformElements.delete(target);
}
if (animationInterval && transformElements.size === 0) {
cancelAnimationFrame(animationInterval);
updateSlideSize();
}
};
updateBreakpointsConfig();
vue.onMounted(() => {
vue.nextTick(() => updateSlideSize());
// Overcome some edge cases
setTimeout(() => updateSlideSize(), 1000);
updateBreakpointsConfig();
if (fallbackConfig.value.breakpointMode === 'carousel') {
updateBreakpointsConfig();
}
initAutoplay();
window.addEventListener('resize', handleResize, { passive: true });
resizeObserver = new ResizeObserver(handleResize);
if (document) {
document.addEventListener('animationstart', setAnimationInterval);
document.addEventListener('animationend', finishAnimation);
}
if (root.value) {
resizeObserver = new ResizeObserver(handleResize);
resizeObserver.observe(root.value);

@@ -444,16 +639,28 @@ }

});
vue.onUnmounted(() => {
vue.onBeforeUnmount(() => {
// Empty the slides before they unregister for better performance
slides.splice(0, slides.length);
indexCbs.splice(0, indexCbs.length);
if (transitionTimer) {
clearTimeout(transitionTimer);
}
if (animationInterval) {
cancelAnimationFrame(animationInterval);
}
if (autoplayTimer) {
clearInterval(autoplayTimer);
}
if (resizeObserver && root.value) {
resizeObserver.unobserve(root.value);
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
window.removeEventListener('resize', handleResize, {
passive: true,
});
if (document) {
document.removeEventListener('keydown', handleArrowKeys);
document.removeEventListener('animationstart', setAnimationInterval);
document.removeEventListener('animationend', finishAnimation);
}
if (root.value) {
root.value.removeEventListener('transitionend', updateSlideSize);
root.value.removeEventListener('animationiteration', updateSlideSize);
}
});

@@ -474,2 +681,36 @@ /**

};
const handleArrowKeys = throttle((event) => {
if (event.ctrlKey)
return;
switch (event.key) {
case 'ArrowLeft':
case 'ArrowUp':
if (isVertical.value === event.key.endsWith('Up')) {
if (isReversed.value) {
nav.next(true);
}
else {
nav.prev(true);
}
}
break;
case 'ArrowRight':
case 'ArrowDown':
if (isVertical.value === event.key.endsWith('Down')) {
if (isReversed.value) {
nav.prev(true);
}
else {
nav.next(true);
}
}
break;
}
}, 200);
const handleFocus = () => {
document.addEventListener('keydown', handleArrowKeys);
};
const handleBlur = () => {
document.removeEventListener('keydown', handleArrowKeys);
};
function handleDragStart(event) {

@@ -492,4 +733,4 @@ // Prevent drag initiation on input elements or if already sliding

// Initialize start positions for the drag
startPosition.x = isTouch ? event.touches[0].clientX : event.clientX;
startPosition.y = isTouch ? event.touches[0].clientY : event.clientY;
startPosition.x = 'touches' in event ? event.touches[0].clientX : event.clientX;
startPosition.y = 'touches' in event ? event.touches[0].clientY : event.clientY;
// Attach event listeners for dragging and drag end

@@ -504,4 +745,4 @@ const moveEvent = isTouch ? 'touchmove' : 'mousemove';

// 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;
const currentX = 'touches' in event ? event.touches[0].clientX : event.clientX;
const currentY = 'touches' in event ? event.touches[0].clientY : event.clientY;
// Calculate deltas for X and Y axes

@@ -517,5 +758,6 @@ const deltaX = currentX - startPosition.x;

function handleDragEnd() {
handleDragging.cancel();
// Determine the active axis and direction multiplier
const dragAxis = isVertical.value ? 'y' : 'x';
const directionMultiplier = ['rtl', 'btt'].includes(normalizeDir.value) ? -1 : 1;
const directionMultiplier = isReversed.value ? -1 : 1;
// Calculate dragged slides with a tolerance to account for incomplete drags

@@ -559,3 +801,3 @@ const tolerance = Math.sign(dragged[dragAxis]) * 0.4; // Smooth out small drags

}
function resetAutoplay() {
function stopAutoplay() {
if (autoplayTimer) {

@@ -565,2 +807,5 @@ clearInterval(autoplayTimer);

}
}
function resetAutoplay() {
stopAutoplay();
initAutoplay();

@@ -572,3 +817,3 @@ }

const isSliding = vue.ref(false);
function slideTo(slideIndex) {
function slideTo(slideIndex, skipTransition = false) {
const currentVal = config.wrapAround

@@ -581,3 +826,4 @@ ? slideIndex

});
if (currentSlideIndex.value === currentVal || isSliding.value) {
if (currentSlideIndex.value === currentVal ||
(!skipTransition && isSliding.value)) {
return;

@@ -591,13 +837,21 @@ }

});
stopAutoplay();
isSliding.value = true;
prevSlideIndex.value = currentSlideIndex.value;
const mappedNumber = config.wrapAround
? mapNumberToRange({
val: currentVal,
max: maxSlideIndex.value,
min: 0,
})
: currentVal;
currentSlideIndex.value = currentVal;
if (mappedNumber !== currentVal) {
modelWatcher.pause();
}
emit('update:modelValue', mappedNumber);
transitionTimer = setTimeout(() => {
if (config.wrapAround) {
const mappedNumber = mapNumberToRange({
val: currentVal,
max: maxSlideIndex.value,
min: 0,
});
if (mappedNumber !== currentSlideIndex.value) {
if (mappedNumber !== currentVal) {
modelWatcher.resume();
currentSlideIndex.value = mappedNumber;

@@ -610,3 +864,2 @@ emit('loop', {

}
emit('update:modelValue', currentSlideIndex.value);
emit('slide-end', {

@@ -621,9 +874,41 @@ currentSlideIndex: currentSlideIndex.value,

}
function next() {
slideTo(currentSlideIndex.value + config.itemsToScroll);
function next(skipTransition = false) {
slideTo(currentSlideIndex.value + config.itemsToScroll, skipTransition);
}
function prev() {
slideTo(currentSlideIndex.value - config.itemsToScroll);
function prev(skipTransition = false) {
slideTo(currentSlideIndex.value - config.itemsToScroll, skipTransition);
}
const nav = { slideTo, next, prev };
const scrolledIndex = vue.computed(() => getScrolledIndex({
config,
currentSlide: currentSlideIndex.value,
slidesCount: slidesCount.value,
}));
const provided = vue.reactive({
config,
slidesCount,
viewport,
slides,
scrolledIndex,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
slideSize,
isVertical,
normalizedDir,
nav,
isSliding,
registerSlide,
unregisterSlide,
});
vue.provide(injectCarousel$1, provided);
/** @deprecated provides */
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', normalizedDir);
vue.provide('nav', nav);

@@ -638,15 +923,14 @@ vue.provide('isSliding', isSliding);

// Update the carousel on props change
vue.watch(() => (Object.assign({}, props)), restartCarousel, { deep: true });
vue.watch(() => [fallbackConfig.value, props.breakpoints], () => updateBreakpointsConfig(), { deep: true });
vue.watch(() => props.autoplay, () => resetAutoplay());
// Handle changing v-model value
vue.watch(() => props['modelValue'], (val) => {
const modelWatcher = vue.watch(() => props.modelValue, (val) => {
if (val === currentSlideIndex.value) {
return;
}
slideTo(Number(val));
slideTo(Number(val), true);
});
// Handel when slides added/removed
vue.watch(slidesCount, updateSlidesData);
// Init carousel
emit('before-init');
const data = {
const data = vue.reactive({
config,

@@ -659,3 +943,3 @@ slidesCount,

middleSlide: middleSlideIndex,
};
});
expose({

@@ -672,2 +956,13 @@ updateBreakpointsConfig,

});
const trackHeight = vue.computed(() => {
if (isVertical.value && slideSize.value && config.height === 'auto') {
return `${slideSize.value * config.itemsToShow + totalGap.value}px`;
}
return config.height !== 'auto'
? typeof config.height === 'number' ||
parseInt(config.height).toString() === config.height
? `${config.height}px`
: config.height
: undefined;
});
/**

@@ -678,13 +973,9 @@ * Track style

// 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;
const cloneOffset = config.wrapAround ? clonedSlidesCount.value : 0;
// Determine direction multiplier for orientation
const isReverseDirection = ['rtl', 'btt'].includes(normalizeDir.value);
const directionMultiplier = isReverseDirection ? -1 : 1;
const directionMultiplier = isReversed.value ? -1 : 1;
// Calculate the total offset for slide transformation
const totalOffset = (scrolledIndex + cloneOffset) * effectiveSlideSize.value * directionMultiplier;
const totalOffset = (scrolledIndex.value + cloneOffset) *
effectiveSlideSize.value *
directionMultiplier;
// Include user drag interaction offset

@@ -696,38 +987,23 @@ const dragOffset = isVertical.value ? dragged.y : dragged.x;

});
const slotSlides = slots.default || slots.slides;
const slotAddons = slots.addons;
const slotsProps = vue.reactive(data);
return () => {
if (!config.enabled) {
const slotSlides = slots.default || slots.slides;
const slotAddons = slots.addons;
let output = (slotSlides === null || slotSlides === void 0 ? void 0 : slotSlides(data)) || [];
if (!config.enabled || !output.length) {
return vue.h('section', {
ref: root,
class: ['carousel', 'is-disabled'],
}, slotSlides === null || slotSlides === void 0 ? void 0 : slotSlides());
}, output);
}
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;
const addonsElements = (slotAddons === null || slotAddons === void 0 ? void 0 : slotAddons(data)) || [];
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];
// Ensure scoped CSS tracks properly
const scopeId = output.length > 0 ? output[0].scopeId : null;
vue.pushScopeId(scopeId);
const toShow = clonedSlidesCount.value;
const slidesBefore = createCloneSlides({ slides, position: 'before', toShow });
const slidesAfter = createCloneSlides({ slides, position: 'after', toShow });
vue.popScopeId();
output = [...slidesBefore, ...output, ...slidesAfter];
}
slides.value = slidesElements;
slidesCount.value = Math.max(slidesElements.length, 1);
const trackEl = vue.h('ol', {

@@ -737,4 +1013,4 @@ class: 'carousel__track',

transform: trackTransform.value,
transition: `${isSliding.value ? config.transition : 0}ms`,
gap: `${config.gap}px`,
'transition-duration': isSliding.value ? `${config.transition}ms` : undefined,
gap: config.gap > 0 ? `${config.gap}px` : undefined,
},

@@ -749,3 +1025,3 @@ onMousedownCapture: config.mouseDrag ? handleDragStart : null,

'carousel',
`is-${normalizeDir.value}`,
`is-${normalizedDir.value}`,
{

@@ -759,10 +1035,12 @@ 'is-vertical': isVertical.value,

style: {
'--vc-trk-height': `${typeof config.height === 'number' ? `${config.height}px` : config.height}`,
'--vc-trk-height': trackHeight.value,
},
dir: normalizeDir.value,
dir: normalizedDir.value,
'aria-label': config.i18n['ariaGallery'],
tabindex: '0',
onFocus: handleFocus,
onBlur: handleBlur,
onMouseenter: handleMouseEnter,
onMouseleave: handleMouseLeave,
}, [viewPortEl, addonsElements, vue.h(ARIAComponent)]);
}, [viewPortEl, addonsElements, vue.h(ARIA)]);
};

@@ -779,2 +1057,10 @@ },

})(IconName || (IconName = {}));
function isIconName(candidate) {
return candidate in IconName;
}
const iconI18n = (name) => `icon${name.charAt(0).toUpperCase() + name.slice(1)}`;
const validateIconName = (value) => {
return value && isIconName(value);
};
const icons = {

@@ -786,170 +1072,129 @@ arrowUp: 'M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z',

};
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',
const Icon = vue.defineComponent({
props: {
index: {
type: Number,
default: 1,
name: {
type: String,
required: true,
validator: validateIconName,
},
isClone: {
type: Boolean,
default: false,
title: {
type: String,
default: (props) => props.name ? DEFAULT_CONFIG.i18n[iconI18n(props.name)] : '',
},
},
setup(props) {
const carousel = vue.inject(injectCarousel$1, null);
return () => {
const iconName = props.name;
if (!iconName || !validateIconName(iconName))
return;
const path = icons[iconName];
const pathEl = vue.h('path', { d: path });
const iconTitle = (carousel === null || carousel === void 0 ? void 0 : carousel.config.i18n[iconI18n(iconName)]) || 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]);
};
},
});
const Navigation = vue.defineComponent({
name: 'CarouselNavigation',
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: '' };
});
const carousel = vue.inject(injectCarousel$1);
if (!carousel) {
return null; // Don't render, let vue warn about the missing provide
}
const { next: slotNext, prev: slotPrev } = slots;
const getPrevIcon = () => {
const directionIcons = {
ltr: 'arrowLeft',
rtl: 'arrowRight',
ttb: 'arrowUp',
btt: 'arrowDown',
};
return directionIcons[carousel.normalizedDir];
};
const getNextIcon = () => {
const directionIcons = {
ltr: 'arrowRight',
rtl: 'arrowLeft',
ttb: 'arrowDown',
btt: 'arrowUp',
};
return directionIcons[carousel.normalizedDir];
};
return () => {
const { wrapAround, i18n } = carousel.config;
const prevButton = vue.h('button', {
type: 'button',
class: [
'carousel__prev',
!wrapAround &&
carousel.currentSlide <= carousel.minSlide &&
'carousel__prev--disabled',
props.class,
],
'aria-label': i18n['ariaPreviousSlide'],
title: i18n['ariaPreviousSlide'],
onClick: carousel.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 &&
carousel.currentSlide >= carousel.maxSlide &&
'carousel__next--disabled',
props.class,
],
'aria-label': i18n['ariaNextSlide'],
title: i18n['ariaNextSlide'],
onClick: carousel.nav.next,
}, (slotNext === null || slotNext === void 0 ? void 0 : slotNext()) || vue.h(Icon, { name: getNextIcon() }));
return [prevButton, nextButton];
};
},
});
const Pagination = vue.defineComponent({
name: 'CarouselPagination',
setup(props) {
const carousel = vue.inject(injectCarousel$1);
if (!carousel) {
return null; // Don't render, let vue warn about the missing provide
}
const isActive = (slide) => mapNumberToRange({
val: carousel.currentSlide,
max: carousel.maxSlide,
min: 0,
}) === slide;
return () => {
var _a, _b;
if (!config.enabled) {
return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
const children = [];
for (let slide = carousel.minSlide; slide <= carousel.maxSlide; slide++) {
const buttonLabel = i18nFormatter(carousel.config.i18n.ariaNavigateToSlide, {
slideNumber: slide + 1,
});
const active = isActive(slide);
const button = vue.h('button', {
type: 'button',
class: {
'carousel__pagination-button': true,
'carousel__pagination-button--active': active,
},
'aria-label': buttonLabel,
'aria-pressed': active,
'aria-controls': (_b = (_a = carousel.slides[slide]) === null || _a === void 0 ? void 0 : _a.exposed) === null || _b === void 0 ? void 0 : _b.id,
title: buttonLabel,
onClick: props.disableOnClick ? undefined : () => carousel.nav.slideTo(slide),
});
const item = vue.h('li', { class: 'carousel__pagination-item', key: slide }, button);
children.push(item);
}
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,
}));
return vue.h('ol', { class: 'carousel__pagination' }, children);
};

@@ -959,8 +1204,17 @@ },

exports.BREAKPOINT_MODE_OPTIONS = BREAKPOINT_MODE_OPTIONS;
exports.Carousel = Carousel;
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
exports.DIR_MAP = DIR_MAP;
exports.DIR_OPTIONS = DIR_OPTIONS;
exports.I18N_DEFAULT_CONFIG = I18N_DEFAULT_CONFIG;
exports.Icon = Icon;
exports.NORMALIZED_DIR_OPTIONS = NORMALIZED_DIR_OPTIONS;
exports.Navigation = Navigation;
exports.Pagination = Pagination;
exports.SNAP_ALIGN_OPTIONS = SNAP_ALIGN_OPTIONS;
exports.Slide = Slide;
exports.icons = icons;
exports.injectCarousel = injectCarousel;
}));
/**
* Vue 3 Carousel 0.8.1
* Vue 3 Carousel 0.9.0
* (c) 2024
* @license MIT
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).VueCarousel={},e.Vue)}(this,(function(e,t){"use strict";const n=["center","start","end","center-even","center-odd"],i=["viewport","carousel"],a=["ltr","left-to-right","rtl","right-to-left","ttb","top-to-bottom","btt","bottom-to-top"],l={enabled:!0,itemsToShow:1,itemsToScroll:1,modelValue:0,transition:300,autoplay:0,gap:0,height:"auto",wrapAround:!1,pauseAutoplayOnHover:!1,mouseDrag:!0,touchDrag:!0,snapAlign:n[0],dir:a[0],breakpointMode:i[0],breakpoints:void 0,i18n:{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"}},o={enabled:{default:l.enabled,type:Boolean},itemsToShow:{default:l.itemsToShow,type:Number},itemsToScroll:{default:l.itemsToScroll,type:Number},wrapAround:{default:l.wrapAround,type:Boolean},gap:{default:l.gap,type:Number},height:{default:l.height,type:[Number,String]},snapAlign:{default:l.snapAlign,validator:e=>n.includes(e)},transition:{default:l.transition,type:Number},breakpointMode:{default:l.breakpointMode,validator:e=>i.includes(e)},breakpoints:{default:l.breakpoints,type:Object},autoplay:{default:l.autoplay,type:Number},pauseAutoplayOnHover:{default:l.pauseAutoplayOnHover,type:Boolean},modelValue:{default:void 0,type:Number},mouseDrag:{default:l.mouseDrag,type:Boolean},touchDrag:{default:l.touchDrag,type:Boolean},dir:{default:l.dir,validator:e=>a.includes(e)},i18n:{default:l.i18n,type:Object}};function r({val:e,max:t,min:n}){return t<n?e:Math.min(Math.max(e,n),t)}function u(e){return e?e.reduce(((e,n)=>{var i;return n.type===t.Fragment?[...e,...u(n.children)]:"CarouselSlide"===(null===(i=n.type)||void 0===i?void 0:i.name)?[...e,n]:e}),[]):[]}function s({val:e,max:t,min:n=0}){const i=t-n+1;return((e-n)%i+i)%i+n}function c(e="",t={}){return Object.entries(t).reduce(((e,[t,n])=>e.replace(`{${t}}`,String(n))),e)}var d,v=t.defineComponent({name:"ARIA",setup(){const e=t.inject("config",t.reactive(Object.assign({},l))),n=t.inject("currentSlide",t.ref(0)),i=t.inject("slidesCount",t.ref(0));return()=>t.h("div",{class:["carousel__liveregion","carousel__sr-only"],"aria-live":"polite","aria-atomic":"true"},c(e.i18n.itemXofY,{currentSlide:n.value+1,slidesCount:i.value}))}}),p=t.defineComponent({name:"Carousel",props:o,emits:["init","drag","slide-start","loop","update:modelValue","slide-end","before-init"],setup(e,{slots:n,emit:i,expose:a}){var o;const c=t.ref(null),d=t.ref(null),p=t.ref([]),f=t.ref(0),m=t.ref(0),g=t.computed((()=>Object.assign(Object.assign(Object.assign({},l),e),{i18n:Object.assign(Object.assign({},l.i18n),e.i18n),breakpoints:void 0}))),h=t.reactive(Object.assign({},g.value)),b=t.ref(null!==(o=e.modelValue)&&void 0!==o?o:0),w=t.ref(0),S=t.ref(0),x=t.ref(0),y=t.ref(0);let j=null,A=null,_=null;const C=t.computed((()=>f.value+h.gap)),T=t.computed((()=>{const e=h.dir||"lrt";return{"left-to-right":"ltr","right-to-left":"rtl","top-to-bottom":"ttb","bottom-to-top":"btt"}[e]||e})),M=t.computed((()=>["ttb","btt"].includes(T.value)));function N(){var t;const n=("carousel"===h.breakpointMode?null===(t=c.value)||void 0===t?void 0:t.getBoundingClientRect().width:window.innerWidth)||0,i=Object.keys(e.breakpoints||{}).map((e=>Number(e))).sort(((e,t)=>+t-+e));let a=Object.assign({},g.value);i.some((t=>{var i;return n>=t&&(a=Object.assign(Object.assign({},a),null===(i=e.breakpoints)||void 0===i?void 0:i[t]),!0)})),Object.assign(h,a)}t.provide("config",h),t.provide("slidesCount",m),t.provide("currentSlide",b),t.provide("maxSlide",x),t.provide("minSlide",y),t.provide("slideSize",f),t.provide("isVertical",M),t.provide("normalizeDir",T);const O=function(e,t){let n;return function(...i){n&&clearTimeout(n),n=setTimeout((()=>{e(...i),n=null}),t)}}((()=>{N(),L(),k()}),16);function k(){if(!d.value)return;const e=d.value.getBoundingClientRect(),t=(h.itemsToShow-1)*h.gap;M.value?f.value=(e.height-t)/h.itemsToShow:f.value=(e.width-t)/h.itemsToShow}function L(){m.value<=0||(S.value=Math.ceil((m.value-1)/2),x.value=function({config:e,slidesCount:t}){var n;const{snapAlign:i="N/A",wrapAround:a,itemsToShow:l=1}=e;if(a)return Math.max(t-1,0);const o=null!==(n={start:Math.ceil(t-l),end:Math.ceil(t-1),center:t-Math.ceil((l-.5)/2),"center-odd":t-Math.ceil((l-.5)/2),"center-even":t-Math.ceil(l/2)}[i])&&void 0!==n?n:0;return Math.max(o,0)}({config:h,slidesCount:m.value}),y.value=function({config:e,slidesCount:t}){var n;const{snapAlign:i="N/A",wrapAround:a,itemsToShow:l=1}=e;return a||l>t?0:null!==(n={start:0,end:Math.floor(l-1),center:Math.floor((l-1)/2),"center-odd":Math.floor((l-1)/2),"center-even":Math.floor((l-2)/2)}[i])&&void 0!==n?n:0}({config:h,slidesCount:m.value}),h.wrapAround||(b.value=r({val:b.value,max:x.value,min:y.value})))}t.onMounted((()=>{t.nextTick((()=>k())),setTimeout((()=>k()),1e3),N(),Y(),window.addEventListener("resize",O,{passive:!0}),_=new ResizeObserver(O),c.value&&_.observe(c.value),i("init")})),t.onUnmounted((()=>{A&&clearTimeout(A),j&&clearInterval(j),_&&c.value&&(_.unobserve(c.value),_=null),window.removeEventListener("resize",O,{passive:!0})}));let D=!1;const I={x:0,y:0},z=t.reactive({x:0,y:0}),E=t.ref(!1),V=t.ref(!1),$=()=>{E.value=!0},R=()=>{E.value=!1};function B(e){const t=e.target.tagName;if(["INPUT","TEXTAREA","SELECT"].includes(t)||H.value)return;if(D="touchstart"===e.type,!D&&(e.preventDefault(),0!==e.button))return;I.x=D?e.touches[0].clientX:e.clientX,I.y=D?e.touches[0].clientY:e.clientY;const n=D?"touchmove":"mousemove",i=D?"touchend":"mouseup";document.addEventListener(n,U,{passive:!1}),document.addEventListener(i,X,{passive:!0})}const U=function(e){let t=!1;return function(...n){t||(t=!0,requestAnimationFrame((()=>{e.apply(this,n),t=!1})))}}((e=>{V.value=!0;const t=D?e.touches[0].clientX:e.clientX,n=D?e.touches[0].clientY:e.clientY,a=t-I.x,l=n-I.y;z.x=a,z.y=l,i("drag",{deltaX:a,deltaY:l})}));function X(){const e=M.value?"y":"x",t=["rtl","btt"].includes(T.value)?-1:1,n=.4*Math.sign(z[e]),i=Math.round(z[e]/C.value+n)*t;if(i&&!D){const e=t=>{t.preventDefault(),window.removeEventListener("click",e)};window.addEventListener("click",e)}G(b.value-i),z.x=0,z.y=0,V.value=!1;const a=D?"touchmove":"mousemove",l=D?"touchend":"mouseup";document.removeEventListener(a,U),document.removeEventListener(l,X)}function Y(){!h.autoplay||h.autoplay<=0||(j=setInterval((()=>{h.pauseAutoplayOnHover&&E.value||q()}),h.autoplay))}function P(){j&&(clearInterval(j),j=null),Y()}const H=t.ref(!1);function G(e){const t=h.wrapAround?e:r({val:e,max:x.value,min:y.value});b.value===t||H.value||(i("slide-start",{slidingToIndex:e,currentSlideIndex:b.value,prevSlideIndex:w.value,slidesCount:m.value}),H.value=!0,w.value=b.value,b.value=t,A=setTimeout((()=>{if(h.wrapAround){const n=s({val:t,max:x.value,min:0});n!==b.value&&(b.value=n,i("loop",{currentSlideIndex:b.value,slidingToIndex:e}))}i("update:modelValue",b.value),i("slide-end",{currentSlideIndex:b.value,prevSlideIndex:w.value,slidesCount:m.value}),H.value=!1,P()}),h.transition))}function q(){G(b.value+h.itemsToScroll)}function F(){G(b.value-h.itemsToScroll)}const W={slideTo:G,next:q,prev:F};function J(){N(),L(),k(),P()}t.provide("nav",W),t.provide("isSliding",H),t.watch((()=>Object.assign({},e)),J,{deep:!0}),t.watch((()=>e.modelValue),(e=>{e!==b.value&&G(Number(e))})),t.watch(m,L),i("before-init");const K={config:h,slidesCount:m,slideSize:f,currentSlide:b,maxSlide:x,minSlide:y,middleSlide:S};a({updateBreakpointsConfig:N,updateSlidesData:L,updateSlideSize:k,restartCarousel:J,slideTo:G,next:q,prev:F,nav:W,data:K});const Q=t.computed((()=>{const e=function({config:e,currentSlide:t,slidesCount:n}){const{snapAlign:i="N/A",wrapAround:a,itemsToShow:l=1}=e,o=((e,t)=>{var n;return null!==(n={start:0,center:(t-1)/2,"center-odd":(t-1)/2,"center-even":(t-2)/2,end:t-1}[e])&&void 0!==n?n:0})(i,l);return a?t-o:r({val:t-o,max:n-l,min:0})}({config:h,currentSlide:b.value,slidesCount:m.value}),t=h.wrapAround?m.value:0,n=["rtl","btt"].includes(T.value)?-1:1,i=(e+t)*C.value*n,a=M.value?z.y:z.x;return`translate${M.value?"Y":"X"}(${a-i}px)`})),Z=n.default||n.slides,ee=n.addons,te=t.reactive(K);return()=>{if(!h.enabled)return t.h("section",{ref:c,class:["carousel","is-disabled"]},null==Z?void 0:Z());const e=u(null==Z?void 0:Z(te)),n=(null==ee?void 0:ee(te))||[];e.forEach(((e,t)=>{e.props?e.props.index=t:e.props={index:t}}));let i=e;if(h.wrapAround){const n=e.map(((n,i)=>t.cloneVNode(n,{index:-e.length+i,isClone:!0,key:`clone-before-${i}`}))),a=e.map(((n,i)=>t.cloneVNode(n,{index:e.length+i,isClone:!0,key:`clone-after-${i}`})));i=[...n,...e,...a]}p.value=e,m.value=Math.max(e.length,1);const a=t.h("ol",{class:"carousel__track",style:{transform:Q.value,transition:`${H.value?h.transition:0}ms`,gap:`${h.gap}px`},onMousedownCapture:h.mouseDrag?B:null,onTouchstartPassiveCapture:h.touchDrag?B:null},i),l=t.h("div",{class:"carousel__viewport",ref:d},a);return t.h("section",{ref:c,class:["carousel",`is-${T.value}`,{"is-vertical":M.value,"is-sliding":H.value,"is-dragging":V.value,"is-hover":E.value}],style:{"--vc-trk-height":`${"number"==typeof h.height?`${h.height}px`:h.height}`},dir:T.value,"aria-label":h.i18n.ariaGallery,tabindex:"0",onMouseenter:$,onMouseleave:R},[l,n,t.h(v)])}}});!function(e){e.arrowUp="arrowUp",e.arrowDown="arrowDown",e.arrowRight="arrowRight",e.arrowLeft="arrowLeft"}(d||(d={}));const f={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"};const m=e=>{const n=t.inject("config",t.reactive(Object.assign({},l))),i=String(e.name),a=`icon${i.charAt(0).toUpperCase()+i.slice(1)}`;if(!i||"string"!=typeof i||!(i in d))return;const o=f[i],r=t.h("path",{d:o}),u=n.i18n[a]||e.title||i,s=t.h("title",u);return t.h("svg",{class:"carousel__icon",viewBox:"0 0 24 24",role:"img","aria-label":u},[s,r])};m.props={name:String,title:String};var g=t.defineComponent({name:"CarouselSlide",props:{index:{type:Number,default:1},isClone:{type:Boolean,default:!1}},setup(e,{slots:n}){const i=t.inject("config",t.reactive(Object.assign({},l))),a=t.inject("currentSlide",t.ref(0)),o=t.inject("slidesToScroll",t.ref(0)),r=t.inject("isSliding",t.ref(!1)),u=t.inject("isVertical",t.ref(!1)),s=t.inject("slideSize",t.ref(0)),c=t.computed((()=>e.index===a.value)),d=t.computed((()=>e.index===a.value-1)),v=t.computed((()=>e.index===a.value+1)),p=t.computed((()=>{const t=Math.floor(o.value),n=Math.ceil(o.value+i.itemsToShow-1);return e.index>=t&&e.index<=n})),f=t.computed((()=>{const e=i.gap?`${s.value}px`:100/i.itemsToShow+"%";return u.value?{height:e,width:""}:{width:e,height:""}}));return()=>{var a,l;return i.enabled?t.h("li",{style:f.value,class:{carousel__slide:!0,"carousel__slide--clone":e.isClone,"carousel__slide--visible":p.value,"carousel__slide--active":c.value,"carousel__slide--prev":d.value,"carousel__slide--next":v.value,"carousel__slide--sliding":r.value},"aria-hidden":!p.value},null===(l=n.default)||void 0===l?void 0:l.call(n,{isActive:c.value,isClone:e.isClone,isPrev:d.value,isNext:v.value,isSliding:r.value,isVisible:p.value})):null===(a=n.default)||void 0===a?void 0:a.call(n)}}});e.Carousel=p,e.Icon=m,e.Navigation=(e,{slots:n,attrs:i})=>{const{next:a,prev:o}=n||{},r=t.inject("config",t.reactive(Object.assign({},l))),u=t.inject("maxSlide",t.ref(1)),s=t.inject("minSlide",t.ref(1)),c=t.inject("normalizeDir",t.ref("ltr")),d=t.inject("currentSlide",t.ref(1)),v=t.inject("nav",{}),{wrapAround:p,i18n:f}=r;return[t.h("button",{type:"button",class:["carousel__prev",!p&&d.value<=s.value&&"carousel__prev--disabled",null==i?void 0:i.class],"aria-label":f.ariaPreviousSlide,title:f.ariaPreviousSlide,onClick:v.prev},(null==o?void 0:o())||t.h(m,{name:{ltr:"arrowLeft",rtl:"arrowRight",ttb:"arrowUp",btt:"arrowDown"}[c.value]})),t.h("button",{type:"button",class:["carousel__next",!p&&d.value>=u.value&&"carousel__next--disabled",null==i?void 0:i.class],"aria-label":f.ariaNextSlide,title:f.ariaNextSlide,onClick:v.next},(null==a?void 0:a())||t.h(m,{name:{ltr:"arrowRight",rtl:"arrowLeft",ttb:"arrowDown",btt:"arrowUp"}[c.value]}))]},e.Pagination=()=>{const e=t.inject("config",t.reactive(Object.assign({},l))),n=t.inject("maxSlide",t.ref(1)),i=t.inject("minSlide",t.ref(1)),a=t.inject("currentSlide",t.ref(1)),o=t.inject("nav",{}),r=e=>s({val:a.value,max:n.value,min:0})===e,u=[];for(let a=i.value;a<n.value+1;a++){const n=c(e.i18n.ariaNavigateToSlide,{slideNumber:a+1}),i=t.h("button",{type:"button",class:{"carousel__pagination-button":!0,"carousel__pagination-button--active":r(a)},"aria-label":n,title:n,onClick:()=>o.slideTo(a)}),l=t.h("li",{class:"carousel__pagination-item",key:a},i);u.push(l)}return t.h("ol",{class:"carousel__pagination"},u)},e.Slide=g}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).VueCarousel={},e.Vue)}(this,(function(e,t){"use strict";const n=Symbol("carousel");function o({val:e,max:t,min:n}){return t<n?e:Math.min(Math.max(e,isNaN(n)?e:n),isNaN(t)?e:t)}function i({val:e,max:t,min:n=0}){const o=t-n+1;return((e-n)%o+o)%o+n}function a(e="",t={}){return Object.entries(t).reduce(((e,[t,n])=>e.replace(`{${t}}`,String(n))),e)}function l(e,t=0){let n=!1,o=0,i=null;function a(...a){if(n)return;n=!0;const l=()=>{i=requestAnimationFrame((i=>{i-o>t?(o=i,e(...a),n=!1):l()}))};l()}return a.cancel=()=>{i&&(cancelAnimationFrame(i),i=null,n=!1)},a}const r=t.defineComponent({name:"CarouselSlide",props:{isClone:{type:Boolean,default:!1},id:{type:String,default:e=>e.isClone?void 0:t.useId()},index:{type:Number,default:0}},setup(e,{slots:o,expose:i}){const a=t.inject(n);if(t.provide(n,void 0),!a)return null;const l=t.ref(e.index);t.watch((()=>e.index),(e=>l.value=e));const r=t.computed((()=>l.value===a.currentSlide)),u=t.computed((()=>l.value===a.currentSlide-1)),s=t.computed((()=>l.value===a.currentSlide+1)),d=t.computed((()=>l.value>=Math.floor(a.scrolledIndex)&&l.value<Math.ceil(a.scrolledIndex)+a.config.itemsToShow)),c=t.computed((()=>{const e=a.config.gap>0&&a.config.itemsToShow>1?`calc(${100/a.config.itemsToShow}% - ${a.config.gap*(a.config.itemsToShow-1)/a.config.itemsToShow}px)`:100/a.config.itemsToShow+"%";return a.isVertical?{height:e}:{width:e}})),v=t.getCurrentInstance();if(e.isClone){const e=e=>{[...(null==e?void 0:e.el)?e.el.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'):[]].filter((e=>!e.hasAttribute("disabled")&&!e.getAttribute("aria-hidden"))).forEach((e=>e.setAttribute("tabindex","-1")))};t.onMounted((()=>{e(v.vnode)})),t.onUpdated((()=>{e(v.vnode)}))}else a.registerSlide(v,(e=>l.value=e)),t.onUnmounted((()=>{a.unregisterSlide(v)}));return i({id:e.id}),()=>{var n,i;return a.config.enabled?t.h("li",{style:c.value,class:{carousel__slide:!0,"carousel__slide--clone":e.isClone,"carousel__slide--visible":d.value,"carousel__slide--active":r.value,"carousel__slide--prev":u.value,"carousel__slide--next":s.value,"carousel__slide--sliding":a.isSliding},onFocusin:()=>{a.viewport&&(a.viewport.scrollLeft=0),a.nav.slideTo(l.value)},id:e.isClone?void 0:e.id,"aria-hidden":e.isClone||void 0},null===(i=o.default)||void 0===i?void 0:i.call(o,{isActive:r.value,isClone:e.isClone,isPrev:u.value,isNext:s.value,isSliding:a.isSliding,isVisible:d.value})):null===(n=o.default)||void 0===n?void 0:n.call(o)}}});function u({slides:e,position:n,toShow:o}){const i=[],a="before"===n,l=a?0:o;for(let u=a?-o:0;u<l;u++){const o={index:a?u:e.length>0?u+e.length:u+99999,isClone:!0,key:`clone-${n}-${u}`};i.push(e.length>0?t.cloneVNode(e[(u+e.length)%e.length].vnode,o):t.h(r,o))}return i}const s=t.defineComponent({name:"CarouselAria",setup(){const e=t.inject(n);if(e)return()=>t.h("div",{class:["carousel__liveregion","carousel__sr-only"],"aria-live":"polite","aria-atomic":"true"},a(e.config.i18n.itemXofY,{currentSlide:e.currentSlide+1,slidesCount:e.slidesCount}))}}),d=Symbol("carousel"),c=["center","start","end","center-even","center-odd"],v=["viewport","carousel"],p=["ltr","left-to-right","rtl","right-to-left","ttb","top-to-bottom","btt","bottom-to-top"],m={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"},f={"left-to-right":"ltr","right-to-left":"rtl","top-to-bottom":"ttb","bottom-to-top":"btt"},h=Object.values(f),g={enabled:!0,itemsToShow:1,itemsToScroll:1,modelValue:0,transition:300,autoplay:0,gap:0,height:"auto",wrapAround:!1,pauseAutoplayOnHover:!1,mouseDrag:!0,touchDrag:!0,snapAlign:c[0],dir:p[0],breakpointMode:v[0],breakpoints:void 0,i18n:m},w={enabled:{default:g.enabled,type:Boolean},itemsToShow:{default:g.itemsToShow,type:Number},itemsToScroll:{default:g.itemsToScroll,type:Number},wrapAround:{default:g.wrapAround,type:Boolean},gap:{default:g.gap,type:Number},height:{default:g.height,type:[Number,String]},snapAlign:{default:g.snapAlign,validator:e=>c.includes(e)},transition:{default:g.transition,type:Number},breakpointMode:{default:g.breakpointMode,validator:e=>v.includes(e)},breakpoints:{default:g.breakpoints,type:Object},autoplay:{default:g.autoplay,type:Number},pauseAutoplayOnHover:{default:g.pauseAutoplayOnHover,type:Boolean},modelValue:{default:void 0,type:Number},mouseDrag:{default:g.mouseDrag,type:Boolean},touchDrag:{default:g.touchDrag,type:Boolean},dir:{type:String,default:g.dir,validator:e=>p.includes(e)},i18n:{default:g.i18n,type:Object}},b=t.defineComponent({name:"VueCarousel",props:w,emits:["init","drag","slide-start","loop","update:modelValue","slide-end","before-init"],setup(e,{slots:a,emit:r,expose:d}){var c;const v=t.ref(null),p=t.ref(null),m=t.shallowReactive([]),h=t.ref(0),w=t.computed((()=>m.length)),b=t.computed((()=>Object.assign(Object.assign(Object.assign({},g),e),{i18n:Object.assign(Object.assign({},g.i18n),e.i18n),breakpoints:void 0}))),S=t.reactive(Object.assign({},b.value)),x=t.ref(null!==(c=e.modelValue)&&void 0!==c?c:0),y=t.ref(0),A=t.computed((()=>Math.ceil((w.value-1)/2))),C=t.computed((()=>function({config:e,slidesCount:t}){const{snapAlign:n="center",wrapAround:o,itemsToShow:i=1}=e;return Math.max(function(){switch(o?"":n){case"start":return Math.ceil(t-i);case"center":case"center-odd":return t-Math.ceil((i-.5)/2);case"center-even":return t-Math.ceil(i/2);default:return Math.ceil(t-1)}}(),0)}({config:S,slidesCount:w.value}))),_=t.computed((()=>function({config:e,slidesCount:t}){const{snapAlign:n="center",wrapAround:o,itemsToShow:i=1}=e;return o||i>t?0:Math.max(0,function(){switch(n){case"end":return Math.floor(i-1);case"center":case"center-odd":return Math.floor((i-1)/2);case"center-even":return Math.floor((i-2)/2)}return 0}())}({config:S,slidesCount:w.value})));let T=null,N=null,I=null;const L=t.computed((()=>h.value+S.gap)),M=t.computed((()=>{const e=S.dir||"ltr";return e in f?f[e]:e})),O=[],k=t.computed((()=>["rtl","btt"].includes(M.value))),D=t.computed((()=>["ttb","btt"].includes(M.value))),E=t.computed((()=>Math.ceil(S.itemsToShow)+1));function j(){var t;const n=("carousel"===b.value.breakpointMode?null===(t=v.value)||void 0===t?void 0:t.getBoundingClientRect().width:"undefined"!=typeof window?window.innerWidth:0)||0,o=Object.keys(e.breakpoints||{}).map((e=>Number(e))).sort(((e,t)=>+t-+e)),i={};o.some((t=>n>=t&&(Object.assign(i,e.breakpoints[t]),i.i18n&&Object.assign(i.i18n,b.value.i18n,e.breakpoints[t].i18n),!0))),Object.assign(S,b.value,i)}const R=l((()=>{j(),B(),z()})),P=t.computed((()=>(S.itemsToShow-1)*S.gap)),U=t.shallowReactive(new Set);function z(){if(!p.value)return;let e=1;if(U.forEach((t=>{const n=function(e){const{transform:t}=window.getComputedStyle(e);return t.split(/[(,)]/).slice(1,-1).map((e=>parseFloat(e)))}(t);6===n.length&&(e*=n[0])})),D.value){if("auto"!==S.height){const e="string"==typeof S.height&&isNaN(parseInt(S.height))?p.value.getBoundingClientRect().height:parseInt(S.height);h.value=(e-P.value)/S.itemsToShow}}else{const t=p.value.getBoundingClientRect().width;h.value=(t/e-P.value)/S.itemsToShow}}function B(){!S.wrapAround&&w.value>0&&(x.value=o({val:x.value,max:C.value,min:_.value}))}let V;t.watchEffect((()=>B())),t.watchEffect((()=>{z()}));const $=e=>{const t=e.target;if(t&&U.add(t),!V){const e=()=>{V=requestAnimationFrame((()=>{z(),e()}))};e()}},F=e=>{const t=e.target;t&&U.delete(t),V&&0===U.size&&(cancelAnimationFrame(V),z())};j(),t.onMounted((()=>{"carousel"===b.value.breakpointMode&&j(),oe(),document&&(document.addEventListener("animationstart",$),document.addEventListener("animationend",F)),v.value&&(I=new ResizeObserver(R),I.observe(v.value)),r("init")})),t.onBeforeUnmount((()=>{m.splice(0,m.length),O.splice(0,O.length),N&&clearTimeout(N),V&&cancelAnimationFrame(V),T&&clearInterval(T),I&&(I.disconnect(),I=null),document&&(document.removeEventListener("keydown",Z),document.removeEventListener("animationstart",$),document.removeEventListener("animationend",F)),v.value&&(v.value.removeEventListener("transitionend",z),v.value.removeEventListener("animationiteration",z))}));let X=!1;const Y={x:0,y:0},G=t.reactive({x:0,y:0}),q=t.ref(!1),H=t.ref(!1),W=()=>{q.value=!0},K=()=>{q.value=!1},Z=l((e=>{if(!e.ctrlKey)switch(e.key){case"ArrowLeft":case"ArrowUp":D.value===e.key.endsWith("Up")&&(k.value?de.next(!0):de.prev(!0));break;case"ArrowRight":case"ArrowDown":D.value===e.key.endsWith("Down")&&(k.value?de.prev(!0):de.next(!0))}}),200),J=()=>{document.addEventListener("keydown",Z)},Q=()=>{document.removeEventListener("keydown",Z)};function ee(e){const t=e.target.tagName;if(["INPUT","TEXTAREA","SELECT"].includes(t)||le.value)return;if(X="touchstart"===e.type,!X&&(e.preventDefault(),0!==e.button))return;Y.x="touches"in e?e.touches[0].clientX:e.clientX,Y.y="touches"in e?e.touches[0].clientY:e.clientY;const n=X?"touchmove":"mousemove",o=X?"touchend":"mouseup";document.addEventListener(n,te,{passive:!1}),document.addEventListener(o,ne,{passive:!0})}const te=l((e=>{H.value=!0;const t="touches"in e?e.touches[0].clientX:e.clientX,n="touches"in e?e.touches[0].clientY:e.clientY,o=t-Y.x,i=n-Y.y;G.x=o,G.y=i,r("drag",{deltaX:o,deltaY:i})}));function ne(){te.cancel();const e=D.value?"y":"x",t=k.value?-1:1,n=.4*Math.sign(G[e]),o=Math.round(G[e]/L.value+n)*t;if(o&&!X){const e=t=>{t.preventDefault(),window.removeEventListener("click",e)};window.addEventListener("click",e)}re(x.value-o),G.x=0,G.y=0,H.value=!1;const i=X?"touchmove":"mousemove",a=X?"touchend":"mouseup";document.removeEventListener(i,te),document.removeEventListener(a,ne)}function oe(){!S.autoplay||S.autoplay<=0||(T=setInterval((()=>{S.pauseAutoplayOnHover&&q.value||ue()}),S.autoplay))}function ie(){T&&(clearInterval(T),T=null)}function ae(){ie(),oe()}const le=t.ref(!1);function re(e,t=!1){const n=S.wrapAround?e:o({val:e,max:C.value,min:_.value});if(x.value===n||!t&&le.value)return;r("slide-start",{slidingToIndex:e,currentSlideIndex:x.value,prevSlideIndex:y.value,slidesCount:w.value}),ie(),le.value=!0,y.value=x.value;const a=S.wrapAround?i({val:n,max:C.value,min:0}):n;x.value=n,a!==n&&pe.pause(),r("update:modelValue",a),N=setTimeout((()=>{S.wrapAround&&a!==n&&(pe.resume(),x.value=a,r("loop",{currentSlideIndex:x.value,slidingToIndex:e})),r("slide-end",{currentSlideIndex:x.value,prevSlideIndex:y.value,slidesCount:w.value}),le.value=!1,ae()}),S.transition)}function ue(e=!1){re(x.value+S.itemsToScroll,e)}function se(e=!1){re(x.value-S.itemsToScroll,e)}const de={slideTo:re,next:ue,prev:se},ce=t.computed((()=>function({config:e,currentSlide:t,slidesCount:n}){const{snapAlign:a="center",wrapAround:l,itemsToShow:r=1}=e,u=((e,t)=>{var n;return null!==(n={start:0,center:(t-1)/2,"center-odd":(t-1)/2,"center-even":(t-2)/2,end:t-1}[e])&&void 0!==n?n:0})(a,r);return l?i({val:t-u,max:n+r,min:0-r}):o({val:t-u,max:n-r,min:0})}({config:S,currentSlide:x.value,slidesCount:w.value}))),ve=t.reactive({config:S,slidesCount:w,viewport:p,slides:m,scrolledIndex:ce,currentSlide:x,maxSlide:C,minSlide:_,slideSize:h,isVertical:D,normalizedDir:M,nav:de,isSliding:le,registerSlide:(e,t)=>{t(m.length),m.push(e),O.push(t)},unregisterSlide:e=>{const t=m.indexOf(e);t>=0&&(m.splice(t,1),O.splice(t,1),O.slice(t).forEach(((e,n)=>e(t+n))))}});t.provide(n,ve),t.provide("config",S),t.provide("slidesCount",w),t.provide("currentSlide",x),t.provide("maxSlide",C),t.provide("minSlide",_),t.provide("slideSize",h),t.provide("isVertical",D),t.provide("normalizeDir",M),t.provide("nav",de),t.provide("isSliding",le),t.watch((()=>[b.value,e.breakpoints]),(()=>j()),{deep:!0}),t.watch((()=>e.autoplay),(()=>ae()));const pe=t.watch((()=>e.modelValue),(e=>{e!==x.value&&re(Number(e),!0)}));r("before-init");const me=t.reactive({config:S,slidesCount:w,slideSize:h,currentSlide:x,maxSlide:C,minSlide:_,middleSlide:A});d({updateBreakpointsConfig:j,updateSlidesData:B,updateSlideSize:z,restartCarousel:function(){j(),B(),z(),ae()},slideTo:re,next:ue,prev:se,nav:de,data:me});const fe=t.computed((()=>D.value&&h.value&&"auto"===S.height?`${h.value*S.itemsToShow+P.value}px`:"auto"!==S.height?"number"==typeof S.height||parseInt(S.height).toString()===S.height?`${S.height}px`:S.height:void 0)),he=t.computed((()=>{const e=S.wrapAround?E.value:0,t=k.value?-1:1,n=(ce.value+e)*L.value*t,o=D.value?G.y:G.x;return`translate${D.value?"Y":"X"}(${o-n}px)`}));return()=>{const e=a.default||a.slides,n=a.addons;let o=(null==e?void 0:e(me))||[];if(!S.enabled||!o.length)return t.h("section",{ref:v,class:["carousel","is-disabled"]},o);const i=(null==n?void 0:n(me))||[];if(S.wrapAround){const e=o.length>0?o[0].scopeId:null;t.pushScopeId(e);const n=E.value,i=u({slides:m,position:"before",toShow:n}),a=u({slides:m,position:"after",toShow:n});t.popScopeId(),o=[...i,...o,...a]}const l=t.h("ol",{class:"carousel__track",style:{transform:he.value,"transition-duration":le.value?`${S.transition}ms`:void 0,gap:S.gap>0?`${S.gap}px`:void 0},onMousedownCapture:S.mouseDrag?ee:null,onTouchstartPassiveCapture:S.touchDrag?ee:null},o),r=t.h("div",{class:"carousel__viewport",ref:p},l);return t.h("section",{ref:v,class:["carousel",`is-${M.value}`,{"is-vertical":D.value,"is-sliding":le.value,"is-dragging":H.value,"is-hover":q.value}],style:{"--vc-trk-height":fe.value},dir:M.value,"aria-label":S.i18n.ariaGallery,tabindex:"0",onFocus:J,onBlur:Q,onMouseenter:W,onMouseleave:K},[r,i,t.h(s)])}}});var S;!function(e){e.arrowUp="arrowUp",e.arrowDown="arrowDown",e.arrowRight="arrowRight",e.arrowLeft="arrowLeft"}(S||(S={}));const x=e=>`icon${e.charAt(0).toUpperCase()+e.slice(1)}`,y=e=>e&&e in S,A={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"},C=t.defineComponent({props:{name:{type:String,required:!0,validator:y},title:{type:String,default:e=>e.name?g.i18n[x(e.name)]:""}},setup(e){const o=t.inject(n,null);return()=>{const n=e.name;if(!n||!y(n))return;const i=A[n],a=t.h("path",{d:i}),l=(null==o?void 0:o.config.i18n[x(n)])||e.title||n,r=t.h("title",l);return t.h("svg",{class:"carousel__icon",viewBox:"0 0 24 24",role:"img","aria-label":l},[r,a])}}}),_=t.defineComponent({name:"CarouselNavigation",setup(e,{slots:o}){const i=t.inject(n);if(!i)return null;const{next:a,prev:l}=o;return()=>{const{wrapAround:n,i18n:o}=i.config;return[t.h("button",{type:"button",class:["carousel__prev",!n&&i.currentSlide<=i.minSlide&&"carousel__prev--disabled",e.class],"aria-label":o.ariaPreviousSlide,title:o.ariaPreviousSlide,onClick:i.nav.prev},(null==l?void 0:l())||t.h(C,{name:{ltr:"arrowLeft",rtl:"arrowRight",ttb:"arrowUp",btt:"arrowDown"}[i.normalizedDir]})),t.h("button",{type:"button",class:["carousel__next",!n&&i.currentSlide>=i.maxSlide&&"carousel__next--disabled",e.class],"aria-label":o.ariaNextSlide,title:o.ariaNextSlide,onClick:i.nav.next},(null==a?void 0:a())||t.h(C,{name:{ltr:"arrowRight",rtl:"arrowLeft",ttb:"arrowDown",btt:"arrowUp"}[i.normalizedDir]}))]}}}),T=t.defineComponent({name:"CarouselPagination",setup(e){const o=t.inject(n);if(!o)return null;const l=e=>i({val:o.currentSlide,max:o.maxSlide,min:0})===e;return()=>{var n,i;const r=[];for(let u=o.minSlide;u<=o.maxSlide;u++){const s=a(o.config.i18n.ariaNavigateToSlide,{slideNumber:u+1}),d=l(u),c=t.h("button",{type:"button",class:{"carousel__pagination-button":!0,"carousel__pagination-button--active":d},"aria-label":s,"aria-pressed":d,"aria-controls":null===(i=null===(n=o.slides[u])||void 0===n?void 0:n.exposed)||void 0===i?void 0:i.id,title:s,onClick:e.disableOnClick?void 0:()=>o.nav.slideTo(u)}),v=t.h("li",{class:"carousel__pagination-item",key:u},c);r.push(v)}return t.h("ol",{class:"carousel__pagination"},r)}}});e.BREAKPOINT_MODE_OPTIONS=v,e.Carousel=b,e.DEFAULT_CONFIG=g,e.DIR_MAP=f,e.DIR_OPTIONS=p,e.I18N_DEFAULT_CONFIG=m,e.Icon=C,e.NORMALIZED_DIR_OPTIONS=h,e.Navigation=_,e.Pagination=T,e.SNAP_ALIGN_OPTIONS=c,e.Slide=r,e.icons=A,e.injectCarousel=d}));
{
"name": "vue3-carousel",
"version": "0.8.1",
"version": "0.9.0",
"type": "module",
"scripts": {
"build": "rollup -c",
"dev": "rollup -cw",
"dev": "vite playground",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:serve": "vitepress serve docs",
"prepublishOnly": "$npm_execpath run build",
"release": "sh scripts/new-release.sh",
"lint": "eslint . --ext .ts",
"lint:fix": "yarn lint --fix",
"lint": "eslint .",
"lint:fix": "$npm_execpath run lint -- --fix",
"prettier": "prettier . --check",
"prettier:fix": "yarn prettier --write",
"test": "vitest",
"test:watch": "npm run test -- --watchAll"
"prettier:fix": "$npm_execpath run prettier --write",
"typecheck": "tsc -p .",
"typecheck:watch": "tsc -p . --watch --preserveWatchOutput",
"test": "vitest run",
"test:update": "vitest run --u",
"test:watch": "vitest watch",
"prepare": "husky"
},

@@ -26,51 +31,58 @@ "repository": {

"types": "./dist/carousel.d.ts",
"import": "./dist/carousel.es.js",
"browser": "./dist/carousel.min.js",
"require": "./dist/carousel.js",
"browser": "./dist/carousel.min.js"
"import": "./dist/carousel.es.js"
},
"./dist/carousel": {
"types": "./dist/carousel.d.ts",
"import": "./dist/carousel.es.js",
"browser": "./dist/carousel.min.js",
"require": "./dist/carousel.js",
"browser": "./dist/carousel.min.js"
"import": "./dist/carousel.es.js"
},
"./dist/carousel.min.js": {
"import": "./dist/carousel.es.min.js",
"require": "./dist/carousel.min.js"
"require": "./dist/carousel.min.js",
"import": "./dist/carousel.es.min.js"
},
"./dist/*.css": {
"import": "./dist/*.css",
"require": "./dist/*.css"
}
"require": "./dist/*.css",
"import": "./dist/*.css"
},
"./carousel.css": "./dist/carousel.css"
},
"main": "dist/carousel.js",
"module": "dist/carousel.es.js",
"style": "dist/carousel.css",
"types": "dist/carousel.d.ts",
"unpkg": "dist/carousel.min.js",
"jsdelivr": "dist/carousel.min.js",
"main": "./dist/carousel.js",
"module": "./dist/carousel.es.js",
"style": "./dist/carousel.css",
"types": "./dist/carousel.d.ts",
"unpkg": "./dist/carousel.min.js",
"jsdelivr": "./dist/carousel.min.js",
"devDependencies": {
"@eslint/js": "^9.16.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.1",
"@stackblitz/sdk": "^1.11.0",
"@typescript-eslint/eslint-plugin": "^5.38.1",
"@typescript-eslint/parser": "^5.38.1",
"@typescript-eslint/eslint-plugin": "^8.16.0",
"@typescript-eslint/parser": "^8.16.0",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/test-utils": "^2.4.6",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^8.3.0",
"eslint": "^9.16.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^2.1.0",
"eslint-import-resolver-typescript": "^3.6.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-vue": "^9.32.0",
"globals": "^15.13.0",
"husky": "^9.1.7",
"jsdom": "^25.0.1",
"prettier": "^2.3.2",
"rollup": "^4.22.4",
"prettier": "^3.4.1",
"rollup": "^4.28.0",
"rollup-plugin-css-only": "^4.5.2",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-delete": "^2.1.0",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-typescript-paths": "^1.5.0",
"typescript": "^5.4.3",
"vite-tsconfig-paths": "^5.0.1",
"vitepress": "^1.3.4",
"vitest": "^2.1.4",
"typescript": "^5.7.2",
"typescript-eslint": "^8.16.0",
"vite": "^6",
"vitepress": "^1.5.0",
"vitest": "^2.1.6",
"vue": "^3.2.0"

@@ -82,7 +94,8 @@ },

"browserslist": [
"> 1%",
"> 0.5%",
"last 2 versions",
"not dead"
"not dead",
"not IE 11"
],
"license": "MIT"
}

@@ -0,1 +1,3 @@

import terser from '@rollup/plugin-terser'
import typescript from '@rollup/plugin-typescript'
import css from 'rollup-plugin-css-only'

@@ -5,6 +7,4 @@ import del from 'rollup-plugin-delete'

import { typescriptPaths } from 'rollup-plugin-typescript-paths'
import typescript from '@rollup/plugin-typescript'
import terser from '@rollup/plugin-terser'
import pkg from './package.json' assert { type: 'json' }
import pkg from './package.json' with { type: 'json' }

@@ -11,0 +11,0 @@ const banner = `/**

@@ -15,3 +15,4 @@ {

"paths": {
"@/*": ["src/*"]
"@/*": ["src/*"],
"rollup/parseAst": ["./node_modules/rollup/dist/parseAst"]
}

@@ -18,0 +19,0 @@ },

import { resolve } from 'path'
import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vitest/config'

@@ -11,3 +12,3 @@

key.replace('/*', ''),
resolve(__dirname, value[0].replace('/*', '')),
resolve(__dirname, value[0].replace('/*', '/')),
])

@@ -18,4 +19,5 @@ )

export default defineConfig({
plugins: [vue()],
test: {
setupFiles: ['./vitest.setup.ts'],
setupFiles: './vitest.setup.ts',
environment: 'jsdom',

@@ -22,0 +24,0 @@ globals: true,

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc