UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

530 lines (448 loc) • 15.7 kB
/** * Name: react-virtual * Version: 2.10.4 * License: MIT * Private: false * Description: Hooks for virtualizing scrollable elements in React * Repository: undefined * Homepage: https://github.com/tannerlinsley/react-virtual#readme * Author: tannerlinsley * License Copyright: * === * * MIT License * * Copyright (c) 2019 Tanner Linsley * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import React from 'react'; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var props = ['bottom', 'height', 'left', 'right', 'top', 'width']; var rectChanged = function rectChanged(a, b) { if (a === void 0) { a = {}; } if (b === void 0) { b = {}; } return props.some(function (prop) { return a[prop] !== b[prop]; }); }; var observedNodes = /*#__PURE__*/new Map(); var rafId; var run = function run() { var changedStates = []; observedNodes.forEach(function (state, node) { var newRect = node.getBoundingClientRect(); if (rectChanged(newRect, state.rect)) { state.rect = newRect; changedStates.push(state); } }); changedStates.forEach(function (state) { state.callbacks.forEach(function (cb) { return cb(state.rect); }); }); rafId = window.requestAnimationFrame(run); }; function observeRect(node, cb) { return { observe: function observe() { var wasEmpty = observedNodes.size === 0; if (observedNodes.has(node)) { observedNodes.get(node).callbacks.push(cb); } else { observedNodes.set(node, { rect: undefined, hasRectChanged: false, callbacks: [cb] }); } if (wasEmpty) run(); }, unobserve: function unobserve() { var state = observedNodes.get(node); if (state) { // Remove the callback var index = state.callbacks.indexOf(cb); if (index >= 0) state.callbacks.splice(index, 1); // Remove the node reference if (!state.callbacks.length) observedNodes["delete"](node); // Stop the loop if (!observedNodes.size) cancelAnimationFrame(rafId); } } }; } var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect; function useRect(nodeRef, initialRect) { if (initialRect === void 0) { initialRect = { width: 0, height: 0 }; } var _React$useState = React.useState(nodeRef.current), element = _React$useState[0], setElement = _React$useState[1]; var _React$useReducer = React.useReducer(rectReducer, initialRect), rect = _React$useReducer[0], dispatch = _React$useReducer[1]; var initialRectSet = React.useRef(false); useIsomorphicLayoutEffect(function () { if (nodeRef.current !== element) { setElement(nodeRef.current); } }); useIsomorphicLayoutEffect(function () { if (element && !initialRectSet.current) { initialRectSet.current = true; var _rect = element.getBoundingClientRect(); dispatch({ rect: _rect }); } }, [element]); React.useEffect(function () { if (!element) { return; } var observer = observeRect(element, function (rect) { dispatch({ rect: rect }); }); observer.observe(); return function () { observer.unobserve(); }; }, [element]); return rect; } function rectReducer(state, action) { var rect = action.rect; if (state.height !== rect.height || state.width !== rect.width) { return rect; } return state; } var defaultEstimateSize = function defaultEstimateSize() { return 50; }; var defaultKeyExtractor = function defaultKeyExtractor(index) { return index; }; var defaultMeasureSize = function defaultMeasureSize(el, horizontal) { var key = horizontal ? 'offsetWidth' : 'offsetHeight'; return el[key]; }; var defaultRangeExtractor = function defaultRangeExtractor(range) { var start = Math.max(range.start - range.overscan, 0); var end = Math.min(range.end + range.overscan, range.size - 1); var arr = []; for (var i = start; i <= end; i++) { arr.push(i); } return arr; }; function useVirtual(_ref) { var _measurements; var _ref$size = _ref.size, size = _ref$size === void 0 ? 0 : _ref$size, _ref$estimateSize = _ref.estimateSize, estimateSize = _ref$estimateSize === void 0 ? defaultEstimateSize : _ref$estimateSize, _ref$overscan = _ref.overscan, overscan = _ref$overscan === void 0 ? 1 : _ref$overscan, _ref$paddingStart = _ref.paddingStart, paddingStart = _ref$paddingStart === void 0 ? 0 : _ref$paddingStart, _ref$paddingEnd = _ref.paddingEnd, paddingEnd = _ref$paddingEnd === void 0 ? 0 : _ref$paddingEnd, parentRef = _ref.parentRef, horizontal = _ref.horizontal, scrollToFn = _ref.scrollToFn, useObserver = _ref.useObserver, initialRect = _ref.initialRect, onScrollElement = _ref.onScrollElement, scrollOffsetFn = _ref.scrollOffsetFn, _ref$keyExtractor = _ref.keyExtractor, keyExtractor = _ref$keyExtractor === void 0 ? defaultKeyExtractor : _ref$keyExtractor, _ref$measureSize = _ref.measureSize, measureSize = _ref$measureSize === void 0 ? defaultMeasureSize : _ref$measureSize, _ref$rangeExtractor = _ref.rangeExtractor, rangeExtractor = _ref$rangeExtractor === void 0 ? defaultRangeExtractor : _ref$rangeExtractor; var sizeKey = horizontal ? 'width' : 'height'; var scrollKey = horizontal ? 'scrollLeft' : 'scrollTop'; var latestRef = React.useRef({ scrollOffset: 0, measurements: [] }); var _React$useState = React.useState(0), scrollOffset = _React$useState[0], setScrollOffset = _React$useState[1]; latestRef.current.scrollOffset = scrollOffset; var useMeasureParent = useObserver || useRect; var _useMeasureParent = useMeasureParent(parentRef, initialRect), outerSize = _useMeasureParent[sizeKey]; latestRef.current.outerSize = outerSize; var defaultScrollToFn = React.useCallback(function (offset) { if (parentRef.current) { parentRef.current[scrollKey] = offset; } }, [parentRef, scrollKey]); var resolvedScrollToFn = scrollToFn || defaultScrollToFn; scrollToFn = React.useCallback(function (offset) { resolvedScrollToFn(offset, defaultScrollToFn); }, [defaultScrollToFn, resolvedScrollToFn]); var _React$useState2 = React.useState({}), measuredCache = _React$useState2[0], setMeasuredCache = _React$useState2[1]; var measure = React.useCallback(function () { return setMeasuredCache({}); }, []); var pendingMeasuredCacheIndexesRef = React.useRef([]); var measurements = React.useMemo(function () { var min = pendingMeasuredCacheIndexesRef.current.length > 0 ? Math.min.apply(Math, pendingMeasuredCacheIndexesRef.current) : 0; pendingMeasuredCacheIndexesRef.current = []; var measurements = latestRef.current.measurements.slice(0, min); for (var i = min; i < size; i++) { var key = keyExtractor(i); var measuredSize = measuredCache[key]; var _start = measurements[i - 1] ? measurements[i - 1].end : paddingStart; var _size = typeof measuredSize === 'number' ? measuredSize : estimateSize(i); var _end = _start + _size; measurements[i] = { index: i, start: _start, size: _size, end: _end, key: key }; } return measurements; }, [estimateSize, measuredCache, paddingStart, size, keyExtractor]); var totalSize = (((_measurements = measurements[size - 1]) == null ? void 0 : _measurements.end) || paddingStart) + paddingEnd; latestRef.current.measurements = measurements; latestRef.current.totalSize = totalSize; var element = onScrollElement ? onScrollElement.current : parentRef.current; var scrollOffsetFnRef = React.useRef(scrollOffsetFn); scrollOffsetFnRef.current = scrollOffsetFn; useIsomorphicLayoutEffect(function () { if (!element) { setScrollOffset(0); return; } var onScroll = function onScroll(event) { var offset = scrollOffsetFnRef.current ? scrollOffsetFnRef.current(event) : element[scrollKey]; setScrollOffset(offset); }; onScroll(); element.addEventListener('scroll', onScroll, { capture: false, passive: true }); return function () { element.removeEventListener('scroll', onScroll); }; }, [element, scrollKey]); var _calculateRange = calculateRange(latestRef.current), start = _calculateRange.start, end = _calculateRange.end; var indexes = React.useMemo(function () { return rangeExtractor({ start: start, end: end, overscan: overscan, size: measurements.length }); }, [start, end, overscan, measurements.length, rangeExtractor]); var measureSizeRef = React.useRef(measureSize); measureSizeRef.current = measureSize; var virtualItems = React.useMemo(function () { var virtualItems = []; var _loop = function _loop(k, len) { var i = indexes[k]; var measurement = measurements[i]; var item = _extends(_extends({}, measurement), {}, { measureRef: function measureRef(el) { if (el) { var measuredSize = measureSizeRef.current(el, horizontal); if (measuredSize !== item.size) { var _scrollOffset = latestRef.current.scrollOffset; if (item.start < _scrollOffset) { defaultScrollToFn(_scrollOffset + (measuredSize - item.size)); } pendingMeasuredCacheIndexesRef.current.push(i); setMeasuredCache(function (old) { var _extends2; return _extends(_extends({}, old), {}, (_extends2 = {}, _extends2[item.key] = measuredSize, _extends2)); }); } } } }); virtualItems.push(item); }; for (var k = 0, len = indexes.length; k < len; k++) { _loop(k); } return virtualItems; }, [indexes, defaultScrollToFn, horizontal, measurements]); var mountedRef = React.useRef(false); useIsomorphicLayoutEffect(function () { if (mountedRef.current) { setMeasuredCache({}); } mountedRef.current = true; }, [estimateSize]); var scrollToOffset = React.useCallback(function (toOffset, _temp) { var _ref2 = _temp === void 0 ? {} : _temp, _ref2$align = _ref2.align, align = _ref2$align === void 0 ? 'start' : _ref2$align; var _latestRef$current = latestRef.current, scrollOffset = _latestRef$current.scrollOffset, outerSize = _latestRef$current.outerSize; if (align === 'auto') { if (toOffset <= scrollOffset) { align = 'start'; } else if (toOffset >= scrollOffset + outerSize) { align = 'end'; } else { align = 'start'; } } if (align === 'start') { scrollToFn(toOffset); } else if (align === 'end') { scrollToFn(toOffset - outerSize); } else if (align === 'center') { scrollToFn(toOffset - outerSize / 2); } }, [scrollToFn]); var tryScrollToIndex = React.useCallback(function (index, _temp2) { var _ref3 = _temp2 === void 0 ? {} : _temp2, _ref3$align = _ref3.align, align = _ref3$align === void 0 ? 'auto' : _ref3$align, rest = _objectWithoutPropertiesLoose(_ref3, ["align"]); var _latestRef$current2 = latestRef.current, measurements = _latestRef$current2.measurements, scrollOffset = _latestRef$current2.scrollOffset, outerSize = _latestRef$current2.outerSize; var measurement = measurements[Math.max(0, Math.min(index, size - 1))]; if (!measurement) { return; } if (align === 'auto') { if (measurement.end >= scrollOffset + outerSize) { align = 'end'; } else if (measurement.start <= scrollOffset) { align = 'start'; } else { return; } } var toOffset = align === 'center' ? measurement.start + measurement.size / 2 : align === 'end' ? measurement.end : measurement.start; scrollToOffset(toOffset, _extends({ align: align }, rest)); }, [scrollToOffset, size]); var scrollToIndex = React.useCallback(function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } // We do a double request here because of // dynamic sizes which can cause offset shift // and end up in the wrong spot. Unfortunately, // we can't know about those dynamic sizes until // we try and render them. So double down! tryScrollToIndex.apply(void 0, args); requestAnimationFrame(function () { tryScrollToIndex.apply(void 0, args); }); }, [tryScrollToIndex]); return { virtualItems: virtualItems, totalSize: totalSize, scrollToOffset: scrollToOffset, scrollToIndex: scrollToIndex, measure: measure }; } var findNearestBinarySearch = function findNearestBinarySearch(low, high, getCurrentValue, value) { while (low <= high) { var middle = (low + high) / 2 | 0; var currentValue = getCurrentValue(middle); if (currentValue < value) { low = middle + 1; } else if (currentValue > value) { high = middle - 1; } else { return middle; } } if (low > 0) { return low - 1; } else { return 0; } }; function calculateRange(_ref4) { var measurements = _ref4.measurements, outerSize = _ref4.outerSize, scrollOffset = _ref4.scrollOffset; var size = measurements.length - 1; var getOffset = function getOffset(index) { return measurements[index].start; }; var start = findNearestBinarySearch(0, size, getOffset, scrollOffset); var end = start; while (end < size && measurements[end].end < scrollOffset + outerSize) { end++; } return { start: start, end: end }; } export { useVirtual };