UNPKG

@shopify/polaris

Version:

Shopify’s product component library

62 lines (61 loc) 2.78 kB
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>); }