react-popper-tooltip
Advanced tools
Comparing version 3.1.1 to 4.0.0
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
var _objectWithoutPropertiesLoose = require('@babel/runtime/helpers/objectWithoutPropertiesLoose'); | ||
var _extends = require('@babel/runtime/helpers/extends'); | ||
var _inheritsLoose = require('@babel/runtime/helpers/inheritsLoose'); | ||
var React = require('react'); | ||
var reactDom = require('react-dom'); | ||
var reactPopper = require('react-popper'); | ||
var TooltipContext = /*#__PURE__*/React.createContext({}); // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function useGetLatest(val) { | ||
var ref = React.useRef(val); | ||
ref.current = val; | ||
return React.useCallback(function () { | ||
return ref.current; | ||
}, []); | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
var callAll = function callAll() { | ||
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) { | ||
fns[_key] = arguments[_key]; | ||
} | ||
return function () { | ||
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
} | ||
return fns.forEach(function (fn) { | ||
return fn && fn.apply(void 0, args); | ||
}); | ||
}; | ||
}; | ||
var noop = function noop() {// do nothing | ||
}; | ||
var canUseDOM = function canUseDOM() { | ||
return !!(typeof window !== 'undefined' && window.document && window.document.createElement); | ||
}; | ||
var setRef = function setRef(ref, node) { | ||
if (typeof ref === 'function') { | ||
return ref(node); | ||
} else if (ref != null) { | ||
ref.current = node; | ||
} | ||
}; | ||
var Tooltip = /*#__PURE__*/function (_Component) { | ||
_inheritsLoose(Tooltip, _Component); | ||
function useControlledState(_ref) { | ||
var initial = _ref.initial, | ||
value = _ref.value, | ||
_ref$onChange = _ref.onChange, | ||
onChange = _ref$onChange === void 0 ? noop : _ref$onChange; | ||
function Tooltip() { | ||
var _this; | ||
if (initial === undefined && value === undefined) { | ||
throw new TypeError('Either "value" or "initial" variable must be set. Now both are undefined'); | ||
} | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
var _React$useState = React.useState(initial), | ||
state = _React$useState[0], | ||
setState = _React$useState[1]; | ||
_this = _Component.call.apply(_Component, [this].concat(args)) || this; | ||
_this.observer = void 0; | ||
_this.tooltipRef = void 0; | ||
var getLatest = useGetLatest(state); | ||
var set = React.useCallback(function (updater) { | ||
var state = getLatest(); | ||
var updatedState = typeof updater === 'function' ? updater(state) : updater; | ||
if (typeof updatedState.persist === 'function') updatedState.persist(); | ||
setState(updatedState); | ||
if (typeof onChange === 'function') onChange(updatedState); | ||
}, [getLatest, onChange]); | ||
var isControlled = value !== undefined; | ||
return [isControlled ? value : state, isControlled ? onChange : set]; | ||
} | ||
function generateBoundingClientRect(x, y) { | ||
if (x === void 0) { | ||
x = 0; | ||
} | ||
_this.handleOutsideClick = function (event) { | ||
if (_this.tooltipRef && !_this.tooltipRef.contains(event.target)) { | ||
var parentOutsideClickHandler = _this.context.parentOutsideClickHandler; | ||
var _this$props = _this.props, | ||
hideTooltip = _this$props.hideTooltip, | ||
clearScheduled = _this$props.clearScheduled; | ||
clearScheduled(); | ||
hideTooltip(); | ||
if (y === void 0) { | ||
y = 0; | ||
} | ||
if (parentOutsideClickHandler) { | ||
parentOutsideClickHandler(event); | ||
} | ||
} | ||
return function () { | ||
return { | ||
width: 0, | ||
height: 0, | ||
top: y, | ||
right: x, | ||
bottom: y, | ||
left: x | ||
}; | ||
}; | ||
} | ||
_this.handleOutsideRightClick = function (event) { | ||
if (_this.tooltipRef && !_this.tooltipRef.contains(event.target)) { | ||
var parentOutsideRightClickHandler = _this.context.parentOutsideRightClickHandler; | ||
var _this$props2 = _this.props, | ||
hideTooltip = _this$props2.hideTooltip, | ||
clearScheduled = _this$props2.clearScheduled; | ||
clearScheduled(); | ||
hideTooltip(); | ||
var virtualElement = { | ||
getBoundingClientRect: generateBoundingClientRect() | ||
}; | ||
var defaultConfig = { | ||
closeOnOutsideClick: true, | ||
closeOnTriggerHidden: false, | ||
defaultVisible: false, | ||
delayHide: 0, | ||
delayShow: 0, | ||
followCursor: false, | ||
interactive: false, | ||
mutationObserverOptions: { | ||
attributes: true, | ||
childList: true, | ||
subtree: true | ||
}, | ||
offset: [0, 4], | ||
trigger: 'hover' | ||
}; | ||
function usePopperTooltip(config, popperOptions) { | ||
var _popperProps$state, _popperProps$state$mo, _popperProps$state$mo2; | ||
if (parentOutsideRightClickHandler) { | ||
parentOutsideRightClickHandler(event); | ||
} | ||
} | ||
}; | ||
if (config === void 0) { | ||
config = {}; | ||
} | ||
_this.addOutsideClickHandler = function () { | ||
document.body.addEventListener('touchend', _this.handleOutsideClick); | ||
document.body.addEventListener('click', _this.handleOutsideClick); | ||
}; | ||
if (popperOptions === void 0) { | ||
popperOptions = {}; | ||
} | ||
_this.removeOutsideClickHandler = function () { | ||
document.body.removeEventListener('touchend', _this.handleOutsideClick); | ||
document.body.removeEventListener('click', _this.handleOutsideClick); | ||
}; | ||
// Merging options with default options. | ||
// Keys with undefined values are replaced with the default ones if any. | ||
// Keys with other values pass through. | ||
var finalConfig = Object.keys(defaultConfig).reduce(function (config, key) { | ||
var _extends2; | ||
_this.addOutsideRightClickHandler = function () { | ||
return document.body.addEventListener('contextmenu', _this.handleOutsideRightClick); | ||
}; | ||
_this.removeOutsideRightClickHandler = function () { | ||
return document.body.removeEventListener('contextmenu', _this.handleOutsideRightClick); | ||
}; | ||
_this.getTooltipRef = function (node) { | ||
_this.tooltipRef = node; | ||
setRef(_this.props.innerRef, node); | ||
}; | ||
_this.getArrowProps = function (props) { | ||
if (props === void 0) { | ||
props = {}; | ||
return _extends({}, config, (_extends2 = {}, _extends2[key] = config[key] !== undefined ? config[key] : defaultConfig[key], _extends2)); | ||
}, config); | ||
var defaultModifiers = React.useMemo(function () { | ||
return [{ | ||
name: 'offset', | ||
options: { | ||
offset: finalConfig.offset | ||
} | ||
}]; | ||
}, [finalConfig.offset]); | ||
return _extends({}, props, { | ||
style: _extends({}, props.style, _this.props.arrowProps.style) | ||
}); | ||
}; | ||
var finalPopperOptions = _extends({}, popperOptions, { | ||
placement: popperOptions.placement || finalConfig.placement, | ||
modifiers: popperOptions.modifiers || defaultModifiers | ||
}); | ||
_this.getTooltipProps = function (props) { | ||
if (props === void 0) { | ||
props = {}; | ||
} | ||
var _React$useState = React.useState(null), | ||
triggerRef = _React$useState[0], | ||
setTriggerRef = _React$useState[1]; | ||
return _extends({}, props, _this.isTriggeredBy('hover') && { | ||
onMouseEnter: callAll(_this.props.clearScheduled, props.onMouseEnter), | ||
onMouseLeave: callAll(_this.props.hideTooltip, props.onMouseLeave) | ||
}, { | ||
style: _extends({}, props.style, _this.props.style) | ||
}); | ||
}; | ||
var _React$useState2 = React.useState(null), | ||
tooltipRef = _React$useState2[0], | ||
setTooltipRef = _React$useState2[1]; | ||
_this.contextValue = { | ||
isParentNoneTriggered: _this.props.trigger === 'none', | ||
addParentOutsideClickHandler: _this.addOutsideClickHandler, | ||
addParentOutsideRightClickHandler: _this.addOutsideRightClickHandler, | ||
parentOutsideClickHandler: _this.handleOutsideClick, | ||
parentOutsideRightClickHandler: _this.handleOutsideRightClick, | ||
removeParentOutsideClickHandler: _this.removeOutsideClickHandler, | ||
removeParentOutsideRightClickHandler: _this.removeOutsideRightClickHandler | ||
}; | ||
return _this; | ||
} | ||
var _useControlledState = useControlledState({ | ||
initial: finalConfig.defaultVisible, | ||
value: finalConfig.visible, | ||
onChange: finalConfig.onVisibleChange | ||
}), | ||
visible = _useControlledState[0], | ||
setVisible = _useControlledState[1]; | ||
var _proto = Tooltip.prototype; | ||
var timer = React.useRef(); | ||
_proto.componentDidMount = function componentDidMount() { | ||
var _this2 = this; | ||
var _usePopper = reactPopper.usePopper(finalConfig.followCursor ? virtualElement : triggerRef, tooltipRef, finalPopperOptions), | ||
styles = _usePopper.styles, | ||
attributes = _usePopper.attributes, | ||
popperProps = _objectWithoutPropertiesLoose(_usePopper, ["styles", "attributes"]); | ||
var observer = this.observer = new MutationObserver(function () { | ||
_this2.props.update(); | ||
}); | ||
observer.observe(this.tooltipRef, this.props.mutationObserverOptions); | ||
if (this.isTriggeredBy('hover') || this.isTriggeredBy('click') || this.isTriggeredBy('right-click')) { | ||
var _this$context = this.context, | ||
removeParentOutsideClickHandler = _this$context.removeParentOutsideClickHandler, | ||
removeParentOutsideRightClickHandler = _this$context.removeParentOutsideRightClickHandler; | ||
this.addOutsideClickHandler(); | ||
this.addOutsideRightClickHandler(); | ||
if (removeParentOutsideClickHandler) { | ||
removeParentOutsideClickHandler(); | ||
} | ||
if (removeParentOutsideRightClickHandler) { | ||
removeParentOutsideRightClickHandler(); | ||
} | ||
var update = popperProps.update; | ||
var getLatest = useGetLatest({ | ||
visible: visible, | ||
triggerRef: triggerRef, | ||
tooltipRef: tooltipRef, | ||
finalConfig: finalConfig | ||
}); | ||
var isTriggeredBy = React.useCallback(function (trigger) { | ||
return Array.isArray(finalConfig.trigger) ? finalConfig.trigger.includes(trigger) : finalConfig.trigger === trigger; | ||
}, [finalConfig.trigger]); | ||
var hideTooltip = React.useCallback(function () { | ||
clearTimeout(timer.current); | ||
timer.current = window.setTimeout(function () { | ||
return setVisible(false); | ||
}, finalConfig.delayHide); | ||
}, [finalConfig.delayHide, setVisible]); | ||
var showTooltip = React.useCallback(function () { | ||
clearTimeout(timer.current); | ||
timer.current = window.setTimeout(function () { | ||
return setVisible(true); | ||
}, finalConfig.delayShow); | ||
}, [finalConfig.delayShow, setVisible]); | ||
var toggleTooltip = React.useCallback(function () { | ||
if (getLatest().visible) { | ||
hideTooltip(); | ||
} else { | ||
showTooltip(); | ||
} | ||
}; | ||
}, [getLatest, hideTooltip, showTooltip]); | ||
React.useEffect(function () { | ||
return clearTimeout(timer.current); | ||
}, []); // Handle click outside | ||
_proto.componentDidUpdate = function componentDidUpdate() { | ||
if (this.props.closeOnReferenceHidden && this.props.isReferenceHidden) { | ||
this.props.hideTooltip(); | ||
} | ||
}; | ||
React.useEffect(function () { | ||
if (!getLatest().finalConfig.closeOnOutsideClick) return; | ||
_proto.componentWillUnmount = function componentWillUnmount() { | ||
if (this.observer) { | ||
this.observer.disconnect(); | ||
} | ||
var handleClickOutside = function handleClickOutside(event) { | ||
var _getLatest = getLatest(), | ||
tooltipRef = _getLatest.tooltipRef, | ||
triggerRef = _getLatest.triggerRef; | ||
if (this.isTriggeredBy('hover') || this.isTriggeredBy('click') || this.isTriggeredBy('right-click')) { | ||
var _this$context2 = this.context, | ||
isParentNoneTriggered = _this$context2.isParentNoneTriggered, | ||
addParentOutsideClickHandler = _this$context2.addParentOutsideClickHandler, | ||
addParentOutsideRightClickHandler = _this$context2.addParentOutsideRightClickHandler; | ||
this.removeOutsideClickHandler(); | ||
this.removeOutsideRightClickHandler(); | ||
this.handleOutsideClick = undefined; | ||
this.handleOutsideRightClick = undefined; | ||
var target = event.target; | ||
if (!isParentNoneTriggered && addParentOutsideClickHandler) { | ||
addParentOutsideClickHandler(); | ||
if (target instanceof Node) { | ||
if (tooltipRef != null && triggerRef != null && !tooltipRef.contains(target) && !triggerRef.contains(target)) { | ||
hideTooltip(); | ||
} | ||
} | ||
}; | ||
if (!isParentNoneTriggered && addParentOutsideRightClickHandler) { | ||
addParentOutsideRightClickHandler(); | ||
} | ||
} | ||
}; | ||
document.addEventListener('mousedown', handleClickOutside); | ||
return function () { | ||
return document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
}, [getLatest, hideTooltip]); // Trigger: click | ||
_proto.render = function render() { | ||
var _this$props3 = this.props, | ||
arrowProps = _this$props3.arrowProps, | ||
placement = _this$props3.placement, | ||
tooltip = _this$props3.tooltip; | ||
return /*#__PURE__*/React.createElement(TooltipContext.Provider, { | ||
value: this.contextValue | ||
}, tooltip({ | ||
arrowRef: arrowProps.ref, | ||
getArrowProps: this.getArrowProps, | ||
getTooltipProps: this.getTooltipProps, | ||
placement: placement, | ||
tooltipRef: this.getTooltipRef | ||
})); | ||
}; | ||
_proto.isTriggeredBy = function isTriggeredBy(event) { | ||
var trigger = this.props.trigger; | ||
return trigger === event || Array.isArray(trigger) && trigger.includes(event); | ||
}; | ||
return Tooltip; | ||
}(React.Component); | ||
Tooltip.contextType = TooltipContext; | ||
var DEFAULT_MUTATION_OBSERVER_CONFIG = { | ||
childList: true, | ||
subtree: true | ||
}; | ||
var TooltipTrigger = /*#__PURE__*/function (_Component) { | ||
_inheritsLoose(TooltipTrigger, _Component); | ||
function TooltipTrigger() { | ||
var _this; | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
_this = _Component.call.apply(_Component, [this].concat(args)) || this; | ||
_this.state = { | ||
tooltipShown: _this.props.defaultTooltipShown | ||
React.useEffect(function () { | ||
if (triggerRef == null || !isTriggeredBy('click')) return; | ||
triggerRef.addEventListener('click', toggleTooltip); | ||
return function () { | ||
return triggerRef.removeEventListener('click', toggleTooltip); | ||
}; | ||
_this.hideTimeout = void 0; | ||
_this.showTimeout = void 0; | ||
_this.popperOffset = void 0; | ||
}, [triggerRef, isTriggeredBy, toggleTooltip]); // Trigger: right-click | ||
_this.setTooltipState = function (state) { | ||
var cb = function cb() { | ||
return _this.props.onVisibilityChange(state.tooltipShown); | ||
}; | ||
React.useEffect(function () { | ||
if (triggerRef == null || !isTriggeredBy('right-click')) return; | ||
_this.isControlled() ? cb() : _this.setState(state, cb); | ||
var preventDefaultAndToggle = function preventDefaultAndToggle(event) { | ||
// Don't show the context menu | ||
event.preventDefault(); | ||
toggleTooltip(); | ||
}; | ||
_this.clearScheduled = function () { | ||
clearTimeout(_this.hideTimeout); | ||
clearTimeout(_this.showTimeout); | ||
triggerRef.addEventListener('contextmenu', preventDefaultAndToggle); | ||
return function () { | ||
return triggerRef.removeEventListener('contextmenu', preventDefaultAndToggle); | ||
}; | ||
}, [triggerRef, isTriggeredBy, toggleTooltip]); // Trigger: focus | ||
_this.showTooltip = function (_ref) { | ||
var pageX = _ref.pageX, | ||
pageY = _ref.pageY; | ||
_this.clearScheduled(); | ||
var state = { | ||
tooltipShown: true | ||
}; | ||
if (_this.props.followCursor) { | ||
state = _extends({}, state, { | ||
pageX: pageX, | ||
pageY: pageY | ||
}); | ||
} | ||
_this.showTimeout = window.setTimeout(function () { | ||
return _this.setTooltipState(state); | ||
}, _this.props.delayShow); | ||
React.useEffect(function () { | ||
if (triggerRef == null || !isTriggeredBy('focus')) return; | ||
triggerRef.addEventListener('focus', showTooltip); | ||
triggerRef.addEventListener('blur', hideTooltip); | ||
return function () { | ||
triggerRef.removeEventListener('focus', showTooltip); | ||
triggerRef.removeEventListener('blur', hideTooltip); | ||
}; | ||
}, [triggerRef, isTriggeredBy, showTooltip, hideTooltip]); // Trigger: hover on trigger | ||
_this.hideTooltip = function () { | ||
_this.clearScheduled(); | ||
_this.hideTimeout = window.setTimeout(function () { | ||
return _this.setTooltipState({ | ||
tooltipShown: false | ||
}); | ||
}, _this.props.delayHide); | ||
React.useEffect(function () { | ||
if (triggerRef == null || !isTriggeredBy('hover')) return; | ||
triggerRef.addEventListener('mouseenter', showTooltip); | ||
triggerRef.addEventListener('mouseleave', hideTooltip); | ||
return function () { | ||
triggerRef.removeEventListener('mouseenter', showTooltip); | ||
triggerRef.removeEventListener('mouseleave', hideTooltip); | ||
}; | ||
}, [triggerRef, isTriggeredBy, showTooltip, hideTooltip]); // Trigger: hover on tooltip, keep it open if hovered | ||
_this.toggleTooltip = function (_ref2) { | ||
var pageX = _ref2.pageX, | ||
pageY = _ref2.pageY; | ||
var action = _this.getState() ? 'hideTooltip' : 'showTooltip'; | ||
_this[action]({ | ||
pageX: pageX, | ||
pageY: pageY | ||
}); | ||
React.useEffect(function () { | ||
if (tooltipRef == null || !getLatest().finalConfig.interactive) return; | ||
tooltipRef.addEventListener('mouseenter', showTooltip); | ||
tooltipRef.addEventListener('mouseleave', hideTooltip); | ||
return function () { | ||
tooltipRef.removeEventListener('mouseenter', showTooltip); | ||
tooltipRef.removeEventListener('mouseleave', hideTooltip); | ||
}; | ||
}, [tooltipRef, showTooltip, hideTooltip, getLatest]); // Handle closing tooltip if trigger hidden | ||
_this.clickToggle = function (event) { | ||
event.preventDefault(); | ||
var pageX = event.pageX, | ||
pageY = event.pageY; | ||
var action = _this.props.followCursor ? 'showTooltip' : 'toggleTooltip'; | ||
var isReferenceHidden = popperProps == null ? void 0 : (_popperProps$state = popperProps.state) == null ? void 0 : (_popperProps$state$mo = _popperProps$state.modifiersData) == null ? void 0 : (_popperProps$state$mo2 = _popperProps$state$mo.hide) == null ? void 0 : _popperProps$state$mo2.isReferenceHidden; | ||
React.useEffect(function () { | ||
if (finalConfig.closeOnTriggerHidden && isReferenceHidden) hideTooltip(); | ||
}, [finalConfig.closeOnTriggerHidden, hideTooltip, isReferenceHidden]); // Handle follow cursor | ||
_this[action]({ | ||
pageX: pageX, | ||
pageY: pageY | ||
}); | ||
}; | ||
React.useEffect(function () { | ||
if (!finalConfig.followCursor || triggerRef == null) return; | ||
_this.contextMenuToggle = function (event) { | ||
event.preventDefault(); | ||
var pageX = event.pageX, | ||
pageY = event.pageY; | ||
var action = _this.props.followCursor ? 'showTooltip' : 'toggleTooltip'; | ||
function setMousePosition(_ref) { | ||
var clientX = _ref.clientX, | ||
clientY = _ref.clientY; | ||
virtualElement.getBoundingClientRect = generateBoundingClientRect(clientX, clientY); | ||
update == null ? void 0 : update(); | ||
} | ||
_this[action]({ | ||
pageX: pageX, | ||
pageY: pageY | ||
}); | ||
triggerRef.addEventListener('mousemove', setMousePosition); | ||
return function () { | ||
return triggerRef.removeEventListener('mousemove', setMousePosition); | ||
}; | ||
}, [finalConfig.followCursor, triggerRef, update]); // Handle tooltip DOM mutation changes (aka mutation observer) | ||
_this.getTriggerProps = function (props) { | ||
if (props === void 0) { | ||
props = {}; | ||
} | ||
return _extends({}, props, _this.isTriggeredBy('click') && { | ||
onClick: callAll(_this.clickToggle, props.onClick), | ||
onTouchEnd: callAll(_this.clickToggle, props.onTouchEnd) | ||
}, _this.isTriggeredBy('right-click') && { | ||
onContextMenu: callAll(_this.contextMenuToggle, props.onContextMenu) | ||
}, _this.isTriggeredBy('hover') && _extends({ | ||
onMouseEnter: callAll(_this.showTooltip, props.onMouseEnter), | ||
onMouseLeave: callAll(_this.hideTooltip, props.onMouseLeave) | ||
}, _this.props.followCursor && { | ||
onMouseMove: callAll(_this.showTooltip, props.onMouseMove) | ||
}), _this.isTriggeredBy('focus') && { | ||
onFocus: callAll(_this.showTooltip, props.onFocus), | ||
onBlur: callAll(_this.hideTooltip, props.onBlur) | ||
}); | ||
React.useEffect(function () { | ||
if (tooltipRef == null || update == null || finalConfig.mutationObserverOptions == null) return; | ||
var observer = new MutationObserver(update); | ||
observer.observe(tooltipRef, finalConfig.mutationObserverOptions); | ||
return function () { | ||
return observer.disconnect(); | ||
}; | ||
}, [finalConfig.mutationObserverOptions, tooltipRef, update]); // Tooltip props getter | ||
return _this; | ||
} | ||
var getTooltipProps = function getTooltipProps(args) { | ||
if (args === void 0) { | ||
args = {}; | ||
} | ||
var _proto = TooltipTrigger.prototype; | ||
return _extends({}, args, { | ||
style: _extends({}, args.style, styles.popper, finalConfig.followCursor && { | ||
pointerEvents: 'none' | ||
}) | ||
}, attributes.popper); | ||
}; // Arrow props getter | ||
_proto.componentWillUnmount = function componentWillUnmount() { | ||
this.clearScheduled(); | ||
}; | ||
_proto.render = function render() { | ||
var _this2 = this; | ||
var getArrowProps = function getArrowProps(args) { | ||
if (args === void 0) { | ||
args = {}; | ||
} | ||
var _this$props = this.props, | ||
children = _this$props.children, | ||
tooltip = _this$props.tooltip, | ||
placement = _this$props.placement, | ||
trigger = _this$props.trigger, | ||
getTriggerRef = _this$props.getTriggerRef, | ||
modifiers = _this$props.modifiers, | ||
closeOnReferenceHidden = _this$props.closeOnReferenceHidden, | ||
usePortal = _this$props.usePortal, | ||
portalContainer = _this$props.portalContainer, | ||
followCursor = _this$props.followCursor, | ||
getTooltipRef = _this$props.getTooltipRef, | ||
mutationObserverOptions = _this$props.mutationObserverOptions, | ||
restProps = _objectWithoutPropertiesLoose(_this$props, ["children", "tooltip", "placement", "trigger", "getTriggerRef", "modifiers", "closeOnReferenceHidden", "usePortal", "portalContainer", "followCursor", "getTooltipRef", "mutationObserverOptions"]); | ||
var popper = /*#__PURE__*/React.createElement(reactPopper.Popper, _extends({ | ||
innerRef: getTooltipRef, | ||
placement: placement, | ||
modifiers: [{ | ||
name: 'followCursor', | ||
enabled: followCursor, | ||
phase: 'main', | ||
fn: function fn(data) { | ||
_this2.popperOffset = data.state.rects.popper; | ||
} | ||
}].concat(modifiers) | ||
}, restProps), function (_ref3) { | ||
var ref = _ref3.ref, | ||
style = _ref3.style, | ||
placement = _ref3.placement, | ||
arrowProps = _ref3.arrowProps, | ||
isReferenceHidden = _ref3.isReferenceHidden, | ||
update = _ref3.update; | ||
if (followCursor && _this2.popperOffset) { | ||
var _this2$state = _this2.state, | ||
pageX = _this2$state.pageX, | ||
pageY = _this2$state.pageY; | ||
var _this2$popperOffset = _this2.popperOffset, | ||
width = _this2$popperOffset.width, | ||
height = _this2$popperOffset.height; | ||
var x = pageX + width > window.pageXOffset + document.body.offsetWidth ? pageX - width : pageX; | ||
var y = pageY + height > window.pageYOffset + document.body.offsetHeight ? pageY - height : pageY; | ||
style.transform = "translate3d(" + x + "px, " + y + "px, 0"; | ||
} | ||
return /*#__PURE__*/React.createElement(Tooltip, _extends({ | ||
arrowProps: arrowProps, | ||
closeOnReferenceHidden: closeOnReferenceHidden, | ||
isReferenceHidden: isReferenceHidden, | ||
placement: placement, | ||
update: update, | ||
style: style, | ||
tooltip: tooltip, | ||
trigger: trigger, | ||
mutationObserverOptions: mutationObserverOptions | ||
}, { | ||
clearScheduled: _this2.clearScheduled, | ||
hideTooltip: _this2.hideTooltip, | ||
innerRef: ref | ||
})); | ||
return _extends({}, args, attributes.arrow, { | ||
style: _extends({}, args.style, styles.arrow), | ||
'data-popper-arrow': true | ||
}); | ||
return /*#__PURE__*/React.createElement(reactPopper.Manager, null, /*#__PURE__*/React.createElement(reactPopper.Reference, { | ||
innerRef: getTriggerRef | ||
}, function (_ref4) { | ||
var ref = _ref4.ref; | ||
return children({ | ||
getTriggerProps: _this2.getTriggerProps, | ||
triggerRef: ref | ||
}); | ||
}), this.getState() && (usePortal ? /*#__PURE__*/reactDom.createPortal(popper, portalContainer) : popper)); | ||
}; | ||
_proto.isControlled = function isControlled() { | ||
return this.props.tooltipShown !== undefined; | ||
}; | ||
return _extends({ | ||
getArrowProps: getArrowProps, | ||
getTooltipProps: getTooltipProps, | ||
setTooltipRef: setTooltipRef, | ||
setTriggerRef: setTriggerRef, | ||
tooltipRef: tooltipRef, | ||
triggerRef: triggerRef, | ||
visible: visible | ||
}, popperProps); | ||
} | ||
_proto.getState = function getState() { | ||
return this.isControlled() ? this.props.tooltipShown : this.state.tooltipShown; | ||
}; | ||
_proto.isTriggeredBy = function isTriggeredBy(event) { | ||
var trigger = this.props.trigger; | ||
return trigger === event || Array.isArray(trigger) && trigger.includes(event); | ||
}; | ||
return TooltipTrigger; | ||
}(React.Component); | ||
TooltipTrigger.defaultProps = { | ||
closeOnReferenceHidden: true, | ||
defaultTooltipShown: false, | ||
delayHide: 0, | ||
delayShow: 0, | ||
followCursor: false, | ||
onVisibilityChange: noop, | ||
placement: 'right', | ||
portalContainer: canUseDOM() ? document.body : null, | ||
trigger: 'hover', | ||
usePortal: canUseDOM(), | ||
mutationObserverOptions: DEFAULT_MUTATION_OBSERVER_CONFIG, | ||
modifiers: [] | ||
}; | ||
module.exports = TooltipTrigger; | ||
exports.usePopperTooltip = usePopperTooltip; | ||
//# sourceMappingURL=react-popper-tooltip.js.map |
import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose'; | ||
import _extends from '@babel/runtime/helpers/esm/extends'; | ||
import _inheritsLoose from '@babel/runtime/helpers/esm/inheritsLoose'; | ||
import React, { Component } from 'react'; | ||
import { createPortal } from 'react-dom'; | ||
import { Popper, Manager, Reference } from 'react-popper'; | ||
import { useCallback, useRef, useState, useMemo, useEffect } from 'react'; | ||
import { usePopper } from 'react-popper'; | ||
var TooltipContext = /*#__PURE__*/React.createContext({}); // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function useGetLatest(val) { | ||
var ref = useRef(val); | ||
ref.current = val; | ||
return useCallback(function () { | ||
return ref.current; | ||
}, []); | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
var callAll = function callAll() { | ||
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) { | ||
fns[_key] = arguments[_key]; | ||
} | ||
return function () { | ||
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
} | ||
return fns.forEach(function (fn) { | ||
return fn && fn.apply(void 0, args); | ||
}); | ||
}; | ||
}; | ||
var noop = function noop() {// do nothing | ||
}; | ||
var canUseDOM = function canUseDOM() { | ||
return !!(typeof window !== 'undefined' && window.document && window.document.createElement); | ||
}; | ||
var setRef = function setRef(ref, node) { | ||
if (typeof ref === 'function') { | ||
return ref(node); | ||
} else if (ref != null) { | ||
ref.current = node; | ||
} | ||
}; | ||
var Tooltip = /*#__PURE__*/function (_Component) { | ||
_inheritsLoose(Tooltip, _Component); | ||
function useControlledState(_ref) { | ||
var initial = _ref.initial, | ||
value = _ref.value, | ||
_ref$onChange = _ref.onChange, | ||
onChange = _ref$onChange === void 0 ? noop : _ref$onChange; | ||
function Tooltip() { | ||
var _this; | ||
if (initial === undefined && value === undefined) { | ||
throw new TypeError('Either "value" or "initial" variable must be set. Now both are undefined'); | ||
} | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
var _React$useState = useState(initial), | ||
state = _React$useState[0], | ||
setState = _React$useState[1]; | ||
_this = _Component.call.apply(_Component, [this].concat(args)) || this; | ||
_this.observer = void 0; | ||
_this.tooltipRef = void 0; | ||
var getLatest = useGetLatest(state); | ||
var set = useCallback(function (updater) { | ||
var state = getLatest(); | ||
var updatedState = typeof updater === 'function' ? updater(state) : updater; | ||
if (typeof updatedState.persist === 'function') updatedState.persist(); | ||
setState(updatedState); | ||
if (typeof onChange === 'function') onChange(updatedState); | ||
}, [getLatest, onChange]); | ||
var isControlled = value !== undefined; | ||
return [isControlled ? value : state, isControlled ? onChange : set]; | ||
} | ||
function generateBoundingClientRect(x, y) { | ||
if (x === void 0) { | ||
x = 0; | ||
} | ||
_this.handleOutsideClick = function (event) { | ||
if (_this.tooltipRef && !_this.tooltipRef.contains(event.target)) { | ||
var parentOutsideClickHandler = _this.context.parentOutsideClickHandler; | ||
var _this$props = _this.props, | ||
hideTooltip = _this$props.hideTooltip, | ||
clearScheduled = _this$props.clearScheduled; | ||
clearScheduled(); | ||
hideTooltip(); | ||
if (y === void 0) { | ||
y = 0; | ||
} | ||
if (parentOutsideClickHandler) { | ||
parentOutsideClickHandler(event); | ||
} | ||
} | ||
return function () { | ||
return { | ||
width: 0, | ||
height: 0, | ||
top: y, | ||
right: x, | ||
bottom: y, | ||
left: x | ||
}; | ||
}; | ||
} | ||
_this.handleOutsideRightClick = function (event) { | ||
if (_this.tooltipRef && !_this.tooltipRef.contains(event.target)) { | ||
var parentOutsideRightClickHandler = _this.context.parentOutsideRightClickHandler; | ||
var _this$props2 = _this.props, | ||
hideTooltip = _this$props2.hideTooltip, | ||
clearScheduled = _this$props2.clearScheduled; | ||
clearScheduled(); | ||
hideTooltip(); | ||
var virtualElement = { | ||
getBoundingClientRect: generateBoundingClientRect() | ||
}; | ||
var defaultConfig = { | ||
closeOnOutsideClick: true, | ||
closeOnTriggerHidden: false, | ||
defaultVisible: false, | ||
delayHide: 0, | ||
delayShow: 0, | ||
followCursor: false, | ||
interactive: false, | ||
mutationObserverOptions: { | ||
attributes: true, | ||
childList: true, | ||
subtree: true | ||
}, | ||
offset: [0, 4], | ||
trigger: 'hover' | ||
}; | ||
function usePopperTooltip(config, popperOptions) { | ||
var _popperProps$state, _popperProps$state$mo, _popperProps$state$mo2; | ||
if (parentOutsideRightClickHandler) { | ||
parentOutsideRightClickHandler(event); | ||
} | ||
} | ||
}; | ||
if (config === void 0) { | ||
config = {}; | ||
} | ||
_this.addOutsideClickHandler = function () { | ||
document.body.addEventListener('touchend', _this.handleOutsideClick); | ||
document.body.addEventListener('click', _this.handleOutsideClick); | ||
}; | ||
if (popperOptions === void 0) { | ||
popperOptions = {}; | ||
} | ||
_this.removeOutsideClickHandler = function () { | ||
document.body.removeEventListener('touchend', _this.handleOutsideClick); | ||
document.body.removeEventListener('click', _this.handleOutsideClick); | ||
}; | ||
// Merging options with default options. | ||
// Keys with undefined values are replaced with the default ones if any. | ||
// Keys with other values pass through. | ||
var finalConfig = Object.keys(defaultConfig).reduce(function (config, key) { | ||
var _extends2; | ||
_this.addOutsideRightClickHandler = function () { | ||
return document.body.addEventListener('contextmenu', _this.handleOutsideRightClick); | ||
}; | ||
_this.removeOutsideRightClickHandler = function () { | ||
return document.body.removeEventListener('contextmenu', _this.handleOutsideRightClick); | ||
}; | ||
_this.getTooltipRef = function (node) { | ||
_this.tooltipRef = node; | ||
setRef(_this.props.innerRef, node); | ||
}; | ||
_this.getArrowProps = function (props) { | ||
if (props === void 0) { | ||
props = {}; | ||
return _extends({}, config, (_extends2 = {}, _extends2[key] = config[key] !== undefined ? config[key] : defaultConfig[key], _extends2)); | ||
}, config); | ||
var defaultModifiers = useMemo(function () { | ||
return [{ | ||
name: 'offset', | ||
options: { | ||
offset: finalConfig.offset | ||
} | ||
}]; | ||
}, [finalConfig.offset]); | ||
return _extends({}, props, { | ||
style: _extends({}, props.style, _this.props.arrowProps.style) | ||
}); | ||
}; | ||
var finalPopperOptions = _extends({}, popperOptions, { | ||
placement: popperOptions.placement || finalConfig.placement, | ||
modifiers: popperOptions.modifiers || defaultModifiers | ||
}); | ||
_this.getTooltipProps = function (props) { | ||
if (props === void 0) { | ||
props = {}; | ||
} | ||
var _React$useState = useState(null), | ||
triggerRef = _React$useState[0], | ||
setTriggerRef = _React$useState[1]; | ||
return _extends({}, props, _this.isTriggeredBy('hover') && { | ||
onMouseEnter: callAll(_this.props.clearScheduled, props.onMouseEnter), | ||
onMouseLeave: callAll(_this.props.hideTooltip, props.onMouseLeave) | ||
}, { | ||
style: _extends({}, props.style, _this.props.style) | ||
}); | ||
}; | ||
var _React$useState2 = useState(null), | ||
tooltipRef = _React$useState2[0], | ||
setTooltipRef = _React$useState2[1]; | ||
_this.contextValue = { | ||
isParentNoneTriggered: _this.props.trigger === 'none', | ||
addParentOutsideClickHandler: _this.addOutsideClickHandler, | ||
addParentOutsideRightClickHandler: _this.addOutsideRightClickHandler, | ||
parentOutsideClickHandler: _this.handleOutsideClick, | ||
parentOutsideRightClickHandler: _this.handleOutsideRightClick, | ||
removeParentOutsideClickHandler: _this.removeOutsideClickHandler, | ||
removeParentOutsideRightClickHandler: _this.removeOutsideRightClickHandler | ||
}; | ||
return _this; | ||
} | ||
var _useControlledState = useControlledState({ | ||
initial: finalConfig.defaultVisible, | ||
value: finalConfig.visible, | ||
onChange: finalConfig.onVisibleChange | ||
}), | ||
visible = _useControlledState[0], | ||
setVisible = _useControlledState[1]; | ||
var _proto = Tooltip.prototype; | ||
var timer = useRef(); | ||
_proto.componentDidMount = function componentDidMount() { | ||
var _this2 = this; | ||
var _usePopper = usePopper(finalConfig.followCursor ? virtualElement : triggerRef, tooltipRef, finalPopperOptions), | ||
styles = _usePopper.styles, | ||
attributes = _usePopper.attributes, | ||
popperProps = _objectWithoutPropertiesLoose(_usePopper, ["styles", "attributes"]); | ||
var observer = this.observer = new MutationObserver(function () { | ||
_this2.props.update(); | ||
}); | ||
observer.observe(this.tooltipRef, this.props.mutationObserverOptions); | ||
if (this.isTriggeredBy('hover') || this.isTriggeredBy('click') || this.isTriggeredBy('right-click')) { | ||
var _this$context = this.context, | ||
removeParentOutsideClickHandler = _this$context.removeParentOutsideClickHandler, | ||
removeParentOutsideRightClickHandler = _this$context.removeParentOutsideRightClickHandler; | ||
this.addOutsideClickHandler(); | ||
this.addOutsideRightClickHandler(); | ||
if (removeParentOutsideClickHandler) { | ||
removeParentOutsideClickHandler(); | ||
} | ||
if (removeParentOutsideRightClickHandler) { | ||
removeParentOutsideRightClickHandler(); | ||
} | ||
var update = popperProps.update; | ||
var getLatest = useGetLatest({ | ||
visible: visible, | ||
triggerRef: triggerRef, | ||
tooltipRef: tooltipRef, | ||
finalConfig: finalConfig | ||
}); | ||
var isTriggeredBy = useCallback(function (trigger) { | ||
return Array.isArray(finalConfig.trigger) ? finalConfig.trigger.includes(trigger) : finalConfig.trigger === trigger; | ||
}, [finalConfig.trigger]); | ||
var hideTooltip = useCallback(function () { | ||
clearTimeout(timer.current); | ||
timer.current = window.setTimeout(function () { | ||
return setVisible(false); | ||
}, finalConfig.delayHide); | ||
}, [finalConfig.delayHide, setVisible]); | ||
var showTooltip = useCallback(function () { | ||
clearTimeout(timer.current); | ||
timer.current = window.setTimeout(function () { | ||
return setVisible(true); | ||
}, finalConfig.delayShow); | ||
}, [finalConfig.delayShow, setVisible]); | ||
var toggleTooltip = useCallback(function () { | ||
if (getLatest().visible) { | ||
hideTooltip(); | ||
} else { | ||
showTooltip(); | ||
} | ||
}; | ||
}, [getLatest, hideTooltip, showTooltip]); | ||
useEffect(function () { | ||
return clearTimeout(timer.current); | ||
}, []); // Handle click outside | ||
_proto.componentDidUpdate = function componentDidUpdate() { | ||
if (this.props.closeOnReferenceHidden && this.props.isReferenceHidden) { | ||
this.props.hideTooltip(); | ||
} | ||
}; | ||
useEffect(function () { | ||
if (!getLatest().finalConfig.closeOnOutsideClick) return; | ||
_proto.componentWillUnmount = function componentWillUnmount() { | ||
if (this.observer) { | ||
this.observer.disconnect(); | ||
} | ||
var handleClickOutside = function handleClickOutside(event) { | ||
var _getLatest = getLatest(), | ||
tooltipRef = _getLatest.tooltipRef, | ||
triggerRef = _getLatest.triggerRef; | ||
if (this.isTriggeredBy('hover') || this.isTriggeredBy('click') || this.isTriggeredBy('right-click')) { | ||
var _this$context2 = this.context, | ||
isParentNoneTriggered = _this$context2.isParentNoneTriggered, | ||
addParentOutsideClickHandler = _this$context2.addParentOutsideClickHandler, | ||
addParentOutsideRightClickHandler = _this$context2.addParentOutsideRightClickHandler; | ||
this.removeOutsideClickHandler(); | ||
this.removeOutsideRightClickHandler(); | ||
this.handleOutsideClick = undefined; | ||
this.handleOutsideRightClick = undefined; | ||
var target = event.target; | ||
if (!isParentNoneTriggered && addParentOutsideClickHandler) { | ||
addParentOutsideClickHandler(); | ||
if (target instanceof Node) { | ||
if (tooltipRef != null && triggerRef != null && !tooltipRef.contains(target) && !triggerRef.contains(target)) { | ||
hideTooltip(); | ||
} | ||
} | ||
}; | ||
if (!isParentNoneTriggered && addParentOutsideRightClickHandler) { | ||
addParentOutsideRightClickHandler(); | ||
} | ||
} | ||
}; | ||
document.addEventListener('mousedown', handleClickOutside); | ||
return function () { | ||
return document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
}, [getLatest, hideTooltip]); // Trigger: click | ||
_proto.render = function render() { | ||
var _this$props3 = this.props, | ||
arrowProps = _this$props3.arrowProps, | ||
placement = _this$props3.placement, | ||
tooltip = _this$props3.tooltip; | ||
return /*#__PURE__*/React.createElement(TooltipContext.Provider, { | ||
value: this.contextValue | ||
}, tooltip({ | ||
arrowRef: arrowProps.ref, | ||
getArrowProps: this.getArrowProps, | ||
getTooltipProps: this.getTooltipProps, | ||
placement: placement, | ||
tooltipRef: this.getTooltipRef | ||
})); | ||
}; | ||
_proto.isTriggeredBy = function isTriggeredBy(event) { | ||
var trigger = this.props.trigger; | ||
return trigger === event || Array.isArray(trigger) && trigger.includes(event); | ||
}; | ||
return Tooltip; | ||
}(Component); | ||
Tooltip.contextType = TooltipContext; | ||
var DEFAULT_MUTATION_OBSERVER_CONFIG = { | ||
childList: true, | ||
subtree: true | ||
}; | ||
var TooltipTrigger = /*#__PURE__*/function (_Component) { | ||
_inheritsLoose(TooltipTrigger, _Component); | ||
function TooltipTrigger() { | ||
var _this; | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
_this = _Component.call.apply(_Component, [this].concat(args)) || this; | ||
_this.state = { | ||
tooltipShown: _this.props.defaultTooltipShown | ||
useEffect(function () { | ||
if (triggerRef == null || !isTriggeredBy('click')) return; | ||
triggerRef.addEventListener('click', toggleTooltip); | ||
return function () { | ||
return triggerRef.removeEventListener('click', toggleTooltip); | ||
}; | ||
_this.hideTimeout = void 0; | ||
_this.showTimeout = void 0; | ||
_this.popperOffset = void 0; | ||
}, [triggerRef, isTriggeredBy, toggleTooltip]); // Trigger: right-click | ||
_this.setTooltipState = function (state) { | ||
var cb = function cb() { | ||
return _this.props.onVisibilityChange(state.tooltipShown); | ||
}; | ||
useEffect(function () { | ||
if (triggerRef == null || !isTriggeredBy('right-click')) return; | ||
_this.isControlled() ? cb() : _this.setState(state, cb); | ||
var preventDefaultAndToggle = function preventDefaultAndToggle(event) { | ||
// Don't show the context menu | ||
event.preventDefault(); | ||
toggleTooltip(); | ||
}; | ||
_this.clearScheduled = function () { | ||
clearTimeout(_this.hideTimeout); | ||
clearTimeout(_this.showTimeout); | ||
triggerRef.addEventListener('contextmenu', preventDefaultAndToggle); | ||
return function () { | ||
return triggerRef.removeEventListener('contextmenu', preventDefaultAndToggle); | ||
}; | ||
}, [triggerRef, isTriggeredBy, toggleTooltip]); // Trigger: focus | ||
_this.showTooltip = function (_ref) { | ||
var pageX = _ref.pageX, | ||
pageY = _ref.pageY; | ||
_this.clearScheduled(); | ||
var state = { | ||
tooltipShown: true | ||
}; | ||
if (_this.props.followCursor) { | ||
state = _extends({}, state, { | ||
pageX: pageX, | ||
pageY: pageY | ||
}); | ||
} | ||
_this.showTimeout = window.setTimeout(function () { | ||
return _this.setTooltipState(state); | ||
}, _this.props.delayShow); | ||
useEffect(function () { | ||
if (triggerRef == null || !isTriggeredBy('focus')) return; | ||
triggerRef.addEventListener('focus', showTooltip); | ||
triggerRef.addEventListener('blur', hideTooltip); | ||
return function () { | ||
triggerRef.removeEventListener('focus', showTooltip); | ||
triggerRef.removeEventListener('blur', hideTooltip); | ||
}; | ||
}, [triggerRef, isTriggeredBy, showTooltip, hideTooltip]); // Trigger: hover on trigger | ||
_this.hideTooltip = function () { | ||
_this.clearScheduled(); | ||
_this.hideTimeout = window.setTimeout(function () { | ||
return _this.setTooltipState({ | ||
tooltipShown: false | ||
}); | ||
}, _this.props.delayHide); | ||
useEffect(function () { | ||
if (triggerRef == null || !isTriggeredBy('hover')) return; | ||
triggerRef.addEventListener('mouseenter', showTooltip); | ||
triggerRef.addEventListener('mouseleave', hideTooltip); | ||
return function () { | ||
triggerRef.removeEventListener('mouseenter', showTooltip); | ||
triggerRef.removeEventListener('mouseleave', hideTooltip); | ||
}; | ||
}, [triggerRef, isTriggeredBy, showTooltip, hideTooltip]); // Trigger: hover on tooltip, keep it open if hovered | ||
_this.toggleTooltip = function (_ref2) { | ||
var pageX = _ref2.pageX, | ||
pageY = _ref2.pageY; | ||
var action = _this.getState() ? 'hideTooltip' : 'showTooltip'; | ||
_this[action]({ | ||
pageX: pageX, | ||
pageY: pageY | ||
}); | ||
useEffect(function () { | ||
if (tooltipRef == null || !getLatest().finalConfig.interactive) return; | ||
tooltipRef.addEventListener('mouseenter', showTooltip); | ||
tooltipRef.addEventListener('mouseleave', hideTooltip); | ||
return function () { | ||
tooltipRef.removeEventListener('mouseenter', showTooltip); | ||
tooltipRef.removeEventListener('mouseleave', hideTooltip); | ||
}; | ||
}, [tooltipRef, showTooltip, hideTooltip, getLatest]); // Handle closing tooltip if trigger hidden | ||
_this.clickToggle = function (event) { | ||
event.preventDefault(); | ||
var pageX = event.pageX, | ||
pageY = event.pageY; | ||
var action = _this.props.followCursor ? 'showTooltip' : 'toggleTooltip'; | ||
var isReferenceHidden = popperProps == null ? void 0 : (_popperProps$state = popperProps.state) == null ? void 0 : (_popperProps$state$mo = _popperProps$state.modifiersData) == null ? void 0 : (_popperProps$state$mo2 = _popperProps$state$mo.hide) == null ? void 0 : _popperProps$state$mo2.isReferenceHidden; | ||
useEffect(function () { | ||
if (finalConfig.closeOnTriggerHidden && isReferenceHidden) hideTooltip(); | ||
}, [finalConfig.closeOnTriggerHidden, hideTooltip, isReferenceHidden]); // Handle follow cursor | ||
_this[action]({ | ||
pageX: pageX, | ||
pageY: pageY | ||
}); | ||
}; | ||
useEffect(function () { | ||
if (!finalConfig.followCursor || triggerRef == null) return; | ||
_this.contextMenuToggle = function (event) { | ||
event.preventDefault(); | ||
var pageX = event.pageX, | ||
pageY = event.pageY; | ||
var action = _this.props.followCursor ? 'showTooltip' : 'toggleTooltip'; | ||
function setMousePosition(_ref) { | ||
var clientX = _ref.clientX, | ||
clientY = _ref.clientY; | ||
virtualElement.getBoundingClientRect = generateBoundingClientRect(clientX, clientY); | ||
update == null ? void 0 : update(); | ||
} | ||
_this[action]({ | ||
pageX: pageX, | ||
pageY: pageY | ||
}); | ||
triggerRef.addEventListener('mousemove', setMousePosition); | ||
return function () { | ||
return triggerRef.removeEventListener('mousemove', setMousePosition); | ||
}; | ||
}, [finalConfig.followCursor, triggerRef, update]); // Handle tooltip DOM mutation changes (aka mutation observer) | ||
_this.getTriggerProps = function (props) { | ||
if (props === void 0) { | ||
props = {}; | ||
} | ||
return _extends({}, props, _this.isTriggeredBy('click') && { | ||
onClick: callAll(_this.clickToggle, props.onClick), | ||
onTouchEnd: callAll(_this.clickToggle, props.onTouchEnd) | ||
}, _this.isTriggeredBy('right-click') && { | ||
onContextMenu: callAll(_this.contextMenuToggle, props.onContextMenu) | ||
}, _this.isTriggeredBy('hover') && _extends({ | ||
onMouseEnter: callAll(_this.showTooltip, props.onMouseEnter), | ||
onMouseLeave: callAll(_this.hideTooltip, props.onMouseLeave) | ||
}, _this.props.followCursor && { | ||
onMouseMove: callAll(_this.showTooltip, props.onMouseMove) | ||
}), _this.isTriggeredBy('focus') && { | ||
onFocus: callAll(_this.showTooltip, props.onFocus), | ||
onBlur: callAll(_this.hideTooltip, props.onBlur) | ||
}); | ||
useEffect(function () { | ||
if (tooltipRef == null || update == null || finalConfig.mutationObserverOptions == null) return; | ||
var observer = new MutationObserver(update); | ||
observer.observe(tooltipRef, finalConfig.mutationObserverOptions); | ||
return function () { | ||
return observer.disconnect(); | ||
}; | ||
}, [finalConfig.mutationObserverOptions, tooltipRef, update]); // Tooltip props getter | ||
return _this; | ||
} | ||
var getTooltipProps = function getTooltipProps(args) { | ||
if (args === void 0) { | ||
args = {}; | ||
} | ||
var _proto = TooltipTrigger.prototype; | ||
return _extends({}, args, { | ||
style: _extends({}, args.style, styles.popper, finalConfig.followCursor && { | ||
pointerEvents: 'none' | ||
}) | ||
}, attributes.popper); | ||
}; // Arrow props getter | ||
_proto.componentWillUnmount = function componentWillUnmount() { | ||
this.clearScheduled(); | ||
}; | ||
_proto.render = function render() { | ||
var _this2 = this; | ||
var getArrowProps = function getArrowProps(args) { | ||
if (args === void 0) { | ||
args = {}; | ||
} | ||
var _this$props = this.props, | ||
children = _this$props.children, | ||
tooltip = _this$props.tooltip, | ||
placement = _this$props.placement, | ||
trigger = _this$props.trigger, | ||
getTriggerRef = _this$props.getTriggerRef, | ||
modifiers = _this$props.modifiers, | ||
closeOnReferenceHidden = _this$props.closeOnReferenceHidden, | ||
usePortal = _this$props.usePortal, | ||
portalContainer = _this$props.portalContainer, | ||
followCursor = _this$props.followCursor, | ||
getTooltipRef = _this$props.getTooltipRef, | ||
mutationObserverOptions = _this$props.mutationObserverOptions, | ||
restProps = _objectWithoutPropertiesLoose(_this$props, ["children", "tooltip", "placement", "trigger", "getTriggerRef", "modifiers", "closeOnReferenceHidden", "usePortal", "portalContainer", "followCursor", "getTooltipRef", "mutationObserverOptions"]); | ||
var popper = /*#__PURE__*/React.createElement(Popper, _extends({ | ||
innerRef: getTooltipRef, | ||
placement: placement, | ||
modifiers: [{ | ||
name: 'followCursor', | ||
enabled: followCursor, | ||
phase: 'main', | ||
fn: function fn(data) { | ||
_this2.popperOffset = data.state.rects.popper; | ||
} | ||
}].concat(modifiers) | ||
}, restProps), function (_ref3) { | ||
var ref = _ref3.ref, | ||
style = _ref3.style, | ||
placement = _ref3.placement, | ||
arrowProps = _ref3.arrowProps, | ||
isReferenceHidden = _ref3.isReferenceHidden, | ||
update = _ref3.update; | ||
if (followCursor && _this2.popperOffset) { | ||
var _this2$state = _this2.state, | ||
pageX = _this2$state.pageX, | ||
pageY = _this2$state.pageY; | ||
var _this2$popperOffset = _this2.popperOffset, | ||
width = _this2$popperOffset.width, | ||
height = _this2$popperOffset.height; | ||
var x = pageX + width > window.pageXOffset + document.body.offsetWidth ? pageX - width : pageX; | ||
var y = pageY + height > window.pageYOffset + document.body.offsetHeight ? pageY - height : pageY; | ||
style.transform = "translate3d(" + x + "px, " + y + "px, 0"; | ||
} | ||
return /*#__PURE__*/React.createElement(Tooltip, _extends({ | ||
arrowProps: arrowProps, | ||
closeOnReferenceHidden: closeOnReferenceHidden, | ||
isReferenceHidden: isReferenceHidden, | ||
placement: placement, | ||
update: update, | ||
style: style, | ||
tooltip: tooltip, | ||
trigger: trigger, | ||
mutationObserverOptions: mutationObserverOptions | ||
}, { | ||
clearScheduled: _this2.clearScheduled, | ||
hideTooltip: _this2.hideTooltip, | ||
innerRef: ref | ||
})); | ||
return _extends({}, args, attributes.arrow, { | ||
style: _extends({}, args.style, styles.arrow), | ||
'data-popper-arrow': true | ||
}); | ||
return /*#__PURE__*/React.createElement(Manager, null, /*#__PURE__*/React.createElement(Reference, { | ||
innerRef: getTriggerRef | ||
}, function (_ref4) { | ||
var ref = _ref4.ref; | ||
return children({ | ||
getTriggerProps: _this2.getTriggerProps, | ||
triggerRef: ref | ||
}); | ||
}), this.getState() && (usePortal ? /*#__PURE__*/createPortal(popper, portalContainer) : popper)); | ||
}; | ||
_proto.isControlled = function isControlled() { | ||
return this.props.tooltipShown !== undefined; | ||
}; | ||
return _extends({ | ||
getArrowProps: getArrowProps, | ||
getTooltipProps: getTooltipProps, | ||
setTooltipRef: setTooltipRef, | ||
setTriggerRef: setTriggerRef, | ||
tooltipRef: tooltipRef, | ||
triggerRef: triggerRef, | ||
visible: visible | ||
}, popperProps); | ||
} | ||
_proto.getState = function getState() { | ||
return this.isControlled() ? this.props.tooltipShown : this.state.tooltipShown; | ||
}; | ||
_proto.isTriggeredBy = function isTriggeredBy(event) { | ||
var trigger = this.props.trigger; | ||
return trigger === event || Array.isArray(trigger) && trigger.includes(event); | ||
}; | ||
return TooltipTrigger; | ||
}(Component); | ||
TooltipTrigger.defaultProps = { | ||
closeOnReferenceHidden: true, | ||
defaultTooltipShown: false, | ||
delayHide: 0, | ||
delayShow: 0, | ||
followCursor: false, | ||
onVisibilityChange: noop, | ||
placement: 'right', | ||
portalContainer: canUseDOM() ? document.body : null, | ||
trigger: 'hover', | ||
usePortal: canUseDOM(), | ||
mutationObserverOptions: DEFAULT_MUTATION_OBSERVER_CONFIG, | ||
modifiers: [] | ||
}; | ||
export default TooltipTrigger; | ||
export { usePopperTooltip }; | ||
//# sourceMappingURL=react-popper-tooltip.js.map |
103
package.json
{ | ||
"name": "react-popper-tooltip", | ||
"version": "3.1.1", | ||
"version": "4.0.0", | ||
"description": "React tooltip library built around react-popper", | ||
"homepage": "https://react-popper-tooltip.netlify.app", | ||
"author": "Mohsin Ul Haq <mohsinulhaq01@gmail.com>", | ||
"license": "MIT", | ||
"repository": { | ||
@@ -10,6 +11,14 @@ "type": "git", | ||
}, | ||
"keywords": [ | ||
"react", | ||
"tooltip", | ||
"popover", | ||
"overlay", | ||
"react-tooltip", | ||
"react-popper" | ||
], | ||
"main": "dist/cjs/react-popper-tooltip.js", | ||
"module": "dist/esm/react-popper-tooltip.js", | ||
"typings": "dist/index.d.ts", | ||
"style": "dist/styles.css", | ||
"typings": "dist/index.d.ts", | ||
"files": [ | ||
@@ -23,10 +32,19 @@ "dist" | ||
"build": "rm -rf dist && rollup -c && cp src/styles.css dist && yarn tsc && rm -rf compiled", | ||
"prepublishOnly": "yarn typecheck && yarn lint && yarn build && yarn test", | ||
"prepare": "yarn typecheck && yarn lint && yarn build && yarn test", | ||
"prettier": "prettier --write src/**/*.{ts,tsx}", | ||
"docs": "docz dev", | ||
"docs:build": "docz build", | ||
"typecheck": "tsc --noEmit", | ||
"lint": "eslint src/**/*.{ts,tsx}", | ||
"test": "jest tests/" | ||
"lint": "eslint \"{src,tests,examples}**/*.{ts,tsx}\"", | ||
"start": "rollup -c -w", | ||
"test": "jest tests/", | ||
"storybook": "start-storybook -p 6006", | ||
"build-storybook": "build-storybook" | ||
}, | ||
"jest": { | ||
"modulePathIgnorePatterns": [ | ||
"<rootDir>/examples/" | ||
], | ||
"setupFilesAfterEnv": [ | ||
"@testing-library/jest-dom/extend-expect" | ||
] | ||
}, | ||
"husky": { | ||
@@ -38,3 +56,3 @@ "hooks": { | ||
"lint-staged": { | ||
"src/**/*.(ts|tsx)": [ | ||
"**/*.(ts|tsx)": [ | ||
"prettier --write", | ||
@@ -44,52 +62,47 @@ "yarn lint --fix" | ||
}, | ||
"keywords": [ | ||
"react", | ||
"tooltip", | ||
"popover", | ||
"overlay", | ||
"react-tooltip", | ||
"react-popper" | ||
], | ||
"author": "Mohsin Ul Haq <mohsinulhaq01@gmail.com>", | ||
"license": "MIT", | ||
"peerDependencies": { | ||
"react": "^16.6.0 || ^17.0.0", | ||
"react-dom": "^16.6.0 || ^17.0.0" | ||
"react": ">=16.6.0", | ||
"react-dom": ">=16.6.0" | ||
}, | ||
"dependencies": { | ||
"@babel/runtime": "^7.12.5", | ||
"@popperjs/core": "^2.5.4", | ||
"@popperjs/core": "^2.6.0", | ||
"react-popper": "^2.2.4" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "^7.12.1", | ||
"@babel/core": "^7.12.3", | ||
"@babel/plugin-proposal-class-properties": "^7.12.1", | ||
"@babel/plugin-transform-runtime": "^7.12.1", | ||
"@babel/preset-env": "^7.12.1", | ||
"@babel/preset-react": "^7.12.5", | ||
"@babel/preset-typescript": "^7.12.1", | ||
"@testing-library/react": "^11.1.1", | ||
"@types/jest": "^26.0.15", | ||
"@types/react": "^16.9.55", | ||
"@types/react-dom": "^16.9.9", | ||
"@typescript-eslint/eslint-plugin": "^4.6.1", | ||
"@typescript-eslint/parser": "^4.6.1", | ||
"docz": "^2.3.1", | ||
"eslint": "^7.12.1", | ||
"eslint-config-prettier": "^6.15.0", | ||
"eslint-plugin-prettier": "^3.1.4", | ||
"eslint-plugin-react": "^7.21.5", | ||
"husky": "^4.3.0", | ||
"@babel/core": "^7.12.10", | ||
"@babel/plugin-transform-runtime": "^7.12.10", | ||
"@babel/preset-env": "^7.12.11", | ||
"@babel/preset-react": "^7.12.10", | ||
"@babel/preset-typescript": "^7.12.7", | ||
"@storybook/addon-actions": "^6.1.15", | ||
"@storybook/addon-essentials": "^6.1.15", | ||
"@storybook/addon-links": "^6.1.15", | ||
"@storybook/react": "^6.1.15", | ||
"@testing-library/jest-dom": "^5.11.9", | ||
"@testing-library/react": "^11.2.3", | ||
"@testing-library/user-event": "^12.6.2", | ||
"@types/jest": "^26.0.20", | ||
"@types/react": "^17.0.0", | ||
"@types/react-dom": "^17.0.0", | ||
"@typescript-eslint/eslint-plugin": "^4.14.1", | ||
"@typescript-eslint/parser": "^4.14.1", | ||
"babel-loader": "^8.2.2", | ||
"eslint": "^7.18.0", | ||
"eslint-config-prettier": "^7.2.0", | ||
"eslint-plugin-prettier": "^3.3.1", | ||
"eslint-plugin-react": "^7.22.0", | ||
"eslint-plugin-react-hooks": "^4.2.0", | ||
"husky": "^4.3.8", | ||
"jest": "^26.6.3", | ||
"lint-staged": "^10.5.1", | ||
"prettier": "^2.1.2", | ||
"lint-staged": "^10.5.3", | ||
"prettier": "^2.2.1", | ||
"react": "^17.0.1", | ||
"react-dom": "^17.0.1", | ||
"rollup": "^2.33.1", | ||
"rollup": "^2.38.0", | ||
"rollup-plugin-babel": "^4.4.0", | ||
"rollup-plugin-node-resolve": "^5.2.0", | ||
"rollup-plugin-size-snapshot": "^0.12.0", | ||
"typescript": "^4.0.5" | ||
"typescript": "^4.1.3" | ||
} | ||
} |
420
README.md
@@ -1,2 +0,2 @@ | ||
# React Tooltip | ||
# react-popper-tooltip | ||
@@ -8,337 +8,259 @@ [![npm version](https://img.shields.io/npm/v/react-popper-tooltip.svg?style=flat-square)](https://www.npmjs.com/package/react-popper-tooltip) | ||
React tooltip component based on [react-popper](https://github.com/FezVrasta/react-popper), the React wrapper around [popper.js](https://popper.js.org) library. | ||
A React hook to effortlessly build smart tooltips. Based on [react-popper](https://github.com/FezVrasta/react-popper) | ||
and [popper.js](https://popper.js.org). | ||
## Homepage | ||
## Docs | ||
https://react-popper-tooltip.netlify.app | ||
NOTE: This is the documentation for v4.x which introduced the `usePopperTooltip` hook. | ||
## Example | ||
If you're looking for the render prop version, | ||
see [3.x docs](https://github.com/mohsinulhaq/react-popper-tooltip/blob/v3/README.md). | ||
https://codesandbox.io/s/pykkz77z5j | ||
If you're moving from 3.x render prop to 4.x hook, read our [migration guide](migrating.md). | ||
### Usage | ||
## Examples | ||
```bash | ||
npm install react-popper-tooltip | ||
``` | ||
- Basic usage [Demo]() [Source](/examples/basic) | ||
- Animating appearance with react-spring [Demo]() [Source](/examples/animation) | ||
- Closing tooltip with Esc button [Demo]() [Source](/examples/close-on-esc) | ||
- Using as a controlled component [Demo]() [Source](/examples/controlled) | ||
- Persist the tooltip in the DOM once it's mounted [Demo]() [Source](/examples/persist-once-mounted) | ||
- Using with react portal [Demo]() [Source](/examples/portal) | ||
- Implementing render prop API [Demo]() [Source](/examples/render-prop) | ||
or | ||
## Installation | ||
You can install **react-popper-tooltip** with [NPM](https://www.npmjs.com/) or [Yarn](https://yarnpkg.com/). | ||
```bash | ||
npm i react-popper-tooltip | ||
# or | ||
yarn add react-popper-tooltip | ||
``` | ||
```jsx | ||
import React from 'react'; | ||
import {render} from 'react-dom'; | ||
import TooltipTrigger from 'react-popper-tooltip'; | ||
const Tooltip = ({ | ||
arrowRef, | ||
tooltipRef, | ||
getArrowProps, | ||
getTooltipProps, | ||
placement | ||
}) => ( | ||
<div | ||
{...getTooltipProps({ | ||
ref: tooltipRef, | ||
className: 'tooltip-container' | ||
/* your props here */ | ||
})} | ||
> | ||
<div | ||
{...getArrowProps({ | ||
ref: arrowRef, | ||
className: 'tooltip-arrow', | ||
'data-placement': placement | ||
/* your props here */ | ||
})} | ||
/> | ||
Hello, World! | ||
</div> | ||
); | ||
const Trigger = ({getTriggerProps, triggerRef}) => ( | ||
<span | ||
{...getTriggerProps({ | ||
ref: triggerRef, | ||
className: 'trigger' | ||
/* your props here */ | ||
})} | ||
> | ||
Click Me! | ||
</span> | ||
); | ||
render( | ||
<TooltipTrigger placement="right" trigger="click" tooltip={Tooltip}> | ||
{Trigger} | ||
</TooltipTrigger>, | ||
document.getElementById('root') | ||
); | ||
``` | ||
`TooltipTrigger` is the only component exposed by the package. It's just a positioning engine. What to render is left completely to the user, which can be provided using render props. Your props should be passed through `getTriggerProps`, `getTooltipProps` and `getArrowProps`. | ||
Read more about [render prop](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce) pattern if you're not familiar with this approach. | ||
## Quick start | ||
If you would like our opinionated container and arrow styles for your tooltip for quick start, you may import `react-popper-tooltip/dist/styles.css`, and use the classes `tooltip-container` and `tooltip-arrow` as follows: | ||
This example illustrates how to create a minimal tooltip with default settings and using our default CSS file. | ||
### Tooltip.js | ||
```jsx | ||
import React from 'react'; | ||
import TooltipTrigger from 'react-popper-tooltip'; | ||
import * as React from 'react'; | ||
import { usePopperTooltip } from 'react-popper-tooltip'; | ||
import 'react-popper-tooltip/dist/styles.css'; | ||
const Tooltip = ({children, tooltip, hideArrow, ...props}) => ( | ||
<TooltipTrigger | ||
{...props} | ||
tooltip={({ | ||
arrowRef, | ||
tooltipRef, | ||
getArrowProps, | ||
getTooltipProps, | ||
placement | ||
}) => ( | ||
<div | ||
{...getTooltipProps({ | ||
ref: tooltipRef, | ||
className: 'tooltip-container' | ||
})} | ||
> | ||
{!hideArrow && ( | ||
<div | ||
{...getArrowProps({ | ||
ref: arrowRef, | ||
className: 'tooltip-arrow', | ||
'data-placement': placement | ||
})} | ||
/> | ||
)} | ||
{tooltip} | ||
</div> | ||
)} | ||
> | ||
{({getTriggerProps, triggerRef}) => ( | ||
<span | ||
{...getTriggerProps({ | ||
ref: triggerRef, | ||
className: 'trigger' | ||
})} | ||
> | ||
{children} | ||
</span> | ||
)} | ||
</TooltipTrigger> | ||
); | ||
function App() { | ||
const { | ||
getArrowProps, | ||
getTooltipProps, | ||
setTooltipRef, | ||
setTriggerRef, | ||
visible, | ||
} = usePopperTooltip(); | ||
export default Tooltip; | ||
``` | ||
return ( | ||
<div className="App"> | ||
<button type="button" ref={setTriggerRef}> | ||
Trigger | ||
</button> | ||
{visible && ( | ||
<div | ||
ref={setTooltipRef} | ||
{...getTooltipProps({ className: 'tooltip-container' })} | ||
> | ||
<div {...getArrowProps({ className: 'tooltip-arrow' })} /> | ||
Tooltip | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} | ||
Then you can use it as shown in the example below. | ||
```jsx | ||
<Tooltip placement="top" trigger="click" tooltip="Hi there!"> | ||
Click me | ||
</Tooltip> | ||
render(<App />, document.getElementById('root')); | ||
``` | ||
## Examples | ||
## Styling | ||
To fiddle with our example recipes, run: | ||
With **react-popper-tooltip**, you can use CSS, LESS, SASS, or any CSS-in-JS library you're already using in your | ||
project. However, we do provide a minimal CSS-file file you can use for a quick start or as a reference to create your | ||
own tooltip styles. | ||
```bash | ||
> npm install | ||
> npm run docs | ||
``` | ||
Import `react-popper-tooltip/dist/styles.css` to import it into your project. Add classes | ||
`tooltip-container` and `tooltip-arrow` to the tooltip container and arrow element accordingly. | ||
or | ||
While the tooltip is being displayed, you have access to some attributes on the tooltip container. You can use them | ||
in your CSS in specific scenarios. | ||
```bash | ||
> yarn | ||
> yarn docs | ||
``` | ||
- `data-popper-placement`: contains the current tooltip placement. You can use it to properly offset and display the | ||
arrow element (e.g., if the tooltip is displayed on the right, the arrow should point to the left and vice versa). | ||
and open up [localhost:3000](http://localhost:3000) in your browser. | ||
- `data-popper-reference-hidden`: set to true when the trigger element is fully clipped and hidden from view, which | ||
causes the tooltip to appear to be attached to nothing. Set to false otherwise. | ||
## Props | ||
- `data-popper-escaped`: set to true when the tooltip escapes the trigger element's boundary (and so it appears | ||
detached). Set to false otherwise. | ||
### children | ||
## API reference | ||
> `function({})` | _required_ | ||
### usePopperTooltip | ||
This is called with an object. Read more about the properties of this object in | ||
the section "[Children and tooltip functions](#children-and-tooltip-functions)". | ||
```jsx | ||
const { | ||
getArrowProps, | ||
getTooltipProps, | ||
setTooltipRef, | ||
setTriggerRef, | ||
tooltipRef, | ||
triggerRef, | ||
visible, | ||
...popperProps | ||
} = usePopperTooltip( | ||
{ | ||
closeOnOutsideClick, | ||
closeOnTriggerHidden, | ||
defaultVisible, | ||
delayHide, | ||
delayShow, | ||
followCursor, | ||
interactive, | ||
mutationObserverOptions, | ||
offset, | ||
onVisibleChange, | ||
placement, | ||
trigger, | ||
visible, | ||
}, | ||
popperOptions | ||
); | ||
``` | ||
### tooltip | ||
#### Options | ||
> `function({})` | _required_ | ||
- `closeOnOutsideClick: boolean`, defaults to `true` | ||
This is called with an object. Read more about the properties of this object in | ||
the section "[Children and tooltip functions](#children-and-tooltip-functions)". | ||
If `true`, closes the tooltip when user clicks outside the trigger element. | ||
### defaultTooltipShown | ||
- `closeOnTriggerHidden: boolean`, defaults to `false` | ||
> `boolean` | defaults to `false` | ||
Whether to close the tooltip when its trigger is out of boundary. | ||
This is the initial visibility state of the tooltip. | ||
- `delayHide: number`, defaults to `0` | ||
### onVisibilityChange | ||
Delay in hiding the tooltip (ms). | ||
> `function(tooltipShown: boolean)` | ||
- `delayShow: number`, defaults to `0` | ||
Called with the tooltip state, when the visibility of the tooltip changes | ||
### tooltipShown | ||
> `boolean` | **control prop** | ||
Use this prop if you want to control the visibility state of the tooltip. | ||
`react-popper-tooltip` manages its own state internally. You can use this prop to pass the | ||
visibility state of the tooltip from the outside. You will be required to keep this state up to | ||
date (this is where `onVisibilityChange` becomes useful), but you can also control the state | ||
from anywhere, be that state from other components, `redux`, `react-router`, or anywhere else. | ||
### delayShow | ||
> `number` | defaults to `0` | ||
Delay in showing the tooltip (ms). | ||
### delayHide | ||
- `defaultVisible: boolean`, defaults to `false` | ||
> `number` | defaults to `0` | ||
The initial visibility state of the tooltip when the hook is initialized. | ||
Delay in hiding the tooltip (ms). | ||
- `followCursor: boolean`, defaults to `false` | ||
### placement | ||
If `true`, the tooltip will stick to the cursor position. You would probably want to use this option with hover trigger. | ||
> `string` | defaults to `right` | ||
- `mutationObserverOptions: MutationObserverInit | null`, defaults | ||
to `{ attributes: true, childList: true, subtree: true }` | ||
The tooltip placement. Valid placements are: | ||
Options to [MutationObserver | ||
](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver), used internally for updating tooltip position based on its DOM changes. When the tooltip is | ||
visible and its content changes, it automatically repositions itself. In some cases | ||
you may need to change which parameters to observe or opt-out of tracking the changes at all. | ||
- `auto` | ||
- `top` | ||
- `right` | ||
- `bottom` | ||
- `left` | ||
- `offset: [number, number]`, defaults to `[0, 4]` | ||
Each placement can have a variation from this list: | ||
This is a shorthand for `popperOptions.modifiers` offset modifier option. The default value means the tooltip will be | ||
placed 4px away from the trigger element (to reserve enough space for the arrow element). | ||
- `-start` | ||
- `-end` | ||
We use this default value to match the size of the arrow element from our default CSS file. Feel free to change it if you are using your | ||
own styles. | ||
### trigger | ||
See [offset modifier docs](https://popper.js.org/docs/v2/modifiers/offset/). | ||
> `string` or `string[]` | defaults to `"hover"` | ||
`popperOptions` takes precedence over this option. | ||
The event(s) that trigger the tooltip. One of `click`, `right-click`, `hover`, `focus`, and `none`, or an array of them. | ||
- `onVisibleChange: (state: boolean) => void` | ||
### getTriggerRef | ||
Called with the tooltip state, when the visibility of the tooltip changes. | ||
> `function(HTMLElement) => void` | ||
- `trigger: TriggerType | TriggerType[] | null`, where `TriggerType = 'click' | 'right-click' | 'hover' | 'focus'`, | ||
defaults to `hover` | ||
Function that can be used to obtain a trigger element reference. | ||
Event or events that trigger the tooltip. Use `null` if you want to disable all events. It's useful in cases when | ||
you control the state of the tooltip. | ||
### closeOnReferenceHidden | ||
- `visible: boolean` | ||
> `boolean` | defaults to `true` | ||
The visibility state of the tooltip. Use this prop if you want to control the state of the tooltip. | ||
Whether to close the tooltip when its trigger is out of boundary. | ||
**react-popper-tooltip** manages its own state internally and calls `onVisibleChange` handler with any relevant changes. | ||
### usePortal | ||
However, if more control is needed, you can pass this prop, and the state becomes controlled. As soon as it's not | ||
undefined, internally, **react-popper-tooltip** will determine its state based on your prop's value rather than its own | ||
internal state. | ||
> `boolean` | defaults to `true` | ||
- `placement: 'auto' | 'auto-start' | 'auto-end' | 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'right' | 'right-start' | 'right-end' | 'left' | 'left-start' | 'left-end';` | ||
Whether to use `React.createPortal` for creating tooltip. | ||
The preferred placement of the tooltip. This is an alias for `popperOptions.placement` option. | ||
### portalContainer | ||
`popperOptions` takes precedence over this option. | ||
> `HTMLElement` | defaults to `document.body` | ||
- `interactive: boolean`, defaults to `false` | ||
Element to be used as portal container | ||
If `true`, hovering the tooltip will keep it open. Normally, if you trigger the tooltip on hover event, the tooltip | ||
closes when the mouse cursor moves out of the trigger element. If it moves to the tooltip element, the tooltip stays | ||
open. It's useful if you want to allow your users to interact with the tooltip's content (select and copy text, click a | ||
link, etc.). In this case you might want to increase `delayHide` value to give the user more time to react. | ||
### followCursor | ||
- `popperOptions: { placement, modifiers, strategy, onFirstUpdate }` | ||
> `boolean` | defaults to `false` | ||
These options passed directly to the underlying `usePopper` hook. | ||
See [https://popper.js.org/docs/v2/constructors/#options](https://popper.js.org/docs/v2/constructors/#options). | ||
Whether to spawn the tooltip at the cursor position. | ||
Keep in mind, if you set `placement` or _any_ `modifiers` here, it replaces `offset` and `placement` options above. They | ||
won't be merged into the final object. You have to add `offset` modifier along with others here to make it work. | ||
Recommended usage with hover trigger and no arrow element | ||
#### Returns | ||
### modifiers | ||
- `triggerRef: HTMLElement | null` | ||
> `array` | defaults to [] | ||
The trigger DOM element ref. | ||
Modifiers passed directly to the underlying popper.js instance. For more information, refer to Popper.js’ [modifier](https://popper.js.org/docs/v2/modifiers) docs. | ||
- `tooltipRef: HTMLElement | null` | ||
### mutationObserverOptions | ||
The tooltip DOM element ref. | ||
> `object` | ||
- `setTooltipRef: (HTMLElement | null) => void | null` | ||
Options to `MutationObserver`, used internally for updating tooltip position based on its DOM changes. | ||
For more information, refer to [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) docs. | ||
A tooltip callback ref. Must be assigned to the tooltip's `ref` prop. | ||
Default options: | ||
- `setTriggerRef: (HTMLElement | null) => void | null` | ||
``` | ||
{ | ||
childList: true, | ||
subtree: true | ||
} | ||
``` | ||
A trigger callback ref. Must be assigned to the trigger's `ref` prop. | ||
## Children and tooltip functions | ||
- `visible: boolean` | ||
This is where you render whatever you want. `react-popper-tooltip` uses two render props `children` | ||
and `tooltip`. `Children` prop is used to trigger the appearance of the tooltip and `tooltip` | ||
displays the tooltip itself. | ||
The current visibility state of the tooltip. Use it to display or hide the tooltip. | ||
You use it like so: | ||
- `getArrowProps: (props) => mergedProps` | ||
```jsx | ||
const tooltip = ( | ||
<TooltipTrigger tooltip={tooltip => <div>{/* more jsx here */}</div>}> | ||
{trigger => <div>{/* more jsx here */}</div>} | ||
</TooltipTrigger> | ||
); | ||
``` | ||
This function merges your props and the internal props of the arrow element. We recommend passing all your props to that | ||
function rather than applying them on the element directly to avoid your props being overridden or overriding the | ||
internal props. | ||
### prop getters | ||
It returns the merged props that you need to pass to the arrow element. | ||
> See [the blog post about prop getters](https://blog.kentcdodds.com/how-to-give-rendering-control-to-users-with-prop-getters-549eaef76acf) | ||
- `getTooltipProps: (props) => mergedProps` | ||
These functions are used to apply props to the elements that you render. | ||
It's advisable to pass all your props to that function rather than applying them on the element | ||
yourself to avoid your props being overridden (or overriding the props returned). For example | ||
`<button {...getTriggerProps({onClick: event => console.log(event))}>Click me</button>` | ||
This function merges your props and the internal props of the tooltip element. We recommend passing all your props to | ||
that function rather than applying them on the element directly to avoid your props being overridden or overriding the | ||
internal props. | ||
### children function | ||
It returns the merged props that you need to pass to tooltip element. | ||
| property | type | description | | ||
| --------------- | -------------- | --------------------------------------------------------------------- | | ||
| triggerRef | `function ref` | returns the react ref you should apply to the trigger element. | | ||
| getTriggerProps | `function({})` | returns the props you should apply to the trigger element you render. | | ||
- `popperProps: { update, forceUpdate, state }` | ||
### tooltip function | ||
Some props returned by the underlying `usePopper` hook. | ||
See [https://popper.js.org/react-popper/v2/hook/](https://popper.js.org/react-popper/v2/hook/). | ||
| property | type | description | | ||
| --------------- | -------------- | --------------------------------------------------------------------- | | ||
| arrowRef | `function ref` | return the react ref you should apply to the tooltip arrow element. | | ||
| tooltipRef | `function ref` | return the react ref you should apply to the tooltip element. | | ||
| getArrowProps | `function({})` | return the props you should apply to the tooltip arrow element. | | ||
| getTooltipProps | `function({})` | returns the props you should apply to the tooltip element you render. | | ||
| placement | `string` | return the dynamic placement of the tooltip. | | ||
## Inspiration and Thanks! | ||
This library is based on [react-popper](https://github.com/FezVrasta/react-popper), the official | ||
react wrapper around [Popper.js](https://popper.js.org). | ||
Using of render props, prop getters and doc style of this library are heavily inspired by | ||
[downshift](https://github.com/paypal/downshift). | ||
This doesn't include `styles` and `attributes` props. They are included into `getArrowProps` and `getTooltipProps` prop | ||
getters. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1030036
5620
34
1
266
1
+ Addedreact@18.3.1(transitive)
+ Addedreact-dom@18.3.1(transitive)
+ Addedscheduler@0.23.2(transitive)
- Removedobject-assign@4.1.1(transitive)
- Removedreact@17.0.2(transitive)
- Removedreact-dom@17.0.2(transitive)
- Removedscheduler@0.20.2(transitive)
Updated@popperjs/core@^2.6.0