diff --git a/examples/timeline/other/localization.html b/examples/timeline/other/localization.html index dfa413e0bb..d4d71b39d7 100644 --- a/examples/timeline/other/localization.html +++ b/examples/timeline/other/localization.html @@ -37,8 +37,8 @@ // Create a DataSet (allows two way data-binding) var items = new vis.DataSet([ - {id: 1, content: 'item 1', start: new Date(new Date().valueOf() - DAY)}, - {id: 2, content: 'item 2', start: new Date(new Date().valueOf() + 2 * DAY)} + {id: 1, content: 'item 1', start: new Date(Date.now() - DAY)}, + {id: 2, content: 'item 2', start: new Date(Date.now() + 2 * DAY)} ]); // Configuration for the Timeline @@ -50,7 +50,7 @@ var timeline = new vis.Timeline(container, items, options); timeline.addCustomTime(new Date()); - timeline.setCustomTime(new Date(new Date().valueOf() + DAY)); + timeline.setCustomTime(new Date(Date.now() + DAY)); // update the locale when changing the select box value var select = document.getElementById('locale'); diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js index 8ca161a6c3..5fdf52a96f 100644 --- a/lib/timeline/Core.js +++ b/lib/timeline/Core.js @@ -897,12 +897,13 @@ class Core { */ _redraw() { this.redrawCount++; + const dom = this.dom; + + if (!dom || !dom.container || dom.root.offsetWidth == 0) return; // when destroyed, or invisible + let resized = false; const options = this.options; const props = this.props; - const dom = this.dom; - - if (!dom || !dom.container || dom.root.offsetWidth == 0) return; // when destroyed, or invisible DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); @@ -929,21 +930,26 @@ class Core { dom.root.style.minHeight = util.option.asSize(options.minHeight, ''); dom.root.style.width = util.option.asSize(options.width, ''); + const rootClientHeight = dom.root.clientHeight; + const rootOffsetHeight = dom.root.offsetHeight; + const rootOffsetWidth = dom.root.offsetWidth; + const centerContainerClientHeight = dom.centerContainer.clientHeight; + // calculate border widths props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2; props.border.right = props.border.left; - props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2; + props.border.top = (dom.centerContainer.offsetHeight - centerContainerClientHeight) / 2; props.border.bottom = props.border.top; - props.borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight; - props.borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; + props.borderRootHeight = rootOffsetHeight - rootClientHeight; + props.borderRootWidth = rootOffsetWidth - dom.root.clientWidth; // workaround for a bug in IE: the clientWidth of an element with // a height:0px and overflow:hidden is not calculated and always has value 0 - if (dom.centerContainer.clientHeight === 0) { + if (centerContainerClientHeight === 0) { props.border.left = props.border.top; props.border.right = props.border.left; } - if (dom.root.clientHeight === 0) { + if (rootClientHeight === 0) { props.borderRootWidth = props.borderRootHeight; } @@ -974,24 +980,27 @@ class Core { props.rightContainer.height = props.leftContainer.height; // calculate the widths of the panels - props.root.width = dom.root.offsetWidth; + props.root.width = rootOffsetWidth; props.background.width = props.root.width - props.borderRootWidth; if (!this.initialDrawDone) { props.scrollbarWidth = util.getScrollBarWidth(); } + const leftContainerClientWidth = dom.leftContainer.clientWidth; + const rightContainerClientWidth = dom.rightContainer.clientWidth; + if (options.verticalScroll) { if (options.rtl) { - props.left.width = dom.leftContainer.clientWidth || -props.border.left; - props.right.width = dom.rightContainer.clientWidth + props.scrollbarWidth || -props.border.right; + props.left.width = leftContainerClientWidth || -props.border.left; + props.right.width = rightContainerClientWidth + props.scrollbarWidth || -props.border.right; } else { - props.left.width = dom.leftContainer.clientWidth + props.scrollbarWidth || -props.border.left; - props.right.width = dom.rightContainer.clientWidth || -props.border.right; + props.left.width = leftContainerClientWidth + props.scrollbarWidth || -props.border.left; + props.right.width = rightContainerClientWidth || -props.border.right; } } else { - props.left.width = dom.leftContainer.clientWidth || -props.border.left; - props.right.width = dom.rightContainer.clientWidth || -props.border.right; + props.left.width = leftContainerClientWidth || -props.border.left; + props.right.width = rightContainerClientWidth || -props.border.right; } this._setDOM(); @@ -1005,7 +1014,7 @@ class Core { offset += Math.max(props.centerContainer.height - props.center.height - props.border.top - props.border.bottom, 0); } - dom.center.style.top = `${offset}px`; + dom.center.style.transform = `translateY(${offset}px)`; // show shadows when vertical scrolling is available const visibilityTop = props.scrollTop == 0 ? 'hidden' : ''; @@ -1035,8 +1044,8 @@ class Core { dom.right.style.top = `${offset}px`; dom.rightContainer.className = dom.rightContainer.className.replace(new RegExp('(?:^|\\s)'+ 'vis-vertical-scroll' + '(?:\\s|$)'), ' '); dom.leftContainer.className = dom.leftContainer.className.replace(new RegExp('(?:^|\\s)'+ 'vis-vertical-scroll' + '(?:\\s|$)'), ' '); - props.left.width = dom.leftContainer.clientWidth || -props.border.left; - props.right.width = dom.rightContainer.clientWidth || -props.border.right; + props.left.width = leftContainerClientWidth || -props.border.left; + props.right.width = rightContainerClientWidth || -props.border.right; this._setDOM(); } @@ -1229,14 +1238,16 @@ class Core { } if (me.dom.root) { + const rootOffsetHeight = me.dom.root.offsetHeight; + const rootOffsetWidth = me.dom.root.offsetWidth; // check whether the frame is resized // Note: we compare offsetWidth here, not clientWidth. For some reason, // IE does not restore the clientWidth from 0 to the actual width after // changing the timeline's container display style from none to visible - if ((me.dom.root.offsetWidth != me.props.lastWidth) || - (me.dom.root.offsetHeight != me.props.lastHeight)) { - me.props.lastWidth = me.dom.root.offsetWidth; - me.props.lastHeight = me.dom.root.offsetHeight; + if ((rootOffsetWidth != me.props.lastWidth) || + (rootOffsetHeight != me.props.lastHeight)) { + me.props.lastWidth = rootOffsetWidth; + me.props.lastHeight = rootOffsetHeight; me.props.scrollbarWidth = util.getScrollBarWidth(); me.body.emitter.emit('_change'); diff --git a/lib/timeline/Range.js b/lib/timeline/Range.js index 712dfb137d..ac3fd076a5 100644 --- a/lib/timeline/Range.js +++ b/lib/timeline/Range.js @@ -210,12 +210,12 @@ export default class Range extends Component { throw new Error(`Unknown easing function ${JSON.stringify(easingName)}. Choose from: ${Object.keys(util.easingFunctions).join(', ')}`); } - const initTime = new Date().valueOf(); + const initTime = Date.now(); let anyChanged = false; const next = () => { if (!me.props.touch.dragging) { - const now = new Date().valueOf(); + const now = Date.now(); const time = now - initTime; const ease = easingFunction(time / duration); const done = time > duration; @@ -745,12 +745,8 @@ export default class Range extends Component { // calculate the time where the mouse is, check whether inside // and no scroll action should happen. const clientX = event.center ? event.center.x : event.clientX; - let x; - if (this.options.rtl) { - x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer); - } else { - x = util.getAbsoluteRight(this.body.dom.centerContainer) - clientX; - } + const centerContainerRect = this.body.dom.centerContainer.getBoundingClientRect(); + const x = this.options.rtl ? clientX - centerContainerRect.left : centerContainerRect.right - clientX; const time = this.body.util.toTime(x); return time >= this.start && time <= this.end; @@ -786,15 +782,16 @@ export default class Range extends Component { * @private */ getPointer(touch, element) { + const elementRect = element.getBoundingClientRect(); if (this.options.rtl) { return { - x: util.getAbsoluteRight(element) - touch.x, - y: touch.y - util.getAbsoluteTop(element) + x: elementRect.right - touch.x, + y: touch.y - elementRect.top }; } else { return { - x: touch.x - util.getAbsoluteLeft(element), - y: touch.y - util.getAbsoluteTop(element) + x: touch.x - elementRect.left, + y: touch.y - elementRect.top }; } } diff --git a/lib/timeline/TimeStep.js b/lib/timeline/TimeStep.js index ab91d198f6..44ee772bb0 100644 --- a/lib/timeline/TimeStep.js +++ b/lib/timeline/TimeStep.js @@ -107,8 +107,8 @@ class TimeStep { throw "No legal start or end date in method setRange"; } - this._start = (start != undefined) ? this.moment(start.valueOf()) : new Date(); - this._end = (end != undefined) ? this.moment(end.valueOf()) : new Date(); + this._start = (start != undefined) ? this.moment(start.valueOf()) : Date.now(); + this._end = (end != undefined) ? this.moment(end.valueOf()) : Date.now(); if (this.autoScale) { this.setMinimumStep(minimumStep); @@ -619,7 +619,7 @@ class TimeStep { * @returns {String} */ function today(date) { - if (date.isSame(new Date(), 'day')) { + if (date.isSame(Date.now(), 'day')) { return ' vis-today'; } if (date.isSame(_moment().add(1, 'day'), 'day')) { @@ -637,7 +637,7 @@ class TimeStep { * @returns {String} */ function currentWeek(date) { - return date.isSame(new Date(), 'week') ? ' vis-current-week' : ''; + return date.isSame(Date.now(), 'week') ? ' vis-current-week' : ''; } /** @@ -646,7 +646,7 @@ class TimeStep { * @returns {String} */ function currentMonth(date) { - return date.isSame(new Date(), 'month') ? ' vis-current-month' : ''; + return date.isSame(Date.now(), 'month') ? ' vis-current-month' : ''; } /** @@ -655,7 +655,7 @@ class TimeStep { * @returns {String} */ function currentYear(date) { - return date.isSame(new Date(), 'year') ? ' vis-current-year' : ''; + return date.isSame(Date.now(), 'year') ? ' vis-current-year' : ''; } switch (this.scale) { diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js index c17c3e668d..c170655444 100644 --- a/lib/timeline/Timeline.js +++ b/lib/timeline/Timeline.js @@ -670,14 +670,10 @@ export default class Timeline extends Core { getEventProperties(event) { const clientX = event.center ? event.center.x : event.clientX; const clientY = event.center ? event.center.y : event.clientY; - let x; - if (this.options.rtl) { - x = util.getAbsoluteRight(this.dom.centerContainer) - clientX; - } else { - x = clientX - util.getAbsoluteLeft(this.dom.centerContainer); - } - const y = clientY - util.getAbsoluteTop(this.dom.centerContainer); - + const centerContainerRect = this.dom.centerContainer.getBoundingClientRect(); + const x = this.options.rtl ? centerContainerRect.right - clientX : clientX - centerContainerRect.left; + const y = clientY - centerContainerRect.top; + const item = this.itemSet.itemFromTarget(event); const group = this.itemSet.groupFromTarget(event); const customTime = CustomTime.customTimeFromTarget(event); diff --git a/lib/timeline/component/CurrentTime.js b/lib/timeline/component/CurrentTime.js index 2ec6df6a58..4520062e25 100644 --- a/lib/timeline/component/CurrentTime.js +++ b/lib/timeline/component/CurrentTime.js @@ -100,7 +100,7 @@ class CurrentTime extends Component { this.start(); } - let now = this.options.moment(new Date().valueOf() + this.offset); + let now = this.options.moment(Date.now() + this.offset); if (this.options.alignCurrentTime) { now = now.startOf(this.options.alignCurrentTime); @@ -120,9 +120,9 @@ class CurrentTime extends Component { title = title.charAt(0).toUpperCase() + title.substring(1); if (this.options.rtl) { - this.bar.style.right = `${x}px`; + this.bar.style.transform = `translateX(${x * -1}px)`; } else { - this.bar.style.left = `${x}px`; + this.bar.style.transform = `translateX(${x}px)`; } this.bar.title = title; } @@ -183,7 +183,7 @@ class CurrentTime extends Component { */ setCurrentTime(time) { const t = util.convert(time, 'Date').valueOf(); - const now = new Date().valueOf(); + const now = Date.now(); this.offset = t - now; this.redraw(); } @@ -193,7 +193,7 @@ class CurrentTime extends Component { * @return {Date} Returns the current time. */ getCurrentTime() { - return new Date(new Date().valueOf() + this.offset); + return new Date(Date.now() + this.offset); } } diff --git a/lib/timeline/component/CustomTime.js b/lib/timeline/component/CustomTime.js index a316120d99..a44e26f4f5 100644 --- a/lib/timeline/component/CustomTime.js +++ b/lib/timeline/component/CustomTime.js @@ -83,7 +83,11 @@ class CustomTime extends Component { const drag = document.createElement('div'); drag.style.position = 'relative'; drag.style.top = '0px'; - this.options.rtl ? drag.style.right = '-10px' : drag.style.left = '-10px'; + if(this.options.rtl) { + drag.style.right = '-10px'; + } else { + drag.style.left = '-10px'; + } drag.style.height = '100%'; drag.style.width = '20px'; diff --git a/lib/timeline/component/DataAxis.js b/lib/timeline/component/DataAxis.js index c2cc3dcb0a..1befd02a58 100644 --- a/lib/timeline/component/DataAxis.js +++ b/lib/timeline/component/DataAxis.js @@ -322,13 +322,15 @@ class DataAxis extends Component { const showMinorLabels = this.options.showMinorLabels; const showMajorLabels = this.options.showMajorLabels; + const backgroundHorizontalOffsetWidth = this.body.dom.backgroundHorizontal.offsetWidth; + // determine the width and height of the elements for the axis props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; + props.minorLineWidth = backgroundHorizontalOffsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; props.minorLineHeight = 1; - props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; + props.majorLineWidth = backgroundHorizontalOffsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; props.majorLineHeight = 1; // take frame offline while updating (is almost twice as fast) diff --git a/lib/timeline/component/Group.js b/lib/timeline/component/Group.js index a077b7c798..216f2f13e5 100644 --- a/lib/timeline/component/Group.js +++ b/lib/timeline/component/Group.js @@ -297,9 +297,7 @@ class Group { * @param {number} pixels */ _calculateGroupSizeAndPosition() { - const offsetTop = this.dom.foreground.offsetTop; - const offsetLeft = this.dom.foreground.offsetLeft; - const offsetWidth = this.dom.foreground.offsetWidth; + const { offsetTop, offsetLeft, offsetWidth } = this.dom.foreground; this.top = offsetTop; this.right = offsetLeft; this.width = offsetWidth; @@ -321,7 +319,7 @@ class Group { let bail = null; if (!this.itemSet.initialDrawDone) { if (bailOptions.shouldBailStackItems) { return true; } - if (Math.abs(new Date() - new Date(bailOptions.relativeBailingTime)) > bailOptions.bailTimeMs) { + if (Math.abs(Date.now() - new Date(bailOptions.relativeBailingTime)) > bailOptions.bailTimeMs) { if (bailOptions.userBailFunction && this.itemSet.userContinueNotBail == null) { bailOptions.userBailFunction(didUserContinue => { me.itemSet.userContinueNotBail = didUserContinue; diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js index 8a9046e073..578b75d861 100644 --- a/lib/timeline/component/ItemSet.js +++ b/lib/timeline/component/ItemSet.js @@ -1587,17 +1587,11 @@ class ItemSet extends Component { * @private */ _onDragStartAddItem(event) { - let xAbs; - let x; const snap = this.options.snap || null; + const frameRect = this.dom.frame.getBoundingClientRect() - if (this.options.rtl) { - xAbs = util.getAbsoluteRight(this.dom.frame); - x = xAbs - event.center.x + 10; // plus 10 to compensate for the drag starting as soon as you've moved 10px - } else { - xAbs = util.getAbsoluteLeft(this.dom.frame); - x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px - } + // plus (if rtl) 10 to compensate for the drag starting as soon as you've moved 10px + const x = this.options.rtl ? frameRect.right - event.center.x + 10 : event.center.x - frameRect.left - 10; const time = this.body.util.toTime(x); const scale = this.body.util.getScale(); @@ -1647,10 +1641,15 @@ class ItemSet extends Component { * @private */ _onDrag(event) { - // deactivate tooltip window - this.clearPopupTimer(); - if (this.popup != null) { - this.popup.hide(); + if (this.popup != null && this.options.showTooltips && !this.popup.hidden) { + // this.popup.hide(); + const container = this.body.dom.centerContainer; + const containerRect = container.getBoundingClientRect() + this.popup.setPosition( + event.center.x - containerRect.left + container.offsetLeft, + event.center.y - containerRect.top + container.offsetTop + ); + this.popup.show(); // redraw } if (this.touchParams.itemProps) { @@ -1658,14 +1657,8 @@ class ItemSet extends Component { const me = this; const snap = this.options.snap || null; - let xOffset; - - if (this.options.rtl) { - xOffset = this.body.dom.root.offsetLeft + this.body.domProps.right.width; - } else { - xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; - } - + const domRootOffsetLeft = this.body.dom.root.offsetLeft; + const xOffset = this.options.rtl ? domRootOffsetLeft + this.body.domProps.right.width : domRootOffsetLeft + this.body.domProps.left.width; const scale = this.body.util.getScale(); const step = this.body.util.getStep(); @@ -1989,17 +1982,17 @@ class ItemSet extends Component { if (group && group.height != this.groupTouchParams.group.height) { const movingUp = (group.top < this.groupTouchParams.group.top); const clientY = event.center ? event.center.y : event.clientY; - const targetGroupTop = util.getAbsoluteTop(group.dom.foreground); + const targetGroup = group.dom.foreground.getBoundingClientRect() const draggedGroupHeight = this.groupTouchParams.group.height; if (movingUp) { // skip swapping the groups when the dragged group is not below clientY afterwards - if (targetGroupTop + draggedGroupHeight < clientY) { + if (targetGroup.top + draggedGroupHeight < clientY) { return; } } else { const targetGroupHeight = group.height; // skip swapping the groups when the dragged group is not below clientY afterwards - if (targetGroupTop + targetGroupHeight - draggedGroupHeight > clientY) { + if (targetGroup.top + targetGroupHeight - draggedGroupHeight > clientY) { return; } } @@ -2206,9 +2199,10 @@ class ItemSet extends Component { this.popup.setText(title); const container = this.body.dom.centerContainer; + const containerRect = container.getBoundingClientRect() this.popup.setPosition( - event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, - event.clientY - util.getAbsoluteTop(container) + container.offsetTop + event.clientX - containerRect.left + container.offsetLeft, + event.clientY - containerRect.top + container.offsetTop ); this.setPopupTimer(this.popup); } else { @@ -2269,17 +2263,14 @@ class ItemSet extends Component { this.setPopupTimer(this.popup); } - if (this.options.showTooltips && this.options.tooltip.followMouse) { - if (this.popup) { - if (!this.popup.hidden) { - const container = this.body.dom.centerContainer; - this.popup.setPosition( - event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, - event.clientY - util.getAbsoluteTop(container) + container.offsetTop - ); - this.popup.show(); // Redraw - } - } + if (this.options.showTooltips && this.options.tooltip.followMouse && this.popup && !this.popup.hidden) { + const container = this.body.dom.centerContainer; + const containerRect = container.getBoundingClientRect() + this.popup.setPosition( + event.clientX - containerRect.left + container.offsetLeft, + event.clientY - containerRect.top + container.offsetTop + ); + this.popup.show(); // Redraw } } @@ -2341,18 +2332,10 @@ class ItemSet extends Component { const me = this; const snap = this.options.snap || null; - let xAbs; - let x; + // add item - if (this.options.rtl) { - xAbs = util.getAbsoluteRight(this.dom.frame); - x = xAbs - event.center.x; - } else { - xAbs = util.getAbsoluteLeft(this.dom.frame); - x = event.center.x - xAbs; - } - // var xAbs = util.getAbsoluteLeft(this.dom.frame); - // var x = event.center.x - xAbs; + const frameRect = this.dom.frame.getBoundingClientRect() + const x = this.options.rtl ? frameRect.right - event.center.x : event.center.x - frameRect.left; const start = this.body.util.toTime(x); const scale = this.body.util.getScale(); const step = this.body.util.getStep(); @@ -2573,18 +2556,18 @@ class ItemSet extends Component { const groupId = groupIds[i]; const group = this.groups[groupId]; const foreground = group.dom.foreground; - const top = util.getAbsoluteTop(foreground); - if (clientY >= top && clientY < top + foreground.offsetHeight) { + const foregroundRect = foreground.getBoundingClientRect() + if (clientY >= foregroundRect.top && clientY < foregroundRect.top + foreground.offsetHeight) { return group; } if (this.options.orientation.item === 'top') { - if (i === this.groupIds.length - 1 && clientY > top) { + if (i === this.groupIds.length - 1 && clientY > foregroundRect.top) { return group; } } else { - if (i === 0 && clientY < top + foreground.offset) { + if (i === 0 && clientY < foregroundRect.top + foreground.offset) { return group; } } diff --git a/lib/timeline/component/TimeAxis.js b/lib/timeline/component/TimeAxis.js index 6a361e8967..b637201e84 100644 --- a/lib/timeline/component/TimeAxis.js +++ b/lib/timeline/component/TimeAxis.js @@ -282,7 +282,7 @@ class TimeAxis extends Component { else { if (line) { // adjust the width of the previous grid - line.style.width = `${parseInt (line.style.width) + width}px`; + line.style.width = `${parseInt(line.style.width) + width}px`; } } } @@ -338,14 +338,10 @@ class TimeAxis extends Component { this.dom.minorTexts.push(label); label.innerHTML = text; - label.style.top = (orientation == 'top') ? (`${this.props.majorLabelHeight}px`) : '0'; - if (this.options.rtl) { - label.style.left = ""; - label.style.right = `${x}px`; - } else { - label.style.left = `${x}px`; - } + let y = (orientation == 'top') ? this.props.majorLabelHeight : 0; + this._setXY(label, x, y); + label.className = `vis-text vis-minor ${className}`; //label.title = title; // TODO: this is a heavy operation @@ -377,28 +373,36 @@ class TimeAxis extends Component { label.className = `vis-text vis-major ${className}`; //label.title = title; // TODO: this is a heavy operation - label.style.top = (orientation == 'top') ? '0' : (`${this.props.minorLabelHeight}px`); - if (this.options.rtl) { - label.style.left = ""; - label.style.right = `${x}px`; - } else { - label.style.left = `${x}px`; - } + let y = (orientation == 'top') ? 0 : this.props.minorLabelHeight; + this._setXY(label, x, y); this.dom.majorTexts.push(label); return label; } /** - * Create a minor line for the axis at position x + * sets xy + * @param {string} label * @param {number} x + * @param {number} y + * @private + */ + _setXY(label, x, y) { + // If rtl is true, inverse x. + const directionX = this.options.rtl ? (x * -1) : x; + label.style.transform = `translate(${directionX}px, ${y}px)`; + } + + /** + * Create a minor line for the axis at position x + * @param {number} left * @param {number} width * @param {string} orientation "top" or "bottom" (default) * @param {string} className * @return {Element} Returns the created line * @private */ - _repaintMinorLine(x, width, orientation, className) { + _repaintMinorLine(left, width, orientation, className) { // reuse redundant line let line = this.dom.redundant.lines.shift(); if (!line) { @@ -409,38 +413,29 @@ class TimeAxis extends Component { this.dom.lines.push(line); const props = this.props; - if (orientation == 'top') { - line.style.top = `${props.majorLabelHeight}px`; - } - else { - line.style.top = `${this.body.domProps.top.height}px`; - } - line.style.height = `${props.minorLineHeight}px`; - if (this.options.rtl) { - line.style.left = ""; - line.style.right = `${x - props.minorLineWidth / 2}px`; - line.className = `vis-grid vis-vertical-rtl vis-minor ${className}`; - } else { - line.style.left = `${x - props.minorLineWidth / 2}px`; - line.className = `vis-grid vis-vertical vis-minor ${className}`; - } + line.style.width = `${width}px`; + line.style.height = `${props.minorLineHeight}px`; + let y = (orientation == 'top') ? props.majorLabelHeight : this.body.domProps.top.height; + let x = left - props.minorLineWidth / 2; + this._setXY(line, x, y); + line.className = `vis-grid ${this.options.rtl ? 'vis-vertical-rtl' : 'vis-vertical'} vis-minor ${className}`; return line; } /** * Create a Major line for the axis at position x - * @param {number} x + * @param {number} left * @param {number} width * @param {string} orientation "top" or "bottom" (default) * @param {string} className * @return {Element} Returns the created line * @private */ - _repaintMajorLine(x, width, orientation, className) { + _repaintMajorLine(left, width, orientation, className) { // reuse redundant line let line = this.dom.redundant.lines.shift(); if (!line) { @@ -451,24 +446,15 @@ class TimeAxis extends Component { this.dom.lines.push(line); const props = this.props; - if (orientation == 'top') { - line.style.top = '0'; - } - else { - line.style.top = `${this.body.domProps.top.height}px`; - } + + line.style.width = `${width}px`; + line.style.height = `${props.majorLineHeight}px`; - if (this.options.rtl) { - line.style.left = ""; - line.style.right = `${x - props.majorLineWidth / 2}px`; - line.className = `vis-grid vis-vertical-rtl vis-major ${className}`; - } else { - line.style.left = `${x - props.majorLineWidth / 2}px`; - line.className = `vis-grid vis-vertical vis-major ${className}`; - } + let y = (orientation == 'top') ? 0 : this.body.domProps.top.height; + let x = left - props.majorLineWidth / 2; - line.style.height = `${props.majorLineHeight}px`; - line.style.width = `${width}px`; + this._setXY(line, x, y); + line.className = `vis-grid ${this.options.rtl ? 'vis-vertical-rtl' : 'vis-vertical'} vis-major ${className}`; return line; } diff --git a/lib/timeline/component/item/BoxItem.js b/lib/timeline/component/item/BoxItem.js index 326ef31b4d..1eb1f64565 100644 --- a/lib/timeline/component/item/BoxItem.js +++ b/lib/timeline/component/item/BoxItem.js @@ -51,14 +51,14 @@ class BoxItem extends Item { const widthInMs = this.width * range.getMillisecondsPerPixel(); if (align == 'right') { - isVisible = (this.data.start.getTime() > range.start ) && (this.data.start.getTime() - widthInMs < range.end); + isVisible = (this.data.start.getTime() > range.start) && (this.data.start.getTime() - widthInMs < range.end); } else if (align == 'left') { - isVisible = (this.data.start.getTime() + widthInMs > range.start ) && (this.data.start.getTime() < range.end); + isVisible = (this.data.start.getTime() + widthInMs > range.start) && (this.data.start.getTime() < range.end); } else { // default or 'center' - isVisible = (this.data.start.getTime() + widthInMs/2 > range.start ) && (this.data.start.getTime() - widthInMs/2 < range.end); + isVisible = (this.data.start.getTime() + widthInMs / 2 > range.start ) && (this.data.start.getTime() - widthInMs/2 < range.end); } return isVisible; } @@ -138,12 +138,12 @@ class BoxItem extends Item { const editable = (this.editable.updateTime || this.editable.updateGroup); // update class - const className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' vis-selected' : '') + - (editable ? ' vis-editable' : ' vis-readonly'); + const className = (this.data.className ? ' ' + this.data.className : '') + + (this.selected ? ' vis-selected' : '') + + (editable ? ' vis-editable' : ' vis-readonly'); this.dom.box.className = `vis-item vis-box${className}`; this.dom.line.className = `vis-item vis-line${className}`; - this.dom.dot.className = `vis-item vis-dot${className}`; + this.dom.dot.className = `vis-item vis-dot${className}`; } } @@ -274,14 +274,44 @@ class BoxItem extends Item { if (this.displayed) { const dom = this.dom; - if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); - if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); - if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); + if (dom.box.parentNode) dom.box.remove(); + if (dom.line.parentNode) dom.line.remove(); + if (dom.dot.parentNode) dom.dot.remove(); this.displayed = false; } } + /** + * Reposition the item XY + */ + repositionXY() { + const rtl = this.options.rtl; + + const repositionXY = (element, x, y, rtl = false) => { + if (x === undefined && y === undefined) return; + // If rtl invert the number. + const directionX = rtl ? (x * -1) : x; + + //no y. translate x + if (y === undefined) { + element.style.transform = `translateX(${directionX}px)`; + return; + } + + //no x. translate y + if (x === undefined) { + element.style.transform = `translateY(${y}px)`; + return; + } + + element.style.transform = `translate(${directionX}px, ${y}px)`; + } + repositionXY(this.dom.box, this.boxX, this.boxY, rtl); + repositionXY(this.dom.dot, this.dotX, this.dotY, rtl); + repositionXY(this.dom.line, this.lineX, this.lineY, rtl); + } + /** * Reposition the item horizontally * @Override @@ -289,60 +319,34 @@ class BoxItem extends Item { repositionX() { const start = this.conversion.toScreen(this.data.start); const align = this.options.align; - - // calculate left position of the box + const lineWidth = this.props.line.width; + const dotWidth = this.props.dot.width; + if (align == 'right') { - if (this.options.rtl) { - this.right = start - this.width; - - // reposition box, line, and dot - this.dom.box.style.right = `${this.right}px`; - this.dom.line.style.right = `${start - this.props.line.width}px`; - this.dom.dot.style.right = `${start - this.props.line.width / 2 - this.props.dot.width / 2}px`; - } else { - this.left = start - this.width; - - // reposition box, line, and dot - this.dom.box.style.left = `${this.left}px`; - this.dom.line.style.left = `${start - this.props.line.width}px`; - this.dom.dot.style.left = `${start - this.props.line.width / 2 - this.props.dot.width / 2}px`; - } + // calculate right position of the box + this.boxX = start - this.width; + this.lineX = start - lineWidth; + this.dotX = start - lineWidth / 2 - dotWidth / 2; } else if (align == 'left') { - if (this.options.rtl) { - this.right = start; - - // reposition box, line, and dot - this.dom.box.style.right = `${this.right}px`; - this.dom.line.style.right = `${start}px`; - this.dom.dot.style.right = `${start + this.props.line.width / 2 - this.props.dot.width / 2}px`; - } else { - this.left = start; - - // reposition box, line, and dot - this.dom.box.style.left = `${this.left}px`; - this.dom.line.style.left = `${start}px`; - this.dom.dot.style.left = `${start + this.props.line.width / 2 - this.props.dot.width / 2}px`; - } + // calculate left position of the box + this.boxX = start; + this.lineX = start; + this.dotX = start + lineWidth / 2 - dotWidth / 2; } else { // default or 'center' - if (this.options.rtl) { - this.right = start - this.width / 2; - - // reposition box, line, and dot - this.dom.box.style.right = `${this.right}px`; - this.dom.line.style.right = `${start - this.props.line.width}px`; - this.dom.dot.style.right = `${start - this.props.dot.width / 2}px`; - } else { - this.left = start - this.width / 2; - - // reposition box, line, and dot - this.dom.box.style.left = `${this.left}px`; - this.dom.line.style.left = `${start - this.props.line.width / 2}px`; - this.dom.dot.style.left = `${start - this.props.dot.width / 2}px`; - } + this.boxX = start - this.width / 2; + this.lineX = this.options.rtl ? start - lineWidth : start - lineWidth / 2; + this.dotX = start - dotWidth / 2; } + + if (this.options.rtl) + this.right = this.boxX; + else + this.left = this.boxX; + + this.repositionXY(); } /** @@ -351,27 +355,29 @@ class BoxItem extends Item { */ repositionY() { const orientation = this.options.orientation.item; - const box = this.dom.box; - const line = this.dom.line; - const dot = this.dom.dot; + const lineStyle = this.dom.line.style; if (orientation == 'top') { - box.style.top = `${this.top || 0}px`; + const lineHeight = this.parent.top + this.top + 1; - line.style.top = '0'; - line.style.height = `${this.parent.top + this.top + 1}px`; - line.style.bottom = ''; + this.boxY = this.top || 0; + lineStyle.height = `${lineHeight}px`; + lineStyle.bottom = ''; + lineStyle.top = '0'; } else { // orientation 'bottom' const itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty const lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; - box.style.top = `${this.parent.height - this.top - this.height || 0}px`; - line.style.top = `${itemSetHeight - lineHeight}px`; - line.style.bottom = '0'; + this.boxY = this.parent.height - this.top - (this.height || 0); + lineStyle.height = `${lineHeight}px`; + lineStyle.top = ''; + lineStyle.bottom = '0'; } - dot.style.top = `${-this.props.dot.height / 2}px`; + this.dotY = -this.props.dot.height / 2; + + this.repositionXY(); } /** diff --git a/lib/timeline/component/item/ClusterItem.js b/lib/timeline/component/item/ClusterItem.js index 6aba5cbd9b..9476873f88 100644 --- a/lib/timeline/component/item/ClusterItem.js +++ b/lib/timeline/component/item/ClusterItem.js @@ -195,13 +195,23 @@ class ClusterItem extends Item { repositionStype(start, end) { this.dom.line.style.display = 'block'; this.dom.dot.style.display = 'block'; + const lineOffsetWidth = this.dom.line.offsetWidth; + const dotOffsetWidth = this.dom.dot.offsetWidth; if (end) { - this.dom.line.style.left = this.dom.line.offsetWidth + start + (end - start) / 2 + 'px'; - this.dom.dot.style.left = this.dom.line.offsetWidth + start + (end - start) / 2 - this.dom.dot.offsetWidth / 2 + 'px' + const lineOffset = lineOffsetWidth + start + (end - start) / 2; + const dotOffset = lineOffset - dotOffsetWidth / 2; + const lineOffsetDirection = this.options.rtl ? lineOffset * -1 : lineOffset; + const dotOffsetDirection = this.options.rtl ? dotOffset * -1 : dotOffset; + + this.dom.line.style.transform = `translateX(${lineOffsetDirection})px`; + this.dom.dot.style.transform = `translateX(${dotOffsetDirection}px)`; } else { - this.dom.line.style.left = `${start}px`; - this.dom.dot.style.left = `${start - this.dom.dot.offsetWidth / 2}px`; + const lineOffsetDirection = this.options.rtl ? start * -1 : start; + const dotOffsetDirection = this.options.rtl ? (start - dotOffsetWidth / 2) * -1 : start - dotOffsetWidth / 2; + + this.dom.line.style.transform = `${lineOffsetDirection}px`; + this.dom.dot.style.transform = `${dotOffsetDirection}px`; } } diff --git a/lib/timeline/component/item/Item.js b/lib/timeline/component/item/Item.js index 878b616767..ae8e2acab5 100644 --- a/lib/timeline/component/item/Item.js +++ b/lib/timeline/component/item/Item.js @@ -324,11 +324,8 @@ class Item { this.dom.onItemUpdateTimeTooltip.style.visibility = this.parent.itemSet.touchParams.itemIsDragging ? 'visible' : 'hidden'; // position relative to item's content - if (this.options.rtl) { - this.dom.onItemUpdateTimeTooltip.style.right = this.dom.content.style.right; - } else { - this.dom.onItemUpdateTimeTooltip.style.left = this.dom.content.style.left; - } + this.dom.onItemUpdateTimeTooltip.style.transform = 'translateX(-50%)'; + this.dom.onItemUpdateTimeTooltip.style.left = '50%'; // position above or below the item depending on the item's position in the window const tooltipOffset = 50; // TODO: should be tooltip height (depends on template) diff --git a/lib/timeline/component/item/PointItem.js b/lib/timeline/component/item/PointItem.js index a0eba0296c..8467166904 100644 --- a/lib/timeline/component/item/PointItem.js +++ b/lib/timeline/component/item/PointItem.js @@ -171,12 +171,10 @@ class PointItem extends Item { // reposition the dot this.dom.dot.style.top = `${(this.height - this.props.dot.height) / 2}px`; - if (this.options.rtl) { - this.dom.dot.style.right = `${this.props.dot.width / 2}px`; - } else { - this.dom.dot.style.left = `${this.props.dot.width / 2}px`; - } - + + const dotWidth = this.props.dot.width; + const translateX = this.options.rtl ? (dotWidth / 2) * -1 : dotWidth / 2; + this.dom.dot.style.transform = `translateX(${translateX}px`; this.dirty = false; } @@ -234,6 +232,35 @@ class PointItem extends Item { } } + + /** + * Reposition XY + */ + repositionXY() { + const rtl = this.options.rtl; + + const repositionXY = (element, x, y, rtl = false) => { + if (x === undefined && y === undefined) return; + // If rtl invert the number. + const directionX = rtl ? (x * -1) : x; + + //no y. translate x + if (y === undefined) { + element.style.transform = `translateX(${directionX}px)`; + return; + } + + //no x. translate y + if (x === undefined) { + element.style.transform = `translateY(${y}px)`; + return; + } + + element.style.transform = `translate(${directionX}px, ${y}px)`; + } + repositionXY(this.dom.point, this.pointX, this.pointY, rtl); + } + /** * Show the item in the DOM (when not already visible). The items DOM will * be created when needed. @@ -266,17 +293,14 @@ class PointItem extends Item { repositionX() { const start = this.conversion.toScreen(this.data.start); + this.pointX = start; if (this.options.rtl) { this.right = start - this.props.dot.width; - - // reposition point - this.dom.point.style.right = `${this.right}px`; } else { this.left = start - this.props.dot.width; - - // reposition point - this.dom.point.style.left = `${this.left}px`; } + + this.repositionXY(); } /** @@ -285,13 +309,14 @@ class PointItem extends Item { */ repositionY() { const orientation = this.options.orientation.item; - const point = this.dom.point; if (orientation == 'top') { - point.style.top = `${this.top}px`; + this.pointY = this.top; } else { - point.style.top = `${this.parent.height - this.top - this.height}px`; + this.pointY = this.parent.height - this.top - this.height } + + this.repositionXY(); } /** diff --git a/lib/timeline/component/item/RangeItem.js b/lib/timeline/component/item/RangeItem.js index a8ec7dd287..bf023f0c36 100644 --- a/lib/timeline/component/item/RangeItem.js +++ b/lib/timeline/component/item/RangeItem.js @@ -300,9 +300,9 @@ class RangeItem extends Item { } if (this.options.rtl) { - this.dom.box.style.right = `${this.right}px`; + this.dom.box.style.transform = `translateX(${this.right * -1}px)`; } else { - this.dom.box.style.left = `${this.left}px`; + this.dom.box.style.transform = `translateX(${this.left}px)`; } this.dom.box.style.width = `${boxWidth}px`; if (this.whiteSpace) { @@ -311,26 +311,24 @@ class RangeItem extends Item { switch (align) { case 'left': - if (this.options.rtl) { - this.dom.content.style.right = '0'; - } else { - this.dom.content.style.left = '0'; - } + this.dom.content.style.transform = 'translateX(0)'; break; case 'right': if (this.options.rtl) { - this.dom.content.style.right = `${Math.max((boxWidth - contentWidth), 0)}px`; + const translateX = Math.max((boxWidth - contentWidth), 0) * -1; + this.dom.content.style.transform = `translateX(${translateX}px)`; } else { - this.dom.content.style.left = `${Math.max((boxWidth - contentWidth), 0)}px`; + this.dom.content.style.transform = `translateX(${Math.max((boxWidth - contentWidth), 0)}px)`; } break; case 'center': if (this.options.rtl) { - this.dom.content.style.right = `${Math.max((boxWidth - contentWidth) / 2, 0)}px`; + const translateX = Math.max((boxWidth - contentWidth) / 2, 0) * -1; + this.dom.content.style.transform = `translateX(${translateX}px)`; } else { - this.dom.content.style.left = `${Math.max((boxWidth - contentWidth) / 2, 0)}px`; + this.dom.content.style.transform = `translateX(${Math.max((boxWidth - contentWidth) / 2, 0)}px)`; } break; @@ -354,9 +352,10 @@ class RangeItem extends Item { } } if (this.options.rtl) { - this.dom.content.style.right = `${contentStartPosition}px`; + const translateX = contentStartPosition * -1; + this.dom.content.style.transform = `translateX(${translateX}px)`; } else { - this.dom.content.style.left = `${contentStartPosition}px`; + this.dom.content.style.transform = `translateX(${contentStartPosition}px)`; // this.dom.content.style.width = `calc(100% - ${contentStartPosition}px)`; } }