( props: AriaToastRegionProps, state: ToastState<T>, ref: RefObject<HTMLElement | null> )
| 44 | * application. |
| 45 | */ |
| 46 | export function useToastRegion<T>( |
| 47 | props: AriaToastRegionProps, |
| 48 | state: ToastState<T>, |
| 49 | ref: RefObject<HTMLElement | null> |
| 50 | ): ToastRegionAria { |
| 51 | let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/toast'); |
| 52 | let {landmarkProps} = useLandmark( |
| 53 | { |
| 54 | role: 'region', |
| 55 | 'aria-label': |
| 56 | props['aria-label'] || |
| 57 | stringFormatter.format('notifications', {count: state.visibleToasts.length}) |
| 58 | }, |
| 59 | ref |
| 60 | ); |
| 61 | |
| 62 | let isHovered = useRef(false); |
| 63 | let isFocused = useRef(false); |
| 64 | let updateTimers = useCallback(() => { |
| 65 | if (isHovered.current || isFocused.current) { |
| 66 | state.pauseAll(); |
| 67 | } else { |
| 68 | state.resumeAll(); |
| 69 | } |
| 70 | }, [state]); |
| 71 | |
| 72 | let {hoverProps} = useHover({ |
| 73 | onHoverStart: () => { |
| 74 | isHovered.current = true; |
| 75 | updateTimers(); |
| 76 | }, |
| 77 | onHoverEnd: () => { |
| 78 | isHovered.current = false; |
| 79 | updateTimers(); |
| 80 | } |
| 81 | }); |
| 82 | |
| 83 | // Manage focus within the toast region. |
| 84 | // If a focused containing toast is removed, move focus to the next toast, or the previous toast if there is no next toast. |
| 85 | let toasts = useRef<FocusableElement[]>([]); |
| 86 | let prevVisibleToasts = useRef(state.visibleToasts); |
| 87 | let focusedToast = useRef<number | null>(null); |
| 88 | useLayoutEffect(() => { |
| 89 | // If no toast has focus, then don't do anything. |
| 90 | if (focusedToast.current === -1 || state.visibleToasts.length === 0 || !ref.current) { |
| 91 | toasts.current = []; |
| 92 | prevVisibleToasts.current = state.visibleToasts; |
| 93 | return; |
| 94 | } |
| 95 | toasts.current = [ |
| 96 | ...ref.current.querySelectorAll('[role="alertdialog"]') |
| 97 | ] as FocusableElement[]; |
| 98 | // If the visible toasts haven't changed, we don't need to do anything. |
| 99 | if ( |
| 100 | prevVisibleToasts.current.length === state.visibleToasts.length && |
| 101 | state.visibleToasts.every((t, i) => t.key === prevVisibleToasts.current[i].key) |
| 102 | ) { |
| 103 | prevVisibleToasts.current = state.visibleToasts; |
no test coverage detected