diff --git a/docs/timeline/index.html b/docs/timeline/index.html
index c45440ed..7cc75507 100644
--- a/docs/timeline/index.html
+++ b/docs/timeline/index.html
@@ -1024,6 +1024,14 @@ function (option, path) {
+
+ verticalScroll |
+ Boolean |
+ false |
+ Show a vertical scroll on the side of the group list.
+ |
+
+
width |
String or Number |
diff --git a/examples/timeline/other/verticalScroll.html b/examples/timeline/other/verticalScroll.html
new file mode 100644
index 00000000..07a68ad4
--- /dev/null
+++ b/examples/timeline/other/verticalScroll.html
@@ -0,0 +1,92 @@
+
+
+ Timeline | Vertical Scroll Option
+
+
+
+
+
+
+
+
+
+Timeline vertical scroll option
+
+With
+verticalScroll: true,
+zoomKey: 'ctrlKey'
+
+
+
+With
+horizontalScroll: true,
+verticalScroll: true,
+zoomKey: 'ctrlKey'
+
+
+
+
+
+
diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js
index 76ed050b..43f336e2 100644
--- a/lib/timeline/Core.js
+++ b/lib/timeline/Core.js
@@ -82,7 +82,6 @@ Core.prototype._create = function (container) {
this.dom.centerContainer.appendChild(this.dom.center);
this.dom.leftContainer.appendChild(this.dom.left);
this.dom.rightContainer.appendChild(this.dom.right);
-
this.dom.centerContainer.appendChild(this.dom.shadowTop);
this.dom.centerContainer.appendChild(this.dom.shadowBottom);
this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
@@ -90,9 +89,26 @@ Core.prototype._create = function (container) {
this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
+ // size properties of each of the panels
+ this.props = {
+ root: {},
+ background: {},
+ centerContainer: {},
+ leftContainer: {},
+ rightContainer: {},
+ center: {},
+ left: {},
+ right: {},
+ top: {},
+ bottom: {},
+ border: {},
+ scrollTop: 0,
+ scrollTopMin: 0
+ };
+
this.on('rangechange', function () {
if (this.initialDrawDone === true) {
- this._redraw(); // this allows overriding the _redraw method
+ this._redraw();
}
}.bind(this));
this.on('touch', this._onTouch.bind(this));
@@ -154,15 +170,15 @@ Core.prototype._create = function (container) {
}.bind(this));
function onMouseWheel(event) {
- if (me.isActive()) {
- me.emit('mousewheel', event);
+ if (this.isActive()) {
+ this.emit('mousewheel', event);
}
// prevent scrolling when zoomKey defined or activated
- if (!me.options.zoomKey || event[me.options.zoomKey]) return
+ if (!this.options.zoomKey || event[this.options.zoomKey]) return
// prevent scrolling vertically when horizontalScroll is true
- if (me.options.horizontalScroll) return
+ if (this.options.horizontalScroll) return
var delta = 0;
if (event.wheelDelta) { /* IE/Opera. */
@@ -173,12 +189,12 @@ Core.prototype._create = function (container) {
delta = -event.detail / 3;
}
- var current = me.props.scrollTop;
+ var current = this.props.scrollTop;
var adjusted = current + delta * 120;
- if (me.isActive()) {
- me._setScrollTop(adjusted);
- me._redraw();
- me.emit('scroll', event);
+ if (this.isActive()) {
+ this._setScrollTop(adjusted);
+ this._redraw();
+ this.emit('scroll', event);
}
// Prevent default actions caused by mouse wheel
@@ -188,30 +204,27 @@ Core.prototype._create = function (container) {
if (this.dom.root.addEventListener) {
// IE9, Chrome, Safari, Opera
- this.dom.root.addEventListener("mousewheel", onMouseWheel, false);
+ this.dom.root.addEventListener("mousewheel", onMouseWheel.bind(this), false);
// Firefox
- this.dom.root.addEventListener("DOMMouseScroll", onMouseWheel, false);
+ this.dom.root.addEventListener("DOMMouseScroll", onMouseWheel.bind(this), false);
} else {
// IE 6/7/8
- this.dom.root.attachEvent("onmousewheel", onMouseWheel);
+ this.dom.root.attachEvent("onmousewheel", onMouseWheel.bind(this));
}
- // size properties of each of the panels
- this.props = {
- root: {},
- background: {},
- centerContainer: {},
- leftContainer: {},
- rightContainer: {},
- center: {},
- left: {},
- right: {},
- top: {},
- bottom: {},
- border: {},
- scrollTop: 0,
- scrollTopMin: 0
- };
+ function onMouseScrollSide(event) {
+ var current = this.scrollTop;
+ var adjusted = -current;
+ if (me.isActive()) {
+ me._setScrollTop(adjusted);
+
+ me._redraw();
+ me.emit('scroll', event);
+ }
+ }
+
+ this.dom.left.parentNode.addEventListener('scroll', onMouseScrollSide);
+ this.dom.right.parentNode.addEventListener('scroll', onMouseScrollSide);
this.customTimes = [];
@@ -257,17 +270,23 @@ Core.prototype.setOptions = function (options) {
var fields = [
'width', 'height', 'minHeight', 'maxHeight', 'autoResize',
'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates',
- 'locale', 'locales', 'moment', 'rtl', 'zoomKey', 'horizontalScroll'
+ 'locale', 'locales', 'moment', 'rtl', 'zoomKey', 'horizontalScroll', 'verticalScroll'
];
util.selectiveExtend(fields, this.options, options);
if (this.options.rtl) {
- var contentContainer = this.dom.leftContainer;
- this.dom.leftContainer = this.dom.rightContainer;
- this.dom.rightContainer = contentContainer;
this.dom.container.style.direction = "rtl";
- this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl'; }
+ this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl';
+ }
+
+ if (this.options.verticalScroll) {
+ if (this.options.rtl) {
+ this.dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll';
+ } else {
+ this.dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll';
+ }
+ }
this.options.orientation = {item:undefined,axis:undefined};
if ('orientation' in options) {
@@ -740,9 +759,25 @@ Core.prototype._redraw = function() {
// calculate the widths of the panels
props.root.width = dom.root.offsetWidth;
props.background.width = props.root.width - borderRootWidth;
- props.left.width = dom.leftContainer.clientWidth || -props.border.left;
+
+ if (!this.initialDrawDone) {
+ props.scrollbarWidth = util.getScrollBarWidth();
+ }
+
+ if (this.options.verticalScroll) {
+ if (this.options.rtl) {
+ props.left.width = dom.leftContainer.clientWidth || -props.border.left;
+ props.right.width = dom.rightContainer.clientWidth + 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;
+ }
+ } else {
+ props.left.width = dom.leftContainer.clientWidth || -props.border.left;
+ props.right.width = dom.rightContainer.clientWidth || -props.border.right;
+ }
+
props.leftContainer.width = props.left.width;
- props.right.width = dom.rightContainer.clientWidth || -props.border.right;
props.rightContainer.width = props.right.width;
var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth;
props.center.width = centerWidth;
@@ -796,10 +831,8 @@ Core.prototype._redraw = function() {
dom.center.style.left = '0';
dom.center.style.top = offset + 'px';
dom.left.style.left = '0';
- dom.left.style.top = offset + 'px';
dom.right.style.left = '0';
- dom.right.style.top = offset + 'px';
-
+
// show shadows when vertical scrolling is available
var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : '';
var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : '';
@@ -810,6 +843,18 @@ Core.prototype._redraw = function() {
dom.shadowTopRight.style.visibility = visibilityTop;
dom.shadowBottomRight.style.visibility = visibilityBottom;
+ if (this.options.verticalScroll) {
+ this.dom.shadowTopRight.style.visibility = "hidden";
+ this.dom.shadowBottomRight.style.visibility = "hidden";
+ this.dom.shadowTopLeft.style.visibility = "hidden";
+ this.dom.shadowBottomLeft.style.visibility = "hidden";
+ document.getElementsByClassName('vis-left')[0].scrollTop = -offset;
+ document.getElementsByClassName('vis-right')[0].scrollTop = -offset;
+ } else {
+ dom.left.style.top = offset + 'px';
+ dom.right.style.top = offset + 'px';
+ }
+
// enable/disable vertical panning
var contentsOverflow = this.props.center.height > this.props.centerContainer.height;
this.hammer.get('pan').set({
@@ -832,6 +877,7 @@ Core.prototype._redraw = function() {
} else {
this.redrawCount = 0;
}
+
this.initialDrawDone = true;
//Emit public 'changed' event for UI updates, see issue #1592
diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js
index a8e55e92..a397498e 100644
--- a/lib/timeline/Timeline.js
+++ b/lib/timeline/Timeline.js
@@ -52,7 +52,6 @@ function Timeline (container, items, groups, options) {
axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'
item: 'bottom' // not relevant
},
- rtl: false,
moment: moment,
width: null,
@@ -61,6 +60,7 @@ function Timeline (container, items, groups, options) {
minHeight: null
};
this.options = util.deepExtend({}, this.defaultOptions);
+ this.options.rtl = options.rtl;
// Create the DOM, props, and emitter
this._create(container);
diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js
index 77912821..9eb46393 100644
--- a/lib/timeline/component/ItemSet.js
+++ b/lib/timeline/component/ItemSet.js
@@ -27,7 +27,6 @@ var BACKGROUND = '__background__'; // reserved group id for background items wit
function ItemSet(body, options) {
this.body = body;
this.defaultOptions = {
- rtl: false,
type: null, // 'box', 'point', 'range', 'background'
orientation: {
item: 'bottom' // item orientation: 'top' or 'bottom'
@@ -96,7 +95,8 @@ function ItemSet(body, options) {
// options is shared by this ItemSet and all its items
this.options = util.extend({}, this.defaultOptions);
-
+ this.options.rtl = options.rtl;
+
// options for getting items from the DataSet with the correct type
this.itemOptions = {
type: {start: 'Date', end: 'Date'}
@@ -230,7 +230,12 @@ ItemSet.prototype._create = function(){
// add item on doubletap
this.hammer.on('doubletap', this._onAddItem.bind(this));
- this.groupHammer = new Hammer(this.body.dom.leftContainer);
+
+ if (this.options.rtl) {
+ this.groupHammer = new Hammer(this.body.dom.rightContainer);
+ } else {
+ this.groupHammer = new Hammer(this.body.dom.leftContainer);
+ }
this.groupHammer.on('panstart', this._onGroupDragStart.bind(this));
this.groupHammer.on('panmove', this._onGroupDrag.bind(this));
@@ -451,7 +456,11 @@ ItemSet.prototype.show = function() {
// show labelset containing labels
if (!this.dom.labelSet.parentNode) {
- this.body.dom.left.appendChild(this.dom.labelSet);
+ if (this.options.rtl) {
+ this.body.dom.right.appendChild(this.dom.labelSet);
+ } else {
+ this.body.dom.left.appendChild(this.dom.labelSet);
+ }
}
};
diff --git a/lib/timeline/component/css/panel.css b/lib/timeline/component/css/panel.css
index 4c5088a9..a02ee5bc 100644
--- a/lib/timeline/component/css/panel.css
+++ b/lib/timeline/component/css/panel.css
@@ -1,4 +1,3 @@
-
.vis-panel {
position: absolute;
@@ -24,6 +23,28 @@
overflow: hidden;
}
+.vis-left.vis-panel.vis-vertical-scroll, .vis-right.vis-panel.vis-vertical-scroll {
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+
+.vis-left.vis-panel.vis-vertical-scroll {
+ direction: rtl;
+}
+
+.vis-left.vis-panel.vis-vertical-scroll .vis-content {
+ direction: ltr;
+}
+
+.vis-right.vis-panel.vis-vertical-scroll {
+ direction: ltr;
+}
+
+.vis-right.vis-panel.vis-vertical-scroll .vis-content {
+ direction: rtl;
+}
+
.vis-panel.vis-center,
.vis-panel.vis-top,
.vis-panel.vis-bottom {
diff --git a/lib/timeline/optionsTimeline.js b/lib/timeline/optionsTimeline.js
index bfcac84b..4c976d85 100644
--- a/lib/timeline/optionsTimeline.js
+++ b/lib/timeline/optionsTimeline.js
@@ -26,6 +26,7 @@ let allOptions = {
//globals :
align: {string},
rtl: {boolean, 'undefined': 'undefined'},
+ verticalScroll: {boolean, 'undefined': 'undefined'},
horizontalScroll: {boolean, 'undefined': 'undefined'},
autoResize: {boolean},
clickToUse: {boolean},
diff --git a/lib/util.js b/lib/util.js
index c1dc1c21..f9506a6a 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -1452,3 +1452,29 @@ exports.easingFunctions = {
return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t
}
};
+
+exports.getScrollBarWidth = function () {
+ var inner = document.createElement('p');
+ inner.style.width = "100%";
+ inner.style.height = "200px";
+
+ var outer = document.createElement('div');
+ outer.style.position = "absolute";
+ outer.style.top = "0px";
+ outer.style.left = "0px";
+ outer.style.visibility = "hidden";
+ outer.style.width = "200px";
+ outer.style.height = "150px";
+ outer.style.overflow = "hidden";
+ outer.appendChild (inner);
+
+ document.body.appendChild (outer);
+ var w1 = inner.offsetWidth;
+ outer.style.overflow = 'scroll';
+ var w2 = inner.offsetWidth;
+ if (w1 == w2) w2 = outer.clientWidth;
+
+ document.body.removeChild (outer);
+
+ return (w1 - w2);
+};
\ No newline at end of file