if (!element || element.style.display == 'none')
return false;
const bounds = element.getBoundingClientRect();
- return point.x >= bounds.left && point.x <= bounds.right && point.y >= bounds.top && point.y <= bounds.bottom;
+ return point.x >= bounds.left - 1 && point.x <= bounds.right + 1 && point.y >= bounds.top - 1 && point.y <= bounds.bottom + 1;
}
class _ToolTip {
this.ref = null;
this.arrow = null;
this.onArrowClick = null;
+
+ this.VERTICAL = 0;
+ this.HORIZONTAL = 1;
}
toString() {
const self = this;
state: {content: null, points: null},
onElementMount: (element) => {
element.addEventListener('mouseleave', (event) => {
+ if (element.style.display === 'none')
+ return;
if (!isPointInElement(self.arrow.element, event))
this.unset()
});
if (stateDiff.content) {
DOM.inject(element, stateDiff.content);
element.style.display = null;
- }
- if (!state.content && !element.style.display) {
+ } else {
element.style.display = 'none';
DOM.inject(element, '');
}
+
if (stateDiff.points) {
element.style.left = '0px';
element.style.top = '0px';
const viewportWitdh = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
const viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
- // Make an effort to place the tooltip in the center of the viewport.
let direction = 'down';
- let tipY = upperPoint.y - 8 - bounds.height;
let point = upperPoint;
- if (tipY < scrollDelta || tipY + bounds.height + (lowerPoint.y - upperPoint.y) / 2 < scrollDelta + viewportHeight / 2) {
- direction = 'up';
- tipY = lowerPoint.y + 16;
- point = lowerPoint;
- }
- element.style.top = `${tipY}px`;
- let tipX = point.x - bounds.width / 2;
- if (tipX + bounds.width > viewportWitdh)
- tipX = viewportWitdh - bounds.width;
- if (tipX < 0)
- tipX = 0;
- element.style.left = `${tipX}px`;
+ if (upperPoint.y == lowerPoint.y) {
+ // Horizontal tooltip
+ const leftPoint = stateDiff.points.length > 1 && stateDiff.points[0].x > stateDiff.points[1].x ? stateDiff.points[1] : stateDiff.points[0];
+ const rightPoint = stateDiff.points.length > 1 && stateDiff.points[1].x > stateDiff.points[0].x ? stateDiff.points[1] : stateDiff.points[0];
+
+ direction = 'left';
+ let tipX = leftPoint.x - 12 - bounds.width;
+ point = rightPoint;
+ if (tipX < 0 || tipX + bounds.width + (rightPoint.x - leftPoint.x) / 2 < viewportWitdh / 2) {
+ direction = 'right';
+ tipX = rightPoint.x + 16;
+ point = rightPoint;
+ }
+ element.style.left = `${tipX}px`;
+
+ let tipY = point.y - bounds.height / 2;
+ if (tipY + bounds.height > scrollDelta + viewportHeight)
+ tipY = scrollDelta + viewportHeight - bounds.height;
+ if (tipY < 0)
+ tipY = 0;
+ element.style.top = `${tipY}px`;
+ } else {
+ // Make an effort to place the tooltip in the center of the viewport.
+ let tipY = upperPoint.y - 8 - bounds.height;
+ point = upperPoint;
+ if (tipY < scrollDelta || tipY + bounds.height + (lowerPoint.y - upperPoint.y) / 2 < scrollDelta + viewportHeight / 2) {
+ direction = 'up';
+ tipY = lowerPoint.y + 16;
+ point = lowerPoint;
+ }
+ element.style.top = `${tipY}px`;
+
+ let tipX = point.x - bounds.width / 2;
+ if (tipX + bounds.width > viewportWitdh)
+ tipX = viewportWitdh - bounds.width;
+ if (tipX < 0)
+ tipX = 0;
+ element.style.left = `${tipX}px`;
+ }
self.arrow.setState({direction: direction, location: point});
}
state: {direction: null, location: null},
onElementMount: (element) => {
element.addEventListener('mouseleave', (event) => {
- if (!isPointInElement(self.ref.element, event))
+ if (element.style.display === 'none')
+ return;
+ if (!isPointInElement(self.ref.element, event) && !isPointInElement(element, event))
this.unset()
});
},
- onStateUpdate: (element, stateDiff, state) => {
- if (!state.direction || !state.location) {
+ onStateUpdate: (element, stateDiff) => {
+ if (!stateDiff.direction || !stateDiff.location) {
element.style.display = 'none';
element.onclick = null;
element.style.cursor = null;
element.style.cursor = null;
}
- element.classList = [`tooltip arrow-${state.direction}`];
- element.style.left = `${state.location.x - 15}px`;
- if (state.direction == 'down')
- element.style.top = `${state.location.y - 8}px`;
- else
- element.style.top = `${state.location.y - 13}px`;
+ element.classList = [`tooltip arrow-${stateDiff.direction}`];
+
+ if (stateDiff.direction == 'down') {
+ element.style.left = `${stateDiff.location.x - 15}px`;
+ element.style.top = `${stateDiff.location.y - 8}px`;
+ } else if (stateDiff.direction == 'left') {
+ element.style.left = `${stateDiff.location.x - 30}px`;
+ element.style.top = `${stateDiff.location.y - 15}px`;
+ } else if (stateDiff.direction == 'right') {
+ element.style.left = `${stateDiff.location.x - 13}px`;
+ element.style.top = `${stateDiff.location.y - 15}px`;
+ } else {
+ element.style.left = `${stateDiff.location.x - 15}px`;
+ element.style.top = `${stateDiff.location.y - 13}px`;
+ }
element.style.display = null;
},
this.onArrowClick = onArrowClick;
this.ref.setState({content: content, points: points});
}
+ setByElement(content, element, options) {
+ const bound = element.getBoundingClientRect();
+ const orientation = options.orientation ? options.orientation : this.VERTICAL;
+ const onArrowClick = options.onArrowClick ? options.onArrowClick : null;
+
+ // Manage the scroll delta
+ let scrollDelta = 0;
+ if (window.getComputedStyle(element.offsetParent).getPropertyValue('position') == 'fixed')
+ scrollDelta = document.documentElement.scrollTop || document.body.scrollTop;
+
+ if (options.orientation) {
+ this.set(content, [
+ {x: bound.right, y: (bound.top + bound.bottom) / 2 + scrollDelta},
+ {x: bound.left, y: (bound.top + bound.bottom) / 2 + scrollDelta},
+ ], onArrowClick);
+ } else {
+ this.set(content, [
+ {x: (bound.right + bound.left) / 2, y: bound.top + scrollDelta},
+ {x: (bound.right + bound.left) / 2, y: bound.bottom + scrollDelta},
+ ], onArrowClick);
+ }
+ }
unset() {
if (this.ref)
this.ref.setState({content: null, points: null});