UNPKG

ndla-ui

Version:

UI component library for NDLA.

187 lines (167 loc) 7.83 kB
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** * Copyright (c) 2016-present, NDLA. * * This source code is licensed under the GPLv3 license found in the * LICENSE file in the root directory of this source tree. * */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import BEMHelper from 'react-bem-helper'; import { isMobile, isIE } from 'react-device-detect'; import { Fade } from '../Animation'; var classes = new BEMHelper({ name: 'tooltip', prefix: 'c-' }); var Tooltip = function (_Component) { _inherits(Tooltip, _Component); function Tooltip(props) { _classCallCheck(this, Tooltip); var _this = _possibleConstructorReturn(this, (Tooltip.__proto__ || Object.getPrototypeOf(Tooltip)).call(this, props)); _this.state = { showTooltip: false }; _this.handleShowTooltip = _this.handleShowTooltip.bind(_this); _this.handleHideTooltip = _this.handleHideTooltip.bind(_this); _this.handleKeyPress = _this.handleKeyPress.bind(_this); _this.contentRef = React.createRef(); _this.tooltipRef = React.createRef(); _this.widthRef = 0; _this.heightRef = 0; _this.leftRef = 0; _this.tooltipRefWidth = props.tooltip ? props.tooltip.length * 5 : 0; // Estimate incase user only uses keyboard navigation. _this.currentStyles = {}; return _this; } _createClass(Tooltip, [{ key: 'getPosition', value: function getPosition() { if (this.state.showTooltip) { this.currentStyles = {}; this.widthRef = this.contentRef.current.offsetWidth; this.heightRef = this.contentRef.current.offsetHeight; var elementRect = this.contentRef.current.getBoundingClientRect(); this.leftRef = elementRect.left; var tooltipWidth = this.tooltipRef.current ? this.tooltipRef.current.offsetWidth : this.tooltipRefWidth; this.tooltipRefWidth = tooltipWidth; if (isIE) { // IE is bad with transform % + px.. this.currentStyles.left = '-' + (this.tooltipRef.current.offsetWidth - this.widthRef) / 2 + 'px'; this.currentStyles.top = '-' + (this.tooltipRef.current.offsetHeight + 10) + 'px'; } else if (this.props.align === 'top' || this.props.align === 'bottom' || this.props.align === 'left' && this.leftRef - tooltipWidth < 20 || this.props.align === 'right' && this.leftRef + this.widthRef + tooltipWidth > window.innerWidth - 40) { var centeredLeft = this.leftRef + this.widthRef / 2; var moveHorizontal = Math.max(centeredLeft + tooltipWidth / 2 + 20 - window.innerWidth, 0); if (moveHorizontal === 0) { moveHorizontal = Math.min(-(tooltipWidth / 2 - centeredLeft + 20), 0); } if (this.props.align === 'bottom') { this.currentStyles.transform = 'translate(calc(-50% + ' + (this.widthRef / 2 - moveHorizontal) + 'px), calc(' + this.heightRef + 'px + 0.25rem))'; } else { this.currentStyles.transform = 'translate(calc(-50% + ' + (this.widthRef / 2 - moveHorizontal) + 'px), calc(-100% - 0.25rem))'; } } else if (this.props.align === 'left') { this.currentStyles.transform = 'translate(calc(-100% - 0.25rem), calc(-50% + ' + this.heightRef / 2 + 'px))'; } else { this.currentStyles.transform = 'translate(calc(' + this.widthRef + 'px + 0.25rem), calc(-50% + ' + this.heightRef / 2 + 'px))'; } } return this.currentStyles; } }, { key: 'handleShowTooltip', value: function handleShowTooltip() { this.setState({ showTooltip: !this.props.disabled }); } }, { key: 'handleHideTooltip', value: function handleHideTooltip() { this.setState({ showTooltip: false }); } }, { key: 'handleKeyPress', value: function handleKeyPress(e) { if (e.key === 'Enter') { try { this.contentRef.current.querySelectorAll('[type="button"], a')[0].click(); } catch (err) { console.log('error', err); // eslint-disable-line no-console } } } }, { key: 'render', value: function render() { // If phone ignore all tooltips // if (isMobile) { return React.createElement( 'div', { className: classes('').className + ' ' + this.props.tooltipContainerClass }, React.createElement( 'span', { className: 'c-tooltip__content ' + this.props.className }, this.props.children ) ); } return React.createElement( 'div', { className: classes('').className + ' ' + this.props.tooltipContainerClass }, React.createElement( Fade, { 'in': this.state.showTooltip, delay: this.props.delay }, React.createElement( 'span', _extends({ role: 'tooltip' }, classes('tooltip'), { style: this.getPosition(), ref: this.tooltipRef }), this.props.tooltip ) ), React.createElement( 'span', { role: 'button', tabIndex: 0, 'aria-label': this.props.tooltip, ref: this.contentRef, onMouseEnter: this.handleShowTooltip, onMouseOut: this.handleHideTooltip, onMouseMove: this.handleShowTooltip, onFocus: this.handleShowTooltip, onKeyPress: this.handleKeyPress, onBlur: this.handleHideTooltip, className: 'c-tooltip__content ' + this.props.className }, this.props.children ) ); } }]); return Tooltip; }(Component); Tooltip.propTypes = { children: PropTypes.node.isRequired, tooltip: PropTypes.string.isRequired, delay: PropTypes.number, disabled: PropTypes.bool, align: PropTypes.oneOf(['left', 'right', 'top', 'bottom']), className: PropTypes.string, tooltipContainerClass: PropTypes.string }; Tooltip.defaultProps = { align: 'top', disabled: false, delay: 0, className: '', tooltipContainerClass: '' }; export default Tooltip;