'use client'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { getIntrinsicElementProps, slot, useEventCallback, useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-utilities'; import * as React from 'react'; import { useEmblaCarousel } from '../useEmblaCarousel'; import { useAnnounce } from '@fluentui/react-shared-contexts'; /** * Create the state required to render Carousel. * * The returned state can be modified with hooks such as useCarouselStyles_unstable, * before being passed to renderCarousel_unstable. * * @param props - props from this instance of Carousel * @param ref - reference to root HTMLDivElement of Carousel */ export function useCarousel_unstable(props, ref) { 'use no memo'; const { align = 'center', circular = false, onActiveIndexChange, groupSize = 'auto', draggable = false, whitespace = false, announcement, motion = 'slide', autoplayInterval = 4000, appearance = 'flat' } = props; const { dir } = useFluent(); const { activeIndex, carouselApi, containerRef, viewportRef, subscribeForValues, enableAutoplay, resetAutoplay } = useEmblaCarousel({ align, direction: dir, loop: circular, slidesToScroll: groupSize, defaultActiveIndex: props.defaultActiveIndex, activeIndex: props.activeIndex, watchDrag: draggable, containScroll: whitespace ? false : 'keepSnaps', motion, onDragIndexChange: onActiveIndexChange, onAutoplayIndexChange: onActiveIndexChange, autoplayInterval }); const selectPageByElement = useEventCallback((event, element, jump)=>{ const foundIndex = carouselApi.scrollToElement(element, jump); onActiveIndexChange === null || onActiveIndexChange === void 0 ? void 0 : onActiveIndexChange(event, { event, type: 'focus', index: foundIndex }); return foundIndex; }); const selectPageByIndex = useEventCallback((event, index, jump)=>{ carouselApi.scrollToIndex(index, jump); onActiveIndexChange === null || onActiveIndexChange === void 0 ? void 0 : onActiveIndexChange(event, { event, type: 'click', index }); }); const selectPageByDirection = useEventCallback((event, direction)=>{ const nextPageIndex = carouselApi.scrollInDirection(direction); onActiveIndexChange === null || onActiveIndexChange === void 0 ? void 0 : onActiveIndexChange(event, { event, type: 'click', index: nextPageIndex }); return nextPageIndex; }); const mergedContainerRef = useMergedRefs(ref, containerRef); // Announce carousel updates const announcementTextRef = React.useRef(''); const totalNavLength = React.useRef(0); const navGroupRef = React.useRef([]); const { announce } = useAnnounce(); const updateAnnouncement = useEventCallback(()=>{ if (totalNavLength.current <= 0 || !announcement) { // Ignore announcements until slides discovered return; } const announcementText = announcement(activeIndex, totalNavLength.current, navGroupRef.current); if (announcementText !== announcementTextRef.current) { announcementTextRef.current = announcementText; announce(announcementText, { polite: true }); } }); useIsomorphicLayoutEffect(()=>{ // Subscribe to any non-index carousel state changes return subscribeForValues((data)=>{ if (totalNavLength.current <= 0 && data.navItemsCount > 0 && announcement) { const announcementText = announcement(data.activeIndex, data.navItemsCount, data.groupIndexList); // Initialize our string to prevent updateAnnouncement from reading an initial load announcementTextRef.current = announcementText; } totalNavLength.current = data.navItemsCount; navGroupRef.current = data.groupIndexList; updateAnnouncement(); }); }, [ subscribeForValues, updateAnnouncement, announcement ]); useIsomorphicLayoutEffect(()=>{ updateAnnouncement(); }, [ activeIndex, updateAnnouncement ]); return { components: { root: 'div' }, root: slot.always(getIntrinsicElementProps('div', { ref: mergedContainerRef, role: 'region', ...props }), { elementType: 'div' }), activeIndex, appearance, circular, containerRef: mergedContainerRef, viewportRef, selectPageByElement, selectPageByDirection, selectPageByIndex, subscribeForValues, enableAutoplay, resetAutoplay }; }