UNPKG

@uppy/utils

Version:

Shared utility functions for Uppy Core and plugins maintained by the Uppy team.

64 lines (63 loc) 2.86 kB
import { jsx as _jsx } from "preact/jsx-runtime"; import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; const STYLE_INNER = { position: 'relative', // Disabled for our use case: the wrapper elements around FileList already deal with overflow, // and this additional property would hide things that we want to show. // // overflow: 'hidden', width: '100%', minHeight: '100%', }; const STYLE_CONTENT = { position: 'absolute', top: 0, left: 0, // Because the `top` value gets set to some offset, this `height` being 100% would make the scrollbar // stretch far beyond the content. For our use case, the content div actually can get its height from // the elements inside it, so we don't need to specify a `height` property at all. // // height: '100%', width: '100%', overflow: 'visible', }; export default function VirtualList({ data, rowHeight, renderRow, overscanCount = 10, padding = 4, ...props }) { const scrollerRef = useRef(null); const [offset, setOffset] = useState(0); const [height, setHeight] = useState(0); useEffect(() => { function resize() { if (scrollerRef.current != null && height !== scrollerRef.current.offsetHeight) { setHeight(scrollerRef.current.offsetHeight); } } resize(); window.addEventListener('resize', resize); return () => { window.removeEventListener('resize', resize); }; }, [height]); const handleScroll = useCallback(() => { if (scrollerRef.current) setOffset(scrollerRef.current.scrollTop); }, []); // first visible row index let start = Math.floor(offset / rowHeight); // actual number of visible rows (without overscan) let visibleRowCount = Math.floor(height / rowHeight); // Overscan: render blocks of rows modulo an overscan row count // This dramatically reduces DOM writes during scrolling if (overscanCount) { start = Math.max(0, start - (start % overscanCount)); visibleRowCount += overscanCount; } const end = start + visibleRowCount + padding; // data slice currently in viewport plus overscan items const selection = data.slice(start, end); const styleInner = { ...STYLE_INNER, height: data.length * rowHeight }; const styleContent = { ...STYLE_CONTENT, top: start * rowHeight }; // The `role="presentation"` attributes ensure that these wrapper elements are not treated as list // items by accessibility and outline tools. return (_jsx("div", { onScroll: handleScroll, ref: scrollerRef, ...props, children: _jsx("div", { role: "presentation", style: styleInner, children: _jsx("div", { role: "presentation", style: styleContent, children: selection.map(renderRow) }) }) })); }