diff --git a/src/timeline/Timeline.js b/src/timeline/Timeline.js index 4fc29cab..16ae1181 100644 --- a/src/timeline/Timeline.js +++ b/src/timeline/Timeline.js @@ -245,7 +245,7 @@ function Timeline (container, items, options) { width: null, height: null }); - this.itemSet = new ItemSet(this.backgroundPanel, this.axisPanel, itemOptions); + this.itemSet = new ItemSet(this.backgroundPanel, this.axisPanel, this.sideContentPanel, itemOptions); this.itemSet.setRange(this.range); this.itemSet.on('change', me.rootPanel.repaint.bind(me.rootPanel)); this.contentPanel.appendChild(this.itemSet); diff --git a/src/timeline/component/Group.js b/src/timeline/component/Group.js index b7fc6d2d..e2d75c87 100644 --- a/src/timeline/component/Group.js +++ b/src/timeline/component/Group.js @@ -1,11 +1,14 @@ /** * @constructor Group * @param {Number | String} groupId + * @param {ItemSet} itemSet */ -function Group (groupId) { +function Group (groupId, itemSet) { this.groupId = groupId; - this.dom = {}; + this.itemSet = itemSet; + + this.dom = {}; this.items = {}; // items filtered by groupId of this group this._create(); @@ -25,15 +28,68 @@ Group.prototype._create = function() { label.appendChild(inner); this.dom.inner = inner; - this.dom.group = document.createElement('div'); + var group = document.createElement('div'); + group.className = 'group'; + group['timeline-group'] = this; + this.dom.group = group; +}; + +/** + * Get the foreground container element + * @return {HTMLElement} foreground + */ +Group.prototype.getForeground = function getForeground() { + return this.dom.group; }; /** - * Repaint the group - * @return {boolean} Returns true if the component is resized + * Get the background container element + * @return {HTMLElement} background */ -Group.prototype.repaint = function repaint() { - // TODO: implement Group.repaint +Group.prototype.getBackground = function getBackground() { + return this.itemSet.getBackground(); +}; + +/** + * Get the axis container element + * @return {HTMLElement} axis + */ +Group.prototype.getAxis = function getAxis() { + return this.itemSet.getAxis(); +}; + +/** + * Get the height of the itemsets background + * @return {Number} height + */ +Group.prototype.getBackgroundHeight = function getBackgroundHeight() { + return this.itemSet.height; +}; + +/** + * Show this group: attach to the DOM + */ +Group.prototype.show = function show() { + if (!this.dom.label.parentNode) { + this.itemSet.getLabelSet().appendChild(this.dom.label); + } + + if (!this.dom.group.parentNode) { + this.itemSet.getForeground().appendChild(this.dom.group); + } +}; + +/** + * Hide this group: remove from the DOM + */ +Group.prototype.hide = function hide() { + if (this.dom.label.parentNode) { + this.dom.label.parentNode.removeChild(this.dom.label); + } + + if (this.dom.group.parentNode) { + this.dom.group.parentNode.removeChild(this.dom.group); + } }; /** @@ -42,6 +98,7 @@ Group.prototype.repaint = function repaint() { */ Group.prototype.add = function add(item) { this.items[item.id] = item; + item.setParent(this); }; /** @@ -50,5 +107,6 @@ Group.prototype.add = function add(item) { */ Group.prototype.remove = function remove(item) { delete this.items[item.id]; + item.setParent(this.itemSet); }; diff --git a/src/timeline/component/ItemSet.js b/src/timeline/component/ItemSet.js index d07073c1..842bb0ed 100644 --- a/src/timeline/component/ItemSet.js +++ b/src/timeline/component/ItemSet.js @@ -6,17 +6,19 @@ * vertical lines of box items. * @param {Panel} axisPanel Panel on the axis where the dots of box-items * can be displayed. + * @param {Panel} labelPanel Left side panel holding labels * @param {Object} [options] See ItemSet.setOptions for the available options. * @constructor ItemSet * @extends Panel */ -function ItemSet(backgroundPanel, axisPanel, options) { +function ItemSet(backgroundPanel, axisPanel, labelPanel, options) { this.id = util.randomUUID(); // one options object is shared by this itemset and all its items this.options = options || {}; this.backgroundPanel = backgroundPanel; this.axisPanel = axisPanel; + this.labelPanel = labelPanel; this.itemOptions = Object.create(this.options); this.dom = {}; this.props = { @@ -377,7 +379,9 @@ ItemSet.prototype.repaint = function repaint() { asString = util.option.asString, options = this.options, orientation = this.getOption('orientation'), - frame = this.frame; + frame = this.frame, + initialPosByStart, + i, ii; // update className frame.className = 'itemset' + (options.className ? (' ' + asString(options.className)) : ''); @@ -395,35 +399,39 @@ ItemSet.prototype.repaint = function repaint() { // first check if the items that were in view previously are still in view. // this handles the case for the ItemRange that is both before and after the current one. if (this.visibleItems.length > 0) { - for (var i = 0; i < this.visibleItems.length; i++) { + for (i = 0; i < this.visibleItems.length; i++) { this._checkIfVisible(this.visibleItems[i], newVisibleItems); } } this.visibleItems = newVisibleItems; // If there were no visible items previously, use binarySearch to find a visible ItemPoint or ItemRange (based on startTime) - if (this.visibleItems.length == 0) {var initialPosByStart = this._binarySearch(false);} - else {var initialPosByStart = orderedItems.byStart.indexOf(this.visibleItems[0]);} + if (this.visibleItems.length == 0) { + initialPosByStart = this._binarySearch(false); + } + else { + initialPosByStart = orderedItems.byStart.indexOf(this.visibleItems[0]); + } // use visible search to find a visible ItemRange (only based on endTime) var initialPosByEnd = this._binarySearch(true); // if we found a initial ID to use, trace it up and down until we meet an invisible item. if (initialPosByStart != -1) { - for (var i = initialPosByStart; i >= 0; i--) { + for (i = initialPosByStart; i >= 0; i--) { if (this._checkIfInvisible(orderedItems.byStart[i])) {break;} } - for (var i = initialPosByStart + 1; i < orderedItems.byStart.length; i++) { + for (i = initialPosByStart + 1; i < orderedItems.byStart.length; i++) { if (this._checkIfInvisible(orderedItems.byStart[i])) {break;} } } // if we found a initial ID to use, trace it up and down until we meet an invisible item. if (initialPosByEnd != -1) { - for (var i = initialPosByEnd; i >= 0; i--) { + for (i = initialPosByEnd; i >= 0; i--) { if (this._checkIfInvisible(orderedItems.byEnd[i])) {break;} } - for (var i = initialPosByEnd + 1; i < orderedItems.byEnd.length; i++) { + for (i = initialPosByEnd + 1; i < orderedItems.byEnd.length; i++) { if (this._checkIfInvisible(orderedItems.byEnd[i])) {break;} } } @@ -433,7 +441,7 @@ ItemSet.prototype.repaint = function repaint() { var force = this.stackDirty || zoomed; // force re-stacking of all items if true this.stack.stack(this.visibleItems, force); this.stackDirty = false; - for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { + for (i = 0, ii = this.visibleItems.length; i < ii; i++) { this.visibleItems[i].repositionY(); } @@ -507,6 +515,14 @@ ItemSet.prototype.getAxis = function getAxis() { return this.dom.axis; }; +/** + * Get the element for the labelset + * @return {HTMLElement} labelSet + */ +ItemSet.prototype.getLabelSet = function getLabelSet() { + return this.labelPanel.frame; +}; + /** * Set items * @param {vis.DataSet | null} items @@ -596,7 +612,7 @@ ItemSet.prototype.setGroups = function setGroups(groups) { me.groupsData.on(event, callback, id); }); - // draw all new groups + // draw all ms ids = this.groupsData.getIds(); this._onAddGroups(ids); } @@ -742,7 +758,7 @@ ItemSet.prototype._onAddGroups = function _onAddGroups(ids) { height: null }); - group = new Group(id); + group = new Group(id, me, me.dom.background, me.dom.axis, me.labelPanel.frame); me.groups[id] = group; // add items with this groupId to the new group @@ -754,6 +770,8 @@ ItemSet.prototype._onAddGroups = function _onAddGroups(ids) { } } } + + group.show(); } }); @@ -771,10 +789,7 @@ ItemSet.prototype._onRemoveGroups = function _onRemoveGroups(ids) { var group = groups[id]; if (group) { - /* TODO - group.setItems(); // detach items data - group.hide(); // FIXME: for some reason when doing setItems after hide, setItems again makes the label visible - */ + group.hide(); delete groups[id]; } }); @@ -906,6 +921,14 @@ ItemSet.prototype.getLabelsWidth = function getLabelsWidth() { return this.props.labels.width; }; +/** + * Get the height of the itemsets background + * @return {Number} height + */ +ItemSet.prototype.getBackgroundHeight = function getBackgroundHeight() { + return this.height; +}; + /** * Start dragging the selected events * @param {Event} event @@ -1073,7 +1096,13 @@ ItemSet.itemFromTarget = function itemFromTarget (event) { * @return {Group | null} group */ ItemSet.groupFromTarget = function groupFromTarget (event) { - // TODO: implement groupFromTarget + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-group')) { + return target['timeline-group']; + } + target = target.parentNode; + } return null; }; diff --git a/src/timeline/component/item/Item.js b/src/timeline/component/item/Item.js index b0d4b074..f8a46828 100644 --- a/src/timeline/component/item/Item.js +++ b/src/timeline/component/item/Item.js @@ -1,6 +1,6 @@ /** * @constructor Item - * @param {ItemSet} parent + * @param {ItemSet | Group} parent * @param {Object} data Object containing (optional) parameters type, * start, end, content, group, className. * @param {Object} [options] Options to set initial property values @@ -41,6 +41,23 @@ Item.prototype.unselect = function unselect() { if (this.displayed) this.repaint(); }; +/** + * Set a parent for the item + * @param {ItemSet | Group} parent + */ +Item.prototype.setParent = function setParent(parent) { + if (this.displayed) { + this.hide(); + this.parent = parent; + if (this.parent) { + this.show(); + } + } + else { + this.parent = parent; + } +}; + /** * Show the Item in the DOM (when not already visible) * @return {Boolean} changed diff --git a/src/timeline/component/item/ItemBox.js b/src/timeline/component/item/ItemBox.js index 05aa8ffb..df8d6fed 100644 --- a/src/timeline/component/item/ItemBox.js +++ b/src/timeline/component/item/ItemBox.js @@ -216,13 +216,13 @@ ItemBox.prototype.repositionY = function repositionY () { line.style.top = '0'; line.style.bottom = ''; - line.style.height = (this.parent.top + this.top + 1) + 'px'; + line.style.height = (this.top + 1) + 'px'; } else { // orientation 'bottom' box.style.top = ''; box.style.bottom = (this.top || 0) + 'px'; - line.style.top = (this.parent.top + this.parent.height - this.top - 1) + 'px'; + line.style.top = (this.parent.getBackgroundHeight() - this.top - 1) + 'px'; line.style.bottom = '0'; line.style.height = ''; }