diff --git a/examples/timeline/other/drag&drop.html b/examples/timeline/other/drag&drop.html new file mode 100644 index 00000000..81bcb1f1 --- /dev/null +++ b/examples/timeline/other/drag&drop.html @@ -0,0 +1,131 @@ + + + + + Timeline | Drag & Drop + + + + + + + + + + + +

Timeline Drag & Drop Example

+ +

For this to work, you will have to define your own 'dragstart' eventListener on each item in your list of items (make sure that any new item added to the list is attached to this eventListener 'dragstart' handler). This 'dragstart' handler must set dataTransfer - notice you can set the item's information as you want this way.

+ +
+
+

Items:

+ +
+ + + + + diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js index a0352cae..4589568b 100644 --- a/lib/timeline/Core.js +++ b/lib/timeline/Core.js @@ -231,6 +231,46 @@ Core.prototype._create = function (container) { this.dom.left.parentNode.addEventListener('scroll', onMouseScrollSide.bind(this)); this.dom.right.parentNode.addEventListener('scroll', onMouseScrollSide.bind(this)); + var itemAddedToTimeline = false; + + function handleDragOver(event) { + if (event.preventDefault) { + event.preventDefault(); // Necessary. Allows us to drop. + } + + // make sure your target is a vis element + if (!event.target.className.includes('vis')) return; + + // make sure only one item is added every time you're over the timeline + if (itemAddedToTimeline) return; + + event.dataTransfer.dropEffect = 'move'; + itemAddedToTimeline = true; + return false; + } + + function handleDrop(event) { + // return when dropping non-vis items + try { + var itemData = JSON.parse(event.dataTransfer.getData("text/plain")) + if (!itemData.content) return + } catch (err) { + return false; + } + + itemAddedToTimeline = false; + event.center = { + x: event.x, + y: event.y + } + me.itemSet._onAddItem(event); + + return false; + } + + this.dom.center.addEventListener('dragover', handleDragOver.bind(this), false); + this.dom.center.addEventListener('drop', handleDrop.bind(this), false); + this.customTimes = []; // store state information needed for touch events diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js index f2020b09..0bd8cb86 100644 --- a/lib/timeline/component/ItemSet.js +++ b/lib/timeline/component/ItemSet.js @@ -95,9 +95,7 @@ function ItemSet(body, options) { // options is shared by this ItemSet and all its items this.options = util.extend({}, this.defaultOptions); - if (options) { - this.options.rtl = options.rtl; // required to determine from the initial creation if rtl - } + this.options.rtl = options.rtl; // options for getting items from the DataSet with the correct type this.itemOptions = { @@ -320,7 +318,7 @@ ItemSet.prototype.setOptions = function(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']; util.selectiveExtend(fields, this.options, options); - + if ('orientation' in options) { if (typeof options.orientation === 'string') { this.options.orientation.item = options.orientation === 'top' ? 'top' : 'bottom'; @@ -1825,13 +1823,30 @@ ItemSet.prototype._onAddItem = function (event) { content: 'new item' }; - // when default type is a range, add a default end date to the new item - if (this.options.type === 'range') { - var end = this.body.util.toTime(x + this.props.width / 5); - newItemData.end = snap ? snap(end, scale, step) : end; - } + if (event.type == 'drop') { + var itemData = JSON.parse(event.dataTransfer.getData("text/plain")) + newItemData.content = itemData.content; // content is required + newItemData.type = itemData.type || 'box'; + newItemData[this.itemsData._fieldId] = itemData.id || util.randomUUID(); + + if (itemData.type == 'range' || (itemData.end && itemData.start)) { + + if (!itemData.end) { + var end = this.body.util.toTime(x + this.props.width / 5); + newItemData.end = snap ? snap(end, scale, step) : end; + } else { + newItemData.end = new Date(newItemData.start._i).getTime() + new Date(itemData.end).getTime() - new Date(itemData.start).getTime(); + } + } + } else { + newItemData[this.itemsData._fieldId] = util.randomUUID(); - newItemData[this.itemsData._fieldId] = util.randomUUID(); + // when default type is a range, add a default end date to the new item + if (this.options.type === 'range') { + var end = this.body.util.toTime(x + this.props.width / 5); + newItemData.end = snap ? snap(end, scale, step) : end; + } + } var group = this.groupFromTarget(event); if (group) { @@ -1843,6 +1858,9 @@ ItemSet.prototype._onAddItem = function (event) { this.options.onAdd(newItemData, function (item) { if (item) { me.itemsData.getDataSet().add(item); + if (event.type == 'drop') { + me.setSelection([item.id]); + } // TODO: need to trigger a redraw? } });