UNPKG

react-beautiful-dnd

Version:

Beautiful, accessible drag and drop for lists with React.js

89 lines (70 loc) 2.19 kB
// @flow import getDragHandleRef from './get-drag-handle-ref'; import type { DraggableId } from '../../../types'; type FocusRetainer = {| retain: (draggableId: DraggableId) => void, tryRestoreFocus: (draggableId: DraggableId, draggableRef: HTMLElement) => void, |} // our shared state let retainingFocusFor: ?DraggableId = null; // If we focus on const clearRetentionOnFocusChange = (() => { let isBound: boolean = false; const bind = () => { if (isBound) { return; } isBound = true; // Using capture: true as focus events do not bubble // Additionally doing this prevents us from intercepting the initial // focus event as it does not bubble up to this listener // eslint-disable-next-line no-use-before-define window.addEventListener('focus', onWindowFocusChange, { capture: true }); }; const unbind = () => { if (!isBound) { return; } isBound = false; // eslint-disable-next-line no-use-before-define window.removeEventListener('focus', onWindowFocusChange, { capture: true }); }; // focusin will fire after the focus event fires on the element const onWindowFocusChange = () => { // unbinding self after single use unbind(); retainingFocusFor = null; }; const result = () => bind(); result.cancel = () => unbind(); return result; })(); const retain = (id: DraggableId) => { retainingFocusFor = id; clearRetentionOnFocusChange(); }; const tryRestoreFocus = (id: DraggableId, draggableRef: HTMLElement) => { // Not needing to retain focus if (!retainingFocusFor) { return; } // Not needing to retain focus for this draggable if (id !== retainingFocusFor) { return; } // We are about to force focus onto a drag handle retainingFocusFor = null; // no need to clear it - we are already clearing it clearRetentionOnFocusChange.cancel(); const dragHandleRef: ?HTMLElement = getDragHandleRef(draggableRef); if (!dragHandleRef) { console.warn('Could not find drag handle in the DOM to focus on it'); return; } dragHandleRef.focus(); }; const retainer: FocusRetainer = { retain, tryRestoreFocus, }; export default retainer;