Browse Source

Implemented handlers onAdd, onUpdate, onMove, onRemove

css_transitions
josdejong 10 years ago
parent
commit
e3998b89d9
7 changed files with 208 additions and 48 deletions
  1. +3
    -1
      examples/timeline/06_event_listeners.html
  2. +91
    -0
      examples/timeline/08_validate_changes.html
  3. +1
    -0
      examples/timeline/index.html
  4. +78
    -35
      src/timeline/Timeline.js
  5. +33
    -10
      src/timeline/component/ItemSet.js
  6. +1
    -1
      src/timeline/component/RootPanel.js
  7. +1
    -1
      src/timeline/component/item/ItemRange.js

+ 3
- 1
examples/timeline/06_event_listeners.html View File

@ -34,7 +34,9 @@
]);
var container = document.getElementById('visualization');
var options = {};
var options = {
editable: true
};
var timeline = new vis.Timeline(container, items, options);
timeline.on('rangechange', function (properties) {

+ 91
- 0
examples/timeline/08_validate_changes.html View File

@ -0,0 +1,91 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline | Validate changes</title>
<style type="text/css">
body, html {
font-family: sans-serif;
}
</style>
<script src="../../dist/vis.js"></script>
<link href="../../dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="visualization"></div>
<p></p>
<div id="log"></div>
<script type="text/javascript">
var items = new vis.DataSet();
items.add([
{id: 1, content: 'item 1', start: new Date(2013, 3, 20)},
{id: 2, content: 'item 2', start: new Date(2013, 3, 14)},
{id: 3, content: 'item 3', start: new Date(2013, 3, 18)},
{id: 4, content: 'item 4', start: new Date(2013, 3, 16), end: new Date(2013, 3, 19)},
{id: 5, content: 'item 5', start: new Date(2013, 3, 25)},
{id: 6, content: 'item 6', start: new Date(2013, 3, 27)}
]);
var container = document.getElementById('visualization');
var options = {
editable: true,
onAdd: function (item, callback) {
item.content = prompt('Enter text content for new item:', item.content);
if (item.content != null) {
callback(item); // send back adjusted new item
}
else {
callback(null); // cancel item creation
}
},
onMove: function (item, callback) {
if (confirm('Do you really want to move the item to\n' +
'start: ' + item.start + '\n' +
'end: ' + item.end + '?')) {
callback(item); // send back item as confirmation (can be changed
}
else {
callback(null); // cancel editing item
}
},
onUpdate: function (item, callback) {
item.content = prompt('Edit items text:', item.content);
if (item.content != null) {
callback(item); // send back adjusted item
}
else {
callback(null); // cancel editing item
}
},
onRemove: function (item, callback) {
if (confirm('Remove item ' + item.content + '?')) {
callback(item); // confirm deletion
}
else {
callback(null); // cancel deletion
}
}
};
var timeline = new vis.Timeline(container, items, options);
items.on('*', function (event, properties) {
logEvent(event, properties);
});
function logEvent(event, properties) {
var log = document.getElementById('log');
var msg = document.createElement('div');
msg.innerHTML = 'event=' + JSON.stringify(event) + ', ' +
'properties=' + JSON.stringify(properties);
log.firstChild ? log.insertBefore(msg, log.firstChild) : log.appendChild(msg);
}
</script>
</body>
</html>

+ 1
- 0
examples/timeline/index.html View File

@ -19,6 +19,7 @@
<p><a href="05_groups.html">05_groups.html</a></p>
<p><a href="06_event_listeners.html">06_event_listeners.html</a></p>
<p><a href="07_custom_time_bar.html">07_custom_time_bar.html</a></p>
<p><a href="08_validate_changes.html">08_validate_changes.html</a></p>
</div>
</body>

+ 78
- 35
src/timeline/Timeline.js View File

@ -11,7 +11,7 @@ function Timeline (container, items, options) {
this.options = {
orientation: 'bottom',
autoResize: true,
editable: true,
editable: false,
selectable: true,
snap: null, // will be specified after timeaxis is created
@ -25,7 +25,20 @@ function Timeline (container, items, options) {
showMinorLabels: true,
showMajorLabels: true,
showCurrentTime: false,
showCustomTime: false
showCustomTime: false,
onAdd: function (item, callback) {
callback(item);
},
onUpdate: function (item, callback) {
callback(item);
},
onMoved: function (item, callback) {
callback(item);
},
onRemove: function (item, callback) {
callback(item);
}
};
// controller
@ -57,6 +70,9 @@ function Timeline (container, items, options) {
// multi select when holding mouse/touch, or on ctrl+click
this.controller.on('hold', this._onMultiSelectItem.bind(this));
// add item on doubletap
this.controller.on('doubletap', this._onAddItem.bind(this));
// item panel
var itemOptions = Object.create(this.options);
itemOptions.left = function () {
@ -408,9 +424,7 @@ Timeline.prototype.getSelection = function getSelection() {
*/
// TODO: move this function to ItemSet
Timeline.prototype._onSelectItem = function (event) {
if (!this.options.selectable) {
return;
}
if (!this.options.selectable) return;
var item = ItemSet.itemFromTarget(event);
@ -424,6 +438,63 @@ Timeline.prototype._onSelectItem = function (event) {
event.stopPropagation();
};
/**
* Handle creation and updates of an item on double tap
* @param event
* @private
*/
Timeline.prototype._onAddItem = function (event) {
if (!this.options.selectable) return;
var me = this,
item = ItemSet.itemFromTarget(event);
if (item) {
// update item
// execute async handler to update the item (or cancel it)
var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset
this.options.onUpdate(itemData, function (itemData) {
if (itemData) {
me.itemsData.update(itemData);
}
});
}
else {
// add item
var xAbs = vis.util.getAbsoluteLeft(this.rootPanel.frame);
var x = event.gesture.center.pageX - xAbs;
var newItem = {
start: this.timeaxis.snap(this._toTime(x)),
content: 'new item'
};
var id = util.randomUUID();
newItem[this.itemsData.fieldId] = id;
var group = GroupSet.groupFromTarget(event);
if (group) {
newItem.group = group.groupId;
}
// execute async handler to customize (or cancel) adding an item
this.options.onAdd(newItem, function (item) {
if (item) {
me.itemsData.add(newItem);
// select the created item after it is repainted
me.controller.once('repaint', function () {
me.setSelection([id]);
me.controller.emit('select', {
items: me.getSelection()
});
}.bind(me));
}
});
}
};
/**
* Handle selecting/deselecting multiple items when holding an item
* @param {Event} event
@ -431,9 +502,7 @@ Timeline.prototype._onSelectItem = function (event) {
*/
// TODO: move this function to ItemSet
Timeline.prototype._onMultiSelectItem = function (event) {
if (!this.options.selectable) {
return;
}
if (!this.options.selectable) return;
var selection,
item = ItemSet.itemFromTarget(event);
@ -460,33 +529,7 @@ Timeline.prototype._onMultiSelectItem = function (event) {
}
else {
// create a new item
var xAbs = vis.util.getAbsoluteLeft(this.rootPanel.frame);
var x = event.gesture.center.pageX - xAbs;
var newItem = {
start: this.timeaxis.snap(this._toTime(x)),
content: 'new item'
};
var id = util.randomUUID();
newItem[this.itemsData.fieldId] = id;
var group = GroupSet.groupFromTarget(event);
if (group) {
newItem.group = group.groupId;
}
// TODO: implement an async handler to customize adding an item
this.itemsData.add(newItem);
// select the created item after it is repainted
this.controller.once('repaint', function () {
this.setSelection([id]);
this.controller.emit('select', {
items: this.getSelection()
});
}.bind(this));
this._onAddItem(event);
}
};

+ 33
- 10
src/timeline/component/ItemSet.js View File

@ -580,8 +580,17 @@ ItemSet.prototype.getItems = function getItems() {
* @param {String | Number} id
*/
ItemSet.prototype.removeItem = function removeItem (id) {
var dataset = this._myDataSet();
dataset.remove(id);
var item = this.itemsData.get(id),
dataset = this._myDataSet();
if (item) {
// confirm deletion
this.options.onRemove(item, function (item) {
if (item) {
dataset.remove(item);
}
});
}
};
/**
@ -751,6 +760,8 @@ ItemSet.prototype._onDrag = function (event) {
}
});
// TODO: implement onMoving handler
// TODO: implement dragging from one group to another
this.requestReflow();
@ -767,25 +778,37 @@ ItemSet.prototype._onDrag = function (event) {
ItemSet.prototype._onDragEnd = function (event) {
if (this.touchParams.itemProps) {
// prepare a change set for the changed items
var changes = [];
var changes = [],
me = this;
this.touchParams.itemProps.forEach(function (props) {
var change = {
id: props.item.id
};
var id = props.item.id,
item = me.itemsData.get(id);
var changed = false;
if ('start' in props.item.data) {
changed = (props.start != props.item.data.start.valueOf());
change.start = props.item.data.start;
item.start = util.convert(props.item.data.start, me.itemsData.convert['start']);
}
if ('end' in props.item.data) {
changed = changed || (props.end != props.item.data.end.valueOf());
change.end = props.item.data.end;
item.end = util.convert(props.item.data.end, me.itemsData.convert['end']);
}
// only add changes when start or end is actually changed
// only apply changes when start or end is actually changed
if (changed) {
changes.push(change);
me.options.onMove(item, function (item) {
if (item) {
// apply changes
changes.push(item);
}
else {
// restore original values
if ('start' in props) props.item.data.start = props.start;
if ('end' in props) props.item.data.end = props.end;
me.requestReflow();
}
});
}
});
this.touchParams.itemProps = null;

+ 1
- 1
src/timeline/component/RootPanel.js View File

@ -17,7 +17,7 @@ function RootPanel(container, options) {
// create listeners for all interesting events, these events will be emitted
// via the controller
var events = [
'touch', 'pinch', 'tap', 'hold',
'touch', 'pinch', 'tap', 'doubletap', 'hold',
'dragstart', 'drag', 'dragend',
'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is for Firefox
];

+ 1
- 1
src/timeline/component/item/ItemRange.js View File

@ -72,7 +72,7 @@ ItemRange.prototype.repaint = function repaint() {
this._repaintDragRight();
// update class
var className = (this.data.className? ' ' + this.data.className : '') +
var className = (this.data.className ? (' ' + this.data.className) : '') +
(this.selected ? ' selected' : '');
if (this.className != className) {
this.className = className;

Loading…
Cancel
Save