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
77
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.12.0 to 0.13.0

7

CHANGELOG.md

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

## [0.12.0](https://github.com/ismail9k/vue3-carousel/releases/tag/v0.12.0) - 2024-12-26
- Generate cloned slides dynamically based on the active slides @ismail9k in #462, #465
- Add logo, footer, and features showcase to documentation by @ismail9k in #463
- Add fade in-out animation effect to carousel by @ismail9k in #464
- General fixes and enhancements
## [0.11.0](https://github.com/ismail9k/vue3-carousel/releases/tag/v0.11.0) - 2024-12-23

@@ -7,0 +14,0 @@

761

dist/carousel.d.ts

@@ -1,8 +0,4 @@

import { Reactive, Ref, ShallowReactive, ComponentInternalInstance, ComputedRef } from 'vue';
import { Ref, Reactive, ComputedRef, ShallowReactive, ComponentInternalInstance } 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
declare const injectCarousel = Symbol('carousel') as InjectionKey<
InjectedCarousel | undefined
>
type BreakpointMode = (typeof BREAKPOINT_MODE_OPTIONS)[number]

@@ -15,35 +11,35 @@ type Breakpoints = {

type SlideEffect = (typeof SLIDE_EFFECTS)[number]
type SnapAlign = (typeof SNAP_ALIGN_OPTIONS)[number]
type Dir = (typeof DIR_OPTIONS)[number]
type BreakpointMode = (typeof BREAKPOINT_MODE_OPTIONS)[number]
type I18nKeys = keyof typeof I18N_DEFAULT_CONFIG
type NonNormalizedDir = keyof typeof DIR_MAP
type NormalizedDir = (typeof NORMALIZED_DIR_OPTIONS)[number]
type NonNormalizedDir = keyof typeof DIR_MAP
type SlideEffect = (typeof SLIDE_EFFECTS)[number]
type I18nKeys = keyof typeof I18N_DEFAULT_CONFIG
type SnapAlign = (typeof SNAP_ALIGN_OPTIONS)[number]
interface CarouselConfig {
enabled: boolean
itemsToShow: number
itemsToScroll: number
modelValue?: number
transition?: number
gap: number
type CarouselConfig = {
autoplay?: number
snapAlign: SnapAlign
wrapAround?: boolean
pauseAutoplayOnHover?: boolean
mouseDrag?: boolean
touchDrag?: boolean
dir?: Dir
breakpointMode?: BreakpointMode
breakpoints?: Breakpoints
dir?: Dir
enabled: boolean
gap: number
height: string | number
i18n: { [key in I18nKeys]?: string }
ignoreAnimations: boolean | string[] | string
itemsToScroll: number
itemsToShow: number | 'auto'
modelValue?: number
mouseDrag?: boolean
pauseAutoplayOnHover?: boolean
preventExcessiveDragging: boolean
slideEffect: SlideEffect
snapAlign: SnapAlign
touchDrag?: boolean
transition?: number
wrapAround?: boolean
}

@@ -53,11 +49,11 @@

declare const SNAP_ALIGN_OPTIONS = [
'center',
'start',
'end',
'center-even',
'center-odd',
] as const
declare const SLIDE_EFFECTS = ['slide', 'fade'] as const
declare const BREAKPOINT_MODE_OPTIONS = ['viewport', 'carousel'] as const
declare const DIR_MAP = {
'bottom-to-top': 'btt',
'left-to-right': 'ltr',
'right-to-left': 'rtl',
'top-to-bottom': 'ttb',
} as const
declare const DIR_OPTIONS = [

@@ -73,49 +69,59 @@ 'ltr',

] as const
declare const I18N_DEFAULT_CONFIG = {
ariaGallery: 'Gallery',
ariaNavigateToPage: 'Navigate to page {slideNumber}',
ariaNavigateToSlide: 'Navigate to slide {slideNumber}',
ariaNextSlide: 'Navigate to next slide',
ariaPreviousSlide: 'Navigate to previous slide',
ariaNavigateToSlide: 'Navigate to slide {slideNumber}',
ariaNavigateToPage: 'Navigate to page {slideNumber}',
ariaGallery: 'Gallery',
itemXofY: 'Item {currentSlide} of {slidesCount}',
iconArrowUp: 'Arrow pointing upwards',
iconArrowDown: 'Arrow pointing downwards',
iconArrowLeft: 'Arrow pointing to the left',
iconArrowRight: 'Arrow pointing to the right',
iconArrowLeft: 'Arrow pointing to the left',
iconArrowUp: 'Arrow pointing upwards',
itemXofY: 'Item {currentSlide} of {slidesCount}',
} as const
declare const DIR_MAP = {
'left-to-right': 'ltr',
'right-to-left': 'rtl',
'top-to-bottom': 'ttb',
'bottom-to-top': 'btt',
} as const
declare const NORMALIZED_DIR_OPTIONS = Object.values(DIR_MAP)
declare const SLIDE_EFFECTS = ['slide', 'fade'] as const
declare const SNAP_ALIGN_OPTIONS = [
'center',
'start',
'end',
'center-even',
'center-odd',
] as const
declare const DEFAULT_CONFIG: CarouselConfig = {
autoplay: 0,
breakpointMode: BREAKPOINT_MODE_OPTIONS[0],
breakpoints: undefined,
dir: DIR_OPTIONS[0],
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,
ignoreAnimations: false,
itemsToScroll: 1,
itemsToShow: 1,
modelValue: 0,
mouseDrag: true,
pauseAutoplayOnHover: false,
preventExcessiveDragging: false,
slideEffect: SLIDE_EFFECTS[0],
snapAlign: SNAP_ALIGN_OPTIONS[0],
touchDrag: true,
transition: 300,
wrapAround: false,
}
// 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
>
declare const createSlideRegistry = (emit: EmitFn) => {
const slides = shallowReactive<Array<ComponentInternalInstance>>([])
const clonedSlides = shallowReactive<Array<ComponentInternalInstance>>([])

@@ -135,2 +141,8 @@ const updateSlideIndexes = (startIndex?: number) => {

return {
cleanup: () => {
slides.splice(0, slides.length)
},
getSlides: () => slides,
registerSlide: (slide: ComponentInternalInstance, index?: number) => {

@@ -140,3 +152,2 @@ if (!slide) return

if (slide.props.isClone) {
clonedSlides.push(slide)
return

@@ -160,9 +171,2 @@ }

},
cleanup: () => {
slides.splice(0, slides.length)
},
getSlides: () => slides,
getClonedSlides: () => clonedSlides,
}

@@ -173,47 +177,58 @@ }

interface CarouselNav {
slideTo: (index: number) => void
next: (skipTransition?: boolean) => void
prev: (skipTransition?: boolean) => void
type ElRect = {
height: number
width: number
}
type InjectedCarousel = Reactive<{
config: CarouselConfig
viewport: Ref<Element | null>
slides: ShallowReactive<Array<ComponentInternalInstance>>
slidesCount: ComputedRef<number>
activeSlide: Ref<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>
slideRegistry: SlideRegistry
}>
type Range = {
min: number
max: number
}
interface CarouselData {
type CarouselData = {
config: CarouselConfig
slidesCount: Ref<number>
slideSize: Ref<number>
currentSlide: Ref<number>
maxSlide: Ref<number>
middleSlide: Ref<number>
minSlide: Ref<number>
middleSlide: Ref<number>
slideSize: Ref<number>
slidesCount: Ref<number>
}
interface CarouselMethods extends CarouselNav {
type CarouselExposed = CarouselMethods & {
data: Reactive<CarouselData>
nav: CarouselNav
}
type CarouselMethods = CarouselNav & {
restartCarousel: () => void
updateBreakpointsConfig: () => void
updateSlideSize: () => void
updateSlidesData: () => void
updateSlideSize: () => void
restartCarousel: () => void
}
interface CarouselExposed extends CarouselMethods {
nav: CarouselNav
data: Reactive<CarouselData>
type CarouselNav = {
next: (skipTransition?: boolean) => void
prev: (skipTransition?: boolean) => void
slideTo: (index: number) => void
}
type InjectedCarousel = Reactive<{
activeSlide: Ref<number>
config: CarouselConfig
currentSlide: Ref<number>
isSliding: Ref<boolean>
isVertical: ComputedRef<boolean>
maxSlide: ComputedRef<number>
minSlide: ComputedRef<number>
nav: CarouselNav
normalizedDir: ComputedRef<NormalizedDir>
slideRegistry: SlideRegistry
slideSize: Ref<number>
slides: ShallowReactive<Array<ComponentInternalInstance>>
slidesCount: ComputedRef<number>
viewport: Ref<Element | null>
visibleRange: ComputedRef<Range>
}>
declare const Carousel = defineComponent({

@@ -223,11 +238,11 @@ name: 'VueCarousel',

emits: [
'before-init',
'drag',
'init',
'drag',
'slide-start',
'loop',
'update:modelValue',
'slide-end',
'before-init',
'slide-registered',
'slide-start',
'slide-unregistered',
'update:modelValue',
],

@@ -260,8 +275,4 @@ setup(props: CarouselConfig, { slots, emit, expose }: SetupContext) {

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 })
})
const maxSlideIndex = computed(() => slidesCount.value - 1)
const minSlideIndex = computed(() => 0)

@@ -281,3 +292,6 @@ let autoplayTimer: ReturnType<typeof setInterval> | null = null

const isVertical = computed(() => ['ttb', 'btt'].includes(normalizedDir.value))
const isAuto = computed(() => config.itemsToShow === 'auto')
const dimension = computed(() => (isVertical.value ? 'height' : 'width'))
function updateBreakpointsConfig(): void {

@@ -324,3 +338,2 @@ if (!mounted.value) {

const totalGap = computed(() => (config.itemsToShow - 1) * config.gap)
const transformElements = shallowReactive<Set<HTMLElement>>(new Set())

@@ -331,26 +344,46 @@

*/
const slidesRect = ref<Array<ElRect>>([])
function updateSlidesRectSize({
widthMultiplier,
heightMultiplier,
}: ScaleMultipliers): void {
slidesRect.value = slides.map((slide) => {
const rect = slide.exposed?.getBoundingRect()
return {
width: rect.width * widthMultiplier,
height: rect.height * heightMultiplier,
}
})
}
const viewportRect: Ref<ElRect> = ref({
width: 0,
height: 0,
})
function updateViewportRectSize({
widthMultiplier,
heightMultiplier,
}: ScaleMultipliers): void {
const rect = viewport.value?.getBoundingClientRect() || { width: 0, height: 0 }
viewportRect.value = {
width: rect.width * widthMultiplier,
height: rect.height * heightMultiplier,
}
}
function updateSlideSize(): void {
if (!viewport.value) return
let multiplierWidth = 1
transformElements.forEach((el) => {
const transformArr = getTransformValues(el)
if (transformArr.length === 6) {
multiplierWidth *= transformArr[0]
}
})
const scaleMultipliers = getScaleMultipliers(transformElements)
// 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)
updateViewportRectSize(scaleMultipliers)
updateSlidesRectSize(scaleMultipliers)
slideSize.value = (height - totalGap.value) / config.itemsToShow
}
if (isAuto.value) {
slideSize.value = calculateAverage(
slidesRect.value.map((slide) => slide[dimension.value])
)
} else {
const width = viewport.value.getBoundingClientRect().width
slideSize.value = (width / multiplierWidth - totalGap.value) / config.itemsToShow
const itemsToShow = Number(config.itemsToShow)
const totalGap = (itemsToShow - 1) * config.gap
slideSize.value = (viewportRect.value[dimension.value] - totalGap) / itemsToShow
}

@@ -369,7 +402,9 @@ }

// Validate itemsToShow
config.itemsToShow = getNumberInRange({
val: config.itemsToShow,
max: slidesCount.value,
min: 1,
})
if (!isAuto.value) {
config.itemsToShow = getNumberInRange({
val: Number(config.itemsToShow),
max: slidesCount.value,
min: 1,
})
}
}

@@ -507,5 +542,5 @@

if (isReversed.value) {
nav.next(true)
next(true)
} else {
nav.prev(true)
prev(true)
}

@@ -518,5 +553,5 @@ }

if (isReversed.value) {
nav.prev(true)
prev(true)
} else {
nav.next(true)
next(true)
}

@@ -674,3 +709,3 @@ }

max: maxSlideIndex.value,
min: 0,
min: minSlideIndex.value,
})

@@ -696,12 +731,10 @@ }

const transitionCallback = (): void => {
if (config.wrapAround) {
if (mappedIndex !== targetIndex) {
modelWatcher.resume()
if (config.wrapAround && mappedIndex !== targetIndex) {
modelWatcher.resume()
currentSlideIndex.value = mappedIndex
emit('loop', {
currentSlideIndex: currentSlideIndex.value,
slidingToIndex: slideIndex,
})
}
currentSlideIndex.value = mappedIndex
emit('loop', {
currentSlideIndex: currentSlideIndex.value,
slidingToIndex: slideIndex,
})
}

@@ -730,43 +763,2 @@

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,
activeSlide: activeSlideIndex,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
slideSize,
isVertical,
normalizedDir,
nav,
isSliding,
slideRegistry,
})
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 {

@@ -805,87 +797,266 @@ updateBreakpointsConfig()

const data = reactive<CarouselData>({
config,
slidesCount,
slideSize,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
middleSlide: middleSlideIndex,
const clonedSlidesCount = computed(() => {
if (!config.wrapAround) {
return { before: 0, after: 0 }
}
if (isAuto.value) {
return { before: slides.length, after: slides.length }
}
const itemsToShow = Number(config.itemsToShow)
const slidesToClone = Math.ceil(itemsToShow + (config.itemsToScroll - 1))
const before = slidesToClone - activeSlideIndex.value
const after = slidesToClone - (slidesCount.value - (activeSlideIndex.value + 1))
return {
before: Math.max(0, before),
after: Math.max(0, after),
}
})
expose<CarouselExposed>({
updateBreakpointsConfig,
updateSlidesData,
updateSlideSize,
restartCarousel,
slideTo,
next,
prev,
nav,
data,
const clonedSlidesOffset = computed(() => {
if (!clonedSlidesCount.value.before) {
return 0
}
if (isAuto.value) {
return (
slidesRect.value
.slice(-1 * clonedSlidesCount.value.before)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) * -1
)
}
return clonedSlidesCount.value.before * effectiveSlideSize.value * -1
})
const trackHeight = computed(() => {
// If the carousel is vertical and height is set to auto, calculate the height based on slide size and gap
if (config.height === 'auto') {
if (isVertical.value && slideSize.value) {
return `${slideSize.value * config.itemsToShow + totalGap.value}px`
}
return undefined
const snapAlignOffset = computed(() => {
if (isAuto.value) {
const slideIndex =
((currentSlideIndex.value % slides.length) + slides.length) % slides.length
return getSnapAlignOffset({
slideSize: slidesRect.value[slideIndex]?.[dimension.value],
viewportSize: viewportRect.value[dimension.value],
align: config.snapAlign,
})
}
if (
typeof config.height === 'number' ||
parseFloat(config.height).toString() === config.height
) {
return `${config.height}px`
return getSnapAlignOffset({
align: config.snapAlign,
itemsToShow: +config.itemsToShow,
})
})
const scrolledOffset = computed(() => {
let output = 0
if (isAuto.value) {
if (currentSlideIndex.value < 0) {
output =
slidesRect.value
.slice(currentSlideIndex.value)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) * -1
} else {
output = slidesRect.value
.slice(0, currentSlideIndex.value)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0)
}
output -= snapAlignOffset.value
// remove whitespace
if (!config.wrapAround) {
const maxSlidingValue =
slidesRect.value.reduce(
(acc, slide) => acc + slide[dimension.value] + config.gap,
0
) -
viewportRect.value[dimension.value] -
config.gap
output = getNumberInRange({
val: output,
max: maxSlidingValue,
min: 0,
})
}
} else {
return config.height
let scrolledSlides = currentSlideIndex.value - snapAlignOffset.value
// remove whitespace
if (!config.wrapAround) {
scrolledSlides = getNumberInRange({
val: scrolledSlides,
max: slidesCount.value - +config.itemsToShow,
min: 0,
})
}
output = scrolledSlides * effectiveSlideSize.value
}
return output * (isReversed.value ? 1 : -1)
})
const clonedSlidesCount = computed(() => {
if (!config.wrapAround) {
return { before: 0, after: 0 }
const visibleRange = computed(() => {
if (!isAuto.value) {
const base = currentSlideIndex.value - snapAlignOffset.value
if (config.wrapAround) {
return {
min: Math.floor(base),
max: Math.ceil(base + Number(config.itemsToShow) - 1),
}
}
return {
min: Math.floor(
getNumberInRange({
val: base,
max: slidesCount.value - Number(config.itemsToShow),
min: 0,
})
),
max: Math.ceil(
getNumberInRange({
val: base + Number(config.itemsToShow) - 1,
max: slidesCount.value - 1,
min: 0,
})
),
}
}
const slidesToClone = Math.ceil(config.itemsToShow + (config.itemsToScroll - 1))
const before = slidesToClone - activeSlideIndex.value
const after = slidesToClone - (slidesCount.value - (activeSlideIndex.value + 1))
// Auto width mode
let minIndex = 0
{
let accumulatedSize = 0
let index = 0 - clonedSlidesCount.value.before
const offset = Math.abs(scrolledOffset.value + clonedSlidesOffset.value)
while (accumulatedSize <= offset) {
const normalizedIndex =
((index % slides.length) + slides.length) % slides.length
accumulatedSize +=
slidesRect.value[normalizedIndex]?.[dimension.value] + config.gap
index++
}
minIndex = index - 1
}
let maxIndex = 0
{
let index = minIndex
let accumulatedSize = 0
if (index < 0) {
accumulatedSize =
slidesRect.value
.slice(0, index)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) -
Math.abs(scrolledOffset.value + clonedSlidesOffset.value)
} else {
accumulatedSize =
slidesRect.value
.slice(0, index)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) -
Math.abs(scrolledOffset.value)
}
while (accumulatedSize < viewportRect.value[dimension.value]) {
const normalizedIndex =
((index % slides.length) + slides.length) % slides.length
accumulatedSize +=
slidesRect.value[normalizedIndex]?.[dimension.value] + config.gap
index++
}
maxIndex = index - 1
}
return {
before: Math.max(0, before),
after: Math.max(0, after),
min: Math.floor(minIndex),
max: Math.ceil(maxIndex),
}
})
const clonedSlidesOffset = computed(
() => clonedSlidesCount.value.before * effectiveSlideSize.value * -1
)
const trackTransform: ComputedRef<string> = computed(() => {
const directionMultiplier = isReversed.value ? 1 : -1
const trackTransform: ComputedRef<string | undefined> = computed(() => {
if (config.slideEffect === 'fade') {
return undefined
}
const translateAxis = isVertical.value ? 'Y' : 'X'
// Calculate the total offset for slide transformation
const scrolledOffset =
scrolledIndex.value * effectiveSlideSize.value * directionMultiplier
// Include user drag interaction offset
const dragOffset = isVertical.value ? dragged.y : dragged.x
const totalOffset = scrolledOffset + dragOffset
let totalOffset = scrolledOffset.value + dragOffset
if (!config.wrapAround && config.preventExcessiveDragging) {
let maxSlidingValue = 0
if (isAuto.value) {
maxSlidingValue = slidesRect.value.reduce(
(acc, slide) => acc + slide[dimension.value],
0
)
} else {
maxSlidingValue =
(slidesCount.value - Number(config.itemsToShow)) * effectiveSlideSize.value
}
const min = isReversed.value ? 0 : -1 * maxSlidingValue
const max = isReversed.value ? maxSlidingValue : 0
totalOffset = getNumberInRange({
val: totalOffset,
min,
max,
})
}
return `translate${translateAxis}(${totalOffset}px)`
})
const trackStyle = computed(() => ({
transform: config.slideEffect === 'slide' ? trackTransform.value : undefined,
gap: config.gap > 0 ? `${config.gap}px` : undefined,
'--vc-trk-transition-duration': isSliding.value
? `${config.transition}ms`
const carouselStyle = computed(() => ({
'--vc-transition-duration': isSliding.value
? toCssValue(config.transition, 'ms')
: undefined,
'--vc-trk-height': trackHeight.value,
'--vc-trk-cloned-offset': `${clonedSlidesOffset.value}px`,
'--vc-slide-gap': toCssValue(config.gap),
'--vc-carousel-height': toCssValue(config.height),
'--vc-cloned-offset': toCssValue(clonedSlidesOffset.value),
}))
const nav: CarouselNav = { slideTo, next, prev }
const provided: InjectedCarousel = reactive({
activeSlide: activeSlideIndex,
config,
currentSlide: currentSlideIndex,
isSliding,
isVertical,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
nav,
normalizedDir,
slideRegistry,
slideSize,
slides,
slidesCount,
viewport,
visibleRange,
})
provide(injectCarousel, provided)
const data = reactive<CarouselData>({
config,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
middleSlide: middleSlideIndex,
minSlide: minSlideIndex,
slideSize,
slidesCount,
})
expose<CarouselExposed>({
data,
nav,
next,
prev,
restartCarousel,
slideTo,
updateBreakpointsConfig,
updateSlideSize,
updateSlidesData,
})
return () => {

@@ -927,3 +1098,3 @@ const slotSlides = slots.default || slots.slides

class: 'carousel__track',
style: trackStyle.value,
style: { transform: trackTransform.value },
onMousedownCapture: config.mouseDrag ? handleDragStart : null,

@@ -952,2 +1123,3 @@ onTouchstartPassiveCapture: config.touchDrag ? handleDragStart : null,

dir: normalizedDir.value,
style: carouselStyle.value,
'aria-label': config.i18n['ariaGallery'],

@@ -967,6 +1139,6 @@ tabindex: '0',

declare enum IconName {
arrowUp = 'arrowUp',
arrowDown = 'arrowDown',
arrowLeft = 'arrowLeft',
arrowRight = 'arrowRight',
arrowLeft = 'arrowLeft',
arrowUp = 'arrowUp',
}

@@ -976,12 +1148,12 @@

interface IconProps {
type IconProps = {
name: IconNameValue
title?: string
name: IconNameValue
}
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',
arrowLeft: 'M15.41 16.59L10.83 12l4.58-4.59L14 6l-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',
arrowUp: 'M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z',
}

@@ -1012,4 +1184,3 @@

const iconTitle: string =
carousel?.config.i18n[iconI18n(iconName)] || props.title!
const iconTitle: string = carousel?.config.i18n[iconI18n(iconName)] || props.title!

@@ -1046,6 +1217,6 @@ const titleEl = h('title', iconTitle)

const directionIcons: Record<NormalizedDir, IconNameValue> = {
btt: 'arrowDown',
ltr: 'arrowLeft',
rtl: 'arrowRight',
ttb: 'arrowUp',
btt: 'arrowDown',
}

@@ -1057,6 +1228,6 @@

const directionIcons: Record<NormalizedDir, IconNameValue> = {
btt: 'arrowUp',
ltr: 'arrowRight',
rtl: 'arrowLeft',
ttb: 'arrowDown',
btt: 'arrowUp',
}

@@ -1067,4 +1238,8 @@

const prevDisabled = computed(() => !carousel.config.wrapAround && carousel.currentSlide <= carousel.minSlide)
const nextDisabled = computed(() => !carousel.config.wrapAround && carousel.currentSlide >= carousel.maxSlide)
const prevDisabled = computed(
() => !carousel.config.wrapAround && carousel.currentSlide <= carousel.minSlide
)
const nextDisabled = computed(
() => !carousel.config.wrapAround && carousel.currentSlide >= carousel.maxSlide
)

@@ -1084,3 +1259,3 @@ return () => {

'carousel__prev',
{'carousel__prev--disabled': prevDisabled.value},
{ 'carousel__prev--disabled': prevDisabled.value },
attrs.class,

@@ -1102,3 +1277,3 @@ ],

'carousel__next',
{'carousel__next--disabled': nextDisabled.value},
{ 'carousel__next--disabled': nextDisabled.value },
attrs.class,

@@ -1115,3 +1290,3 @@ ],

interface PaginationProps {
type PaginationProps = {
disableOnClick?: boolean

@@ -1138,14 +1313,16 @@ paginateByItemsToShow?: boolean

const itemsToShow = computed(() => carousel.config.itemsToShow as number)
const offset = computed(() =>
calculateOffset(carousel.config.snapAlign, carousel.config.itemsToShow)
getSnapAlignOffset({
align: carousel.config.snapAlign,
itemsToShow: itemsToShow.value,
})
)
const isPaginated = computed(
() => props.paginateByItemsToShow && carousel.config.itemsToShow > 1
() => props.paginateByItemsToShow && itemsToShow.value > 1
)
const currentPage = computed(() =>
Math.ceil((carousel.activeSlide - offset.value) / carousel.config.itemsToShow)
Math.ceil((carousel.activeSlide - offset.value) / itemsToShow.value)
)
const pageCount = computed(() =>
Math.ceil(carousel.slidesCount / carousel.config.itemsToShow)
)
const pageCount = computed(() => Math.ceil(carousel.slidesCount / itemsToShow.value))

@@ -1198,3 +1375,3 @@ const isActive = (slide: number): boolean =>

isPaginated.value
? slide * carousel.config.itemsToShow + offset.value
? Math.floor(slide * +carousel.config.itemsToShow + offset.value)
: slide

@@ -1212,6 +1389,7 @@ ),

interface SlideProps {
type SlideProps = {
id?: string
index: number
isClone?: boolean
position?: 'before' | 'after'
}

@@ -1222,6 +1400,2 @@

props: {
isClone: {
type: Boolean,
default: false,
},
id: {

@@ -1235,4 +1409,12 @@ type: String,

},
isClone: {
type: Boolean,
default: false,
},
position: {
type: String,
default: undefined,
},
},
setup(props: DeepReadonly<SlideProps>, { slots, expose }: SetupContext) {
setup(props: DeepReadonly<SlideProps>, { attrs, slots, expose }: SetupContext) {
const carousel = inject(injectCarousel)

@@ -1251,5 +1433,13 @@ provide(injectCarousel, undefined) // Don't provide for nested slides

const instance = getCurrentInstance()!
const getBoundingRect = () => {
const el = instance.vnode.el as HTMLElement
return el ? el.getBoundingClientRect() : { width: 0, height: 0 }
}
expose({
id: props.id,
setIndex,
getBoundingRect,
})

@@ -1268,15 +1458,17 @@

() =>
currentIndex.value >= Math.floor(carousel.scrolledIndex) &&
currentIndex.value <
Math.ceil(carousel.scrolledIndex) + carousel.config.itemsToShow
currentIndex.value >= carousel.visibleRange.min &&
currentIndex.value <= carousel.visibleRange.max
)
const slideStyle = computed(() => {
if (carousel.config.itemsToShow === 'auto') {
return
}
const itemsToShow = carousel.config.itemsToShow
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
carousel.config.gap > 0 && itemsToShow > 1
? `calc(${100 / itemsToShow}% - ${
(carousel.config.gap * (itemsToShow - 1)) / itemsToShow
}px)`
: `${100 / carousel.config.itemsToShow}%`
: `${100 / itemsToShow}%`

@@ -1286,4 +1478,2 @@ return carousel.isVertical ? { height: dimension } : { width: dimension }

const instance = getCurrentInstance()!
carousel.slideRegistry.registerSlide(instance, props.index)

@@ -1312,3 +1502,3 @@ onUnmounted(() => {

{
style: slideStyle.value,
style: [attrs.style, { ...slideStyle.value }],
class: {

@@ -1334,2 +1524,3 @@ carousel__slide: true,

slots.default?.({
currentIndex: currentIndex.value,
isActive: isActive.value,

@@ -1347,2 +1538,2 @@ isClone: props.isClone,

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, SLIDE_EFFECTS, SNAP_ALIGN_OPTIONS, Slide, type SlideEffect, type SlideProps, type SlideRegistry, type SnapAlign, type VueClass, createSlideRegistry, icons, injectCarousel };
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, type ElRect, 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, type Range, SLIDE_EFFECTS, SNAP_ALIGN_OPTIONS, Slide, type SlideEffect, type SlideProps, type SlideRegistry, type SnapAlign, type VueClass, createSlideRegistry, icons, injectCarousel };
/**
* Vue 3 Carousel 0.12.0
* (c) 2024
* Vue 3 Carousel 0.13.0
* (c) 2025
* @license MIT

@@ -12,15 +12,9 @@ */

// 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',
];
const SLIDE_EFFECTS = ['slide', 'fade'];
const BREAKPOINT_MODE_OPTIONS = ['viewport', 'carousel'];
const DIR_MAP = {
'bottom-to-top': 'btt',
'left-to-right': 'ltr',
'right-to-left': 'rtl',
'top-to-bottom': 'ttb',
};
const DIR_OPTIONS = [

@@ -37,45 +31,51 @@ 'ltr',

const I18N_DEFAULT_CONFIG = {
ariaGallery: 'Gallery',
ariaNavigateToPage: 'Navigate to page {slideNumber}',
ariaNavigateToSlide: 'Navigate to slide {slideNumber}',
ariaNextSlide: 'Navigate to next slide',
ariaPreviousSlide: 'Navigate to previous slide',
ariaNavigateToSlide: 'Navigate to slide {slideNumber}',
ariaNavigateToPage: 'Navigate to page {slideNumber}',
ariaGallery: 'Gallery',
itemXofY: 'Item {currentSlide} of {slidesCount}',
iconArrowUp: 'Arrow pointing upwards',
iconArrowDown: 'Arrow pointing downwards',
iconArrowLeft: 'Arrow pointing to the left',
iconArrowRight: 'Arrow pointing to the right',
iconArrowLeft: 'Arrow pointing to the left',
iconArrowUp: 'Arrow pointing upwards',
itemXofY: 'Item {currentSlide} of {slidesCount}',
};
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 SLIDE_EFFECTS = ['slide', 'fade'];
const SNAP_ALIGN_OPTIONS = [
'center',
'start',
'end',
'center-even',
'center-odd',
];
const DEFAULT_CONFIG = {
autoplay: 0,
breakpointMode: BREAKPOINT_MODE_OPTIONS[0],
breakpoints: undefined,
dir: DIR_OPTIONS[0],
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,
ignoreAnimations: false,
itemsToScroll: 1,
itemsToShow: 1,
modelValue: 0,
mouseDrag: true,
pauseAutoplayOnHover: false,
preventExcessiveDragging: false,
slideEffect: SLIDE_EFFECTS[0],
snapAlign: SNAP_ALIGN_OPTIONS[0],
touchDrag: true,
transition: 300,
wrapAround: false,
};
// 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 createSlideRegistry = (emit) => {
const slides = vue.shallowReactive([]);
const clonedSlides = vue.shallowReactive([]);
const updateSlideIndexes = (startIndex) => {

@@ -96,2 +96,6 @@ if (startIndex !== undefined) {

return {
cleanup: () => {
slides.splice(0, slides.length);
},
getSlides: () => slides,
registerSlide: (slide, index) => {

@@ -101,3 +105,2 @@ if (!slide)

if (slide.props.isClone) {
clonedSlides.push(slide);
return;

@@ -118,10 +121,78 @@ }

},
cleanup: () => {
slides.splice(0, slides.length);
},
getSlides: () => slides,
getClonedSlides: () => clonedSlides,
};
};
function calculateAverage(numbers) {
if (numbers.length === 0)
return 0;
const sum = numbers.reduce((acc, num) => acc + num, 0);
return sum / numbers.length;
}
function createCloneSlides({ slides, position, toShow }) {
const clones = [];
const isBefore = position === 'before';
const start = isBefore ? -toShow : 0;
const end = isBefore ? 0 : toShow;
if (slides.length <= 0) {
return clones;
}
for (let i = start; i < end; i++) {
const index = isBefore ? i : i + slides.length;
const props = {
index,
isClone: true,
position,
id: undefined, // Make sure we don't duplicate the id which would be invalid html
key: `clone-${position}-${i}`,
};
const vnode = slides[((i % slides.length) + slides.length) % slides.length].vnode;
const clone = vue.cloneVNode(vnode, props);
clone.el = null;
clones.push(clone);
}
return clones;
}
const FOCUSABLE_ELEMENTS_SELECTOR = 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
/**
* Disables keyboard tab navigation for all focusable child elements
* @param node Vue virtual node containing the elements to disable
*/
function disableChildrenTabbing(node) {
if (!node.el || !(node.el instanceof Element)) {
return;
}
const elements = node.el.querySelectorAll(FOCUSABLE_ELEMENTS_SELECTOR);
for (const el of elements) {
if (el instanceof HTMLElement &&
!el.hasAttribute('disabled') &&
el.getAttribute('aria-hidden') !== 'true') {
el.setAttribute('tabindex', '-1');
}
}
}
/** Useful function to destructure props without triggering reactivity for certain keys */
function except(obj, keys) {
return Object.keys(obj).filter((k) => !keys.includes(k))
.reduce((acc, key) => (acc[key] = obj[key], acc), {});
}
/**
* Calculates the number of slides to move based on drag movement
* @param params Configuration parameters for drag calculation
* @returns Number of slides to move (positive or negative)
*/
function getDraggedSlidesCount(params) {
const { isVertical, isReversed, dragged, effectiveSlideSize } = params;
// Get drag value based on direction
const dragValue = isVertical ? dragged.y : dragged.x;
// If no drag, return +0 explicitly
if (dragValue === 0)
return 0;
const slidesDragged = Math.round(dragValue / effectiveSlideSize);
return isReversed ? slidesDragged : -slidesDragged;
}
function getNumberInRange({ val, max, min }) {

@@ -134,9 +205,36 @@ if (max < min) {

const calculateOffset = (snapAlign, itemsToShow) => {
switch (snapAlign) {
default:
function getTransformValues(el) {
const { transform } = window.getComputedStyle(el);
//add sanity check
return transform
.split(/[(,)]/)
.slice(1, -1)
.map((v) => parseFloat(v));
}
function getScaleMultipliers(transformElements) {
let widthMultiplier = 1;
let heightMultiplier = 1;
transformElements.forEach((el) => {
const transformArr = getTransformValues(el);
if (transformArr.length === 6) {
widthMultiplier /= transformArr[0];
heightMultiplier /= transformArr[3];
}
});
return { widthMultiplier, heightMultiplier };
}
/**
* Calculates the snap align offset for a carousel item based on items to show.
* Returns the number of slides to offset.
*
* @param align - The alignment type.
* @param itemsToShow - The number of items to show.
* @returns The calculated offset.
*/
function getSnapAlignOffsetByItemsToShow(align, itemsToShow) {
switch (align) {
case 'start':
return 0;
case 'center':
return (itemsToShow - 1) / 2;
case 'center-odd':

@@ -148,63 +246,50 @@ return (itemsToShow - 1) / 2;

return itemsToShow - 1;
default:
return 0;
}
};
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 currentSlide - offset;
}
return getNumberInRange({
val: currentSlide - offset,
max: slidesCount - itemsToShow,
min: 0,
});
}
/**
* Determines the minimum slide index based on the configuration.
* Calculates the snap align offset for a carousel item based on slide and viewport size.
* Returns the real width to offset.
*
* @param {GetMinSlideIndexArgs} args - The carousel configuration and slide count.
* @returns {number} The minimum slide index.
* @param align - The alignment type.
* @param slideSize - The size of the slide.
* @param viewportSize - The size of the viewport.
* @returns The calculated offset.
*/
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;
function getSnapAlignOffsetBySlideAndViewport(align, slideSize, viewportSize) {
switch (align) {
case 'start':
return 0;
case 'center':
case 'center-odd':
return (viewportSize - slideSize) / 2;
case 'center-even':
return viewportSize / 2 - slideSize;
case 'end':
return viewportSize - slideSize;
default:
return 0;
}
// Return the calculated offset or default to 0 for invalid snapAlign values
return Math.max(0, Math.floor(calculateOffset(snapAlign, itemsToShow)));
}
/**
* Determines the maximum slide index based on the configuration.
* Calculates the snap align offset for a carousel item.
*
* @param {Args} args - The carousel configuration and slide count.
* @returns {number} The maximum slide index.
* @param params - The parameters for calculating the offset.
* @returns The calculated offset.
*/
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);
}
function getSnapAlignOffset({ slideSize, viewportSize, align, itemsToShow, }) {
if (itemsToShow !== undefined) {
return getSnapAlignOffsetByItemsToShow(align, itemsToShow);
}
// Return the result ensuring it's non-negative
return Math.max(snapAlignCalculations(), 0);
if (slideSize !== undefined && viewportSize !== undefined) {
return getSnapAlignOffsetBySlideAndViewport(align, slideSize, viewportSize);
}
return 0;
}
function i18nFormatter(string = '', values = {}) {
return Object.entries(values).reduce((acc, [key, value]) => acc.replace(`{${key}}`, String(value)), string);
}
function mapNumberToRange({ val, max, min = 0 }) {

@@ -215,6 +300,2 @@ const mod = max - min + 1;

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

@@ -259,76 +340,17 @@ * Returns a throttled version of the function using requestAnimationFrame.

/** Useful function to destructure props without triggering reactivity for certain keys */
function except(obj, keys) {
return Object.keys(obj).filter((k) => !keys.includes(k))
.reduce((acc, key) => (acc[key] = obj[key], acc), {});
}
function getTransformValues(el) {
const { transform } = window.getComputedStyle(el);
//add sanity check
return transform
.split(/[(,)]/)
.slice(1, -1)
.map((v) => parseFloat(v));
}
function createCloneSlides({ slides, position, toShow }) {
const clones = [];
const isBefore = position === 'before';
const start = isBefore ? -toShow : 0;
const end = isBefore ? 0 : toShow;
if (slides.length <= 0) {
return clones;
}
for (let i = start; i < end; i++) {
const index = isBefore ? i : i + slides.length;
const props = {
index,
isClone: true,
id: undefined, // Make sure we don't duplicate the id which would be invalid html
key: `clone-${position}-${i}`,
};
const vnode = slides[((i % slides.length) + slides.length) % slides.length].vnode;
const clone = vue.cloneVNode(vnode, props);
clone.el = null;
clones.push(clone);
}
return clones;
}
const FOCUSABLE_ELEMENTS_SELECTOR = 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
/**
* Disables keyboard tab navigation for all focusable child elements
* @param node Vue virtual node containing the elements to disable
*/
function disableChildrenTabbing(node) {
if (!node.el || !(node.el instanceof Element)) {
return;
* Converts a value to a CSS-compatible string.
* @param value - The value to convert.
* @returns The CSS-compatible string.
**/
function toCssValue(value, unit = 'px') {
if (value === null || value === undefined || value === '') {
return undefined;
}
const elements = node.el.querySelectorAll(FOCUSABLE_ELEMENTS_SELECTOR);
for (const el of elements) {
if (el instanceof HTMLElement &&
!el.hasAttribute('disabled') &&
el.getAttribute('aria-hidden') !== 'true') {
el.setAttribute('tabindex', '-1');
}
if (typeof value === 'number' || parseFloat(value).toString() === value) {
return `${value}${unit}`;
}
return value;
}
/**
* Calculates the number of slides to move based on drag movement
* @param params Configuration parameters for drag calculation
* @returns Number of slides to move (positive or negative)
*/
function getDraggedSlidesCount(params) {
const { isVertical, isReversed, dragged, effectiveSlideSize } = params;
// Get drag value based on direction
const dragValue = isVertical ? dragged.y : dragged.x;
// If no drag, return +0 explicitly
if (dragValue === 0)
return 0;
const slidesDragged = Math.round(dragValue / effectiveSlideSize);
return isReversed ? slidesDragged : -slidesDragged;
}
const ARIA = vue.defineComponent({

@@ -353,2 +375,19 @@ name: 'CarouselAria',

const carouselProps = {
// time to auto advance slides in ms
autoplay: {
default: DEFAULT_CONFIG.autoplay,
type: Number,
},
// an object to store breakpoints
breakpoints: {
default: DEFAULT_CONFIG.breakpoints,
type: Object,
},
// controls the breakpoint mode relative to the carousel container or the viewport
breakpointMode: {
default: DEFAULT_CONFIG.breakpointMode,
validator(value) {
return BREAKPOINT_MODE_OPTIONS.includes(value);
},
},
// enable/disable the carousel component

@@ -359,17 +398,2 @@ enabled: {

},
// count of items to showed per view
itemsToShow: {
default: DEFAULT_CONFIG.itemsToShow,
type: Number,
},
// count of items to be scrolled
itemsToScroll: {
default: DEFAULT_CONFIG.itemsToScroll,
type: Number,
},
// control infinite scrolling mode
wrapAround: {
default: DEFAULT_CONFIG.wrapAround,
type: Boolean,
},
// control the gap between slides

@@ -385,36 +409,21 @@ gap: {

},
// control snap position alignment
snapAlign: {
default: DEFAULT_CONFIG.snapAlign,
validator(value) {
return SNAP_ALIGN_OPTIONS.includes(value);
},
ignoreAnimations: {
default: false,
type: [Array, Boolean, String],
},
// sliding transition time in ms
transition: {
default: DEFAULT_CONFIG.transition,
// count of items to be scrolled
itemsToScroll: {
default: DEFAULT_CONFIG.itemsToScroll,
type: Number,
},
// controls the breakpoint mode relative to the carousel container or the viewport
breakpointMode: {
default: DEFAULT_CONFIG.breakpointMode,
validator(value) {
return BREAKPOINT_MODE_OPTIONS.includes(value);
},
// count of items to showed per view
itemsToShow: {
default: DEFAULT_CONFIG.itemsToShow,
type: [Number, String],
},
// an object to store breakpoints
breakpoints: {
default: DEFAULT_CONFIG.breakpoints,
// aria-labels and additional text labels
i18n: {
default: DEFAULT_CONFIG.i18n,
type: Object,
},
// time to auto advance slides in ms
autoplay: {
default: DEFAULT_CONFIG.autoplay,
type: Number,
},
// pause autoplay when mouse hover over the carousel
pauseAutoplayOnHover: {
default: DEFAULT_CONFIG.pauseAutoplayOnHover,
type: Boolean,
},
// slide number number of initial slide

@@ -435,20 +444,23 @@ modelValue: {

},
pauseAutoplayOnHover: {
default: DEFAULT_CONFIG.pauseAutoplayOnHover,
type: Boolean,
},
preventExcessiveDragging: {
default: false,
type: Boolean,
validator(value, props) {
if (value && props.wrapAround) {
console.warn(`[vue3-carousel warn]: "preventExcessiveDragging" cannot be used with wrapAround. The setting will be ignored.`);
}
return true;
},
},
// control snap position alignment
dir: {
type: String,
default: DEFAULT_CONFIG.dir,
snapAlign: {
default: DEFAULT_CONFIG.snapAlign,
validator(value) {
// The value must match one of these strings
return DIR_OPTIONS.includes(value);
return SNAP_ALIGN_OPTIONS.includes(value);
},
},
// aria-labels and additional text labels
i18n: {
default: DEFAULT_CONFIG.i18n,
type: Object,
},
ignoreAnimations: {
default: false,
type: [Array, Boolean, String],
},
slideEffect: {

@@ -461,2 +473,29 @@ type: String,

},
// sliding transition time in ms
transition: {
default: DEFAULT_CONFIG.transition,
type: Number,
},
// control the gap between slides
dir: {
type: String,
default: DEFAULT_CONFIG.dir,
validator(value, props) {
// The value must match one of these strings
if (!DIR_OPTIONS.includes(value)) {
return false;
}
const normalizedDir = value in DIR_MAP ? DIR_MAP[value] : value;
if (['ttb', 'btt'].includes(normalizedDir) &&
(!props.height || props.height === 'auto')) {
console.warn(`[vue3-carousel warn]: The dir "${value}" is not supported with height "auto".`);
}
return true;
},
},
// control infinite scrolling mode
wrapAround: {
default: DEFAULT_CONFIG.wrapAround,
type: Boolean,
},
};

@@ -468,11 +507,11 @@

emits: [
'before-init',
'drag',
'init',
'drag',
'slide-start',
'loop',
'update:modelValue',
'slide-end',
'before-init',
'slide-registered',
'slide-start',
'slide-unregistered',
'update:modelValue',
],

@@ -496,8 +535,4 @@ setup(props, { slots, emit, expose }) {

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 });
});
const maxSlideIndex = vue.computed(() => slidesCount.value - 1);
const minSlideIndex = vue.computed(() => 0);
let autoplayTimer = null;

@@ -513,2 +548,4 @@ let transitionTimer = null;

const isVertical = vue.computed(() => ['ttb', 'btt'].includes(normalizedDir.value));
const isAuto = vue.computed(() => config.itemsToShow === 'auto');
const dimension = vue.computed(() => (isVertical.value ? 'height' : 'width'));
function updateBreakpointsConfig() {

@@ -546,3 +583,2 @@ var _a;

});
const totalGap = vue.computed(() => (config.itemsToShow - 1) * config.gap);
const transformElements = vue.shallowReactive(new Set());

@@ -552,24 +588,38 @@ /**

*/
const slidesRect = vue.ref([]);
function updateSlidesRectSize({ widthMultiplier, heightMultiplier, }) {
slidesRect.value = slides.map((slide) => {
var _a;
const rect = (_a = slide.exposed) === null || _a === void 0 ? void 0 : _a.getBoundingRect();
return {
width: rect.width * widthMultiplier,
height: rect.height * heightMultiplier,
};
});
}
const viewportRect = vue.ref({
width: 0,
height: 0,
});
function updateViewportRectSize({ widthMultiplier, heightMultiplier, }) {
var _a;
const rect = ((_a = viewport.value) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || { width: 0, height: 0 };
viewportRect.value = {
width: rect.width * widthMultiplier,
height: rect.height * heightMultiplier,
};
}
function updateSlideSize() {
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);
slideSize.value = (height - totalGap.value) / config.itemsToShow;
}
const scaleMultipliers = getScaleMultipliers(transformElements);
updateViewportRectSize(scaleMultipliers);
updateSlidesRectSize(scaleMultipliers);
if (isAuto.value) {
slideSize.value = calculateAverage(slidesRect.value.map((slide) => slide[dimension.value]));
}
else {
const width = viewport.value.getBoundingClientRect().width;
slideSize.value = (width / multiplierWidth - totalGap.value) / config.itemsToShow;
const itemsToShow = Number(config.itemsToShow);
const totalGap = (itemsToShow - 1) * config.gap;
slideSize.value = (viewportRect.value[dimension.value] - totalGap) / itemsToShow;
}

@@ -586,7 +636,9 @@ }

// Validate itemsToShow
config.itemsToShow = getNumberInRange({
val: config.itemsToShow,
max: slidesCount.value,
min: 1,
});
if (!isAuto.value) {
config.itemsToShow = getNumberInRange({
val: Number(config.itemsToShow),
max: slidesCount.value,
min: 1,
});
}
}

@@ -708,6 +760,6 @@ const ignoreAnimations = vue.computed(() => {

if (isReversed.value) {
nav.next(true);
next(true);
}
else {
nav.prev(true);
prev(true);
}

@@ -720,6 +772,6 @@ }

if (isReversed.value) {
nav.prev(true);
prev(true);
}
else {
nav.next(true);
next(true);
}

@@ -851,3 +903,3 @@ }

max: maxSlideIndex.value,
min: 0,
min: minSlideIndex.value,
});

@@ -869,11 +921,9 @@ }

const transitionCallback = () => {
if (config.wrapAround) {
if (mappedIndex !== targetIndex) {
modelWatcher.resume();
currentSlideIndex.value = mappedIndex;
emit('loop', {
currentSlideIndex: currentSlideIndex.value,
slidingToIndex: slideIndex,
});
}
if (config.wrapAround && mappedIndex !== targetIndex) {
modelWatcher.resume();
currentSlideIndex.value = mappedIndex;
emit('loop', {
currentSlideIndex: currentSlideIndex.value,
slidingToIndex: slideIndex,
});
}

@@ -896,37 +946,2 @@ emit('slide-end', {

}
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,
activeSlide: activeSlideIndex,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
slideSize,
isVertical,
normalizedDir,
nav,
isSliding,
slideRegistry,
});
vue.provide(injectCarousel, 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);
vue.provide('isSliding', isSliding);
function restartCarousel() {

@@ -950,38 +965,2 @@ updateBreakpointsConfig();

emit('before-init');
const data = vue.reactive({
config,
slidesCount,
slideSize,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
middleSlide: middleSlideIndex,
});
expose({
updateBreakpointsConfig,
updateSlidesData,
updateSlideSize,
restartCarousel,
slideTo,
next,
prev,
nav,
data,
});
const trackHeight = vue.computed(() => {
// If the carousel is vertical and height is set to auto, calculate the height based on slide size and gap
if (config.height === 'auto') {
if (isVertical.value && slideSize.value) {
return `${slideSize.value * config.itemsToShow + totalGap.value}px`;
}
return undefined;
}
if (typeof config.height === 'number' ||
parseFloat(config.height).toString() === config.height) {
return `${config.height}px`;
}
else {
return config.height;
}
});
const clonedSlidesCount = vue.computed(() => {

@@ -991,3 +970,7 @@ if (!config.wrapAround) {

}
const slidesToClone = Math.ceil(config.itemsToShow + (config.itemsToScroll - 1));
if (isAuto.value) {
return { before: slides.length, after: slides.length };
}
const itemsToShow = Number(config.itemsToShow);
const slidesToClone = Math.ceil(itemsToShow + (config.itemsToScroll - 1));
const before = slidesToClone - activeSlideIndex.value;

@@ -1000,22 +983,211 @@ const after = slidesToClone - (slidesCount.value - (activeSlideIndex.value + 1));

});
const clonedSlidesOffset = vue.computed(() => clonedSlidesCount.value.before * effectiveSlideSize.value * -1);
const clonedSlidesOffset = vue.computed(() => {
if (!clonedSlidesCount.value.before) {
return 0;
}
if (isAuto.value) {
return (slidesRect.value
.slice(-1 * clonedSlidesCount.value.before)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) * -1);
}
return clonedSlidesCount.value.before * effectiveSlideSize.value * -1;
});
const snapAlignOffset = vue.computed(() => {
var _a;
if (isAuto.value) {
const slideIndex = ((currentSlideIndex.value % slides.length) + slides.length) % slides.length;
return getSnapAlignOffset({
slideSize: (_a = slidesRect.value[slideIndex]) === null || _a === void 0 ? void 0 : _a[dimension.value],
viewportSize: viewportRect.value[dimension.value],
align: config.snapAlign,
});
}
return getSnapAlignOffset({
align: config.snapAlign,
itemsToShow: +config.itemsToShow,
});
});
const scrolledOffset = vue.computed(() => {
let output = 0;
if (isAuto.value) {
if (currentSlideIndex.value < 0) {
output =
slidesRect.value
.slice(currentSlideIndex.value)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) * -1;
}
else {
output = slidesRect.value
.slice(0, currentSlideIndex.value)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0);
}
output -= snapAlignOffset.value;
// remove whitespace
if (!config.wrapAround) {
const maxSlidingValue = slidesRect.value.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) -
viewportRect.value[dimension.value] -
config.gap;
output = getNumberInRange({
val: output,
max: maxSlidingValue,
min: 0,
});
}
}
else {
let scrolledSlides = currentSlideIndex.value - snapAlignOffset.value;
// remove whitespace
if (!config.wrapAround) {
scrolledSlides = getNumberInRange({
val: scrolledSlides,
max: slidesCount.value - +config.itemsToShow,
min: 0,
});
}
output = scrolledSlides * effectiveSlideSize.value;
}
return output * (isReversed.value ? 1 : -1);
});
const visibleRange = vue.computed(() => {
var _a, _b;
if (!isAuto.value) {
const base = currentSlideIndex.value - snapAlignOffset.value;
if (config.wrapAround) {
return {
min: Math.floor(base),
max: Math.ceil(base + Number(config.itemsToShow) - 1),
};
}
return {
min: Math.floor(getNumberInRange({
val: base,
max: slidesCount.value - Number(config.itemsToShow),
min: 0,
})),
max: Math.ceil(getNumberInRange({
val: base + Number(config.itemsToShow) - 1,
max: slidesCount.value - 1,
min: 0,
})),
};
}
// Auto width mode
let minIndex = 0;
{
let accumulatedSize = 0;
let index = 0 - clonedSlidesCount.value.before;
const offset = Math.abs(scrolledOffset.value + clonedSlidesOffset.value);
while (accumulatedSize <= offset) {
const normalizedIndex = ((index % slides.length) + slides.length) % slides.length;
accumulatedSize +=
((_a = slidesRect.value[normalizedIndex]) === null || _a === void 0 ? void 0 : _a[dimension.value]) + config.gap;
index++;
}
minIndex = index - 1;
}
let maxIndex = 0;
{
let index = minIndex;
let accumulatedSize = 0;
if (index < 0) {
accumulatedSize =
slidesRect.value
.slice(0, index)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) -
Math.abs(scrolledOffset.value + clonedSlidesOffset.value);
}
else {
accumulatedSize =
slidesRect.value
.slice(0, index)
.reduce((acc, slide) => acc + slide[dimension.value] + config.gap, 0) -
Math.abs(scrolledOffset.value);
}
while (accumulatedSize < viewportRect.value[dimension.value]) {
const normalizedIndex = ((index % slides.length) + slides.length) % slides.length;
accumulatedSize +=
((_b = slidesRect.value[normalizedIndex]) === null || _b === void 0 ? void 0 : _b[dimension.value]) + config.gap;
index++;
}
maxIndex = index - 1;
}
return {
min: Math.floor(minIndex),
max: Math.ceil(maxIndex),
};
});
const trackTransform = vue.computed(() => {
const directionMultiplier = isReversed.value ? 1 : -1;
if (config.slideEffect === 'fade') {
return undefined;
}
const translateAxis = isVertical.value ? 'Y' : 'X';
// Calculate the total offset for slide transformation
const scrolledOffset = scrolledIndex.value * effectiveSlideSize.value * directionMultiplier;
// Include user drag interaction offset
const dragOffset = isVertical.value ? dragged.y : dragged.x;
const totalOffset = scrolledOffset + dragOffset;
let totalOffset = scrolledOffset.value + dragOffset;
if (!config.wrapAround && config.preventExcessiveDragging) {
let maxSlidingValue = 0;
if (isAuto.value) {
maxSlidingValue = slidesRect.value.reduce((acc, slide) => acc + slide[dimension.value], 0);
}
else {
maxSlidingValue =
(slidesCount.value - Number(config.itemsToShow)) * effectiveSlideSize.value;
}
const min = isReversed.value ? 0 : -1 * maxSlidingValue;
const max = isReversed.value ? maxSlidingValue : 0;
totalOffset = getNumberInRange({
val: totalOffset,
min,
max,
});
}
return `translate${translateAxis}(${totalOffset}px)`;
});
const trackStyle = vue.computed(() => ({
transform: config.slideEffect === 'slide' ? trackTransform.value : undefined,
gap: config.gap > 0 ? `${config.gap}px` : undefined,
'--vc-trk-transition-duration': isSliding.value
? `${config.transition}ms`
const carouselStyle = vue.computed(() => ({
'--vc-transition-duration': isSliding.value
? toCssValue(config.transition, 'ms')
: undefined,
'--vc-trk-height': trackHeight.value,
'--vc-trk-cloned-offset': `${clonedSlidesOffset.value}px`,
'--vc-slide-gap': toCssValue(config.gap),
'--vc-carousel-height': toCssValue(config.height),
'--vc-cloned-offset': toCssValue(clonedSlidesOffset.value),
}));
const nav = { slideTo, next, prev };
const provided = vue.reactive({
activeSlide: activeSlideIndex,
config,
currentSlide: currentSlideIndex,
isSliding,
isVertical,
maxSlide: maxSlideIndex,
minSlide: minSlideIndex,
nav,
normalizedDir,
slideRegistry,
slideSize,
slides,
slidesCount,
viewport,
visibleRange,
});
vue.provide(injectCarousel, provided);
const data = vue.reactive({
config,
currentSlide: currentSlideIndex,
maxSlide: maxSlideIndex,
middleSlide: middleSlideIndex,
minSlide: minSlideIndex,
slideSize,
slidesCount,
});
expose({
data,
nav,
next,
prev,
restartCarousel,
slideTo,
updateBreakpointsConfig,
updateSlideSize,
updateSlidesData,
});
return () => {

@@ -1046,3 +1218,3 @@ var _a;

class: 'carousel__track',
style: trackStyle.value,
style: { transform: trackTransform.value },
onMousedownCapture: config.mouseDrag ? handleDragStart : null,

@@ -1066,2 +1238,3 @@ onTouchstartPassiveCapture: config.touchDrag ? handleDragStart : null,

dir: normalizedDir.value,
style: carouselStyle.value,
'aria-label': config.i18n['ariaGallery'],

@@ -1080,21 +1253,21 @@ tabindex: '0',

(function (IconName) {
IconName["arrowUp"] = "arrowUp";
IconName["arrowDown"] = "arrowDown";
IconName["arrowLeft"] = "arrowLeft";
IconName["arrowRight"] = "arrowRight";
IconName["arrowLeft"] = "arrowLeft";
IconName["arrowUp"] = "arrowUp";
})(IconName || (IconName = {}));
const iconI18n = (name) => `icon${name.charAt(0).toUpperCase() + name.slice(1)}`;
const icons = {
arrowDown: 'M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 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',
arrowRight: 'M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z',
arrowUp: 'M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z',
};
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 = {
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 Icon = vue.defineComponent({

@@ -1143,6 +1316,6 @@ props: {

const directionIcons = {
btt: 'arrowDown',
ltr: 'arrowLeft',
rtl: 'arrowRight',
ttb: 'arrowUp',
btt: 'arrowDown',
};

@@ -1153,6 +1326,6 @@ return directionIcons[carousel.normalizedDir];

const directionIcons = {
btt: 'arrowUp',
ltr: 'arrowRight',
rtl: 'arrowLeft',
ttb: 'arrowDown',
btt: 'arrowUp',
};

@@ -1195,6 +1368,10 @@ return directionIcons[carousel.normalizedDir];

}
const offset = vue.computed(() => calculateOffset(carousel.config.snapAlign, carousel.config.itemsToShow));
const isPaginated = vue.computed(() => props.paginateByItemsToShow && carousel.config.itemsToShow > 1);
const currentPage = vue.computed(() => Math.ceil((carousel.activeSlide - offset.value) / carousel.config.itemsToShow));
const pageCount = vue.computed(() => Math.ceil(carousel.slidesCount / carousel.config.itemsToShow));
const itemsToShow = vue.computed(() => carousel.config.itemsToShow);
const offset = vue.computed(() => getSnapAlignOffset({
align: carousel.config.snapAlign,
itemsToShow: itemsToShow.value,
}));
const isPaginated = vue.computed(() => props.paginateByItemsToShow && itemsToShow.value > 1);
const currentPage = vue.computed(() => Math.ceil((carousel.activeSlide - offset.value) / itemsToShow.value));
const pageCount = vue.computed(() => Math.ceil(carousel.slidesCount / itemsToShow.value));
const isActive = (slide) => mapNumberToRange(isPaginated.value

@@ -1231,3 +1408,3 @@ ? {

onClick: () => carousel.nav.slideTo(isPaginated.value
? slide * carousel.config.itemsToShow + offset.value
? Math.floor(slide * +carousel.config.itemsToShow + offset.value)
: slide),

@@ -1246,6 +1423,2 @@ });

props: {
isClone: {
type: Boolean,
default: false,
},
id: {

@@ -1259,4 +1432,12 @@ type: String,

},
isClone: {
type: Boolean,
default: false,
},
position: {
type: String,
default: undefined,
},
},
setup(props, { slots, expose }) {
setup(props, { attrs, slots, expose }) {
const carousel = vue.inject(injectCarousel);

@@ -1271,5 +1452,11 @@ vue.provide(injectCarousel, undefined); // Don't provide for nested slides

};
const instance = vue.getCurrentInstance();
const getBoundingRect = () => {
const el = instance.vnode.el;
return el ? el.getBoundingClientRect() : { width: 0, height: 0 };
};
expose({
id: props.id,
setIndex,
getBoundingRect,
});

@@ -1279,13 +1466,14 @@ const isActive = vue.computed(() => currentIndex.value === carousel.activeSlide);

const isNext = vue.computed(() => currentIndex.value === carousel.activeSlide + 1);
const isVisible = vue.computed(() => currentIndex.value >= Math.floor(carousel.scrolledIndex) &&
currentIndex.value <
Math.ceil(carousel.scrolledIndex) + carousel.config.itemsToShow);
const isVisible = vue.computed(() => currentIndex.value >= carousel.visibleRange.min &&
currentIndex.value <= carousel.visibleRange.max);
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}%`;
if (carousel.config.itemsToShow === 'auto') {
return;
}
const itemsToShow = carousel.config.itemsToShow;
const dimension = carousel.config.gap > 0 && itemsToShow > 1
? `calc(${100 / itemsToShow}% - ${(carousel.config.gap * (itemsToShow - 1)) / itemsToShow}px)`
: `${100 / itemsToShow}%`;
return carousel.isVertical ? { height: dimension } : { width: dimension };
});
const instance = vue.getCurrentInstance();
carousel.slideRegistry.registerSlide(instance, props.index);

@@ -1310,3 +1498,3 @@ vue.onUnmounted(() => {

return vue.h('li', {
style: slideStyle.value,
style: [attrs.style, Object.assign({}, slideStyle.value)],
class: {

@@ -1331,2 +1519,3 @@ carousel__slide: true,

}, (_b = slots.default) === null || _b === void 0 ? void 0 : _b.call(slots, {
currentIndex: currentIndex.value,
isActive: isActive.value,

@@ -1333,0 +1522,0 @@ isClone: props.isClone,

/**
* Vue 3 Carousel 0.12.0
* (c) 2024
* Vue 3 Carousel 0.13.0
* (c) 2025
* @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 i=Symbol("carousel"),n=["center","start","end","center-even","center-odd"],o=["slide","fade"],a=["viewport","carousel"],l=["ltr","left-to-right","rtl","right-to-left","ttb","top-to-bottom","btt","bottom-to-top"],r={ariaNextSlide:"Navigate to next slide",ariaPreviousSlide:"Navigate to previous slide",ariaNavigateToSlide:"Navigate to slide {slideNumber}",ariaNavigateToPage:"Navigate to page {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"},s={"left-to-right":"ltr","right-to-left":"rtl","top-to-bottom":"ttb","bottom-to-top":"btt"},u=Object.values(s),d={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:l[0],breakpointMode:a[0],breakpoints:void 0,i18n:r,ignoreAnimations:!1,slideEffect:o[0]},c=e=>{const i=t.shallowReactive([]),n=t.shallowReactive([]),o=e=>{void 0!==e?i.slice(e).forEach(((t,i)=>{var n;null===(n=t.exposed)||void 0===n||n.setIndex(e+i)})):i.forEach(((e,t)=>{var i;null===(i=e.exposed)||void 0===i||i.setIndex(t)}))};return{registerSlide:(t,a)=>{if(!t)return;if(t.props.isClone)return void n.push(t);const l=null!=a?a:i.length;i.splice(l,0,t),o(l),e("slide-registered",{slide:t,index:l})},unregisterSlide:t=>{const n=i.indexOf(t);-1!==n&&(e("slide-unregistered",{slide:t,index:n}),i.splice(n,1),o(n))},cleanup:()=>{i.splice(0,i.length)},getSlides:()=>i,getClonedSlides:()=>n}};function v({val:e,max:t,min:i}){return t<i?e:Math.min(Math.max(e,isNaN(i)?e:i),isNaN(t)?e:t)}const p=(e,t)=>{switch(e){default:case"start":return 0;case"center":case"center-odd":return(t-1)/2;case"center-even":return(t-2)/2;case"end":return t-1}};function m({val:e,max:t,min:i=0}){const n=t-i+1;return((e-i)%n+n)%n+i}function f(e="",t={}){return Object.entries(t).reduce(((e,[t,i])=>e.replace(`{${t}}`,String(i))),e)}function g(e,t=0){let i=!1,n=0,o=null;function a(...a){if(i)return;i=!0;const l=()=>{o=requestAnimationFrame((o=>{o-n>t?(n=o,e(...a),i=!1):l()}))};l()}return a.cancel=()=>{o&&(cancelAnimationFrame(o),o=null,i=!1)},a}function h({slides:e,position:i,toShow:n}){const o=[],a="before"===i,l=a?-n:0,r=a?0:n;if(e.length<=0)return o;for(let n=l;n<r;n++){const l={index:a?n:n+e.length,isClone:!0,id:void 0,key:`clone-${i}-${n}`},r=e[(n%e.length+e.length)%e.length].vnode,s=t.cloneVNode(r,l);s.el=null,o.push(s)}return o}function w(e){if(!(e.el&&e.el instanceof Element))return;const t=e.el.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])');for(const e of t)e instanceof HTMLElement&&!e.hasAttribute("disabled")&&"true"!==e.getAttribute("aria-hidden")&&e.setAttribute("tabindex","-1")}const S=t.defineComponent({name:"CarouselAria",setup(){const e=t.inject(i);return e?()=>t.h("div",{class:["carousel__liveregion","carousel__sr-only"],"aria-live":"polite","aria-atomic":"true"},f(e.config.i18n.itemXofY,{currentSlide:e.currentSlide+1,slidesCount:e.slidesCount})):()=>""}}),b={enabled:{default:d.enabled,type:Boolean},itemsToShow:{default:d.itemsToShow,type:Number},itemsToScroll:{default:d.itemsToScroll,type:Number},wrapAround:{default:d.wrapAround,type:Boolean},gap:{default:d.gap,type:Number},height:{default:d.height,type:[Number,String]},snapAlign:{default:d.snapAlign,validator:e=>n.includes(e)},transition:{default:d.transition,type:Number},breakpointMode:{default:d.breakpointMode,validator:e=>a.includes(e)},breakpoints:{default:d.breakpoints,type:Object},autoplay:{default:d.autoplay,type:Number},pauseAutoplayOnHover:{default:d.pauseAutoplayOnHover,type:Boolean},modelValue:{default:void 0,type:Number},mouseDrag:{default:d.mouseDrag,type:Boolean},touchDrag:{default:d.touchDrag,type:Boolean},dir:{type:String,default:d.dir,validator:e=>l.includes(e)},i18n:{default:d.i18n,type:Object},ignoreAnimations:{default:!1,type:[Array,Boolean,String]},slideEffect:{type:String,default:d.slideEffect,validator:e=>o.includes(e)}},y=t.defineComponent({name:"VueCarousel",props:b,emits:["init","drag","slide-start","loop","update:modelValue","slide-end","before-init","slide-registered","slide-unregistered"],setup(e,{slots:n,emit:o,expose:a}){var l;const r=c(o),u=r.getSlides(),f=t.computed((()=>u.length)),w=t.ref(null),b=t.ref(null),y=t.ref(0),x=t.computed((()=>{return Object.assign(Object.assign(Object.assign({},d),(t=e,i=["breakpoints","modelValue"],Object.keys(t).filter((e=>!i.includes(e))).reduce(((e,i)=>(e[i]=t[i],e)),{}))),{i18n:Object.assign(Object.assign({},d.i18n),e.i18n)});var t,i})),A=t.shallowReactive(Object.assign({},x.value)),T=t.ref(null!==(l=e.modelValue)&&void 0!==l?l:0),C=t.ref(T.value);t.watch(T,(e=>C.value=e));const _=t.ref(0),N=t.computed((()=>Math.ceil((f.value-1)/2))),O=t.computed((()=>function({config:e,slidesCount:t}){const{snapAlign:i="center",wrapAround:n,itemsToShow:o=1}=e;return Math.max(function(){switch(n?"":i){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:A,slidesCount:f.value}))),E=t.computed((()=>function({config:e,slidesCount:t}){const{snapAlign:i="center",wrapAround:n,itemsToShow:o=1}=e;return n||o>t?0:Math.max(0,Math.floor(p(i,o)))}({config:A,slidesCount:f.value})));let I=null,L=null,M=null;const k=t.computed((()=>y.value+A.gap)),D=t.computed((()=>{const e=A.dir||"ltr";return e in s?s[e]:e})),R=t.computed((()=>["rtl","btt"].includes(D.value))),j=t.computed((()=>["ttb","btt"].includes(D.value)));function B(){var t;if(!q.value)return;const i=("carousel"===x.value.breakpointMode?null===(t=w.value)||void 0===t?void 0:t.getBoundingClientRect().width:"undefined"!=typeof window?window.innerWidth:0)||0,n=Object.keys(e.breakpoints||{}).map((e=>Number(e))).sort(((e,t)=>+t-+e)),o={};n.some((t=>i>=t&&(Object.assign(o,e.breakpoints[t]),o.i18n&&Object.assign(o.i18n,x.value.i18n,e.breakpoints[t].i18n),!0))),Object.assign(A,x.value,o)}const P=g((()=>{B(),U(),F()})),V=t.computed((()=>(A.itemsToShow-1)*A.gap)),z=t.shallowReactive(new Set);function F(){if(!b.value)return;let e=1;if(z.forEach((t=>{const i=function(e){const{transform:t}=window.getComputedStyle(e);return t.split(/[(,)]/).slice(1,-1).map((e=>parseFloat(e)))}(t);6===i.length&&(e*=i[0])})),j.value){if("auto"!==A.height){const e="string"==typeof A.height&&isNaN(parseInt(A.height))?b.value.getBoundingClientRect().height:parseInt(A.height);y.value=(e-V.value)/A.itemsToShow}}else{const t=b.value.getBoundingClientRect().width;y.value=(t/e-V.value)/A.itemsToShow}}function U(){!A.wrapAround&&f.value>0&&(T.value=v({val:T.value,max:O.value,min:E.value})),A.itemsToShow=v({val:A.itemsToShow,max:f.value,min:1})}const $=t.computed((()=>"string"==typeof e.ignoreAnimations?e.ignoreAnimations.split(","):Array.isArray(e.ignoreAnimations)?e.ignoreAnimations:!e.ignoreAnimations&&[]));let X;t.watchEffect((()=>U())),t.watchEffect((()=>{F()}));const Y=e=>{const t=e.target;if(!(!(null==t?void 0:t.contains(w.value))||Array.isArray($.value)&&$.value.includes(e.animationName)||(z.add(t),X))){const e=()=>{X=requestAnimationFrame((()=>{F(),e()}))};e()}},G=e=>{const t=e.target;t&&z.delete(t),X&&0===z.size&&(cancelAnimationFrame(X),F())},q=t.ref(!1);"undefined"!=typeof document&&t.watchEffect((()=>{q.value&&!1!==$.value?(document.addEventListener("animationstart",Y),document.addEventListener("animationend",G)):(document.removeEventListener("animationstart",Y),document.removeEventListener("animationend",G))})),t.onMounted((()=>{q.value=!0,B(),re(),w.value&&(M=new ResizeObserver(P),M.observe(w.value)),o("init")})),t.onBeforeUnmount((()=>{q.value=!1,r.cleanup(),L&&clearTimeout(L),X&&cancelAnimationFrame(X),I&&clearInterval(I),M&&(M.disconnect(),M=null),"undefined"!=typeof document&&ne(),w.value&&(w.value.removeEventListener("transitionend",F),w.value.removeEventListener("animationiteration",F))}));let H=!1;const W={x:0,y:0},K=t.reactive({x:0,y:0}),Z=t.ref(!1),J=t.ref(!1),Q=()=>{Z.value=!0},ee=()=>{Z.value=!1},te=g((e=>{if(!e.ctrlKey)switch(e.key){case"ArrowLeft":case"ArrowUp":j.value===e.key.endsWith("Up")&&(R.value?me.next(!0):me.prev(!0));break;case"ArrowRight":case"ArrowDown":j.value===e.key.endsWith("Down")&&(R.value?me.prev(!0):me.next(!0))}}),200),ie=()=>{document.addEventListener("keydown",te)},ne=()=>{document.removeEventListener("keydown",te)};function oe(e){const t=e.target.tagName;if(["INPUT","TEXTAREA","SELECT"].includes(t)||de.value)return;if(H="touchstart"===e.type,!H&&(e.preventDefault(),0!==e.button))return;W.x="touches"in e?e.touches[0].clientX:e.clientX,W.y="touches"in e?e.touches[0].clientY:e.clientY;const i=H?"touchmove":"mousemove",n=H?"touchend":"mouseup";document.addEventListener(i,ae,{passive:!1}),document.addEventListener(n,le,{passive:!0})}const ae=g((e=>{J.value=!0;const t="touches"in e?e.touches[0].clientX:e.clientX,i="touches"in e?e.touches[0].clientY:e.clientY;K.x=t-W.x,K.y=i-W.y;const n=function(e){const{isVertical:t,isReversed:i,dragged:n,effectiveSlideSize:o}=e,a=t?n.y:n.x;if(0===a)return 0;const l=Math.round(a/o);return i?l:-l}({isVertical:j.value,isReversed:R.value,dragged:K,effectiveSlideSize:k.value});C.value=A.wrapAround?T.value+n:v({val:T.value+n,max:O.value,min:E.value}),o("drag",{deltaX:K.x,deltaY:K.y})}));function le(){if(ae.cancel(),C.value!==T.value&&!H){const e=t=>{t.preventDefault(),window.removeEventListener("click",e)};window.addEventListener("click",e)}ce(C.value),K.x=0,K.y=0,J.value=!1;const e=H?"touchmove":"mousemove",t=H?"touchend":"mouseup";document.removeEventListener(e,ae),document.removeEventListener(t,le)}function re(){!A.autoplay||A.autoplay<=0||(I=setInterval((()=>{A.pauseAutoplayOnHover&&Z.value||ve()}),A.autoplay))}function se(){I&&(clearInterval(I),I=null)}function ue(){se(),re()}const de=t.ref(!1);function ce(e,t=!1){if(!t&&de.value)return;let i=e,n=e;_.value=T.value,A.wrapAround?n=m({val:i,max:O.value,min:0}):i=v({val:i,max:O.value,min:E.value}),o("slide-start",{slidingToIndex:e,currentSlideIndex:T.value,prevSlideIndex:_.value,slidesCount:f.value}),se(),de.value=!0,T.value=i,n!==i&&he.pause(),o("update:modelValue",n);L=setTimeout((()=>{A.wrapAround&&n!==i&&(he.resume(),T.value=n,o("loop",{currentSlideIndex:T.value,slidingToIndex:e})),o("slide-end",{currentSlideIndex:T.value,prevSlideIndex:_.value,slidesCount:f.value}),de.value=!1,ue()}),A.transition)}function ve(e=!1){ce(T.value+A.itemsToScroll,e)}function pe(e=!1){ce(T.value-A.itemsToScroll,e)}const me={slideTo:ce,next:ve,prev:pe},fe=t.computed((()=>function({config:e,currentSlide:t,slidesCount:i}){const{snapAlign:n="center",wrapAround:o,itemsToShow:a=1}=e,l=p(n,a);return o?t-l:v({val:t-l,max:i-a,min:0})}({config:A,currentSlide:T.value,slidesCount:f.value}))),ge=t.reactive({config:A,slidesCount:f,viewport:b,slides:u,scrolledIndex:fe,currentSlide:T,activeSlide:C,maxSlide:O,minSlide:E,slideSize:y,isVertical:j,normalizedDir:D,nav:me,isSliding:de,slideRegistry:r});t.provide(i,ge),t.provide("config",A),t.provide("slidesCount",f),t.provide("currentSlide",T),t.provide("maxSlide",O),t.provide("minSlide",E),t.provide("slideSize",y),t.provide("isVertical",j),t.provide("normalizeDir",D),t.provide("nav",me),t.provide("isSliding",de),t.watch((()=>[x.value,e.breakpoints]),(()=>B()),{deep:!0}),t.watch((()=>e.autoplay),(()=>ue()));const he=t.watch((()=>e.modelValue),(e=>{e!==T.value&&ce(Number(e),!0)}));o("before-init");const we=t.reactive({config:A,slidesCount:f,slideSize:y,currentSlide:T,maxSlide:O,minSlide:E,middleSlide:N});a({updateBreakpointsConfig:B,updateSlidesData:U,updateSlideSize:F,restartCarousel:function(){B(),U(),F(),ue()},slideTo:ce,next:ve,prev:pe,nav:me,data:we});const Se=t.computed((()=>"auto"===A.height?j.value&&y.value?`${y.value*A.itemsToShow+V.value}px`:void 0:"number"==typeof A.height||parseFloat(A.height).toString()===A.height?`${A.height}px`:A.height)),be=t.computed((()=>{if(!A.wrapAround)return{before:0,after:0};const e=Math.ceil(A.itemsToShow+(A.itemsToScroll-1)),t=e-C.value,i=e-(f.value-(C.value+1));return{before:Math.max(0,t),after:Math.max(0,i)}})),ye=t.computed((()=>be.value.before*k.value*-1)),xe=t.computed((()=>{const e=R.value?1:-1;return`translate${j.value?"Y":"X"}(${fe.value*k.value*e+(j.value?K.y:K.x)}px)`})),Ae=t.computed((()=>({transform:"slide"===A.slideEffect?xe.value:void 0,gap:A.gap>0?`${A.gap}px`:void 0,"--vc-trk-transition-duration":de.value?`${A.transition}ms`:void 0,"--vc-trk-height":Se.value,"--vc-trk-cloned-offset":`${ye.value}px`})));return()=>{var e;const i=n.default||n.slides,o=(null==i?void 0:i(we))||[],{before:a,after:l}=be.value,r=[...h({slides:u,position:"before",toShow:a}),...o,...h({slides:u,position:"after",toShow:l})];if(!A.enabled||!r.length)return t.h("section",{ref:w,class:["carousel","is-disabled"]},r);const s=(null===(e=n.addons)||void 0===e?void 0:e.call(n,we))||[],d=t.h("ol",{class:"carousel__track",style:Ae.value,onMousedownCapture:A.mouseDrag?oe:null,onTouchstartPassiveCapture:A.touchDrag?oe:null},r),c=t.h("div",{class:"carousel__viewport",ref:b},d);return t.h("section",{ref:w,class:["carousel",`is-${D.value}`,`is-effect-${A.slideEffect}`,{"is-vertical":j.value,"is-sliding":de.value,"is-dragging":J.value,"is-hover":Z.value}],dir:D.value,"aria-label":A.i18n.ariaGallery,tabindex:"0",onFocus:ie,onBlur:ne,onMouseenter:Q,onMouseleave:ee},[c,s,t.h(S)])}}});var x;!function(e){e.arrowUp="arrowUp",e.arrowDown="arrowDown",e.arrowRight="arrowRight",e.arrowLeft="arrowLeft"}(x||(x={}));const A=e=>`icon${e.charAt(0).toUpperCase()+e.slice(1)}`,T=e=>e&&e in x,C={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"},_=t.defineComponent({props:{name:{type:String,required:!0,validator:T},title:{type:String,default:e=>e.name?d.i18n[A(e.name)]:""}},setup(e){const n=t.inject(i,null);return()=>{const i=e.name;if(!i||!T(i))return;const o=C[i],a=t.h("path",{d:o}),l=(null==n?void 0:n.config.i18n[A(i)])||e.title,r=t.h("title",l);return t.h("svg",{class:"carousel__icon",viewBox:"0 0 24 24",role:"img","aria-label":l},[r,a])}}}),N=t.defineComponent({name:"CarouselNavigation",inheritAttrs:!1,setup(e,{slots:n,attrs:o}){const a=t.inject(i);if(!a)return()=>"";const{next:l,prev:r}=n,s=t.computed((()=>!a.config.wrapAround&&a.currentSlide<=a.minSlide)),u=t.computed((()=>!a.config.wrapAround&&a.currentSlide>=a.maxSlide));return()=>{const{i18n:e}=a.config;return[t.h("button",Object.assign(Object.assign({type:"button",disabled:s.value,"aria-label":e.ariaPreviousSlide,title:e.ariaPreviousSlide,onClick:a.nav.prev},o),{class:["carousel__prev",{"carousel__prev--disabled":s.value},o.class]}),(null==r?void 0:r())||t.h(_,{name:{ltr:"arrowLeft",rtl:"arrowRight",ttb:"arrowUp",btt:"arrowDown"}[a.normalizedDir]})),t.h("button",Object.assign(Object.assign({type:"button",disabled:u.value,"aria-label":e.ariaNextSlide,title:e.ariaNextSlide,onClick:a.nav.next},o),{class:["carousel__next",{"carousel__next--disabled":u.value},o.class]}),(null==l?void 0:l())||t.h(_,{name:{ltr:"arrowRight",rtl:"arrowLeft",ttb:"arrowDown",btt:"arrowUp"}[a.normalizedDir]}))]}}}),O=t.defineComponent({name:"CarouselPagination",props:{disableOnClick:{type:Boolean},paginateByItemsToShow:{type:Boolean}},setup(e){const n=t.inject(i);if(!n)return()=>"";const o=t.computed((()=>p(n.config.snapAlign,n.config.itemsToShow))),a=t.computed((()=>e.paginateByItemsToShow&&n.config.itemsToShow>1)),l=t.computed((()=>Math.ceil((n.activeSlide-o.value)/n.config.itemsToShow))),r=t.computed((()=>Math.ceil(n.slidesCount/n.config.itemsToShow))),s=e=>m(a.value?{val:l.value,max:r.value-1,min:0}:{val:n.activeSlide,max:n.maxSlide,min:n.minSlide})===e;return()=>{var i,l;const u=[];for(let d=a.value?0:n.minSlide;d<=(a.value?r.value-1:n.maxSlide);d++){const r=f(n.config.i18n[a.value?"ariaNavigateToPage":"ariaNavigateToSlide"],{slideNumber:d+1}),c=s(d),v=t.h("button",{type:"button",class:{"carousel__pagination-button":!0,"carousel__pagination-button--active":c},"aria-label":r,"aria-pressed":c,"aria-controls":null===(l=null===(i=n.slides[d])||void 0===i?void 0:i.exposed)||void 0===l?void 0:l.id,title:r,disabled:e.disableOnClick,onClick:()=>n.nav.slideTo(a.value?d*n.config.itemsToShow+o.value:d)}),p=t.h("li",{class:"carousel__pagination-item",key:d},v);u.push(p)}return t.h("ol",{class:"carousel__pagination"},u)}}}),E=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:void 0}},setup(e,{slots:n,expose:o}){const a=t.inject(i);if(t.provide(i,void 0),!a)return()=>"";const l=t.ref(e.index);o({id:e.id,setIndex:e=>{l.value=e}});const r=t.computed((()=>l.value===a.activeSlide)),s=t.computed((()=>l.value===a.activeSlide-1)),u=t.computed((()=>l.value===a.activeSlide+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();return a.slideRegistry.registerSlide(v,e.index),t.onUnmounted((()=>{a.slideRegistry.unregisterSlide(v)})),e.isClone&&(t.onMounted((()=>{w(v.vnode)})),t.onUpdated((()=>{w(v.vnode)}))),()=>{var i,o;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":s.value,"carousel__slide--next":u.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===(o=n.default)||void 0===o?void 0:o.call(n,{isActive:r.value,isClone:e.isClone,isPrev:s.value,isNext:u.value,isSliding:a.isSliding,isVisible:d.value})):null===(i=n.default)||void 0===i?void 0:i.call(n)}}});e.BREAKPOINT_MODE_OPTIONS=a,e.Carousel=y,e.DEFAULT_CONFIG=d,e.DIR_MAP=s,e.DIR_OPTIONS=l,e.I18N_DEFAULT_CONFIG=r,e.Icon=_,e.NORMALIZED_DIR_OPTIONS=u,e.Navigation=N,e.Pagination=O,e.SLIDE_EFFECTS=o,e.SNAP_ALIGN_OPTIONS=n,e.Slide=E,e.createSlideRegistry=c,e.icons=C,e.injectCarousel=i}));
!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 i=["viewport","carousel"],n={"bottom-to-top":"btt","left-to-right":"ltr","right-to-left":"rtl","top-to-bottom":"ttb"},a=["ltr","left-to-right","rtl","right-to-left","ttb","top-to-bottom","btt","bottom-to-top"],l={ariaGallery:"Gallery",ariaNavigateToPage:"Navigate to page {slideNumber}",ariaNavigateToSlide:"Navigate to slide {slideNumber}",ariaNextSlide:"Navigate to next slide",ariaPreviousSlide:"Navigate to previous slide",iconArrowDown:"Arrow pointing downwards",iconArrowLeft:"Arrow pointing to the left",iconArrowRight:"Arrow pointing to the right",iconArrowUp:"Arrow pointing upwards",itemXofY:"Item {currentSlide} of {slidesCount}"},o=Object.values(n),r=["slide","fade"],u=["center","start","end","center-even","center-odd"],s={autoplay:0,breakpointMode:i[0],breakpoints:void 0,dir:a[0],enabled:!0,gap:0,height:"auto",i18n:l,ignoreAnimations:!1,itemsToScroll:1,itemsToShow:1,modelValue:0,mouseDrag:!0,pauseAutoplayOnHover:!1,preventExcessiveDragging:!1,slideEffect:r[0],snapAlign:u[0],touchDrag:!0,transition:300,wrapAround:!1},d=Symbol("carousel"),c=e=>{const i=t.shallowReactive([]),n=e=>{void 0!==e?i.slice(e).forEach(((t,i)=>{var n;null===(n=t.exposed)||void 0===n||n.setIndex(e+i)})):i.forEach(((e,t)=>{var i;null===(i=e.exposed)||void 0===i||i.setIndex(t)}))};return{cleanup:()=>{i.splice(0,i.length)},getSlides:()=>i,registerSlide:(t,a)=>{if(!t)return;if(t.props.isClone)return;const l=null!=a?a:i.length;i.splice(l,0,t),n(l),e("slide-registered",{slide:t,index:l})},unregisterSlide:t=>{const a=i.indexOf(t);-1!==a&&(e("slide-unregistered",{slide:t,index:a}),i.splice(a,1),n(a))}}};function v({slides:e,position:i,toShow:n}){const a=[],l="before"===i,o=l?-n:0,r=l?0:n;if(e.length<=0)return a;for(let n=o;n<r;n++){const o={index:l?n:n+e.length,isClone:!0,position:i,id:void 0,key:`clone-${i}-${n}`},r=e[(n%e.length+e.length)%e.length].vnode,u=t.cloneVNode(r,o);u.el=null,a.push(u)}return a}function p(e){if(!(e.el&&e.el instanceof Element))return;const t=e.el.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])');for(const e of t)e instanceof HTMLElement&&!e.hasAttribute("disabled")&&"true"!==e.getAttribute("aria-hidden")&&e.setAttribute("tabindex","-1")}function m({val:e,max:t,min:i}){return t<i?e:Math.min(Math.max(e,isNaN(i)?e:i),isNaN(t)?e:t)}function g(e){let t=1,i=1;return e.forEach((e=>{const n=function(e){const{transform:t}=window.getComputedStyle(e);return t.split(/[(,)]/).slice(1,-1).map((e=>parseFloat(e)))}(e);6===n.length&&(t/=n[0],i/=n[3])})),{widthMultiplier:t,heightMultiplier:i}}function f({slideSize:e,viewportSize:t,align:i,itemsToShow:n}){return void 0!==n?function(e,t){switch(e){case"start":default:return 0;case"center":case"center-odd":return(t-1)/2;case"center-even":return(t-2)/2;case"end":return t-1}}(i,n):void 0!==e&&void 0!==t?function(e,t,i){switch(e){case"start":default:return 0;case"center":case"center-odd":return(i-t)/2;case"center-even":return i/2-t;case"end":return i-t}}(i,e,t):0}function h(e="",t={}){return Object.entries(t).reduce(((e,[t,i])=>e.replace(`{${t}}`,String(i))),e)}function w({val:e,max:t,min:i=0}){const n=t-i+1;return((e-i)%n+n)%n+i}function b(e,t=0){let i=!1,n=0,a=null;function l(...l){if(i)return;i=!0;const o=()=>{a=requestAnimationFrame((a=>{a-n>t?(n=a,e(...l),i=!1):o()}))};o()}return l.cancel=()=>{a&&(cancelAnimationFrame(a),a=null,i=!1)},l}function S(e,t="px"){if(null!=e&&""!==e)return"number"==typeof e||parseFloat(e).toString()===e?`${e}${t}`:e}const y=t.defineComponent({name:"CarouselAria",setup(){const e=t.inject(d);return e?()=>t.h("div",{class:["carousel__liveregion","carousel__sr-only"],"aria-live":"polite","aria-atomic":"true"},h(e.config.i18n.itemXofY,{currentSlide:e.currentSlide+1,slidesCount:e.slidesCount})):()=>""}}),x={autoplay:{default:s.autoplay,type:Number},breakpoints:{default:s.breakpoints,type:Object},breakpointMode:{default:s.breakpointMode,validator:e=>i.includes(e)},enabled:{default:s.enabled,type:Boolean},gap:{default:s.gap,type:Number},height:{default:s.height,type:[Number,String]},ignoreAnimations:{default:!1,type:[Array,Boolean,String]},itemsToScroll:{default:s.itemsToScroll,type:Number},itemsToShow:{default:s.itemsToShow,type:[Number,String]},i18n:{default:s.i18n,type:Object},modelValue:{default:void 0,type:Number},mouseDrag:{default:s.mouseDrag,type:Boolean},touchDrag:{default:s.touchDrag,type:Boolean},pauseAutoplayOnHover:{default:s.pauseAutoplayOnHover,type:Boolean},preventExcessiveDragging:{default:!1,type:Boolean,validator:(e,t)=>(e&&t.wrapAround&&console.warn('[vue3-carousel warn]: "preventExcessiveDragging" cannot be used with wrapAround. The setting will be ignored.'),!0)},snapAlign:{default:s.snapAlign,validator:e=>u.includes(e)},slideEffect:{type:String,default:s.slideEffect,validator:e=>r.includes(e)},transition:{default:s.transition,type:Number},dir:{type:String,default:s.dir,validator(e,t){if(!a.includes(e))return!1;return!["ttb","btt"].includes(e in n?n[e]:e)||t.height&&"auto"!==t.height||console.warn(`[vue3-carousel warn]: The dir "${e}" is not supported with height "auto".`),!0}},wrapAround:{default:s.wrapAround,type:Boolean}},A=t.defineComponent({name:"VueCarousel",props:x,emits:["before-init","drag","init","loop","slide-end","slide-registered","slide-start","slide-unregistered","update:modelValue"],setup(e,{slots:i,emit:a,expose:l}){var o;const r=c(a),u=r.getSlides(),p=t.computed((()=>u.length)),h=t.ref(null),x=t.ref(null),A=t.ref(0),T=t.computed((()=>{return Object.assign(Object.assign(Object.assign({},s),(t=e,i=["breakpoints","modelValue"],Object.keys(t).filter((e=>!i.includes(e))).reduce(((e,i)=>(e[i]=t[i],e)),{}))),{i18n:Object.assign(Object.assign({},s.i18n),e.i18n)});var t,i})),N=t.shallowReactive(Object.assign({},T.value)),_=t.ref(null!==(o=e.modelValue)&&void 0!==o?o:0),C=t.ref(_.value);t.watch(_,(e=>C.value=e));const E=t.ref(0),M=t.computed((()=>Math.ceil((p.value-1)/2))),O=t.computed((()=>p.value-1)),L=t.computed((()=>0));let I=null,D=null,k=null;const R=t.computed((()=>A.value+N.gap)),j=t.computed((()=>{const e=N.dir||"ltr";return e in n?n[e]:e})),B=t.computed((()=>["rtl","btt"].includes(j.value))),z=t.computed((()=>["ttb","btt"].includes(j.value))),P=t.computed((()=>"auto"===N.itemsToShow)),V=t.computed((()=>z.value?"height":"width"));function F(){var t;if(!J.value)return;const i=("carousel"===T.value.breakpointMode?null===(t=h.value)||void 0===t?void 0:t.getBoundingClientRect().width:"undefined"!=typeof window?window.innerWidth:0)||0,n=Object.keys(e.breakpoints||{}).map((e=>Number(e))).sort(((e,t)=>+t-+e)),a={};n.some((t=>i>=t&&(Object.assign(a,e.breakpoints[t]),a.i18n&&Object.assign(a.i18n,T.value.i18n,e.breakpoints[t].i18n),!0))),Object.assign(N,T.value,a)}const U=b((()=>{F(),q(),G()})),$=t.shallowReactive(new Set),X=t.ref([]);const Y=t.ref({width:0,height:0});function G(){if(!x.value)return;const e=g($);if(function({widthMultiplier:e,heightMultiplier:t}){var i;const n=(null===(i=x.value)||void 0===i?void 0:i.getBoundingClientRect())||{width:0,height:0};Y.value={width:n.width*e,height:n.height*t}}(e),function({widthMultiplier:e,heightMultiplier:t}){X.value=u.map((i=>{var n;const a=null===(n=i.exposed)||void 0===n?void 0:n.getBoundingRect();return{width:a.width*e,height:a.height*t}}))}(e),P.value)A.value=0===(t=X.value.map((e=>e[V.value]))).length?0:t.reduce(((e,t)=>e+t),0)/t.length;else{const e=Number(N.itemsToShow),t=(e-1)*N.gap;A.value=(Y.value[V.value]-t)/e}var t}function q(){!N.wrapAround&&p.value>0&&(_.value=m({val:_.value,max:O.value,min:L.value})),P.value||(N.itemsToShow=m({val:Number(N.itemsToShow),max:p.value,min:1}))}const H=t.computed((()=>"string"==typeof e.ignoreAnimations?e.ignoreAnimations.split(","):Array.isArray(e.ignoreAnimations)?e.ignoreAnimations:!e.ignoreAnimations&&[]));let W;t.watchEffect((()=>q())),t.watchEffect((()=>{G()}));const K=e=>{const t=e.target;if(!(!(null==t?void 0:t.contains(h.value))||Array.isArray(H.value)&&H.value.includes(e.animationName)||($.add(t),W))){const e=()=>{W=requestAnimationFrame((()=>{G(),e()}))};e()}},Z=e=>{const t=e.target;t&&$.delete(t),W&&0===$.size&&(cancelAnimationFrame(W),G())},J=t.ref(!1);"undefined"!=typeof document&&t.watchEffect((()=>{J.value&&!1!==H.value?(document.addEventListener("animationstart",K),document.addEventListener("animationend",Z)):(document.removeEventListener("animationstart",K),document.removeEventListener("animationend",Z))})),t.onMounted((()=>{J.value=!0,F(),ve(),h.value&&(k=new ResizeObserver(U),k.observe(h.value)),a("init")})),t.onBeforeUnmount((()=>{J.value=!1,r.cleanup(),D&&clearTimeout(D),W&&cancelAnimationFrame(W),I&&clearInterval(I),k&&(k.disconnect(),k=null),"undefined"!=typeof document&&ue(),h.value&&(h.value.removeEventListener("transitionend",G),h.value.removeEventListener("animationiteration",G))}));let Q=!1;const ee={x:0,y:0},te=t.reactive({x:0,y:0}),ie=t.ref(!1),ne=t.ref(!1),ae=()=>{ie.value=!0},le=()=>{ie.value=!1},oe=b((e=>{if(!e.ctrlKey)switch(e.key){case"ArrowLeft":case"ArrowUp":z.value===e.key.endsWith("Up")&&(B.value?he(!0):we(!0));break;case"ArrowRight":case"ArrowDown":z.value===e.key.endsWith("Down")&&(B.value?we(!0):he(!0))}}),200),re=()=>{document.addEventListener("keydown",oe)},ue=()=>{document.removeEventListener("keydown",oe)};function se(e){const t=e.target.tagName;if(["INPUT","TEXTAREA","SELECT"].includes(t)||ge.value)return;if(Q="touchstart"===e.type,!Q&&(e.preventDefault(),0!==e.button))return;ee.x="touches"in e?e.touches[0].clientX:e.clientX,ee.y="touches"in e?e.touches[0].clientY:e.clientY;const i=Q?"touchmove":"mousemove",n=Q?"touchend":"mouseup";document.addEventListener(i,de,{passive:!1}),document.addEventListener(n,ce,{passive:!0})}const de=b((e=>{ne.value=!0;const t="touches"in e?e.touches[0].clientX:e.clientX,i="touches"in e?e.touches[0].clientY:e.clientY;te.x=t-ee.x,te.y=i-ee.y;const n=function(e){const{isVertical:t,isReversed:i,dragged:n,effectiveSlideSize:a}=e,l=t?n.y:n.x;if(0===l)return 0;const o=Math.round(l/a);return i?o:-o}({isVertical:z.value,isReversed:B.value,dragged:te,effectiveSlideSize:R.value});C.value=N.wrapAround?_.value+n:m({val:_.value+n,max:O.value,min:L.value}),a("drag",{deltaX:te.x,deltaY:te.y})}));function ce(){if(de.cancel(),C.value!==_.value&&!Q){const e=t=>{t.preventDefault(),window.removeEventListener("click",e)};window.addEventListener("click",e)}fe(C.value),te.x=0,te.y=0,ne.value=!1;const e=Q?"touchmove":"mousemove",t=Q?"touchend":"mouseup";document.removeEventListener(e,de),document.removeEventListener(t,ce)}function ve(){!N.autoplay||N.autoplay<=0||(I=setInterval((()=>{N.pauseAutoplayOnHover&&ie.value||he()}),N.autoplay))}function pe(){I&&(clearInterval(I),I=null)}function me(){pe(),ve()}const ge=t.ref(!1);function fe(e,t=!1){if(!t&&ge.value)return;let i=e,n=e;E.value=_.value,N.wrapAround?n=w({val:i,max:O.value,min:L.value}):i=m({val:i,max:O.value,min:L.value}),a("slide-start",{slidingToIndex:e,currentSlideIndex:_.value,prevSlideIndex:E.value,slidesCount:p.value}),pe(),ge.value=!0,_.value=i,n!==i&&be.pause(),a("update:modelValue",n);D=setTimeout((()=>{N.wrapAround&&n!==i&&(be.resume(),_.value=n,a("loop",{currentSlideIndex:_.value,slidingToIndex:e})),a("slide-end",{currentSlideIndex:_.value,prevSlideIndex:E.value,slidesCount:p.value}),ge.value=!1,me()}),N.transition)}function he(e=!1){fe(_.value+N.itemsToScroll,e)}function we(e=!1){fe(_.value-N.itemsToScroll,e)}t.watch((()=>[T.value,e.breakpoints]),(()=>F()),{deep:!0}),t.watch((()=>e.autoplay),(()=>me()));const be=t.watch((()=>e.modelValue),(e=>{e!==_.value&&fe(Number(e),!0)}));a("before-init");const Se=t.computed((()=>{if(!N.wrapAround)return{before:0,after:0};if(P.value)return{before:u.length,after:u.length};const e=Number(N.itemsToShow),t=Math.ceil(e+(N.itemsToScroll-1)),i=t-C.value,n=t-(p.value-(C.value+1));return{before:Math.max(0,i),after:Math.max(0,n)}})),ye=t.computed((()=>Se.value.before?P.value?-1*X.value.slice(-1*Se.value.before).reduce(((e,t)=>e+t[V.value]+N.gap),0):Se.value.before*R.value*-1:0)),xe=t.computed((()=>{var e;if(P.value){const t=(_.value%u.length+u.length)%u.length;return f({slideSize:null===(e=X.value[t])||void 0===e?void 0:e[V.value],viewportSize:Y.value[V.value],align:N.snapAlign})}return f({align:N.snapAlign,itemsToShow:+N.itemsToShow})})),Ae=t.computed((()=>{let e=0;if(P.value){if(e=_.value<0?-1*X.value.slice(_.value).reduce(((e,t)=>e+t[V.value]+N.gap),0):X.value.slice(0,_.value).reduce(((e,t)=>e+t[V.value]+N.gap),0),e-=xe.value,!N.wrapAround){e=m({val:e,max:X.value.reduce(((e,t)=>e+t[V.value]+N.gap),0)-Y.value[V.value]-N.gap,min:0})}}else{let t=_.value-xe.value;N.wrapAround||(t=m({val:t,max:p.value-+N.itemsToShow,min:0})),e=t*R.value}return e*(B.value?1:-1)})),Te=t.computed((()=>{var e,t;if(!P.value){const e=_.value-xe.value;return N.wrapAround?{min:Math.floor(e),max:Math.ceil(e+Number(N.itemsToShow)-1)}:{min:Math.floor(m({val:e,max:p.value-Number(N.itemsToShow),min:0})),max:Math.ceil(m({val:e+Number(N.itemsToShow)-1,max:p.value-1,min:0}))}}let i=0;{let t=0,n=0-Se.value.before;const a=Math.abs(Ae.value+ye.value);for(;t<=a;){const i=(n%u.length+u.length)%u.length;t+=(null===(e=X.value[i])||void 0===e?void 0:e[V.value])+N.gap,n++}i=n-1}let n=0;{let e=i,a=0;for(a=e<0?X.value.slice(0,e).reduce(((e,t)=>e+t[V.value]+N.gap),0)-Math.abs(Ae.value+ye.value):X.value.slice(0,e).reduce(((e,t)=>e+t[V.value]+N.gap),0)-Math.abs(Ae.value);a<Y.value[V.value];){const i=(e%u.length+u.length)%u.length;a+=(null===(t=X.value[i])||void 0===t?void 0:t[V.value])+N.gap,e++}n=e-1}return{min:Math.floor(i),max:Math.ceil(n)}})),Ne=t.computed((()=>{if("fade"===N.slideEffect)return;const e=z.value?"Y":"X",t=z.value?te.y:te.x;let i=Ae.value+t;if(!N.wrapAround&&N.preventExcessiveDragging){let e=0;e=P.value?X.value.reduce(((e,t)=>e+t[V.value]),0):(p.value-Number(N.itemsToShow))*R.value;i=m({val:i,min:B.value?0:-1*e,max:B.value?e:0})}return`translate${e}(${i}px)`})),_e=t.computed((()=>({"--vc-transition-duration":ge.value?S(N.transition,"ms"):void 0,"--vc-slide-gap":S(N.gap),"--vc-carousel-height":S(N.height),"--vc-cloned-offset":S(ye.value)}))),Ce={slideTo:fe,next:he,prev:we},Ee=t.reactive({activeSlide:C,config:N,currentSlide:_,isSliding:ge,isVertical:z,maxSlide:O,minSlide:L,nav:Ce,normalizedDir:j,slideRegistry:r,slideSize:A,slides:u,slidesCount:p,viewport:x,visibleRange:Te});t.provide(d,Ee);const Me=t.reactive({config:N,currentSlide:_,maxSlide:O,middleSlide:M,minSlide:L,slideSize:A,slidesCount:p});return l({data:Me,nav:Ce,next:he,prev:we,restartCarousel:function(){F(),q(),G(),me()},slideTo:fe,updateBreakpointsConfig:F,updateSlideSize:G,updateSlidesData:q}),()=>{var e;const n=i.default||i.slides,a=(null==n?void 0:n(Me))||[],{before:l,after:o}=Se.value,r=[...v({slides:u,position:"before",toShow:l}),...a,...v({slides:u,position:"after",toShow:o})];if(!N.enabled||!r.length)return t.h("section",{ref:h,class:["carousel","is-disabled"]},r);const s=(null===(e=i.addons)||void 0===e?void 0:e.call(i,Me))||[],d=t.h("ol",{class:"carousel__track",style:{transform:Ne.value},onMousedownCapture:N.mouseDrag?se:null,onTouchstartPassiveCapture:N.touchDrag?se:null},r),c=t.h("div",{class:"carousel__viewport",ref:x},d);return t.h("section",{ref:h,class:["carousel",`is-${j.value}`,`is-effect-${N.slideEffect}`,{"is-vertical":z.value,"is-sliding":ge.value,"is-dragging":ne.value,"is-hover":ie.value}],dir:j.value,style:_e.value,"aria-label":N.i18n.ariaGallery,tabindex:"0",onFocus:re,onBlur:ue,onMouseenter:ae,onMouseleave:le},[c,s,t.h(y)])}}});var T;!function(e){e.arrowDown="arrowDown",e.arrowLeft="arrowLeft",e.arrowRight="arrowRight",e.arrowUp="arrowUp"}(T||(T={}));const N=e=>`icon${e.charAt(0).toUpperCase()+e.slice(1)}`,_={arrowDown:"M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 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",arrowRight:"M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z",arrowUp:"M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"};const C=e=>e&&e in T,E=t.defineComponent({props:{name:{type:String,required:!0,validator:C},title:{type:String,default:e=>e.name?s.i18n[N(e.name)]:""}},setup(e){const i=t.inject(d,null);return()=>{const n=e.name;if(!n||!C(n))return;const a=_[n],l=t.h("path",{d:a}),o=(null==i?void 0:i.config.i18n[N(n)])||e.title,r=t.h("title",o);return t.h("svg",{class:"carousel__icon",viewBox:"0 0 24 24",role:"img","aria-label":o},[r,l])}}}),M=t.defineComponent({name:"CarouselNavigation",inheritAttrs:!1,setup(e,{slots:i,attrs:n}){const a=t.inject(d);if(!a)return()=>"";const{next:l,prev:o}=i,r=t.computed((()=>!a.config.wrapAround&&a.currentSlide<=a.minSlide)),u=t.computed((()=>!a.config.wrapAround&&a.currentSlide>=a.maxSlide));return()=>{const{i18n:e}=a.config;return[t.h("button",Object.assign(Object.assign({type:"button",disabled:r.value,"aria-label":e.ariaPreviousSlide,title:e.ariaPreviousSlide,onClick:a.nav.prev},n),{class:["carousel__prev",{"carousel__prev--disabled":r.value},n.class]}),(null==o?void 0:o())||t.h(E,{name:{btt:"arrowDown",ltr:"arrowLeft",rtl:"arrowRight",ttb:"arrowUp"}[a.normalizedDir]})),t.h("button",Object.assign(Object.assign({type:"button",disabled:u.value,"aria-label":e.ariaNextSlide,title:e.ariaNextSlide,onClick:a.nav.next},n),{class:["carousel__next",{"carousel__next--disabled":u.value},n.class]}),(null==l?void 0:l())||t.h(E,{name:{btt:"arrowUp",ltr:"arrowRight",rtl:"arrowLeft",ttb:"arrowDown"}[a.normalizedDir]}))]}}}),O=t.defineComponent({name:"CarouselPagination",props:{disableOnClick:{type:Boolean},paginateByItemsToShow:{type:Boolean}},setup(e){const i=t.inject(d);if(!i)return()=>"";const n=t.computed((()=>i.config.itemsToShow)),a=t.computed((()=>f({align:i.config.snapAlign,itemsToShow:n.value}))),l=t.computed((()=>e.paginateByItemsToShow&&n.value>1)),o=t.computed((()=>Math.ceil((i.activeSlide-a.value)/n.value))),r=t.computed((()=>Math.ceil(i.slidesCount/n.value))),u=e=>w(l.value?{val:o.value,max:r.value-1,min:0}:{val:i.activeSlide,max:i.maxSlide,min:i.minSlide})===e;return()=>{var n,o;const s=[];for(let d=l.value?0:i.minSlide;d<=(l.value?r.value-1:i.maxSlide);d++){const r=h(i.config.i18n[l.value?"ariaNavigateToPage":"ariaNavigateToSlide"],{slideNumber:d+1}),c=u(d),v=t.h("button",{type:"button",class:{"carousel__pagination-button":!0,"carousel__pagination-button--active":c},"aria-label":r,"aria-pressed":c,"aria-controls":null===(o=null===(n=i.slides[d])||void 0===n?void 0:n.exposed)||void 0===o?void 0:o.id,title:r,disabled:e.disableOnClick,onClick:()=>i.nav.slideTo(l.value?Math.floor(d*+i.config.itemsToShow+a.value):d)}),p=t.h("li",{class:"carousel__pagination-item",key:d},v);s.push(p)}return t.h("ol",{class:"carousel__pagination"},s)}}}),L=t.defineComponent({name:"CarouselSlide",props:{id:{type:String,default:e=>e.isClone?void 0:t.useId()},index:{type:Number,default:void 0},isClone:{type:Boolean,default:!1},position:{type:String,default:void 0}},setup(e,{attrs:i,slots:n,expose:a}){const l=t.inject(d);if(t.provide(d,void 0),!l)return()=>"";const o=t.ref(e.index),r=t.getCurrentInstance();a({id:e.id,setIndex:e=>{o.value=e},getBoundingRect:()=>{const e=r.vnode.el;return e?e.getBoundingClientRect():{width:0,height:0}}});const u=t.computed((()=>o.value===l.activeSlide)),s=t.computed((()=>o.value===l.activeSlide-1)),c=t.computed((()=>o.value===l.activeSlide+1)),v=t.computed((()=>o.value>=l.visibleRange.min&&o.value<=l.visibleRange.max)),m=t.computed((()=>{if("auto"===l.config.itemsToShow)return;const e=l.config.itemsToShow,t=l.config.gap>0&&e>1?`calc(${100/e}% - ${l.config.gap*(e-1)/e}px)`:100/e+"%";return l.isVertical?{height:t}:{width:t}}));return l.slideRegistry.registerSlide(r,e.index),t.onUnmounted((()=>{l.slideRegistry.unregisterSlide(r)})),e.isClone&&(t.onMounted((()=>{p(r.vnode)})),t.onUpdated((()=>{p(r.vnode)}))),()=>{var a,r;return l.config.enabled?t.h("li",{style:[i.style,Object.assign({},m.value)],class:{carousel__slide:!0,"carousel__slide--clone":e.isClone,"carousel__slide--visible":v.value,"carousel__slide--active":u.value,"carousel__slide--prev":s.value,"carousel__slide--next":c.value,"carousel__slide--sliding":l.isSliding},onFocusin:()=>{l.viewport&&(l.viewport.scrollLeft=0),l.nav.slideTo(o.value)},id:e.isClone?void 0:e.id,"aria-hidden":e.isClone||void 0},null===(r=n.default)||void 0===r?void 0:r.call(n,{currentIndex:o.value,isActive:u.value,isClone:e.isClone,isPrev:s.value,isNext:c.value,isSliding:l.isSliding,isVisible:v.value})):null===(a=n.default)||void 0===a?void 0:a.call(n)}}});e.BREAKPOINT_MODE_OPTIONS=i,e.Carousel=A,e.DEFAULT_CONFIG=s,e.DIR_MAP=n,e.DIR_OPTIONS=a,e.I18N_DEFAULT_CONFIG=l,e.Icon=E,e.NORMALIZED_DIR_OPTIONS=o,e.Navigation=M,e.Pagination=O,e.SLIDE_EFFECTS=r,e.SNAP_ALIGN_OPTIONS=u,e.Slide=L,e.createSlideRegistry=c,e.icons=_,e.injectCarousel=d}));
//# sourceMappingURL=carousel.min.js.map

@@ -24,3 +24,3 @@ import eslint from '@eslint/js'

rules: {
'no-console': 'error',
'no-console': 'off',
'@typescript-eslint/no-unused-vars': 'error',

@@ -27,0 +27,0 @@ '@typescript-eslint/no-empty-function': 'off',

{
"name": "vue3-carousel",
"version": "0.12.0",
"version": "0.13.0",
"description": "A simple carousel component for Vue 3",

@@ -5,0 +5,0 @@ "author": "Abdelrahman Ismail <dev@ismail9k.com>",

@@ -1,6 +0,12 @@

# Vue 3 Carousel
<p align="center">
<img src="docs/public/vue3-carousel-logo-light.svg" width="200" alt="Vue 3 Carousel Logo">
</p>
<h1 align="center">Vue 3 Carousel</h1>
<p align="center">
Modern lightweight Vue 3 carousel component
</p>
<p>
<p align="center">
<a href="https://npm-stat.com/charts.html?package=vue3-carousel"><img src="https://img.shields.io/npm/dm/vue3-carousel.svg" alt="npm"/></a>

@@ -11,35 +17,27 @@ <a href="https://www.npmjs.com/package/vue3-carousel"><img src="https://img.shields.io/npm/v/vue3-carousel.svg" alt="npm"/></a>

## Documentation
## ✨ Features
https://vue3-carousel.ismail9k.com/
- 📱 **Responsive** - Breakpoints support
- 🔄 **Infinite Scroll** - Wrap around sliding
- 🖱️ **Mouse/Touch** - Dragging support
- ⚡ **Auto Play** - Automatic sliding
- 🎯 **Slide Classes** - Active & visible states
- 🌐 **RTL** - Right-to-left support
- ♿ **A11y** - Keyboard navigation & ARIA labels
- 📊 **Vertical** - Vertical sliding mode
## Features
## 🚀 Installation
- [x] Responsive breakpoints
- [x] Mouse/touch dragging
- [x] Infinity scroll (wrapping around)
- [x] Auto play
- [x] Add classes for active and for visible slides
- [x] RTL
- [x] Enrich a11y
- [x] Vertical Slides
## Nuxt Module
If you're using Nuxt and prefer to use it via module, please refer to [vue3-carousel-nuxt](https://github.com/gaetansenn/vue3-carousel-nuxt?tab=readme-ov-file)
## Getting started
### Installation
First step is to install it using `yarn` or `npm`:
```bash
npm install vue3-carousel
# npm
npm i vue3-carousel
# or use yarn
# yarn
yarn add vue3-carousel
# pnpm
pnpm install vue3-carousel
```
### Basic Using
## 📖 Basic Usage

@@ -49,7 +47,8 @@ ```vue

// If you are using PurgeCSS, make sure to whitelist the carousel CSS classes
import 'vue3-carousel/dist/carousel.css'
import 'vue3-carousel/carousel.css'
import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel'
const config = {
itemsToShow: 1.5
const carouselConfig = {
itemsToShow: 2.5,
wrapAround: true
}

@@ -59,5 +58,5 @@ </script>

<template>
<Carousel v-bind="config">
<Carousel v-bind="carouselConfig">
<Slide v-for="slide in 10" :key="slide">
{{ slide }}
<div class="carousel__item">{{ slide }}</div>
</Slide>

@@ -72,1 +71,15 @@

```
## 📚 Documentation
Visit our [documentation website](https://vue3-carousel.ismail9k.com/) for detailed usage and examples:
- [Getting Started](https://vue3-carousel.ismail9k.com/getting-started)
- [Carousel Config](https://vue3-carousel.ismail9k.com/config)
- [Slide Component](https://vue3-carousel.ismail9k.com/components/slide)
- [Navigation Component](https://vue3-carousel.ismail9k.com/components/navigation)
- [Pagination Component](https://vue3-carousel.ismail9k.com/components/pagination)
## 💚 Nuxt Module
For Nuxt users, check out [vue3-carousel-nuxt](https://github.com/gaetansenn/vue3-carousel-nuxt) module.

@@ -72,3 +72,3 @@ import { mount } from '@vue/test-utils'

expect(wrapper.props('modelValue')).toBe(1)
await wrapper.setProps({ dir: 'ttb' })
await wrapper.setProps({ dir: 'ttb', height: 200 })
await triggerKeyEvent('ArrowDown')

@@ -83,3 +83,3 @@ expect(wrapper.props('modelValue')).toBe(2)

await wrapper.setProps({ dir: 'btt' })
await wrapper.setProps({ dir: 'btt', height: 200 })
await triggerKeyEvent('ArrowDown')

@@ -118,3 +118,3 @@ expect(wrapper.props('modelValue')).toBe(0)

await wrapper.setProps({ itemsToShow: 10 })
const slides = wrapper.findAll('.carousel__slide--visible')
const slides = wrapper.findAll('.carousel__slide')
expect(slides.length).toBe(5)

@@ -206,2 +206,3 @@ })

const [html, wrapper] = await renderSSR(App, {
height: 200,
wrapAround: true,

@@ -208,0 +209,0 @@ modelValue: 1,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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