UNPKG

chayns-components

Version:

A set of beautiful React components for developing chayns® applications.

305 lines (302 loc) 9.23 kB
/** * @component */ import classNames from 'clsx'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import Bubble from '../../react-chayns-bubble/component/Bubble'; import Button from '../../react-chayns-button/component/Button'; import Icon from '../../react-chayns-icon/component/Icon'; let currentId = 0; const PREFIX = 'CC_TOOLTIP_'; /** * Wraps a child component and displays a message when the child is hovered or * clicked on. Allows to be shown imperatively by calling `.show()` or `.hide()` * on its reference. */ export default class Tooltip extends Component { constructor(props) { super(props); this.state = { position: null, x: 0, y: 0 }; this.show = this.show.bind(this); this.hide = this.hide.bind(this); this.getContent = this.getContent.bind(this); this.getPosition = this.getPosition.bind(this); this.tooltipKey = `${PREFIX}${currentId++}`; this.bubble = /*#__PURE__*/React.createRef(); this.childrenWrapper = /*#__PURE__*/React.createRef(); } componentDidMount() { this.getPosition(); } componentDidUpdate(prevProps) { const { coordinates, position } = this.props; if (prevProps.coordinates !== coordinates || prevProps.position !== position) { this.getPosition(); } } getContent() { const { content } = this.props; if (content.html) { return content.html; } const nodeArray = [/*#__PURE__*/React.createElement("p", { key: `p${this.tooltipKey}` }, content.text)]; if (content.imageUrl) { nodeArray.unshift( /*#__PURE__*/React.createElement("div", { key: `divImg${this.tooltipKey}`, className: "cc__tooltip__image", style: { backgroundImage: `url(${content.imageUrl})` } })); } if (content.headline) { nodeArray.unshift( /*#__PURE__*/React.createElement("h5", { key: `h5${this.tooltipKey}` }, content.headline)); } if (content.buttonText && content.buttonOnClick) { nodeArray.push( /*#__PURE__*/React.createElement("div", { className: "cc__tooltip__button", key: `divBtn${this.tooltipKey}` }, /*#__PURE__*/React.createElement(Button, { onClick: content.buttonOnClick }, content.buttonText))); } return nodeArray; } async getPosition() { var _chayns$env; const { position, coordinates, parent, removeParentSpace } = this.props; const { position: statePosition, x: stateX, y: stateY } = this.state; let x = coordinates ? coordinates.x : 0; let top = coordinates ? coordinates.y : 0; let bottom = coordinates ? coordinates.y : 0; if (this.childrenWrapper && !coordinates) { const rect = this.childrenWrapper.current.getBoundingClientRect(); x = rect.left + rect.width / 2; top = rect.top; bottom = rect.bottom; } let pos = position; if (position === null) { const posArray = x > window.innerWidth / 2 ? [Tooltip.position.TOP_LEFT, Tooltip.position.BOTTOM_LEFT] : [Tooltip.position.TOP_RIGHT, Tooltip.position.BOTTOM_RIGHT]; pos = (top + bottom) / 2 > window.innerHeight / 2 ? posArray[0] : posArray[1]; } let y = Bubble.isPositionBottom(pos) ? bottom : top; if (typeof chayns !== 'undefined' && (_chayns$env = chayns.env) !== null && _chayns$env !== void 0 && _chayns$env.isApp) { const { pageYOffset } = await chayns.getWindowMetrics(); y += pageYOffset; } if (removeParentSpace) { const parentRect = (parent || document.getElementsByClassName('tapp')[0] || document.body).getBoundingClientRect(); x -= parentRect.left; y -= parentRect.top; } if (statePosition !== pos || x !== stateX || y !== stateY) { this.setState({ position: pos, x, y }); } } show() { this.getPosition(); if (this.bubble.current) { this.bubble.current.show(); } } hide(ev) { const { stopPropagation } = this.props; if (ev && stopPropagation) { ev.stopPropagation(); } if (this.bubble.current) { this.bubble.current.hide(); } } render() { var _chayns$env$isIOS, _chayns$env2; const { children, parent, childrenStyle, preventTriggerStyle, childrenClassNames, removeIcon, bindListeners, minWidth, maxWidth, hideOnChildrenLeave } = this.props; const { position, x, y } = this.state; const isIOS = typeof chayns !== 'undefined' ? (_chayns$env$isIOS = (_chayns$env2 = chayns.env) === null || _chayns$env2 === void 0 ? void 0 : _chayns$env2.isIOS) !== null && _chayns$env$isIOS !== void 0 ? _chayns$env$isIOS : false : false; const showRemoveIcon = removeIcon !== null && removeIcon !== void 0 ? removeIcon : isIOS; return [position !== null ? /*#__PURE__*/React.createElement(Bubble, { coordinates: { x, y }, parent: parent, position: position, onMouseEnter: bindListeners ? this.show : null, onMouseLeave: bindListeners ? this.hide : null, style: { minWidth, maxWidth, padding: '12px' }, topDivStyle: { ...(!hideOnChildrenLeave || { userSelect: 'none', pointerEvents: 'none' }) }, key: "bubble", ref: this.bubble }, showRemoveIcon ? /*#__PURE__*/React.createElement("div", { className: "cc__tooltip__icon", onClick: this.hide }, /*#__PURE__*/React.createElement(Icon, { icon: "fa fa-times" })) : null, this.getContent()) : null, /*#__PURE__*/React.createElement("div", { className: classNames('cc__tooltip__children', childrenClassNames, !preventTriggerStyle && 'cc__tooltip__children--trigger'), ref: this.childrenWrapper, key: `cc__tooltip__children${this.tooltipKey}`, style: childrenStyle, onMouseEnter: !isIOS && bindListeners ? this.show : null, onMouseLeave: bindListeners ? this.hide : null, onClick: isIOS && bindListeners ? this.show : null }, children)]; } } Tooltip.position = Bubble.position; Tooltip.propTypes = { /** * The content of the tooltip. Either specify an object with the accepted * properties or render custom elements by passing an object like so: * `{ html: <div /> }`. */ content: PropTypes.oneOfType([PropTypes.shape({ text: PropTypes.string.isRequired, headline: PropTypes.string, imageUrl: PropTypes.string, buttonText: PropTypes.string, buttonOnClick: PropTypes.func }), PropTypes.shape({ html: PropTypes.node.isRequired })]).isRequired, /** * The `ReactNode` the tooltip should refer to. If the `children` node is a * `<span>` or `<p>` element, it will be decorated with a dotted underline. */ children: PropTypes.node, /** * Wether `mouseover` and `mouseleave` listeners should be added to the * children elements, which makes the tooltip automatically appear on hover. */ bindListeners: PropTypes.bool, /** * The position of the tooltip. `0` is top left, `1` is bottom left, `2` is * bottom right and `3` is top right. */ position: PropTypes.number, /** * The minimum width of the tooltip. */ minWidth: PropTypes.number, /** * The maximum width of the tooltip. */ maxWidth: PropTypes.number, /** * Wether the close icon in the top right corner of the tooltip should be * shown. */ removeIcon: PropTypes.bool, /** * A DOM node the tooltip should be rendered into. */ parent: typeof Element !== 'undefined' ? PropTypes.instanceOf(Element) : () => {}, /** * An object with coordinates at which the tooltip should point. */ coordinates: PropTypes.shape({ x: PropTypes.number.isRequired, y: PropTypes.number.isRequired }), /** * A React style object that is applied to the children. */ childrenStyle: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])), /** * A classname string that should be applied to the children. */ childrenClassNames: PropTypes.string, /** * Prevent adding an underline to the children. */ preventTriggerStyle: PropTypes.bool, /** * Hide the tooltip when the cursor leaves the children, even if the cursor * is over the bubble. */ hideOnChildrenLeave: PropTypes.bool, /** * Removes any padding of the page from the tooltip position. This is only * needed when the parent is padded to the page and is relatively * positioned. */ removeParentSpace: PropTypes.bool, /** * Whether to stop propagation for click on close icon */ stopPropagation: PropTypes.bool }; Tooltip.defaultProps = { children: null, bindListeners: false, position: null, minWidth: 100, maxWidth: 250, removeIcon: null, parent: null, coordinates: null, childrenStyle: null, childrenClassNames: null, preventTriggerStyle: false, hideOnChildrenLeave: false, removeParentSpace: false, stopPropagation: false }; Tooltip.displayName = 'Tooltip'; //# sourceMappingURL=Tooltip.js.map