123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- import {
- isString,
- assign,
- forEach
- } from 'min-dash';
- import {
- domify,
- attr as domAttr,
- classes as domClasses,
- remove as domRemove,
- delegate as domDelegate
- } from 'min-dom';
- import Ids from '../../util/IdGenerator';
- // document wide unique tooltip ids
- var ids = new Ids('tt');
- function createRoot(parentNode) {
- var root = domify(
- '<div class="djs-tooltip-container" style="position: absolute; width: 0; height: 0;" />'
- );
- parentNode.insertBefore(root, parentNode.firstChild);
- return root;
- }
- function setPosition(el, x, y) {
- assign(el.style, { left: x + 'px', top: y + 'px' });
- }
- function setVisible(el, visible) {
- el.style.display = visible === false ? 'none' : '';
- }
- var tooltipClass = 'djs-tooltip',
- tooltipSelector = '.' + tooltipClass;
- /**
- * A service that allows users to render tool tips on the diagram.
- *
- * The tooltip service will take care of updating the tooltip positioning
- * during navigation + zooming.
- *
- * @example
- *
- * ```javascript
- *
- * // add a pink badge on the top left of the shape
- * tooltips.add({
- * position: {
- * x: 50,
- * y: 100
- * },
- * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>'
- * });
- *
- * // or with optional life span
- * tooltips.add({
- * position: {
- * top: -5,
- * left: -5
- * },
- * html: '<div style="width: 10px; background: fuchsia; color: white;">0</div>',
- * ttl: 2000
- * });
- *
- * // remove a tool tip
- * var id = tooltips.add(...);
- * tooltips.remove(id);
- * ```
- *
- * @param {EventBus} eventBus
- * @param {Canvas} canvas
- */
- export default function Tooltips(eventBus, canvas) {
- this._eventBus = eventBus;
- this._canvas = canvas;
- this._ids = ids;
- this._tooltipDefaults = {
- show: {
- minZoom: 0.7,
- maxZoom: 5.0
- }
- };
- /**
- * Mapping tooltipId -> tooltip
- */
- this._tooltips = {};
- // root html element for all tooltips
- this._tooltipRoot = createRoot(canvas.getContainer());
- var self = this;
- domDelegate.bind(this._tooltipRoot, tooltipSelector, 'mousedown', function(event) {
- event.stopPropagation();
- });
- domDelegate.bind(this._tooltipRoot, tooltipSelector, 'mouseover', function(event) {
- self.trigger('mouseover', event);
- });
- domDelegate.bind(this._tooltipRoot, tooltipSelector, 'mouseout', function(event) {
- self.trigger('mouseout', event);
- });
- this._init();
- }
- Tooltips.$inject = [ 'eventBus', 'canvas' ];
- /**
- * Adds a HTML tooltip to the diagram
- *
- * @param {Object} tooltip the tooltip configuration
- *
- * @param {String|DOMElement} tooltip.html html element to use as an tooltip
- * @param {Object} [tooltip.show] show configuration
- * @param {Number} [tooltip.show.minZoom] minimal zoom level to show the tooltip
- * @param {Number} [tooltip.show.maxZoom] maximum zoom level to show the tooltip
- * @param {Object} tooltip.position where to attach the tooltip
- * @param {Number} [tooltip.position.left] relative to element bbox left attachment
- * @param {Number} [tooltip.position.top] relative to element bbox top attachment
- * @param {Number} [tooltip.position.bottom] relative to element bbox bottom attachment
- * @param {Number} [tooltip.position.right] relative to element bbox right attachment
- * @param {Number} [tooltip.timeout=-1]
- *
- * @return {String} id that may be used to reference the tooltip for update or removal
- */
- Tooltips.prototype.add = function(tooltip) {
- if (!tooltip.position) {
- throw new Error('must specifiy tooltip position');
- }
- if (!tooltip.html) {
- throw new Error('must specifiy tooltip html');
- }
- var id = this._ids.next();
- tooltip = assign({}, this._tooltipDefaults, tooltip, {
- id: id
- });
- this._addTooltip(tooltip);
- if (tooltip.timeout) {
- this.setTimeout(tooltip);
- }
- return id;
- };
- Tooltips.prototype.trigger = function(action, event) {
- var node = event.delegateTarget || event.target;
- var tooltip = this.get(domAttr(node, 'data-tooltip-id'));
- if (!tooltip) {
- return;
- }
- if (action === 'mouseover' && tooltip.timeout) {
- this.clearTimeout(tooltip);
- }
- if (action === 'mouseout' && tooltip.timeout) {
- // cut timeout after mouse out
- tooltip.timeout = 1000;
- this.setTimeout(tooltip);
- }
- };
- /**
- * Get a tooltip with the given id
- *
- * @param {String} id
- */
- Tooltips.prototype.get = function(id) {
- if (typeof id !== 'string') {
- id = id.id;
- }
- return this._tooltips[id];
- };
- Tooltips.prototype.clearTimeout = function(tooltip) {
- tooltip = this.get(tooltip);
- if (!tooltip) {
- return;
- }
- var removeTimer = tooltip.removeTimer;
- if (removeTimer) {
- clearTimeout(removeTimer);
- tooltip.removeTimer = null;
- }
- };
- Tooltips.prototype.setTimeout = function(tooltip) {
- tooltip = this.get(tooltip);
- if (!tooltip) {
- return;
- }
- this.clearTimeout(tooltip);
- var self = this;
- tooltip.removeTimer = setTimeout(function() {
- self.remove(tooltip);
- }, tooltip.timeout);
- };
- /**
- * Remove an tooltip with the given id
- *
- * @param {String} id
- */
- Tooltips.prototype.remove = function(id) {
- var tooltip = this.get(id);
- if (tooltip) {
- domRemove(tooltip.html);
- domRemove(tooltip.htmlContainer);
- delete tooltip.htmlContainer;
- delete this._tooltips[tooltip.id];
- }
- };
- Tooltips.prototype.show = function() {
- setVisible(this._tooltipRoot);
- };
- Tooltips.prototype.hide = function() {
- setVisible(this._tooltipRoot, false);
- };
- Tooltips.prototype._updateRoot = function(viewbox) {
- var a = viewbox.scale || 1;
- var d = viewbox.scale || 1;
- var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')';
- this._tooltipRoot.style.transform = matrix;
- this._tooltipRoot.style['-ms-transform'] = matrix;
- };
- Tooltips.prototype._addTooltip = function(tooltip) {
- var id = tooltip.id,
- html = tooltip.html,
- htmlContainer,
- tooltipRoot = this._tooltipRoot;
- // unwrap jquery (for those who need it)
- if (html.get && html.constructor.prototype.jquery) {
- html = html.get(0);
- }
- // create proper html elements from
- // tooltip HTML strings
- if (isString(html)) {
- html = domify(html);
- }
- htmlContainer = domify('<div data-tooltip-id="' + id + '" class="' + tooltipClass + '" style="position: absolute">');
- htmlContainer.appendChild(html);
- if (tooltip.type) {
- domClasses(htmlContainer).add('djs-tooltip-' + tooltip.type);
- }
- if (tooltip.className) {
- domClasses(htmlContainer).add(tooltip.className);
- }
- tooltip.htmlContainer = htmlContainer;
- tooltipRoot.appendChild(htmlContainer);
- this._tooltips[id] = tooltip;
- this._updateTooltip(tooltip);
- };
- Tooltips.prototype._updateTooltip = function(tooltip) {
- var position = tooltip.position,
- htmlContainer = tooltip.htmlContainer;
- // update overlay html based on tooltip x, y
- setPosition(htmlContainer, position.x, position.y);
- };
- Tooltips.prototype._updateTooltipVisibilty = function(viewbox) {
- forEach(this._tooltips, function(tooltip) {
- var show = tooltip.show,
- htmlContainer = tooltip.htmlContainer,
- visible = true;
- if (show) {
- if (show.minZoom > viewbox.scale ||
- show.maxZoom < viewbox.scale) {
- visible = false;
- }
- setVisible(htmlContainer, visible);
- }
- });
- };
- Tooltips.prototype._init = function() {
- var self = this;
- // scroll/zoom integration
- function updateViewbox(viewbox) {
- self._updateRoot(viewbox);
- self._updateTooltipVisibilty(viewbox);
- self.show();
- }
- this._eventBus.on('canvas.viewbox.changing', function(event) {
- self.hide();
- });
- this._eventBus.on('canvas.viewbox.changed', function(event) {
- updateViewbox(event.viewbox);
- });
- };
|