Browse Source

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
fix2580
Lewis B 7 years ago
committed by yotamberk
parent
commit
8645404f4d
5 changed files with 138 additions and 24 deletions
  1. +24
    -0
      docs/timeline/index.html
  2. +35
    -0
      examples/timeline/items/tooltip.html
  3. +43
    -20
      lib/shared/Popup.js
  4. +27
    -4
      lib/timeline/component/ItemSet.js
  5. +9
    -0
      lib/timeline/optionsTimeline.js

+ 24
- 0
docs/timeline/index.html View File

@ -1096,6 +1096,30 @@ function (option, path) {
</td>
</tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','tooltip', this);">
<td><span parent="tooltip" class="right-caret"></span> tooltip</td>
<td>Object</td>
<td><code>Object</code></td>
<td>Specify how the tooltip is positioned.</td>
</tr>
<tr parent="tooltip" class="hidden">
<td class="indent">tooltip.followMouse</td>
<td>boolean</td>
<td><code>false</code></td>
<td>If true, tooltips will follow the mouse as they move around in the item.</td>
</tr>
<tr parent="tooltip" class="hidden">
<td class="indent">tooltip.overflowMethod</td>
<td>String</td>
<td><code>'flip'</code></td>
<td>
Set how the tooltip should act if it is about to overflow out of the timeline.<br />
Choose from <code>'cap'</code> and <code>'flip'</code>. <br />
If it is set to <code>'cap'</code>, the tooltip will just cap its position to inside to timeline. <br />
While if it is set to <code>'flip'</code>, 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. <br />
</tr>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','tooltipOnItemUpdateTime', this);">
<td><span parent="tooltipOnItemUpdateTime" class="right-caret"></span> tooltipOnItemUpdateTime</td>
<td>Object/Boolean</td>

+ 35
- 0
examples/timeline/items/tooltip.html View File

@ -24,6 +24,20 @@
<div id="tooltips"></div>
<p>
The example below has the tooltip follow the mouse.
</p>
<div id="tooltips-follow"></div>
<p>
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.
</p>
<div id="tooltips-cap"></div>
<script type="text/javascript">
// Create a DataSet (allows two way data-binding)
var items = new vis.DataSet([
@ -43,6 +57,27 @@
var timelineTooltips = new vis.Timeline(document.getElementById('tooltips'),
items, options
);
// Follow options
var follow_options = {
tooltip: {
followMouse: true
}
};
var timelineFollow = new vis.Timeline(document.getElementById('tooltips-follow'),
items, follow_options);
// Cap options
var cap_options = {
tooltip: {
followMouse: true,
overflowMethod: 'cap'
}
}
var timelineCap = new vis.Timeline(document.getElementById('tooltips-cap'),
items, cap_options);
</script>
</body>

+ 43
- 20
lib/shared/Popup.js View File

@ -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";

+ 27
- 4
lib/timeline/component/ItemSet.js View File

@ -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
}
}
}
};
/**

+ 9
- 0
lib/timeline/optionsTimeline.js View File

@ -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%',

Loading…
Cancel
Save