UNPKG

@yamada-ui/react

Version:

React UI components of the Yamada, by the Yamada, for the Yamada built with React and Emotion

300 lines (296 loc) • 8.6 kB
"use client"; import { createContext as createContext$1 } from "../../utils/context.js"; import { runKeyAction, useIds } from "../../utils/dom.js"; import { useUpdateEffect } from "../../utils/effect.js"; import { assignRef, mergeRefs } from "../../utils/ref.js"; import { utils_exports } from "../../utils/index.js"; import { useControllableState } from "../../hooks/use-controllable-state/index.js"; import { useI18n } from "../../providers/i18n-provider/i18n-provider.js"; import { useBoolean } from "../../hooks/use-boolean/index.js"; import { useCallback, useEffect, useRef, useState } from "react"; import useEmblaCarousel from "embla-carousel-react"; //#region src/components/carousel/use-carousel.ts const [CarouselContext, useCarouselContext] = createContext$1({ name: "CarouselContext" }); const useCarousel = ({ id, align = "center", autoplay = false, containScroll = false, controlRef, defaultIndex = 0, delay = 4e3, dragFree = false, draggable = true, duration = 25, index: indexProp, inViewThreshold = 0, loop = true, orientation = "horizontal", plugins = [], skipSnaps = false, slidesToScroll = 1, stopMouseEnterAutoplay = true, watchDrag = draggable, watchResize: watchResizeProp = true, watchSlides = true, onChange, onScrollProgress,...rest } = {}) => { const { t } = useI18n("carousel"); const [index, setIndex] = useControllableState({ defaultValue: defaultIndex, value: indexProp, onChange }); const [rootId, listId] = useIds(); const [hover, { off: onMouseLeave, on: onMouseEnter }] = useBoolean(); const timeoutId = useRef(null); const indicatorMapRef = useRef(/* @__PURE__ */ new Map()); const listRef = useRef(null); const horizontal = orientation === "horizontal"; const axis = horizontal ? "x" : "y"; const [snapCount, setSnapCount] = useState(0); const [total, setTotal] = useState(0); const watchResize = useCallback((methods, entries) => { const result = (0, utils_exports.isFunction)(watchResizeProp) ? watchResizeProp(methods, entries) : true; const snapCount$1 = methods.scrollSnapList().length; const total$1 = methods.slideNodes().length; setSnapCount(snapCount$1); setTotal(total$1); return result; }, [watchResizeProp]); const [carouselRef, carousel] = useEmblaCarousel({ align, axis, container: listRef.current, containScroll, dragFree, duration, inViewThreshold, loop, skipSnaps, slidesToScroll, startIndex: defaultIndex, watchDrag, watchResize, watchSlides }, plugins); id ??= rootId; const onInit = useCallback((methods) => { const snapCount$1 = methods.scrollSnapList().length; const total$1 = methods.slideNodes().length; setSnapCount(snapCount$1); setTotal(total$1); }, []); const onScroll = useCallback(() => { if (!carousel) return; const progress = Math.round(Math.max(0, Math.min(1, carousel.scrollProgress())) * 100); onScrollProgress?.(progress); }, [carousel, onScrollProgress]); const onSelect = useCallback(() => { if (!carousel) return; setIndex(carousel.selectedScrollSnap()); }, [carousel, setIndex]); const onFocusIndicator = useCallback((index$1) => { indicatorMapRef.current.get(index$1)?.focus(); carousel?.scrollTo(index$1); }, [carousel]); const onKeyDown = useCallback((index$1) => (ev) => { const lastIndex = snapCount - 1; runKeyAction(ev, { ArrowDown: () => { if (horizontal) return; index$1 = index$1 === lastIndex ? 0 : index$1 + 1; onFocusIndicator(index$1); }, ArrowLeft: () => { if (!horizontal) return; index$1 = index$1 === 0 ? lastIndex : index$1 - 1; onFocusIndicator(index$1); }, ArrowRight: () => { if (!horizontal) return; index$1 = index$1 === lastIndex ? 0 : index$1 + 1; onFocusIndicator(index$1); }, ArrowUp: () => { if (horizontal) return; index$1 = index$1 === 0 ? lastIndex : index$1 - 1; onFocusIndicator(index$1); }, End: () => { onFocusIndicator(lastIndex); }, Home: () => { onFocusIndicator(0); } }); }, [ snapCount, horizontal, onFocusIndicator ]); assignRef(controlRef, carousel); useEffect(() => { if (carousel) { carousel.on("reInit", onInit); carousel.on("select", onSelect); carousel.on("scroll", onScroll); onScroll(); return () => { carousel.off("reInit", onInit); carousel.off("select", onSelect); carousel.off("scroll", onScroll); }; } }, [ carousel, onInit, onScroll, onSelect ]); useEffect(() => { const stop = hover && stopMouseEnterAutoplay; const last = !carousel?.canScrollNext(); if (carousel && autoplay && !stop && !last) timeoutId.current = setInterval(() => { carousel.scrollNext(); }, delay); else { if (timeoutId.current) clearInterval(timeoutId.current); timeoutId.current = null; } return () => { if (timeoutId.current) clearInterval(timeoutId.current); }; }, [ autoplay, carousel, delay, hover, stopMouseEnterAutoplay ]); useUpdateEffect(() => { if (!carousel) return; if (indexProp === void 0) return; carousel.scrollTo(indexProp); }, [indexProp]); useUpdateEffect(() => { if (!carousel) return; carousel.reInit(); }, [ carousel, total, align, axis, containScroll, dragFree, duration, inViewThreshold, loop, skipSnaps, slidesToScroll ]); const getRootProps = useCallback(({ ref,...props } = {}) => ({ id, "aria-roledescription": "carousel", "data-orientation": orientation, ...rest, ...props, ref: mergeRefs(ref, rest.ref, carouselRef), onMouseEnter: (0, utils_exports.handlerAll)(props.onMouseEnter, onMouseEnter), onMouseLeave: (0, utils_exports.handlerAll)(props.onMouseLeave, onMouseLeave) }), [ id, onMouseEnter, onMouseLeave, rest, carouselRef, orientation ]); const getListProps = useCallback(({ ref,...props } = {}) => ({ id: listId, "aria-live": autoplay ? "off" : "polite", "data-orientation": orientation, ...props, ref: mergeRefs(ref, listRef) }), [ autoplay, listId, orientation ]); const getItemProps = useCallback(({ index: indexProp$1,...props }) => { const page = indexProp$1 + 1; const selected = index === indexProp$1; return { id: `${listId}-${indexProp$1}`, "aria-label": t("{page} of {total}", { page, total }), "aria-roledescription": "slide", "data-index": indexProp$1.toString(), "data-orientation": orientation, "data-selected": (0, utils_exports.dataAttr)(selected), role: "tabpanel", ...props }; }, [ index, listId, total, orientation, t ]); const getPrevTriggerProps = useCallback((props = {}) => ({ "aria-controls": listId, "aria-label": t("Go to previous slide"), "data-orientation": orientation, disabled: !carousel?.canScrollPrev(), ...props, onClick: (0, utils_exports.handlerAll)(props.onClick, () => carousel?.scrollPrev()) }), [ carousel, listId, orientation, t ]); const getNextTriggerProps = useCallback((props = {}) => ({ "aria-controls": listId, "aria-label": t("Go to next slide"), "data-orientation": orientation, disabled: !carousel?.canScrollNext(), ...props, onClick: (0, utils_exports.handlerAll)(props.onClick, () => carousel?.scrollNext()) }), [ carousel, listId, orientation, t ]); const getIndicatorsProps = useCallback((props = {}) => ({ "aria-label": t("Slides"), "aria-orientation": orientation, role: "tablist", ...props }), [orientation, t]); return { carousel, index, setIndex, snapCount, total, getIndicatorProps: useCallback(({ ref, index: indexProp$1,...props }) => { const page = indexProp$1 + 1; const selected = index === indexProp$1; return { type: "button", "aria-controls": `${listId}-${indexProp$1}`, "aria-label": t("Go to {page} slide", { page }), "aria-selected": selected, "data-index": indexProp$1.toString(), "data-orientation": orientation, "data-selected": (0, utils_exports.dataAttr)(selected), role: "tab", tabIndex: selected ? 0 : -1, ...props, ref: mergeRefs(ref, (node) => { indicatorMapRef.current.set(indexProp$1, node); }), onClick: (0, utils_exports.handlerAll)(props.onClick, () => carousel?.scrollTo(indexProp$1)), onKeyDown: (0, utils_exports.handlerAll)(props.onKeyDown, onKeyDown(indexProp$1)) }; }, [ index, listId, t, orientation, onKeyDown, carousel ]), getIndicatorsProps, getItemProps, getListProps, getNextTriggerProps, getPrevTriggerProps, getRootProps }; }; //#endregion export { CarouselContext, useCarousel, useCarouselContext }; //# sourceMappingURL=use-carousel.js.map