Permalink
Browse files

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
  • Loading branch information...
1 parent ecf1cad commit 51528bae5aa1e9968cc280053912b39648210167 @yotamberk yotamberk committed with mojoaxel Nov 4, 2016
@@ -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>
+ <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>
@@ -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" />
+
+ <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
+ 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>
@@ -90,7 +90,9 @@ function ItemSet(body, options) {
vertical: 10
},
axis: 20
- }
+ },
+
+ tooltipOnItemUpdateTime: false
};
// options is shared by this ItemSet and all its items
@@ -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) {
@@ -90,6 +90,17 @@
padding: 5px;
}
+.vis-item .vis-onUpdateTime-tooltip {
+ 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;
@@ -164,6 +164,7 @@ BoxItem.prototype.redraw = function() {
this.dirty = false;
}
+ this._repaintOnItemUpdateTimeTooltip(dom.box);
this._repaintDragCenter();
this._repaintDeleteButton(dom.box);
};
@@ -1,5 +1,7 @@
var Hammer = require('../../../module/hammer');
var util = require('../../../util');
+var moment = require('../../../module/moment');
+
/**
* @constructor Item
@@ -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;
@@ -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
* @private
@@ -141,6 +141,7 @@ PointItem.prototype.redraw = function() {
this.dirty = false;
}
+ this._repaintOnItemUpdateTimeTooltip(dom.point);
this._repaintDragCenter();
this._repaintDeleteButton(dom.point);
};
@@ -123,6 +123,8 @@ RangeItem.prototype.redraw = function() {
this.dirty = false;
}
+
+ this._repaintOnItemUpdateTimeTooltip(dom.box);
this._repaintDeleteButton(dom.box);
this._repaintDragCenter();
this._repaintDragLeft();
@@ -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'},
@@ -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,

0 comments on commit 51528ba

Please sign in to comment.