@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
214 lines • 8.76 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import React from 'react';
import { isTrue } from '../../shared/component-helper';
import { getOffsetLeft, getOffsetTop } from '../../shared/helpers';
import classnames from 'classnames';
export default function TooltipContainer(props) {
const {
internalId,
active,
attributes,
arrow,
position,
align,
hideDelay,
fixedPosition,
noAnimation,
skipPortal,
useHover,
children,
targetElement: target
} = props;
const [style, setStyle] = React.useState(null);
const [arrowStyle, setArrowStyle] = React.useState(null);
const [hover, setHover] = React.useState(false);
const isActive = isTrue(active) || hover;
const [wasActive, makeActive] = React.useState(false);
const [renewStyles, forceRerender] = React.useState(getBodySize);
const elementRef = React.useRef(null);
const offset = React.useRef(16);
const debounceTimeout = React.useRef();
const resizeObserver = React.useRef(null);
function getBodySize() {
if (!isActive || typeof document === 'undefined') {
return 0;
}
const {
width,
height
} = document.body.getBoundingClientRect();
return width + height;
}
const clearTimers = () => {
clearTimeout(debounceTimeout.current);
};
React.useLayoutEffect(() => {
const addPositionObserver = () => {
if (resizeObserver.current || typeof document === 'undefined') {
return;
}
try {
resizeObserver.current = new ResizeObserver(() => {
clearTimers();
debounceTimeout.current = setTimeout(() => forceRerender(getBodySize()), 100);
});
resizeObserver.current.observe(document.body);
} catch (e) {}
};
const removePositionObserver = () => {
var _resizeObserver$curre;
clearTimers();
(_resizeObserver$curre = resizeObserver.current) === null || _resizeObserver$curre === void 0 ? void 0 : _resizeObserver$curre.disconnect();
};
if (isActive) {
makeActive(true);
addPositionObserver();
} else {
removePositionObserver();
}
return removePositionObserver;
}, [isActive]);
const offsetLeft = React.useRef(0);
const offsetTop = React.useRef(0);
React.useLayoutEffect(() => {
var _stylesFromPosition$p, _stylesFromArrow$arro;
if (!isActive) {
if (wasActive) {
clearTimeout(debounceTimeout.current);
debounceTimeout.current = setTimeout(() => setStyle(null), hideDelay);
}
return;
}
const element = elementRef === null || elementRef === void 0 ? void 0 : elementRef.current;
if (typeof window === 'undefined' || !element || !(target !== null && target !== void 0 && target.getBoundingClientRect)) {
return;
}
let alignOffset = 0;
const elementWidth = element.offsetWidth;
const elementHeight = element.offsetHeight;
const rect = target.getBoundingClientRect();
const targetBodySize = {
width: target.offsetWidth,
height: target.offsetHeight
};
if (!target.offsetHeight) {
targetBodySize.width = rect.width;
targetBodySize.height = rect.height;
}
if (skipPortal && (!offsetLeft.current || !offsetTop.current)) {
offsetLeft.current = getOffsetLeft(element) - offset.current;
offsetTop.current = getOffsetTop(element) - offset.current;
}
const scrollY = window.scrollY !== undefined ? window.scrollY : window.pageYOffset;
const scrollX = window.scrollX !== undefined ? window.scrollX : window.pageXOffset;
const top = (isTrue(fixedPosition) ? 0 : scrollY) + rect.top - offsetTop.current;
const useMouseWhen = targetBodySize.width > 400;
const mousePos = getOffsetLeft(target) + rect.left / 2 + (element ? element.offsetWidth : 0);
const widthBased = scrollX + rect.left;
const left = (useMouseWhen && mousePos < targetBodySize.width ? mousePos : widthBased) - offsetLeft.current;
const style = _objectSpread({}, props.style);
const arrowStyle = {
top: null,
left: null
};
if (align === 'left') {
alignOffset = -targetBodySize.width / 2;
} else if (align === 'right') {
alignOffset = targetBodySize.width / 2;
}
const topHorizontal = top + targetBodySize.height / 2 - elementHeight / 2;
const leftVertical = left - elementWidth / 2 + targetBodySize.width / 2 + alignOffset;
const stylesFromPosition = {
left: () => {
style.top = topHorizontal;
style.left = left - elementWidth - offset.current;
},
right: () => {
style.top = topHorizontal;
style.left = left + targetBodySize.width + offset.current;
},
top: () => {
style.left = leftVertical;
style.top = top - elementHeight - offset.current;
},
bottom: () => {
style.left = leftVertical;
style.top = top + targetBodySize.height + offset.current;
}
};
const stylesFromArrow = {
left: () => {
style.left = left + targetBodySize.width / 2 - offset.current + alignOffset;
},
right: () => {
style.left = left - elementWidth + targetBodySize.width / 2 + offset.current + alignOffset;
},
top: () => {
style.top = top + targetBodySize.height / 2 - offset.current;
},
bottom: () => {
style.top = top + targetBodySize.height / 2 - elementHeight + offset.current;
}
};
(_stylesFromPosition$p = stylesFromPosition[position]) === null || _stylesFromPosition$p === void 0 ? void 0 : _stylesFromPosition$p.call(stylesFromPosition);
(_stylesFromArrow$arro = stylesFromArrow[arrow]) === null || _stylesFromArrow$arro === void 0 ? void 0 : _stylesFromArrow$arro.call(stylesFromArrow);
const rightOffset = parseFloat(String(style.left)) + elementWidth - window.innerWidth;
if (rightOffset > 0) {
style.left = window.innerWidth - elementWidth;
}
if (parseFloat(String(style.left)) < 0) {
style.left = 0;
if (position === 'top' || position === 'bottom') {
const arrowWidth = 16;
const arrowStyleBasisWidth = left - arrowWidth / 2;
if (align === 'left') {
arrowStyle.left = arrowStyleBasisWidth;
} else if (align === 'right') {
arrowStyle.left = arrowStyleBasisWidth + targetBodySize.width;
} else {
arrowStyle.left = arrowStyleBasisWidth + targetBodySize.width / 2;
}
}
}
if (parseFloat(String(style.top)) < 0) {
style.top = 0;
arrowStyle.top = 0;
}
setStyle(style);
setArrowStyle(arrowStyle);
}, [active, arrow, position, children, renewStyles]);
const handleMouseEnter = () => {
if (isTrue(active) && useHover !== false) {
setHover(true);
}
};
const handleMouseLeave = () => {
if (useHover !== false) {
setHover(false);
}
};
const handlePropagation = e => e.stopPropagation();
return React.createElement("span", _extends({
role: "tooltip",
"aria-hidden": target ? true : undefined,
ref: elementRef,
onMouseEnter: handleMouseEnter,
onMouseLeave: handleMouseLeave,
onMouseMove: handlePropagation,
onMouseDown: handlePropagation,
onTouchStart: handlePropagation
}, attributes, {
className: classnames(attributes === null || attributes === void 0 ? void 0 : attributes.className, isActive ? 'dnb-tooltip--active' : wasActive && 'dnb-tooltip--hide', isTrue(noAnimation) && 'dnb-tooltip--no-animation', isTrue(fixedPosition) && 'dnb-tooltip--fixed'),
style: _objectSpread(_objectSpread({}, style), attributes.style)
}), arrow && React.createElement("span", {
className: `dnb-tooltip__arrow dnb-tooltip__arrow__arrow--${arrow} dnb-tooltip__arrow__position--${position}`,
style: _objectSpread({}, arrowStyle)
}), React.createElement("span", {
id: internalId,
className: "dnb-tooltip__content"
}, children));
}
//# sourceMappingURL=TooltipContainer.js.map