UNPKG

create-expo-cljs-app

Version:

Create a react native application with Expo and Shadow-CLJS!

1,279 lines (1,044 loc) 63.2 kB
function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 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 ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format */ import Batchinator from '../Batchinator'; import FillRateHelper from '../FillRateHelper'; import PropTypes from 'prop-types'; import * as React from 'react'; import RefreshControl from '../../../exports/RefreshControl'; import ScrollView from '../../../exports/ScrollView'; import StyleSheet from '../../../exports/StyleSheet'; import UIManager from '../../../exports/UIManager'; import View from '../../../exports/View'; import ViewabilityHelper from '../ViewabilityHelper'; import findNodeHandle from '../../../exports/findNodeHandle'; var flattenStyle = StyleSheet.flatten; import infoLog from '../infoLog'; import invariant from 'fbjs/lib/invariant'; import warning from 'fbjs/lib/warning'; import { computeWindowedRenderLimits } from '../VirtualizeUtils'; import { VirtualizedListCellContextProvider, VirtualizedListContext, VirtualizedListContextProvider } from './VirtualizedListContext.js'; var __DEV__ = process.env.NODE_ENV !== 'production'; var _usedIndexForKey = false; var _keylessItemComponentName = ''; /** * Base implementation for the more convenient [`<FlatList>`](https://reactnative.dev/docs/flatlist.html) * and [`<SectionList>`](https://reactnative.dev/docs/sectionlist.html) components, which are also better * documented. In general, this should only really be used if you need more flexibility than * `FlatList` provides, e.g. for use with immutable data instead of plain arrays. * * Virtualization massively improves memory consumption and performance of large lists by * maintaining a finite render window of active items and replacing all items outside of the render * window with appropriately sized blank space. The window adapts to scrolling behavior, and items * are rendered incrementally with low-pri (after any running interactions) if they are far from the * visible area, or with hi-pri otherwise to minimize the potential of seeing blank space. * * Some caveats: * * - Internal state is not preserved when content scrolls out of the render window. Make sure all * your data is captured in the item data or external stores like Flux, Redux, or Relay. * - This is a `PureComponent` which means that it will not re-render if `props` remain shallow- * equal. Make sure that everything your `renderItem` function depends on is passed as a prop * (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on * changes. This includes the `data` prop and parent component state. * - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously * offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see * blank content. This is a tradeoff that can be adjusted to suit the needs of each application, * and we are working on improving it behind the scenes. * - By default, the list looks for a `key` or `id` prop on each item and uses that for the React key. * Alternatively, you can provide a custom `keyExtractor` prop. * */ var VirtualizedList = /*#__PURE__*/function (_React$PureComponent) { _inheritsLoose(VirtualizedList, _React$PureComponent); var _proto = VirtualizedList.prototype; // scrollToEnd may be janky without getItemLayout prop _proto.scrollToEnd = function scrollToEnd(params) { var animated = params ? params.animated : true; var veryLast = this.props.getItemCount(this.props.data) - 1; var frame = this._getFrameMetricsApprox(veryLast); var offset = Math.max(0, frame.offset + frame.length + this._footerLength - this._scrollMetrics.visibleLength); if (this._scrollRef == null) { return; } if (this._scrollRef.scrollTo == null) { console.warn('No scrollTo method provided. This may be because you have two nested ' + 'VirtualizedLists with the same orientation, or because you are ' + 'using a custom component that does not implement scrollTo.'); return; } this._scrollRef.scrollTo(this.props.horizontal ? { x: offset, animated: animated } : { y: offset, animated: animated }); } // scrollToIndex may be janky without getItemLayout prop ; _proto.scrollToIndex = function scrollToIndex(params) { var _this$props = this.props, data = _this$props.data, horizontal = _this$props.horizontal, getItemCount = _this$props.getItemCount, getItemLayout = _this$props.getItemLayout, onScrollToIndexFailed = _this$props.onScrollToIndexFailed; var animated = params.animated, index = params.index, viewOffset = params.viewOffset, viewPosition = params.viewPosition; invariant(index >= 0, "scrollToIndex out of range: requested index " + index + " but minimum is 0"); invariant(getItemCount(data) >= 1, "scrollToIndex out of range: item length " + getItemCount(data) + " but minimum is 1"); invariant(index < getItemCount(data), "scrollToIndex out of range: requested index " + index + " is out of 0 to " + (getItemCount(data) - 1)); if (!getItemLayout && index > this._highestMeasuredFrameIndex) { invariant(!!onScrollToIndexFailed, 'scrollToIndex should be used in conjunction with getItemLayout or onScrollToIndexFailed, ' + 'otherwise there is no way to know the location of offscreen indices or handle failures.'); onScrollToIndexFailed({ averageItemLength: this._averageCellLength, highestMeasuredFrameIndex: this._highestMeasuredFrameIndex, index: index }); return; } var frame = this._getFrameMetricsApprox(index); var offset = Math.max(0, frame.offset - (viewPosition || 0) * (this._scrollMetrics.visibleLength - frame.length)) - (viewOffset || 0); if (this._scrollRef == null) { return; } if (this._scrollRef.scrollTo == null) { console.warn('No scrollTo method provided. This may be because you have two nested ' + 'VirtualizedLists with the same orientation, or because you are ' + 'using a custom component that does not implement scrollTo.'); return; } this._scrollRef.scrollTo(horizontal ? { x: offset, animated: animated } : { y: offset, animated: animated }); } // scrollToItem may be janky without getItemLayout prop. Required linear scan through items - // use scrollToIndex instead if possible. ; _proto.scrollToItem = function scrollToItem(params) { var item = params.item; var _this$props2 = this.props, data = _this$props2.data, getItem = _this$props2.getItem, getItemCount = _this$props2.getItemCount; var itemCount = getItemCount(data); for (var _index = 0; _index < itemCount; _index++) { if (getItem(data, _index) === item) { this.scrollToIndex(_objectSpread(_objectSpread({}, params), {}, { index: _index })); break; } } } /** * Scroll to a specific content pixel offset in the list. * * Param `offset` expects the offset to scroll to. * In case of `horizontal` is true, the offset is the x-value, * in any other case the offset is the y-value. * * Param `animated` (`true` by default) defines whether the list * should do an animation while scrolling. */ ; _proto.scrollToOffset = function scrollToOffset(params) { var animated = params.animated, offset = params.offset; if (this._scrollRef == null) { return; } if (this._scrollRef.scrollTo == null) { console.warn('No scrollTo method provided. This may be because you have two nested ' + 'VirtualizedLists with the same orientation, or because you are ' + 'using a custom component that does not implement scrollTo.'); return; } this._scrollRef.scrollTo(this.props.horizontal ? { x: offset, animated: animated } : { y: offset, animated: animated }); }; _proto.recordInteraction = function recordInteraction() { this._nestedChildLists.forEach(function (childList) { childList.ref && childList.ref.recordInteraction(); }); this._viewabilityTuples.forEach(function (t) { t.viewabilityHelper.recordInteraction(); }); this._updateViewableItems(this.props.data); }; _proto.flashScrollIndicators = function flashScrollIndicators() { if (this._scrollRef == null) { return; } this._scrollRef.flashScrollIndicators(); } /** * Provides a handle to the underlying scroll responder. * Note that `this._scrollRef` might not be a `ScrollView`, so we * need to check that it responds to `getScrollResponder` before calling it. */ ; _proto.getScrollResponder = function getScrollResponder() { if (this._scrollRef && this._scrollRef.getScrollResponder) { return this._scrollRef.getScrollResponder(); } }; _proto.getScrollableNode = function getScrollableNode() { if (this._scrollRef && this._scrollRef.getScrollableNode) { return this._scrollRef.getScrollableNode(); } else { return findNodeHandle(this._scrollRef); } }; _proto.getScrollRef = function getScrollRef() { if (this._scrollRef && this._scrollRef.getScrollRef) { return this._scrollRef.getScrollRef(); } else { return this._scrollRef; } }; _proto.setNativeProps = function setNativeProps(props) { if (this._scrollRef) { this._scrollRef.setNativeProps(props); } }; _proto._getCellKey = function _getCellKey() { var _this$context; return ((_this$context = this.context) == null ? void 0 : _this$context.cellKey) || 'rootList'; }; _proto._getListKey = function _getListKey() { return this.props.listKey || this._getCellKey(); }; _proto._getDebugInfo = function _getDebugInfo() { var _this$context2; return { listKey: this._getListKey(), cellKey: this._getCellKey(), horizontal: !!this.props.horizontal, parent: (_this$context2 = this.context) == null ? void 0 : _this$context2.debugInfo }; }; _proto.hasMore = function hasMore() { return this._hasMore; }; function VirtualizedList(_props) { var _this; _this = _React$PureComponent.call(this, _props) || this; _this._getScrollMetrics = function () { return _this._scrollMetrics; }; _this._getOutermostParentListRef = function () { if (_this._isNestedWithSameOrientation()) { return _this.context.getOutermostParentListRef(); } else { return _assertThisInitialized(_this); } }; _this._getNestedChildState = function (key) { var existingChildData = _this._nestedChildLists.get(key); return existingChildData && existingChildData.state; }; _this._registerAsNestedChild = function (childList) { // Register the mapping between this child key and the cellKey for its cell var childListsInCell = _this._cellKeysToChildListKeys.get(childList.cellKey) || new Set(); childListsInCell.add(childList.key); _this._cellKeysToChildListKeys.set(childList.cellKey, childListsInCell); var existingChildData = _this._nestedChildLists.get(childList.key); if (existingChildData && existingChildData.ref !== null) { console.error('A VirtualizedList contains a cell which itself contains ' + 'more than one VirtualizedList of the same orientation as the parent ' + 'list. You must pass a unique listKey prop to each sibling list.\n\n' + describeNestedLists(_objectSpread(_objectSpread({}, childList), {}, { // We're called from the child's componentDidMount, so it's safe to // read the child's props here (albeit weird). horizontal: !!childList.ref.props.horizontal }))); } _this._nestedChildLists.set(childList.key, { ref: childList.ref, state: null }); if (_this._hasInteracted) { childList.ref.recordInteraction(); } }; _this._unregisterAsNestedChild = function (childList) { _this._nestedChildLists.set(childList.key, { ref: null, state: childList.state }); }; _this._onUpdateSeparators = function (keys, newProps) { keys.forEach(function (key) { var ref = key != null && _this._cellRefs[key]; ref && ref.updateSeparatorProps(newProps); }); }; _this._averageCellLength = 0; _this._cellKeysToChildListKeys = new Map(); _this._cellRefs = {}; _this._frames = {}; _this._footerLength = 0; _this._hasDoneInitialScroll = false; _this._hasInteracted = false; _this._hasMore = false; _this._hasWarned = {}; _this._headerLength = 0; _this._hiPriInProgress = false; _this._highestMeasuredFrameIndex = 0; _this._indicesToKeys = new Map(); _this._nestedChildLists = new Map(); _this._offsetFromParentVirtualizedList = 0; _this._prevParentOffset = 0; _this._scrollMetrics = { contentLength: 0, dOffset: 0, dt: 10, offset: 0, timestamp: 0, velocity: 0, visibleLength: 0 }; _this._scrollRef = null; _this._sentEndForContentLength = 0; _this._totalCellLength = 0; _this._totalCellsMeasured = 0; _this._viewabilityTuples = []; _this._captureScrollRef = function (ref) { _this._scrollRef = ref; }; _this._defaultRenderScrollComponent = function (props) { var onRefresh = props.onRefresh; if (_this._isNestedWithSameOrientation()) { // $FlowFixMe - Typing ReactNativeComponent revealed errors return /*#__PURE__*/React.createElement(View, props); } else if (onRefresh) { invariant(typeof props.refreshing === 'boolean', '`refreshing` prop must be set as a boolean in order to use `onRefresh`, but got `' + /* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment suppresses * an error found when Flow v0.111 was deployed. To see the error, * delete this comment and run Flow. */ JSON.stringify(props.refreshing) + '`'); return ( /*#__PURE__*/ // $FlowFixMe Invalid prop usage React.createElement(ScrollView, _extends({}, props, { refreshControl: props.refreshControl == null ? /*#__PURE__*/React.createElement(RefreshControl, { refreshing: props.refreshing, onRefresh: onRefresh, progressViewOffset: props.progressViewOffset }) : props.refreshControl })) ); } else { // $FlowFixMe Invalid prop usage return /*#__PURE__*/React.createElement(ScrollView, props); } }; _this._onCellUnmount = function (cellKey) { var curr = _this._frames[cellKey]; if (curr) { _this._frames[cellKey] = _objectSpread(_objectSpread({}, curr), {}, { inLayout: false }); } }; _this._onLayout = function (e) { if (_this._isNestedWithSameOrientation()) { // Need to adjust our scroll metrics to be relative to our containing // VirtualizedList before we can make claims about list item viewability _this.measureLayoutRelativeToContainingList(); } else { _this._scrollMetrics.visibleLength = _this._selectLength(e.nativeEvent.layout); } _this.props.onLayout && _this.props.onLayout(e); _this._scheduleCellsToRenderUpdate(); _this._maybeCallOnEndReached(); }; _this._onLayoutEmpty = function (e) { _this.props.onLayout && _this.props.onLayout(e); }; _this._onLayoutFooter = function (e) { _this._triggerRemeasureForChildListsInCell(_this._getFooterCellKey()); _this._footerLength = _this._selectLength(e.nativeEvent.layout); }; _this._onLayoutHeader = function (e) { _this._headerLength = _this._selectLength(e.nativeEvent.layout); }; _this._onContentSizeChange = function (width, height) { if (width > 0 && height > 0 && _this.props.initialScrollIndex != null && _this.props.initialScrollIndex > 0 && !_this._hasDoneInitialScroll) { _this._hasDoneInitialScroll = true; } if (_this.props.onContentSizeChange) { _this.props.onContentSizeChange(width, height); } _this._scrollMetrics.contentLength = _this._selectLength({ height: height, width: width }); _this._scheduleCellsToRenderUpdate(); _this._maybeCallOnEndReached(); }; _this._convertParentScrollMetrics = function (metrics) { // Offset of the top of the nested list relative to the top of its parent's viewport var offset = metrics.offset - _this._offsetFromParentVirtualizedList; // Child's visible length is the same as its parent's var visibleLength = metrics.visibleLength; var dOffset = offset - _this._scrollMetrics.offset; var contentLength = _this._scrollMetrics.contentLength; return { visibleLength: visibleLength, contentLength: contentLength, offset: offset, dOffset: dOffset }; }; _this._onScroll = function (e) { _this._nestedChildLists.forEach(function (childList) { childList.ref && childList.ref._onScroll(e); }); if (_this.props.onScroll) { _this.props.onScroll(e); } var timestamp = e.timeStamp; var visibleLength = _this._selectLength(e.nativeEvent.layoutMeasurement); var contentLength = _this._selectLength(e.nativeEvent.contentSize); var offset = _this._selectOffset(e.nativeEvent.contentOffset); var dOffset = offset - _this._scrollMetrics.offset; if (_this._isNestedWithSameOrientation()) { if (_this._scrollMetrics.contentLength === 0) { // Ignore scroll events until onLayout has been called and we // know our offset from our offset from our parent return; } var _this$_convertParentS = _this._convertParentScrollMetrics({ visibleLength: visibleLength, offset: offset }); visibleLength = _this$_convertParentS.visibleLength; contentLength = _this$_convertParentS.contentLength; offset = _this$_convertParentS.offset; dOffset = _this$_convertParentS.dOffset; } var dt = _this._scrollMetrics.timestamp ? Math.max(1, timestamp - _this._scrollMetrics.timestamp) : 1; var velocity = dOffset / dt; if (dt > 500 && _this._scrollMetrics.dt > 500 && contentLength > 5 * visibleLength && !_this._hasWarned.perf) { infoLog('VirtualizedList: You have a large list that is slow to update - make sure your ' + 'renderItem function renders components that follow React performance best practices ' + 'like PureComponent, shouldComponentUpdate, etc.', { dt: dt, prevDt: _this._scrollMetrics.dt, contentLength: contentLength }); _this._hasWarned.perf = true; } _this._scrollMetrics = { contentLength: contentLength, dt: dt, dOffset: dOffset, offset: offset, timestamp: timestamp, velocity: velocity, visibleLength: visibleLength }; _this._updateViewableItems(_this.props.data); if (!_this.props) { return; } _this._maybeCallOnEndReached(); if (velocity !== 0) { _this._fillRateHelper.activate(); } _this._computeBlankness(); _this._scheduleCellsToRenderUpdate(); }; _this._onScrollBeginDrag = function (e) { _this._nestedChildLists.forEach(function (childList) { childList.ref && childList.ref._onScrollBeginDrag(e); }); _this._viewabilityTuples.forEach(function (tuple) { tuple.viewabilityHelper.recordInteraction(); }); _this._hasInteracted = true; _this.props.onScrollBeginDrag && _this.props.onScrollBeginDrag(e); }; _this._onScrollEndDrag = function (e) { _this._nestedChildLists.forEach(function (childList) { childList.ref && childList.ref._onScrollEndDrag(e); }); var velocity = e.nativeEvent.velocity; if (velocity) { _this._scrollMetrics.velocity = _this._selectOffset(velocity); } _this._computeBlankness(); _this.props.onScrollEndDrag && _this.props.onScrollEndDrag(e); }; _this._onMomentumScrollBegin = function (e) { _this._nestedChildLists.forEach(function (childList) { childList.ref && childList.ref._onMomentumScrollBegin(e); }); _this.props.onMomentumScrollBegin && _this.props.onMomentumScrollBegin(e); }; _this._onMomentumScrollEnd = function (e) { _this._nestedChildLists.forEach(function (childList) { childList.ref && childList.ref._onMomentumScrollEnd(e); }); _this._scrollMetrics.velocity = 0; _this._computeBlankness(); _this.props.onMomentumScrollEnd && _this.props.onMomentumScrollEnd(e); }; _this._updateCellsToRender = function () { var _this$props3 = _this.props, data = _this$props3.data, getItemCount = _this$props3.getItemCount, onEndReachedThreshold = _this$props3.onEndReachedThreshold; var isVirtualizationDisabled = _this._isVirtualizationDisabled(); _this._updateViewableItems(data); if (!data) { return; } _this.setState(function (state) { var newState; var _this$_scrollMetrics = _this._scrollMetrics, contentLength = _this$_scrollMetrics.contentLength, offset = _this$_scrollMetrics.offset, visibleLength = _this$_scrollMetrics.visibleLength; if (!isVirtualizationDisabled) { // If we run this with bogus data, we'll force-render window {first: 0, last: 0}, // and wipe out the initialNumToRender rendered elements. // So let's wait until the scroll view metrics have been set up. And until then, // we will trust the initialNumToRender suggestion if (visibleLength > 0 && contentLength > 0) { // If we have a non-zero initialScrollIndex and run this before we've scrolled, // we'll wipe out the initialNumToRender rendered elements starting at initialScrollIndex. // So let's wait until we've scrolled the view to the right place. And until then, // we will trust the initialScrollIndex suggestion. if (!_this.props.initialScrollIndex || _this._scrollMetrics.offset) { newState = computeWindowedRenderLimits(_this.props, state, _this._getFrameMetricsApprox, _this._scrollMetrics); } } } else { var distanceFromEnd = contentLength - visibleLength - offset; var renderAhead = /* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses * an error found when Flow v0.63 was deployed. To see the error * delete this comment and run Flow. */ distanceFromEnd < onEndReachedThreshold * visibleLength ? _this.props.maxToRenderPerBatch : 0; newState = { first: 0, last: Math.min(state.last + renderAhead, getItemCount(data) - 1) }; } if (newState && _this._nestedChildLists.size > 0) { var newFirst = newState.first; var newLast = newState.last; // If some cell in the new state has a child list in it, we should only render // up through that item, so that we give that list a chance to render. // Otherwise there's churn from multiple child lists mounting and un-mounting // their items. for (var ii = newFirst; ii <= newLast; ii++) { var cellKeyForIndex = _this._indicesToKeys.get(ii); var childListKeys = cellKeyForIndex && _this._cellKeysToChildListKeys.get(cellKeyForIndex); if (!childListKeys) { continue; } var someChildHasMore = false; // For each cell, need to check whether any child list in it has more elements to render for (var _iterator = _createForOfIteratorHelperLoose(childListKeys), _step; !(_step = _iterator()).done;) { var childKey = _step.value; var childList = _this._nestedChildLists.get(childKey); if (childList && childList.ref && childList.ref.hasMore()) { someChildHasMore = true; break; } } if (someChildHasMore && newState) { newState.last = ii; break; } } } if (newState != null && newState.first === state.first && newState.last === state.last) { newState = null; } return newState; }); }; _this._createViewToken = function (index, isViewable) { var _this$props4 = _this.props, data = _this$props4.data, getItem = _this$props4.getItem, keyExtractor = _this$props4.keyExtractor; var item = getItem(data, index); return { index: index, item: item, key: keyExtractor(item, index), isViewable: isViewable }; }; _this._getFrameMetricsApprox = function (index) { var frame = _this._getFrameMetrics(index); if (frame && frame.index === index) { // check for invalid frames due to row re-ordering return frame; } else { var getItemLayout = _this.props.getItemLayout; invariant(!getItemLayout, 'Should not have to estimate frames when a measurement metrics function is provided'); return { length: _this._averageCellLength, offset: _this._averageCellLength * index }; } }; _this._getFrameMetrics = function (index) { var _this$props5 = _this.props, data = _this$props5.data, getItem = _this$props5.getItem, getItemCount = _this$props5.getItemCount, getItemLayout = _this$props5.getItemLayout, keyExtractor = _this$props5.keyExtractor; invariant(getItemCount(data) > index, 'Tried to get frame for out of range index ' + index); var item = getItem(data, index); var frame = item && _this._frames[keyExtractor(item, index)]; if (!frame || frame.index !== index) { if (getItemLayout) { frame = getItemLayout(data, index); } } /* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an * error found when Flow v0.63 was deployed. To see the error delete this * comment and run Flow. */ return frame; }; invariant( // $FlowFixMe !_props.onScroll || !_props.onScroll.__isNative, 'Components based on VirtualizedList must be wrapped with Animated.createAnimatedComponent ' + 'to support native onScroll events with useNativeDriver'); invariant(_props.windowSize > 0, 'VirtualizedList: The windowSize prop must be present and set to a value greater than 0.'); _this._fillRateHelper = new FillRateHelper(_this._getFrameMetrics); _this._updateCellsToRenderBatcher = new Batchinator(_this._updateCellsToRender, _this.props.updateCellsBatchingPeriod); if (_this.props.viewabilityConfigCallbackPairs) { _this._viewabilityTuples = _this.props.viewabilityConfigCallbackPairs.map(function (pair) { return { viewabilityHelper: new ViewabilityHelper(pair.viewabilityConfig), onViewableItemsChanged: pair.onViewableItemsChanged }; }); } else if (_this.props.onViewableItemsChanged) { var onViewableItemsChanged = _this.props.onViewableItemsChanged; _this._viewabilityTuples.push({ viewabilityHelper: new ViewabilityHelper(_this.props.viewabilityConfig), onViewableItemsChanged: onViewableItemsChanged }); } var initialState = { first: _this.props.initialScrollIndex || 0, last: Math.min(_this.props.getItemCount(_this.props.data), (_this.props.initialScrollIndex || 0) + _this.props.initialNumToRender) - 1 }; if (_this._isNestedWithSameOrientation()) { var storedState = _this.context.getNestedChildState(_this._getListKey()); if (storedState) { initialState = storedState; _this.state = storedState; _this._frames = storedState.frames; } } _this.state = initialState; return _this; } _proto.componentDidMount = function componentDidMount() { if (this._isNestedWithSameOrientation()) { this.context.registerAsNestedChild({ cellKey: this._getCellKey(), key: this._getListKey(), ref: this, // NOTE: When the child mounts (here) it's not necessarily safe to read // the parent's props. This is why we explicitly propagate debugInfo // "down" via context and "up" again via this method call on the // parent. parentDebugInfo: this.context.debugInfo }); } }; _proto.componentWillUnmount = function componentWillUnmount() { if (this._isNestedWithSameOrientation()) { this.context.unregisterAsNestedChild({ key: this._getListKey(), state: { first: this.state.first, last: this.state.last, frames: this._frames } }); } this._updateViewableItems(null); this._updateCellsToRenderBatcher.dispose({ abort: true }); this._viewabilityTuples.forEach(function (tuple) { tuple.viewabilityHelper.dispose(); }); this._fillRateHelper.deactivateAndFlush(); }; VirtualizedList.getDerivedStateFromProps = function getDerivedStateFromProps(newProps, prevState) { var data = newProps.data, getItemCount = newProps.getItemCount, maxToRenderPerBatch = newProps.maxToRenderPerBatch; // first and last could be stale (e.g. if a new, shorter items props is passed in), so we make // sure we're rendering a reasonable range here. return { first: Math.max(0, Math.min(prevState.first, getItemCount(data) - 1 - maxToRenderPerBatch)), last: Math.max(0, Math.min(prevState.last, getItemCount(data) - 1)) }; }; _proto._pushCells = function _pushCells(cells, stickyHeaderIndices, stickyIndicesFromProps, first, last, inversionStyle) { var _this2 = this; var _this$props6 = this.props, CellRendererComponent = _this$props6.CellRendererComponent, ItemSeparatorComponent = _this$props6.ItemSeparatorComponent, data = _this$props6.data, getItem = _this$props6.getItem, getItemCount = _this$props6.getItemCount, horizontal = _this$props6.horizontal, keyExtractor = _this$props6.keyExtractor; var stickyOffset = this.props.ListHeaderComponent ? 1 : 0; var end = getItemCount(data) - 1; var prevCellKey; last = Math.min(end, last); var _loop = function _loop(ii) { var item = getItem(data, ii); var key = keyExtractor(item, ii); _this2._indicesToKeys.set(ii, key); if (stickyIndicesFromProps.has(ii + stickyOffset)) { stickyHeaderIndices.push(cells.length); } cells.push( /*#__PURE__*/React.createElement(CellRenderer, { CellRendererComponent: CellRendererComponent, ItemSeparatorComponent: ii < end ? ItemSeparatorComponent : undefined, cellKey: key, fillRateHelper: _this2._fillRateHelper, horizontal: horizontal, index: ii, inversionStyle: inversionStyle, item: item, key: key, prevCellKey: prevCellKey, onUpdateSeparators: _this2._onUpdateSeparators, onLayout: function onLayout(e) { return _this2._onCellLayout(e, key, ii); }, onUnmount: _this2._onCellUnmount, parentProps: _this2.props, ref: function ref(_ref) { _this2._cellRefs[key] = _ref; } })); prevCellKey = key; }; for (var ii = first; ii <= last; ii++) { _loop(ii); } }; _proto._isVirtualizationDisabled = function _isVirtualizationDisabled() { return this.props.disableVirtualization || false; }; _proto._isNestedWithSameOrientation = function _isNestedWithSameOrientation() { var nestedContext = this.context; return !!(nestedContext && !!nestedContext.horizontal === !!this.props.horizontal); }; _proto.render = function render() { var _this3 = this; if (__DEV__) { var flatStyles = flattenStyle(this.props.contentContainerStyle); if (flatStyles != null && flatStyles.flexWrap === 'wrap') { console.warn('`flexWrap: `wrap`` is not supported with the `VirtualizedList` components.' + 'Consider using `numColumns` with `FlatList` instead.'); } } var _this$props7 = this.props, ListEmptyComponent = _this$props7.ListEmptyComponent, ListFooterComponent = _this$props7.ListFooterComponent, ListHeaderComponent = _this$props7.ListHeaderComponent; var _this$props8 = this.props, data = _this$props8.data, horizontal = _this$props8.horizontal; var isVirtualizationDisabled = this._isVirtualizationDisabled(); var inversionStyle = this.props.inverted ? this.props.horizontal ? styles.horizontallyInverted : styles.verticallyInverted : null; var cells = []; var stickyIndicesFromProps = new Set(this.props.stickyHeaderIndices); var stickyHeaderIndices = []; if (ListHeaderComponent) { if (stickyIndicesFromProps.has(0)) { stickyHeaderIndices.push(0); } var element = /*#__PURE__*/React.isValidElement(ListHeaderComponent) ? ListHeaderComponent : /*#__PURE__*/ // $FlowFixMe React.createElement(ListHeaderComponent, null); cells.push( /*#__PURE__*/React.createElement(VirtualizedListCellContextProvider, { cellKey: this._getCellKey() + '-header', key: "$header" }, /*#__PURE__*/React.createElement(View, { onLayout: this._onLayoutHeader, style: StyleSheet.compose(inversionStyle, this.props.ListHeaderComponentStyle) }, // $FlowFixMe - Typing ReactNativeComponent revealed errors element))); } var itemCount = this.props.getItemCount(data); if (itemCount > 0) { _usedIndexForKey = false; _keylessItemComponentName = ''; var spacerKey = !horizontal ? 'height' : 'width'; var lastInitialIndex = this.props.initialScrollIndex ? -1 : this.props.initialNumToRender - 1; var _this$state = this.state, first = _this$state.first, last = _this$state.last; this._pushCells(cells, stickyHeaderIndices, stickyIndicesFromProps, 0, lastInitialIndex, inversionStyle); var firstAfterInitial = Math.max(lastInitialIndex + 1, first); if (!isVirtualizationDisabled && first > lastInitialIndex + 1) { var insertedStickySpacer = false; if (stickyIndicesFromProps.size > 0) { var stickyOffset = ListHeaderComponent ? 1 : 0; // See if there are any sticky headers in the virtualized space that we need to render. for (var ii = firstAfterInitial - 1; ii > lastInitialIndex; ii--) { if (stickyIndicesFromProps.has(ii + stickyOffset)) { var _ref2, _ref3; var initBlock = this._getFrameMetricsApprox(lastInitialIndex); var stickyBlock = this._getFrameMetricsApprox(ii); var leadSpace = stickyBlock.offset - initBlock.offset - (this.props.initialScrollIndex ? 0 : initBlock.length); cells.push( /*#__PURE__*/ /* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment * suppresses an error found when Flow v0.111 was deployed. To * see the error, delete this comment and run Flow. */ React.createElement(View, { key: "$sticky_lead", style: (_ref2 = {}, _ref2[spacerKey] = leadSpace, _ref2) })); this._pushCells(cells, stickyHeaderIndices, stickyIndicesFromProps, ii, ii, inversionStyle); var trailSpace = this._getFrameMetricsApprox(first).offset - (stickyBlock.offset + stickyBlock.length); cells.push( /*#__PURE__*/ /* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment * suppresses an error found when Flow v0.111 was deployed. To * see the error, delete this comment and run Flow. */ React.createElement(View, { key: "$sticky_trail", style: (_ref3 = {}, _ref3[spacerKey] = trailSpace, _ref3) })); insertedStickySpacer = true; break; } } } if (!insertedStickySpacer) { var _ref4; var _initBlock = this._getFrameMetricsApprox(lastInitialIndex); var firstSpace = this._getFrameMetricsApprox(first).offset - (_initBlock.offset + _initBlock.length); cells.push( /*#__PURE__*/ /* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment * suppresses an error found when Flow v0.111 was deployed. To see * the error, delete this comment and run Flow. */ React.createElement(View, { key: "$lead_spacer", style: (_ref4 = {}, _ref4[spacerKey] = firstSpace, _ref4) })); } } this._pushCells(cells, stickyHeaderIndices, stickyIndicesFromProps, firstAfterInitial, last, inversionStyle); if (!this._hasWarned.keys && _usedIndexForKey) { console.warn('VirtualizedList: missing keys for items, make sure to specify a key or id property on each ' + 'item or provide a custom keyExtractor.', _keylessItemComponentName); this._hasWarned.keys = true; } if (!isVirtualizationDisabled && last < itemCount - 1) { var _ref5; var lastFrame = this._getFrameMetricsApprox(last); // Without getItemLayout, we limit our tail spacer to the _highestMeasuredFrameIndex to // prevent the user for hyperscrolling into un-measured area because otherwise content will // likely jump around as it renders in above the viewport. var end = this.props.getItemLayout ? itemCount - 1 : Math.min(itemCount - 1, this._highestMeasuredFrameIndex); var endFrame = this._getFrameMetricsApprox(end); var tailSpacerLength = endFrame.offset + endFrame.length - (lastFrame.offset + lastFrame.length); cells.push( /*#__PURE__*/ /* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment suppresses * an error found when Flow v0.111 was deployed. To see the error, * delete this comment and run Flow. */ React.createElement(View, { key: "$tail_spacer", style: (_ref5 = {}, _ref5[spacerKey] = tailSpacerLength, _ref5) })); } } else if (ListEmptyComponent) { var _element = /*#__PURE__*/React.isValidElement(ListEmptyComponent) ? ListEmptyComponent : /*#__PURE__*/ // $FlowFixMe React.createElement(ListEmptyComponent, null); cells.push( /*#__PURE__*/React.cloneElement(_element, { key: '$empty', onLayout: function onLayout(event) { _this3._onLayoutEmpty(event); if (_element.props.onLayout) { _element.props.onLayout(event); } }, style: StyleSheet.compose(inversionStyle, _element.props.style) })); } if (ListFooterComponent) { var _element2 = /*#__PURE__*/React.isValidElement(ListFooterComponent) ? ListFooterComponent : /*#__PURE__*/ // $FlowFixMe React.createElement(ListFooterComponent, null); cells.push( /*#__PURE__*/React.createElement(VirtualizedListCellContextProvider, { cellKey: this._getFooterCellKey(), key: "$footer" }, /*#__PURE__*/React.createElement(View, { onLayout: this._onLayoutFooter, style: StyleSheet.compose(inversionStyle, this.props.ListFooterComponentStyle) }, // $FlowFixMe - Typing ReactNativeComponent revealed errors _element2))); } var scrollProps = _objectSpread(_objectSpread({}, this.props), {}, { onContentSizeChange: this._onContentSizeChange, onLayout: this._onLayout, onScroll: this._onScroll, onScrollBeginDrag: this._onScrollBeginDrag, onScrollEndDrag: this._onScrollEndDrag, onMomentumScrollBegin: this._onMomentumScrollBegin, onMomentumScrollEnd: this._onMomentumScrollEnd, scrollEventThrottle: this.props.scrollEventThrottle, // TODO: Android support stickyHeaderIndices: stickyHeaderIndices, style: inversionStyle ? [inversionStyle, this.props.style] : this.props.style }); this._hasMore = this.state.last < this.props.getItemCount(this.props.data) - 1; var innerRet = /*#__PURE__*/React.createElement(VirtualizedListContextProvider, { value: { cellKey: null, getScrollMetrics: this._getScrollMetrics, horizontal: this.props.horizontal, getOutermostParentListRef: this._getOutermostParentListRef, getNestedChildState: this._getNestedChildState, registerAsNestedChild: this._registerAsNestedChild, unregisterAsNestedChild: this._unregisterAsNestedChild, debugInfo: this._getDebugInfo() } }, /*#__PURE__*/React.cloneElement((this.props.renderScrollComponent || this._defaultRenderScrollComponent)(scrollProps), { ref: this._captureScrollRef }, cells)); var ret = innerRet; if (this.props.debug) { return /*#__PURE__*/React.createElement(View, { style: styles.debug }, ret, this._renderDebugOverlay()); } else { return ret; } }; _proto.componentDidUpdate = function componentDidUpdate(prevProps) { var _this$props9 = this.props, data = _this$props9.data, extraData = _this$props9.extraData; if (data !== prevProps.data || extraData !== prevProps.extraData) { // clear the viewableIndices cache to also trigger // the onViewableItemsChanged callback with the new data this._viewabilityTuples.forEach(function (tuple) { tuple.viewabilityHelper.resetViewableIndices(); }); } // The `this._hiPriInProgress` is guaranteeing a hiPri cell update will only happen // once per fiber update. The `_scheduleCellsToRenderUpdate` will set it to true // if a hiPri update needs to perform. If `componentDidUpdate` is triggered with // `this._hiPriInProgress=true`, means it's triggered by the hiPri update. The // `_scheduleCellsToRenderUpdate` will check this condition and not perform // another hiPri update. var hiPriInProgress = this._hiPriInProgress; this._scheduleCellsToRenderUpdate(); // Make sure setting `this._hiPriInProgress` back to false after `componentDidUpdate` // is triggered with `this._hiPriInProgress = true` if (hiPriInProgress) { this._hiPriInProgress = false; } }; _proto._computeBlankness = function _computeBlankness() { this._fillRateHelper.computeBlankness(this.props, this.state, this._scrollMetrics); }; _proto._onCellLayout = function _onCellLayout(e, cellKey, index) { var layout = e.nativeEvent.layout; var next = { offset: this._selectOffset(layout), length: this._selectLength(layout), index: index, inLayout: true }; var curr = this._frames[cellKey]; if (!curr || next.offset !== curr.offset || next.length !== curr.length || index !== curr.index) { this._totalCellLength += next.length - (curr ? curr.length : 0); this._totalCellsMeasured += curr ? 0 : 1; this._averageCellLength = this._totalCellLength / this._totalCellsMeasured; this._frames[cellKey] = next; this._highestMeasuredFrameIndex = Math.max(this._highestMeasuredFrameIndex, index); this._scheduleCellsToRenderUpdate(); } else { this._frames[cellKey].inLayout = true; } this._triggerRemeasureForChildListsInCell(cellKey); this._computeBlankness(); this._updateViewableItems(this.props.data); }; _proto._triggerRemeasureForChildListsInCell = function _triggerRemeasureForChildListsInCell(cellKey) { var childListKeys = this._cellKeysToChildListKeys.get(cellKey); if (childListKeys) { for (var _iterator2 = _createForOfIteratorHelperLoose(childListKeys), _step2; !(_step2 = _iterator2()).done;) { var childKey = _step2.value; var childList = this._nestedChildLists.get(childKey); childList && childList.ref && childList.ref.measureLayoutRelativeToContainingList(); } } }; _proto.measureLayoutRelativeToContainingList = function measureLayoutRelativeToContainingList() { var _this4 = this; // TODO (T35574538): findNodeHandle sometimes crashes with "Unable to find // node on an unmounted component" during scrolling try { if (!this._scrollRef) { return; } // We are assuming that getOutermostParentListRef().getScrollRef() // is a non-null reference to a ScrollView this._scrollRef.measureLayout(this.context.getOutermostParentListRef().getScrollRef(), function (x, y, width, height) { _this4._offsetFromParentVirtualizedList = _this4._selectOffset({ x: x, y: y }); _this4._scrollMetrics.contentLength = _this4._selectLength({ width: width, height: height }); var scrollMetrics = _this4._convertParentScrollMetrics(_this4.context.getScrollMetrics()); _this4._scrollMetrics.visibleLength = scrollMetrics.visibleLength; _this4._scrollMetrics.offset = scrollMetrics.offset; }, function (error) { console.warn("VirtualizedList: Encountered an error while measuring a list's" + ' offset from its containing VirtualizedList.'); }); } catch (error) { console.warn('measureLayoutRelativeToContainingList threw an error', error.stack); } }; _proto._getFooterCellKey = function _getFooterCellKey() { return this._getCellKey() + '-footer'; }; _proto._renderDebugOverlay = function _renderDebugOverlay() { var normalize = this._scrollMetrics.visibleLength / (this._scrollMetrics.contentLength || 1); var framesInLayout = []; var itemCount = this.props.getItemCount(this.props.data); for (var ii = 0; ii < itemCount; ii++) { var frame = this._getFrameMetricsApprox(ii); /* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an * error found when Flow v0.68 was deployed. To see the error delete this * comment and