123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923 |
- /**
- * Vue 3 Carousel 0.8.1
- * (c) 2024
- * @license MIT
- */
- import { Fragment, defineComponent, inject, reactive, ref, h, computed, provide, onMounted, nextTick, onUnmounted, watch, cloneVNode } from 'vue';
- const SNAP_ALIGN_OPTIONS = ['center', 'start', 'end', 'center-even', 'center-odd'];
- const BREAKPOINT_MODE_OPTIONS = ['viewport', 'carousel'];
- const DIR_OPTIONS = [
- 'ltr',
- 'left-to-right',
- 'rtl',
- 'right-to-left',
- 'ttb',
- 'top-to-bottom',
- 'btt',
- 'bottom-to-top',
- ];
- const I18N_DEFAULT_CONFIG = {
- ariaNextSlide: 'Navigate to next slide',
- ariaPreviousSlide: 'Navigate to previous slide',
- ariaNavigateToSlide: 'Navigate to slide {slideNumber}',
- ariaGallery: 'Gallery',
- itemXofY: 'Item {currentSlide} of {slidesCount}',
- iconArrowUp: 'Arrow pointing upwards',
- iconArrowDown: 'Arrow pointing downwards',
- iconArrowRight: 'Arrow pointing to the right',
- iconArrowLeft: 'Arrow pointing to the left',
- };
- const DEFAULT_CONFIG = {
- enabled: true,
- itemsToShow: 1,
- itemsToScroll: 1,
- modelValue: 0,
- transition: 300,
- autoplay: 0,
- gap: 0,
- height: 'auto',
- wrapAround: false,
- pauseAutoplayOnHover: false,
- mouseDrag: true,
- touchDrag: true,
- snapAlign: SNAP_ALIGN_OPTIONS[0],
- dir: DIR_OPTIONS[0],
- breakpointMode: BREAKPOINT_MODE_OPTIONS[0],
- breakpoints: undefined,
- i18n: I18N_DEFAULT_CONFIG,
- };
- const carouselProps = {
- // enable/disable the carousel component
- enabled: {
- default: DEFAULT_CONFIG.enabled,
- type: Boolean,
- },
- // count of items to showed per view
- itemsToShow: {
- default: DEFAULT_CONFIG.itemsToShow,
- type: Number,
- },
- // count of items to be scrolled
- itemsToScroll: {
- default: DEFAULT_CONFIG.itemsToScroll,
- type: Number,
- },
- // control infinite scrolling mode
- wrapAround: {
- default: DEFAULT_CONFIG.wrapAround,
- type: Boolean,
- },
- // control the gap between slides
- gap: {
- default: DEFAULT_CONFIG.gap,
- type: Number,
- },
- // control the gap between slides
- height: {
- default: DEFAULT_CONFIG.height,
- type: [Number, String],
- },
- // control snap position alignment
- snapAlign: {
- default: DEFAULT_CONFIG.snapAlign,
- validator(value) {
- return SNAP_ALIGN_OPTIONS.includes(value);
- },
- },
- // sliding transition time in ms
- transition: {
- default: DEFAULT_CONFIG.transition,
- type: Number,
- },
- // controls the breakpoint mode relative to the carousel container or the viewport
- breakpointMode: {
- default: DEFAULT_CONFIG.breakpointMode,
- validator(value) {
- return BREAKPOINT_MODE_OPTIONS.includes(value);
- },
- },
- // an object to store breakpoints
- breakpoints: {
- default: DEFAULT_CONFIG.breakpoints,
- type: Object,
- },
- // time to auto advance slides in ms
- autoplay: {
- default: DEFAULT_CONFIG.autoplay,
- type: Number,
- },
- // pause autoplay when mouse hover over the carousel
- pauseAutoplayOnHover: {
- default: DEFAULT_CONFIG.pauseAutoplayOnHover,
- type: Boolean,
- },
- // slide number number of initial slide
- modelValue: {
- default: undefined,
- type: Number,
- },
- // toggle mouse dragging.
- mouseDrag: {
- default: DEFAULT_CONFIG.mouseDrag,
- type: Boolean,
- },
- // toggle mouse dragging.
- touchDrag: {
- default: DEFAULT_CONFIG.touchDrag,
- type: Boolean,
- },
- // control snap position alignment
- dir: {
- default: DEFAULT_CONFIG.dir,
- validator(value) {
- // The value must match one of these strings
- return DIR_OPTIONS.includes(value);
- },
- },
- // aria-labels and additional text labels
- i18n: {
- default: DEFAULT_CONFIG.i18n,
- type: Object,
- },
- };
- /**
- * Determines the maximum slide index based on the configuration.
- *
- * @param {Args} args - The carousel configuration and slide count.
- * @returns {number} The maximum slide index.
- */
- function getMaxSlideIndex({ config, slidesCount }) {
- var _a;
- const { snapAlign = 'N/A', wrapAround, itemsToShow = 1 } = config;
- // If wrapAround is enabled, the max index is the last slide
- if (wrapAround) {
- return Math.max(slidesCount - 1, 0);
- }
- // Map snapAlign values to calculation logic
- const snapAlignCalculations = {
- start: Math.ceil(slidesCount - itemsToShow),
- end: Math.ceil(slidesCount - 1),
- center: slidesCount - Math.ceil((itemsToShow - 0.5) / 2),
- 'center-odd': slidesCount - Math.ceil((itemsToShow - 0.5) / 2),
- 'center-even': slidesCount - Math.ceil(itemsToShow / 2),
- };
- // Compute the max index based on snapAlign, or default to 0
- const calculateMaxIndex = (_a = snapAlignCalculations[snapAlign]) !== null && _a !== void 0 ? _a : 0;
- // Return the result ensuring it's non-negative
- return Math.max(calculateMaxIndex, 0);
- }
- /**
- * Determines the minimum slide index based on the configuration.
- *
- * @param {Args} args - The carousel configuration and slide count.
- * @returns {number} The minimum slide index.
- */
- function getMinSlideIndex({ config, slidesCount }) {
- var _a;
- const { snapAlign = 'N/A', wrapAround, itemsToShow = 1 } = config;
- // If wrapAround is enabled or itemsToShow exceeds slidesCount, the minimum index is always 0
- if (wrapAround || itemsToShow > slidesCount) {
- return 0;
- }
- // Map of snapAlign to offset calculations
- const snapAlignCalculations = {
- start: 0,
- end: Math.floor(itemsToShow - 1),
- center: Math.floor((itemsToShow - 1) / 2),
- 'center-odd': Math.floor((itemsToShow - 1) / 2),
- 'center-even': Math.floor((itemsToShow - 2) / 2),
- };
- // Return the calculated offset or default to 0 for invalid snapAlign values
- return (_a = snapAlignCalculations[snapAlign]) !== null && _a !== void 0 ? _a : 0;
- }
- function getNumberInRange({ val, max, min }) {
- if (max < min) {
- return val;
- }
- return Math.min(Math.max(val, min), max);
- }
- const calculateOffset = (snapAlign, itemsToShow) => {
- var _a;
- const offsetMap = {
- start: 0,
- center: (itemsToShow - 1) / 2,
- 'center-odd': (itemsToShow - 1) / 2,
- 'center-even': (itemsToShow - 2) / 2,
- end: itemsToShow - 1,
- };
- return (_a = offsetMap[snapAlign]) !== null && _a !== void 0 ? _a : 0; // Fallback to 0 for unknown snapAlign
- };
- function getScrolledIndex({ config, currentSlide, slidesCount }) {
- const { snapAlign = 'N/A', wrapAround, itemsToShow = 1 } = config;
- // Calculate the offset based on snapAlign
- const offset = calculateOffset(snapAlign, itemsToShow);
- // Compute the index with or without wrapAround
- if (!wrapAround) {
- return getNumberInRange({
- val: currentSlide - offset,
- max: slidesCount - itemsToShow,
- min: 0,
- });
- }
- return currentSlide - offset;
- }
- function getSlidesVNodes(vNode) {
- if (!vNode)
- return [];
- return vNode.reduce((acc, node) => {
- var _a;
- if (node.type === Fragment) {
- return [...acc, ...getSlidesVNodes(node.children)];
- }
- if (((_a = node.type) === null || _a === void 0 ? void 0 : _a.name) === 'CarouselSlide') {
- return [...acc, node];
- }
- return acc;
- }, []);
- }
- function mapNumberToRange({ val, max, min = 0 }) {
- const mod = max - min + 1;
- return ((val - min) % mod + mod) % mod + min;
- }
- /**
- * return a throttle version of the function
- * Throttling
- *
- */
- // eslint-disable-next-line no-unused-vars
- function throttle(fn) {
- let isRunning = false;
- return function (...args) {
- if (!isRunning) {
- isRunning = true;
- requestAnimationFrame(() => {
- fn.apply(this, args);
- isRunning = false;
- });
- }
- };
- }
- /**
- * return a debounced version of the function
- * @param fn
- * @param delay
- */
- // eslint-disable-next-line no-unused-vars
- function debounce(fn, delay) {
- let timerId;
- return function (...args) {
- if (timerId) {
- clearTimeout(timerId);
- }
- timerId = setTimeout(() => {
- fn(...args);
- timerId = null;
- }, delay);
- };
- }
- function i18nFormatter(string = '', values = {}) {
- return Object.entries(values).reduce((acc, [key, value]) => acc.replace(`{${key}}`, String(value)), string);
- }
- var ARIAComponent = defineComponent({
- name: 'ARIA',
- setup() {
- const config = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
- const currentSlide = inject('currentSlide', ref(0));
- const slidesCount = inject('slidesCount', ref(0));
- return () => h('div', {
- class: ['carousel__liveregion', 'carousel__sr-only'],
- 'aria-live': 'polite',
- 'aria-atomic': 'true',
- }, i18nFormatter(config.i18n['itemXofY'], {
- currentSlide: currentSlide.value + 1,
- slidesCount: slidesCount.value,
- }));
- },
- });
- var Carousel = defineComponent({
- name: 'Carousel',
- props: carouselProps,
- emits: [
- 'init',
- 'drag',
- 'slide-start',
- 'loop',
- 'update:modelValue',
- 'slide-end',
- 'before-init',
- ],
- setup(props, { slots, emit, expose }) {
- var _a;
- const root = ref(null);
- const viewport = ref(null);
- const slides = ref([]);
- const slideSize = ref(0);
- const slidesCount = ref(0);
- const fallbackConfig = computed(() => (Object.assign(Object.assign(Object.assign({}, DEFAULT_CONFIG), props), { i18n: Object.assign(Object.assign({}, DEFAULT_CONFIG.i18n), props.i18n), breakpoints: undefined })));
- // current active config
- const config = reactive(Object.assign({}, fallbackConfig.value));
- // slides
- const currentSlideIndex = ref((_a = props.modelValue) !== null && _a !== void 0 ? _a : 0);
- const prevSlideIndex = ref(0);
- const middleSlideIndex = ref(0);
- const maxSlideIndex = ref(0);
- const minSlideIndex = ref(0);
- let autoplayTimer = null;
- let transitionTimer = null;
- let resizeObserver = null;
- const effectiveSlideSize = computed(() => slideSize.value + config.gap);
- const normalizeDir = computed(() => {
- const dir = config.dir || 'lrt';
- const dirMap = {
- 'left-to-right': 'ltr',
- 'right-to-left': 'rtl',
- 'top-to-bottom': 'ttb',
- 'bottom-to-top': 'btt',
- };
- return dirMap[dir] || dir;
- });
- const isVertical = computed(() => ['ttb', 'btt'].includes(normalizeDir.value));
- provide('config', config);
- provide('slidesCount', slidesCount);
- provide('currentSlide', currentSlideIndex);
- provide('maxSlide', maxSlideIndex);
- provide('minSlide', minSlideIndex);
- provide('slideSize', slideSize);
- provide('isVertical', isVertical);
- provide('normalizeDir', normalizeDir);
- function updateBreakpointsConfig() {
- var _a;
- // Determine the width source based on the 'breakpointMode' config
- const widthSource = (config.breakpointMode === 'carousel'
- ? (_a = root.value) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width
- : window.innerWidth) || 0;
- const breakpointsArray = Object.keys(props.breakpoints || {})
- .map((key) => Number(key))
- .sort((a, b) => +b - +a);
- let newConfig = Object.assign({}, fallbackConfig.value);
- breakpointsArray.some((breakpoint) => {
- var _a;
- if (widthSource >= breakpoint) {
- newConfig = Object.assign(Object.assign({}, newConfig), (_a = props.breakpoints) === null || _a === void 0 ? void 0 : _a[breakpoint]);
- return true;
- }
- return false;
- });
- Object.assign(config, newConfig);
- }
- const handleResize = debounce(() => {
- updateBreakpointsConfig();
- updateSlidesData();
- updateSlideSize();
- }, 16);
- /**
- * Setup functions
- */
- function updateSlideSize() {
- if (!viewport.value)
- return;
- const rect = viewport.value.getBoundingClientRect();
- // Calculate the total gap space
- const totalGap = (config.itemsToShow - 1) * config.gap;
- // Calculate size based on orientation
- if (isVertical.value) {
- slideSize.value = (rect.height - totalGap) / config.itemsToShow;
- }
- else {
- slideSize.value = (rect.width - totalGap) / config.itemsToShow;
- }
- }
- function updateSlidesData() {
- if (slidesCount.value <= 0)
- return;
- middleSlideIndex.value = Math.ceil((slidesCount.value - 1) / 2);
- maxSlideIndex.value = getMaxSlideIndex({ config, slidesCount: slidesCount.value });
- minSlideIndex.value = getMinSlideIndex({ config, slidesCount: slidesCount.value });
- if (!config.wrapAround) {
- currentSlideIndex.value = getNumberInRange({
- val: currentSlideIndex.value,
- max: maxSlideIndex.value,
- min: minSlideIndex.value,
- });
- }
- }
- onMounted(() => {
- nextTick(() => updateSlideSize());
- // Overcome some edge cases
- setTimeout(() => updateSlideSize(), 1000);
- updateBreakpointsConfig();
- initAutoplay();
- window.addEventListener('resize', handleResize, { passive: true });
- resizeObserver = new ResizeObserver(handleResize);
- if (root.value) {
- resizeObserver.observe(root.value);
- }
- emit('init');
- });
- onUnmounted(() => {
- if (transitionTimer) {
- clearTimeout(transitionTimer);
- }
- if (autoplayTimer) {
- clearInterval(autoplayTimer);
- }
- if (resizeObserver && root.value) {
- resizeObserver.unobserve(root.value);
- resizeObserver = null;
- }
- window.removeEventListener('resize', handleResize, {
- passive: true,
- });
- });
- /**
- * Carousel Event listeners
- */
- let isTouch = false;
- const startPosition = { x: 0, y: 0 };
- const dragged = reactive({ x: 0, y: 0 });
- const isHover = ref(false);
- const isDragging = ref(false);
- const handleMouseEnter = () => {
- isHover.value = true;
- };
- const handleMouseLeave = () => {
- isHover.value = false;
- };
- function handleDragStart(event) {
- // Prevent drag initiation on input elements or if already sliding
- const targetTagName = event.target.tagName;
- if (['INPUT', 'TEXTAREA', 'SELECT'].includes(targetTagName) || isSliding.value) {
- return;
- }
- // Detect if the event is a touchstart or mousedown event
- isTouch = event.type === 'touchstart';
- // For mouse events, prevent default to avoid text selection
- if (!isTouch) {
- event.preventDefault();
- if (event.button !== 0) {
- // Ignore non-left-click mouse events
- return;
- }
- }
- // Initialize start positions for the drag
- startPosition.x = isTouch ? event.touches[0].clientX : event.clientX;
- startPosition.y = isTouch ? event.touches[0].clientY : event.clientY;
- // Attach event listeners for dragging and drag end
- const moveEvent = isTouch ? 'touchmove' : 'mousemove';
- const endEvent = isTouch ? 'touchend' : 'mouseup';
- document.addEventListener(moveEvent, handleDragging, { passive: false });
- document.addEventListener(endEvent, handleDragEnd, { passive: true });
- }
- const handleDragging = throttle((event) => {
- isDragging.value = true;
- // Get the current position based on the interaction type (touch or mouse)
- const currentX = isTouch ? event.touches[0].clientX : event.clientX;
- const currentY = isTouch ? event.touches[0].clientY : event.clientY;
- // Calculate deltas for X and Y axes
- const deltaX = currentX - startPosition.x;
- const deltaY = currentY - startPosition.y;
- // Update dragged state reactively
- dragged.x = deltaX;
- dragged.y = deltaY;
- // Emit a drag event for further customization if needed
- emit('drag', { deltaX, deltaY });
- });
- function handleDragEnd() {
- // Determine the active axis and direction multiplier
- const dragAxis = isVertical.value ? 'y' : 'x';
- const directionMultiplier = ['rtl', 'btt'].includes(normalizeDir.value) ? -1 : 1;
- // Calculate dragged slides with a tolerance to account for incomplete drags
- const tolerance = Math.sign(dragged[dragAxis]) * 0.4; // Smooth out small drags
- const draggedSlides = Math.round(dragged[dragAxis] / effectiveSlideSize.value + tolerance) *
- directionMultiplier;
- // Prevent accidental clicks when there is a slide drag
- if (draggedSlides && !isTouch) {
- const preventClick = (e) => {
- e.preventDefault();
- window.removeEventListener('click', preventClick);
- };
- window.addEventListener('click', preventClick);
- }
- // Slide to the appropriate slide index
- const targetSlideIndex = currentSlideIndex.value - draggedSlides;
- slideTo(targetSlideIndex);
- // Reset drag state
- dragged.x = 0;
- dragged.y = 0;
- isDragging.value = false;
- const moveEvent = isTouch ? 'touchmove' : 'mousemove';
- const endEvent = isTouch ? 'touchend' : 'mouseup';
- document.removeEventListener(moveEvent, handleDragging);
- document.removeEventListener(endEvent, handleDragEnd);
- }
- /**
- * Autoplay
- */
- function initAutoplay() {
- if (!config.autoplay || config.autoplay <= 0) {
- return;
- }
- autoplayTimer = setInterval(() => {
- if (config.pauseAutoplayOnHover && isHover.value) {
- return;
- }
- next();
- }, config.autoplay);
- }
- function resetAutoplay() {
- if (autoplayTimer) {
- clearInterval(autoplayTimer);
- autoplayTimer = null;
- }
- initAutoplay();
- }
- /**
- * Navigation function
- */
- const isSliding = ref(false);
- function slideTo(slideIndex) {
- const currentVal = config.wrapAround
- ? slideIndex
- : getNumberInRange({
- val: slideIndex,
- max: maxSlideIndex.value,
- min: minSlideIndex.value,
- });
- if (currentSlideIndex.value === currentVal || isSliding.value) {
- return;
- }
- emit('slide-start', {
- slidingToIndex: slideIndex,
- currentSlideIndex: currentSlideIndex.value,
- prevSlideIndex: prevSlideIndex.value,
- slidesCount: slidesCount.value,
- });
- isSliding.value = true;
- prevSlideIndex.value = currentSlideIndex.value;
- currentSlideIndex.value = currentVal;
- transitionTimer = setTimeout(() => {
- if (config.wrapAround) {
- const mappedNumber = mapNumberToRange({
- val: currentVal,
- max: maxSlideIndex.value,
- min: 0,
- });
- if (mappedNumber !== currentSlideIndex.value) {
- currentSlideIndex.value = mappedNumber;
- emit('loop', {
- currentSlideIndex: currentSlideIndex.value,
- slidingToIndex: slideIndex,
- });
- }
- }
- emit('update:modelValue', currentSlideIndex.value);
- emit('slide-end', {
- currentSlideIndex: currentSlideIndex.value,
- prevSlideIndex: prevSlideIndex.value,
- slidesCount: slidesCount.value,
- });
- isSliding.value = false;
- resetAutoplay();
- }, config.transition);
- }
- function next() {
- slideTo(currentSlideIndex.value + config.itemsToScroll);
- }
- function prev() {
- slideTo(currentSlideIndex.value - config.itemsToScroll);
- }
- const nav = { slideTo, next, prev };
- provide('nav', nav);
- provide('isSliding', isSliding);
- function restartCarousel() {
- updateBreakpointsConfig();
- updateSlidesData();
- updateSlideSize();
- resetAutoplay();
- }
- // Update the carousel on props change
- watch(() => (Object.assign({}, props)), restartCarousel, { deep: true });
- // Handle changing v-model value
- watch(() => props['modelValue'], (val) => {
- if (val === currentSlideIndex.value) {
- return;
- }
- slideTo(Number(val));
- });
- // Handel when slides added/removed
- watch(slidesCount, updateSlidesData);
- // Init carousel
- emit('before-init');
- const data = {
- config,
- slidesCount,
- slideSize,
- currentSlide: currentSlideIndex,
- maxSlide: maxSlideIndex,
- minSlide: minSlideIndex,
- middleSlide: middleSlideIndex,
- };
- expose({
- updateBreakpointsConfig,
- updateSlidesData,
- updateSlideSize,
- restartCarousel,
- slideTo,
- next,
- prev,
- nav,
- data,
- });
- /**
- * Track style
- */
- const trackTransform = computed(() => {
- // Calculate the scrolled index with wrapping offset if applicable
- const scrolledIndex = getScrolledIndex({
- config,
- currentSlide: currentSlideIndex.value,
- slidesCount: slidesCount.value,
- });
- const cloneOffset = config.wrapAround ? slidesCount.value : 0;
- // Determine direction multiplier for orientation
- const isReverseDirection = ['rtl', 'btt'].includes(normalizeDir.value);
- const directionMultiplier = isReverseDirection ? -1 : 1;
- // Calculate the total offset for slide transformation
- const totalOffset = (scrolledIndex + cloneOffset) * effectiveSlideSize.value * directionMultiplier;
- // Include user drag interaction offset
- const dragOffset = isVertical.value ? dragged.y : dragged.x;
- // Generate the appropriate CSS transformation
- const translateAxis = isVertical.value ? 'Y' : 'X';
- return `translate${translateAxis}(${dragOffset - totalOffset}px)`;
- });
- const slotSlides = slots.default || slots.slides;
- const slotAddons = slots.addons;
- const slotsProps = reactive(data);
- return () => {
- if (!config.enabled) {
- return h('section', {
- ref: root,
- class: ['carousel', 'is-disabled'],
- }, slotSlides === null || slotSlides === void 0 ? void 0 : slotSlides());
- }
- const slidesElements = getSlidesVNodes(slotSlides === null || slotSlides === void 0 ? void 0 : slotSlides(slotsProps));
- const addonsElements = (slotAddons === null || slotAddons === void 0 ? void 0 : slotAddons(slotsProps)) || [];
- slidesElements.forEach((el, index) => {
- if (el.props) {
- el.props.index = index;
- }
- else {
- el.props = { index };
- }
- });
- let output = slidesElements;
- if (config.wrapAround) {
- const slidesBefore = slidesElements.map((el, index) => cloneVNode(el, {
- index: -slidesElements.length + index,
- isClone: true,
- key: `clone-before-${index}`,
- }));
- const slidesAfter = slidesElements.map((el, index) => cloneVNode(el, {
- index: slidesElements.length + index,
- isClone: true,
- key: `clone-after-${index}`,
- }));
- output = [...slidesBefore, ...slidesElements, ...slidesAfter];
- }
- slides.value = slidesElements;
- slidesCount.value = Math.max(slidesElements.length, 1);
- const trackEl = h('ol', {
- class: 'carousel__track',
- style: {
- transform: trackTransform.value,
- transition: `${isSliding.value ? config.transition : 0}ms`,
- gap: `${config.gap}px`,
- },
- onMousedownCapture: config.mouseDrag ? handleDragStart : null,
- onTouchstartPassiveCapture: config.touchDrag ? handleDragStart : null,
- }, output);
- const viewPortEl = h('div', { class: 'carousel__viewport', ref: viewport }, trackEl);
- return h('section', {
- ref: root,
- class: [
- 'carousel',
- `is-${normalizeDir.value}`,
- {
- 'is-vertical': isVertical.value,
- 'is-sliding': isSliding.value,
- 'is-dragging': isDragging.value,
- 'is-hover': isHover.value,
- },
- ],
- style: {
- '--vc-trk-height': `${typeof config.height === 'number' ? `${config.height}px` : config.height}`,
- },
- dir: normalizeDir.value,
- 'aria-label': config.i18n['ariaGallery'],
- tabindex: '0',
- onMouseenter: handleMouseEnter,
- onMouseleave: handleMouseLeave,
- }, [viewPortEl, addonsElements, h(ARIAComponent)]);
- };
- },
- });
- var IconName;
- (function (IconName) {
- IconName["arrowUp"] = "arrowUp";
- IconName["arrowDown"] = "arrowDown";
- IconName["arrowRight"] = "arrowRight";
- IconName["arrowLeft"] = "arrowLeft";
- })(IconName || (IconName = {}));
- const icons = {
- arrowUp: 'M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z',
- arrowDown: 'M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z',
- arrowRight: 'M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z',
- arrowLeft: 'M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z',
- };
- function isIconName(candidate) {
- return candidate in IconName;
- }
- const Icon = (props) => {
- const config = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
- const iconName = String(props.name);
- const iconI18n = `icon${iconName.charAt(0).toUpperCase() + iconName.slice(1)}`;
- if (!iconName || typeof iconName !== 'string' || !isIconName(iconName)) {
- return;
- }
- const path = icons[iconName];
- const pathEl = h('path', { d: path });
- const iconTitle = config.i18n[iconI18n] || props.title || iconName;
- const titleEl = h('title', iconTitle);
- return h('svg', {
- class: 'carousel__icon',
- viewBox: '0 0 24 24',
- role: 'img',
- 'aria-label': iconTitle,
- }, [titleEl, pathEl]);
- };
- Icon.props = { name: String, title: String };
- const Navigation = (props, { slots, attrs }) => {
- const { next: slotNext, prev: slotPrev } = slots || {};
- const config = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
- const maxSlide = inject('maxSlide', ref(1));
- const minSlide = inject('minSlide', ref(1));
- const normalizeDir = inject('normalizeDir', ref('ltr'));
- const currentSlide = inject('currentSlide', ref(1));
- const nav = inject('nav', {});
- const { wrapAround, i18n } = config;
- const getPrevIcon = () => {
- const directionIcons = {
- ltr: 'arrowLeft',
- rtl: 'arrowRight',
- ttb: 'arrowUp',
- btt: 'arrowDown',
- };
- return directionIcons[normalizeDir.value];
- };
- const getNextIcon = () => {
- const directionIcons = {
- ltr: 'arrowRight',
- rtl: 'arrowLeft',
- ttb: 'arrowDown',
- btt: 'arrowUp',
- };
- return directionIcons[normalizeDir.value];
- };
- const prevButton = h('button', {
- type: 'button',
- class: [
- 'carousel__prev',
- !wrapAround && currentSlide.value <= minSlide.value && 'carousel__prev--disabled',
- attrs === null || attrs === void 0 ? void 0 : attrs.class,
- ],
- 'aria-label': i18n['ariaPreviousSlide'],
- title: i18n['ariaPreviousSlide'],
- onClick: nav.prev,
- }, (slotPrev === null || slotPrev === void 0 ? void 0 : slotPrev()) || h(Icon, { name: getPrevIcon() }));
- const nextButton = h('button', {
- type: 'button',
- class: [
- 'carousel__next',
- !wrapAround && currentSlide.value >= maxSlide.value && 'carousel__next--disabled',
- attrs === null || attrs === void 0 ? void 0 : attrs.class,
- ],
- 'aria-label': i18n['ariaNextSlide'],
- title: i18n['ariaNextSlide'],
- onClick: nav.next,
- }, (slotNext === null || slotNext === void 0 ? void 0 : slotNext()) || h(Icon, { name: getNextIcon() }));
- return [prevButton, nextButton];
- };
- const Pagination = () => {
- const config = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
- const maxSlide = inject('maxSlide', ref(1));
- const minSlide = inject('minSlide', ref(1));
- const currentSlide = inject('currentSlide', ref(1));
- const nav = inject('nav', {});
- const isActive = (slide) => mapNumberToRange({
- val: currentSlide.value,
- max: maxSlide.value,
- min: 0,
- }) === slide;
- const children = [];
- for (let slide = minSlide.value; slide < maxSlide.value + 1; slide++) {
- const buttonLabel = i18nFormatter(config.i18n['ariaNavigateToSlide'], {
- slideNumber: slide + 1,
- });
- const button = h('button', {
- type: 'button',
- class: {
- 'carousel__pagination-button': true,
- 'carousel__pagination-button--active': isActive(slide),
- },
- 'aria-label': buttonLabel,
- title: buttonLabel,
- onClick: () => nav.slideTo(slide),
- });
- const item = h('li', { class: 'carousel__pagination-item', key: slide }, button);
- children.push(item);
- }
- return h('ol', { class: 'carousel__pagination' }, children);
- };
- var Slide = defineComponent({
- name: 'CarouselSlide',
- props: {
- index: {
- type: Number,
- default: 1,
- },
- isClone: {
- type: Boolean,
- default: false,
- },
- },
- setup(props, { slots }) {
- const config = inject('config', reactive(Object.assign({}, DEFAULT_CONFIG)));
- const currentSlide = inject('currentSlide', ref(0));
- const slidesToScroll = inject('slidesToScroll', ref(0));
- const isSliding = inject('isSliding', ref(false));
- const isVertical = inject('isVertical', ref(false));
- const slideSize = inject('slideSize', ref(0));
- const isActive = computed(() => props.index === currentSlide.value);
- const isPrev = computed(() => props.index === currentSlide.value - 1);
- const isNext = computed(() => props.index === currentSlide.value + 1);
- const isVisible = computed(() => {
- const min = Math.floor(slidesToScroll.value);
- const max = Math.ceil(slidesToScroll.value + config.itemsToShow - 1);
- return props.index >= min && props.index <= max;
- });
- const slideStyle = computed(() => {
- const dimension = config.gap
- ? `${slideSize.value}px`
- : `${100 / config.itemsToShow}%`;
- return isVertical.value
- ? { height: dimension, width: '' }
- : { width: dimension, height: '' };
- });
- return () => {
- var _a, _b;
- if (!config.enabled) {
- return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
- }
- return h('li', {
- style: slideStyle.value,
- class: {
- carousel__slide: true,
- 'carousel__slide--clone': props.isClone,
- 'carousel__slide--visible': isVisible.value,
- 'carousel__slide--active': isActive.value,
- 'carousel__slide--prev': isPrev.value,
- 'carousel__slide--next': isNext.value,
- 'carousel__slide--sliding': isSliding.value,
- },
- 'aria-hidden': !isVisible.value,
- }, (_b = slots.default) === null || _b === void 0 ? void 0 : _b.call(slots, {
- isActive: isActive.value,
- isClone: props.isClone,
- isPrev: isPrev.value,
- isNext: isNext.value,
- isSliding: isSliding.value,
- isVisible: isVisible.value,
- }));
- };
- },
- });
- export { Carousel, Icon, Navigation, Pagination, Slide };
|