Browse Source

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
codeClimate
yotamberk 8 years ago
committed by Alexander Wunschik
parent
commit
51528bae5a
9 changed files with 258 additions and 5 deletions
  1. +14
    -1
      docs/timeline/index.html
  2. +130
    -0
      examples/timeline/editing/tooltipOnItemChange.html
  3. +8
    -2
      lib/timeline/component/ItemSet.js
  4. +11
    -0
      lib/timeline/component/css/item.css
  5. +1
    -0
      lib/timeline/component/item/BoxItem.js
  6. +86
    -2
      lib/timeline/component/item/Item.js
  7. +1
    -0
      lib/timeline/component/item/PointItem.js
  8. +2
    -0
      lib/timeline/component/item/RangeItem.js
  9. +5
    -0
      lib/timeline/optionsTimeline.js

+ 14
- 1
docs/timeline/index.html View File

@ -1024,10 +1024,23 @@ function (option, path) {
</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>

+ 130
- 0
examples/timeline/editing/tooltipOnItemChange.html View File

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

+ 8
- 2
lib/timeline/component/ItemSet.js View File

@ -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) {

+ 11
- 0
lib/timeline/component/css/item.css View File

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

+ 1
- 0
lib/timeline/component/item/BoxItem.js View File

@ -164,6 +164,7 @@ BoxItem.prototype.redraw = function() {
this.dirty = false;
}
this._repaintOnItemUpdateTimeTooltip(dom.box);
this._repaintDragCenter();
this._repaintDeleteButton(dom.box);
};

+ 86
- 2
lib/timeline/component/item/Item.js View File

@ -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;
@ -179,6 +180,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

+ 1
- 0
lib/timeline/component/item/PointItem.js View File

@ -141,6 +141,7 @@ PointItem.prototype.redraw = function() {
this.dirty = false;
}
this._repaintOnItemUpdateTimeTooltip(dom.point);
this._repaintDragCenter();
this._repaintDeleteButton(dom.point);
};

+ 2
- 0
lib/timeline/component/item/RangeItem.js View File

@ -123,6 +123,8 @@ RangeItem.prototype.redraw = function() {
this.dirty = false;
}
this._repaintOnItemUpdateTimeTooltip(dom.box);
this._repaintDeleteButton(dom.box);
this._repaintDragCenter();
this._repaintDragLeft();

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

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

Loading…
Cancel
Save