@shopify/polaris
Version:
Shopify’s product component library
62 lines (61 loc) • 2.82 kB
JavaScript
import React, { useState, useRef, useEffect } from 'react';
import { focusFirstFocusableNode } from '@shopify/javascript-utilities/focus';
import { Key } from '../../types';
import { EventListener } from '../EventListener';
import { KeypressListener } from '../KeypressListener';
import { Focus } from '../Focus';
import { findFirstKeyboardFocusableNode, focusFirstKeyboardFocusableNode, findLastKeyboardFocusableNode, focusLastKeyboardFocusableNode, } from '../../utilities/focus';
import { useFocusManager } from '../../utilities/focus-manager';
export function TrapFocus({ trapping = true, children }) {
const [shouldFocusSelf, setFocusSelf] = useState(undefined);
const { canSafelyFocus } = useFocusManager();
const focusTrapWrapper = useRef(null);
useEffect(() => {
setFocusSelf(!(canSafelyFocus &&
focusTrapWrapper.current &&
focusTrapWrapper.current.contains(document.activeElement)));
}, [canSafelyFocus]);
const shouldDisableFirstElementFocus = () => {
if (shouldFocusSelf === undefined || !canSafelyFocus) {
return true;
}
return shouldFocusSelf ? !trapping : !shouldFocusSelf;
};
const handleFocusIn = (event) => {
const containerContentsHaveFocus = focusTrapWrapper.current &&
focusTrapWrapper.current.contains(document.activeElement);
if (trapping === false ||
!focusTrapWrapper.current ||
containerContentsHaveFocus) {
return;
}
if (canSafelyFocus &&
event.target instanceof HTMLElement &&
focusTrapWrapper.current !== event.target &&
!focusTrapWrapper.current.contains(event.target)) {
focusFirstFocusableNode(focusTrapWrapper.current);
}
};
const handleTab = (event) => {
if (trapping === false || !focusTrapWrapper.current) {
return;
}
const firstFocusableNode = findFirstKeyboardFocusableNode(focusTrapWrapper.current);
const lastFocusableNode = findLastKeyboardFocusableNode(focusTrapWrapper.current);
if (event.target === lastFocusableNode && !event.shiftKey) {
event.preventDefault();
focusFirstKeyboardFocusableNode(focusTrapWrapper.current);
}
if (event.target === firstFocusableNode && event.shiftKey) {
event.preventDefault();
focusLastKeyboardFocusableNode(focusTrapWrapper.current);
}
};
return (<Focus disabled={shouldDisableFirstElementFocus()} root={focusTrapWrapper.current}>
<div ref={focusTrapWrapper}>
<EventListener event="focusin" handler={handleFocusIn}/>
<KeypressListener keyCode={Key.Tab} keyEvent="keydown" handler={handleTab}/>
{children}
</div>
</Focus>);
}