Files
iD/modules/util/tooltip.js
Quincy Morgan f3836c36aa Fix toolbar tooltip placement (close #6112)
Tweak pane positions for the new toolbar height
2019-03-27 14:18:04 -04:00

217 lines
5.6 KiB
JavaScript

import { select as d3_select } from 'd3-selection';
import { utilFunctor } from './index';
var _tooltipID = 0;
export function tooltip(klass) {
var _id = _tooltipID++;
var tooltip = function(selection) {
selection.each(setup);
};
var _animation = utilFunctor(false);
var _title = function() {
var title = this.getAttribute('data-original-title');
if (title) {
return title;
} else {
title = this.getAttribute('title');
this.removeAttribute('title');
this.setAttribute('data-original-title', title);
}
return title;
};
var _html = utilFunctor(false);
var _placement = utilFunctor('top');
tooltip.title = function(val) {
if (arguments.length) {
_title = utilFunctor(val);
return tooltip;
} else {
return _title;
}
};
tooltip.html = function(val) {
if (arguments.length) {
_html = utilFunctor(val);
return tooltip;
} else {
return _html;
}
};
tooltip.placement = function(val) {
if (arguments.length) {
_placement = utilFunctor(val);
return tooltip;
} else {
return _placement;
}
};
tooltip.show = function(selection) {
selection.each(show);
};
tooltip.hide = function(selection) {
selection.each(hide);
};
tooltip.toggle = function(selection) {
selection.each(toggle);
};
tooltip.destroy = function(selection, selector) {
// by default, just destroy the current tooltip
selector = selector || '.tooltip-' + _id;
selection
.on('mouseenter.tooltip', null)
.on('mouseleave.tooltip', null)
.attr('title', function() {
return this.getAttribute('data-original-title') || this.getAttribute('title');
})
.attr('data-original-title', null)
.selectAll(selector)
.remove();
};
tooltip.destroyAny = function(selection) {
selection.call(tooltip.destroy, '.tooltip');
};
var isTouchEvent = false;
function setup() {
var root = d3_select(this);
var animate = _animation.apply(this, arguments);
var tip = root.selectAll('.tooltip-' + _id)
.data([0]);
var enter = tip.enter()
.append('div')
.attr('class', 'tooltip tooltip-' + _id + ' ' + (klass ? klass : ''));
enter
.append('div')
.attr('class', 'tooltip-arrow');
enter
.append('div')
.attr('class', 'tooltip-inner');
tip = enter
.merge(tip);
if (animate) {
tip.classed('fade', true);
}
var place = _placement.apply(this, arguments);
tip.classed(place, true);
root.on('touchstart.tooltip', function() {
// hack to avoid showing tooltips upon touch input
isTouchEvent = true;
});
root.on('mouseenter.tooltip', show);
root.on('mouseleave.tooltip', hide);
}
function show() {
if (isTouchEvent) {
isTouchEvent = false;
return;
}
var root = d3_select(this);
var content = _title.apply(this, arguments);
var tip = root.selectAll('.tooltip-' + _id);
if (tip.empty()) { // tooltip was removed somehow, put it back
root.call(tooltip.destroy);
root.each(setup);
tip = root.selectAll('.tooltip-' + _id);
}
tip.classed('in', true);
var markup = _html.apply(this, arguments);
tip.selectAll('.tooltip-inner')[markup ? 'html' : 'text'](content);
var place = _placement.apply(this, arguments);
var outer = getPosition(root.node());
var inner = getPosition(tip.node());
var pos;
switch (place) {
case 'top':
pos = { x: outer.x + (outer.w - inner.w) / 2, y: outer.y - inner.h };
break;
case 'right':
pos = { x: outer.x + outer.w, y: outer.y + (outer.h - inner.h) / 2 };
break;
case 'left':
pos = { x: outer.x - inner.w, y: outer.y + (outer.h - inner.h) / 2 };
break;
case 'bottom':
pos = { x: outer.x + (outer.w - inner.w) / 2, y: outer.y + outer.h };
break;
}
if (pos) {
tip.style('left', ~~pos.x + 'px').style('top', ~~pos.y + 'px');
} else {
tip.style('left', null).style('top', null);
}
this.tooltipVisible = true;
function getPosition(node) {
var mode = d3_select(node).style('position');
if (mode === 'absolute' || mode === 'static') {
return {
x: node.offsetLeft,
y: node.offsetTop,
w: node.offsetWidth,
h: node.offsetHeight
};
} else {
return {
x: 0,
y: 0,
w: node.offsetWidth,
h: node.offsetHeight
};
}
}
}
function hide() {
d3_select(this).selectAll('.tooltip-' + _id).classed('in', false);
this.tooltipVisible = false;
}
function toggle() {
if (this.tooltipVisible) {
hide.apply(this, arguments);
} else {
show.apply(this, arguments);
}
}
return tooltip;
}