@shopify/polaris
Version:
Shopify’s product component library
62 lines (61 loc) • 2.78 kB
JavaScript
import React, { useState, useRef } from 'react';
import { focusFirstFocusableNode } from '@shopify/javascript-utilities/focus';
import { Key } from '../../types';
import { useComponentDidMount } from '../../utilities/use-component-did-mount';
import { EventListener } from '../EventListener';
import { KeypressListener, KeyEvent } from '../KeypressListener';
import { Focus } from '../Focus';
import { findFirstKeyboardFocusableNode, focusFirstKeyboardFocusableNode, findLastKeyboardFocusableNode, focusLastKeyboardFocusableNode, } from '../../utilities/focus';
export function TrapFocus({ trapping = true, children }) {
const [shouldFocusSelf, setFocusSelf] = useState(undefined);
const focusTrapWrapper = useRef(null);
const handleTrappingChange = () => {
if (focusTrapWrapper.current &&
focusTrapWrapper.current.contains(document.activeElement)) {
return false;
}
return trapping;
};
useComponentDidMount(() => setFocusSelf(handleTrappingChange()));
const shouldDisableFirstElementFocus = () => {
if (shouldFocusSelf === undefined) {
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 (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={KeyEvent.KeyDown} handler={handleTab}/>
{children}
</div>
</Focus>);
}