Mirror: React hooks for accessible, common web interactions. UI super powers without the UI.
at v0.1.1 1.8 kB view raw
1import { contains } from './element'; 2 3export interface RestoreSelection { 4 element: HTMLElement; 5 restore(): void; 6} 7 8const hasSelection = (node: HTMLElement): node is HTMLInputElement => 9 typeof (node as HTMLInputElement).selectionStart === 'number' && 10 typeof (node as HTMLInputElement).selectionEnd === 'number'; 11 12/** Snapshots the current focus or selection target, optinally using a ref if it's passed. */ 13export const snapshotSelection = ( 14 node?: HTMLElement | null 15): RestoreSelection | null => { 16 const target = document.activeElement as HTMLElement | null; 17 const element = node && target && node !== target ? node : target; 18 if (!element || !target) { 19 return null; 20 } else if (hasSelection(element)) { 21 const { selectionStart, selectionEnd, selectionDirection } = element; 22 return { 23 element, 24 restore() { 25 element.focus(); 26 element.setSelectionRange( 27 selectionStart, 28 selectionEnd, 29 selectionDirection || undefined 30 ); 31 }, 32 }; 33 } 34 35 let range: Range | undefined; 36 37 const selection = window.getSelection(); 38 if (selection && selection.rangeCount) { 39 const _range = selection.getRangeAt(0); 40 if (_range.startContainer && contains(target, _range.startContainer)) { 41 range = _range; 42 } 43 } 44 45 return { 46 element, 47 restore() { 48 element.focus(); 49 const selection = window.getSelection(); 50 if (range && selection) { 51 selection.removeAllRanges(); 52 selection.addRange(range); 53 } 54 }, 55 }; 56}; 57 58/** Restores a given snapshot of a selection, falling back to a simple focus. */ 59export const restoreSelection = (selection: RestoreSelection | null) => { 60 if (selection && selection.element.parentNode) { 61 selection.restore(); 62 } 63};