@wordpress/components
Version:
UI components for WordPress.
230 lines (187 loc) • 6.2 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.TOOLTIP_DELAY = void 0;
var _element = require("@wordpress/element");
var _lodash = require("lodash");
var _compose = require("@wordpress/compose");
var _popover = _interopRequireDefault(require("../popover"));
var _shortcut = _interopRequireDefault(require("../shortcut"));
var _next = require("./next");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
/**
* Time over children to wait before showing tooltip
*
* @type {number}
*/
const TOOLTIP_DELAY = 700;
exports.TOOLTIP_DELAY = TOOLTIP_DELAY;
const eventCatcher = (0, _element.createElement)("div", {
className: "event-catcher"
});
const getDisabledElement = ({
eventHandlers,
child,
childrenWithPopover
}) => (0, _element.cloneElement)((0, _element.createElement)("span", {
className: "disabled-element-wrapper"
}, (0, _element.cloneElement)(eventCatcher, eventHandlers), (0, _element.cloneElement)(child, {
children: childrenWithPopover
}), ","), eventHandlers);
const getRegularElement = ({
child,
eventHandlers,
childrenWithPopover
}) => (0, _element.cloneElement)(child, { ...eventHandlers,
children: childrenWithPopover
});
const addPopoverToGrandchildren = ({
grandchildren,
isOver,
position,
text,
shortcut
}) => (0, _element.concatChildren)(grandchildren, isOver && (0, _element.createElement)(_popover.default, {
focusOnMount: false,
position: position,
className: "components-tooltip",
"aria-hidden": "true",
animate: false,
noArrow: true
}, text, (0, _element.createElement)(_shortcut.default, {
className: "components-tooltip__shortcut",
shortcut: shortcut
})));
const emitToChild = (children, eventName, event) => {
if (_element.Children.count(children) !== 1) {
return;
}
const child = _element.Children.only(children);
if (typeof child.props[eventName] === 'function') {
child.props[eventName](event);
}
};
function Tooltip({
children,
position,
text,
shortcut
}) {
/**
* Whether a mouse is currently pressed, used in determining whether
* to handle a focus event as displaying the tooltip immediately.
*
* @type {boolean}
*/
const [isMouseDown, setIsMouseDown] = (0, _element.useState)(false);
const [isOver, setIsOver] = (0, _element.useState)(false);
const delayedSetIsOver = (0, _compose.useDebounce)(setIsOver, TOOLTIP_DELAY);
const createMouseDown = event => {
// Preserve original child callback behavior
emitToChild(children, 'onMouseDown', event); // On mouse down, the next `mouseup` should revert the value of the
// instance property and remove its own event handler. The bind is
// made on the document since the `mouseup` might not occur within
// the bounds of the element.
document.addEventListener('mouseup', cancelIsMouseDown);
setIsMouseDown(true);
};
const createMouseUp = event => {
emitToChild(children, 'onMouseUp', event);
document.removeEventListener('mouseup', cancelIsMouseDown);
setIsMouseDown(false);
};
const createMouseEvent = type => {
if (type === 'mouseUp') return createMouseUp;
if (type === 'mouseDown') return createMouseDown;
};
/**
* Prebound `isInMouseDown` handler, created as a constant reference to
* assure ability to remove in component unmount.
*
* @type {Function}
*/
const cancelIsMouseDown = createMouseEvent('mouseUp');
const createToggleIsOver = (eventName, isDelayed) => {
return event => {
// Preserve original child callback behavior
emitToChild(children, eventName, event); // Mouse events behave unreliably in React for disabled elements,
// firing on mouseenter but not mouseleave. Further, the default
// behavior for disabled elements in some browsers is to ignore
// mouse events. Don't bother trying to to handle them.
//
// See: https://github.com/facebook/react/issues/4251
if (event.currentTarget.disabled) {
return;
} // A focus event will occur as a result of a mouse click, but it
// should be disambiguated between interacting with the button and
// using an explicit focus shift as a cue to display the tooltip.
if ('focus' === event.type && isMouseDown) {
return;
} // Needed in case unsetting is over while delayed set pending, i.e.
// quickly blur/mouseleave before delayedSetIsOver is called
delayedSetIsOver.cancel();
const _isOver = (0, _lodash.includes)(['focus', 'mouseenter'], event.type);
if (_isOver === isOver) {
return;
}
if (isDelayed) {
delayedSetIsOver(_isOver);
} else {
setIsOver(_isOver);
}
};
};
const clearOnUnmount = () => {
delayedSetIsOver.cancel();
};
(0, _element.useEffect)(() => clearOnUnmount, []);
if (_element.Children.count(children) !== 1) {
if ('development' === process.env.NODE_ENV) {
// eslint-disable-next-line no-console
console.error('Tooltip should be called with only a single child element.');
}
return children;
}
const eventHandlers = {
onMouseEnter: createToggleIsOver('onMouseEnter', true),
onMouseLeave: createToggleIsOver('onMouseLeave'),
onClick: createToggleIsOver('onClick'),
onFocus: createToggleIsOver('onFocus'),
onBlur: createToggleIsOver('onBlur'),
onMouseDown: createMouseEvent('mouseDown')
};
const child = _element.Children.only(children);
const {
children: grandchildren,
disabled
} = child.props;
const getElementWithPopover = disabled ? getDisabledElement : getRegularElement;
const popoverData = {
isOver,
position,
text,
shortcut
};
const childrenWithPopover = addPopoverToGrandchildren({
grandchildren,
...popoverData
});
return getElementWithPopover({
child,
eventHandlers,
childrenWithPopover
});
}
var _default = (0, _next.withNextComponent)(Tooltip);
exports.default = _default;
//# sourceMappingURL=index.js.map