@oruga-ui/oruga-next
Version:
UI components for Vue.js and CSS framework agnostic
513 lines (512 loc) • 19.7 kB
JavaScript
/*! 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