From 8645404f4d43bb203ddc3837957ac96c2dd6a172 Mon Sep 17 00:00:00 2001 From: Lewis B Date: Thu, 5 Jan 2017 18:59:31 +1000 Subject: [PATCH] Added followMouse & overflowMethod to tooltip options (#2544) - Also updated docs and examples for this - Fixed the positioning of the tooltip if groups was enabled - Removed missing parameters from Popup docstring --- docs/timeline/index.html | 24 +++++++++++ examples/timeline/items/tooltip.html | 35 ++++++++++++++++ lib/shared/Popup.js | 63 +++++++++++++++++++--------- lib/timeline/component/ItemSet.js | 31 ++++++++++++-- lib/timeline/optionsTimeline.js | 9 ++++ 5 files changed, 138 insertions(+), 24 deletions(-) diff --git a/docs/timeline/index.html b/docs/timeline/index.html index ce8df517..2f905929 100644 --- a/docs/timeline/index.html +++ b/docs/timeline/index.html @@ -1096,6 +1096,30 @@ function (option, path) { + + tooltip + Object + Object + Specify how the tooltip is positioned. + + + tooltip.followMouse + boolean + false + If true, tooltips will follow the mouse as they move around in the item. + + + + tooltip.overflowMethod + String + 'flip' + + Set how the tooltip should act if it is about to overflow out of the timeline.
+ Choose from 'cap' and 'flip'.
+ If it is set to 'cap', the tooltip will just cap its position to inside to timeline.
+ While if it is set to 'flip', the position of the tooltip will flip around the cursor so that a corner is at the cursor, and the rest of it is visible.
+ + tooltipOnItemUpdateTime Object/Boolean diff --git a/examples/timeline/items/tooltip.html b/examples/timeline/items/tooltip.html index 3e767838..38701586 100644 --- a/examples/timeline/items/tooltip.html +++ b/examples/timeline/items/tooltip.html @@ -24,6 +24,20 @@
+

+ The example below has the tooltip follow the mouse. +

+ +
+ +

+ The example below has the tooltip overflow set to 'cap'. Compare this to the one above, + to see how they differ. For the best results, move the cursor to the top right, + where the tool-tip is going to overflow out of the timeline. +

+ +
+ diff --git a/lib/shared/Popup.js b/lib/shared/Popup.js index eafd94b7..c1351e84 100644 --- a/lib/shared/Popup.js +++ b/lib/shared/Popup.js @@ -1,15 +1,12 @@ /** * Popup is a class to create a popup window with some text - * @param {Element} container The container object. - * @param {Number} [x] - * @param {Number} [y] - * @param {String} [text] - * @param {Object} [style] An object containing borderColor, - * backgroundColor, etc. + * @param {Element} container The container object. + * @param {string} overflowMethod How the popup should act to overflowing ('flip' or 'cap') */ class Popup { - constructor(container) { + constructor(container, overflowMethod) { this.container = container; + this.overflowMethod = overflowMethod || 'cap'; this.x = 0; this.y = 0; @@ -60,20 +57,46 @@ class Popup { var maxHeight = this.frame.parentNode.clientHeight; var maxWidth = this.frame.parentNode.clientWidth; - var top = (this.y - height); - if (top + height + this.padding > maxHeight) { - top = maxHeight - height - this.padding; - } - if (top < this.padding) { - top = this.padding; - } + var left = 0, top = 0; - var left = this.x; - if (left + width + this.padding > maxWidth) { - left = maxWidth - width - this.padding; - } - if (left < this.padding) { - left = this.padding; + if (this.overflowMethod == 'flip') { + var isLeft = false, isTop = true; // Where around the position it's located + + if (this.y - height < this.padding) { + isTop = false; + } + + if (this.x + width > maxWidth - this.padding) { + isLeft = true; + } + + if (isLeft) { + left = this.x - width; + } else { + left = this.x; + } + + if (isTop) { + top = this.y - height; + } else { + top = this.y; + } + } else { + top = (this.y - height); + if (top + height + this.padding > maxHeight) { + top = maxHeight - height - this.padding; + } + if (top < this.padding) { + top = this.padding; + } + + left = this.x; + if (left + width + this.padding > maxWidth) { + left = maxWidth - width - this.padding; + } + if (left < this.padding) { + left = this.padding; + } } this.frame.style.left = left + "px"; diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js index 07c0ce74..948f5758 100644 --- a/lib/timeline/component/ItemSet.js +++ b/lib/timeline/component/ItemSet.js @@ -95,6 +95,11 @@ function ItemSet(body, options) { axis: 20 }, + tooltip: { + followMouse: false, + overflowMethod: 'flip' + }, + tooltipOnItemUpdateTime: false }; @@ -250,6 +255,7 @@ ItemSet.prototype._create = function(){ this.body.dom.centerContainer.addEventListener('mouseover', this._onMouseOver.bind(this)); this.body.dom.centerContainer.addEventListener('mouseout', this._onMouseOut.bind(this)); + this.body.dom.centerContainer.addEventListener('mousemove', this._onMouseMove.bind(this)); // attach to the DOM this.show(); @@ -325,7 +331,7 @@ ItemSet.prototype.setOptions = function(options) { var fields = [ 'type', 'rtl', 'align', 'order', 'stack', 'stackSubgroups', 'selectable', 'multiselect', 'itemsAlwaysDraggable', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'visibleFrameTemplate', - 'hide', 'snap', 'groupOrderSwap', 'tooltipOnItemUpdateTime' + 'hide', 'snap', 'groupOrderSwap', 'tooltip', 'tooltipOnItemUpdateTime' ]; util.selectiveExtend(fields, this.options, options); @@ -1891,13 +1897,13 @@ ItemSet.prototype._onMouseOver = function (event) { if (item.getTitle()) { if (item.popup == null) { - item.setPopup(new Popup(this.body.dom.root)); + item.setPopup(new Popup(this.body.dom.root, this.options.tooltip.overflowMethod || 'flip')); } var container = this.body.dom.centerContainer; item.popup.setPosition( - event.clientX - util.getAbsoluteLeft(container), - event.clientY - util.getAbsoluteTop(container) + event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, + event.clientY - util.getAbsoluteTop(container) + container.offsetTop ); item.popup.show(); } @@ -1927,6 +1933,23 @@ ItemSet.prototype._onMouseOut = function (event) { event: util.elementsCensor(event) }); }; +ItemSet.prototype._onMouseMove = function (event) { + var item = this.itemFromTarget(event); + if (!item) return; + + if (this.options.tooltip.followMouse) { + if (item.popup) { + if (!item.popup.hidden) { + var container = this.body.dom.centerContainer; + item.popup.setPosition( + event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, + event.clientY - util.getAbsoluteTop(container) + container.offsetTop + ); + item.popup.show(); // Redraw + } + } + } +}; /** diff --git a/lib/timeline/optionsTimeline.js b/lib/timeline/optionsTimeline.js index 517a0e00..6251bcac 100644 --- a/lib/timeline/optionsTimeline.js +++ b/lib/timeline/optionsTimeline.js @@ -131,6 +131,11 @@ let allOptions = { template: {'function': 'function'}, groupTemplate: {'function': 'function'}, visibleFrameTemplate: {string, 'function': 'function'}, + tooltip: { + followMouse: { 'boolean': bool }, + overflowMethod: { 'string': ['cap', 'flip'] }, + __type__: {object} + }, tooltipOnItemUpdateTime: { template: {'function': 'function'}, __type__: { 'boolean': bool, object} @@ -230,6 +235,10 @@ let configureOptions = { // scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'], // step: [1, 1, 10, 1] //}, + tooltip: { + followMouse: false, + overflowMethod: 'flip' + }, tooltipOnItemUpdateTime: false, type: ['box', 'point', 'range', 'background'], width: '100%',