UNPKG

@gpa-gemstone/react-forms

Version:
190 lines (189 loc) 12.2 kB
"use strict"; // ****************************************************************************************************** // ToolTip.tsx - Gbtc // // Copyright © 2020, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this // file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 01/14/2021 - Christoph Lackner // Generated original version of source code. // ****************************************************************************************************** var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Tooltip = void 0; var React = require("react"); var styled_components_1 = require("styled-components"); var helper_functions_1 = require("@gpa-gemstone/helper-functions"); var react_portal_1 = require("react-portal"); var lodash_1 = require("lodash"); // The styled tooltip wrapper component. var PopoverDiv = styled_components_1.default.div(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n & {\n display: inline-block;\n font-size: 13px;\n position: fixed;\n pointer-events: ", ";\n transition: opacity 0.3s ease-out;\n z-index: ", ";\n opacity: ", ";\n top: ", ";\n left: ", ";\n max-width: 100%;\n background-color: ", ";\n color: ", ";\n border: ", ";\n padding: 0.1em;\n }\n"], ["\n & {\n display: inline-block;\n font-size: 13px;\n position: fixed;\n pointer-events: ", ";\n transition: opacity 0.3s ease-out;\n z-index: ", ";\n opacity: ", ";\n top: ", ";\n left: ", ";\n max-width: 100%;\n background-color: ", ";\n color: ", ";\n border: ", ";\n padding: 0.1em;\n }\n"])), function (props) { return props.Show ? 'auto' : 'none'; }, function (props) { return props.Zindex; }, function (props) { return props.Show ? "0.9" : "0"; }, function (props) { return "".concat(props.Top, "px"); }, function (props) { return "".concat(props.Left, "px"); }, function (props) { return props.BgColor; }, function (props) { return props.Color; }, function (props) { return "1px solid ".concat(props.Color); }); //Arrow needs to be a styled div as the arrow class has pseduo elements var Arrow = styled_components_1.default.div(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n &.arrow {\n ", "\n &::after {\n content: '';\n position: absolute;\n ", "\n ", "\n ", "\n ", "\n }\n }\n"], ["\n &.arrow {\n ", "\n &::after {\n content: '';\n position: absolute;\n ", "\n ", "\n ", "\n ", "\n }\n }\n"])), function (props) { return (props.Position === 'left' || props.Position === 'right') ? "top: calc(".concat(props.ArrowPositionPercent, "% - 1em);") : "left: calc(".concat(props.ArrowPositionPercent, "% - 1em);"); }, function (props) { return props.Position === 'top' && "border-top-color: ".concat(props.BackgroundColor, ";"); }, function (props) { return props.Position === 'bottom' && "border-bottom-color: ".concat(props.BackgroundColor, ";"); }, function (props) { return props.Position === 'left' && "border-left-color: ".concat(props.BackgroundColor, ";"); }, function (props) { return props.Position === 'right' && "border-right-color: ".concat(props.BackgroundColor, ";"); }); var defaultTargetPosition = { Top: -999, Left: -999, Width: 0, Height: 0 }; // The other element needs to have data-tooltip attribute equal the target prop used for positioning var Tooltip = function (props) { var _a; var targetPosition = (_a = props.Position) !== null && _a !== void 0 ? _a : 'top'; var toolTip = React.useRef(null); var _b = React.useState(false), isTooltipHovered = _b[0], setIsTooltipHovered = _b[1]; var _c = React.useState(targetPosition), activePosition = _c[0], setActivePosition = _c[1]; var _d = React.useState(0), top = _d[0], setTop = _d[1]; var _e = React.useState(0), left = _e[0], setLeft = _e[1]; var _f = React.useState(50), arrowPositionPercent = _f[0], setArrowPositionPercent = _f[1]; var _g = React.useState(defaultTargetPosition), targetElementPosition = _g[0], setTargetElementPosition = _g[1]; var alertRef = React.useRef(null); var _h = React.useState('green'), backgroundColor = _h[0], setBackgroundColor = _h[1]; var _j = React.useState('red'), color = _j[0], setColor = _j[1]; var alarmClass = React.useMemo(function () { return props.Class != null ? "alert-".concat(props.Class, " d-none") : 'popover d-none'; }, [props.Class]); var closeTimer = React.useRef(); var _k = React.useState(props.Show), delayedShow = _k[0], setDelayedShow = _k[1]; var shouldShow = delayedShow || isTooltipHovered; //Effect to handle mouse hover state React.useEffect(function () { if (toolTip.current == null) return; var tootipDiv = toolTip.current; function onMouseMoveInside() { clearTimeout(closeTimer.current); setIsTooltipHovered(true); } function onMouseLeave() { setIsTooltipHovered(false); } tootipDiv.addEventListener('mousemove', onMouseMoveInside); tootipDiv.addEventListener('mouseleave', onMouseLeave); return function () { tootipDiv.removeEventListener('mousemove', onMouseMoveInside); tootipDiv.removeEventListener('mouseleave', onMouseLeave); }; }, [props.Show]); // Effect to handle delayed hiding of tooltip to allow for hover logic React.useEffect(function () { if (props.Show) { clearTimeout(closeTimer.current); setDelayedShow(true); } else if (!isTooltipHovered) { //delay closing by a quarter of a second to allow for hover closeTimer.current = window.setTimeout(function () { return setDelayedShow(false); }, 250); } }, [props.Show, isTooltipHovered]); React.useLayoutEffect(function () { if (alertRef.current == null) return; var style = getComputedStyle(alertRef.current); setBackgroundColor(style.backgroundColor); setColor(style.color); }); React.useEffect(function () { var target = document.querySelectorAll("[data-tooltip".concat(props.Target === undefined ? '' : "=\"".concat(props.Target, "\""), "]")); if (target.length === 0) { setTargetElementPosition(defaultTargetPosition); return; } var targetLocation = (0, helper_functions_1.GetNodeSize)(target[0]); var newPosition = { Height: targetLocation.height, Top: targetLocation.top, Left: targetLocation.left, Width: targetLocation.width }; if (!(0, lodash_1.isEqual)(newPosition, targetElementPosition)) setTargetElementPosition(newPosition); }, [shouldShow, props.Target, targetElementPosition]); React.useLayoutEffect(function () { var _a = getPosition(toolTip, targetElementPosition, targetPosition), t = _a[0], l = _a[1], arrowLeft = _a[2], actPosition = _a[3]; setTop(t); setLeft(l); setArrowPositionPercent(arrowLeft); setActivePosition(actPosition); }, [targetElementPosition, props === null || props === void 0 ? void 0 : props.children, targetPosition]); var zIndex = (props.Zindex === undefined ? 9999 : props.Zindex); return (React.createElement(React.Fragment, null, React.createElement("div", { className: alarmClass, ref: alertRef }), React.createElement(react_portal_1.Portal, null, React.createElement(PopoverDiv, { className: "popover bs-popover-".concat(activePosition), Show: shouldShow, Top: top, Left: left, ref: toolTip, Zindex: zIndex, BgColor: backgroundColor, Color: color }, React.createElement(Arrow, { className: 'arrow', Position: activePosition, ArrowPositionPercent: arrowPositionPercent, BackgroundColor: backgroundColor, Color: color }), React.createElement("div", { className: 'popover-body', style: { backgroundColor: backgroundColor, color: color } }, props.children))))); }; exports.Tooltip = Tooltip; //Helper function var getPosition = function (toolTip, targetPosition, position) { if (toolTip.current === null) return [-999, -999]; var tipLocation = (0, helper_functions_1.GetNodeSize)(toolTip.current); var offset = 5; var top = 0; var left = 0; var windowWidth = window.innerWidth; var windowHeight = window.innerHeight; var effectivePosition = position; if (position === 'left') { top = targetPosition.Top + 0.5 * targetPosition.Height - 0.5 * tipLocation.height; left = targetPosition.Left - tipLocation.width - offset; } else if (position === 'right') { top = targetPosition.Top + 0.5 * targetPosition.Height - 0.5 * tipLocation.height; left = targetPosition.Left + targetPosition.Width + offset; } else if (position === 'top' || position === undefined) { // if not enough room above, flip to bottom if (targetPosition.Top >= tipLocation.height + offset) { effectivePosition = 'top'; top = targetPosition.Top - tipLocation.height - offset; } else { effectivePosition = 'bottom'; top = targetPosition.Top + targetPosition.Height + offset; } left = targetPosition.Left + 0.5 * targetPosition.Width - 0.5 * tipLocation.width; // If tooltip goes beyond right viewport boundary adjust left position to fit if (left + tipLocation.width > windowWidth) left = windowWidth - tipLocation.width - offset; // If tooltip goes beyond left viewport boundary adjust left position to fit if (left < 0) left = offset; } else if (position === 'bottom') { if (windowHeight - (targetPosition.Top + targetPosition.Height) >= tipLocation.height + offset) { effectivePosition = 'bottom'; top = targetPosition.Top + targetPosition.Height + offset; } else { effectivePosition = 'top'; top = targetPosition.Top - tipLocation.height - offset; } left = targetPosition.Left + 0.5 * targetPosition.Width - 0.5 * tipLocation.width; //If tooltip goes beyond right viewport boundary adjust left position to fit if (left + tipLocation.width >= windowWidth) left = windowWidth - tipLocation.width - offset; //If tooltip goes beyond left viewport boundary adjust left position to fit if (left <= 0) left = offset; } var arrowPositionPercent = 50; // Default to center if (position === 'top' || position === 'bottom') { var targetCenter = targetPosition.Left + 0.5 * targetPosition.Width; arrowPositionPercent = ((targetCenter - left) / tipLocation.width) * 100; } else if (position === 'left' || position === 'right') { var targetCenter = targetPosition.Top + 0.5 * targetPosition.Height; arrowPositionPercent = ((targetCenter - top) / tipLocation.height) * 100; } return [top, left, arrowPositionPercent, effectivePosition]; }; exports.default = exports.Tooltip; var templateObject_1, templateObject_2;