sc-react-ions
Version:
An open source set of React components that implement Ambassador's Design and UX patterns.
280 lines (220 loc) • 9.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _bind = require('classnames/bind');
var _bind2 = _interopRequireDefault(_bind);
var _style = require('./style.scss');
var _style2 = _interopRequireDefault(_style);
var _OptClass = require('../internal/OptClass');
var _OptClass2 = _interopRequireDefault(_OptClass);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
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; }
var Tooltip = function (_React$Component) {
_inherits(Tooltip, _React$Component);
function Tooltip(props) {
_classCallCheck(this, Tooltip);
var _this = _possibleConstructorReturn(this, (Tooltip.__proto__ || Object.getPrototypeOf(Tooltip)).call(this, props));
_this.state = {
showing: false
};
_this.componentDidMount = function () {
if (_this.props.show) {
setTimeout(function () {
_this.showTip();
}, 1000);
}
_this.renderTipWrapper();
};
_this.shouldComponentUpdate = function (nextProps, nextState) {
if (nextProps.show !== _this.props.show) return true;
if (nextProps.tooltipPlacement !== _this.props.tooltipPlacement) return true;
if (nextState.showing !== _this.state.showing) return true;
if (nextProps.content !== _this.props.content) return true;
return false;
};
_this.componentWillReceiveProps = function (nextProps) {
// If not currently showing and should show or already showing but the content changed
if (nextProps.show && (!_this.state.showing || nextProps.content !== _this.props.content)) {
_this.showTip();
}
// If currently showing and should not show
else if (_this.state.showing && !nextProps.show) {
_this.hideTip();
}
};
_this.componentWillUnmount = function () {
_this.hideTip();
};
_this.renderTipWrapper = function () {
// Look for an existing reference
var tipNode = _this.nodeReference();
// If none exists
if (!tipNode) {
// Create the wrapper node
tipNode = document.createElement('div');
// Add the CSS hook
tipNode.setAttribute('class', _style2.default['tip-wrapper']);
// Set the DOM reference
tipNode.setAttribute('id', _this.props.tipWrapper);
document.getElementById(_this.props.appWrapper).appendChild(tipNode);
}
};
_this.getTipElementBoundingRect = function () {
return _this._tipElement.getBoundingClientRect();
};
_this.tooltipPlacement = function () {
var tipRect = _this.getTipElementBoundingRect();
_this._tooltipPlacement = {};
_this._tooltipPlacement.translate = tipRect.width / 2;
switch (_this.props.tooltipPlacement) {
case 'bottom':
_this._tooltipPlacement.left = tipRect.left + (tipRect.right - tipRect.left) / 2;
_this._tooltipPlacement.top = tipRect.bottom;
break;
case 'right':
_this._tooltipPlacement.left = tipRect.right;
_this._tooltipPlacement.top = tipRect.top + (tipRect.bottom - tipRect.top) / 2;
break;
case 'left':
_this._tooltipPlacement.left = tipRect.left;
_this._tooltipPlacement.top = tipRect.top + (tipRect.bottom - tipRect.top) / 2;
break;
default:
_this._tooltipPlacement.left = tipRect.left + (tipRect.right - tipRect.left) / 2;
_this._tooltipPlacement.top = tipRect.top;
}
};
_this.showTip = function () {
if (!_this.props.detectEllipsis || _this.isEllipsisActive()) {
// We set the placement each time the user hovers over a tooltip-bound element
_this.tooltipPlacement();
_this.setState({ showing: true }, function () {
_this.renderTooltip();
});
}
};
_this.hideTip = function () {
_this.setState({ showing: false });
// Get the node
var tipNode = _this.nodeReference();
// Re-assign the wrapper style
// because we blow away the classnames
tipNode.setAttribute('class', _style2.default['tip-wrapper']);
// Set the position to it's original (off screen)
tipNode.setAttribute('style', 'top: -300px; left: -300px;');
};
_this.getTranslate = function () {
return _this._tooltipPlacement.translate + 'px';
};
_this.getComputedStyle = function (propVal) {
// getComputedStyle allows us to access a node's CSS values
return window.getComputedStyle(_this._tipElement, null).getPropertyValue(propVal);
};
_this.isEllipsisActive = function () {
var clone = _this._tipElement.cloneNode();
// Returns the CSS values for properties
// that affect the element's width
var cloneFontSize = _this.getComputedStyle('font-size');
var cloneFontWeight = _this.getComputedStyle('font-weight');
var cloneTextTransform = _this.getComputedStyle('text-transform');
// Inline the values, with visibility: hidden
clone.setAttribute('style', 'display: inline; width: auto; visibility: hidden; font-size: ' + cloneFontSize + '; font-weight: ' + cloneFontWeight + '; text-transform: ' + cloneTextTransform);
clone.textContent = _this._tipElement.textContent;
// Append the node so we can read the DOM attributes
document.body.appendChild(clone);
// Detect whether the hidden node width is wider than the reference element
var isEllipsized = clone.offsetWidth > _this._tipElement.offsetWidth;
// Remove the clone
document.body.removeChild(clone);
return isEllipsized;
};
_this.nodeReference = function () {
return document.getElementById(_this.props.tipWrapper);
};
_this.renderTooltip = function () {
var tipNode = _this.nodeReference();
var tipShowingClass = _this.state.showing ? _style2.default['tip-showing'] : null;
var tipClass = (0, _OptClass2.default)(_style2.default, ['tip-wrapper', 'is-visible', _this.props.optClass, tipShowingClass, _this.props.tooltipPlacement]);
var styles = _this.state.showing && 'top: ' + (_this._tooltipPlacement.top + window.pageYOffset) + 'px; left: ' + (_this._tooltipPlacement.left + window.pageXOffset) + 'px;';
tipNode.setAttribute('style', styles);
tipNode.className = tipClass;
tipNode.textContent = _this.props.content;
};
_this.handleMouseOver = function () {
_this.showTip();
_this.props.mouseOverCallback && _this.props.mouseOverCallback();
};
_this.handleMouseOut = function () {
if (!_this.props.show) _this.hideTip();
_this.props.mouseOutCallback && _this.props.mouseOutCallback();
};
_this.render = function () {
return _react2.default.createElement(
'span',
{ onMouseOver: _this.handleMouseOver, onMouseOut: _this.handleMouseOut, ref: function ref(c) {
return _this._tipElement = c;
} },
_this.props.children
);
};
return _this;
}
/**
* Helper function to return the tooltip wrapper
* Note: a future implmementation might allow for a node to
* be passed in here, to allow for a custom tooltip wrapper
*/
return Tooltip;
}(_react2.default.Component);
Tooltip.propTypes = {
/**
* The ID of the app wrapper.
*/
appWrapper: _propTypes2.default.string,
/**
* The content to display inside the `Tooltip`.
*/
content: _propTypes2.default.string,
/**
* Optional styles to add to the tooltip.
*/
optClass: _propTypes2.default.string,
/**
* The placement of the tooltip.
*/
tooltipPlacement: _propTypes2.default.oneOf(['left', 'right', 'top', 'bottom']),
/**
* Whether to show the tooltip element by default.
*/
show: _propTypes2.default.bool,
/**
* ID to use for referencing the tooltip (default: tip-wrapper)
*/
tipWrapper: _propTypes2.default.string,
/**
* When set to true, the tooltip will only appear if the tip wrapper
* is ellipsized
*/
detectEllipsis: _propTypes2.default.bool,
/**
* Callback to call when mouseover is called.
*/
mouseOverCallback: _propTypes2.default.func,
/**
* Callback to call when mouseout is called.
*/
mouseOutCallback: _propTypes2.default.func
};
Tooltip.defaultProps = {
appWrapper: 'app',
tooltipPlacement: 'top',
tipWrapper: 'tip-wrapper'
};
exports.default = Tooltip;