UNPKG

react-beautiful-dnd

Version:

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

107 lines (84 loc) 2.23 kB
// @flow import invariant from 'tiny-invariant'; import type { Announce } from '../../types'; import type { Announcer } from './announcer-types'; type State = {| el: ?HTMLElement, |} let count: number = 0; // https://allyjs.io/tutorials/hiding-elements.html // Element is visually hidden but is readable by screen readers const visuallyHidden: Object = { position: 'absolute', width: '1px', height: '1px', margin: '-1px', border: '0', padding: '0', overflow: 'hidden', clip: 'rect(0 0 0 0)', // for if 'clip' is ever removed 'clip-path': 'inset(100%)', }; export default (): Announcer => { const id: string = `react-beautiful-dnd-announcement-${count++}`; let state: State = { el: null, }; const setState = (newState: State) => { state = newState; }; const announce: Announce = (message: string): void => { const el: ?HTMLElement = state.el; if (!el) { console.error('Cannot announce to unmounted node'); return; } el.textContent = message; }; const mount = () => { if (state.el) { console.error('Announcer already mounted'); return; } const el: HTMLElement = document.createElement('div'); // identifier el.id = id; // Aria live region // will force itself to be read el.setAttribute('aria-live', 'assertive'); el.setAttribute('role', 'log'); // must read the whole thing every time el.setAttribute('aria-atomic', 'true'); // hide the element visually Object.assign(el.style, visuallyHidden); invariant(document.body, 'Cannot find the head to append a style to'); // add el tag to body document.body.appendChild(el); setState({ el, }); }; const unmount = () => { if (!state.el) { console.error('Will not unmount annoucer as it is already unmounted'); return; } const node: HTMLElement = state.el; setState({ el: null, }); if (!node.parentNode) { console.error('Cannot unmount style marshal as cannot find parent'); return; } node.parentNode.removeChild(node); }; const announcer: Announcer = { announce, id, mount, unmount, }; return announcer; };