UNPKG

@gechiui/components

Version:
247 lines (202 loc) 6.52 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.TOOLTIP_DELAY = void 0; var _element = require("@gechiui/element"); var _lodash = require("lodash"); var _compose = require("@gechiui/compose"); var _popover = _interopRequireDefault(require("../popover")); var _shortcut = _interopRequireDefault(require("../shortcut")); // @ts-nocheck /** * External dependencies */ /** * GeChiUI 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 = _ref => { let { eventHandlers, child, childrenWithPopover } = _ref; return (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 = _ref2 => { let { child, eventHandlers, childrenWithPopover } = _ref2; return (0, _element.cloneElement)(child, { ...eventHandlers, children: childrenWithPopover }); }; const addPopoverToGrandchildren = _ref3 => { let { grandchildren, isOver, position, text, shortcut } = _ref3; return (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 the underlying element is disabled, do not emit the event. if (child.props.disabled) { return; } if (typeof child.props[eventName] === 'function') { child.props[eventName](event); } }; function Tooltip(props) { const { children, position, text, shortcut, delay = TOOLTIP_DELAY } = props; /** * 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, 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(); document.removeEventListener('mouseup', cancelIsMouseDown); }; (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 = Tooltip; exports.default = _default; //# sourceMappingURL=index.js.map