@zag-js/carousel
Version:
Core logic for the carousel widget implemented as a state machine
799 lines (795 loc) • 28.1 kB
JavaScript
import { createAnatomy } from '@zag-js/anatomy';
import { raf, addDomEvent, trackPointerMove, queryAll, resizeObserverBorderBox, getTabbables, dataAttr, getEventKey, ariaAttr, isLeftClick, getEventTarget, isFocusable, contains } from '@zag-js/dom-query';
import { clampValue, prevIndex, nextIndex, add, remove, uniq, callAll, isObject, ensureProps, createSplitProps, throttle } from '@zag-js/utils';
import { createMachine } from '@zag-js/core';
import { getScrollSnapPositions, findSnapPoint } from '@zag-js/scroll-snap';
import { createProps } from '@zag-js/types';
// src/carousel.anatomy.ts
var anatomy = createAnatomy("carousel").parts(
"root",
"itemGroup",
"item",
"control",
"nextTrigger",
"prevTrigger",
"indicatorGroup",
"indicator",
"autoplayTrigger",
"progressText"
);
var parts = anatomy.build();
var getRootId = (ctx) => ctx.ids?.root ?? `carousel:${ctx.id}`;
var getItemId = (ctx, index) => ctx.ids?.item?.(index) ?? `carousel:${ctx.id}:item:${index}`;
var getItemGroupId = (ctx) => ctx.ids?.itemGroup ?? `carousel:${ctx.id}:item-group`;
var getNextTriggerId = (ctx) => ctx.ids?.nextTrigger ?? `carousel:${ctx.id}:next-trigger`;
var getPrevTriggerId = (ctx) => ctx.ids?.prevTrigger ?? `carousel:${ctx.id}:prev-trigger`;
var getIndicatorGroupId = (ctx) => ctx.ids?.indicatorGroup ?? `carousel:${ctx.id}:indicator-group`;
var getIndicatorId = (ctx, index) => ctx.ids?.indicator?.(index) ?? `carousel:${ctx.id}:indicator:${index}`;
var getItemGroupEl = (ctx) => ctx.getById(getItemGroupId(ctx));
var getItemEls = (ctx) => queryAll(getItemGroupEl(ctx), `[data-part=item]`);
var getIndicatorEl = (ctx, page) => ctx.getById(getIndicatorId(ctx, page));
var syncTabIndex = (ctx) => {
const el = getItemGroupEl(ctx);
if (!el) return;
const tabbables = getTabbables(el);
el.setAttribute("tabindex", tabbables.length > 0 ? "-1" : "0");
};
// src/carousel.connect.ts
function connect(service, normalize) {
const { state, context, computed, send, scope, prop } = service;
const isPlaying = state.matches("autoplay");
const isDragging = state.matches("dragging");
const canScrollNext = computed("canScrollNext");
const canScrollPrev = computed("canScrollPrev");
const horizontal = computed("isHorizontal");
const autoSize = prop("autoSize");
const pageSnapPoints = Array.from(context.get("pageSnapPoints"));
const page = context.get("page");
const slidesPerPage = prop("slidesPerPage");
const padding = prop("padding");
const translations = prop("translations");
return {
isPlaying,
isDragging,
page,
pageSnapPoints,
canScrollNext,
canScrollPrev,
getProgress() {
return page / pageSnapPoints.length;
},
getProgressText() {
const details = { page: page + 1, totalPages: pageSnapPoints.length };
return translations.progressText?.(details) ?? "";
},
scrollToIndex(index, instant) {
send({ type: "INDEX.SET", index, instant });
},
scrollTo(index, instant) {
send({ type: "PAGE.SET", index, instant });
},
scrollNext(instant) {
send({ type: "PAGE.NEXT", instant });
},
scrollPrev(instant) {
send({ type: "PAGE.PREV", instant });
},
play() {
send({ type: "AUTOPLAY.START" });
},
pause() {
send({ type: "AUTOPLAY.PAUSE" });
},
isInView(index) {
return Array.from(context.get("slidesInView")).includes(index);
},
refresh() {
send({ type: "SNAP.REFRESH" });
},
getRootProps() {
return normalize.element({
...parts.root.attrs,
id: getRootId(scope),
role: "region",
"aria-roledescription": "carousel",
"data-orientation": prop("orientation"),
dir: prop("dir"),
style: {
"--slides-per-page": slidesPerPage,
"--slide-spacing": prop("spacing"),
"--slide-item-size": autoSize ? "auto" : "calc(100% / var(--slides-per-page) - var(--slide-spacing) * (var(--slides-per-page) - 1) / var(--slides-per-page))"
}
});
},
getItemGroupProps() {
return normalize.element({
...parts.itemGroup.attrs,
id: getItemGroupId(scope),
"data-orientation": prop("orientation"),
"data-dragging": dataAttr(isDragging),
dir: prop("dir"),
"aria-live": isPlaying ? "off" : "polite",
onFocus(event) {
if (!contains(event.currentTarget, getEventTarget(event))) return;
send({ type: "VIEWPORT.FOCUS" });
},
onBlur(event) {
if (contains(event.currentTarget, event.relatedTarget)) return;
send({ type: "VIEWPORT.BLUR" });
},
onMouseDown(event) {
if (event.defaultPrevented) return;
if (!prop("allowMouseDrag")) return;
if (!isLeftClick(event)) return;
const target = getEventTarget(event);
if (isFocusable(target) && target !== event.currentTarget) return;
event.preventDefault();
send({ type: "DRAGGING.START" });
},
onWheel: throttle((event) => {
const axis = prop("orientation") === "horizontal" ? "deltaX" : "deltaY";
const isScrollingLeft = event[axis] < 0;
if (isScrollingLeft && !computed("canScrollPrev")) return;
const isScrollingRight = event[axis] > 0;
if (isScrollingRight && !computed("canScrollNext")) return;
send({ type: "USER.SCROLL" });
}, 150),
onTouchStart() {
send({ type: "USER.SCROLL" });
},
style: {
display: autoSize ? "flex" : "grid",
gap: "var(--slide-spacing)",
scrollSnapType: [horizontal ? "x" : "y", prop("snapType")].join(" "),
gridAutoFlow: horizontal ? "column" : "row",
scrollbarWidth: "none",
overscrollBehaviorX: "contain",
[horizontal ? "gridAutoColumns" : "gridAutoRows"]: autoSize ? void 0 : "var(--slide-item-size)",
[horizontal ? "scrollPaddingInline" : "scrollPaddingBlock"]: padding,
[horizontal ? "paddingInline" : "paddingBlock"]: padding,
[horizontal ? "overflowX" : "overflowY"]: "auto"
}
});
},
getItemProps(props2) {
const isInView = context.get("slidesInView").includes(props2.index);
return normalize.element({
...parts.item.attrs,
id: getItemId(scope, props2.index),
dir: prop("dir"),
role: "group",
"data-index": props2.index,
"data-inview": dataAttr(isInView),
"aria-roledescription": "slide",
"data-orientation": prop("orientation"),
"aria-label": translations.item(props2.index, prop("slideCount")),
"aria-hidden": ariaAttr(!isInView),
style: {
flex: "0 0 auto",
[horizontal ? "maxWidth" : "maxHeight"]: "100%",
scrollSnapAlign: (() => {
const snapAlign = props2.snapAlign ?? "start";
const slidesPerMove = prop("slidesPerMove");
const perMove = slidesPerMove === "auto" ? Math.floor(prop("slidesPerPage")) : slidesPerMove;
const shouldSnap = (props2.index + perMove) % perMove === 0;
return shouldSnap ? snapAlign : void 0;
})()
}
});
},
getControlProps() {
return normalize.element({
...parts.control.attrs,
"data-orientation": prop("orientation")
});
},
getPrevTriggerProps() {
return normalize.button({
...parts.prevTrigger.attrs,
id: getPrevTriggerId(scope),
type: "button",
disabled: !canScrollPrev,
dir: prop("dir"),
"aria-label": translations.prevTrigger,
"data-orientation": prop("orientation"),
"aria-controls": getItemGroupId(scope),
onClick(event) {
if (event.defaultPrevented) return;
send({ type: "PAGE.PREV", src: "trigger" });
}
});
},
getNextTriggerProps() {
return normalize.button({
...parts.nextTrigger.attrs,
dir: prop("dir"),
id: getNextTriggerId(scope),
type: "button",
"aria-label": translations.nextTrigger,
"data-orientation": prop("orientation"),
"aria-controls": getItemGroupId(scope),
disabled: !canScrollNext,
onClick(event) {
if (event.defaultPrevented) return;
send({ type: "PAGE.NEXT", src: "trigger" });
}
});
},
getIndicatorGroupProps() {
return normalize.element({
...parts.indicatorGroup.attrs,
dir: prop("dir"),
id: getIndicatorGroupId(scope),
"data-orientation": prop("orientation"),
onKeyDown(event) {
if (event.defaultPrevented) return;
const src = "indicator";
const keyMap = {
ArrowDown(event2) {
if (horizontal) return;
send({ type: "PAGE.NEXT", src });
event2.preventDefault();
},
ArrowUp(event2) {
if (horizontal) return;
send({ type: "PAGE.PREV", src });
event2.preventDefault();
},
ArrowRight(event2) {
if (!horizontal) return;
send({ type: "PAGE.NEXT", src });
event2.preventDefault();
},
ArrowLeft(event2) {
if (!horizontal) return;
send({ type: "PAGE.PREV", src });
event2.preventDefault();
},
Home(event2) {
send({ type: "PAGE.SET", index: 0, src });
event2.preventDefault();
},
End(event2) {
send({ type: "PAGE.SET", index: pageSnapPoints.length - 1, src });
event2.preventDefault();
}
};
const key = getEventKey(event, {
dir: prop("dir"),
orientation: prop("orientation")
});
const exec = keyMap[key];
exec?.(event);
}
});
},
getIndicatorProps(props2) {
return normalize.button({
...parts.indicator.attrs,
dir: prop("dir"),
id: getIndicatorId(scope, props2.index),
type: "button",
"data-orientation": prop("orientation"),
"data-index": props2.index,
"data-readonly": dataAttr(props2.readOnly),
"data-current": dataAttr(props2.index === page),
"aria-label": translations.indicator(props2.index),
onClick(event) {
if (event.defaultPrevented) return;
if (props2.readOnly) return;
send({ type: "PAGE.SET", index: props2.index, src: "indicator" });
}
});
},
getAutoplayTriggerProps() {
return normalize.button({
...parts.autoplayTrigger.attrs,
type: "button",
"data-orientation": prop("orientation"),
"data-pressed": dataAttr(isPlaying),
"aria-label": isPlaying ? translations.autoplayStop : translations.autoplayStart,
onClick(event) {
if (event.defaultPrevented) return;
send({ type: isPlaying ? "AUTOPLAY.PAUSE" : "AUTOPLAY.START" });
}
});
},
getProgressTextProps() {
return normalize.element({
...parts.progressText.attrs
});
}
};
}
var machine = createMachine({
props({ props: props2 }) {
ensureProps(props2, ["slideCount"], "carousel");
return {
dir: "ltr",
defaultPage: 0,
orientation: "horizontal",
snapType: "mandatory",
loop: !!props2.autoplay,
slidesPerPage: 1,
slidesPerMove: "auto",
spacing: "0px",
autoplay: false,
allowMouseDrag: false,
inViewThreshold: 0.6,
autoSize: false,
...props2,
translations: {
nextTrigger: "Next slide",
prevTrigger: "Previous slide",
indicator: (index) => `Go to slide ${index + 1}`,
item: (index, count) => `${index + 1} of ${count}`,
autoplayStart: "Start slide rotation",
autoplayStop: "Stop slide rotation",
progressText: ({ page, totalPages }) => `${page} / ${totalPages}`,
...props2.translations
}
};
},
refs() {
return {
timeoutRef: void 0
};
},
initialState({ prop }) {
return prop("autoplay") ? "autoplay" : "idle";
},
context({ prop, bindable, getContext }) {
return {
page: bindable(() => ({
defaultValue: prop("defaultPage"),
value: prop("page"),
onChange(page) {
const ctx = getContext();
const pageSnapPoints = ctx.get("pageSnapPoints");
prop("onPageChange")?.({ page, pageSnapPoint: pageSnapPoints[page] });
}
})),
pageSnapPoints: bindable(() => {
return {
defaultValue: prop("autoSize") ? Array.from({ length: prop("slideCount") }, (_, i) => i) : getPageSnapPoints(prop("slideCount"), prop("slidesPerMove"), prop("slidesPerPage"))
};
}),
slidesInView: bindable(() => ({
defaultValue: []
}))
};
},
computed: {
isRtl: ({ prop }) => prop("dir") === "rtl",
isHorizontal: ({ prop }) => prop("orientation") === "horizontal",
canScrollNext: ({ prop, context }) => prop("loop") || context.get("page") < context.get("pageSnapPoints").length - 1,
canScrollPrev: ({ prop, context }) => prop("loop") || context.get("page") > 0,
autoplayInterval: ({ prop }) => {
const autoplay = prop("autoplay");
return isObject(autoplay) ? autoplay.delay : 4e3;
}
},
watch({ track, action, context, prop, send }) {
track([() => prop("slidesPerPage"), () => prop("slidesPerMove")], () => {
action(["setSnapPoints"]);
});
track([() => context.get("page")], () => {
action(["scrollToPage", "focusIndicatorEl"]);
});
track([() => prop("orientation"), () => prop("autoSize")], () => {
action(["setSnapPoints", "scrollToPage"]);
});
track([() => prop("slideCount")], () => {
send({ type: "SNAP.REFRESH", src: "slide.count" });
});
track([() => !!prop("autoplay")], () => {
send({ type: prop("autoplay") ? "AUTOPLAY.START" : "AUTOPLAY.PAUSE", src: "autoplay.prop.change" });
});
},
on: {
"PAGE.NEXT": {
target: "idle",
actions: ["clearScrollEndTimer", "setNextPage"]
},
"PAGE.PREV": {
target: "idle",
actions: ["clearScrollEndTimer", "setPrevPage"]
},
"PAGE.SET": {
target: "idle",
actions: ["clearScrollEndTimer", "setPage"]
},
"INDEX.SET": {
target: "idle",
actions: ["clearScrollEndTimer", "setMatchingPage"]
},
"SNAP.REFRESH": {
actions: ["setSnapPoints", "clampPage"]
},
"PAGE.SCROLL": {
actions: ["scrollToPage"]
}
},
effects: ["trackSlideMutation", "trackSlideIntersections", "trackSlideResize"],
entry: ["setSnapPoints", "setPage"],
exit: ["clearScrollEndTimer"],
states: {
idle: {
on: {
"DRAGGING.START": {
target: "dragging",
actions: ["invokeDragStart"]
},
"AUTOPLAY.START": {
target: "autoplay",
actions: ["invokeAutoplayStart"]
},
"USER.SCROLL": {
target: "userScroll"
},
"VIEWPORT.FOCUS": {
target: "focus"
}
}
},
focus: {
effects: ["trackKeyboardScroll"],
on: {
"VIEWPORT.BLUR": {
target: "idle"
},
"PAGE.NEXT": {
actions: ["clearScrollEndTimer", "setNextPage"]
},
"PAGE.PREV": {
actions: ["clearScrollEndTimer", "setPrevPage"]
},
"PAGE.SET": {
actions: ["clearScrollEndTimer", "setPage"]
},
"INDEX.SET": {
actions: ["clearScrollEndTimer", "setMatchingPage"]
},
"USER.SCROLL": {
target: "userScroll"
}
}
},
dragging: {
effects: ["trackPointerMove"],
entry: ["disableScrollSnap"],
on: {
DRAGGING: {
actions: ["scrollSlides", "invokeDragging"]
},
"DRAGGING.END": {
target: "idle",
actions: ["endDragging", "invokeDraggingEnd"]
}
}
},
userScroll: {
effects: ["trackScroll"],
on: {
"DRAGGING.START": {
target: "dragging",
actions: ["invokeDragStart"]
},
"SCROLL.END": [
{
guard: "isFocused",
target: "focus",
actions: ["setClosestPage"]
},
{
target: "idle",
actions: ["setClosestPage"]
}
]
}
},
autoplay: {
effects: ["trackDocumentVisibility", "trackScroll", "autoUpdateSlide"],
exit: ["invokeAutoplayEnd"],
on: {
"AUTOPLAY.TICK": {
actions: ["setNextPage", "invokeAutoplay"]
},
"DRAGGING.START": {
target: "dragging",
actions: ["invokeDragStart"]
},
"AUTOPLAY.PAUSE": {
target: "idle"
}
}
}
},
implementations: {
guards: {
isFocused: ({ scope }) => scope.isActiveElement(getItemGroupEl(scope))
},
effects: {
autoUpdateSlide({ computed, send }) {
const id = setInterval(() => {
send({
type: computed("canScrollNext") ? "AUTOPLAY.TICK" : "AUTOPLAY.PAUSE",
src: "autoplay.interval"
});
}, computed("autoplayInterval"));
return () => clearInterval(id);
},
trackSlideMutation({ scope, send }) {
const el = getItemGroupEl(scope);
if (!el) return;
const win = scope.getWin();
const observer = new win.MutationObserver(() => {
send({ type: "SNAP.REFRESH", src: "slide.mutation" });
syncTabIndex(scope);
});
syncTabIndex(scope);
observer.observe(el, { childList: true, subtree: true });
return () => observer.disconnect();
},
trackSlideResize({ scope, send }) {
const el = getItemGroupEl(scope);
if (!el) return;
const exec = () => {
send({ type: "SNAP.REFRESH", src: "slide.resize" });
};
raf(() => {
exec();
raf(() => {
send({ type: "PAGE.SCROLL", instant: true });
});
});
const itemEls = getItemEls(scope);
itemEls.forEach(exec);
const cleanups = itemEls.map((el2) => resizeObserverBorderBox.observe(el2, exec));
return callAll(...cleanups);
},
trackSlideIntersections({ scope, prop, context }) {
const el = getItemGroupEl(scope);
const win = scope.getWin();
const observer = new win.IntersectionObserver(
(entries) => {
const slidesInView = entries.reduce((acc, entry) => {
const target = entry.target;
const index = Number(target.dataset.index ?? "-1");
if (index == null || Number.isNaN(index) || index === -1) return acc;
return entry.isIntersecting ? add(acc, index) : remove(acc, index);
}, context.get("slidesInView"));
context.set("slidesInView", uniq(slidesInView));
},
{
root: el,
threshold: prop("inViewThreshold")
}
);
getItemEls(scope).forEach((slide) => observer.observe(slide));
return () => observer.disconnect();
},
trackScroll({ send, refs, scope }) {
const el = getItemGroupEl(scope);
if (!el) return;
const onScroll = () => {
clearTimeout(refs.get("timeoutRef"));
refs.set("timeoutRef", void 0);
refs.set(
"timeoutRef",
setTimeout(() => {
send({ type: "SCROLL.END" });
}, 150)
);
};
return addDomEvent(el, "scroll", onScroll, { passive: true });
},
trackDocumentVisibility({ scope, send }) {
const doc = scope.getDoc();
const onVisibilityChange = () => {
if (doc.visibilityState === "visible") return;
send({ type: "AUTOPLAY.PAUSE", src: "doc.hidden" });
};
return addDomEvent(doc, "visibilitychange", onVisibilityChange);
},
trackPointerMove({ scope, send }) {
const doc = scope.getDoc();
return trackPointerMove(doc, {
onPointerMove({ event }) {
send({ type: "DRAGGING", left: -event.movementX, top: -event.movementY });
},
onPointerUp() {
send({ type: "DRAGGING.END" });
}
});
},
trackKeyboardScroll({ scope, send, context }) {
const win = scope.getWin();
const onKeyDown = (event) => {
switch (event.key) {
case "ArrowRight":
event.preventDefault();
send({ type: "PAGE.NEXT" });
break;
case "ArrowLeft":
event.preventDefault();
send({ type: "PAGE.PREV" });
break;
case "Home":
event.preventDefault();
send({ type: "PAGE.SET", index: 0 });
break;
case "End":
event.preventDefault();
send({ type: "PAGE.SET", index: context.get("pageSnapPoints").length - 1 });
}
};
return addDomEvent(win, "keydown", onKeyDown, { capture: true });
}
},
actions: {
clearScrollEndTimer({ refs }) {
if (refs.get("timeoutRef") == null) return;
clearTimeout(refs.get("timeoutRef"));
refs.set("timeoutRef", void 0);
},
scrollToPage({ context, event, scope, computed, flush }) {
const behavior = event.instant ? "instant" : "smooth";
const index = clampValue(event.index ?? context.get("page"), 0, context.get("pageSnapPoints").length - 1);
const el = getItemGroupEl(scope);
if (!el) return;
const axis = computed("isHorizontal") ? "left" : "top";
flush(() => {
el.scrollTo({ [axis]: context.get("pageSnapPoints")[index], behavior });
});
},
setClosestPage({ context, scope, computed }) {
const el = getItemGroupEl(scope);
if (!el) return;
const scrollPosition = computed("isHorizontal") ? el.scrollLeft : el.scrollTop;
const page = context.get("pageSnapPoints").findIndex((point) => Math.abs(point - scrollPosition) < 1);
if (page === -1) return;
context.set("page", page);
},
setNextPage({ context, prop, state }) {
const loop = state.matches("autoplay") || prop("loop");
const page = nextIndex(context.get("pageSnapPoints"), context.get("page"), { loop });
context.set("page", page);
},
setPrevPage({ context, prop, state }) {
const loop = state.matches("autoplay") || prop("loop");
const page = prevIndex(context.get("pageSnapPoints"), context.get("page"), { loop });
context.set("page", page);
},
setMatchingPage({ context, event, computed, scope }) {
const el = getItemGroupEl(scope);
if (!el) return;
const snapPoint = findSnapPoint(
el,
computed("isHorizontal") ? "x" : "y",
(node) => node.dataset.index === event.index.toString()
);
if (snapPoint == null) return;
const page = context.get("pageSnapPoints").findIndex((point) => Math.abs(point - snapPoint) < 1);
context.set("page", page);
},
setPage({ context, event }) {
const page = event.index ?? context.get("page");
context.set("page", page);
},
clampPage({ context }) {
const index = clampValue(context.get("page"), 0, context.get("pageSnapPoints").length - 1);
context.set("page", index);
},
setSnapPoints({ context, computed, scope }) {
const el = getItemGroupEl(scope);
if (!el) return;
const scrollSnapPoints = getScrollSnapPositions(el);
context.set("pageSnapPoints", computed("isHorizontal") ? scrollSnapPoints.x : scrollSnapPoints.y);
},
disableScrollSnap({ scope }) {
const el = getItemGroupEl(scope);
if (!el) return;
const styles = getComputedStyle(el);
el.dataset.scrollSnapType = styles.getPropertyValue("scroll-snap-type");
el.style.setProperty("scroll-snap-type", "none");
},
scrollSlides({ scope, event }) {
const el = getItemGroupEl(scope);
el?.scrollBy({ left: event.left, top: event.top, behavior: "instant" });
},
endDragging({ scope, context, computed }) {
const el = getItemGroupEl(scope);
if (!el) return;
const isHorizontal = computed("isHorizontal");
const scrollPos = isHorizontal ? el.scrollLeft : el.scrollTop;
const snapPoints = context.get("pageSnapPoints");
const closest = snapPoints.reduce((closest2, curr) => {
return Math.abs(curr - scrollPos) < Math.abs(closest2 - scrollPos) ? curr : closest2;
}, snapPoints[0]);
raf(() => {
el.scrollTo({
left: isHorizontal ? closest : el.scrollLeft,
top: isHorizontal ? el.scrollTop : closest,
behavior: "smooth"
});
context.set("page", snapPoints.indexOf(closest));
const scrollSnapType = el.dataset.scrollSnapType;
if (scrollSnapType) {
el.style.setProperty("scroll-snap-type", scrollSnapType);
delete el.dataset.scrollSnapType;
}
});
},
focusIndicatorEl({ context, event, scope }) {
if (event.src !== "indicator") return;
const el = getIndicatorEl(scope, context.get("page"));
if (!el) return;
raf(() => el.focus({ preventScroll: true }));
},
invokeDragStart({ context, prop }) {
prop("onDragStatusChange")?.({ type: "dragging.start", isDragging: true, page: context.get("page") });
},
invokeDragging({ context, prop }) {
prop("onDragStatusChange")?.({ type: "dragging", isDragging: true, page: context.get("page") });
},
invokeDraggingEnd({ context, prop }) {
prop("onDragStatusChange")?.({ type: "dragging.end", isDragging: false, page: context.get("page") });
},
invokeAutoplay({ context, prop }) {
prop("onAutoplayStatusChange")?.({ type: "autoplay", isPlaying: true, page: context.get("page") });
},
invokeAutoplayStart({ context, prop }) {
prop("onAutoplayStatusChange")?.({ type: "autoplay.start", isPlaying: true, page: context.get("page") });
},
invokeAutoplayEnd({ context, prop }) {
prop("onAutoplayStatusChange")?.({ type: "autoplay.stop", isPlaying: false, page: context.get("page") });
}
}
}
});
function getPageSnapPoints(totalSlides, slidesPerMove, slidesPerPage) {
if (totalSlides == null || slidesPerPage <= 0) {
return [];
}
const snapPoints = [];
const perMove = slidesPerMove === "auto" ? Math.floor(slidesPerPage) : slidesPerMove;
if (perMove <= 0) {
return [];
}
for (let i = 0; i < totalSlides; i += perMove) {
if (i + slidesPerPage > totalSlides) break;
snapPoints.push(i);
}
return snapPoints;
}
var props = createProps()([
"dir",
"getRootNode",
"id",
"ids",
"loop",
"page",
"defaultPage",
"onPageChange",
"orientation",
"slideCount",
"slidesPerPage",
"slidesPerMove",
"spacing",
"padding",
"autoplay",
"allowMouseDrag",
"inViewThreshold",
"translations",
"snapType",
"autoSize",
"onDragStatusChange",
"onAutoplayStatusChange"
]);
var splitProps = createSplitProps(props);
var indicatorProps = createProps()(["index", "readOnly"]);
var splitIndicatorProps = createSplitProps(indicatorProps);
var itemProps = createProps()(["index", "snapAlign"]);
var splitItemProps = createSplitProps(itemProps);
export { anatomy, connect, indicatorProps, itemProps, machine, props, splitIndicatorProps, splitItemProps, splitProps };