@orfeas126/box-ui-elements
Version:
Box UI Elements
302 lines (300 loc) • 10.7 kB
JavaScript
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import * as React from 'react';
import classNames from 'classnames';
import uniqueId from 'lodash/uniqueId';
import getProp from 'lodash/get';
import TetherComponent from 'react-tether';
import TetherPosition from '../../common/tether-positions';
import CloseButton from './CloseButton';
import './Tooltip.scss';
export let TooltipTheme = /*#__PURE__*/function (TooltipTheme) {
TooltipTheme["CALLOUT"] = "callout";
TooltipTheme["DEFAULT"] = "default";
TooltipTheme["ERROR"] = "error";
return TooltipTheme;
}({});
export let TooltipPosition = /*#__PURE__*/function (TooltipPosition) {
TooltipPosition["BOTTOM_CENTER"] = "bottom-center";
TooltipPosition["BOTTOM_LEFT"] = "bottom-left";
TooltipPosition["BOTTOM_RIGHT"] = "bottom-right";
TooltipPosition["MIDDLE_LEFT"] = "middle-left";
TooltipPosition["MIDDLE_RIGHT"] = "middle-right";
TooltipPosition["TOP_CENTER"] = "top-center";
TooltipPosition["TOP_LEFT"] = "top-left";
TooltipPosition["TOP_RIGHT"] = "top-right";
return TooltipPosition;
}({});
const positions = {
[TooltipPosition.BOTTOM_CENTER]: {
attachment: TetherPosition.TOP_CENTER,
targetAttachment: TetherPosition.BOTTOM_CENTER
},
[TooltipPosition.BOTTOM_LEFT]: {
attachment: TetherPosition.TOP_RIGHT,
targetAttachment: TetherPosition.BOTTOM_RIGHT
},
[TooltipPosition.BOTTOM_RIGHT]: {
attachment: TetherPosition.TOP_LEFT,
targetAttachment: TetherPosition.BOTTOM_LEFT
},
[TooltipPosition.MIDDLE_LEFT]: {
attachment: TetherPosition.MIDDLE_RIGHT,
targetAttachment: TetherPosition.MIDDLE_LEFT
},
[TooltipPosition.MIDDLE_RIGHT]: {
attachment: TetherPosition.MIDDLE_LEFT,
targetAttachment: TetherPosition.MIDDLE_RIGHT
},
[TooltipPosition.TOP_CENTER]: {
attachment: TetherPosition.BOTTOM_CENTER,
targetAttachment: TetherPosition.TOP_CENTER
},
[TooltipPosition.TOP_LEFT]: {
attachment: TetherPosition.BOTTOM_RIGHT,
targetAttachment: TetherPosition.TOP_RIGHT
},
[TooltipPosition.TOP_RIGHT]: {
attachment: TetherPosition.BOTTOM_LEFT,
targetAttachment: TetherPosition.TOP_LEFT
}
};
class Tooltip extends React.Component {
constructor(props) {
super(props);
_defineProperty(this, "tooltipID", uniqueId('tooltip'));
_defineProperty(this, "tetherRef", /*#__PURE__*/React.createRef());
// Instance API: Forces the radar to be repositioned
_defineProperty(this, "position", () => {
if (this.tetherRef.current && this.isShown()) {
this.tetherRef.current.position();
}
});
_defineProperty(this, "closeTooltip", () => {
const {
onDismiss
} = this.props;
this.setState({
wasClosedByUser: true
});
if (onDismiss) {
onDismiss();
}
});
_defineProperty(this, "fireChildEvent", (type, event) => {
const {
children
} = this.props;
const handler = children.props[type];
if (handler) {
handler(event);
}
});
_defineProperty(this, "handleTooltipEvent", event => {
event.stopPropagation();
event.nativeEvent.stopImmediatePropagation();
});
_defineProperty(this, "handleMouseEnter", event => {
this.setState({
isShown: true
});
this.fireChildEvent('onMouseEnter', event);
});
_defineProperty(this, "handleMouseLeave", event => {
this.setState({
isShown: false
});
this.fireChildEvent('onMouseLeave', event);
});
_defineProperty(this, "handleFocus", event => {
this.setState({
isShown: true
});
this.fireChildEvent('onFocus', event);
});
_defineProperty(this, "handleBlur", event => {
this.setState({
isShown: false
});
this.fireChildEvent('onBlur', event);
});
_defineProperty(this, "isControlled", () => {
const {
isShown: isShownProp
} = this.props;
return typeof isShownProp !== 'undefined';
});
_defineProperty(this, "handleKeyDown", event => {
if (event.key === 'Escape') {
event.stopPropagation();
this.setState({
isShown: false
});
}
this.fireChildEvent('onKeyDown', event);
});
_defineProperty(this, "isShown", () => {
const {
isShown: isShownProp
} = this.props;
const isControlled = this.isControlled();
const isShown = isControlled ? isShownProp : this.state.isShown;
const showTooltip = isShown && !this.state.wasClosedByUser && this.state.hasRendered;
return showTooltip;
});
this.state = {
isShown: !!props.isShown,
hasRendered: false,
wasClosedByUser: false
};
}
componentDidMount() {
this.setState({
hasRendered: true
});
}
componentDidUpdate(prevProps, prevState) {
const isControlled = this.isControlled();
// Reset wasClosedByUser state when isShown transitions from false to true
if (isControlled) {
if (!prevProps.isShown && this.props.isShown) {
this.setState({
wasClosedByUser: false
});
}
} else {
if (!prevState.isShown && this.state.isShown) {
// capture event so that tooltip closes before any other floating components that can be closed by
// "Escape" key(e.g. Modal, Menu, etc.)
document.addEventListener('keydown', this.handleKeyDown, true);
}
if (prevState.isShown && !this.state.isShown) {
document.removeEventListener('keydown', this.handleKeyDown, true);
}
}
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeyDown, true);
}
render() {
const {
ariaHidden,
bodyElement,
children,
className,
constrainToScrollParent,
constrainToWindow,
isDisabled,
isTabbable = true,
offset,
position = TooltipPosition.TOP_CENTER,
showCloseButton,
stopBubble,
tetherElementClassName,
text,
theme
} = this.props;
const childAriaLabel = getProp(children, 'props.aria-label');
const isLabelMatchingTooltipText = !!childAriaLabel && childAriaLabel === text;
// If the tooltip is disabled just render the children
if (isDisabled) {
return React.Children.only(children);
}
const isControlled = this.isControlled();
const showTooltip = this.isShown();
const withCloseButton = showCloseButton && isControlled;
const tetherPosition = typeof position === 'string' ? positions[position] : position;
const constraints = [];
const componentProps = {};
if (constrainToScrollParent) {
constraints.push({
to: 'scrollParent',
attachment: 'together'
});
}
if (constrainToWindow) {
constraints.push({
to: 'window',
attachment: 'together'
});
}
if (showTooltip && !ariaHidden) {
if (!isLabelMatchingTooltipText || childAriaLabel === undefined) {
componentProps['aria-describedby'] = this.tooltipID;
}
if (theme === TooltipTheme.ERROR) {
componentProps['aria-errormessage'] = this.tooltipID;
}
}
if (!isControlled) {
componentProps.onBlur = this.handleBlur;
componentProps.onFocus = this.handleFocus;
componentProps.onMouseEnter = this.handleMouseEnter;
componentProps.onMouseLeave = this.handleMouseLeave;
if (isTabbable) {
componentProps.tabIndex = '0';
}
}
const bodyEl = bodyElement instanceof HTMLElement ? bodyElement : document.body;
const classes = classNames('tooltip', 'bdl-Tooltip', className, {
'is-callout': theme === TooltipTheme.CALLOUT,
'is-error': theme === TooltipTheme.ERROR,
'with-close-button': withCloseButton
});
const tetherProps = {
attachment: tetherPosition.attachment,
bodyElement: bodyEl,
classPrefix: 'tooltip',
constraints,
enabled: showTooltip,
targetAttachment: tetherPosition.targetAttachment
};
if (tetherElementClassName) {
tetherProps.className = tetherElementClassName;
}
if (offset) {
tetherProps.offset = offset;
}
const tooltipInner = /*#__PURE__*/React.createElement(React.Fragment, null, text, withCloseButton && /*#__PURE__*/React.createElement(CloseButton, {
onClick: this.closeTooltip
}));
const tooltip = stopBubble ? /*#__PURE__*/React.createElement("div", {
className: classes,
id: this.tooltipID,
onClick: this.handleTooltipEvent,
onContextMenu: this.handleTooltipEvent,
onKeyPress: this.handleTooltipEvent,
onMouseEnter: this.handleMouseEnter,
onMouseLeave: this.handleMouseLeave,
role: "presentation"
}, /*#__PURE__*/React.createElement("div", {
role: theme === TooltipTheme.ERROR ? undefined : 'tooltip',
"aria-live": "polite",
"aria-hidden": ariaHidden || isLabelMatchingTooltipText,
"data-testid": "bdl-Tooltip"
}, tooltipInner)) : /*#__PURE__*/React.createElement("div", {
"aria-live": "polite",
"aria-hidden": ariaHidden || isLabelMatchingTooltipText,
className: classes,
"data-testid": "bdl-Tooltip",
id: this.tooltipID,
onMouseEnter: this.handleMouseEnter,
onMouseLeave: this.handleMouseLeave,
role: theme === TooltipTheme.ERROR ? undefined : 'tooltip'
}, tooltipInner);
return /*#__PURE__*/React.createElement(TetherComponent, _extends({
ref: this.tetherRef
}, tetherProps), /*#__PURE__*/React.cloneElement(React.Children.only(children), componentProps), showTooltip && tooltip);
}
}
_defineProperty(Tooltip, "defaultProps", {
constrainToScrollParent: false,
constrainToWindow: true,
isDisabled: false,
position: TooltipPosition.TOP_CENTER,
theme: TooltipTheme.DEFAULT
});
export default Tooltip;
//# sourceMappingURL=Tooltip.js.map