885 lines
29 KiB
JavaScript
Raw Normal View History

2025-02-28 19:43:11 +08:00
/* 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;
}, []);
}