From c9f586195a1127c97dd58b163bb62ed1cf1b1537 Mon Sep 17 00:00:00 2001 From: Lewis B Date: Sat, 31 Dec 2016 03:05:28 +1000 Subject: [PATCH] [Timeline] Added HTML tool-tip support (#2498) * Added HTML tooltip support for Timeline * Fixed tooltip position on Firefox * Added Timeline tooltip example * Updated tooltip location to be next to the mouse * Added HTML element to example --- docs/timeline/index.html | 2 +- examples/timeline/items/tooltip.html | 49 +++++++++++++++++++ lib/network/modules/InteractionHandler.js | 2 +- .../modules/components => shared}/Popup.js | 2 +- .../tooltip.css} | 6 ++- lib/timeline/component/ItemSet.js | 20 ++++++++ lib/timeline/component/item/BackgroundItem.js | 2 +- lib/timeline/component/item/BoxItem.js | 2 +- lib/timeline/component/item/Item.js | 28 ++++++++--- lib/timeline/component/item/PointItem.js | 2 +- lib/timeline/component/item/RangeItem.js | 2 +- 11 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 examples/timeline/items/tooltip.html rename lib/{network/modules/components => shared}/Popup.js (97%) rename lib/{network/css/network-tooltip.css => shared/tooltip.css} (90%) diff --git a/docs/timeline/index.html b/docs/timeline/index.html index 266eb576..f536ecc1 100644 --- a/docs/timeline/index.html +++ b/docs/timeline/index.html @@ -324,7 +324,7 @@ var items = new vis.DataSet([ String none Add a title for the item, displayed when holding the mouse on the item. - The title can only contain plain text. + The title can be an HTML element or a string containing plain text or HTML. diff --git a/examples/timeline/items/tooltip.html b/examples/timeline/items/tooltip.html new file mode 100644 index 00000000..3e767838 --- /dev/null +++ b/examples/timeline/items/tooltip.html @@ -0,0 +1,49 @@ + + + + Timeline | Tooltips + + + + + + + + + +

Tooltips

+ +

+ Setting the tooltip in various ways. +

+ +
+ + + + + diff --git a/lib/network/modules/InteractionHandler.js b/lib/network/modules/InteractionHandler.js index 14ab3a7e..4d61d5a1 100644 --- a/lib/network/modules/InteractionHandler.js +++ b/lib/network/modules/InteractionHandler.js @@ -1,7 +1,7 @@ let util = require('../../util'); import NavigationHandler from './components/NavigationHandler' -import Popup from './components/Popup' +import Popup from './../../shared/Popup' class InteractionHandler { constructor(body, canvas, selectionHandler) { diff --git a/lib/network/modules/components/Popup.js b/lib/shared/Popup.js similarity index 97% rename from lib/network/modules/components/Popup.js rename to lib/shared/Popup.js index 4a70a021..43d4537b 100644 --- a/lib/network/modules/components/Popup.js +++ b/lib/shared/Popup.js @@ -18,7 +18,7 @@ class Popup { // create the frame this.frame = document.createElement('div'); - this.frame.className = 'vis-network-tooltip'; + this.frame.className = 'vis-tooltip'; this.container.appendChild(this.frame); } diff --git a/lib/network/css/network-tooltip.css b/lib/shared/tooltip.css similarity index 90% rename from lib/network/css/network-tooltip.css rename to lib/shared/tooltip.css index f8ababcf..1c9694aa 100644 --- a/lib/network/css/network-tooltip.css +++ b/lib/shared/tooltip.css @@ -1,4 +1,4 @@ -div.vis-network-tooltip { +div.vis-tooltip { position: absolute; visibility: hidden; padding: 5px; @@ -16,4 +16,6 @@ div.vis-network-tooltip { box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2); pointer-events: none; -} \ No newline at end of file + + z-index: 5; +} diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js index 4041f16b..e76eb49d 100644 --- a/lib/timeline/component/ItemSet.js +++ b/lib/timeline/component/ItemSet.js @@ -10,6 +10,7 @@ var BoxItem = require('./item/BoxItem'); var PointItem = require('./item/PointItem'); var RangeItem = require('./item/RangeItem'); var BackgroundItem = require('./item/BackgroundItem'); +import Popup from '../../shared/Popup'; var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items @@ -1872,6 +1873,20 @@ ItemSet.prototype._onSelectItem = function (event) { ItemSet.prototype._onMouseOver = function (event) { var item = this.itemFromTarget(event); if (!item) return; + + if (item.getTitle()) { + if (item.popup == null) { + item.setPopup(new Popup(this.body.dom.root)); + } + + var container = this.body.dom.centerContainer; + item.popup.setPosition( + event.clientX - util.getAbsoluteLeft(container), + event.clientY - util.getAbsoluteTop(container) + ); + item.popup.show(); + } + this.body.emitter.emit('itemover', { item: item.id, event: util.elementsCensor(event) @@ -1880,6 +1895,11 @@ ItemSet.prototype._onMouseOver = function (event) { ItemSet.prototype._onMouseOut = function (event) { var item = this.itemFromTarget(event); if (!item) return; + + if (item.popup != null) { + item.popup.hide(); + } + this.body.emitter.emit('itemout', { item: item.id, event: util.elementsCensor(event) diff --git a/lib/timeline/component/item/BackgroundItem.js b/lib/timeline/component/item/BackgroundItem.js index 44048527..8a1c8859 100644 --- a/lib/timeline/component/item/BackgroundItem.js +++ b/lib/timeline/component/item/BackgroundItem.js @@ -100,7 +100,7 @@ BackgroundItem.prototype.redraw = function() { // - the item is selected/deselected if (this.dirty) { this._updateContents(this.dom.content); - this._updateTitle(this.dom.content); + this._updateTitle(); this._updateDataAttributes(this.dom.content); this._updateStyle(this.dom.box); diff --git a/lib/timeline/component/item/BoxItem.js b/lib/timeline/component/item/BoxItem.js index 2a2fc415..02e35937 100644 --- a/lib/timeline/component/item/BoxItem.js +++ b/lib/timeline/component/item/BoxItem.js @@ -119,7 +119,7 @@ BoxItem.prototype.redraw = function() { // - the item is selected/deselected if (this.dirty) { this._updateContents(this.dom.content); - this._updateTitle(this.dom.box); + this._updateTitle(); this._updateDataAttributes(this.dom.box); this._updateStyle(this.dom.box); diff --git a/lib/timeline/component/item/Item.js b/lib/timeline/component/item/Item.js index b8f9fe60..edae028a 100644 --- a/lib/timeline/component/item/Item.js +++ b/lib/timeline/component/item/Item.js @@ -23,6 +23,7 @@ function Item (data, conversion, options) { this.displayed = false; this.groupShowing = true; this.dirty = true; + this.popup = null; this.top = null; this.right = null; @@ -398,15 +399,13 @@ Item.prototype._updateContents = function (element) { /** * Set HTML contents for the item - * @param {Element} element HTML element to fill with the contents * @private */ -Item.prototype._updateTitle = function (element) { +Item.prototype._updateTitle = function () { if (this.data.title != null) { - element.title = this.data.title || ''; - } - else { - element.removeAttribute('vis-title'); + if (this.popup != null) { + this.popup.setText(this.data.title || ''); + } } }; @@ -491,4 +490,21 @@ Item.prototype.getWidthRight = function () { return 0; }; +/** + * Return the title of the item + * @return {string | undefined} + */ +Item.prototype.getTitle = function () { + return this.data.title; +}; + +/** + * Set the popup object, and update the title + * @param {Popup} popup + */ +Item.prototype.setPopup = function (popup) { + this.popup = popup; + this._updateTitle(); +}; + module.exports = Item; diff --git a/lib/timeline/component/item/PointItem.js b/lib/timeline/component/item/PointItem.js index aebedaa4..c4c14c91 100644 --- a/lib/timeline/component/item/PointItem.js +++ b/lib/timeline/component/item/PointItem.js @@ -97,7 +97,7 @@ PointItem.prototype.redraw = function() { // - the item is selected/deselected if (this.dirty) { this._updateContents(this.dom.content); - this._updateTitle(this.dom.point); + this._updateTitle(); this._updateDataAttributes(this.dom.point); this._updateStyle(this.dom.point); diff --git a/lib/timeline/component/item/RangeItem.js b/lib/timeline/component/item/RangeItem.js index bc586e8f..d432376b 100644 --- a/lib/timeline/component/item/RangeItem.js +++ b/lib/timeline/component/item/RangeItem.js @@ -100,7 +100,7 @@ RangeItem.prototype.redraw = function() { // - the item is selected/deselected if (this.dirty) { this._updateContents(this.dom.content); - this._updateTitle(this.dom.box); + this._updateTitle(); this._updateDataAttributes(this.dom.box); this._updateStyle(this.dom.box);