UNPKG

@wordpress/compose

Version:
67 lines (61 loc) 1.72 kB
/** * WordPress dependencies */ import { TAB } from '@wordpress/keycodes'; import { focus } from '@wordpress/dom'; import { useCallback } from '@wordpress/element'; /** * In Dialogs/modals, the tabbing must be constrained to the content of * the wrapper element. This hook adds the behavior to the returned ref. * * @return {Object|Function} Element Ref. * * @example * ```js * import { useConstrainedTabbing } from '@wordpress/compose'; * * const ConstrainedTabbingExample = () => { * const constrainedTabbingRef = useConstrainedTabbing() * return ( * <div ref={ constrainedTabbingRef }> * <Button /> * <Button /> * </div> * ); * } * ``` */ function useConstrainedTabbing() { const ref = useCallback( ( node ) => { if ( ! node ) { return; } node.addEventListener( 'keydown', ( event ) => { if ( event.keyCode !== TAB ) { return; } const tabbables = focus.tabbable.find( node ); if ( ! tabbables.length ) { return; } const firstTabbable = tabbables[ 0 ]; const lastTabbable = tabbables[ tabbables.length - 1 ]; if ( event.shiftKey && event.target === firstTabbable ) { event.preventDefault(); lastTabbable.focus(); } else if ( ! event.shiftKey && event.target === lastTabbable ) { event.preventDefault(); firstTabbable.focus(); /* * When pressing Tab and none of the tabbables has focus, the keydown * event happens on the wrapper div: move focus on the first tabbable. */ } else if ( ! tabbables.includes( event.target ) ) { event.preventDefault(); firstTabbable.focus(); } } ); }, [] ); return ref; } export default useConstrainedTabbing;