/* eslint-disable @typescript-eslint/no-unsafe-argument */ import { h, defineComponent, ref, cloneVNode, computed, onBeforeUnmount, watch, withDirectives, vShow, Transition, toRef, nextTick, onMounted, watchEffect, normalizeStyle, onUpdated } from 'vue'; import { VResizeObserver } from 'vueuc'; import { useMergedState } from 'vooks'; import { on, off } from 'evtd'; import { getPreciseEventTarget } from 'seemly'; import { useConfig, useTheme, useThemeClass } from "../../_mixins/index.mjs"; import { flatten, keep, resolveSlotWithProps } from "../../_utils/index.mjs"; import { carouselLight } from "../styles/index.mjs"; import { calculateSize, clampValue, resolveSpeed, isTouchEvent, getNextIndex, getPrevIndex, getDisplayIndex, getRealIndex, getDisplayTotalView, addDuplicateSlides } from "./utils/index.mjs"; import { provideCarouselContext } from "./CarouselContext.mjs"; import NCarouselDots from "./CarouselDots.mjs"; import NCarouselArrow from "./CarouselArrow.mjs"; import NCarouselItem, { isCarouselItem } from "./CarouselItem.mjs"; import style from "./styles/index.cssr.mjs"; const transitionProperties = ['transitionDuration', 'transitionTimingFunction']; export const carouselProps = Object.assign(Object.assign({}, useTheme.props), { defaultIndex: { type: Number, default: 0 }, currentIndex: Number, showArrow: Boolean, dotType: { type: String, default: 'dot' }, dotPlacement: { type: String, default: 'bottom' }, slidesPerView: { type: [Number, String], default: 1 }, spaceBetween: { type: Number, default: 0 }, centeredSlides: Boolean, direction: { type: String, default: 'horizontal' }, autoplay: Boolean, interval: { type: Number, default: 5000 }, loop: { type: Boolean, default: true }, effect: { type: String, default: 'slide' }, showDots: { type: Boolean, default: true }, trigger: { type: String, default: 'click' }, transitionStyle: { type: Object, default: () => ({ transitionDuration: '300ms' }) }, transitionProps: Object, draggable: Boolean, prevSlideStyle: [Object, String], nextSlideStyle: [Object, String], touchable: { type: Boolean, default: true }, mousewheel: Boolean, keyboard: Boolean, 'onUpdate:currentIndex': Function, onUpdateCurrentIndex: Function }); // only one carousel is allowed to trigger touch globally let globalDragging = false; export default defineComponent({ name: 'Carousel', props: carouselProps, setup(props) { const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); // Dom const selfElRef = ref(null); const slidesElRef = ref(null); const slideElsRef = ref([]); const slideVNodesRef = { value: [] }; // Computed states const verticalRef = computed(() => props.direction === 'vertical'); const sizeAxisRef = computed(() => verticalRef.value ? 'height' : 'width'); const spaceAxisRef = computed(() => verticalRef.value ? 'bottom' : 'right'); const sequenceLayoutRef = computed(() => props.effect === 'slide'); const duplicatedableRef = computed( // duplicate the copy operation in `slide` mode, // because only its DOM is sequence layout () => props.loop && props.slidesPerView === 1 && sequenceLayoutRef.value); // user wants to control the transition animation const userWantsControlRef = computed(() => props.effect === 'custom'); // used to calculate total views const displaySlidesPerViewRef = computed(() => !sequenceLayoutRef.value || props.centeredSlides ? 1 : props.slidesPerView); // used to calculate the size of each slide const realSlidesPerViewRef = computed(() => userWantsControlRef.value ? 1 : props.slidesPerView); // we automatically calculate total view for special slides per view const autoSlideSizeRef = computed(() => displaySlidesPerViewRef.value === 'auto' || props.slidesPerView === 'auto' && props.centeredSlides); // Carousel size const perViewSizeRef = ref({ width: 0, height: 0 }); const slideSizesRef = computed(() => { const { value: slidesEls } = slideElsRef; if (!slidesEls.length) return []; const { value: autoSlideSize } = autoSlideSizeRef; if (autoSlideSize) { return slidesEls.map(slide => calculateSize(slide)); } const { value: slidesPerView } = realSlidesPerViewRef; const { value: perViewSize } = perViewSizeRef; const { value: axis } = sizeAxisRef; let axisSize = perViewSize[axis]; if (slidesPerView !== 'auto') { const { spaceBetween } = props; const remaining = axisSize - (slidesPerView - 1) * spaceBetween; const percentage = 1 / Math.max(1, slidesPerView); axisSize = remaining * percentage; } const slideSize = Object.assign(Object.assign({}, perViewSize), { [axis]: axisSize }); return slidesEls.map(() => slideSize); }); // The translate required to reach each slide const slideTranlatesRef = computed(() => { const { value: slideSizes } = slideSizesRef; if (!slideSizes.length) return []; const { centeredSlides, spaceBetween } = props; const { value: axis } = sizeAxisRef; const { [axis]: perViewSize } = perViewSizeRef.value; let previousTranslate = 0; return slideSizes.map(({ [axis]: slideSize }) => { let translate = previousTranslate; if (centeredSlides) { translate += (slideSize - perViewSize) / 2; } previousTranslate += slideSize + spaceBetween; return translate; }); }); // Styles const isMountedRef = ref(false); const transitionStyleRef = computed(() => { const { transitionStyle } = props; return transitionStyle ? keep(transitionStyle, transitionProperties) : {}; }); const speedRef = computed(() => userWantsControlRef.value ? 0 : resolveSpeed(transitionStyleRef.value.transitionDuration)); const slideStylesRef = computed(() => { const { value: slidesEls } = slideElsRef; if (!slidesEls.length) return []; const useComputedSize = !(autoSlideSizeRef.value || realSlidesPerViewRef.value === 1); const getSlideSize = index => { if (useComputedSize) { const { value: axis } = sizeAxisRef; return { [axis]: `${slideSizesRef.value[index][axis]}px` }; } }; if (userWantsControlRef.value) { // We center each slide when user wants to control the transition animation, // so there is no need to calculate the offset return slidesEls.map((_, i) => getSlideSize(i)); } const { effect, spaceBetween } = props; const { value: spaceAxis } = spaceAxisRef; return slidesEls.reduce((styles, _, i) => { const style = Object.assign(Object.assign({}, getSlideSize(i)), { [`margin-${spaceAxis}`]: `${spaceBetween}px` }); styles.push(style); if (isMountedRef.value && (effect === 'fade' || effect === 'card')) { Object.assign(style, transitionStyleRef.value); } return styles; }, []); }); // Total const totalViewRef = computed(() => { const { value: slidesPerView } = displaySlidesPerViewRef; const { length: totalSlides } = slideElsRef.value; if (slidesPerView !== 'auto') { return Math.max(totalSlides - slidesPerView, 0) + 1; } else { const { value: slideSizes } = slideSizesRef; const { length } = slideSizes; if (!length) return totalSlides; const { value: translates } = slideTranlatesRef; const { value: axis } = sizeAxisRef; const perViewSize = perViewSizeRef.value[axis]; let lastViewSize = slideSizes[slideSizes.length - 1][axis]; let i = length; while (i > 1 && lastViewSize < perViewSize) { i--; lastViewSize += translates[i] - translates[i - 1]; } return clampValue(i + 1, 1, length); } }); const displayTotalViewRef = computed(() => getDisplayTotalView(totalViewRef.value, duplicatedableRef.value)); // Index const defaultRealIndex = getRealIndex(props.defaultIndex, duplicatedableRef.value); const uncontrolledDisplayIndexRef = ref(getDisplayIndex(defaultRealIndex, totalViewRef.value, duplicatedableRef.value)); const mergedDisplayIndexRef = useMergedState(toRef(props, 'currentIndex'), uncontrolledDisplayIndexRef); const realIndexRef = computed(() => getRealIndex(mergedDisplayIndexRef.value, duplicatedableRef.value)); // Reality methods function toRealIndex(index) { var _a, _b; index = clampValue(index, 0, totalViewRef.value - 1); const displayIndex = getDisplayIndex(index, totalViewRef.value, duplicatedableRef.value); const { value: lastDisplayIndex } = mergedDisplayIndexRef; if (displayIndex !== mergedDisplayIndexRef.value) { uncontrolledDisplayIndexRef.value = displayIndex; (_a = props['onUpdate:currentIndex']) === null || _a === void 0 ? void 0 : _a.call(props, displayIndex, lastDisplayIndex); (_b = props.onUpdateCurrentIndex) === null || _b === void 0 ? void 0 : _b.call(props, displayIndex, lastDisplayIndex); } } function getRealPrevIndex(index = realIndexRef.value) { return getPrevIndex(index, totalViewRef.value, props.loop); } function getRealNextIndex(index = realIndexRef.value) { return getNextIndex(index, totalViewRef.value, props.loop); } function isRealPrev(slideOrIndex) { const index = getSlideIndex(slideOrIndex); return index !== null && getRealPrevIndex() === index; } function isRealNext(slideOrIndex) { const index = getSlideIndex(slideOrIndex); return index !== null && getRealNextIndex() === index; } function isRealActive(slideOrIndex) { return realIndexRef.value === getSlideIndex(slideOrIndex); } // Display methods // They are used to deal with the actual values displayed on the UI function isDisplayActive(index) { return mergedDisplayIndexRef.value === index; } function isPrevDisabled() { return getRealPrevIndex() === null; } function isNextDisabled() { return getRealNextIndex() === null; } // To function to(index) { const realIndex = clampValue(getRealIndex(index, duplicatedableRef.value), 0, totalViewRef.value); if (index !== mergedDisplayIndexRef.value || realIndex !== realIndexRef.value) { toRealIndex(realIndex); } } function prev() { const prevIndex = getRealPrevIndex(); if (prevIndex !== null) toRealIndex(prevIndex); } function next() { const nextIndex = getRealNextIndex(); if (nextIndex !== null) toRealIndex(nextIndex); } function prevIfSlideTransitionEnd() { if (!inTransition || !duplicatedableRef.value) prev(); } function nextIfSlideTransitionEnd() { if (!inTransition || !duplicatedableRef.value) next(); } // Translate to let inTransition = false; // record the translate of each slide, so that it can be restored at touch let previousTranslate = 0; const translateStyleRef = ref({}); function updateTranslate(translate, speed = 0) { translateStyleRef.value = Object.assign({}, transitionStyleRef.value, { transform: verticalRef.value ? `translateY(${-translate}px)` : `translateX(${-translate}px)`, transitionDuration: `${speed}ms` }); } function fixTranslate(speed = 0) { if (sequenceLayoutRef.value) { translateTo(realIndexRef.value, speed); } else if (previousTranslate !== 0) { if (!inTransition && speed > 0) { inTransition = true; } updateTranslate(previousTranslate = 0, speed); } } function translateTo(index, speed) { const translate = getTranslate(index); if (translate !== previousTranslate && speed > 0) { inTransition = true; } previousTranslate = getTranslate(realIndexRef.value); updateTranslate(translate, speed); } function getTranslate(index) { let translate; // Deal with auto slides pre view if (index >= totalViewRef.value - 1) { translate = getLastViewTranslate(); } else { translate = slideTranlatesRef.value[index] || 0; } return translate; } function getLastViewTranslate() { if (displaySlidesPerViewRef.value === 'auto') { const { value: axis } = sizeAxisRef; const { [axis]: perViewSize } = perViewSizeRef.value; const { value: translates } = slideTranlatesRef; const lastTranslate = translates[translates.length - 1]; let overallSize; if (lastTranslate === undefined) { overallSize = perViewSize; } else { const { value: slideSizes } = slideSizesRef; overallSize = lastTranslate + slideSizes[slideSizes.length - 1][axis]; } // Bring the last slide to the edge return overallSize - perViewSize; } else { const { value: translates } = slideTranlatesRef; return translates[totalViewRef.value - 1] || 0; } } // Provide const carouselContext = { currentIndexRef: mergedDisplayIndexRef, to, prev: prevIfSlideTransitionEnd, next: nextIfSlideTransitionEnd, isVertical: () => verticalRef.value, isHorizontal: () => !verticalRef.value, isPrev: isRealPrev, isNext: isRealNext, isActive: isRealActive, isPrevDisabled, isNextDisabled, getSlideIndex, getSlideStyle, addSlide, removeSlide, onCarouselItemClick }; provideCarouselContext(carouselContext); function addSlide(slide) { if (!slide) return; slideElsRef.value.push(slide); } function removeSlide(slide) { if (!slide) return; const index = getSlideIndex(slide); if (index !== -1) { slideElsRef.value.splice(index, 1); } } function getSlideIndex(slideOrIndex) { return typeof slideOrIndex === 'number' ? slideOrIndex : slideOrIndex ? slideElsRef.value.indexOf(slideOrIndex) : -1; } function getSlideStyle(slide) { const index = getSlideIndex(slide); if (index !== -1) { const styles = [slideStylesRef.value[index]]; const isPrev = carouselContext.isPrev(index); const isNext = carouselContext.isNext(index); if (isPrev) { styles.push(props.prevSlideStyle || ''); } if (isNext) { styles.push(props.nextSlideStyle || ''); } return normalizeStyle(styles); } } function onCarouselItemClick(index, event) { let allowClick = !inTransition && !dragging && !isEffectiveDrag; if (props.effect === 'card' && allowClick && !isRealActive(index)) { to(index); allowClick = false; } if (!allowClick) { event.preventDefault(); event.stopPropagation(); } } // Autoplay let autoplayTimer = null; function stopAutoplay() { if (autoplayTimer) { clearInterval(autoplayTimer); autoplayTimer = null; } } function resetAutoplay() { stopAutoplay(); const disabled = !props.autoplay || displayTotalViewRef.value < 2; if (!disabled) { autoplayTimer = window.setInterval(next, props.interval); } } // Drag let dragStartX = 0; let dragStartY = 0; let dragOffset = 0; let dragStartTime = 0; let dragging = false; let isEffectiveDrag = false; function handleTouchstart(event) { var _a; if (globalDragging) return; if (!((_a = slidesElRef.value) === null || _a === void 0 ? void 0 : _a.contains(getPreciseEventTarget(event)))) { return; } globalDragging = true; dragging = true; isEffectiveDrag = false; dragStartTime = Date.now(); stopAutoplay(); if (event.type !== 'touchstart' && !event.target.isContentEditable) { event.preventDefault(); } const touchEvent = isTouchEvent(event) ? event.touches[0] : event; if (verticalRef.value) { dragStartY = touchEvent.clientY; } else { dragStartX = touchEvent.clientX; } if (props.touchable) { on('touchmove', document, handleTouchmove); on('touchend', document, handleTouchend); on('touchcancel', document, handleTouchend); } if (props.draggable) { on('mousemove', document, handleTouchmove); on('mouseup', document, handleTouchend); } } function handleTouchmove(event) { const { value: vertical } = verticalRef; const { value: axis } = sizeAxisRef; const touchEvent = isTouchEvent(event) ? event.touches[0] : event; const offset = vertical ? touchEvent.clientY - dragStartY : touchEvent.clientX - dragStartX; const perViewSize = perViewSizeRef.value[axis]; dragOffset = clampValue(offset, -perViewSize, perViewSize); if (event.cancelable) { event.preventDefault(); } if (sequenceLayoutRef.value) { updateTranslate(previousTranslate - dragOffset, 0); } } function handleTouchend() { const { value: realIndex } = realIndexRef; let currentIndex = realIndex; if (!inTransition && dragOffset !== 0 && sequenceLayoutRef.value) { const currentTranslate = previousTranslate - dragOffset; const translates = [...slideTranlatesRef.value.slice(0, totalViewRef.value - 1), getLastViewTranslate()]; let prevOffset = null; for (let i = 0; i < translates.length; i++) { const offset = Math.abs(translates[i] - currentTranslate); if (prevOffset !== null && prevOffset < offset) { break; } prevOffset = offset; currentIndex = i; } } if (currentIndex === realIndex) { const timeElapsed = Date.now() - dragStartTime; const { value: axis } = sizeAxisRef; const perViewSize = perViewSizeRef.value[axis]; // more than 50% width or faster than 0.4px per ms if (dragOffset > perViewSize / 2 || dragOffset / timeElapsed > 0.4) { currentIndex = getRealPrevIndex(realIndex); } else if (dragOffset < -perViewSize / 2 || dragOffset / timeElapsed < -0.4) { currentIndex = getRealNextIndex(realIndex); } } if (currentIndex !== null && currentIndex !== realIndex) { isEffectiveDrag = true; toRealIndex(currentIndex); void nextTick(() => { if (!duplicatedableRef.value || uncontrolledDisplayIndexRef.value !== mergedDisplayIndexRef.value) { fixTranslate(speedRef.value); } }); } else { fixTranslate(speedRef.value); } resetDragStatus(); resetAutoplay(); } function resetDragStatus() { if (dragging) { globalDragging = false; } dragging = false; dragStartX = 0; dragStartY = 0; dragOffset = 0; dragStartTime = 0; off('touchmove', document, handleTouchmove); off('touchend', document, handleTouchend); off('touchcancel', document, handleTouchend); off('mousemove', document, handleTouchmove); off('mouseup', document, handleTouchend); } function handleTransitionEnd() { if (sequenceLayoutRef.value && inTransition) { const { value: realIndex } = realIndexRef; translateTo(realIndex, 0); } else { resetAutoplay(); } if (sequenceLayoutRef.value) { translateStyleRef.value.transitionDuration = '0ms'; } inTransition = false; } function handleMousewheel(event) { event.preventDefault(); if (inTransition) return; let { deltaX, deltaY } = event; if (event.shiftKey && !deltaX) { deltaX = deltaY; } const prevMultiplier = -1; const nextMultiplier = 1; const m = (deltaX || deltaY) > 0 ? nextMultiplier : prevMultiplier; let rx = 0; let ry = 0; if (verticalRef.value) { ry = m; } else { rx = m; } const responseStep = 10; if (ry * deltaY >= responseStep || rx * deltaX >= responseStep) { if (m === nextMultiplier && !isNextDisabled()) { next(); } else if (m === prevMultiplier && !isPrevDisabled()) { prev(); } } } function handleResize() { perViewSizeRef.value = calculateSize(selfElRef.value, true); resetAutoplay(); } function handleSlideResize() { var _a, _b; if (autoSlideSizeRef.value) { (_b = (_a = slideSizesRef.effect).scheduler) === null || _b === void 0 ? void 0 : _b.call(_a); slideSizesRef.effect.run(); } } function handleMouseenter() { if (props.autoplay) { stopAutoplay(); } } function handleMouseleave() { if (props.autoplay) { resetAutoplay(); } } onMounted(() => { watchEffect(resetAutoplay); requestAnimationFrame(() => isMountedRef.value = true); }); onBeforeUnmount(() => { resetDragStatus(); stopAutoplay(); }); // Fix index when remounting onUpdated(() => { const { value: slidesEls } = slideElsRef; const { value: slideVNodes } = slideVNodesRef; const indexMap = new Map(); const getDisplayIndex = el => // eslint-disable-next-line @typescript-eslint/no-non-null-assertion indexMap.has(el) ? indexMap.get(el) : -1; let isChanged = false; for (let i = 0; i < slidesEls.length; i++) { const index = slideVNodes.findIndex(v => v.el === slidesEls[i]); if (index !== i) { isChanged = true; } indexMap.set(slidesEls[i], index); } if (isChanged) { slidesEls.sort((a, b) => getDisplayIndex(a) - getDisplayIndex(b)); } }); watch(realIndexRef, (realIndex, lastRealIndex) => { if (realIndex === lastRealIndex) return; resetAutoplay(); if (sequenceLayoutRef.value) { if (duplicatedableRef.value) { const { value: length } = totalViewRef; if (displayTotalViewRef.value > 2 && realIndex === length - 2 && lastRealIndex === 1) { realIndex = 0; } else if (realIndex === 1 && lastRealIndex === length - 2) { realIndex = length - 1; } } translateTo(realIndex, speedRef.value); } else { fixTranslate(); } }, { immediate: true }); watch([duplicatedableRef, displaySlidesPerViewRef], () => void nextTick(() => { toRealIndex(realIndexRef.value); })); watch(slideTranlatesRef, () => { sequenceLayoutRef.value && fixTranslate(); }, { deep: true }); watch(sequenceLayoutRef, value => { if (!value) { inTransition = false; // if the current mode does not support translate, reset the position of the wrapper updateTranslate(previousTranslate = 0); } else { fixTranslate(); } }); const slidesControlListenersRef = computed(() => { return { onTouchstartPassive: props.touchable ? handleTouchstart : undefined, onMousedown: props.draggable ? handleTouchstart : undefined, onWheel: props.mousewheel ? handleMousewheel : undefined }; }); const arrowSlotPropsRef = computed(() => Object.assign(Object.assign({}, keep(carouselContext, ['to', 'prev', 'next', 'isPrevDisabled', 'isNextDisabled'])), { total: displayTotalViewRef.value, currentIndex: mergedDisplayIndexRef.value })); const dotSlotPropsRef = computed(() => ({ total: displayTotalViewRef.value, currentIndex: mergedDisplayIndexRef.value, to: carouselContext.to })); const caroulseExposedMethod = { getCurrentIndex: () => mergedDisplayIndexRef.value, to, prev, next }; const themeRef = useTheme('Carousel', '-carousel', style, carouselLight, props, mergedClsPrefixRef); const cssVarsRef = computed(() => { const { common: { cubicBezierEaseInOut }, self: { dotSize, dotColor, dotColorActive, dotColorFocus, dotLineWidth, dotLineWidthActive, arrowColor } } = themeRef.value; return { '--n-bezier': cubicBezierEaseInOut, '--n-dot-color': dotColor, '--n-dot-color-focus': dotColorFocus, '--n-dot-color-active': dotColorActive, '--n-dot-size': dotSize, '--n-dot-line-width': dotLineWidth, '--n-dot-line-width-active': dotLineWidthActive, '--n-arrow-color': arrowColor }; }); const themeClassHandle = inlineThemeDisabled ? useThemeClass('carousel', undefined, cssVarsRef, props) : undefined; return Object.assign(Object.assign({ mergedClsPrefix: mergedClsPrefixRef, selfElRef, slidesElRef, slideVNodes: slideVNodesRef, duplicatedable: duplicatedableRef, userWantsControl: userWantsControlRef, autoSlideSize: autoSlideSizeRef, realIndex: realIndexRef, slideStyles: slideStylesRef, translateStyle: translateStyleRef, slidesControlListeners: slidesControlListenersRef, handleTransitionEnd, handleResize, handleSlideResize, handleMouseenter, handleMouseleave, isActive: isDisplayActive, arrowSlotProps: arrowSlotPropsRef, dotSlotProps: dotSlotPropsRef }, caroulseExposedMethod), { cssVars: inlineThemeDisabled ? undefined : cssVarsRef, themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass, onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender }); }, render() { var _a; const { mergedClsPrefix, showArrow, userWantsControl, slideStyles, dotType, dotPlacement, slidesControlListeners, transitionProps = {}, arrowSlotProps, dotSlotProps, $slots: { default: defaultSlot, dots: dotsSlot, arrow: arrowSlot } } = this; const children = defaultSlot && flatten(defaultSlot()) || []; let slides = filterCarouselItem(children); if (!slides.length) { slides = children.map(ch => h(NCarouselItem, null, { default: () => cloneVNode(ch) })); } if (this.duplicatedable) { slides = addDuplicateSlides(slides); } this.slideVNodes.value = slides; // When users need to customize the size of the slide, // we listen to them to fix the current translate if (this.autoSlideSize) { slides = slides.map(slide => h(VResizeObserver, { onResize: this.handleSlideResize }, { default: () => slide })); } (_a = this.onRender) === null || _a === void 0 ? void 0 : _a.call(this); return h("div", Object.assign({ ref: "selfElRef", class: [this.themeClass, `${mergedClsPrefix}-carousel`, this.direction === 'vertical' && `${mergedClsPrefix}-carousel--vertical`, this.showArrow && `${mergedClsPrefix}-carousel--show-arrow`, `${mergedClsPrefix}-carousel--${dotPlacement}`, `${mergedClsPrefix}-carousel--${this.direction}`, `${mergedClsPrefix}-carousel--${this.effect}`, userWantsControl && `${mergedClsPrefix}-carousel--usercontrol`], style: this.cssVars }, slidesControlListeners, { onMouseenter: this.handleMouseenter, onMouseleave: this.handleMouseleave }), h(VResizeObserver, { onResize: this.handleResize }, { default: () => h("div", { ref: "slidesElRef", class: `${mergedClsPrefix}-carousel__slides`, role: "listbox", style: this.translateStyle, onTransitionend: this.handleTransitionEnd }, userWantsControl ? slides.map((slide, i) => h("div", { style: slideStyles[i], key: i }, withDirectives(h(Transition, Object.assign({}, transitionProps), { default: () => slide }), [[vShow, this.isActive(i)]]))) : slides) }), this.showDots && dotSlotProps.total > 1 && resolveSlotWithProps(dotsSlot, dotSlotProps, () => [h(NCarouselDots, { key: dotType + dotPlacement, total: dotSlotProps.total, currentIndex: dotSlotProps.currentIndex, dotType: dotType, trigger: this.trigger, keyboard: this.keyboard })]), showArrow && resolveSlotWithProps(arrowSlot, arrowSlotProps, () => [h(NCarouselArrow, null)])); } }); function filterCarouselItem(vnodes) { return vnodes.reduce((carouselItems, vnode) => { if (isCarouselItem(vnode)) { carouselItems.push(vnode); } return carouselItems; }, []); }