@atlaskit/tooltip
Advanced tools
Comparing version 9.0.0 to 9.1.0
# @atlaskit/tooltip | ||
## 9.1.0 | ||
- [patch] Improve viewport edge collision detection. Tooltips will now shift along the secondary position axis (e.g. left/right when position is top/bottom) to show within viewport. Fix auto flip occurring incorrectly in these situations as well. [ebf331a](https://bitbucket.org/atlassian/atlaskit-mk-2/commits/ebf331a) | ||
- [minor] Add new 'mouse' value for position prop and mousePosition prop to allow the tooltip to display relative to the mouse. [1d5577d](https://bitbucket.org/atlassian/atlaskit-mk-2/commits/1d5577d) | ||
## 9.0.0 | ||
@@ -4,0 +8,0 @@ - [major] Bump to React 16.3. [4251858](https://bitbucket.org/atlassian/atlaskit-mk-2/commits/4251858) |
@@ -112,6 +112,8 @@ 'use strict'; | ||
position = _ref2.position, | ||
props = (0, _objectWithoutProperties3.default)(_ref2, ['immediatelyHide', 'immediatelyShow', 'position']); | ||
mousePosition = _ref2.mousePosition, | ||
props = (0, _objectWithoutProperties3.default)(_ref2, ['immediatelyHide', 'immediatelyShow', 'position', 'mousePosition']); | ||
var horizontalOffset = xPos[position]; | ||
var verticalOffset = yPos[position]; | ||
var truePosition = position === 'mouse' ? mousePosition : position; | ||
var horizontalOffset = xPos[truePosition]; | ||
var verticalOffset = yPos[truePosition]; | ||
@@ -118,0 +120,0 @@ var restingTransform = 'translate3d(0, 0, 0)'; |
@@ -43,2 +43,7 @@ 'use strict'; | ||
(0, _createClass3.default)(TooltipMarshal, [{ | ||
key: 'immediatelySwitch', | ||
value: function immediatelySwitch() { | ||
return this.visibleTooltip && this.visibleTooltip.state.position !== 'mouse'; | ||
} | ||
}, { | ||
key: 'show', | ||
@@ -66,3 +71,3 @@ value: function show(tooltip) { | ||
// displayed, immediately switch them | ||
if (this.visibleTooltip) { | ||
if (this.immediatelySwitch()) { | ||
// the visible tooltip may be queued to be hidden; prevent that | ||
@@ -69,0 +74,0 @@ if (this.queuedForHide) { |
@@ -7,10 +7,2 @@ 'use strict'; | ||
var _extends2 = require('babel-runtime/helpers/extends'); | ||
var _extends3 = _interopRequireDefault(_extends2); | ||
var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties'); | ||
var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2); | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
@@ -73,2 +65,3 @@ | ||
position: props.position, | ||
mousePosition: props.mousePosition, | ||
coordinates: null | ||
@@ -79,2 +72,3 @@ }; | ||
/* eslint-disable react/sort-comp */ | ||
var Tooltip = function (_Component) { | ||
@@ -94,3 +88,3 @@ (0, _inherits3.default)(Tooltip, _Component); | ||
return _ret = (_temp = (_this = (0, _possibleConstructorReturn3.default)(this, (_ref = Tooltip.__proto__ || (0, _getPrototypeOf2.default)(Tooltip)).call.apply(_ref, [this].concat(args))), _this), _this.state = getInitialState(_this.props), _this.handleWrapperRef = function (ref) { | ||
return _ret = (_temp = (_this = (0, _possibleConstructorReturn3.default)(this, (_ref = Tooltip.__proto__ || (0, _getPrototypeOf2.default)(Tooltip)).call.apply(_ref, [this].concat(args))), _this), _this.state = getInitialState(_this.props), _this.mouseCoordinates = null, _this.handleWrapperRef = function (ref) { | ||
_this.wrapper = ref; | ||
@@ -100,12 +94,18 @@ }, _this.handleMeasureRef = function (tooltip) { | ||
var position = _this.props.position; | ||
var _this$props = _this.props, | ||
position = _this$props.position, | ||
mousePosition = _this$props.mousePosition; | ||
var _this2 = _this, | ||
mouseCoordinates = _this2.mouseCoordinates; | ||
var target = _this.wrapper.children.length ? _this.wrapper.children[0] : _this.wrapper; | ||
// NOTE getPosition returns: | ||
// position Enum(top | left | bottom | right) | ||
// - adjusted for edge collision | ||
// coordinates: Object(left: number, top: number) | ||
// - coordinates passed to Transition | ||
_this.setState((0, _utils.getPosition)({ position: position, target: target, tooltip: tooltip })); | ||
var positionData = (0, _utils.getPosition)({ | ||
position: position, | ||
target: target, | ||
tooltip: tooltip, | ||
mouseCoordinates: mouseCoordinates, | ||
mousePosition: mousePosition | ||
}); | ||
_this.setState(positionData); | ||
}, _this.show = function (_ref2) { | ||
@@ -129,3 +129,2 @@ var immediate = _ref2.immediate; | ||
var onMouseOver = _this.props.onMouseOver; | ||
// bail if over the wrapper, we only want to target the first child. | ||
@@ -148,2 +147,7 @@ | ||
if (onMouseOut) onMouseOut(event); | ||
}, _this.handleMouseMove = function (event) { | ||
_this.mouseCoordinates = { | ||
left: event.clientX, | ||
top: event.clientY | ||
}; | ||
}, _this.handleClick = function () { | ||
@@ -161,8 +165,9 @@ var hideTooltipOnClick = _this.props.hideTooltipOnClick; | ||
var position = nextProps.position, | ||
truncate = nextProps.truncate; | ||
truncate = nextProps.truncate, | ||
mousePosition = nextProps.mousePosition; | ||
// handle case where position is changed while visible | ||
if (position !== this.props.position) { | ||
this.setState({ position: position, coordinates: null }); | ||
if (position !== this.props.position || mousePosition !== this.props.mousePosition) { | ||
this.setState({ position: position, mousePosition: mousePosition, coordinates: null }); | ||
} | ||
@@ -186,2 +191,3 @@ | ||
isVisible = _state.isVisible, | ||
mousePosition = _state.mousePosition, | ||
position = _state.position, | ||
@@ -216,2 +222,3 @@ coordinates = _state.coordinates; | ||
immediatelyShow: immediatelyShow, | ||
mousePosition: mousePosition, | ||
position: position, | ||
@@ -227,21 +234,17 @@ coordinates: coordinates, | ||
} | ||
// eslint-disable-next-line react/no-unused-prop-types | ||
// Update mouse coordinates, used when position is 'mouse'. | ||
// We are not debouncing/throttling this function because we aren't causing any | ||
// re-renders or performaing any intensive calculations, we're just updating a value. | ||
// React also doesn't play nice debounced DOM event handlers because they pool their | ||
// SyntheticEvent objects. Need to use event.persist as a workaround - https://stackoverflow.com/a/24679479/893630 | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
// NOTE removing props from rest: | ||
// - `content` is a valid HTML attribute, but has a different semantic meaning | ||
// - `component` is NOT valid and react will warn | ||
// - `hideTooltipOnClick` is NOT valid and react will warn | ||
// - `position` is NOT valid and react will warn | ||
// - `truncate` is NOT valid and react will warn | ||
// eslint-disable-next-line no-unused-vars | ||
var _props2 = this.props, | ||
children = _props2.children, | ||
component = _props2.component, | ||
content = _props2.content, | ||
hideTooltipOnClick = _props2.hideTooltipOnClick, | ||
position = _props2.position, | ||
truncate = _props2.truncate, | ||
Tag = _props2.tag, | ||
rest = (0, _objectWithoutProperties3.default)(_props2, ['children', 'component', 'content', 'hideTooltipOnClick', 'position', 'truncate', 'tag']); | ||
Tag = _props2.tag; | ||
@@ -251,8 +254,9 @@ | ||
Tag, | ||
(0, _extends3.default)({ | ||
{ | ||
onClick: this.handleClick, | ||
onMouseMove: this.handleMouseMove, | ||
onMouseOver: this.handleMouseOver, | ||
onMouseOut: this.handleMouseOut, | ||
ref: this.handleWrapperRef | ||
}, rest), | ||
}, | ||
_react.Children.only(children), | ||
@@ -269,2 +273,3 @@ this.renderTooltip() | ||
position: 'bottom', | ||
mousePosition: 'bottom', | ||
tag: 'div' | ||
@@ -271,0 +276,0 @@ }; |
@@ -54,2 +54,3 @@ 'use strict'; | ||
immediatelyShow = _props.immediatelyShow, | ||
mousePosition = _props.mousePosition, | ||
position = _props.position, | ||
@@ -69,2 +70,3 @@ truncate = _props.truncate; | ||
'in': transitionIn, | ||
mousePosition: mousePosition, | ||
position: position, | ||
@@ -71,0 +73,0 @@ style: coordinates, |
@@ -6,8 +6,13 @@ 'use strict'; | ||
}); | ||
exports.default = getPosition; | ||
var _inViewport = require('./inViewport'); | ||
var _assign = require('babel-runtime/core-js/object/assign'); | ||
var _inViewport2 = _interopRequireDefault(_inViewport); | ||
var _assign2 = _interopRequireDefault(_assign); | ||
var _keys = require('babel-runtime/core-js/object/keys'); | ||
var _keys2 = _interopRequireDefault(_keys); | ||
exports.default = getPosition; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -22,8 +27,98 @@ | ||
function getCoords(_ref) { | ||
var targetRect = _ref.targetRect, | ||
tooltipRect = _ref.tooltipRect, | ||
gutter = _ref.gutter; | ||
// Returns a top or left position that shifts the original coord to within viewport | ||
function shiftCoord(coordName, coords, gutter) { | ||
var shiftedCoord = {}; | ||
if (coordName === 'top' || coordName === 'left') { | ||
shiftedCoord[coordName] = 0 + gutter; | ||
} | ||
var docEl = document.documentElement; | ||
if (coordName === 'bottom') { | ||
var viewportHeight = window.innerHeight || docEl && docEl.clientHeight || 0; | ||
var amountClipped = coords.bottom - viewportHeight; | ||
var shiftedTop = coords.top - amountClipped - gutter; | ||
shiftedCoord.top = shiftedTop >= 0 ? shiftedTop : coords.top; | ||
} else if (coordName === 'right') { | ||
var viewportWidth = window.innerWidth || docEl && docEl.clientWidth || 0; | ||
var _amountClipped = coords.right - viewportWidth; | ||
var shiftedLeft = coords.left - _amountClipped - gutter; | ||
shiftedCoord.left = shiftedLeft >= 0 ? shiftedLeft : coords.top; | ||
} | ||
return shiftedCoord; | ||
} | ||
// Returns a map of positions to whether they fit in viewport | ||
function getViewportBounds(_ref, gutter) { | ||
var top = _ref.top, | ||
right = _ref.right, | ||
bottom = _ref.bottom, | ||
left = _ref.left; | ||
var docEl = document.documentElement; | ||
return { | ||
top: top >= 0 + gutter, | ||
left: left >= 0 + gutter, | ||
bottom: bottom <= (window.innerHeight || docEl && docEl.clientHeight || 0) - gutter, | ||
right: right <= (window.innerWidth || docEl && docEl.clientWidth || 0) - gutter | ||
}; | ||
} | ||
// Get the viewport bounds for each position coord | ||
function getAllViewportBounds(allCoords, gutter) { | ||
var viewportBounds = {}; | ||
(0, _keys2.default)(allCoords).forEach(function (position) { | ||
var coords = allCoords[position]; | ||
viewportBounds[position] = getViewportBounds(coords, gutter); | ||
}); | ||
return viewportBounds; | ||
} | ||
// Adjust the position and top/left coords to fit inside viewport | ||
// Performs flipping on the primary axis and shifting on the secondary axis | ||
function adjustPosition(originalPosition, positionCoords, gutter) { | ||
var flippedPosition = FLIPPED_POSITION[originalPosition]; | ||
var viewportBounds = getAllViewportBounds(positionCoords, gutter); | ||
// Should flip if the original position was not within bounds and the new position is | ||
var shouldFlip = !viewportBounds[originalPosition][originalPosition] && viewportBounds[flippedPosition][originalPosition]; | ||
var adjustedPosition = shouldFlip ? flippedPosition : originalPosition; | ||
// Check secondary axis, for positional shift | ||
var shiftedCoords = {}; | ||
var secondaryPositions = (0, _keys2.default)(FLIPPED_POSITION).filter(function (position) { | ||
return position !== originalPosition && position !== flippedPosition; | ||
}); | ||
secondaryPositions.forEach(function (position) { | ||
var inViewport = viewportBounds[adjustedPosition][position]; | ||
if (!inViewport) { | ||
(0, _assign2.default)(shiftedCoords, shiftCoord(position, positionCoords[adjustedPosition], gutter)); | ||
} | ||
}); | ||
// adjust positions with flipped position on main axis + shifted position on secondary axis | ||
var left = shiftedCoords.left != null ? shiftedCoords.left : positionCoords[adjustedPosition].left; | ||
var top = shiftedCoords.top != null ? shiftedCoords.top : positionCoords[adjustedPosition].top; | ||
return { left: left, top: top, adjustedPosition: adjustedPosition }; | ||
} | ||
function getCoords(_ref2) { | ||
var targetRect = _ref2.targetRect, | ||
tooltipRect = _ref2.tooltipRect, | ||
gutter = _ref2.gutter; | ||
return { | ||
top: { | ||
@@ -56,12 +151,93 @@ top: targetRect.top - (tooltipRect.height + gutter), | ||
function getPosition(_ref2) { | ||
var position = _ref2.position, | ||
target = _ref2.target, | ||
tooltip = _ref2.tooltip; | ||
function getMouseCoords(_ref3) { | ||
var mouseCoordinates = _ref3.mouseCoordinates, | ||
tooltipRect = _ref3.tooltipRect, | ||
gutter = _ref3.gutter; | ||
var cursorPaddingRight = 8; | ||
var cursorPaddingBottom = 16; | ||
return { | ||
top: { | ||
top: mouseCoordinates.top - (tooltipRect.height + gutter), | ||
right: mouseCoordinates.left + tooltipRect.width / 2, | ||
bottom: mouseCoordinates.top - gutter, | ||
left: mouseCoordinates.left - tooltipRect.width / 2 | ||
}, | ||
right: { | ||
top: mouseCoordinates.top - tooltipRect.height / 2, | ||
right: mouseCoordinates.left + cursorPaddingRight + gutter + tooltipRect.width, | ||
bottom: mouseCoordinates.top + tooltipRect.height / 2, | ||
left: mouseCoordinates.left + cursorPaddingRight + gutter | ||
}, | ||
bottom: { | ||
top: mouseCoordinates.top + cursorPaddingBottom + gutter, | ||
right: mouseCoordinates.left + tooltipRect.width / 2, | ||
bottom: mouseCoordinates.top + cursorPaddingBottom + gutter + tooltipRect.height, | ||
left: mouseCoordinates.left - tooltipRect.width / 2 | ||
}, | ||
left: { | ||
top: mouseCoordinates.top - tooltipRect.height / 2, | ||
right: mouseCoordinates.left - gutter, | ||
bottom: mouseCoordinates.top + tooltipRect.height / 2, | ||
left: mouseCoordinates.left - (tooltipRect.width + gutter) | ||
} | ||
}; | ||
} | ||
function getMousePosition(_ref4) { | ||
var mousePosition = _ref4.mousePosition, | ||
tooltip = _ref4.tooltip, | ||
mouseCoordinates = _ref4.mouseCoordinates; | ||
var noPosition = { | ||
coordinates: { left: 0, top: 0 }, | ||
position: 'bottom' | ||
position: 'mouse', | ||
mousePosition: 'bottom' | ||
}; | ||
if (!mousePosition) throw new Error('Property "mousePosition" is required.'); | ||
if (!tooltip) throw new Error('Property "tooltip" is required.'); | ||
if (!mouseCoordinates) return noPosition; | ||
// get the original coordinates | ||
var gutter = 8; | ||
var tooltipRect = tooltip.getBoundingClientRect(); | ||
var POSITIONS = getMouseCoords({ mouseCoordinates: mouseCoordinates, tooltipRect: tooltipRect, gutter: gutter }); | ||
var _adjustPosition = adjustPosition(mousePosition, POSITIONS, gutter), | ||
left = _adjustPosition.left, | ||
top = _adjustPosition.top, | ||
adjustedPosition = _adjustPosition.adjustedPosition; | ||
return { | ||
coordinates: { left: left, top: top }, | ||
position: 'mouse', | ||
mousePosition: adjustedPosition | ||
}; | ||
} | ||
/** | ||
* Gets the coordinates and adjusted position of a tooltip. | ||
* Position will be flipped on the primary axis with respect to the initial position | ||
* if there is not enough space in the viewport. | ||
* Coordinates will be shifted along the secondary axis to render within viewport. | ||
*/ | ||
function getPosition(_ref5) { | ||
var position = _ref5.position, | ||
target = _ref5.target, | ||
tooltip = _ref5.tooltip, | ||
mouseCoordinates = _ref5.mouseCoordinates, | ||
mousePosition = _ref5.mousePosition; | ||
if (position === 'mouse') { | ||
return getMousePosition({ mousePosition: mousePosition, tooltip: tooltip, mouseCoordinates: mouseCoordinates }); | ||
} | ||
var noPosition = { | ||
coordinates: { left: 0, top: 0 }, | ||
position: 'bottom', | ||
mousePosition: mousePosition | ||
}; | ||
/* eslint-disable no-console */ | ||
@@ -81,16 +257,12 @@ if (!position) console.error('Property "position" is required.'); | ||
// set tooltip positions before viewport check | ||
var attemptedPosition = POSITIONS[position]; | ||
var _adjustPosition2 = adjustPosition(position, POSITIONS, gutter), | ||
left = _adjustPosition2.left, | ||
top = _adjustPosition2.top, | ||
adjustedPosition = _adjustPosition2.adjustedPosition; | ||
// check if the tooltip is in view or must be flipped | ||
var adjustedPosition = (0, _inViewport2.default)(attemptedPosition) ? position : FLIPPED_POSITION[position]; | ||
// adjust positions with (possibly) flipped position | ||
var left = POSITIONS[adjustedPosition].left; | ||
var top = POSITIONS[adjustedPosition].top; | ||
return { | ||
coordinates: { left: left, top: top }, | ||
position: adjustedPosition | ||
position: adjustedPosition, | ||
mousePosition: mousePosition | ||
}; | ||
} |
@@ -7,11 +7,2 @@ 'use strict'; | ||
var _inViewport = require('./inViewport'); | ||
Object.defineProperty(exports, 'inViewport', { | ||
enumerable: true, | ||
get: function get() { | ||
return _interopRequireDefault(_inViewport).default; | ||
} | ||
}); | ||
var _getPosition = require('./getPosition'); | ||
@@ -18,0 +9,0 @@ |
@@ -96,6 +96,8 @@ import _extends from 'babel-runtime/helpers/extends'; | ||
position = _ref2.position, | ||
props = _objectWithoutProperties(_ref2, ['immediatelyHide', 'immediatelyShow', 'position']); | ||
mousePosition = _ref2.mousePosition, | ||
props = _objectWithoutProperties(_ref2, ['immediatelyHide', 'immediatelyShow', 'position', 'mousePosition']); | ||
var horizontalOffset = xPos[position]; | ||
var verticalOffset = yPos[position]; | ||
var truePosition = position === 'mouse' ? mousePosition : position; | ||
var horizontalOffset = xPos[truePosition]; | ||
var verticalOffset = yPos[truePosition]; | ||
@@ -102,0 +104,0 @@ var restingTransform = 'translate3d(0, 0, 0)'; |
@@ -32,2 +32,7 @@ import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; | ||
_createClass(TooltipMarshal, [{ | ||
key: 'immediatelySwitch', | ||
value: function immediatelySwitch() { | ||
return this.visibleTooltip && this.visibleTooltip.state.position !== 'mouse'; | ||
} | ||
}, { | ||
key: 'show', | ||
@@ -55,3 +60,3 @@ value: function show(tooltip) { | ||
// displayed, immediately switch them | ||
if (this.visibleTooltip) { | ||
if (this.immediatelySwitch()) { | ||
// the visible tooltip may be queued to be hidden; prevent that | ||
@@ -58,0 +63,0 @@ if (this.queuedForHide) { |
@@ -1,3 +0,1 @@ | ||
import _extends from 'babel-runtime/helpers/extends'; | ||
import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties'; | ||
import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of'; | ||
@@ -31,2 +29,3 @@ import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; | ||
position: props.position, | ||
mousePosition: props.mousePosition, | ||
coordinates: null | ||
@@ -37,2 +36,3 @@ }; | ||
/* eslint-disable react/sort-comp */ | ||
var Tooltip = function (_Component) { | ||
@@ -52,3 +52,3 @@ _inherits(Tooltip, _Component); | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Tooltip.__proto__ || _Object$getPrototypeOf(Tooltip)).call.apply(_ref, [this].concat(args))), _this), _this.state = getInitialState(_this.props), _this.handleWrapperRef = function (ref) { | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Tooltip.__proto__ || _Object$getPrototypeOf(Tooltip)).call.apply(_ref, [this].concat(args))), _this), _this.state = getInitialState(_this.props), _this.mouseCoordinates = null, _this.handleWrapperRef = function (ref) { | ||
_this.wrapper = ref; | ||
@@ -58,12 +58,18 @@ }, _this.handleMeasureRef = function (tooltip) { | ||
var position = _this.props.position; | ||
var _this$props = _this.props, | ||
position = _this$props.position, | ||
mousePosition = _this$props.mousePosition; | ||
var _this2 = _this, | ||
mouseCoordinates = _this2.mouseCoordinates; | ||
var target = _this.wrapper.children.length ? _this.wrapper.children[0] : _this.wrapper; | ||
// NOTE getPosition returns: | ||
// position Enum(top | left | bottom | right) | ||
// - adjusted for edge collision | ||
// coordinates: Object(left: number, top: number) | ||
// - coordinates passed to Transition | ||
_this.setState(getPosition({ position: position, target: target, tooltip: tooltip })); | ||
var positionData = getPosition({ | ||
position: position, | ||
target: target, | ||
tooltip: tooltip, | ||
mouseCoordinates: mouseCoordinates, | ||
mousePosition: mousePosition | ||
}); | ||
_this.setState(positionData); | ||
}, _this.show = function (_ref2) { | ||
@@ -87,3 +93,2 @@ var immediate = _ref2.immediate; | ||
var onMouseOver = _this.props.onMouseOver; | ||
// bail if over the wrapper, we only want to target the first child. | ||
@@ -106,2 +111,7 @@ | ||
if (onMouseOut) onMouseOut(event); | ||
}, _this.handleMouseMove = function (event) { | ||
_this.mouseCoordinates = { | ||
left: event.clientX, | ||
top: event.clientY | ||
}; | ||
}, _this.handleClick = function () { | ||
@@ -119,8 +129,9 @@ var hideTooltipOnClick = _this.props.hideTooltipOnClick; | ||
var position = nextProps.position, | ||
truncate = nextProps.truncate; | ||
truncate = nextProps.truncate, | ||
mousePosition = nextProps.mousePosition; | ||
// handle case where position is changed while visible | ||
if (position !== this.props.position) { | ||
this.setState({ position: position, coordinates: null }); | ||
if (position !== this.props.position || mousePosition !== this.props.mousePosition) { | ||
this.setState({ position: position, mousePosition: mousePosition, coordinates: null }); | ||
} | ||
@@ -144,2 +155,3 @@ | ||
isVisible = _state.isVisible, | ||
mousePosition = _state.mousePosition, | ||
position = _state.position, | ||
@@ -174,2 +186,3 @@ coordinates = _state.coordinates; | ||
immediatelyShow: immediatelyShow, | ||
mousePosition: mousePosition, | ||
position: position, | ||
@@ -185,30 +198,28 @@ coordinates: coordinates, | ||
} | ||
// eslint-disable-next-line react/no-unused-prop-types | ||
// Update mouse coordinates, used when position is 'mouse'. | ||
// We are not debouncing/throttling this function because we aren't causing any | ||
// re-renders or performaing any intensive calculations, we're just updating a value. | ||
// React also doesn't play nice debounced DOM event handlers because they pool their | ||
// SyntheticEvent objects. Need to use event.persist as a workaround - https://stackoverflow.com/a/24679479/893630 | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
// NOTE removing props from rest: | ||
// - `content` is a valid HTML attribute, but has a different semantic meaning | ||
// - `component` is NOT valid and react will warn | ||
// - `hideTooltipOnClick` is NOT valid and react will warn | ||
// - `position` is NOT valid and react will warn | ||
// - `truncate` is NOT valid and react will warn | ||
// eslint-disable-next-line no-unused-vars | ||
var _props2 = this.props, | ||
children = _props2.children, | ||
component = _props2.component, | ||
content = _props2.content, | ||
hideTooltipOnClick = _props2.hideTooltipOnClick, | ||
position = _props2.position, | ||
truncate = _props2.truncate, | ||
Tag = _props2.tag, | ||
rest = _objectWithoutProperties(_props2, ['children', 'component', 'content', 'hideTooltipOnClick', 'position', 'truncate', 'tag']); | ||
Tag = _props2.tag; | ||
return React.createElement( | ||
Tag, | ||
_extends({ | ||
{ | ||
onClick: this.handleClick, | ||
onMouseMove: this.handleMouseMove, | ||
onMouseOver: this.handleMouseOver, | ||
onMouseOut: this.handleMouseOut, | ||
ref: this.handleWrapperRef | ||
}, rest), | ||
}, | ||
Children.only(children), | ||
@@ -226,2 +237,3 @@ this.renderTooltip() | ||
position: 'bottom', | ||
mousePosition: 'bottom', | ||
tag: 'div' | ||
@@ -228,0 +240,0 @@ }; |
@@ -31,2 +31,3 @@ import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of'; | ||
immediatelyShow = _props.immediatelyShow, | ||
mousePosition = _props.mousePosition, | ||
position = _props.position, | ||
@@ -46,2 +47,3 @@ truncate = _props.truncate; | ||
'in': transitionIn, | ||
mousePosition: mousePosition, | ||
position: position, | ||
@@ -48,0 +50,0 @@ style: coordinates, |
@@ -1,3 +0,5 @@ | ||
import inViewport from './inViewport'; | ||
import _Object$assign from 'babel-runtime/core-js/object/assign'; | ||
import _Object$keys from 'babel-runtime/core-js/object/keys'; | ||
var FLIPPED_POSITION = { | ||
@@ -10,8 +12,98 @@ top: 'bottom', | ||
function getCoords(_ref) { | ||
var targetRect = _ref.targetRect, | ||
tooltipRect = _ref.tooltipRect, | ||
gutter = _ref.gutter; | ||
// Returns a top or left position that shifts the original coord to within viewport | ||
function shiftCoord(coordName, coords, gutter) { | ||
var shiftedCoord = {}; | ||
if (coordName === 'top' || coordName === 'left') { | ||
shiftedCoord[coordName] = 0 + gutter; | ||
} | ||
var docEl = document.documentElement; | ||
if (coordName === 'bottom') { | ||
var viewportHeight = window.innerHeight || docEl && docEl.clientHeight || 0; | ||
var amountClipped = coords.bottom - viewportHeight; | ||
var shiftedTop = coords.top - amountClipped - gutter; | ||
shiftedCoord.top = shiftedTop >= 0 ? shiftedTop : coords.top; | ||
} else if (coordName === 'right') { | ||
var viewportWidth = window.innerWidth || docEl && docEl.clientWidth || 0; | ||
var _amountClipped = coords.right - viewportWidth; | ||
var shiftedLeft = coords.left - _amountClipped - gutter; | ||
shiftedCoord.left = shiftedLeft >= 0 ? shiftedLeft : coords.top; | ||
} | ||
return shiftedCoord; | ||
} | ||
// Returns a map of positions to whether they fit in viewport | ||
function getViewportBounds(_ref, gutter) { | ||
var top = _ref.top, | ||
right = _ref.right, | ||
bottom = _ref.bottom, | ||
left = _ref.left; | ||
var docEl = document.documentElement; | ||
return { | ||
top: top >= 0 + gutter, | ||
left: left >= 0 + gutter, | ||
bottom: bottom <= (window.innerHeight || docEl && docEl.clientHeight || 0) - gutter, | ||
right: right <= (window.innerWidth || docEl && docEl.clientWidth || 0) - gutter | ||
}; | ||
} | ||
// Get the viewport bounds for each position coord | ||
function getAllViewportBounds(allCoords, gutter) { | ||
var viewportBounds = {}; | ||
_Object$keys(allCoords).forEach(function (position) { | ||
var coords = allCoords[position]; | ||
viewportBounds[position] = getViewportBounds(coords, gutter); | ||
}); | ||
return viewportBounds; | ||
} | ||
// Adjust the position and top/left coords to fit inside viewport | ||
// Performs flipping on the primary axis and shifting on the secondary axis | ||
function adjustPosition(originalPosition, positionCoords, gutter) { | ||
var flippedPosition = FLIPPED_POSITION[originalPosition]; | ||
var viewportBounds = getAllViewportBounds(positionCoords, gutter); | ||
// Should flip if the original position was not within bounds and the new position is | ||
var shouldFlip = !viewportBounds[originalPosition][originalPosition] && viewportBounds[flippedPosition][originalPosition]; | ||
var adjustedPosition = shouldFlip ? flippedPosition : originalPosition; | ||
// Check secondary axis, for positional shift | ||
var shiftedCoords = {}; | ||
var secondaryPositions = _Object$keys(FLIPPED_POSITION).filter(function (position) { | ||
return position !== originalPosition && position !== flippedPosition; | ||
}); | ||
secondaryPositions.forEach(function (position) { | ||
var inViewport = viewportBounds[adjustedPosition][position]; | ||
if (!inViewport) { | ||
_Object$assign(shiftedCoords, shiftCoord(position, positionCoords[adjustedPosition], gutter)); | ||
} | ||
}); | ||
// adjust positions with flipped position on main axis + shifted position on secondary axis | ||
var left = shiftedCoords.left != null ? shiftedCoords.left : positionCoords[adjustedPosition].left; | ||
var top = shiftedCoords.top != null ? shiftedCoords.top : positionCoords[adjustedPosition].top; | ||
return { left: left, top: top, adjustedPosition: adjustedPosition }; | ||
} | ||
function getCoords(_ref2) { | ||
var targetRect = _ref2.targetRect, | ||
tooltipRect = _ref2.tooltipRect, | ||
gutter = _ref2.gutter; | ||
return { | ||
top: { | ||
@@ -44,12 +136,93 @@ top: targetRect.top - (tooltipRect.height + gutter), | ||
export default function getPosition(_ref2) { | ||
var position = _ref2.position, | ||
target = _ref2.target, | ||
tooltip = _ref2.tooltip; | ||
function getMouseCoords(_ref3) { | ||
var mouseCoordinates = _ref3.mouseCoordinates, | ||
tooltipRect = _ref3.tooltipRect, | ||
gutter = _ref3.gutter; | ||
var cursorPaddingRight = 8; | ||
var cursorPaddingBottom = 16; | ||
return { | ||
top: { | ||
top: mouseCoordinates.top - (tooltipRect.height + gutter), | ||
right: mouseCoordinates.left + tooltipRect.width / 2, | ||
bottom: mouseCoordinates.top - gutter, | ||
left: mouseCoordinates.left - tooltipRect.width / 2 | ||
}, | ||
right: { | ||
top: mouseCoordinates.top - tooltipRect.height / 2, | ||
right: mouseCoordinates.left + cursorPaddingRight + gutter + tooltipRect.width, | ||
bottom: mouseCoordinates.top + tooltipRect.height / 2, | ||
left: mouseCoordinates.left + cursorPaddingRight + gutter | ||
}, | ||
bottom: { | ||
top: mouseCoordinates.top + cursorPaddingBottom + gutter, | ||
right: mouseCoordinates.left + tooltipRect.width / 2, | ||
bottom: mouseCoordinates.top + cursorPaddingBottom + gutter + tooltipRect.height, | ||
left: mouseCoordinates.left - tooltipRect.width / 2 | ||
}, | ||
left: { | ||
top: mouseCoordinates.top - tooltipRect.height / 2, | ||
right: mouseCoordinates.left - gutter, | ||
bottom: mouseCoordinates.top + tooltipRect.height / 2, | ||
left: mouseCoordinates.left - (tooltipRect.width + gutter) | ||
} | ||
}; | ||
} | ||
function getMousePosition(_ref4) { | ||
var mousePosition = _ref4.mousePosition, | ||
tooltip = _ref4.tooltip, | ||
mouseCoordinates = _ref4.mouseCoordinates; | ||
var noPosition = { | ||
coordinates: { left: 0, top: 0 }, | ||
position: 'bottom' | ||
position: 'mouse', | ||
mousePosition: 'bottom' | ||
}; | ||
if (!mousePosition) throw new Error('Property "mousePosition" is required.'); | ||
if (!tooltip) throw new Error('Property "tooltip" is required.'); | ||
if (!mouseCoordinates) return noPosition; | ||
// get the original coordinates | ||
var gutter = 8; | ||
var tooltipRect = tooltip.getBoundingClientRect(); | ||
var POSITIONS = getMouseCoords({ mouseCoordinates: mouseCoordinates, tooltipRect: tooltipRect, gutter: gutter }); | ||
var _adjustPosition = adjustPosition(mousePosition, POSITIONS, gutter), | ||
left = _adjustPosition.left, | ||
top = _adjustPosition.top, | ||
adjustedPosition = _adjustPosition.adjustedPosition; | ||
return { | ||
coordinates: { left: left, top: top }, | ||
position: 'mouse', | ||
mousePosition: adjustedPosition | ||
}; | ||
} | ||
/** | ||
* Gets the coordinates and adjusted position of a tooltip. | ||
* Position will be flipped on the primary axis with respect to the initial position | ||
* if there is not enough space in the viewport. | ||
* Coordinates will be shifted along the secondary axis to render within viewport. | ||
*/ | ||
export default function getPosition(_ref5) { | ||
var position = _ref5.position, | ||
target = _ref5.target, | ||
tooltip = _ref5.tooltip, | ||
mouseCoordinates = _ref5.mouseCoordinates, | ||
mousePosition = _ref5.mousePosition; | ||
if (position === 'mouse') { | ||
return getMousePosition({ mousePosition: mousePosition, tooltip: tooltip, mouseCoordinates: mouseCoordinates }); | ||
} | ||
var noPosition = { | ||
coordinates: { left: 0, top: 0 }, | ||
position: 'bottom', | ||
mousePosition: mousePosition | ||
}; | ||
/* eslint-disable no-console */ | ||
@@ -69,16 +242,12 @@ if (!position) console.error('Property "position" is required.'); | ||
// set tooltip positions before viewport check | ||
var attemptedPosition = POSITIONS[position]; | ||
var _adjustPosition2 = adjustPosition(position, POSITIONS, gutter), | ||
left = _adjustPosition2.left, | ||
top = _adjustPosition2.top, | ||
adjustedPosition = _adjustPosition2.adjustedPosition; | ||
// check if the tooltip is in view or must be flipped | ||
var adjustedPosition = inViewport(attemptedPosition) ? position : FLIPPED_POSITION[position]; | ||
// adjust positions with (possibly) flipped position | ||
var left = POSITIONS[adjustedPosition].left; | ||
var top = POSITIONS[adjustedPosition].top; | ||
return { | ||
coordinates: { left: left, top: top }, | ||
position: adjustedPosition | ||
position: adjustedPosition, | ||
mousePosition: mousePosition | ||
}; | ||
} |
@@ -1,3 +0,2 @@ | ||
export { default as inViewport } from './inViewport'; | ||
export { default as getPosition } from './getPosition'; | ||
export { default as getStyle } from './getStyle'; |
{ | ||
"name": "@atlaskit/tooltip", | ||
"version": "8.4.2" | ||
"version": "9.0.0" | ||
} |
{ | ||
"name": "@atlaskit/tooltip", | ||
"version": "9.0.0", | ||
"version": "9.1.0", | ||
"description": "A React Component for displaying Tooltips", | ||
@@ -40,2 +40,2 @@ "license": "Apache-2.0", | ||
] | ||
} | ||
} |
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
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
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
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
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
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
134186
1717
49