Private
Public Access
1
0
Files

135 lines
5.8 KiB
JavaScript

'use client';
import * as React from 'react';
import { isHTMLElement, useMergedRefs, useControllableState, useEventCallback } from '@fluentui/react-utilities';
import { useAnnounce, useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
import { CAROUSEL_ITEM } from './constants';
import { useCarouselWalker_unstable } from './useCarouselWalker';
import { createCarouselStore } from './createCarouselStore';
// TODO: Migrate this into an external @fluentui/carousel component
// For now, we won't export this publicly, is only for internal TeachingPopover use until stabilized.
export function useCarousel_unstable(options) {
'use no memo';
const { announcement, onValueChange, onFinish } = options;
const { targetDocument } = useFluent();
const win = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.defaultView;
const { ref: carouselRef, walker: carouselWalker } = useCarouselWalker_unstable();
const [store] = React.useState(()=>createCarouselStore());
const [value, setValue] = useControllableState({
defaultState: options.defaultValue,
state: options.value,
initialState: null
});
const rootRef = React.useRef(null);
const { announce } = useAnnounce();
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line react-hooks/rules-of-hooks
React.useEffect(()=>{
if (value === null) {
// eslint-disable-next-line no-console
console.error('useCarousel: Carousel needs to have a `defaultValue` or `value` prop set. If you want to control the value, use the `value` prop.');
}
}, [
value
]);
}
React.useEffect(()=>{
var _rootRef_current;
const allItems = (_rootRef_current = rootRef.current) === null || _rootRef_current === void 0 ? void 0 : _rootRef_current.querySelectorAll(`[${CAROUSEL_ITEM}]`);
for(let i = 0; i < allItems.length; i++){
store.addValue(allItems.item(i).getAttribute(CAROUSEL_ITEM));
}
return ()=>{
store.clear();
};
}, [
store
]);
React.useEffect(()=>{
if (!win) {
return;
}
const config = {
attributes: true,
attributeFilter: [
CAROUSEL_ITEM
],
childList: true,
subtree: true
};
// Callback function to execute when mutations are observed
const callback = (mutationList)=>{
for (const mutation of mutationList){
for (const addedNode of Array.from(mutation.addedNodes)){
if (isHTMLElement(addedNode) && addedNode.hasAttribute(CAROUSEL_ITEM)) {
const newValue = addedNode.getAttribute(CAROUSEL_ITEM);
const newNode = carouselWalker.find(newValue);
if (!(newNode === null || newNode === void 0 ? void 0 : newNode.value)) {
return;
}
const previousNode = carouselWalker.prevPage(newNode === null || newNode === void 0 ? void 0 : newNode.value);
var _previousNode_value;
store.insertValue(newValue, (_previousNode_value = previousNode === null || previousNode === void 0 ? void 0 : previousNode.value) !== null && _previousNode_value !== void 0 ? _previousNode_value : null);
}
}
for (const removedNode of Array.from(mutation.removedNodes)){
if (isHTMLElement(removedNode) && (removedNode === null || removedNode === void 0 ? void 0 : removedNode.hasAttribute(CAROUSEL_ITEM))) {
const removedValue = removedNode.getAttribute(CAROUSEL_ITEM);
store.removeValue(removedValue);
}
}
}
};
// Create an observer instance linked to the callback function
const observer = new win.MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(rootRef.current, config);
// Later, you can stop observing
return ()=>{
observer.disconnect();
};
}, [
carouselWalker,
store,
win
]);
const updateSlide = useEventCallback((event, newValue)=>{
setValue(newValue);
onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(event, {
event,
type: 'click',
value: newValue
});
const announceText = announcement === null || announcement === void 0 ? void 0 : announcement(newValue);
if (announceText) {
announce(announceText, {
polite: true
});
}
});
const selectPageByDirection = useEventCallback((event, direction)=>{
const active = carouselWalker.active();
if (!(active === null || active === void 0 ? void 0 : active.value)) {
return;
}
const newPage = direction === 'prev' ? carouselWalker.prevPage(active.value) : carouselWalker.nextPage(active.value);
if (newPage) {
updateSlide(event, newPage === null || newPage === void 0 ? void 0 : newPage.value);
} else {
onFinish === null || onFinish === void 0 ? void 0 : onFinish(event, {
event,
type: 'click',
value: active === null || active === void 0 ? void 0 : active.value
});
}
});
return {
carouselRef: useMergedRefs(rootRef, carouselRef),
carousel: {
store,
value,
selectPageByDirection,
selectPageByValue: updateSlide
}
};
}