UNPKG

@enact/sandstone

Version:

Large-screen/TV support library for Enact, containing a variety of UI components.

229 lines (217 loc) 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getPosition = exports.getLabelOffset = exports.calcOverflow = exports.adjustDirection = exports.adjustAnchor = void 0; var _resolution = _interopRequireDefault(require("@enact/ui/resolution")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } // TooltipDecorator util.js // /** * Calculates the unusable space of a centered Tooltip, which the Tooltip arrow cannot be positioned, * as a percentage of the Tooltip's total width. * * @param {Number} tooltipWidth The measured width of the Tooltip * @return {Number} The percent of the width of the tooltip that the Tooltip's * decorations allocate for themselves (on one side). * @private */ var getLabelUnavailableSpace = function getLabelUnavailableSpace(tooltipWidth) { // Arrow is 30px wide total, we need to know how wide half of it is, since it's centered on the anchor point. var arrowWidth = 30 / 2; // Tooltip is 108px tall, divide by half to get the curve radius, add the tooltip width // to determine the distance that the anchor cannot progress past. var tooltipUnavailableEdge = _resolution["default"].scale(108 / 2 + arrowWidth); // cap the offset at 50% - that percentage var tooltipUnavaliablePercentage = 0.5 - tooltipUnavailableEdge / tooltipWidth; return tooltipUnavaliablePercentage; }; /** * Adjust anchor position for `Tooltip` based on overflow and rtl. * Takes the output of `adjustDirection`, and `calcOverflow`. * * @method * @memberof sandstone/TooltipDecorator * @param {String} arrowAnchor Initial anchor position * @param {String} tooltipDirection Direction of tooltip; value from `adjustDirection` * @param {Object} overflow Tooltip's calculated overflow from `calcOverflow` * @param {Boolean} rtl RTL mode * @returns {String} Adjusted anchor position * @private */ var adjustAnchor = exports.adjustAnchor = function adjustAnchor(arrowAnchor, tooltipDirection, overflow, rtl) { if (tooltipDirection === 'above' || tooltipDirection === 'below') { if (rtl && arrowAnchor !== 'center') { arrowAnchor = arrowAnchor === 'left' ? 'right' : 'left'; } // Flip sideways if it overflows to the sides if (arrowAnchor === 'center') { // If tooltip is anchored in the center, don't flip. We'll offset it appropriately later. } else if (overflow.isOverRight) { arrowAnchor = 'left'; } else if (overflow.isOverLeft) { arrowAnchor = 'right'; } // If tooltip is just too wide for the whole screen, switch it to a center tooltip if (overflow.isOverWide && tooltipDirection !== 'left' && tooltipDirection !== 'right') { arrowAnchor = 'center'; } } return arrowAnchor; }; /** * Adjust direction for `Tooltip` based on overflow and rtl. * Takes the output from `calcOverflow`. * * @method * @memberof sandstone/TooltipDecorator * @param {String} tooltipDirection Direction of tooltip * @param {Object} overflow Tooltip's calculated overflow from `calcOverflow` * @param {Boolean} rtl RTL mode * @returns {String} Adjusted tooltip direction * @private */ var adjustDirection = exports.adjustDirection = function adjustDirection(tooltipDirection, overflow, rtl) { if (rtl && (tooltipDirection === 'left' || tooltipDirection === 'right')) { tooltipDirection = tooltipDirection === 'left' ? 'right' : 'left'; } // Flip tooltip if it overflows towards the tooltip direction if (overflow.isOverTop && tooltipDirection === 'above') { tooltipDirection = 'below'; } else if (overflow.isOverBottom && tooltipDirection === 'below') { tooltipDirection = 'above'; } else if (overflow.isOverLeft && tooltipDirection === 'left' && !overflow.isOverWide) { tooltipDirection = 'right'; } else if (overflow.isOverRight && tooltipDirection === 'right' && !overflow.isOverWide) { tooltipDirection = 'left'; } return tooltipDirection; }; /** * Calculates the overflow of `Tooltip` — if `Tooltip` is at the edge of the viewport. * Return the amount of overflow in a particular direction if there is overflow (false otherwise). * * @method * @memberof sandstone/TooltipDecorator * @param {Object} tooltipNode The `getBoundingClientRect` values for tooltip node * @param {Object} clientNode The `getBoundingClientRect` values for client node * @param {String} tooltipDirection Direction of tooltip * @param {Number} tooltipHeight Tooltip height * @param {Number} edgeKeepout Extra margin around the screen to avoid * @returns {Object} Tooltip's calculated overflow * @private */ var calcOverflow = exports.calcOverflow = function calcOverflow(tooltipNode, clientNode, tooltipDirection, edgeKeepout) { // get the distance of space on both the right and left side of the client node. `clientNode.width / 2` because we want the tooltip to be positioned horizontally in middle of the client node. var windowWidth = window.innerWidth; var windowHeight = window.innerHeight; var clientHorizontalCenter = clientNode.left + clientNode.width / 2; var tooltipSafeWidth = tooltipNode.width + edgeKeepout; var tooltipCenterdSafeWidth = tooltipNode.width / 2 + edgeKeepout; var rightDelta = tooltipSafeWidth > clientHorizontalCenter; var leftDelta = tooltipSafeWidth > windowWidth - clientHorizontalCenter; var isTooltipWide = tooltipSafeWidth > windowWidth || leftDelta && rightDelta; if (tooltipDirection === 'above' || tooltipDirection === 'below') { var isOverTop = clientNode.top - tooltipNode.height - edgeKeepout; var isOverBottom = clientNode.bottom + tooltipNode.height + edgeKeepout; var isOverLeft = clientHorizontalCenter - tooltipSafeWidth; var isOverRight = clientHorizontalCenter + tooltipSafeWidth; var isOverCenterLeft = clientHorizontalCenter - tooltipCenterdSafeWidth; var isOverCenterRight = clientHorizontalCenter + tooltipCenterdSafeWidth; return { isOverTop: isOverTop < 0 ? isOverTop : false, isOverBottom: isOverBottom > windowHeight ? isOverBottom - windowHeight : false, isOverLeft: isOverLeft < 0 ? isOverLeft : false, isOverRight: isOverRight > windowWidth ? isOverRight - windowWidth : false, isOverCenterLeft: isOverCenterLeft < 0 ? isOverCenterLeft : false, isOverCenterRight: isOverCenterRight > windowWidth ? isOverCenterRight - windowWidth : false, isOverWide: isTooltipWide }; } else if (tooltipDirection === 'left' || tooltipDirection === 'right') { var _isOverTop = clientNode.top - tooltipNode.height + clientNode.height - edgeKeepout; var _isOverBottom = clientNode.bottom + tooltipNode.height - clientNode.height / 2 + edgeKeepout; var _isOverLeft = clientNode.left - tooltipNode.width - edgeKeepout; var _isOverRight = clientNode.right + tooltipNode.width + edgeKeepout; return { isOverTop: _isOverTop < 0 ? _isOverTop : false, isOverBottom: _isOverBottom > windowHeight ? _isOverBottom - windowHeight : false, isOverLeft: _isOverLeft < 0 ? _isOverLeft : false, isOverRight: _isOverRight > windowWidth ? _isOverRight - windowWidth : false, isOverWide: isTooltipWide }; } }; /** * Calculates the top and left position for `Tooltip`. * Takes the output of `adjustAnchor`, `adjustDirection`, and `calcOverflow`. * * @method * @memberof sandstone/TooltipDecorator * @param {Object} tooltipNode The `getBoundingClientRect` values for tooltip node * @param {Object} clientNode The `getBoundingClientRect` values for client node * @param {String} arrowAnchor Anchor position from `adjustAnchor` * @param {String} tooltipDirection Direction of tooltip * @param {Number} tooltipHeight Tooltip height * @param {Object} overflow Tooltip's calculated overflow from `calcOverflow` * @param {Boolean} rtl RTL mode * @returns {Object} Tooltip top and left position * @private */ var getPosition = exports.getPosition = function getPosition(clientNode, tooltipDirection) { var position = {}; switch (tooltipDirection) { case 'above': position.top = clientNode.top; break; case 'below': position.top = clientNode.bottom; break; case 'right': position.left = clientNode.right; break; case 'left': position.left = clientNode.left; break; default: position = {}; } if (tooltipDirection === 'above' || tooltipDirection === 'below') { position.left = clientNode.left + clientNode.width / 2; } else if (tooltipDirection === 'left' || tooltipDirection === 'right') { position.top = clientNode.top + clientNode.height / 2; } return position; }; /** * Adjusts the `Tooltip` arrow anchor when the tooltip is too wide. * Takes the output of `calcOverflow`. * * @method * @memberof sandstone/TooltipDecorator * @param {Object} tooltipNode The `getBoundingClientRect` values for tooltip node * @param {String} tooltipDirection Direction of tooltip * @param {Object} tooltipPosition Calculated tooltip position from `getPosition` * @param {Object} overflow Tooltip's calculated overflow from `calcOverflow` * @private */ var getLabelOffset = exports.getLabelOffset = function getLabelOffset(tooltipNode, tooltipDirection, tooltipPosition, overflow) { // This method is irrelevent to left and right anchored tooltips, skip entirely. if (tooltipDirection !== 'left' && tooltipDirection !== 'right') { var tooltipWidth = tooltipNode.width; if (overflow.isOverWide || overflow.isOverCenterLeft || overflow.isOverCenterRight) { var pixelOffset = 0; if (overflow.isOverCenterLeft) { // Start shifting the label to the right (negative offset) pixelOffset = overflow.isOverCenterLeft; } else if (overflow.isOverCenterRight) { // Start shifting the label to the left (positive offset) pixelOffset = overflow.isOverCenterRight; } var percentageOffset = pixelOffset / tooltipWidth * -1; var offsetBoundaryPercentage = getLabelUnavailableSpace(tooltipWidth); var cappedPercentageOffset = Math.max(offsetBoundaryPercentage * -1, Math.min(offsetBoundaryPercentage, percentageOffset)); return cappedPercentageOffset; } } return null; };