Skip to content

Commit caab5f2

Browse files
LukasWillinThomaashvis-bot
authored
feat(timeline): Implement new options 'horizontalScrollKey' (#1670, #1323) and 'horizontalScrollInvert' (#1595) (#1852)
* feat(timeline): (History.md: FEAT-3162) Implement new options 'horizontalScrollKey' (#1670, #1323) and 'horizontalScrollInvert' (#1595) to allow bi-directional scrolling modified: lib/timeline/Core.js modified: lib/timeline/Range.js modified: lib/timeline/optionsTimeline.js modified: types/index.d.ts * feat(timeline): (History.md: FEAT-3162) Document and add examples for new options 'horizontalScrollKey' (#1670, #1323) and 'horizontalScrollInvert' (#1595) new file: examples/timeline/other/biDirectionalScroll.html modified: examples/timeline/other/horizontalScroll.html new file: examples/timeline/other/horizontalScrollInvert.html * refactor(lint-staged) Core.js, Range.js, optionsTimeline.js, index.d.ts * feat(timeline): (History.md: FEAT-3162) Document 'horizontalScrollKey' (#1670, #1323) and 'horizontalScrollInvert' (#1595) `docs/timeline/index.html`. modified: docs/timeline/index.html * docs(timeline): fix syntax error in horizontalScroll example * docs(timeline): remove duplicated block of code in horizontalScroll example This likely happened during merging. * style: reformat * refactor(timeline): remove unused function params * docs(timeline): only invert horizontal scroll in inverted scroll example * docs(timeline): use minified bundles for consistency accross examples --------- Co-authored-by: Tomina <[email protected]> Co-authored-by: Vis Bot <[email protected]>
1 parent d893339 commit caab5f2

File tree

9 files changed

+291
-48
lines changed

9 files changed

+291
-48
lines changed

docs/timeline/index.html

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -973,12 +973,54 @@ <h2 id="Configuration_Options">Configuration Options</h2>
973973
<tr>
974974
<td>horizontalScroll</td>
975975
<td>Boolean</td>
976-
<td>false</td>
976+
<td><code>false</code></td>
977977
<td>
978978
This option allows you to scroll horizontally to move backwards and
979-
forwards in the time range. Only applicable when option
980-
<code>zoomKey</code> is defined or <code>zoomable</code> is
981-
<code>false</code>.
979+
forwards in the time range.<br />
980+
Only applicable when option <code>zoomKey</code> is defined or
981+
<code>zoomable</code> is <code>false</code>.<br />
982+
If you also set <code>verticalScroll: true</code>, then the vertical
983+
direction will be preferred, unless the user has hardware to
984+
specifically scroll horizontally.<br />
985+
If you need bi-directional scrolling with regular vertical
986+
scroll-wheels, then take a look at
987+
<code>horizontalScrollKey</code>.<br />
988+
And if you want to invert the direction see
989+
<code>horizontalScrollInvert</code>.
990+
</td>
991+
</tr>
992+
993+
<tr>
994+
<td>horizontalScrollKey</td>
995+
<td>String</td>
996+
<td><code>''</code></td>
997+
<td>
998+
This option allows you to scroll horizontally with a vertical
999+
scroll-wheel, while holding down a key.<br />
1000+
Available values are <code>''</code> (does not apply),
1001+
<code>'altKey'</code>, <code>'ctrlKey'</code>,
1002+
<code>'shiftKey'</code> or <code>'metaKey'</code>.<br />
1003+
Only applicable when option <code>horizontalScroll</code> is
1004+
defined.<br />
1005+
Note: Hardware which directly enables horizontal scrolling is
1006+
unaffected by this setting, since the user can control that
1007+
behaviour independently via the driver.
1008+
</td>
1009+
</tr>
1010+
1011+
<tr>
1012+
<td>horizontalScrollInvert</td>
1013+
<td>Boolean</td>
1014+
<td><code>false</code></td>
1015+
<td>
1016+
This option allows you to invert the horizontal scroll direction.<br />
1017+
By default scroll-up will move the view to the right (future) and
1018+
scroll-down to the left (past).<br />
1019+
Only applicable when option <code>horizontalScroll</code> is
1020+
defined.<br />
1021+
Note: Hardware which directly enables horizontal scrolling is
1022+
unaffected by this setting, since the user can control that
1023+
behaviour independently via the driver.
9821024
</td>
9831025
</tr>
9841026

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<html>
2+
<head>
3+
<title>Timeline | other | Bi-Directional Scroll Option</title>
4+
5+
<script src="../../../standalone/umd/vis-timeline-graph2d.js"></script>
6+
<link
7+
href="../../../styles/vis-timeline-graph2d.min.css"
8+
rel="stylesheet"
9+
type="text/css"
10+
/>
11+
</head>
12+
13+
<body>
14+
<h1>Timeline bi-directional scroll option</h1>
15+
16+
<div id="visualization"></div>
17+
18+
<script>
19+
// create groups
20+
var numberOfGroups = 25;
21+
var groups = new vis.DataSet();
22+
for (var i = 0; i < numberOfGroups; i++) {
23+
groups.add({
24+
id: i,
25+
content: "Truck&nbsp;" + i,
26+
});
27+
}
28+
29+
// create items
30+
var numberOfItems = 1000;
31+
var items = new vis.DataSet();
32+
33+
var itemsPerGroup = Math.round(numberOfItems / numberOfGroups);
34+
35+
for (var truck = 0; truck < numberOfGroups; truck++) {
36+
var date = new Date();
37+
for (var order = 0; order < itemsPerGroup; order++) {
38+
date.setHours(date.getHours() + 4 * (Math.random() < 0.2));
39+
var start = new Date(date);
40+
41+
date.setHours(date.getHours() + 2 + Math.floor(Math.random() * 4));
42+
var end = new Date(date);
43+
44+
items.add({
45+
id: order + itemsPerGroup * truck,
46+
group: truck,
47+
start: start,
48+
end: end,
49+
content: "Order " + order,
50+
});
51+
}
52+
}
53+
54+
// specify options
55+
var options = {
56+
stack: true,
57+
// Enable both vertical and horizontal scrolling
58+
verticalScroll: true,
59+
horizontalScroll: true,
60+
// Set a key that switches between vertical and horizontal scrolling.
61+
horizontalScrollKey: "shiftKey",
62+
// Invert the horizontal scroll direction when using a vertical scroll-wheel in combination with 'horizontalScrollKey'
63+
horizontalScrollInvert: true,
64+
zoomKey: "ctrlKey",
65+
maxHeight: 400,
66+
start: new Date(),
67+
end: new Date(1000 * 60 * 60 * 24 + new Date().valueOf()),
68+
editable: true,
69+
margin: {
70+
item: 10, // minimal margin between items
71+
axis: 5, // minimal margin between items and the axis
72+
},
73+
orientation: "top",
74+
};
75+
76+
// create a Timeline
77+
var container = document.getElementById("visualization");
78+
timeline = new vis.Timeline(container, items, groups, options);
79+
</script>
80+
</body>
81+
</html>

examples/timeline/other/horizontalScroll.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ <h1>Timeline horizontal scroll option</h1>
5555
var options = {
5656
stack: true,
5757
horizontalScroll: true,
58+
horizontalScrollInvert: false,
5859
zoomKey: "ctrlKey",
5960
maxHeight: 400,
6061
start: new Date(),
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<html>
2+
<head>
3+
<title>Timeline | other | Horizontal Scroll Invert Option</title>
4+
5+
<script src="../../../standalone/umd/vis-timeline-graph2d.min.js"></script>
6+
<link
7+
href="../../../styles/vis-timeline-graph2d.min.css"
8+
rel="stylesheet"
9+
type="text/css"
10+
/>
11+
</head>
12+
13+
<body>
14+
<h1>Timeline horizontal scroll invert direction option</h1>
15+
16+
<div id="visualization"></div>
17+
18+
<script>
19+
// create groups
20+
var numberOfGroups = 25;
21+
var groups = new vis.DataSet();
22+
for (var i = 0; i < numberOfGroups; i++) {
23+
groups.add({
24+
id: i,
25+
content: "Truck&nbsp;" + i,
26+
});
27+
}
28+
29+
// create items
30+
var numberOfItems = 1000;
31+
var items = new vis.DataSet();
32+
33+
var itemsPerGroup = Math.round(numberOfItems / numberOfGroups);
34+
35+
for (var truck = 0; truck < numberOfGroups; truck++) {
36+
var date = new Date();
37+
for (var order = 0; order < itemsPerGroup; order++) {
38+
date.setHours(date.getHours() + 4 * (Math.random() < 0.2));
39+
var start = new Date(date);
40+
41+
date.setHours(date.getHours() + 2 + Math.floor(Math.random() * 4));
42+
var end = new Date(date);
43+
44+
items.add({
45+
id: order + itemsPerGroup * truck,
46+
group: truck,
47+
start: start,
48+
end: end,
49+
content: "Order " + order,
50+
});
51+
}
52+
}
53+
54+
// specify options
55+
var options = {
56+
stack: true,
57+
horizontalScroll: true,
58+
// Invert the horizontal scroll direction when using a vertical scroll-wheel in combination with 'horizontalScrollKey'
59+
// Note: This only inverts the direction when using the vertical scroll-wheel.
60+
// Horizontal scroll-wheels as found on the "Logi MX Master" series are unaffected.
61+
horizontalScrollInvert: true,
62+
zoomKey: "ctrlKey",
63+
maxHeight: 400,
64+
start: new Date(),
65+
end: new Date(1000 * 60 * 60 * 24 + new Date().valueOf()),
66+
editable: true,
67+
margin: {
68+
item: 10, // minimal margin between items
69+
axis: 5, // minimal margin between items and the axis
70+
},
71+
orientation: "top",
72+
};
73+
74+
// create a Timeline
75+
var container = document.getElementById("visualization");
76+
timeline = new vis.Timeline(container, items, groups, options);
77+
</script>
78+
</body>
79+
</html>

lib/timeline/Core.js

Lines changed: 75 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -187,31 +187,34 @@ class Core {
187187
* @param {WheelEvent} event
188188
*/
189189
function onMouseWheel(event) {
190-
// Reasonable default wheel deltas
191-
const LINE_HEIGHT = 40;
192-
const PAGE_HEIGHT = 800;
190+
// Only allow scroll-wheel interaction if timeline is active and clickToUse is set to true.
191+
if (!this.isActive()) return;
192+
193+
this.emit("mousewheel", event);
193194

194-
if (this.isActive()) {
195-
this.emit("mousewheel", event);
195+
// Prevent scrolling when zooming (no zoom key, or pressing zoom key)
196+
if (this.options.preferZoom) {
197+
// Return if key not configured OR is currently pressed (zoom only).
198+
// TODO: Possible Bug: Should it not be the inverse behaviour? Aka. if pressed should allow user to scroll?
199+
if (!this.options.zoomKey || event[this.options.zoomKey]) return;
200+
} else {
201+
// Return if key configured AND currently pressed
202+
if (this.options.zoomKey && event[this.options.zoomKey]) return;
196203
}
197204

205+
// Don't preventDefault if you can't scroll
206+
if (!this.options.verticalScroll && !this.options.horizontalScroll)
207+
return;
208+
198209
// deltaX and deltaY normalization from jquery.mousewheel.js
199210
let deltaX = 0;
200211
let deltaY = 0;
201212

202213
// Old school scrollwheel delta
203-
if ("detail" in event) {
204-
deltaY = event.detail * -1;
205-
}
206-
if ("wheelDelta" in event) {
207-
deltaY = event.wheelDelta;
208-
}
209-
if ("wheelDeltaY" in event) {
210-
deltaY = event.wheelDeltaY;
211-
}
212-
if ("wheelDeltaX" in event) {
213-
deltaX = event.wheelDeltaX * -1;
214-
}
214+
if ("detail" in event) deltaY = event.detail * -1;
215+
if ("wheelDelta" in event) deltaY = event.wheelDelta;
216+
if ("wheelDeltaY" in event) deltaY = event.wheelDeltaY;
217+
if ("wheelDeltaX" in event) deltaX = event.wheelDeltaX * -1;
215218

216219
// Firefox < 17 horizontal scrolling related to DOMMouseScroll event
217220
if ("axis" in event && event.axis === event.HORIZONTAL_AXIS) {
@@ -227,6 +230,10 @@ class Core {
227230
deltaX = event.deltaX;
228231
}
229232

233+
// Reasonable default wheel deltas
234+
var LINE_HEIGHT = 40;
235+
var PAGE_HEIGHT = 800;
236+
230237
// Normalize deltas
231238
if (event.deltaMode) {
232239
if (event.deltaMode === 1) {
@@ -239,49 +246,72 @@ class Core {
239246
deltaY *= PAGE_HEIGHT;
240247
}
241248
}
242-
// Prevent scrolling when zooming (no zoom key, or pressing zoom key)
243-
if (this.options.preferZoom) {
244-
if (!this.options.zoomKey || event[this.options.zoomKey]) return;
245-
} else {
246-
if (this.options.zoomKey && event[this.options.zoomKey]) return;
247-
}
248-
// Don't preventDefault if you can't scroll
249-
if (!this.options.verticalScroll && !this.options.horizontalScroll)
250-
return;
251249

252-
if (this.options.verticalScroll && Math.abs(deltaY) >= Math.abs(deltaX)) {
250+
// Vertical scroll preferred unless 'horizontalScrollKey' is configured and currently pressed.
251+
const isVerticalScrollingPreferred = this.options.verticalScroll;
252+
const isCurrentlyScrollingWithVerticalScrollWheel =
253+
Math.abs(deltaY) >= Math.abs(deltaX);
254+
const isHorizontalScrollKeyConfiguredAndPressed =
255+
this.options.horizontalScroll &&
256+
this.options.horizontalScrollKey &&
257+
event[this.options.horizontalScrollKey];
258+
if (
259+
isVerticalScrollingPreferred &&
260+
isCurrentlyScrollingWithVerticalScrollWheel &&
261+
!isHorizontalScrollKeyConfiguredAndPressed
262+
) {
253263
const current = this.props.scrollTop;
254264
const adjusted = current + deltaY;
255265

256-
if (this.isActive()) {
257-
const newScrollTop = this._setScrollTop(adjusted);
258-
259-
if (newScrollTop !== current) {
260-
this._redraw();
261-
this.emit("scroll", event);
266+
const newScrollTop = this._setScrollTop(adjusted);
267+
if (newScrollTop !== current) {
268+
this._redraw();
269+
this.emit("scroll", event);
262270

263-
// Prevent default actions caused by mouse wheel
264-
// (else the page and timeline both scroll)
265-
event.preventDefault();
266-
}
271+
// Prevent default actions caused by mouse wheel
272+
// (else the page and timeline both scroll)
273+
event.preventDefault();
267274
}
268-
} else if (this.options.horizontalScroll) {
269-
const delta = Math.abs(deltaX) >= Math.abs(deltaY) ? deltaX : deltaY;
270275

271-
// calculate a single scroll jump relative to the range scale
272-
const diff = ((delta / 120) * (this.range.end - this.range.start)) / 20;
276+
return;
277+
}
278+
279+
// If option 'verticalScroll' disabled or 'horizontalScroll' is configured
280+
if (this.options.horizontalScroll) {
281+
this.range.stopRolling();
282+
283+
// Depending on the mouse wheel used chose the delta (some mice have the hardware for both)
284+
const delta = isCurrentlyScrollingWithVerticalScrollWheel
285+
? deltaY
286+
: deltaX;
287+
288+
// Calculate a single scroll jump relative to the range scale
289+
let diff = ((delta / 120) * (this.range.end - this.range.start)) / 20;
290+
291+
// Invert scroll direction
292+
// ...unless the user uses a horizontal mouse-wheel as found on the "Logi Master" series.
293+
// In other words only invert the direction when a vertical scroll-wheel is used.
294+
// Reason: Usually the user controls this behaviour via the driver software and it isn't linked to the vertical-scroll behaviour.
295+
if (
296+
this.options.horizontalScrollInvert &&
297+
isCurrentlyScrollingWithVerticalScrollWheel
298+
)
299+
diff = -diff;
300+
273301
// calculate new start and end
274302
const newStart = this.range.start + diff;
275303
const newEnd = this.range.end + diff;
276-
277304
const options = {
278305
animation: false,
279306
byUser: true,
280-
event,
307+
event: event,
281308
};
282309
this.range.setRange(newStart, newEnd, options);
283310

284311
event.preventDefault();
312+
313+
// Here in case of any future behaviour following after
314+
return;
285315
}
286316
}
287317

@@ -459,6 +489,8 @@ class Core {
459489
"rtl",
460490
"zoomKey",
461491
"horizontalScroll",
492+
"horizontalScrollKey",
493+
"horizontalScrollInvert",
462494
"verticalScroll",
463495
"longSelectPressTime",
464496
"snap",

0 commit comments

Comments
 (0)