From 339d39ec3d4b4f8e4a8603a5e810fa31c3b5f126 Mon Sep 17 00:00:00 2001 From: josdejong Date: Fri, 7 Feb 2014 17:25:55 +0100 Subject: [PATCH] Dragging left and right side of a range almost working --- HISTORY.md | 2 +- src/timeline/component/ItemSet.js | 69 ++++++++++++------- src/timeline/component/css/item.css | 39 ++++++++--- src/timeline/component/item/Item.js | 32 ++++----- src/timeline/component/item/ItemRange.js | 62 +++++++++++++++++ .../component/item/ItemRangeOverflow.js | 2 + 6 files changed, 149 insertions(+), 57 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 2fa69cc0..775135e1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,7 +6,7 @@ http://visjs.org ### Timeline -- items can be dragged and deleted. +- items can be dragged and removed. - Implemented options `selectable`, `editable`. - added events when dragging the custom time bar. diff --git a/src/timeline/component/ItemSet.js b/src/timeline/component/ItemSet.js index ecd7359b..381f098a 100644 --- a/src/timeline/component/ItemSet.js +++ b/src/timeline/component/ItemSet.js @@ -688,9 +688,34 @@ ItemSet.prototype._onDragStart = function (event) { me = this; if (item && item.selected) { - this.touchParams.items = this.getSelection().map(function (id) { - return me.items[id]; - }); + + var dragLeftItem = event.target.dragLeftItem; + var dragRightItem = event.target.dragRightItem; + if (dragLeftItem) { + this.touchParams.itemProps = [{ + item: dragLeftItem, + start: item.data.start.valueOf() + }]; + } + else if (dragRightItem) { + this.touchParams.itemProps = [{ + item: dragRightItem, + end: item.data.end.valueOf() + }]; + } + else { + this.touchParams.itemProps = this.getSelection().map(function (id) { + var item = me.items[id]; + var props = { + item: item + }; + + if ('start' in item.data) { props.start = item.data.start.valueOf() } + if ('end' in item.data) { props.end = item.data.end.valueOf() } + + return props; + }); + } event.stopPropagation(); } @@ -702,16 +727,16 @@ ItemSet.prototype._onDragStart = function (event) { * @private */ ItemSet.prototype._onDrag = function (event) { - if (this.touchParams.items) { - var deltaX = event.gesture.deltaX; + if (this.touchParams.itemProps) { + var deltaX = event.gesture.deltaX, + offset = deltaX / this.conversion.scale; - // adjust the offset of the items being dragged - this.touchParams.items.forEach(function (item) { - item.setOffset(deltaX); + // move + this.touchParams.itemProps.forEach(function (props) { + if ('start' in props) { props.item.data.start = new Date(props.start + offset); } + if ('end' in props) { props.item.data.end = new Date(props.end + offset); } }); - // TODO: stacking on dragend changes the order. (stacking orders by start date, which isn't yet changed) - // TODO: implement snapping to nice dates // TODO: implement dragging from one group to another @@ -728,29 +753,21 @@ ItemSet.prototype._onDrag = function (event) { * @private */ ItemSet.prototype._onDragEnd = function (event) { - if (this.touchParams.items) { - var deltaX = event.gesture.deltaX, - scale = this.conversion.scale; - + if (this.touchParams.itemProps) { // prepare a changeset for the changed items - var changes = this.touchParams.items.map(function (item) { - item.setOffset(0); - + var changes = this.touchParams.itemProps.map(function (props) { var change = { - id: item.id + id: props.item.id }; - if ('start' in item.data) { - change.start = new Date(item.data.start.valueOf() + deltaX / scale); - } - if ('end' in item.data) { - change.end = new Date(item.data.end.valueOf() + deltaX / scale); - } + if ('start' in props.item.data) { change.start = props.item.data.start; } + if ('end' in props.item.data) { change.end = props.item.data.end; } + + // TODO: only fire changes when start or end is actually changed return change; }); - this.touchParams.items = null; - + this.touchParams.itemProps = null; // apply the changes to the data var dataset = this._myDataSet(); diff --git a/src/timeline/component/css/item.css b/src/timeline/component/css/item.css index b1ba0d6f..8063bfd0 100644 --- a/src/timeline/component/css/item.css +++ b/src/timeline/component/css/item.css @@ -57,6 +57,7 @@ border-width: 1px; border-radius: 2px; -moz-border-radius: 2px; /* For Firefox 3.6 and older */ + box-sizing: border-box; } .vis.timeline .item.rangeoverflow { @@ -64,19 +65,11 @@ border-width: 1px; border-radius: 2px; -moz-border-radius: 2px; /* For Firefox 3.6 and older */ + box-sizing: border-box; } -.vis.timeline .item.range .drag-left, .vis.timeline .item.rangeoverflow .drag-left { - cursor: w-resize; - z-index: 1000; -} - -.vis.timeline .item.range .drag-right, .vis.timeline .item.rangeoverflow .drag-right { - cursor: e-resize; - z-index: 1000; -} - -.vis.timeline .item.range .content, .vis.timeline .item.rangeoverflow .content { +.vis.timeline .item.range .content, +.vis.timeline .item.rangeoverflow .content { position: relative; display: inline-block; } @@ -107,3 +100,27 @@ right: -24px; cursor: pointer; } + +.vis.timeline .item.range .drag-left, +.vis.timeline .item.rangeoverflow .drag-left { + position: absolute; + width: 24px; + height: 100%; + top: 0; + left: -4px; + + cursor: w-resize; + z-index: 10000; +} + +.vis.timeline .item.range .drag-right, +.vis.timeline .item.rangeoverflow .drag-right { + position: absolute; + width: 24px; + height: 100%; + top: 0; + right: -4px; + + cursor: e-resize; + z-index: 10001; /* a little higher z-index than .drag-left */ +} diff --git a/src/timeline/component/item/Item.js b/src/timeline/component/item/Item.js index 0fa8fbb9..eeacf4c6 100644 --- a/src/timeline/component/item/Item.js +++ b/src/timeline/component/item/Item.js @@ -87,35 +87,29 @@ Item.prototype.setOffset = function setOffset(offset) { * @private */ Item.prototype._repaintDeleteButton = function (anchor) { - // show/remove delete button if (this.selected && !this.dom.deleteButton) { + // create and show button var parent = this.parent; var id = this.id; - this.dom.deleteButton = Item.createDeleteButton(function () { + + var deleteButton = document.createElement('div'); + deleteButton.className = 'delete'; + deleteButton.title = 'Delete this item'; + + Hammer(deleteButton, { + preventDefault: true + }).on('tap', function () { parent.removeItem(id); }); - anchor.appendChild(this.dom.deleteButton); + + anchor.appendChild(deleteButton); + this.dom.deleteButton = deleteButton; } else if (!this.selected && this.dom.deleteButton) { + // remove button 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/ItemRange.js b/src/timeline/component/item/ItemRange.js index 705ac31d..76cbf3df 100644 --- a/src/timeline/component/item/ItemRange.js +++ b/src/timeline/component/item/ItemRange.js @@ -68,6 +68,8 @@ ItemRange.prototype.repaint = function repaint() { } this._repaintDeleteButton(dom.box); + this._repaintDragLeft(); + this._repaintDragRight(); // update class var className = (this.data.className? ' ' + this.data.className : '') + @@ -251,3 +253,63 @@ ItemRange.prototype.reposition = function reposition() { dom.content.style.left = props.content.left + 'px'; } }; + +/** + * Repaint a drag area on the left side of the range when the range is selected + * @private + */ +ItemRange.prototype._repaintDragLeft = function () { + if (this.selected && !this.dom.dragLeft) { + // create and show drag area + var dragLeft = document.createElement('div'); + dragLeft.className = 'drag-left'; + dragLeft.dragLeftItem = this; + + // TODO: this should be redundant? + Hammer(dragLeft, { + preventDefault: true + }).on('drag', function () { + //console.log('drag left') + }); + + this.dom.box.appendChild(dragLeft); + this.dom.dragLeft = dragLeft; + } + else if (!this.selected && this.dom.dragLeft) { + // delete drag area + if (this.dom.dragLeft.parentNode) { + this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); + } + this.dom.dragLeft = null; + } +}; + +/** + * Repaint a drag area on the right side of the range when the range is selected + * @private + */ +ItemRange.prototype._repaintDragRight = function () { + if (this.selected && !this.dom.dragRight) { + // create and show drag area + var dragRight = document.createElement('div'); + dragRight.className = 'drag-right'; + dragRight.dragRightItem = this; + + // TODO: this should be redundant? + Hammer(dragRight, { + preventDefault: true + }).on('drag', function () { + //console.log('drag right') + }); + + this.dom.box.appendChild(dragRight); + this.dom.dragRight = dragRight; + } + else if (!this.selected && this.dom.dragRight) { + // delete drag area + if (this.dom.dragRight.parentNode) { + this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); + } + this.dom.dragRight = null; + } +}; diff --git a/src/timeline/component/item/ItemRangeOverflow.js b/src/timeline/component/item/ItemRangeOverflow.js index 8ef0e211..9db06cb5 100644 --- a/src/timeline/component/item/ItemRangeOverflow.js +++ b/src/timeline/component/item/ItemRangeOverflow.js @@ -84,6 +84,8 @@ ItemRangeOverflow.prototype.repaint = function repaint() { } this._repaintDeleteButton(dom.box); + this._repaintDragLeft(); + this._repaintDragRight(); // update class var className = (this.data.className? ' ' + this.data.className : '') +