New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@zag-js/carousel

Package Overview
Dependencies
Maintainers
0
Versions
728
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zag-js/carousel - npm Package Compare versions

Comparing version 0.78.3 to 0.79.0

201

dist/index.d.ts
import * as _zag_js_anatomy from '@zag-js/anatomy';
import { RequiredBy, PropTypes, DirectionProperty, CommonProperties, NormalizeProps } from '@zag-js/types';
import { RequiredBy, PropTypes, DirectionProperty, CommonProperties, OrientationProperty, NormalizeProps } from '@zag-js/types';
import * as _zag_js_core from '@zag-js/core';
import { Machine, StateMachine } from '@zag-js/core';
import { Machine, StateMachine, ContextRef } from '@zag-js/core';
declare const anatomy: _zag_js_anatomy.AnatomyInstance<"root" | "viewport" | "itemGroup" | "item" | "nextTrigger" | "prevTrigger" | "indicatorGroup" | "indicator">;
declare const anatomy: _zag_js_anatomy.AnatomyInstance<"root" | "itemGroup" | "item" | "control" | "nextTrigger" | "prevTrigger" | "indicatorGroup" | "indicator" | "autoplayTrigger">;
interface SlideChangeDetails {
index: number;
interface PageChangeDetails {
page: number;
pageSnapPoint: number;
}
type RectEdge = "top" | "right" | "bottom" | "left";
interface DragStatusDetails {
type: "dragging.start" | "dragging" | "dragging.end";
page: number;
isDragging: boolean;
}
interface AutoplayStatusDetails {
type: "autoplay.start" | "autoplay" | "autoplay.stop";
page: number;
isPlaying: boolean;
}
interface IntlTranslations {
nextTrigger: string;
prevTrigger: string;
indicator: (index: number) => string;
item: (index: number, count: number) => string;
autoplayStart: string;
autoplayStop: string;
}
type ElementIds = Partial<{
root: string;
viewport: string;
item(index: number): string;

@@ -22,13 +39,11 @@ itemGroup: string;

}>;
interface PublicContext extends DirectionProperty, CommonProperties {
interface PublicContext extends DirectionProperty, CommonProperties, OrientationProperty {
/**
* The orientation of the carousel.
* @default "horizontal"
* The ids of the elements in the carousel. Useful for composition.
*/
orientation: "horizontal" | "vertical";
ids?: ElementIds | undefined;
/**
* The alignment of the slides in the carousel.
* @default "start"
* The localized messages to use.
*/
align: "start" | "center" | "end";
translations: IntlTranslations;
/**

@@ -38,4 +53,25 @@ * The number of slides to show at a time.

*/
slidesPerView: number | "auto";
slidesPerPage: number;
/**
* The number of slides to scroll at a time.
*
* When set to `auto`, the number of slides to scroll is determined by the
* `slidesPerPage` property.
*
* @default "auto"
*/
slidesPerMove: number | "auto";
/**
* Whether to scroll automatically. The default delay is 4000ms.
* @default false
*/
autoplay?: boolean | {
delay: number;
} | undefined;
/**
* Whether to allow scrolling via dragging with mouse
* @default false
*/
allowMouseDrag: boolean;
/**
* Whether the carousel should loop around.

@@ -46,7 +82,7 @@ * @default false

/**
* The current slide index.
* The index of the active page.
*/
index: number;
page: number;
/**
* The amount of space between slides.
* The amount of space between items.
* @default "0px"

@@ -56,16 +92,38 @@ */

/**
* Function called when the slide changes.
* Defines the extra space added around the scrollable area,
* enabling nearby items to remain partially in view.
*/
onIndexChange?: ((details: SlideChangeDetails) => void) | undefined;
padding?: string;
/**
* The ids of the elements in the carousel. Useful for composition.
* Function called when the page changes.
*/
ids?: ElementIds | undefined;
onPageChange?: ((details: PageChangeDetails) => void) | undefined;
/**
* The threshold for determining if an item is in view.
* @default 0.6
*/
inViewThreshold: number | number[];
/**
* The snap type of the item.
* @default "mandatory"
*/
snapType: "proximity" | "mandatory";
/**
* The total number of slides.
* Useful for SSR to render the initial ating the snap points.
*/
slideCount?: number | undefined;
/**
* Function called when the drag status changes.
*/
onDragStatusChange?: ((details: DragStatusDetails) => void) | undefined;
/**
* Function called when the autoplay status changes.
*/
onAutoplayStatusChange?: ((details: AutoplayStatusDetails) => void) | undefined;
}
interface PrivateContext {
slideRects: DOMRect[];
containerRect?: DOMRect | undefined;
containerSize: number;
scrollSnaps: number[];
scrollProgress: number;
pageSnapPoints: number[];
slidesInView: number[];
timeoutRef: ContextRef<ReturnType<typeof setTimeout>>;
}

@@ -75,8 +133,5 @@ type ComputedContext = Readonly<{

isHorizontal: boolean;
isVertical: boolean;
startEdge: RectEdge;
endEdge: RectEdge;
translateValue: string;
canScrollNext: boolean;
canScrollPrev: boolean;
autoplayInterval: number;
}>;

@@ -97,27 +152,17 @@ type UserDefinedContext = RequiredBy<PublicContext, "id">;

index: number;
}
interface ItemState {
/**
* The text value of the item. Used for accessibility.
* The snap alignment of the item.
* @default "start"
*/
valueText: string;
snapAlign?: "start" | "end" | "center" | undefined;
}
interface IndicatorProps {
/**
* Whether the item is the current item in the carousel
* The index of the indicator.
*/
current: boolean;
index: number;
/**
* Whether the item is the next item in the carousel
* Whether the indicator is read only.
* @default false
*/
next: boolean;
/**
* Whether the item is the previous item in the carousel
*/
previous: boolean;
/**
* Whether the item is in view
*/
inView: boolean;
}
interface IndicatorProps {
index: number;
readOnly?: boolean | undefined;

@@ -129,36 +174,44 @@ }

*/
index: number;
page: number;
/**
* The current scroll progress of the carousel
* The current snap points of the carousel
*/
scrollProgress: number;
pageSnapPoints: number[];
/**
* Whether the carousel is auto playing
*/
autoPlaying: boolean;
isPlaying: boolean;
/**
* Whether the carousel is can scroll to the next slide
* Whether the carousel is being dragged. This only works when `draggable` is true.
*/
isDragging: boolean;
/**
* Whether the carousel is can scroll to the next view
*/
canScrollNext: boolean;
/**
* Whether the carousel is can scroll to the previous slide
* Whether the carousel is can scroll to the previous view
*/
canScrollPrev: boolean;
/**
* Function to scroll to a specific slide index
* Function to scroll to a specific item index
*/
scrollTo(index: number, jump?: boolean): void;
scrollToIndex(index: number, instant?: boolean): void;
/**
* Function to scroll to the next slide
* Function to scroll to a specific page
*/
scrollToNext(): void;
scrollTo(page: number, instant?: boolean): void;
/**
* Function to scroll to the previous slide
* Function to scroll to the next page
*/
scrollToPrevious(): void;
scrollNext(instant?: boolean): void;
/**
* Returns the state of a specific slide
* Function to scroll to the previous page
*/
getItemState(props: ItemProps): ItemState;
scrollPrev(instant?: boolean): void;
/**
* Returns the current scroll progress as a percentage
*/
getProgress(): number;
/**
* Function to start/resume autoplay

@@ -171,4 +224,13 @@ */

pause(): void;
/**
* Whether the item is in view
*/
isInView(index: number): boolean;
/**
* Function to re-compute the snap points
* and clamp the page
*/
refresh(): void;
getRootProps(): T["element"];
getViewportProps(): T["element"];
getControlProps(): T["element"];
getItemGroupProps(): T["element"];

@@ -178,2 +240,3 @@ getItemProps(props: ItemProps): T["element"];

getNextTriggerProps(): T["button"];
getAutoplayTriggerProps(): T["button"];
getIndicatorGroupProps(): T["element"];

@@ -187,7 +250,9 @@ getIndicatorProps(props: IndicatorProps): T["button"];

declare const props: ("dir" | "id" | "getRootNode" | "loop" | "align" | "orientation" | "spacing" | "slidesPerView" | "index" | "onIndexChange" | "ids")[];
declare const splitProps: <Props extends Partial<UserDefinedContext>>(props: Props) => [Partial<UserDefinedContext>, Omit<Props, "dir" | "id" | "getRootNode" | "loop" | "align" | "orientation" | "spacing" | "slidesPerView" | "index" | "onIndexChange" | "ids">];
declare const props: ("dir" | "id" | "getRootNode" | "padding" | "page" | "loop" | "orientation" | "spacing" | "autoplay" | "ids" | "translations" | "slidesPerPage" | "slidesPerMove" | "allowMouseDrag" | "onPageChange" | "inViewThreshold" | "snapType" | "slideCount" | "onDragStatusChange" | "onAutoplayStatusChange")[];
declare const splitProps: <Props extends Partial<UserDefinedContext>>(props: Props) => [Partial<UserDefinedContext>, Omit<Props, "dir" | "id" | "getRootNode" | "padding" | "page" | "loop" | "orientation" | "spacing" | "autoplay" | "ids" | "translations" | "slidesPerPage" | "slidesPerMove" | "allowMouseDrag" | "onPageChange" | "inViewThreshold" | "snapType" | "slideCount" | "onDragStatusChange" | "onAutoplayStatusChange">];
declare const indicatorProps: (keyof IndicatorProps)[];
declare const splitIndicatorProps: <Props extends IndicatorProps>(props: Props) => [IndicatorProps, Omit<Props, keyof IndicatorProps>];
declare const itemProps: (keyof ItemProps)[];
declare const splitItemProps: <Props extends ItemProps>(props: Props) => [ItemProps, Omit<Props, keyof ItemProps>];
export { type MachineApi as Api, type UserDefinedContext as Context, type ElementIds, type IndicatorProps, type ItemProps, type ItemState, type Service, type SlideChangeDetails, anatomy, connect, indicatorProps, machine, props, splitIndicatorProps, splitProps };
export { type MachineApi as Api, type AutoplayStatusDetails, type UserDefinedContext as Context, type DragStatusDetails, type ElementIds, type IndicatorProps, type IntlTranslations, type ItemProps, type PageChangeDetails, type Service, anatomy, connect, indicatorProps, itemProps, machine, props, splitIndicatorProps, splitItemProps, splitProps };
'use strict';
var anatomy$1 = require('@zag-js/anatomy');
var domEvent = require('@zag-js/dom-event');
var domQuery = require('@zag-js/dom-query');
var core = require('@zag-js/core');
var scrollSnap = require('@zag-js/scroll-snap');
var utils = require('@zag-js/utils');
var core = require('@zag-js/core');
var types = require('@zag-js/types');

@@ -12,9 +14,10 @@

"root",
"viewport",
"itemGroup",
"item",
"control",
"nextTrigger",
"prevTrigger",
"indicatorGroup",
"indicator"
"indicator",
"autoplayTrigger"
);

@@ -24,3 +27,2 @@ var parts = anatomy.build();

getRootId: (ctx) => ctx.ids?.root ?? `carousel:${ctx.id}`,
getViewportId: (ctx) => ctx.ids?.viewport ?? `carousel:${ctx.id}:viewport`,
getItemId: (ctx, index) => ctx.ids?.item?.(index) ?? `carousel:${ctx.id}:item:${index}`,

@@ -33,201 +35,64 @@ getItemGroupId: (ctx) => ctx.ids?.itemGroup ?? `carousel:${ctx.id}:item-group`,

getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
getViewportEl: (ctx) => dom.getById(ctx, dom.getViewportId(ctx)),
getSlideGroupEl: (ctx) => dom.getById(ctx, dom.getItemGroupId(ctx)),
getSlideEls: (ctx) => domQuery.queryAll(dom.getSlideGroupEl(ctx), `[data-part=item]`)
getItemGroupEl: (ctx) => dom.getById(ctx, dom.getItemGroupId(ctx)),
getItemEl: (ctx, index) => dom.getById(ctx, dom.getItemId(ctx, index)),
getItemEls: (ctx) => domQuery.queryAll(dom.getItemGroupEl(ctx), `[data-part=item]`),
getActiveIndicatorEl: (ctx) => dom.getById(ctx, dom.getIndicatorId(ctx, ctx.page)),
syncTabIndex(ctx) {
const el = dom.getItemGroupEl(ctx);
if (!el) return;
const tabbables = domQuery.getTabbables(el);
if (tabbables.length > 0) {
el.removeAttribute("tabindex");
} else {
el.setAttribute("tabindex", "0");
}
}
});
// src/utils/get-limit.ts
function getLimit(min, max) {
const length = Math.abs(min - max);
function reachedMin(n) {
return n < min;
}
function reachedMax(n) {
return n > max;
}
function reachedAny(n) {
return reachedMin(n) || reachedMax(n);
}
function constrain(n) {
if (!reachedAny(n)) return n;
return reachedMin(n) ? min : max;
}
function removeOffset(n) {
if (!length) return n;
return n - length * Math.ceil((n - max) / length);
}
return {
length,
max,
min,
constrain,
reachedAny,
reachedMax,
reachedMin,
removeOffset
};
}
var getAlignment = (align, containerSize) => {
const predefined = { start, center, end };
function start() {
return 0;
}
function center(n) {
return end(n) / 2;
}
function end(n) {
return containerSize - n;
}
function percent() {
return containerSize * Number(align);
}
return (n) => {
if (utils.isNumber(align)) return percent();
return predefined[align](n);
};
};
function getSlidesToScroll(containerSize, slideSizesWithGaps, slidesPerView) {
function byNumber(array, groupSize) {
return Array.from(array.keys()).filter((i) => i % groupSize === 0).map((i) => array.slice(i, i + groupSize));
}
function bySize(array) {
return Array.from(array.keys()).reduce((groups, i) => {
const chunk = slideSizesWithGaps.slice(groups.at(-1), i + 1);
const chunkSize = chunk.reduce((a, s) => a + s, 0);
return !i || chunkSize > containerSize ? groups.concat(i) : groups;
}, []).map((start, i, groups) => array.slice(start, groups[i + 1]));
}
return function groupSlides(array) {
return utils.isNumber(slidesPerView) ? byNumber(array, slidesPerView) : bySize(array);
};
}
// src/utils/get-slide-sizes.ts
function getSlideSizes(ctx) {
const startGap = measureStartGap();
function measureStartGap() {
if (!ctx.containerRect) return 0;
const slideRect = ctx.slideRects[0];
return Math.abs(ctx.containerRect[ctx.startEdge] - slideRect[ctx.startEdge]);
}
function measureWithGaps() {
return ctx.slideRects.map((rect, index, rects) => {
const isFirst = !index;
if (isFirst) return Math.abs(slideSizes[index] + startGap);
const isLast = index === rects.length - 1;
if (isLast) return Math.abs(slideSizes[index]);
return Math.abs(rects[index + 1][ctx.startEdge] - rect[ctx.startEdge]);
});
}
const slideSizes = ctx.slideRects.map((slideRect) => {
return ctx.isVertical ? slideRect.height : slideRect.width;
});
const slideSizesWithGaps = measureWithGaps();
return {
slideSizes,
slideSizesWithGaps
};
}
// src/utils/get-scroll-snaps.ts
var arrayLast = (array) => array[arrayLastIndex(array)];
var arrayLastIndex = (array) => Math.max(0, array.length - 1);
function getScrollSnaps(ctx) {
const { slideSizes, slideSizesWithGaps } = getSlideSizes(ctx);
const groupSlides = getSlidesToScroll(ctx.containerSize, slideSizesWithGaps, ctx.slidesPerView);
function measureSizes() {
return groupSlides(ctx.slideRects).map((rects) => arrayLast(rects)[ctx.endEdge] - rects[0][ctx.startEdge]).map(Math.abs);
}
function measureUnaligned() {
return ctx.slideRects.map((slideRect) => ctx.containerRect[ctx.startEdge] - slideRect[ctx.startEdge]).map((snap) => -Math.abs(snap));
}
function measureAligned() {
const measureFn = getAlignment(ctx.align, ctx.containerSize);
const alignments = measureSizes().map(measureFn);
return groupSlides(snaps).map((snap) => snap[0]).map((snap, index) => snap + alignments[index]);
}
const snaps = measureUnaligned();
const snapsAligned = measureAligned();
const contentSize = -arrayLast(snaps) + arrayLast(slideSizesWithGaps);
const scrollLimit = getLimit(snaps[snaps.length - 1], snaps[0]);
const scrollProgress = (snapsAligned[ctx.index] - scrollLimit.max) / -scrollLimit.length;
return {
snaps,
snapsAligned,
slideSizes,
slideSizesWithGaps,
contentSize,
scrollLimit,
scrollProgress: Math.abs(scrollProgress)
};
}
// src/utils/get-slide-in-view.ts
var slideThreshold = 0;
function getSlidesInView(ctx) {
const roundingSafety = 0.5;
const slideOffsets = [0];
const { snaps, slideSizes, scrollLimit } = getScrollSnaps(ctx);
const slideThresholds = slideSizes.map((slideSize) => {
const thresholdLimit = getLimit(roundingSafety, slideSize - roundingSafety);
return thresholdLimit.constrain(slideSize * slideThreshold);
});
const slideBounds = slideOffsets.reduce((acc, offset) => {
const bounds = snaps.map((snap, index) => ({
start: snap - slideSizes[index] + slideThresholds[index] + offset,
end: snap + ctx.containerSize - slideThresholds[index] + offset,
index
}));
return acc.concat(bounds);
}, []);
return (location) => {
const loc = scrollLimit.constrain(location);
return slideBounds.reduce((list, bound) => {
const { index, start, end } = bound;
const inList = list.includes(index);
const inView = start < loc && end > loc;
return !inList && inView ? list.concat([index]) : list;
}, []);
};
}
// src/carousel.connect.ts
function connect(state, send, normalize) {
const isPlaying = state.matches("autoplay");
const isDragging = state.matches("dragging");
const canScrollNext = state.context.canScrollNext;
const canScrollPrev = state.context.canScrollPrev;
const horizontal = state.context.isHorizontal;
const autoPlaying = state.matches("autoplay");
const activeSnap = state.context.scrollSnaps[state.context.index];
const slidesInView = domQuery.isDom() ? getSlidesInView(state.context)(activeSnap) : [];
function getItemState(props2) {
return {
valueText: `Slide ${props2.index + 1}`,
current: props2.index === state.context.index,
next: props2.index === state.context.index + 1,
previous: props2.index === state.context.index - 1,
inView: slidesInView.includes(props2.index)
};
}
const pageSnapPoints = Array.from(state.context.pageSnapPoints);
const page = state.context.page;
const slidesPerPage = state.context.slidesPerPage;
const padding = state.context.padding;
const translations = state.context.translations;
return {
index: state.context.index,
scrollProgress: state.context.scrollProgress,
autoPlaying,
isPlaying,
isDragging,
page,
pageSnapPoints,
canScrollNext,
canScrollPrev,
scrollTo(index, jump) {
send({ type: "GOTO", index, jump });
getProgress() {
return page / pageSnapPoints.length;
},
scrollToNext() {
send("NEXT");
scrollToIndex(index, instant) {
send({ type: "INDEX.SET", index, instant });
},
scrollToPrevious() {
send("PREV");
scrollTo(index, instant) {
send({ type: "PAGE.SET", index, instant });
},
getItemState,
scrollNext(instant) {
send({ type: "PAGE.NEXT", instant });
},
scrollPrev(instant) {
send({ type: "PAGE.PREV", instant });
},
play() {
send("PLAY");
send("AUTOPLAY.START");
},
pause() {
send("PAUSE");
send("AUTOPLAY.PAUSE");
},
isInView(index) {
return Array.from(state.context.slidesInView).includes(index);
},
refresh() {
send({ type: "SNAP.REFRESH" });
},
getRootProps() {

@@ -241,17 +106,9 @@ return normalize.element({

dir: state.context.dir,
"aria-label": "Carousel",
style: {
"--slides-per-page": slidesPerPage,
"--slide-spacing": state.context.spacing,
"--slide-size": `calc(100% / ${state.context.slidesPerView} - var(--slide-spacing))`
"--slide-item-size": "calc(100% / var(--slides-per-page) - var(--slide-spacing) * (var(--slides-per-page) - 1) / var(--slides-per-page))"
}
});
},
getViewportProps() {
return normalize.element({
...parts.viewport.attrs,
dir: state.context.dir,
id: dom.getViewportId(state.context),
"data-orientation": state.context.orientation
});
},
getItemGroupProps() {

@@ -262,13 +119,25 @@ return normalize.element({

"data-orientation": state.context.orientation,
"data-dragging": domQuery.dataAttr(isDragging),
dir: state.context.dir,
"aria-live": isPlaying ? "off" : "polite",
onMouseDown(event) {
if (!state.context.allowMouseDrag) return;
if (event.button !== 0) return;
if (event.defaultPrevented) return;
const target = domQuery.getEventTarget(event);
if (domQuery.isFocusable(target) && target !== event.currentTarget) return;
event.preventDefault();
send({ type: "DRAGGING.START" });
},
style: {
display: "flex",
flexDirection: horizontal ? "row" : "column",
[horizontal ? "height" : "width"]: "auto",
display: "grid",
gap: "var(--slide-spacing)",
transform: state.context.translateValue,
transitionProperty: "transform",
willChange: "transform",
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
transitionDuration: "0.3s"
scrollSnapType: [horizontal ? "x" : "y", state.context.snapType].join(" "),
gridAutoFlow: horizontal ? "column" : "row",
scrollbarWidth: "none",
overscrollBehavior: "contain",
[horizontal ? "gridAutoColumns" : "gridAutoRows"]: "var(--slide-item-size)",
[horizontal ? "scrollPaddingInline" : "scrollPaddingBlock"]: padding,
[horizontal ? "paddingInline" : "paddingBlock"]: padding,
[horizontal ? "overflowX" : "overflowY"]: "auto"
}

@@ -278,3 +147,3 @@ });

getItemProps(props2) {
const itemState = getItemState(props2);
const isInView = state.context.slidesInView.includes(props2.index);
return normalize.element({

@@ -284,15 +153,20 @@ ...parts.item.attrs,

dir: state.context.dir,
"data-current": domQuery.dataAttr(itemState.current),
"data-inview": domQuery.dataAttr(itemState.inView),
role: "group",
"data-index": props2.index,
"data-inview": domQuery.dataAttr(isInView),
"aria-roledescription": "slide",
"data-orientation": state.context.orientation,
"aria-label": itemState.valueText,
"aria-label": state.context.slideCount ? translations.item(props2.index, state.context.slideCount) : void 0,
"aria-hidden": domQuery.ariaAttr(!isInView),
style: {
position: "relative",
flex: "0 0 var(--slide-size)",
[horizontal ? "minWidth" : "minHeight"]: "0px"
scrollSnapAlign: getSnapAlign(state.context, props2)
}
});
},
getControlProps() {
return normalize.element({
...parts.control.attrs,
"data-orientation": state.context.orientation
});
},
getPrevTriggerProps() {

@@ -303,10 +177,10 @@ return normalize.button({

type: "button",
tabIndex: -1,
disabled: !canScrollPrev,
dir: state.context.dir,
"aria-label": "Previous Slide",
"aria-label": translations.prevTrigger,
"data-orientation": state.context.orientation,
"aria-controls": dom.getItemGroupId(state.context),
onClick() {
send("PREV");
onClick(event) {
if (event.defaultPrevented) return;
send({ type: "PAGE.PREV", src: "trigger" });
}

@@ -321,9 +195,9 @@ });

type: "button",
tabIndex: -1,
"aria-label": "Next Slide",
"aria-label": translations.nextTrigger,
"data-orientation": state.context.orientation,
"aria-controls": dom.getItemGroupId(state.context),
disabled: !canScrollNext,
onClick() {
send("NEXT");
onClick(event) {
if (event.defaultPrevented) return;
send({ type: "PAGE.NEXT", src: "trigger" });
}

@@ -337,3 +211,43 @@ });

id: dom.getIndicatorGroupId(state.context),
"data-orientation": state.context.orientation
"data-orientation": state.context.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 = domEvent.getEventKey(event, {
dir: state.context.dir,
orientation: state.context.orientation
});
const exec = keyMap[key];
exec?.(event);
}
});

@@ -350,11 +264,34 @@ },

"data-readonly": domQuery.dataAttr(props2.readOnly),
"data-current": domQuery.dataAttr(props2.index === state.context.index),
onClick() {
"data-current": domQuery.dataAttr(props2.index === state.context.page),
"aria-label": translations.indicator(props2.index),
onClick(event) {
if (event.defaultPrevented) return;
if (props2.readOnly) return;
send({ type: "GOTO", index: props2.index });
send({ type: "PAGE.SET", index: props2.index, src: "indicator" });
}
});
},
getAutoplayTriggerProps() {
return normalize.button({
...parts.autoplayTrigger.attrs,
type: "button",
"data-orientation": state.context.orientation,
"data-pressed": domQuery.dataAttr(isPlaying),
"aria-label": isPlaying ? translations.autoplayStop : translations.autoplayStart,
onClick(event) {
if (event.defaultPrevented) return;
send({ type: isPlaying ? "AUTOPLAY.PAUSE" : "AUTOPLAY.START" });
}
});
}
};
}
function getSnapAlign(ctx, props2) {
const { snapAlign = "start", index } = props2;
const perMove = ctx.slidesPerMove === "auto" ? Math.floor(ctx.slidesPerPage) : ctx.slidesPerMove;
const shouldSnap = (index + perMove) % perMove === 0;
return shouldSnap ? snapAlign : void 0;
}
var DEFAULT_SLIDES_PER_PAGE = 1;
var DEFAULT_SLIDES_PER_MOVE = "auto";
function machine(userContext) {

@@ -365,78 +302,111 @@ const ctx = utils.compact(userContext);

id: "carousel",
initial: "idle",
initial: ctx.autoplay ? "autoplay" : "idle",
context: {
index: 0,
dir: "ltr",
page: 0,
orientation: "horizontal",
align: "start",
snapType: "mandatory",
loop: false,
slidesPerView: 1,
slidesPerPage: DEFAULT_SLIDES_PER_PAGE,
slidesPerMove: DEFAULT_SLIDES_PER_MOVE,
spacing: "0px",
autoplay: false,
allowMouseDrag: false,
inViewThreshold: 0.6,
...ctx,
scrollSnaps: [],
scrollProgress: 0,
containerSize: 0,
slideRects: []
timeoutRef: core.ref({ current: void 0 }),
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",
...ctx.translations
},
pageSnapPoints: getPageSnapPoints(
ctx.slideCount,
ctx.slidesPerMove ?? DEFAULT_SLIDES_PER_MOVE,
ctx.slidesPerPage ?? DEFAULT_SLIDES_PER_PAGE
),
slidesInView: []
},
computed: {
isRtl: (ctx2) => ctx2.dir === "rtl",
isHorizontal: (ctx2) => ctx2.orientation === "horizontal",
canScrollNext: (ctx2) => ctx2.loop || ctx2.page < ctx2.pageSnapPoints.length - 1,
canScrollPrev: (ctx2) => ctx2.loop || ctx2.page > 0,
autoplayInterval: (ctx2) => utils.isObject(ctx2.autoplay) ? ctx2.autoplay.delay : 4e3
},
watch: {
index: ["setScrollSnaps"]
slidesPerPage: ["setSnapPoints"],
slidesPerMove: ["setSnapPoints"],
page: ["scrollToPage", "focusIndicatorEl"],
orientation: ["setSnapPoints", "scrollToPage"]
},
on: {
NEXT: {
actions: ["scrollToNext"]
"PAGE.NEXT": {
target: "idle",
actions: ["clearScrollEndTimer", "setNextPage"]
},
PREV: {
actions: ["scrollToPrev"]
"PAGE.PREV": {
target: "idle",
actions: ["clearScrollEndTimer", "setPrevPage"]
},
GOTO: {
actions: ["scrollTo"]
"PAGE.SET": {
target: "idle",
actions: ["clearScrollEndTimer", "setPage"]
},
MEASURE_DOM: {
actions: ["measureElements", "setScrollSnaps"]
"INDEX.SET": {
target: "idle",
actions: ["clearScrollEndTimer", "setMatchingPage"]
},
PLAY: "autoplay"
"SNAP.REFRESH": {
actions: ["setSnapPoints", "clampPage"]
}
},
activities: ["trackSlideMutation", "trackSlideIntersections", "trackSlideResize"],
entry: ["resetScrollPosition", "setSnapPoints", "setPage"],
exit: ["clearScrollEndTimer"],
states: {
idle: {
activities: ["trackScroll"],
on: {
POINTER_DOWN: "dragging"
"DRAGGING.START": {
target: "dragging",
actions: ["invokeDragStart"]
},
"AUTOPLAY.START": {
target: "autoplay",
actions: ["invokeAutoplayStart"]
}
}
},
dragging: {
activities: ["trackPointerMove"],
entry: ["disableScrollSnap"],
on: {
DRAGGING: {
actions: ["scrollSlides", "invokeDragging"]
},
"DRAGGING.END": {
target: "idle",
actions: ["endDragging", "invokeDraggingEnd"]
}
}
},
autoplay: {
activities: ["trackDocumentVisibility"],
activities: ["trackDocumentVisibility", "trackScroll"],
exit: ["invokeAutoplayEnd"],
every: {
2e3: ["scrollToNext"]
AUTOPLAY_INTERVAL: ["setNextPage", "invokeAutoplay"]
},
on: {
PAUSE: "idle"
"DRAGGING.START": {
target: "dragging",
actions: ["invokeDragStart"]
},
"AUTOPLAY.PAUSE": "idle"
}
},
dragging: {
on: {
POINTER_UP: "idle",
POINTER_MOVE: {
actions: ["setScrollSnaps"]
}
}
}
},
activities: ["trackContainerResize", "trackSlideMutation"],
entry: ["measureElements", "setScrollSnaps"],
computed: {
isRtl: (ctx2) => ctx2.dir === "rtl",
isHorizontal: (ctx2) => ctx2.orientation === "horizontal",
isVertical: (ctx2) => ctx2.orientation === "vertical",
canScrollNext: (ctx2) => ctx2.loop || ctx2.index < ctx2.scrollSnaps.length - 1,
canScrollPrev: (ctx2) => ctx2.loop || ctx2.index > 0,
startEdge(ctx2) {
if (ctx2.isVertical) return "top";
return ctx2.isRtl ? "right" : "left";
},
endEdge(ctx2) {
if (ctx2.isVertical) return "bottom";
return ctx2.isRtl ? "left" : "right";
},
translateValue: (ctx2) => {
const scrollSnap = ctx2.scrollSnaps[ctx2.index];
return ctx2.isHorizontal ? `translate3d(${scrollSnap}px, 0, 0)` : `translate3d(0, ${scrollSnap}px, 0)`;
}
}

@@ -447,28 +417,61 @@ },

trackSlideMutation(ctx2, _evt, { send }) {
const slideGroupEl = dom.getSlideGroupEl(ctx2);
if (!slideGroupEl) return;
const el = dom.getItemGroupEl(ctx2);
if (!el) return;
const win = dom.getWin(ctx2);
const observer = new win.MutationObserver(() => {
send({ type: "MEASURE_DOM", src: "mutation" });
send({ type: "SNAP.REFRESH", src: "slide.mutation" });
dom.syncTabIndex(ctx2);
});
observer.observe(slideGroupEl, { childList: true });
return () => {
observer.disconnect();
};
dom.syncTabIndex(ctx2);
observer.observe(el, { childList: true, subtree: true });
return () => observer.disconnect();
},
trackContainerResize(ctx2, _evt, { send }) {
const slideGroupEl = dom.getSlideGroupEl(ctx2);
if (!slideGroupEl) return;
trackSlideResize(ctx2, _evt, { send }) {
const el = dom.getItemGroupEl(ctx2);
if (!el) return;
const win = dom.getWin(ctx2);
const observer = new win.ResizeObserver((entries) => {
entries.forEach((entry) => {
if (entry.target === slideGroupEl) {
send({ type: "MEASURE_DOM", src: "resize" });
}
});
const observer = new win.ResizeObserver(() => {
send({ type: "SNAP.REFRESH", src: "slide.resize" });
});
observer.observe(slideGroupEl);
return () => {
observer.disconnect();
dom.getItemEls(ctx2).forEach((slide) => observer.observe(slide));
return () => observer.disconnect();
},
trackSlideIntersections(ctx2) {
const el = dom.getItemGroupEl(ctx2);
const win = dom.getWin(ctx2);
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 ? utils.add(acc, index) : utils.remove(acc, index);
}, ctx2.slidesInView);
ctx2.slidesInView = utils.uniq(slidesInView);
},
{
root: el,
threshold: ctx2.inViewThreshold
}
);
dom.getItemEls(ctx2).forEach((slide) => observer.observe(slide));
return () => observer.disconnect();
},
trackScroll(ctx2) {
const el = dom.getItemGroupEl(ctx2);
if (!el) return;
const onScrollEnd = () => {
if (ctx2.slidesInView.length === 0) return;
const scrollPosition = ctx2.isHorizontal ? el.scrollLeft : el.scrollTop;
const page = ctx2.pageSnapPoints.findIndex((point) => Math.abs(point - scrollPosition) < 1);
if (page === -1) return;
set.page(ctx2, page);
};
const onScroll = () => {
clearTimeout(ctx2.timeoutRef.current);
ctx2.timeoutRef.current = setTimeout(() => {
onScrollEnd?.();
}, 150);
};
return domEvent.addDomEvent(el, "scroll", onScroll, { passive: true });
},

@@ -478,36 +481,124 @@ trackDocumentVisibility(ctx2, _evt, { send }) {

const onVisibilityChange = () => {
if (doc.visibilityState !== "visible") {
send({ type: "PAUSE", src: "document-hidden" });
if (doc.visibilityState === "visible") return;
send({ type: "AUTOPLAY.PAUSE", src: "doc.hidden" });
};
return domEvent.addDomEvent(doc, "visibilitychange", onVisibilityChange);
},
trackPointerMove(ctx2, _evt, { send }) {
const doc = dom.getDoc(ctx2);
return domEvent.trackPointerMove(doc, {
onPointerMove({ event }) {
send({ type: "DRAGGING", left: -event.movementX, top: -event.movementY });
},
onPointerUp() {
send({ type: "DRAGGING.END" });
}
};
doc.addEventListener("visibilitychange", onVisibilityChange);
return () => {
doc.removeEventListener("visibilitychange", onVisibilityChange);
};
});
}
},
guards: {
loop: (ctx2) => ctx2.loop,
isLastSlide: (ctx2) => ctx2.index === ctx2.scrollSnaps.length - 1,
isFirstSlide: (ctx2) => ctx2.index === 0
},
actions: {
scrollToNext(ctx2) {
const index = utils.nextIndex(ctx2.scrollSnaps, ctx2.index);
set.index(ctx2, index);
resetScrollPosition(ctx2) {
const el = dom.getItemGroupEl(ctx2);
el.scrollTo(0, 0);
},
scrollToPrev(ctx2) {
const index = utils.prevIndex(ctx2.scrollSnaps, ctx2.index);
set.index(ctx2, index);
clearScrollEndTimer(ctx2) {
if (ctx2.timeoutRef.current == null) return;
clearTimeout(ctx2.timeoutRef.current);
ctx2.timeoutRef.current = void 0;
},
setScrollSnaps(ctx2) {
const { snapsAligned, scrollProgress } = getScrollSnaps(ctx2);
ctx2.scrollSnaps = snapsAligned;
ctx2.scrollProgress = scrollProgress;
scrollToPage(ctx2, evt) {
const behavior = evt.instant ? "instant" : "smooth";
const index = clamp(evt.index ?? ctx2.page, 0, ctx2.pageSnapPoints.length - 1);
const el = dom.getItemGroupEl(ctx2);
const axis = ctx2.isHorizontal ? "left" : "top";
el.scrollTo({ [axis]: ctx2.pageSnapPoints[index], behavior });
},
scrollTo(ctx2, evt) {
const index = Math.max(0, Math.min(evt.index, ctx2.scrollSnaps.length - 1));
set.index(ctx2, index);
setNextPage(ctx2) {
const page = utils.nextIndex(ctx2.pageSnapPoints, ctx2.page, { loop: ctx2.loop });
set.page(ctx2, page);
},
measureElements
setPrevPage(ctx2) {
const page = utils.prevIndex(ctx2.pageSnapPoints, ctx2.page, { loop: ctx2.loop });
set.page(ctx2, page);
},
setMatchingPage(ctx2, evt) {
const snapPoint = scrollSnap.findSnapPoint(
dom.getItemGroupEl(ctx2),
ctx2.isHorizontal ? "x" : "y",
(node) => node.dataset.index === evt.index.toString()
);
if (snapPoint == null) return;
const page = ctx2.pageSnapPoints.indexOf(snapPoint);
set.page(ctx2, page);
},
setPage(ctx2, evt) {
set.page(ctx2, evt.index ?? ctx2.page);
},
clampPage(ctx2) {
const index = clamp(ctx2.page, 0, ctx2.pageSnapPoints.length - 1);
set.page(ctx2, index);
},
setSnapPoints(ctx2) {
queueMicrotask(() => {
const el = dom.getItemGroupEl(ctx2);
const scrollSnapPoints = scrollSnap.getScrollSnapPositions(el);
ctx2.pageSnapPoints = ctx2.isHorizontal ? scrollSnapPoints.x : scrollSnapPoints.y;
});
},
disableScrollSnap(ctx2) {
const el = dom.getItemGroupEl(ctx2);
const styles = getComputedStyle(el);
el.dataset.scrollSnapType = styles.getPropertyValue("scroll-snap-type");
el.style.setProperty("scroll-snap-type", "none");
},
scrollSlides(ctx2, evt) {
const el = dom.getItemGroupEl(ctx2);
el.scrollBy({ left: evt.left, top: evt.top, behavior: "instant" });
},
endDragging(ctx2) {
const el = dom.getItemGroupEl(ctx2);
const startX = el.scrollLeft;
const startY = el.scrollTop;
const snapPositions = scrollSnap.getScrollSnapPositions(el);
const closestX = snapPositions.x.reduce((closest, curr) => {
return Math.abs(curr - startX) < Math.abs(closest - startX) ? curr : closest;
}, snapPositions.x[0]);
const closestY = snapPositions.y.reduce((closest, curr) => {
return Math.abs(curr - startY) < Math.abs(closest - startY) ? curr : closest;
}, snapPositions.y[0]);
domQuery.raf(() => {
el.scrollTo({ left: closestX, top: closestY, behavior: "smooth" });
const scrollSnapType = el.dataset.scrollSnapType;
if (scrollSnapType) {
el.style.removeProperty("scroll-snap-type");
delete el.dataset.scrollSnapType;
}
});
},
focusIndicatorEl(ctx2, evt) {
if (evt.src !== "indicator") return;
const el = dom.getActiveIndicatorEl(ctx2);
domQuery.raf(() => el.focus({ preventScroll: true }));
},
invokeDragStart(ctx2) {
ctx2.onDragStatusChange?.({ type: "dragging.start", isDragging: true, page: ctx2.page });
},
invokeDragging(ctx2) {
ctx2.onDragStatusChange?.({ type: "dragging", isDragging: true, page: ctx2.page });
},
invokeDraggingEnd(ctx2) {
ctx2.onDragStatusChange?.({ type: "dragging.end", isDragging: false, page: ctx2.page });
},
invokeAutoplay(ctx2) {
ctx2.onAutoplayStatusChange?.({ type: "autoplay", isPlaying: true, page: ctx2.page });
},
invokeAutoplayStart(ctx2) {
ctx2.onAutoplayStatusChange?.({ type: "autoplay.start", isPlaying: true, page: ctx2.page });
},
invokeAutoplayEnd(ctx2) {
ctx2.onAutoplayStatusChange?.({ type: "autoplay.stop", isPlaying: false, page: ctx2.page });
}
},
delays: {
AUTOPLAY_INTERVAL: (ctx2) => ctx2.autoplayInterval
}

@@ -517,23 +608,29 @@ }

}
var measureElements = (ctx) => {
const slideGroupEl = dom.getSlideGroupEl(ctx);
if (!slideGroupEl) return;
ctx.containerRect = core.ref(slideGroupEl.getBoundingClientRect());
ctx.containerSize = ctx.isHorizontal ? ctx.containerRect.width : ctx.containerRect.height;
ctx.slideRects = core.ref(dom.getSlideEls(ctx).map((slide) => slide.getBoundingClientRect()));
};
var invoke = {
change: (ctx) => {
ctx.onIndexChange?.({ index: ctx.index });
pageChange: (ctx) => {
ctx.onPageChange?.({
page: ctx.page,
pageSnapPoint: ctx.pageSnapPoints[ctx.page]
});
}
};
var set = {
index: (ctx, index) => {
if (utils.isEqual(ctx.index, index)) return;
ctx.index = index;
invoke.change(ctx);
page: (ctx, value) => {
const page = clamp(value, 0, ctx.pageSnapPoints.length - 1);
if (utils.isEqual(ctx.page, page)) return;
ctx.page = page;
invoke.pageChange(ctx);
}
};
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
function getPageSnapPoints(totalSlides, slidesPerMove, slidesPerPage) {
if (totalSlides == null) return [];
const snapPoints = [];
const perMove = slidesPerMove === "auto" ? Math.floor(slidesPerPage) : slidesPerMove;
for (let i = 0; i < totalSlides - 1; i += perMove) snapPoints.push(i);
return snapPoints;
}
var props = types.createProps()([
"align",
"dir",

@@ -543,8 +640,18 @@ "getRootNode",

"ids",
"index",
"loop",
"onIndexChange",
"page",
"onPageChange",
"orientation",
"slidesPerView",
"spacing"
"slideCount",
"slidesPerPage",
"slidesPerMove",
"spacing",
"padding",
"autoplay",
"allowMouseDrag",
"inViewThreshold",
"translations",
"snapType",
"onDragStatusChange",
"onAutoplayStatusChange"
]);

@@ -554,2 +661,4 @@ var splitProps = utils.createSplitProps(props);

var splitIndicatorProps = utils.createSplitProps(indicatorProps);
var itemProps = types.createProps()(["index", "snapAlign"]);
var splitItemProps = utils.createSplitProps(itemProps);

@@ -559,5 +668,7 @@ exports.anatomy = anatomy;

exports.indicatorProps = indicatorProps;
exports.itemProps = itemProps;
exports.machine = machine;
exports.props = props;
exports.splitIndicatorProps = splitIndicatorProps;
exports.splitItemProps = splitItemProps;
exports.splitProps = splitProps;
{
"name": "@zag-js/carousel",
"version": "0.78.3",
"version": "0.79.0",
"description": "Core logic for the carousel widget implemented as a state machine",

@@ -30,7 +30,9 @@ "keywords": [

"dependencies": {
"@zag-js/anatomy": "0.78.3",
"@zag-js/core": "0.78.3",
"@zag-js/types": "0.78.3",
"@zag-js/dom-query": "0.78.3",
"@zag-js/utils": "0.78.3"
"@zag-js/anatomy": "0.79.0",
"@zag-js/core": "0.79.0",
"@zag-js/types": "0.79.0",
"@zag-js/dom-event": "0.79.0",
"@zag-js/dom-query": "0.79.0",
"@zag-js/scroll-snap": "0.79.0",
"@zag-js/utils": "0.79.0"
},

@@ -37,0 +39,0 @@ "devDependencies": {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc