Skip to content
This repository has been archived by the owner on Jul 29, 2019. It is now read-only.

Tooltip on item update time #2247

Merged
merged 10 commits into from Nov 4, 2016
15 changes: 14 additions & 1 deletion docs/timeline/index.html
Expand Up @@ -1024,10 +1024,23 @@ <h2 id="Configuration_Options">Configuration Options</h2>
</td>
</tr>

<tr class='toggle collapsible' onclick="toggleTable('optionTable','tooltipOnItemUpdateTime', this);">
<td><span parent="tooltipOnItemUpdateTime" class="right-caret"></span> tooltipOnItemUpdateTime</td>
<td>Object/Boolean</td>
<td><code>false</code></td>
<td>Show a tooltip on updating an item's time. Note: <code>editable.updateTime</code> must be <code>true</code></td>
</tr>
<tr parent="tooltipOnItemUpdateTime" class="hidden">
<td class="indent">template</td>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There already is a option template. Maybe we should call it tooltipTemplate or hoverTemplate or something like this? I'm not sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's in the tooltipOnItemUpdateTime so I don't think we need to change the name.

<td>Function</td>
<td>none</td>
<td>A template function used to generate the contents of the tooltip. The function is called by the Timeline with an item data as the first argument, and must return HTML code as result. See section <a href="#Templates">Templates</a> for a detailed explanation.
</td>
</tr>
<tr>
<td>verticalScroll</td>
<td>Boolean</td>
<td>false</td>
<td><code>false</code></td>
<td> Show a vertical scroll on the side of the group list and link it to the scroll event when zoom is not triggered. Notice that defining this option as <code>true</code> will NOT override <code>horizontalScroll</code>. The scroll event will be vertically ignored, but a vertical scrollbar will be visible
</td>
</tr>
Expand Down
130 changes: 130 additions & 0 deletions examples/timeline/editing/tooltipOnItemChange.html
@@ -0,0 +1,130 @@
<html>
<head>
<title>Timeline | Tooltip on item onUpdateTime Option</title>

<script src="../../../dist/vis.js"></script>
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be nice to demonstrate also how to overwrite the tooltip style. e.g. with something simple like:

.vis-item .vis-onUpdateTime-tooltip {
    border-radius: 4px;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea! done

<style type="text/css">
.vis-item .vis-onUpdateTime-tooltip {
border-radius: 4px;
}
</style>

<script src="../../googleAnalytics.js"></script>
</head>

<body>

<h1>Timeline Tooltip on item onUpdateTime Option</h1>

<h2>With <code>tooltipOnItemUpdateTime: true</code>
</h2>

<div id="mytimeline1"></div>

<h2>With <code>tooltipOnItemUpdateTime: { template: [Function] }</code>
</h2>

<div id="mytimeline2"></div>


<h2>With groups</h2>

<div id="mytimeline3"></div>
<script>

// create items
var numberOfItems = 10;
var items = new vis.DataSet();
var types = [ 'box', 'point', 'range']


for (var order = 0; order < numberOfItems; order++) {
var date = vis.moment();


date.add(Math.round(Math.random() * 2), 'hour');
items.add({
id: order,
type: types[Math.floor(3 * Math.random())],
content: 'Item ' + order,
start: date.clone().add(order + 1, 'hour'),
end: date.clone().add(order + 3, 'hour')
});
}

// specify options
var options = {
multiselect: true,
maxHeight: 400,
start: new Date((new Date()).valueOf() - 10000000),
end: new Date(1000*60*60*24 + (new Date()).valueOf()),
editable: true
};

var options1 = Object.assign({
tooltipOnItemUpdateTime: true
}, options)
var container1 = document.getElementById('mytimeline1');
timeline1 = new vis.Timeline(container1, items, null, options1);

var options2 = Object.assign({
orientation: 'top',
tooltipOnItemUpdateTime: {
template: function(item) {
return 'html template for tooltip with <b>item.start</b>: ' + item.start;
}
}
}, options)
var container2 = document.getElementById('mytimeline2');
timeline2 = new vis.Timeline(container2, items, null, options2);


// create groups
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally would prefer a much more simple example without groups and stuff. Just something like the basicExample.

var numberOfGroups = 25;
var groups = new vis.DataSet()
for (var i = 0; i < numberOfGroups; i++) {
groups.add({
id: i,
content: 'Truck&nbsp;' + i
})
}

// create items for groups
var numberOfItems = 1000;
var itemsWithGroups = new vis.DataSet();

var itemsPerGroup = Math.round(numberOfItems/numberOfGroups);

for (var truck = 0; truck < numberOfGroups; truck++) {
var date = new Date();
for (var order = 0; order < itemsPerGroup; order++) {
date.setHours(date.getHours() + 4 * (Math.random() < 0.2));
var start = new Date(date);

date.setHours(date.getHours() + 2 + Math.floor(Math.random()*4));
var end = new Date(date);

itemsWithGroups.add({
id: order + itemsPerGroup * truck,
group: truck,
start: start,
end: end,
content: 'Order ' + order
});
}
}


var options3 = Object.assign({
orientation: 'top',
tooltipOnItemUpdateTime: true
}, options)
var container3 = document.getElementById('mytimeline3');
timeline3 = new vis.Timeline(container3, itemsWithGroups, groups, options3);

</script>

</body>
</html>
10 changes: 8 additions & 2 deletions lib/timeline/component/ItemSet.js
Expand Up @@ -90,7 +90,9 @@ function ItemSet(body, options) {
vertical: 10
},
axis: 20
}
},

tooltipOnItemUpdateTime: false
};

// options is shared by this ItemSet and all its items
Expand Down Expand Up @@ -316,7 +318,11 @@ ItemSet.prototype._create = function(){
ItemSet.prototype.setOptions = function(options) {
if (options) {
// copy all options that we know
var fields = ['type', 'rtl', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap', 'groupOrderSwap'];
var fields = [
'type', 'rtl', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable',
'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap',
'groupOrderSwap', 'tooltipOnItemUpdateTime'
];
util.selectiveExtend(fields, this.options, options);

if ('orientation' in options) {
Expand Down
11 changes: 11 additions & 0 deletions lib/timeline/component/css/item.css
Expand Up @@ -90,6 +90,17 @@
padding: 5px;
}

.vis-item .vis-onUpdateTime-tooltip {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add text-align: center and white-space: nowrap, make it autosize and remove the double background:

.vis-item .vis-onUpdateTime-tooltip {
    position: absolute;
    color: white;
    width: auto;
    background: #4f81bd;
    padding: 5px;
    border-radius: 1px;
    text-align: center;
    white-space: nowrap;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

position: absolute;
background: #4f81bd;
color: white;
width: 200px;
text-align: center;
white-space: nowrap;
padding: 5px;
border-radius: 1px;
}

.vis-item .vis-delete, .vis-item .vis-delete-rtl {
position: absolute;
top: 0px;
Expand Down
1 change: 1 addition & 0 deletions lib/timeline/component/item/BoxItem.js
Expand Up @@ -164,6 +164,7 @@ BoxItem.prototype.redraw = function() {
this.dirty = false;
}

this._repaintOnItemUpdateTimeTooltip(dom.box);
this._repaintDragCenter();
this._repaintDeleteButton(dom.box);
};
Expand Down
88 changes: 86 additions & 2 deletions lib/timeline/component/item/Item.js
@@ -1,5 +1,7 @@
var Hammer = require('../../../module/hammer');
var util = require('../../../util');
var moment = require('../../../module/moment');


/**
* @constructor Item
Expand All @@ -16,8 +18,7 @@ function Item (data, conversion, options) {
this.data = data;
this.dom = null;
this.conversion = conversion || {};
this.options = options || {};

this.options = options || {};
this.selected = false;
this.displayed = false;
this.dirty = true;
Expand Down Expand Up @@ -180,6 +181,89 @@ Item.prototype._repaintDeleteButton = function (anchor) {
}
};

/**
* Repaint a onChange tooltip on the top right of the item when the item is selected
* @param {HTMLElement} anchor
* @protected
*/
Item.prototype._repaintOnItemUpdateTimeTooltip = function (anchor) {
if (!this.options.tooltipOnItemUpdateTime) return;

var editable = (this.options.editable.updateTime ||
this.data.editable === true) &&
this.data.editable !== false;

if (this.selected && editable && !this.dom.onItemUpdateTimeTooltip) {
// create and show tooltip
var me = this;

var onItemUpdateTimeTooltip = document.createElement('div');

onItemUpdateTimeTooltip.className = 'vis-onUpdateTime-tooltip';
anchor.appendChild(onItemUpdateTimeTooltip);
this.dom.onItemUpdateTimeTooltip = onItemUpdateTimeTooltip;

} else if (!this.selected && this.dom.onItemUpdateTimeTooltip) {
// remove button
if (this.dom.onItemUpdateTimeTooltip.parentNode) {
this.dom.onItemUpdateTimeTooltip.parentNode.removeChild(this.dom.onItemUpdateTimeTooltip);
}
this.dom.onItemUpdateTimeTooltip = null;
}

// position onChange tooltip
if (this.dom.onItemUpdateTimeTooltip) {

// only show when editing
this.dom.onItemUpdateTimeTooltip.style.visibility = this.parent.itemSet.touchParams.itemIsDragging ? 'visible' : 'hidden';

// position relative to item's content
if (this.options.rtl) {
this.dom.onItemUpdateTimeTooltip.style.right = this.dom.content.style.right;
} else {
this.dom.onItemUpdateTimeTooltip.style.left = this.dom.content.style.left;
}

// position above or below the item depending on the item's position in the window
var tooltipOffset = 50; // TODO: should be tooltip height (depends on template)
var scrollTop = this.parent.itemSet.body.domProps.scrollTop;

// TODO: this.top for orientation:true is actually the items distance from the bottom...
// (should be this.bottom)
var itemDistanceFromTop
if (this.options.orientation.item == 'top') {
itemDistanceFromTop = this.top;
} else {
itemDistanceFromTop = (this.parent.height - this.top - this.height)
}
var isCloseToTop = itemDistanceFromTop + this.parent.top - tooltipOffset < -scrollTop;

if (isCloseToTop) {
this.dom.onItemUpdateTimeTooltip.style.bottom = "";
this.dom.onItemUpdateTimeTooltip.style.top = this.height + 2 + "px";
} else {
this.dom.onItemUpdateTimeTooltip.style.top = "";
this.dom.onItemUpdateTimeTooltip.style.bottom = this.height + 2 + "px";
}

// handle tooltip content
var content;
var templateFunction;

if (this.options.tooltipOnItemUpdateTime && this.options.tooltipOnItemUpdateTime.template) {
templateFunction = this.options.tooltipOnItemUpdateTime.template.bind(this);
content = templateFunction(this.data);
} else {
content = 'start: ' + moment(this.data.start).format('MM/DD/YYYY hh:mm');
if (this.data.end) {
content += '<br> end: ' + moment(this.data.end).format('MM/DD/YYYY hh:mm');
}
}
this.dom.onItemUpdateTimeTooltip.innerHTML = content;
}
};


/**
* Set HTML contents for the item
* @param {Element} element HTML element to fill with the contents
Expand Down
1 change: 1 addition & 0 deletions lib/timeline/component/item/PointItem.js
Expand Up @@ -141,6 +141,7 @@ PointItem.prototype.redraw = function() {
this.dirty = false;
}

this._repaintOnItemUpdateTimeTooltip(dom.point);
this._repaintDragCenter();
this._repaintDeleteButton(dom.point);
};
Expand Down
2 changes: 2 additions & 0 deletions lib/timeline/component/item/RangeItem.js
Expand Up @@ -123,6 +123,8 @@ RangeItem.prototype.redraw = function() {

this.dirty = false;
}

this._repaintOnItemUpdateTimeTooltip(dom.box);
this._repaintDeleteButton(dom.box);
this._repaintDragCenter();
this._repaintDragLeft();
Expand Down
5 changes: 5 additions & 0 deletions lib/timeline/optionsTimeline.js
Expand Up @@ -126,6 +126,10 @@ let allOptions = {
start: {date, number, string, moment},
template: {'function': 'function'},
groupTemplate: {'function': 'function'},
tooltipOnItemUpdateTime: {
template: {'function': 'function'},
__type__: {boolean, object}
},
timeAxis: {
scale: {string,'undefined': 'undefined'},
step: {number,'undefined': 'undefined'},
Expand Down Expand Up @@ -220,6 +224,7 @@ let configureOptions = {
// scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'],
// step: [1, 1, 10, 1]
//},
tooltipOnItemUpdateTime: false,
type: ['box', 'point', 'range', 'background'],
width: '100%',
zoomable: true,
Expand Down