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

Commit 51528ba

Browse files
yotamberkmojoaxel
authored andcommitted
Tooltip on item update time (#2247)
* Add initial onChange item tooltip * Add example and tooltipOnItemEdit template option * Add docs and rename option * Fix docs * Fix example * Change example's item types * Fix point item tooltip * Fix comments from PR and support bottom orientation properly * Add semi-colon
1 parent ecf1cad commit 51528ba

File tree

9 files changed

+258
-5
lines changed

9 files changed

+258
-5
lines changed

docs/timeline/index.html

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1024,10 +1024,23 @@ <h2 id="Configuration_Options">Configuration Options</h2>
10241024
</td>
10251025
</tr>
10261026

1027+
<tr class='toggle collapsible' onclick="toggleTable('optionTable','tooltipOnItemUpdateTime', this);">
1028+
<td><span parent="tooltipOnItemUpdateTime" class="right-caret"></span> tooltipOnItemUpdateTime</td>
1029+
<td>Object/Boolean</td>
1030+
<td><code>false</code></td>
1031+
<td>Show a tooltip on updating an item's time. Note: <code>editable.updateTime</code> must be <code>true</code></td>
1032+
</tr>
1033+
<tr parent="tooltipOnItemUpdateTime" class="hidden">
1034+
<td class="indent">template</td>
1035+
<td>Function</td>
1036+
<td>none</td>
1037+
<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.
1038+
</td>
1039+
</tr>
10271040
<tr>
10281041
<td>verticalScroll</td>
10291042
<td>Boolean</td>
1030-
<td>false</td>
1043+
<td><code>false</code></td>
10311044
<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
10321045
</td>
10331046
</tr>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<html>
2+
<head>
3+
<title>Timeline | Tooltip on item onUpdateTime Option</title>
4+
5+
<script src="../../../dist/vis.js"></script>
6+
<link href="../../../dist/vis.css" rel="stylesheet" type="text/css" />
7+
8+
<style type="text/css">
9+
.vis-item .vis-onUpdateTime-tooltip {
10+
border-radius: 4px;
11+
}
12+
</style>
13+
14+
<script src="../../googleAnalytics.js"></script>
15+
</head>
16+
17+
<body>
18+
19+
<h1>Timeline Tooltip on item onUpdateTime Option</h1>
20+
21+
<h2>With <code>tooltipOnItemUpdateTime: true</code>
22+
</h2>
23+
24+
<div id="mytimeline1"></div>
25+
26+
<h2>With <code>tooltipOnItemUpdateTime: { template: [Function] }</code>
27+
</h2>
28+
29+
<div id="mytimeline2"></div>
30+
31+
32+
<h2>With groups</h2>
33+
34+
<div id="mytimeline3"></div>
35+
<script>
36+
37+
// create items
38+
var numberOfItems = 10;
39+
var items = new vis.DataSet();
40+
var types = [ 'box', 'point', 'range']
41+
42+
43+
for (var order = 0; order < numberOfItems; order++) {
44+
var date = vis.moment();
45+
46+
47+
date.add(Math.round(Math.random() * 2), 'hour');
48+
items.add({
49+
id: order,
50+
type: types[Math.floor(3 * Math.random())],
51+
content: 'Item ' + order,
52+
start: date.clone().add(order + 1, 'hour'),
53+
end: date.clone().add(order + 3, 'hour')
54+
});
55+
}
56+
57+
// specify options
58+
var options = {
59+
multiselect: true,
60+
maxHeight: 400,
61+
start: new Date((new Date()).valueOf() - 10000000),
62+
end: new Date(1000*60*60*24 + (new Date()).valueOf()),
63+
editable: true
64+
};
65+
66+
var options1 = Object.assign({
67+
tooltipOnItemUpdateTime: true
68+
}, options)
69+
var container1 = document.getElementById('mytimeline1');
70+
timeline1 = new vis.Timeline(container1, items, null, options1);
71+
72+
var options2 = Object.assign({
73+
orientation: 'top',
74+
tooltipOnItemUpdateTime: {
75+
template: function(item) {
76+
return 'html template for tooltip with <b>item.start</b>: ' + item.start;
77+
}
78+
}
79+
}, options)
80+
var container2 = document.getElementById('mytimeline2');
81+
timeline2 = new vis.Timeline(container2, items, null, options2);
82+
83+
84+
// create groups
85+
var numberOfGroups = 25;
86+
var groups = new vis.DataSet()
87+
for (var i = 0; i < numberOfGroups; i++) {
88+
groups.add({
89+
id: i,
90+
content: 'Truck&nbsp;' + i
91+
})
92+
}
93+
94+
// create items for groups
95+
var numberOfItems = 1000;
96+
var itemsWithGroups = new vis.DataSet();
97+
98+
var itemsPerGroup = Math.round(numberOfItems/numberOfGroups);
99+
100+
for (var truck = 0; truck < numberOfGroups; truck++) {
101+
var date = new Date();
102+
for (var order = 0; order < itemsPerGroup; order++) {
103+
date.setHours(date.getHours() + 4 * (Math.random() < 0.2));
104+
var start = new Date(date);
105+
106+
date.setHours(date.getHours() + 2 + Math.floor(Math.random()*4));
107+
var end = new Date(date);
108+
109+
itemsWithGroups.add({
110+
id: order + itemsPerGroup * truck,
111+
group: truck,
112+
start: start,
113+
end: end,
114+
content: 'Order ' + order
115+
});
116+
}
117+
}
118+
119+
120+
var options3 = Object.assign({
121+
orientation: 'top',
122+
tooltipOnItemUpdateTime: true
123+
}, options)
124+
var container3 = document.getElementById('mytimeline3');
125+
timeline3 = new vis.Timeline(container3, itemsWithGroups, groups, options3);
126+
127+
</script>
128+
129+
</body>
130+
</html>

lib/timeline/component/ItemSet.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ function ItemSet(body, options) {
9090
vertical: 10
9191
},
9292
axis: 20
93-
}
93+
},
94+
95+
tooltipOnItemUpdateTime: false
9496
};
9597

9698
// options is shared by this ItemSet and all its items
@@ -316,7 +318,11 @@ ItemSet.prototype._create = function(){
316318
ItemSet.prototype.setOptions = function(options) {
317319
if (options) {
318320
// copy all options that we know
319-
var fields = ['type', 'rtl', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap', 'groupOrderSwap'];
321+
var fields = [
322+
'type', 'rtl', 'align', 'order', 'stack', 'selectable', 'multiselect', 'itemsAlwaysDraggable',
323+
'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap',
324+
'groupOrderSwap', 'tooltipOnItemUpdateTime'
325+
];
320326
util.selectiveExtend(fields, this.options, options);
321327

322328
if ('orientation' in options) {

lib/timeline/component/css/item.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@
9090
padding: 5px;
9191
}
9292

93+
.vis-item .vis-onUpdateTime-tooltip {
94+
position: absolute;
95+
background: #4f81bd;
96+
color: white;
97+
width: 200px;
98+
text-align: center;
99+
white-space: nowrap;
100+
padding: 5px;
101+
border-radius: 1px;
102+
}
103+
93104
.vis-item .vis-delete, .vis-item .vis-delete-rtl {
94105
position: absolute;
95106
top: 0px;

lib/timeline/component/item/BoxItem.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ BoxItem.prototype.redraw = function() {
164164
this.dirty = false;
165165
}
166166

167+
this._repaintOnItemUpdateTimeTooltip(dom.box);
167168
this._repaintDragCenter();
168169
this._repaintDeleteButton(dom.box);
169170
};

lib/timeline/component/item/Item.js

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
var Hammer = require('../../../module/hammer');
22
var util = require('../../../util');
3+
var moment = require('../../../module/moment');
4+
35

46
/**
57
* @constructor Item
@@ -16,8 +18,7 @@ function Item (data, conversion, options) {
1618
this.data = data;
1719
this.dom = null;
1820
this.conversion = conversion || {};
19-
this.options = options || {};
20-
21+
this.options = options || {};
2122
this.selected = false;
2223
this.displayed = false;
2324
this.dirty = true;
@@ -179,6 +180,89 @@ Item.prototype._repaintDeleteButton = function (anchor) {
179180
}
180181
};
181182

183+
/**
184+
* Repaint a onChange tooltip on the top right of the item when the item is selected
185+
* @param {HTMLElement} anchor
186+
* @protected
187+
*/
188+
Item.prototype._repaintOnItemUpdateTimeTooltip = function (anchor) {
189+
if (!this.options.tooltipOnItemUpdateTime) return;
190+
191+
var editable = (this.options.editable.updateTime ||
192+
this.data.editable === true) &&
193+
this.data.editable !== false;
194+
195+
if (this.selected && editable && !this.dom.onItemUpdateTimeTooltip) {
196+
// create and show tooltip
197+
var me = this;
198+
199+
var onItemUpdateTimeTooltip = document.createElement('div');
200+
201+
onItemUpdateTimeTooltip.className = 'vis-onUpdateTime-tooltip';
202+
anchor.appendChild(onItemUpdateTimeTooltip);
203+
this.dom.onItemUpdateTimeTooltip = onItemUpdateTimeTooltip;
204+
205+
} else if (!this.selected && this.dom.onItemUpdateTimeTooltip) {
206+
// remove button
207+
if (this.dom.onItemUpdateTimeTooltip.parentNode) {
208+
this.dom.onItemUpdateTimeTooltip.parentNode.removeChild(this.dom.onItemUpdateTimeTooltip);
209+
}
210+
this.dom.onItemUpdateTimeTooltip = null;
211+
}
212+
213+
// position onChange tooltip
214+
if (this.dom.onItemUpdateTimeTooltip) {
215+
216+
// only show when editing
217+
this.dom.onItemUpdateTimeTooltip.style.visibility = this.parent.itemSet.touchParams.itemIsDragging ? 'visible' : 'hidden';
218+
219+
// position relative to item's content
220+
if (this.options.rtl) {
221+
this.dom.onItemUpdateTimeTooltip.style.right = this.dom.content.style.right;
222+
} else {
223+
this.dom.onItemUpdateTimeTooltip.style.left = this.dom.content.style.left;
224+
}
225+
226+
// position above or below the item depending on the item's position in the window
227+
var tooltipOffset = 50; // TODO: should be tooltip height (depends on template)
228+
var scrollTop = this.parent.itemSet.body.domProps.scrollTop;
229+
230+
// TODO: this.top for orientation:true is actually the items distance from the bottom...
231+
// (should be this.bottom)
232+
var itemDistanceFromTop
233+
if (this.options.orientation.item == 'top') {
234+
itemDistanceFromTop = this.top;
235+
} else {
236+
itemDistanceFromTop = (this.parent.height - this.top - this.height)
237+
}
238+
var isCloseToTop = itemDistanceFromTop + this.parent.top - tooltipOffset < -scrollTop;
239+
240+
if (isCloseToTop) {
241+
this.dom.onItemUpdateTimeTooltip.style.bottom = "";
242+
this.dom.onItemUpdateTimeTooltip.style.top = this.height + 2 + "px";
243+
} else {
244+
this.dom.onItemUpdateTimeTooltip.style.top = "";
245+
this.dom.onItemUpdateTimeTooltip.style.bottom = this.height + 2 + "px";
246+
}
247+
248+
// handle tooltip content
249+
var content;
250+
var templateFunction;
251+
252+
if (this.options.tooltipOnItemUpdateTime && this.options.tooltipOnItemUpdateTime.template) {
253+
templateFunction = this.options.tooltipOnItemUpdateTime.template.bind(this);
254+
content = templateFunction(this.data);
255+
} else {
256+
content = 'start: ' + moment(this.data.start).format('MM/DD/YYYY hh:mm');
257+
if (this.data.end) {
258+
content += '<br> end: ' + moment(this.data.end).format('MM/DD/YYYY hh:mm');
259+
}
260+
}
261+
this.dom.onItemUpdateTimeTooltip.innerHTML = content;
262+
}
263+
};
264+
265+
182266
/**
183267
* Set HTML contents for the item
184268
* @param {Element} element HTML element to fill with the contents

lib/timeline/component/item/PointItem.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ PointItem.prototype.redraw = function() {
141141
this.dirty = false;
142142
}
143143

144+
this._repaintOnItemUpdateTimeTooltip(dom.point);
144145
this._repaintDragCenter();
145146
this._repaintDeleteButton(dom.point);
146147
};

lib/timeline/component/item/RangeItem.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ RangeItem.prototype.redraw = function() {
123123

124124
this.dirty = false;
125125
}
126+
127+
this._repaintOnItemUpdateTimeTooltip(dom.box);
126128
this._repaintDeleteButton(dom.box);
127129
this._repaintDragCenter();
128130
this._repaintDragLeft();

lib/timeline/optionsTimeline.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ let allOptions = {
126126
start: {date, number, string, moment},
127127
template: {'function': 'function'},
128128
groupTemplate: {'function': 'function'},
129+
tooltipOnItemUpdateTime: {
130+
template: {'function': 'function'},
131+
__type__: {boolean, object}
132+
},
129133
timeAxis: {
130134
scale: {string,'undefined': 'undefined'},
131135
step: {number,'undefined': 'undefined'},
@@ -220,6 +224,7 @@ let configureOptions = {
220224
// scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'],
221225
// step: [1, 1, 10, 1]
222226
//},
227+
tooltipOnItemUpdateTime: false,
223228
type: ['box', 'point', 'range', 'background'],
224229
width: '100%',
225230
zoomable: true,

0 commit comments

Comments
 (0)