diff --git a/docs/timeline/index.html b/docs/timeline/index.html index 3336823d..b7a14a1c 100644 --- a/docs/timeline/index.html +++ b/docs/timeline/index.html @@ -1024,10 +1024,23 @@ function (option, path) { + + tooltipOnItemUpdateTime + Object/Boolean + false + Show a tooltip on updating an item's time. Note: editable.updateTime must be true + + + template + Function + none + 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 Templates for a detailed explanation. + + verticalScroll Boolean - false + false 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 true will NOT override horizontalScroll. The scroll event will be vertically ignored, but a vertical scrollbar will be visible diff --git a/examples/timeline/editing/tooltipOnItemChange.html b/examples/timeline/editing/tooltipOnItemChange.html new file mode 100644 index 00000000..18380beb --- /dev/null +++ b/examples/timeline/editing/tooltipOnItemChange.html @@ -0,0 +1,130 @@ + + + Timeline | Tooltip on item onUpdateTime Option + + + + + + + + + + + +

Timeline Tooltip on item onUpdateTime Option

+ +

With tooltipOnItemUpdateTime: true +

+ +
+ +

With tooltipOnItemUpdateTime: { template: [Function] } +

+ +
+ + +

With groups

+ +
+ + + + diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js index 9f4fc7a7..b7c28eb0 100644 --- a/lib/timeline/component/ItemSet.js +++ b/lib/timeline/component/ItemSet.js @@ -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) { diff --git a/lib/timeline/component/css/item.css b/lib/timeline/component/css/item.css index 0e9327ae..37f05533 100644 --- a/lib/timeline/component/css/item.css +++ b/lib/timeline/component/css/item.css @@ -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; diff --git a/lib/timeline/component/item/BoxItem.js b/lib/timeline/component/item/BoxItem.js index 2963619b..2a2fc415 100644 --- a/lib/timeline/component/item/BoxItem.js +++ b/lib/timeline/component/item/BoxItem.js @@ -164,6 +164,7 @@ BoxItem.prototype.redraw = function() { this.dirty = false; } + this._repaintOnItemUpdateTimeTooltip(dom.box); this._repaintDragCenter(); this._repaintDeleteButton(dom.box); }; diff --git a/lib/timeline/component/item/Item.js b/lib/timeline/component/item/Item.js index 08727bed..900377cd 100644 --- a/lib/timeline/component/item/Item.js +++ b/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 @@ -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 += '
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 diff --git a/lib/timeline/component/item/PointItem.js b/lib/timeline/component/item/PointItem.js index 91b86909..aebedaa4 100644 --- a/lib/timeline/component/item/PointItem.js +++ b/lib/timeline/component/item/PointItem.js @@ -141,6 +141,7 @@ PointItem.prototype.redraw = function() { this.dirty = false; } + this._repaintOnItemUpdateTimeTooltip(dom.point); this._repaintDragCenter(); this._repaintDeleteButton(dom.point); }; diff --git a/lib/timeline/component/item/RangeItem.js b/lib/timeline/component/item/RangeItem.js index 5e287f4f..a840b529 100644 --- a/lib/timeline/component/item/RangeItem.js +++ b/lib/timeline/component/item/RangeItem.js @@ -123,6 +123,8 @@ RangeItem.prototype.redraw = function() { this.dirty = false; } + + this._repaintOnItemUpdateTimeTooltip(dom.box); this._repaintDeleteButton(dom.box); this._repaintDragCenter(); this._repaintDragLeft(); diff --git a/lib/timeline/optionsTimeline.js b/lib/timeline/optionsTimeline.js index 4c976d85..2e94ef2e 100644 --- a/lib/timeline/optionsTimeline.js +++ b/lib/timeline/optionsTimeline.js @@ -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,