UNPKG

9.16 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _react = require('react');
8
9var _react2 = _interopRequireDefault(_react);
10
11var _propTypes = require('prop-types');
12
13var _propTypes2 = _interopRequireDefault(_propTypes);
14
15var _bind = require('classnames/bind');
16
17var _bind2 = _interopRequireDefault(_bind);
18
19var _style = require('./style.scss');
20
21var _style2 = _interopRequireDefault(_style);
22
23var _OptClass = require('../internal/OptClass');
24
25var _OptClass2 = _interopRequireDefault(_OptClass);
26
27function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
29function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
30
31function _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; }
32
33function _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; }
34
35var Tooltip = function (_React$Component) {
36 _inherits(Tooltip, _React$Component);
37
38 function Tooltip(props) {
39 _classCallCheck(this, Tooltip);
40
41 var _this = _possibleConstructorReturn(this, (Tooltip.__proto__ || Object.getPrototypeOf(Tooltip)).call(this, props));
42
43 _this.state = {
44 showing: false
45 };
46
47 _this.componentDidMount = function () {
48 if (_this.props.show) {
49 setTimeout(function () {
50 _this.showTip();
51 }, 1000);
52 }
53
54 _this.renderTipWrapper();
55 };
56
57 _this.UNSAFE_componentWillReceiveProps = function (nextProps) {
58 // If not currently showing and should show or already showing but the content changed
59 if (nextProps.show && (!_this.state.showing || nextProps.content !== _this.props.content)) {
60 _this.showTip();
61 }
62 // If currently showing and should not show
63 else if (_this.state.showing && !nextProps.show) {
64 _this.hideTip();
65 }
66 };
67
68 _this.componentWillUnmount = function () {
69 _this.hideTip();
70 };
71
72 _this.renderTipWrapper = function () {
73 // Look for an existing reference
74 var tipNode = _this.nodeReference();
75
76 // If none exists
77 if (!tipNode) {
78 // Create the wrapper node
79 tipNode = document.createElement('div');
80
81 // Add the CSS hook
82 tipNode.setAttribute('class', _style2.default['tip-wrapper']);
83
84 // Set the DOM reference
85 tipNode.setAttribute('id', _this.props.tipWrapper);
86
87 document.getElementById(_this.props.appWrapper).appendChild(tipNode);
88 }
89 };
90
91 _this.getTipElementBoundingRect = function () {
92 return _this._tipElement.getBoundingClientRect();
93 };
94
95 _this.tooltipPlacement = function () {
96 var tipRect = _this.getTipElementBoundingRect();
97
98 _this._tooltipPlacement = {};
99 _this._tooltipPlacement.translate = tipRect.width / 2;
100
101 switch (_this.props.tooltipPlacement) {
102 case 'bottom':
103 _this._tooltipPlacement.left = tipRect.left + (tipRect.right - tipRect.left) / 2;
104 _this._tooltipPlacement.top = tipRect.bottom;
105 break;
106 case 'right':
107 _this._tooltipPlacement.left = tipRect.right;
108 _this._tooltipPlacement.top = tipRect.top + (tipRect.bottom - tipRect.top) / 2;
109 break;
110 case 'left':
111 _this._tooltipPlacement.left = tipRect.left;
112 _this._tooltipPlacement.top = tipRect.top + (tipRect.bottom - tipRect.top) / 2;
113 break;
114 default:
115 _this._tooltipPlacement.left = tipRect.left + (tipRect.right - tipRect.left) / 2;
116 _this._tooltipPlacement.top = tipRect.top;
117 }
118 };
119
120 _this.showTip = function () {
121 if (!_this.props.detectEllipsis || _this.isEllipsisActive()) {
122 // We set the placement each time the user hovers over a tooltip-bound element
123 _this.tooltipPlacement();
124
125 _this.setState({ showing: true }, function () {
126 _this.renderTooltip();
127 });
128 }
129 };
130
131 _this.hideTip = function () {
132 _this.setState({ showing: false });
133
134 // Get the node
135 var tipNode = _this.nodeReference();
136
137 // Re-assign the wrapper style
138 // because we blow away the classnames
139 tipNode.setAttribute('class', _style2.default['tip-wrapper']);
140
141 // Set the position to it's original (off screen)
142 tipNode.setAttribute('style', 'top: -300px; left: -300px;');
143 };
144
145 _this.getTranslate = function () {
146 return _this._tooltipPlacement.translate + 'px';
147 };
148
149 _this.getComputedStyle = function (propVal) {
150 // getComputedStyle allows us to access a node's CSS values
151 return window.getComputedStyle(_this._tipElement, null).getPropertyValue(propVal);
152 };
153
154 _this.isEllipsisActive = function () {
155 var clone = _this._tipElement.cloneNode();
156
157 // Returns the CSS values for properties
158 // that affect the element's width
159 var cloneFontSize = _this.getComputedStyle('font-size');
160 var cloneFontWeight = _this.getComputedStyle('font-weight');
161 var cloneTextTransform = _this.getComputedStyle('text-transform');
162
163 // Inline the values, with visibility: hidden
164 clone.setAttribute('style', 'display: inline; width: auto; visibility: hidden; font-size: ' + cloneFontSize + '; font-weight: ' + cloneFontWeight + '; text-transform: ' + cloneTextTransform);
165 clone.textContent = _this._tipElement.textContent;
166
167 // Append the node so we can read the DOM attributes
168 document.body.appendChild(clone);
169
170 // Detect whether the hidden node width is wider than the reference element
171 var isEllipsized = clone.offsetWidth > _this._tipElement.offsetWidth;
172
173 // Remove the clone
174 document.body.removeChild(clone);
175
176 return isEllipsized;
177 };
178
179 _this.nodeReference = function () {
180 return document.getElementById(_this.props.tipWrapper);
181 };
182
183 _this.renderTooltip = function () {
184 var tipNode = _this.nodeReference();
185 var tipShowingClass = _this.state.showing ? _style2.default['tip-showing'] : null;
186 var tipClass = (0, _OptClass2.default)(_style2.default, ['tip-wrapper', 'is-visible', _this.props.optClass, tipShowingClass, _this.props.tooltipPlacement]);
187 var styles = _this.state.showing && 'top: ' + (_this._tooltipPlacement.top + window.pageYOffset) + 'px; left: ' + (_this._tooltipPlacement.left + window.pageXOffset) + 'px;';
188
189 tipNode.setAttribute('style', styles);
190 tipNode.className = tipClass;
191 tipNode.textContent = _this.props.content;
192 };
193
194 _this.handleMouseOver = function () {
195 _this.showTip();
196 _this.props.mouseOverCallback && _this.props.mouseOverCallback();
197 };
198
199 _this.handleMouseOut = function () {
200 if (!_this.props.show) _this.hideTip();
201 _this.props.mouseOutCallback && _this.props.mouseOutCallback();
202 };
203
204 _this.render = function () {
205 return _react2.default.createElement(
206 'span',
207 { onMouseOver: _this.handleMouseOver, onMouseOut: _this.handleMouseOut, ref: function ref(c) {
208 return _this._tipElement = c;
209 }, className: 'tooltip-node-wrapper' },
210 _this.props.children
211 );
212 };
213
214 return _this;
215 }
216
217 /**
218 * Helper function to return the tooltip wrapper
219 * Note: a future implmementation might allow for a node to
220 * be passed in here, to allow for a custom tooltip wrapper
221 */
222
223
224 return Tooltip;
225}(_react2.default.Component);
226
227Tooltip.propTypes = {
228 /**
229 * The ID of the app wrapper.
230 */
231 appWrapper: _propTypes2.default.string,
232 /**
233 * The content to display inside the `Tooltip`.
234 */
235 content: _propTypes2.default.string,
236 /**
237 * Optional styles to add to the tooltip.
238 */
239 optClass: _propTypes2.default.string,
240 /**
241 * The placement of the tooltip.
242 */
243 tooltipPlacement: _propTypes2.default.oneOf(['left', 'right', 'top', 'bottom']),
244 /**
245 * Whether to show the tooltip element by default.
246 */
247 show: _propTypes2.default.bool,
248 /**
249 * ID to use for referencing the tooltip (default: tip-wrapper)
250 */
251 tipWrapper: _propTypes2.default.string,
252 /**
253 * When set to true, the tooltip will only appear if the tip wrapper
254 * is ellipsized
255 */
256 detectEllipsis: _propTypes2.default.bool,
257 /**
258 * Callback to call when mouseover is called.
259 */
260 mouseOverCallback: _propTypes2.default.func,
261 /**
262 * Callback to call when mouseout is called.
263 */
264 mouseOutCallback: _propTypes2.default.func
265};
266Tooltip.defaultProps = {
267 appWrapper: 'app',
268 tooltipPlacement: 'top',
269 tipWrapper: 'tip-wrapper'
270};
271exports.default = Tooltip;
\No newline at end of file