Browse Source

Items are selectable and editable largely

css_transitions
jos 10 years ago
parent
commit
08d486a53f
8 changed files with 148 additions and 140 deletions
  1. +2
    -133
      src/timeline/Timeline.js
  2. +1
    -1
      src/timeline/component/CurrentTime.js
  3. +1
    -1
      src/timeline/component/CustomTime.js
  4. +137
    -1
      src/timeline/component/ItemSet.js
  5. +1
    -0
      src/timeline/component/css/currenttime.css
  6. +1
    -0
      src/timeline/component/css/customtime.css
  7. +4
    -3
      src/timeline/component/css/itemset.css
  8. +1
    -1
      src/timeline/component/css/panel.css

+ 2
- 133
src/timeline/Timeline.js View File

@ -346,7 +346,6 @@ Timeline.prototype._create = function () {
this.dom.right = document.createElement('div'); this.dom.right = document.createElement('div');
this.dom.top = document.createElement('div'); this.dom.top = document.createElement('div');
this.dom.bottom = document.createElement('div'); this.dom.bottom = document.createElement('div');
this.dom.foregroundVertical = document.createElement('div');
this.dom.background.className = 'vispanel background'; this.dom.background.className = 'vispanel background';
this.dom.backgroundVertical.className = 'vispanel background vertical'; this.dom.backgroundVertical.className = 'vispanel background vertical';
@ -359,7 +358,6 @@ Timeline.prototype._create = function () {
this.dom.left.className = 'content'; this.dom.left.className = 'content';
this.dom.center.className = 'content'; this.dom.center.className = 'content';
this.dom.right.className = 'content'; this.dom.right.className = 'content';
this.dom.foregroundVertical.className = 'vispanel foreground vertical';
this.dom.root.appendChild(this.dom.background); this.dom.root.appendChild(this.dom.background);
this.dom.root.appendChild(this.dom.backgroundVertical); this.dom.root.appendChild(this.dom.backgroundVertical);
@ -369,7 +367,6 @@ Timeline.prototype._create = function () {
this.dom.root.appendChild(this.dom.rightContainer); this.dom.root.appendChild(this.dom.rightContainer);
this.dom.root.appendChild(this.dom.top); this.dom.root.appendChild(this.dom.top);
this.dom.root.appendChild(this.dom.bottom); this.dom.root.appendChild(this.dom.bottom);
this.dom.root.appendChild(this.dom.foregroundVertical);
this.dom.centerContainer.appendChild(this.dom.center); this.dom.centerContainer.appendChild(this.dom.center);
this.dom.leftContainer.appendChild(this.dom.left); this.dom.leftContainer.appendChild(this.dom.left);
@ -389,7 +386,8 @@ Timeline.prototype._create = function () {
var me = this; var me = this;
var events = [ var events = [
'touch', 'pinch', 'tap', 'doubletap', 'hold',
'pinch',
//'tap', 'doubletap', 'hold', // TODO: catching the events here disables selecting an item
'dragstart', 'drag', 'dragend', 'dragstart', 'drag', 'dragend',
'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
]; ];
@ -807,7 +805,6 @@ Timeline.prototype.redraw = function() {
dom.centerContainer.style.height = props.centerContainer.height + 'px'; dom.centerContainer.style.height = props.centerContainer.height + 'px';
dom.leftContainer.style.height = props.leftContainer.height + 'px'; dom.leftContainer.style.height = props.leftContainer.height + 'px';
dom.rightContainer.style.height = props.rightContainer.height + 'px'; dom.rightContainer.style.height = props.rightContainer.height + 'px';
dom.foregroundVertical.style.height = props.background.height + 'px';
dom.background.style.width = props.background.width + 'px'; dom.background.style.width = props.background.width + 'px';
dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; dom.backgroundVertical.style.width = props.centerContainer.width + 'px';
@ -815,7 +812,6 @@ Timeline.prototype.redraw = function() {
dom.centerContainer.style.width = props.center.width + 'px'; dom.centerContainer.style.width = props.center.width + 'px';
dom.top.style.width = props.top.width + 'px'; dom.top.style.width = props.top.width + 'px';
dom.bottom.style.width = props.bottom.width + 'px'; dom.bottom.style.width = props.bottom.width + 'px';
dom.foregroundVertical.style.width = props.centerContainer.width + 'px';
// reposition the panels // reposition the panels
dom.background.style.left = '0'; dom.background.style.left = '0';
@ -834,8 +830,6 @@ Timeline.prototype.redraw = function() {
dom.top.style.top = '0'; dom.top.style.top = '0';
dom.bottom.style.left = props.left.width + 'px'; dom.bottom.style.left = props.left.width + 'px';
dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px'; dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px';
dom.foregroundVertical.style.left = props.left.width + 'px';
dom.foregroundVertical.style.top = '0';
// redraw all components // redraw all components
this.components.forEach(function (component) { this.components.forEach(function (component) {
@ -852,131 +846,6 @@ Timeline.prototype.repaint = function () {
throw new Error('Function repaint is deprecated. Use redraw instead.'); throw new Error('Function repaint is deprecated. Use redraw instead.');
}; };
/**
* Handle selecting/deselecting an item when tapping it
* @param {Event} event
* @private
*/
// TODO: move this function to ItemSet
Timeline.prototype._onSelectItem = function (event) {
if (!this.options.selectable) return;
var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey;
var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey;
if (ctrlKey || shiftKey) {
this._onMultiSelectItem(event);
return;
}
var oldSelection = this.getSelection();
var item = ItemSet.itemFromTarget(event);
var selection = item ? [item.id] : [];
this.setSelection(selection);
var newSelection = this.getSelection();
// emit a select event,
// except when old selection is empty and new selection is still empty
if (newSelection.length > 0 || oldSelection.length > 0) {
this.emit('select', {
items: this.getSelection()
});
}
event.stopPropagation();
};
/**
* Handle creation and updates of an item on double tap
* @param event
* @private
*/
// TODO: move this function to ItemSet
Timeline.prototype._onAddItem = function (event) {
if (!this.options.selectable) return;
if (!this.options.editable.add) 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.contentPanel.frame);
var x = event.gesture.center.pageX - xAbs;
var newItem = {
start: this.timeAxis.snap(this._toTime(x)),
content: 'new item'
};
// when default type is a range, add a default end date to the new item
if (this.options.type === 'range' || this.options.type == 'rangeoverflow') {
newItem.end = this.timeAxis.snap(this._toTime(x + this.rootPanel.width / 5));
}
var id = util.randomUUID();
newItem[this.itemsData.fieldId] = id;
var group = ItemSet.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);
// TODO: need to trigger a redraw?
}
});
}
};
/**
* Handle selecting/deselecting multiple items when holding an item
* @param {Event} event
* @private
*/
// TODO: move this function to ItemSet
Timeline.prototype._onMultiSelectItem = function (event) {
if (!this.options.selectable) return;
var selection,
item = ItemSet.itemFromTarget(event);
if (item) {
// multi select items
selection = this.getSelection(); // current selection
var index = selection.indexOf(item.id);
if (index == -1) {
// item is not yet selected -> select it
selection.push(item.id);
}
else {
// item is already selected -> deselect it
selection.splice(index, 1);
}
this.setSelection(selection);
this.emit('select', {
items: this.getSelection()
});
event.stopPropagation();
}
};
/** /**
* Convert a position on screen (pixels) to a datetime * Convert a position on screen (pixels) to a datetime
* @param {int} x Position on the screen in pixels * @param {int} x Position on the screen in pixels

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

@ -37,7 +37,7 @@ CurrentTime.prototype._create = function _create () {
*/ */
CurrentTime.prototype.redraw = function redraw() { CurrentTime.prototype.redraw = function redraw() {
if (this.options.showCurrentTime) { if (this.options.showCurrentTime) {
var parent = this.timeline.dom.foregroundVertical;
var parent = this.timeline.dom.backgroundVertical;
if (this.bar.parentNode != parent) { if (this.bar.parentNode != parent) {
// attach to the dom // attach to the dom
if (this.bar.parentNode) { if (this.bar.parentNode) {

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

@ -55,7 +55,7 @@ CustomTime.prototype._create = function _create () {
*/ */
CustomTime.prototype.redraw = function () { CustomTime.prototype.redraw = function () {
if (this.options.showCustomTime) { if (this.options.showCustomTime) {
var parent = this.timeline.dom.foregroundVertical;
var parent = this.timeline.dom.backgroundVertical;
if (this.bar.parentNode != parent) { if (this.bar.parentNode != parent) {
// attach to the dom // attach to the dom
if (this.bar.parentNode) { if (this.bar.parentNode) {

+ 137
- 1
src/timeline/component/ItemSet.js View File

@ -111,10 +111,21 @@ ItemSet.prototype._create = function _create(){
this.hammer = Hammer(frame, { this.hammer = Hammer(frame, {
prevent_default: true prevent_default: true
}); });
// drag items when selected
this.hammer.on('dragstart', this._onDragStart.bind(this)); this.hammer.on('dragstart', this._onDragStart.bind(this));
this.hammer.on('drag', this._onDrag.bind(this)); this.hammer.on('drag', this._onDrag.bind(this));
this.hammer.on('dragend', this._onDragEnd.bind(this)); this.hammer.on('dragend', this._onDragEnd.bind(this));
// single select (or unselect) when tapping an item
this.hammer.on('tap', this._onSelectItem.bind(this));
// multi select when holding mouse/touch, or on ctrl+click
this.hammer.on('hold', this._onMultiSelectItem.bind(this));
// add item on doubletap
this.hammer.on('doubletap', this._onAddItem.bind(this));
// attach to the DOM // attach to the DOM
this.show(); this.show();
}; };
@ -187,7 +198,7 @@ ItemSet.prototype.show = function show() {
// show axis with dots // show axis with dots
if (!this.dom.axis.parentNode) { if (!this.dom.axis.parentNode) {
this.timeline.dom.foregroundVertical.appendChild(this.dom.axis);
this.timeline.dom.backgroundVertical.appendChild(this.dom.axis);
} }
// show labelset containing labels // show labelset containing labels
@ -1003,6 +1014,131 @@ ItemSet.prototype._onDragEnd = function (event) {
} }
}; };
/**
* Handle selecting/deselecting an item when tapping it
* @param {Event} event
* @private
*/
ItemSet.prototype._onSelectItem = function (event) {
if (!this.options.selectable) return;
var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey;
var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey;
if (ctrlKey || shiftKey) {
this._onMultiSelectItem(event);
return;
}
var oldSelection = this.getSelection();
var item = ItemSet.itemFromTarget(event);
var selection = item ? [item.id] : [];
this.setSelection(selection);
var newSelection = this.getSelection();
// emit a select event,
// except when old selection is empty and new selection is still empty
if (newSelection.length > 0 || oldSelection.length > 0) {
this.timeline.emitter.emit('select', {
items: this.getSelection()
});
}
event.stopPropagation();
};
/**
* Handle creation and updates of an item on double tap
* @param event
* @private
*/
ItemSet.prototype._onAddItem = function (event) {
if (!this.options.selectable) return;
if (!this.options.editable.add) return;
var me = this,
snap = this.options.snap || null,
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.dom.frame);
var x = event.gesture.center.pageX - xAbs;
var start = this._toTime(x);
var newItem = {
start: snap ? snap(start) : start,
content: 'new item'
};
// when default type is a range, add a default end date to the new item
if (this.options.type === 'range' || this.options.type == 'rangeoverflow') {
var end = this._toTime(x + this.props.width / 5);
newItem.end = snap ? snap(end) : end;
}
var id = util.randomUUID();
newItem[this.itemsData.fieldId] = id;
var group = ItemSet.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);
// TODO: need to trigger a redraw?
}
});
}
};
/**
* Handle selecting/deselecting multiple items when holding an item
* @param {Event} event
* @private
*/
ItemSet.prototype._onMultiSelectItem = function (event) {
if (!this.options.selectable) return;
var selection,
item = ItemSet.itemFromTarget(event);
if (item) {
// multi select items
selection = this.getSelection(); // current selection
var index = selection.indexOf(item.id);
if (index == -1) {
// item is not yet selected -> select it
selection.push(item.id);
}
else {
// item is already selected -> deselect it
selection.splice(index, 1);
}
this.setSelection(selection);
this.timeline.emitter.emit('select', {
items: this.getSelection()
});
event.stopPropagation();
}
};
/** /**
* Find an item from an event target: * Find an item from an event target:
* searches for the attribute 'timeline-item' in the event target's element tree * searches for the attribute 'timeline-item' in the event target's element tree

+ 1
- 0
src/timeline/component/css/currenttime.css View File

@ -1,4 +1,5 @@
.vis.timeline .currenttime { .vis.timeline .currenttime {
background-color: #FF7F6E; background-color: #FF7F6E;
width: 2px; width: 2px;
z-index: 1;
} }

+ 1
- 0
src/timeline/component/css/customtime.css View File

@ -2,4 +2,5 @@
background-color: #6E94FF; background-color: #6E94FF;
width: 2px; width: 2px;
cursor: move; cursor: move;
z-index: 1;
} }

+ 4
- 3
src/timeline/component/css/itemset.css View File

@ -12,14 +12,14 @@
/**/ /**/
} }
.vis.timeline .background,
.vis.timeline .foreground {
.vis.timeline .itemset .background,
.vis.timeline .itemset .foreground {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.vis.timeline .foreground {
.vis.timeline .itemset.foreground {
overflow: hidden; overflow: hidden;
} }
@ -28,6 +28,7 @@
width: 100%; width: 100%;
height: 0; height: 0;
left: 1px; left: 1px;
z-index: 1;
} }
.vis.timeline .group { .vis.timeline .group {

+ 1
- 1
src/timeline/component/css/panel.css View File

@ -44,4 +44,4 @@
.vis.timeline .background { .vis.timeline .background {
overflow: hidden; overflow: hidden;
}
}

Loading…
Cancel
Save