UNPKG

@oruga-ui/oruga-next

Version:

UI components for Vue.js and CSS framework agnostic

513 lines (512 loc) 19.7 kB
/*! Oruga v0.11.0 | MIT License | github.com/oruga-ui/oruga */ import { defineComponent, mergeModels, useTemplateRef, computed, useModel, watch, ref, nextTick, triggerRef, onMounted, onBeforeUnmount, toRaw, readonly, createElementBlock, openBlock, withKeys, normalizeClass, withModifiers, unref, createElementVNode, renderSlot, createCommentVNode, createBlock, Fragment, withDirectives, createVNode, vShow, normalizeStyle, renderList } from "vue"; import { _ as _sfc_main$2 } from "./Icon.vue_vue_type_script_setup_true_lang-C6IfOTXx.mjs"; import { i as isClient, g as getDefault, b as registerComponent } from "./config-Dl7tu_Ly.mjs"; import { mod, bound, isDefined, sign } from "./helpers.mjs"; import { d as defineClasses } from "./defineClasses-CWB9NuS-.mjs"; import { u as useProviderParent, a as useProviderChild } from "./useParentProvider-26MPTCZ6.mjs"; const _hoisted_1$1 = ["onKeydown"]; const _hoisted_2 = ["aria-live"]; const _hoisted_3 = ["id", "tabindex", "aria-label", "aria-controls", "aria-selected", "onClick", "onKeydown"]; const _sfc_main$1 = /* @__PURE__ */ defineComponent({ ...{ isOruga: true, name: "OCarousel", configField: "carousel" }, __name: "Carousel", props: /* @__PURE__ */ mergeModels({ override: { type: Boolean, default: void 0 }, modelValue: { default: 0 }, dragable: { type: Boolean, default: true }, autoplay: { type: Boolean, default: false }, interval: { default: () => getDefault("carousel.interval", 3500) }, pauseHover: { type: Boolean, default: false }, repeat: { type: Boolean, default: false }, overlay: { type: Boolean, default: false }, indicators: { type: Boolean, default: true }, indicatorInside: { type: Boolean, default: false }, indicatorPosition: { default: () => getDefault("carousel.indicatorPosition", "bottom") }, indicatorStyle: { default: () => getDefault("carousel.indicatorStyle", "dots") }, itemsToShow: { default: () => getDefault("carousel.itemsToShow", 1) }, itemsToList: { default: () => getDefault("carousel.itemsToList", 1) }, arrows: { type: Boolean, default: () => getDefault("carousel.arrows", true) }, arrowsHover: { type: Boolean, default: () => getDefault("carousel.arrowsHover", true) }, iconPack: { default: () => getDefault("carousel.iconPack") }, iconSize: { default: () => getDefault("carousel.iconSize") }, iconPrev: { default: () => getDefault("carousel.iconPrev", "chevron-left") }, iconNext: { default: () => getDefault("carousel.iconNext", "chevron-right") }, iconAutoplayPause: { default: () => getDefault("carousel.iconAutoplayPause", "pause") }, iconAutoplayResume: { default: () => getDefault("carousel.iconAutoplayResume", "play") }, breakpoints: { default: () => ({}) }, ariaAutoplayPauseLabel: { default: () => getDefault( "carousel.ariaAutoplayPauseLabel", "Stop Automatic Slide Show" ) }, ariaAutoplayResumeLabel: { default: () => getDefault( "carousel.ariaAutoplayResumeLabel", "Start Automatic Slide Show" ) }, ariaNextLabel: { default: () => getDefault("carousel.ariaNextLabel", "Next Slide") }, ariaPreviousLabel: { default: () => getDefault("carousel.ariaPreviousLabel", "Previous Slide") }, rootClass: {}, overlayClass: {}, wrapperClass: {}, itemsClass: {}, itemsDraggingClass: {}, iconClass: {}, iconPrevClass: {}, iconNextClass: {}, iconAutoplayClass: {}, indicatorsClass: {}, indicatorsInsideClass: {}, indicatorsPositionClass: {}, indicatorClass: {}, indicatorItemClass: {}, indicatorItemActiveClass: {}, indicatorItemStyleClass: {} }, { "modelValue": { default: 0 }, "modelModifiers": {} }), emits: /* @__PURE__ */ mergeModels(["update:model-value", "change", "click"], ["update:modelValue"]), setup(__props, { emit: __emit }) { const props = __props; const emits = __emit; const rootRef = useTemplateRef("rootElement"); const provideData = computed(() => ({ activeIndex: activeIndex.value, indicators: props.indicators, total: total.value, itemWidth: itemWidth.value, onDrag: onDragStart, onClick: (event) => emits("click", event), setActive: (index2) => switchTo(index2) })); const { childItems } = useProviderParent({ rootRef, data: provideData }); const activeIndex = useModel(__props, "modelValue"); const total = computed(() => childItems.value.length); const indicatorItems = computed( () => childItems.value.filter( (el, i) => mod(i, settings.value.itemsToList) === 0 ) ); let resizeObserver; if (isClient && window.ResizeObserver) { resizeObserver = new window.ResizeObserver(onRefresh); } watch( [ () => props.itemsToList, () => props.itemsToShow, () => props.arrowsHover, () => props.repeat ], () => onRefresh() ); const windowWidth = ref(0); function onRefresh() { activeIndex.value = 0; windowWidth.value = window.innerWidth; nextTick(() => triggerRef(settings)); } onMounted(() => { if (isClient) { if (window.ResizeObserver && resizeObserver && rootRef.value) resizeObserver.observe(rootRef.value); windowWidth.value = window.innerWidth; const hasReducedMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ); if (!(hasReducedMotion == null ? void 0 : hasReducedMotion.matches)) startTimer(); } }); onBeforeUnmount(() => { if (isClient) { if (window.ResizeObserver && resizeObserver) resizeObserver.disconnect(); onDragEnd(); pauseTimer(); } }); const settings = computed(() => { const breakpoints = Object.keys(props.breakpoints).map(Number).sort((a, b) => b - a); const breakpoint = breakpoints.find( (breakpoint2) => windowWidth.value >= breakpoint2 ); const settings2 = toRaw( breakpoint ? { ...props, ...props.breakpoints[breakpoint] } : props ); if (!settings2.itemsToList) settings2.itemsToList = 1; if (!settings2.itemsToShow) settings2.itemsToShow = 1; return readonly(settings2); }); const itemWidth = computed(() => { if (!windowWidth.value || !rootRef.value) return 0; const rect = rootRef.value.getBoundingClientRect(); return rect.width / settings.value.itemsToShow; }); const hasArrows = computed( () => settings.value.arrowsHover && isHovered.value || !settings.value.arrowsHover ); const hasPrev = computed(() => settings.value.repeat || activeIndex.value > 0); function onPrev() { switchTo(activeIndex.value - settings.value.itemsToList); } const hasNext = computed( () => settings.value.repeat || activeIndex.value < total.value - settings.value.itemsToList ); function onNext() { switchTo(activeIndex.value + settings.value.itemsToList); } function onHomePressed() { switchTo(0); } function onEndPressed() { switchTo(total.value - settings.value.itemsToList); } function switchTo(index2 = 0) { if (settings.value.repeat) index2 = mod(index2, total.value); index2 = bound(index2, 0, total.value - 1); activeIndex.value = index2; emits("change", index2); } function onChange(item) { switchTo(item.index); } const isHovered = ref(false); let timer; const isAutoplayPaused = ref(false); function onMouseEnter() { isHovered.value = true; if (props.autoplay && props.pauseHover) pauseTimer(); } function onMouseLeave() { isHovered.value = false; if (props.autoplay && props.pauseHover) startTimer(); } watch( () => props.autoplay, (status) => { if (status) startTimer(); else pauseTimer(); } ); watch( () => props.repeat, (status) => { if (status) startTimer(); } ); function onToggleAutoplay() { if (!isAutoplayPaused.value) { isAutoplayPaused.value = true; pauseTimer(); } else { isAutoplayPaused.value = false; startTimer(); } } function startTimer() { if (!props.autoplay || timer) return; if (isAutoplayPaused.value) return; timer = setInterval(() => { if (!props.repeat && !hasNext.value) pauseTimer(); else onNext(); }, props.interval); } function pauseTimer() { if (timer) { clearInterval(timer); timer = void 0; } } const dragX = ref(); const delta = ref(0); const isDragging = computed(() => isDefined(dragX.value)); const translation = computed( () => -bound( delta.value + activeIndex.value * itemWidth.value, 0, (childItems.value.length - settings.value.itemsToShow) * itemWidth.value ) ); function onDragStart(event) { if (isDragging.value || !settings.value.dragable || event.button !== 0 && event.type !== "touchstart") return; delta.value = 0; dragX.value = !!event.touches ? event.touches[0].clientX : event.clientX; pauseTimer(); } function onDragOver(event) { if (!isDragging.value) return; const dragEndX = !!event.touches ? (event.changedTouches[0] || event.touches[0]).clientX : event.clientX; delta.value = dragX.value - dragEndX; } function onDragEnd() { if (!isDragging.value) return; const signCheck = sign(delta.value); const results = Math.round(Math.abs(delta.value / itemWidth.value) + 0.15); switchTo(activeIndex.value + signCheck * results); delta.value = 0; dragX.value = void 0; startTimer(); } const rootClasses = defineClasses( ["rootClass", "o-carousel"], [ "overlayClass", "o-carousel__overlay", null, computed(() => props.overlay) ] ); const wrapperClasses = defineClasses(["wrapperClass", "o-carousel__wrapper"]); const itemsClasses = defineClasses( ["itemsClass", "o-carousel__items"], ["itemsDraggingClass", "o-carousel__items--dragging", null, isDragging] ); const prevIconClasses = defineClasses( ["iconClass", "o-carousel__icon"], ["iconPrevClass", "o-carousel__icon-prev"] ); const nextIconClasses = defineClasses( ["iconClass", "o-carousel__icon"], ["iconNextClass", "o-carousel__icon-next"] ); const autoplayIconClasses = defineClasses( ["iconClass", "o-carousel__icon"], ["iconAutoplayClass", "o-carousel__icon-autoplay"] ); const indicatorsClasses = defineClasses( ["indicatorsClass", "o-carousel__indicators"], [ "indicatorsInsideClass", "o-carousel__indicators--inside", null, computed(() => !!props.indicatorInside) ], [ "indicatorsPositionClass", "o-carousel__indicators--", computed(() => props.indicatorPosition), computed(() => !!props.indicatorPosition) ] ); const indicatorClasses = defineClasses([ "indicatorClass", "o-carousel__indicator" ]); const indicatorItemClasses = defineClasses( ["indicatorItemClass", "o-carousel__indicator__item"], [ "indicatorItemStyleClass", "o-carousel__indicator__item--", computed(() => props.indicatorStyle), computed(() => !!props.indicatorStyle) ] ); const indicatorItemActiveClasses = defineClasses([ "indicatorItemActiveClass", "o-carousel__indicator__item--active" ]); function indicatorItemAppliedClasses(item) { const activeClasses = activeIndex.value === item.index ? indicatorItemActiveClasses.value : []; return [...indicatorItemClasses.value, ...activeClasses]; } return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { ref: "rootElement", "data-oruga": "carousel", class: normalizeClass(unref(rootClasses)), role: "region", "aria-roledescription": "carousel", onMouseover: onMouseEnter, onMouseleave: onMouseLeave, onFocusin: onMouseEnter, onFocusout: onMouseLeave, onKeydown: [ withKeys(onPrev, ["left"]), withKeys(onNext, ["right"]), withKeys(withModifiers(onHomePressed, ["prevent"]), ["home"]), withKeys(withModifiers(onEndPressed, ["prevent"]), ["end"]) ] }, [ createElementVNode("div", { class: normalizeClass(unref(wrapperClasses)) }, [ renderSlot(_ctx.$slots, "pause", { autoplay: !isAutoplayPaused.value, toggle: onToggleAutoplay }, () => [ _ctx.autoplay ? (openBlock(), createBlock(_sfc_main$2, { key: 0, class: normalizeClass(unref(autoplayIconClasses)), pack: _ctx.iconPack, icon: !isAutoplayPaused.value ? _ctx.iconAutoplayPause : _ctx.iconAutoplayResume, size: _ctx.iconSize, clickable: "", "aria-label": !isAutoplayPaused.value ? _ctx.ariaAutoplayPauseLabel : _ctx.ariaAutoplayResumeLabel, onClick: onToggleAutoplay }, null, 8, ["class", "pack", "icon", "size", "aria-label"])) : createCommentVNode("", true) ]), renderSlot(_ctx.$slots, "arrow", { hasPrev: hasPrev.value, prev: onPrev, hasNext: hasNext.value, next: onNext }, () => [ _ctx.arrows ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [ withDirectives(createVNode(_sfc_main$2, { class: normalizeClass(unref(prevIconClasses)), pack: _ctx.iconPack, icon: _ctx.iconPrev, size: _ctx.iconSize, clickable: "", "aria-label": _ctx.ariaPreviousLabel, onClick: onPrev }, null, 8, ["class", "pack", "icon", "size", "aria-label"]), [ [vShow, hasArrows.value && hasPrev.value] ]), withDirectives(createVNode(_sfc_main$2, { class: normalizeClass(unref(nextIconClasses)), pack: _ctx.iconPack, icon: _ctx.iconNext, size: _ctx.iconSize, clickable: "", "aria-label": _ctx.ariaNextLabel, onClick: onNext }, null, 8, ["class", "pack", "icon", "size", "aria-label"]), [ [vShow, hasArrows.value && hasNext.value] ]) ], 64)) : createCommentVNode("", true) ]), createElementVNode("div", { class: normalizeClass(unref(itemsClasses)), style: normalizeStyle("transform:translateX(" + translation.value + "px)"), "aria-roledescription": "carousel-slide", "aria-atomic": "false", "aria-live": _ctx.autoplay ? "off" : "polite", onDragend: onDragEnd, onDragover: onDragOver, onTouchmove: onDragOver, onTouchend: onDragEnd }, [ renderSlot(_ctx.$slots, "default") ], 46, _hoisted_2) ], 2), renderSlot(_ctx.$slots, "indicators", { active: activeIndex.value, switchTo }, () => [ _ctx.indicators ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass(unref(indicatorsClasses)), role: "tablist", "aria-label": "Slides" }, [ (openBlock(true), createElementBlock(Fragment, null, renderList(indicatorItems.value, (item) => { return openBlock(), createElementBlock("div", { id: `carousel-${item.identifier}`, key: item.index, class: normalizeClass(unref(indicatorClasses)), role: "tab", tabindex: __props.modelValue === item.index ? "0" : "-1", "aria-label": `Slide ${item.identifier}`, "aria-controls": `carouselpanel-${item.identifier}`, "aria-selected": __props.modelValue === item.index, onClick: ($event) => onChange(item), onKeydown: [ withKeys(($event) => onChange(item), ["enter"]), withKeys(($event) => onChange(item), ["space"]) ] }, [ renderSlot(_ctx.$slots, "indicator", { index: item.index }, () => [ createElementVNode("span", { class: normalizeClass(indicatorItemAppliedClasses(item)) }, null, 2) ]) ], 42, _hoisted_3); }), 128)) ], 2)) : createCommentVNode("", true) ]), _ctx.overlay ? renderSlot(_ctx.$slots, "overlay", { key: 0 }) : createCommentVNode("", true) ], 42, _hoisted_1$1); }; } }); const _hoisted_1 = ["id", "data-id", "role", "aria-labelledby", "aria-label"]; const _sfc_main = /* @__PURE__ */ defineComponent({ ...{ isOruga: true, name: "OCarouselItem", configField: "carousel" }, __name: "CarouselItem", props: { override: { type: Boolean, default: void 0 }, clickable: { type: Boolean, default: false }, itemClass: {}, itemActiveClass: {}, itemClickableClass: {} }, setup(__props) { const props = __props; const { parent, item } = useProviderChild(); const isActive = computed(() => parent.value.activeIndex === item.value.index); const itemStyle = computed(() => ({ width: `${parent.value.itemWidth}px` })); function onClick(event) { if (isActive.value) parent.value.onClick(event); if (props.clickable) parent.value.setActive(item.value.index); } const itemClasses = defineClasses( ["itemClass", "o-carousel__item"], ["itemActiveClass", "o-carousel__item--active", null, isActive], [ "itemClickableClass", "o-carousel__item--clickable", null, computed(() => props.clickable) ] ); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { id: `carouselpanel-${unref(item).identifier}`, "data-oruga": "carousel-item", "data-id": `carousel-${unref(item).identifier}`, class: normalizeClass(unref(itemClasses)), style: normalizeStyle(itemStyle.value), role: unref(parent).indicators ? "tabpanel" : "group", "aria-labelledby": `carousel-${unref(item).identifier}`, "aria-roledescription": "slide", "aria-label": `${unref(item).index + 1} of ${unref(parent).total}`, draggable: "true", onClick, onKeydown: [ withKeys(onClick, ["enter"]), withKeys(onClick, ["space"]) ], onDragstart: _cache[0] || (_cache[0] = //@ts-ignore (...args) => unref(parent).onDrag && unref(parent).onDrag(...args)), onTouchstart: _cache[1] || (_cache[1] = //@ts-ignore (...args) => unref(parent).onDrag && unref(parent).onDrag(...args)) }, [ renderSlot(_ctx.$slots, "default") ], 46, _hoisted_1); }; } }); const index = { install(app) { registerComponent(app, _sfc_main$1); registerComponent(app, _sfc_main); } }; export { _sfc_main$1 as OCarousel, _sfc_main as OCarouselItem, index as default }; //# sourceMappingURL=carousel.mjs.map