Mirror: React hooks for accessible, common web interactions. UI super powers without the UI.
at v0.1.1 3.1 kB view raw
1const resizeOptions: ResizeObserverOptions = { box: 'border-box' }; 2const mutationObservers: Map<HTMLElement, MutationObserver> = new Map(); 3const resizeListeners: Map<HTMLElement, Array<() => void>> = new Map(); 4 5const resizeObserver = 6 typeof ResizeObserver !== 'undefined' 7 ? new ResizeObserver(entries => { 8 const parents = new Set<Element>(); 9 for (let i = 0; i < entries.length; i++) { 10 const parent = entries[i].target.parentElement; 11 if (parent && !parents.has(parent)) { 12 parents.add(parent); 13 const listeners = resizeListeners.get(parent) || []; 14 for (let i = 0; i < listeners.length; i++) listeners[i](); 15 } 16 } 17 }) 18 : undefined; 19 20export function observeScrollArea( 21 element: HTMLElement, 22 onAreaChange: (width: number, height: number) => void 23): () => void { 24 const listeners = resizeListeners.get(element) || []; 25 const isFirstListener = !listeners.length; 26 resizeListeners.set(element, listeners); 27 28 let previousScrollHeight: null | number = null; 29 let hasUnmounted = false; 30 const onResize = () => { 31 const scrollHeight = element.scrollHeight || 0; 32 if (!hasUnmounted && scrollHeight !== previousScrollHeight) { 33 onAreaChange(element.scrollWidth, element.scrollHeight); 34 previousScrollHeight = scrollHeight; 35 } 36 }; 37 38 listeners.push(onResize); 39 40 if (isFirstListener) { 41 const mutationObserver = new MutationObserver(entries => { 42 for (let i = 0; i < entries.length; i++) { 43 const entry = entries[i]; 44 for (let j = 0; j < entry.addedNodes.length; j++) { 45 const node = entry.addedNodes[j]; 46 if (node.nodeType === Node.ELEMENT_NODE) { 47 resizeObserver!.observe(node as Element, resizeOptions); 48 } 49 } 50 51 for (let j = 0; j < entry.removedNodes.length; j++) { 52 const node = entry.removedNodes[j]; 53 if (node.nodeType === Node.ELEMENT_NODE) { 54 resizeObserver!.unobserve(node as Element); 55 } 56 } 57 } 58 }); 59 60 requestAnimationFrame(() => { 61 const childNodes = element.childNodes; 62 for (let i = 0; i < childNodes.length; i++) 63 if (childNodes[i].nodeType === Node.ELEMENT_NODE) 64 resizeObserver!.observe(childNodes[i] as Element, resizeOptions); 65 mutationObserver.observe(element, { childList: true }); 66 }); 67 68 mutationObservers.set(element, mutationObserver); 69 } 70 71 return () => { 72 const listeners = resizeListeners.get(element) || []; 73 listeners.splice(listeners.indexOf(onResize), 1); 74 hasUnmounted = true; 75 76 if (!listeners.length) { 77 const mutationObserver = mutationObservers.get(element); 78 if (mutationObserver) mutationObserver.disconnect(); 79 80 const childNodes = element.childNodes; 81 for (let i = 0; i < childNodes.length; i++) 82 if (childNodes[i].nodeType === Node.ELEMENT_NODE) 83 resizeObserver!.unobserve(childNodes[i] as Element); 84 85 resizeListeners.delete(element); 86 mutationObservers.delete(element); 87 } 88 }; 89}