@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
JavaScript
/**
* 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 };