@tamagui/react-native-web-lite
Version:
React Native for Web
154 lines (153 loc) • 6.37 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: !0 });
}, __copyProps = (to, from, except, desc) => {
if (from && typeof from == "object" || typeof from == "function")
for (let key of __getOwnPropNames(from))
!__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: !0 }), mod);
var ViewabilityHelper_exports = {};
__export(ViewabilityHelper_exports, {
ViewabilityHelper: () => ViewabilityHelper,
default: () => ViewabilityHelper_default
});
module.exports = __toCommonJS(ViewabilityHelper_exports);
var import_react_native_web_internals = require("@tamagui/react-native-web-internals");
class ViewabilityHelper {
_config;
_hasInteracted = !1;
_timers = /* @__PURE__ */ new Set();
_viewableIndices = [];
_viewableItems = /* @__PURE__ */ new Map();
constructor(config = { viewAreaCoveragePercentThreshold: 0 }) {
this._config = config;
}
/**
* Cleanup, e.g. on unmount. Clears any pending timers.
*/
dispose() {
this._timers.forEach(clearTimeout);
}
/**
* Determines which items are viewable based on the current metrics and config.
*/
computeViewableItems(props, scrollOffset, viewportHeight, getFrameMetrics, renderRange) {
const itemCount = props.getItemCount(props.data), { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = this._config, viewAreaMode = viewAreaCoveragePercentThreshold != null, viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
(0, import_react_native_web_internals.invariant)(
viewablePercentThreshold != null && itemVisiblePercentThreshold != null != (viewAreaCoveragePercentThreshold != null),
"Must set exactly one of itemVisiblePercentThreshold or viewAreaCoveragePercentThreshold"
);
const viewableIndices = [];
if (itemCount === 0)
return viewableIndices;
let firstVisible = -1;
const { first, last } = renderRange || { first: 0, last: itemCount - 1 };
if (last >= itemCount)
return console.warn(
"Invalid render range computing viewability " + JSON.stringify({ renderRange, itemCount })
), [];
for (let idx = first; idx <= last; idx++) {
const metrics = getFrameMetrics(idx, props);
if (!metrics)
continue;
const top = metrics.offset - scrollOffset, bottom = top + metrics.length;
if (top < viewportHeight && bottom > 0)
firstVisible = idx, _isViewable(
viewAreaMode,
viewablePercentThreshold,
top,
bottom,
viewportHeight,
metrics.length
) && viewableIndices.push(idx);
else if (firstVisible >= 0)
break;
}
return viewableIndices;
}
/**
* Figures out which items are viewable and how that has changed from before and calls
* `onViewableItemsChanged` as appropriate.
*/
onUpdate(props, scrollOffset, viewportHeight, getFrameMetrics, createViewToken, onViewableItemsChanged, renderRange) {
const itemCount = props.getItemCount(props.data);
if (this._config.waitForInteraction && !this._hasInteracted || itemCount === 0 || !getFrameMetrics(0, props))
return;
let viewableIndices = [];
if (itemCount && (viewableIndices = this.computeViewableItems(
props,
scrollOffset,
viewportHeight,
getFrameMetrics,
renderRange
)), !(this._viewableIndices.length === viewableIndices.length && this._viewableIndices.every((v, ii) => v === viewableIndices[ii])))
if (this._viewableIndices = viewableIndices, this._config.minimumViewTime) {
const handle = setTimeout(() => {
this._timers.delete(handle), this._onUpdateSync(
props,
viewableIndices,
onViewableItemsChanged,
createViewToken
);
}, this._config.minimumViewTime);
this._timers.add(handle);
} else
this._onUpdateSync(props, viewableIndices, onViewableItemsChanged, createViewToken);
}
/**
* clean-up cached _viewableIndices to evaluate changed items on next update
*/
resetViewableIndices() {
this._viewableIndices = [];
}
/**
* Records that an interaction has happened even if there has been no scroll.
*/
recordInteraction() {
this._hasInteracted = !0;
}
_onUpdateSync(props, viewableIndicesToCheck, onViewableItemsChanged, createViewToken) {
viewableIndicesToCheck = viewableIndicesToCheck.filter(
(ii) => this._viewableIndices.includes(ii)
);
const prevItems = this._viewableItems, nextItems = new Map(
viewableIndicesToCheck.map((ii) => {
const viewable = createViewToken(ii, !0, props);
return [viewable.key, viewable];
})
), changed = [];
for (const [key, viewable] of nextItems)
prevItems.has(key) || changed.push(viewable);
for (const [key, viewable] of prevItems)
nextItems.has(key) || changed.push({ ...viewable, isViewable: !1 });
changed.length > 0 && (this._viewableItems = nextItems, onViewableItemsChanged({
viewableItems: Array.from(nextItems.values()),
changed,
viewabilityConfig: this._config
}));
}
}
function _isViewable(viewAreaMode, viewablePercentThreshold, top, bottom, viewportHeight, itemLength) {
if (_isEntirelyVisible(top, bottom, viewportHeight))
return !0;
{
const pixels = _getPixelsVisible(top, bottom, viewportHeight);
return 100 * (viewAreaMode ? pixels / viewportHeight : pixels / itemLength) >= viewablePercentThreshold;
}
}
function _getPixelsVisible(top, bottom, viewportHeight) {
const visibleHeight = Math.min(bottom, viewportHeight) - Math.max(top, 0);
return Math.max(0, visibleHeight);
}
function _isEntirelyVisible(top, bottom, viewportHeight) {
return top >= 0 && bottom <= viewportHeight && bottom > top;
}
var ViewabilityHelper_default = ViewabilityHelper;
//# sourceMappingURL=index.js.map