Private
Public Access
1
0
Files
power-apps-codeapps-blog-part2/node_modules/@fluentui/react-toast/lib/components/ToastContainer/useToastContainer.js

233 lines
9.6 KiB
JavaScript

'use client';
import * as React from 'react';
import { getIntrinsicElementProps, useMergedRefs, useEventCallback, useId, slot } from '@fluentui/react-utilities';
import { useFluent_unstable } from '@fluentui/react-shared-contexts';
import { Delete, Tab } from '@fluentui/keyboard-keys';
import { useFocusableGroup, useFocusFinders } from '@fluentui/react-tabster';
import { Timer } from '../Timer/Timer';
const intentPolitenessMap = {
success: 'assertive',
warning: 'assertive',
error: 'assertive',
info: 'polite'
};
/**
* Create the state required to render ToastContainer.
*
* The returned state can be modified with hooks such as useToastContainerStyles_unstable,
* before being passed to renderToastContainer_unstable.
*
* @param props - props from this instance of ToastContainer
* @param ref - reference to root HTMLElement of ToastContainer
*/ export const useToastContainer_unstable = (props, ref)=>{
const { visible, children, close: closeProp, remove, updateId, announce, data, timeout: timerTimeout, politeness: desiredPoliteness, intent = 'info', pauseOnHover, pauseOnWindowBlur, imperativeRef, tryRestoreFocus, content: _content, ...rest } = props;
const titleId = useId('toast-title');
const bodyId = useId('toast-body');
const toastRef = React.useRef(null);
const { targetDocument } = useFluent_unstable();
const [running, setRunning] = React.useState(false);
const imperativePauseRef = React.useRef(false);
const focusedToastBeforeClose = React.useRef(false);
const focusableGroupAttribute = useFocusableGroup({
tabBehavior: 'limited-trap-focus',
// Users should only use Tab to focus into the toast
// Escape is already reserved to dismiss all toasts
ignoreDefaultKeydown: {
Tab: true,
Escape: true,
Enter: true
}
});
const close = useEventCallback(()=>{
var _toastRef_current;
const activeElement = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.activeElement;
if (activeElement && ((_toastRef_current = toastRef.current) === null || _toastRef_current === void 0 ? void 0 : _toastRef_current.contains(activeElement))) {
focusedToastBeforeClose.current = true;
}
closeProp();
});
const onStatusChange = useEventCallback((status)=>{
var _props_onStatusChange;
return (_props_onStatusChange = props.onStatusChange) === null || _props_onStatusChange === void 0 ? void 0 : _props_onStatusChange.call(props, null, {
status,
...props
});
});
const pause = useEventCallback(()=>setRunning(false));
const play = useEventCallback(()=>{
var _toastRef_current;
if (imperativePauseRef.current) {
return;
}
var _targetDocument_activeElement;
const containsActive = !!((_toastRef_current = toastRef.current) === null || _toastRef_current === void 0 ? void 0 : _toastRef_current.contains((_targetDocument_activeElement = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.activeElement) !== null && _targetDocument_activeElement !== void 0 ? _targetDocument_activeElement : null));
if (timerTimeout < 0) {
setRunning(true);
return;
}
if (!containsActive) {
setRunning(true);
}
});
React.useImperativeHandle(imperativeRef, ()=>({
focus: ()=>{
if (!toastRef.current) {
return;
}
toastRef.current.focus();
},
play: ()=>{
imperativePauseRef.current = false;
play();
},
pause: ()=>{
imperativePauseRef.current = true;
pause();
}
}));
React.useEffect(()=>{
return ()=>onStatusChange('unmounted');
}, [
onStatusChange
]);
React.useEffect(()=>{
if (!targetDocument) {
return;
}
if (pauseOnWindowBlur) {
var _targetDocument_defaultView, _targetDocument_defaultView1;
(_targetDocument_defaultView = targetDocument.defaultView) === null || _targetDocument_defaultView === void 0 ? void 0 : _targetDocument_defaultView.addEventListener('focus', play);
(_targetDocument_defaultView1 = targetDocument.defaultView) === null || _targetDocument_defaultView1 === void 0 ? void 0 : _targetDocument_defaultView1.addEventListener('blur', pause);
return ()=>{
var _targetDocument_defaultView, _targetDocument_defaultView1;
(_targetDocument_defaultView = targetDocument.defaultView) === null || _targetDocument_defaultView === void 0 ? void 0 : _targetDocument_defaultView.removeEventListener('focus', play);
(_targetDocument_defaultView1 = targetDocument.defaultView) === null || _targetDocument_defaultView1 === void 0 ? void 0 : _targetDocument_defaultView1.removeEventListener('blur', pause);
};
}
}, [
targetDocument,
pause,
play,
pauseOnWindowBlur
]);
// Users never actually use ToastContainer as a JSX but imperatively through useToastContainerController
const userRootSlot = data.root;
const onMotionFinish = React.useCallback((_, { direction })=>{
if (direction === 'exit') {
remove();
}
if (direction === 'enter') {
// start toast once it's fully animated in
play();
onStatusChange('visible');
}
}, [
onStatusChange,
play,
remove
]);
const onMouseEnter = useEventCallback((e)=>{
var _userRootSlot_onMouseEnter;
pause();
userRootSlot === null || userRootSlot === void 0 ? void 0 : (_userRootSlot_onMouseEnter = userRootSlot.onMouseEnter) === null || _userRootSlot_onMouseEnter === void 0 ? void 0 : _userRootSlot_onMouseEnter.call(userRootSlot, e);
});
const onMouseLeave = useEventCallback((e)=>{
var _userRootSlot_onMouseEnter;
play();
userRootSlot === null || userRootSlot === void 0 ? void 0 : (_userRootSlot_onMouseEnter = userRootSlot.onMouseEnter) === null || _userRootSlot_onMouseEnter === void 0 ? void 0 : _userRootSlot_onMouseEnter.call(userRootSlot, e);
});
const { findFirstFocusable, findLastFocusable } = useFocusFinders();
const onKeyDown = useEventCallback((e)=>{
var _userRootSlot_onKeyDown;
if (e.key === Delete) {
e.preventDefault();
close();
}
if (e.key === Tab && e.currentTarget === e.target) {
e.preventDefault();
if (e.shiftKey) {
var _findLastFocusable;
(_findLastFocusable = findLastFocusable(e.currentTarget)) === null || _findLastFocusable === void 0 ? void 0 : _findLastFocusable.focus();
} else {
var _findFirstFocusable;
(_findFirstFocusable = findFirstFocusable(e.currentTarget)) === null || _findFirstFocusable === void 0 ? void 0 : _findFirstFocusable.focus();
}
}
userRootSlot === null || userRootSlot === void 0 ? void 0 : (_userRootSlot_onKeyDown = userRootSlot.onKeyDown) === null || _userRootSlot_onKeyDown === void 0 ? void 0 : _userRootSlot_onKeyDown.call(userRootSlot, e);
});
React.useEffect(()=>{
var _toastRef_current;
if (!visible) {
return;
}
const politeness = desiredPoliteness !== null && desiredPoliteness !== void 0 ? desiredPoliteness : intentPolitenessMap[intent];
var _toastRef_current_textContent;
announce((_toastRef_current_textContent = (_toastRef_current = toastRef.current) === null || _toastRef_current === void 0 ? void 0 : _toastRef_current.textContent) !== null && _toastRef_current_textContent !== void 0 ? _toastRef_current_textContent : '', {
politeness
});
}, [
announce,
desiredPoliteness,
toastRef,
visible,
updateId,
intent
]);
React.useEffect(()=>{
return ()=>{
if (focusedToastBeforeClose.current) {
focusedToastBeforeClose.current = false;
tryRestoreFocus();
}
};
}, [
tryRestoreFocus
]);
return {
components: {
timer: Timer,
root: 'div'
},
timer: slot.always({
onTimeout: close,
running,
timeout: timerTimeout !== null && timerTimeout !== void 0 ? timerTimeout : -1
}, {
elementType: Timer
}),
root: slot.always(getIntrinsicElementProps('div', {
// FIXME:
// `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`
// but since it would be a breaking change to fix it, we are casting ref to it's proper type
ref: useMergedRefs(ref, toastRef),
children,
tabIndex: 0,
role: 'listitem',
'aria-labelledby': titleId,
'aria-describedby': bodyId,
...rest,
...userRootSlot,
...focusableGroupAttribute,
onMouseEnter,
onMouseLeave,
onKeyDown
}), {
elementType: 'div'
}),
timerTimeout,
transitionTimeout: 0,
running,
visible,
remove,
close,
onTransitionEntering: ()=>{
/* no-op */ },
updateId,
nodeRef: toastRef,
intent,
titleId,
bodyId,
onMotionFinish
};
};