@gpa-gemstone/react-forms
Version:
React Form modules for gpa webapps
190 lines (189 loc) • 12.2 kB
JavaScript
// ******************************************************************************************************
// 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;
;