diff --git a/HISTORY.md b/HISTORY.md index 7028a3a2..2fa69cc0 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,7 +6,7 @@ http://visjs.org ### Timeline -- items can be dragged. +- items can be dragged and deleted. - Implemented options `selectable`, `editable`. - added events when dragging the custom time bar. diff --git a/Jakefile.js b/Jakefile.js index 0d568d52..ab42efaa 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -96,6 +96,9 @@ task('build', {async: true}, function () { wrench.copyDirSyncRecursive('./src/graph/img', DIST+ '/img', { forceDelete: true }); + wrench.copyDirSyncRecursive('./src/timeline/img', DIST+ '/img/timeline', { + forceDelete: true + }); var timeStart = Date.now(); // bundle the concatenated script and dependencies into one file diff --git a/examples/timeline/01_basic.html b/examples/timeline/01_basic.html index b196a78c..514eefd1 100644 --- a/examples/timeline/01_basic.html +++ b/examples/timeline/01_basic.html @@ -21,7 +21,7 @@ {id: 1, content: 'item 1', start: '2013-04-20'}, {id: 2, content: 'item 2', start: '2013-04-14'}, {id: 3, content: 'item 3', start: '2013-04-18'}, - {id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19', type:'rangeoverflow'}, + {id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'}, {id: 5, content: 'item 5', start: '2013-04-25'}, {id: 6, content: 'item 6', start: '2013-04-27'} ]; diff --git a/src/timeline/component/ItemSet.js b/src/timeline/component/ItemSet.js index 28e3011e..ecd7359b 100644 --- a/src/timeline/component/ItemSet.js +++ b/src/timeline/component/ItemSet.js @@ -572,6 +572,15 @@ ItemSet.prototype.getItems = function getItems() { return this.itemsData; }; +/** + * Remove an item by its id + * @param {String | Number} id + */ +ItemSet.prototype.removeItem = function removeItem (id) { + var dataset = this._myDataSet(); + dataset.remove(id); +}; + /** * Handle updated items * @param {Number[]} ids @@ -742,14 +751,10 @@ ItemSet.prototype._onDragEnd = function (event) { }); this.touchParams.items = null; - // find the root DataSet from our DataSet/DataView - var data = this.itemsData; - while (data instanceof DataView) { - data = data.data; - } // apply the changes to the data - data.update(changes); + var dataset = this._myDataSet(); + dataset.update(changes); event.stopPropagation(); } @@ -790,3 +795,17 @@ ItemSet.itemSetFromTarget = function itemSetFromTarget (event) { return null; }; + +/** + * Find the DataSet to which this ItemSet is connected + * @returns {null | DataSet} dataset + * @private + */ +ItemSet.prototype._myDataSet = function _myDataSet() { + // find the root DataSet + var dataset = this.itemsData; + while (dataset instanceof DataView) { + dataset = dataset.data; + } + return dataset; +}; \ No newline at end of file diff --git a/src/timeline/component/css/item.css b/src/timeline/component/css/item.css index cc6cdccb..b1ba0d6f 100644 --- a/src/timeline/component/css/item.css +++ b/src/timeline/component/css/item.css @@ -5,12 +5,14 @@ border-color: #97B0F8; background-color: #D5DDF6; display: inline-block; + padding: 5px; } .vis.timeline .item.selected { border-color: #FFC200; background-color: #FFF785; z-index: 999; + cursor: move; } .vis.timeline .item.point.selected { @@ -43,6 +45,7 @@ } .vis.timeline .dot { + padding: 0; border: 5px solid #97B0F8; position: absolute; border-radius: 5px; @@ -50,7 +53,6 @@ } .vis.timeline .item.range { - overflow: hidden; border-style: solid; border-width: 1px; border-radius: 2px; @@ -80,6 +82,7 @@ } .vis.timeline .item.line { + padding: 0; position: absolute; width: 0; border-left-width: 1px; @@ -87,7 +90,20 @@ } .vis.timeline .item .content { - margin: 5px; white-space: nowrap; overflow: hidden; } + +.vis.timeline .item.range .content { + width: 100%; +} + +.vis.timeline .item .delete { + background: url('img/timeline/delete.png') no-repeat top center; + position: absolute; + width: 24px; + height: 24px; + top: 0; + right: -24px; + cursor: pointer; +} diff --git a/src/timeline/component/item/Item.js b/src/timeline/component/item/Item.js index f0deaec0..0fa8fbb9 100644 --- a/src/timeline/component/item/Item.js +++ b/src/timeline/component/item/Item.js @@ -80,3 +80,42 @@ Item.prototype.reflow = function reflow() { Item.prototype.setOffset = function setOffset(offset) { this.offset = offset; }; + +/** + * Repaint a delete button on the top right of the item when the item is selected + * @param {HTMLElement} anchor + * @private + */ +Item.prototype._repaintDeleteButton = function (anchor) { + // show/remove delete button + if (this.selected && !this.dom.deleteButton) { + var parent = this.parent; + var id = this.id; + this.dom.deleteButton = Item.createDeleteButton(function () { + parent.removeItem(id); + }); + anchor.appendChild(this.dom.deleteButton); + } + else if (!this.selected && this.dom.deleteButton) { + if (this.dom.deleteButton.parentNode) { + this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); + } + this.dom.deleteButton = null; + } +}; + +/** + * Create a delete button which can be attached to this item + * @param {function} callback Called when the button is clicked + * @returns {HTMLElement} deleteButton + */ +Item.createDeleteButton = function createDeleteButton (callback) { + var button = document.createElement('div'); + button.className = 'delete'; + + Hammer(button, { + preventDefault: true + }).on('tap', callback); + + return button; +}; diff --git a/src/timeline/component/item/ItemBox.js b/src/timeline/component/item/ItemBox.js index 8ea236c9..5415cb04 100644 --- a/src/timeline/component/item/ItemBox.js +++ b/src/timeline/component/item/ItemBox.js @@ -79,6 +79,8 @@ ItemBox.prototype.repaint = function repaint() { changed = true; } + this._repaintDeleteButton(dom.box); + // update contents if (this.data.content != this.content) { this.content = this.data.content; diff --git a/src/timeline/component/item/ItemPoint.js b/src/timeline/component/item/ItemPoint.js index 2d5124e1..776ec14a 100644 --- a/src/timeline/component/item/ItemPoint.js +++ b/src/timeline/component/item/ItemPoint.js @@ -73,6 +73,8 @@ ItemPoint.prototype.repaint = function repaint() { changed = true; } + this._repaintDeleteButton(dom.point); + // update class var className = (this.data.className? ' ' + this.data.className : '') + (this.selected ? ' selected' : ''); diff --git a/src/timeline/component/item/ItemRange.js b/src/timeline/component/item/ItemRange.js index bb7b8386..705ac31d 100644 --- a/src/timeline/component/item/ItemRange.js +++ b/src/timeline/component/item/ItemRange.js @@ -67,6 +67,8 @@ ItemRange.prototype.repaint = function repaint() { changed = true; } + this._repaintDeleteButton(dom.box); + // update class var className = (this.data.className? ' ' + this.data.className : '') + (this.selected ? ' selected' : ''); diff --git a/src/timeline/component/item/ItemRangeOverflow.js b/src/timeline/component/item/ItemRangeOverflow.js index d2bc5087..8ef0e211 100644 --- a/src/timeline/component/item/ItemRangeOverflow.js +++ b/src/timeline/component/item/ItemRangeOverflow.js @@ -83,6 +83,8 @@ ItemRangeOverflow.prototype.repaint = function repaint() { changed = true; } + this._repaintDeleteButton(dom.box); + // update class var className = (this.data.className? ' ' + this.data.className : '') + (this.selected ? ' selected' : '');