"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getScrollBy = getScrollBy; var _canScrollOnEdge = require("../shared/can-scroll-on-edge"); var _edges = require("../shared/edges"); var _engagementHistory = require("../shared/engagement-history"); var _getScrollChange = require("../shared/get-scroll-change"); var _isAxisAllowed = require("../shared/is-axis-allowed"); var _isWithin = require("../shared/is-within"); var _hitbox = require("./hitbox"); // Distance dampening is enabled when we are inside the edge // In order to match "over element" scrolling function getIsDistanceDampeningEnabled(value) { return value.type === 'inside-of-edge'; } function getSpacingFromProvided(value) { var _value$top, _value$right, _value$bottom, _value$left; return { top: (_value$top = value === null || value === void 0 ? void 0 : value.top) !== null && _value$top !== void 0 ? _value$top : 0, right: (_value$right = value === null || value === void 0 ? void 0 : value.right) !== null && _value$right !== void 0 ? _value$right : 0, bottom: (_value$bottom = value === null || value === void 0 ? void 0 : value.bottom) !== null && _value$bottom !== void 0 ? _value$bottom : 0, left: (_value$left = value === null || value === void 0 ? void 0 : value.left) !== null && _value$left !== void 0 ? _value$left : 0 }; } function getHitboxSpacing(provided) { return { top: getSpacingFromProvided(provided.fromTopEdge), right: getSpacingFromProvided(provided.fromRightEdge), bottom: getSpacingFromProvided(provided.fromBottomEdge), left: getSpacingFromProvided(provided.fromLeftEdge) }; } function getScrollBy(_ref) { var entry = _ref.entry, timeSinceLastFrame = _ref.timeSinceLastFrame, input = _ref.input, config = _ref.config, allowedAxis = _ref.allowedAxis; var client = { x: input.clientX, y: input.clientY }; // 🔥 // For each registered item we need to do `getBoundingClientRect()` which is not great // Why? // 1. The hitbox can extend outside of an elements bounds // 2. We want overflow scrolling to start before the user has entered the bounds of the element // Otherwise we could search upwards in the DOM from the `elementFromPoint` var clientRect = entry.element.getBoundingClientRect(); var overflow = getHitboxSpacing(entry.getOverflow()); var inHitboxForEdge = _edges.edges.map(function (edge) { var _getHitbox$edge = _hitbox.getHitbox[edge]({ clientRect: clientRect, overflow: overflow, config: config }), insideOfEdge = _getHitbox$edge.insideOfEdge, outsideOfEdge = _getHitbox$edge.outsideOfEdge; /** Note: * Intentionally _not_ doing an explicit check to * see if `client` is with within the `overElementHitbox`. * * **Why?** * * 1. 🥱 Redundant * This check is already achieved by `element.contains(underUsersPointer)` * * 2. 📐 Overlap on boundaries * Two elements can share the same `{x,y}` points on shared edges. * It's not clear which of the two will be picked by * `const underUsersPointer = document.elementFromPoint(x,y)` * The edge of an "outside" element, can have shared `{x,y}` * values along the edge of an "inside element". * So when `underUsersPointer` is the "outer" element, the `client` * point might actually be also within the "inner" element. * We are exclusively relying on `underUsersPointer` make the decision * on what we are "over" so we should not be doing "over element" hitbox * testing here. * https://twitter.com/alexandereardon/status/1721758766507638996 * * * 3. 🐞 Chrome bug * `document.getElementFromPoint(x, y)` can return an element that does not contain `{x,y}`, * In these cases, `isWithin({client, clientRect: overElementHitbox})` can return `false`. * https://bugs.chromium.org/p/chromium/issues/detail?id=1500073 */ if ((0, _isWithin.isWithin)({ client: client, clientRect: outsideOfEdge })) { return { edge: edge, hitbox: outsideOfEdge, type: 'outside-of-edge' }; } if ((0, _isWithin.isWithin)({ client: client, clientRect: insideOfEdge })) { return { edge: edge, hitbox: insideOfEdge, type: 'inside-of-edge' }; } return false; }).filter(function (value) { return Boolean(value); }); if (!inHitboxForEdge.length) { return null; } // Even if no edges are scrollable, we are marking the element // as being engaged with to start applying time dampening var engagement = (0, _engagementHistory.markAndGetEngagement)(entry.element); // Note: changing the allowed axis during a drag will not // reset time dampening. It was decided it would be too // complex to implement initially, and we can add it // later if needed. var scrollableEdges = inHitboxForEdge.filter(function (value) { return (0, _isAxisAllowed.isAxisAllowed)(_edges.edgeAxisLookup[value.edge], allowedAxis) && _canScrollOnEdge.canScrollOnEdge[value.edge](entry.element); }); // Nothing can be scrolled if (!scrollableEdges.length) { return null; } var lookup = new Map(scrollableEdges.map(function (value) { return [value.edge, value]; })); var left = function () { var axis = 'horizontal'; var leftEdge = lookup.get('left'); if (leftEdge) { return (0, _getScrollChange.getScrollChange)({ client: client, isDistanceDampeningEnabled: getIsDistanceDampeningEnabled(leftEdge), hitbox: leftEdge.hitbox, edge: 'left', axis: axis, timeSinceLastFrame: timeSinceLastFrame, engagement: engagement, config: config }); } var rightEdge = lookup.get('right'); if (rightEdge) { return (0, _getScrollChange.getScrollChange)({ client: client, isDistanceDampeningEnabled: getIsDistanceDampeningEnabled(rightEdge), hitbox: rightEdge.hitbox, edge: 'right', axis: axis, timeSinceLastFrame: timeSinceLastFrame, engagement: engagement, config: config }); } return 0; }(); var top = function () { var axis = 'vertical'; var bottomEdge = lookup.get('bottom'); if (bottomEdge) { return (0, _getScrollChange.getScrollChange)({ client: client, isDistanceDampeningEnabled: getIsDistanceDampeningEnabled(bottomEdge), hitbox: bottomEdge.hitbox, edge: 'bottom', axis: axis, timeSinceLastFrame: timeSinceLastFrame, engagement: engagement, config: config }); } var topEdge = lookup.get('top'); if (topEdge) { return (0, _getScrollChange.getScrollChange)({ client: client, isDistanceDampeningEnabled: getIsDistanceDampeningEnabled(topEdge), hitbox: topEdge.hitbox, edge: 'top', axis: axis, timeSinceLastFrame: timeSinceLastFrame, engagement: engagement, config: config }); } return 0; }(); return { left: left, top: top }; }