diff --git a/package.json b/package.json
index e70633ac9a8..4b29ebdccfb 100644
--- a/package.json
+++ b/package.json
@@ -65,6 +65,7 @@
"dependencies": {
"eventemitter3": "^2.0.2",
"gaze": "^1.1.2",
+ "gemini-scrollbar": "^1.5.2",
"grunt-jscs": "3.0.1",
"grunt-sass-lint": "^0.2.2",
"grunt-sync": "^0.6.2",
diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts
index 914f3ba2dd9..06bdcb8c864 100644
--- a/public/app/core/components/scroll/scroll.ts
+++ b/public/app/core/components/scroll/scroll.ts
@@ -1,211 +1,19 @@
-// ///
-//
-// import _ from 'lodash';
-//
-// var objectAssign = require('object-assign');
-// var Emitter = require('tiny-emitter');
-// var Lethargy = require('lethargy').Lethargy;
-// var support = require('./support');
-// var clone = require('./clone');
-// var bindAll = require('bindall-standalone');
-// var EVT_ID = 'virtualscroll';
-//
-// var keyCodes = {
-// LEFT: 37,
-// UP: 38,
-// RIGHT: 39,
-// DOWN: 40
-// };
-//
-// function VirtualScroll(this: any, options) {
-// _.bindAll(this, '_onWheel', '_onMouseWheel', '_onTouchStart', '_onTouchMove', '_onKeyDown');
-//
-// this.el = window;
-// if (options && options.el) {
-// this.el = options.el;
-// delete options.el;
-// }
-//
-// this.options = _.assign({
-// mouseMultiplier: 1,
-// touchMultiplier: 2,
-// firefoxMultiplier: 15,
-// keyStep: 120,
-// preventTouch: false,
-// unpreventTouchClass: 'vs-touchmove-allowed',
-// limitInertia: false
-// }, options);
-//
-// if (this.options.limitInertia) this._lethargy = new Lethargy();
-//
-// this._emitter = new Emitter();
-// this._event = {
-// y: 0,
-// x: 0,
-// deltaX: 0,
-// deltaY: 0
-// };
-//
-// this.touchStartX = null;
-// this.touchStartY = null;
-// this.bodyTouchAction = null;
-// }
-//
-// VirtualScroll.prototype._notify = function(e) {
-// var evt = this._event;
-// evt.x += evt.deltaX;
-// evt.y += evt.deltaY;
-//
-// this._emitter.emit(EVT_ID, {
-// x: evt.x,
-// y: evt.y,
-// deltaX: evt.deltaX,
-// deltaY: evt.deltaY,
-// originalEvent: e
-// });
-// };
-//
-// VirtualScroll.prototype._onWheel = function(e) {
-// var options = this.options;
-// if (this._lethargy && this._lethargy.check(e) === false) return;
-//
-// var evt = this._event;
-//
-// // In Chrome and in Firefox (at least the new one)
-// evt.deltaX = e.wheelDeltaX || e.deltaX * -1;
-// evt.deltaY = e.wheelDeltaY || e.deltaY * -1;
-//
-// // for our purpose deltamode = 1 means user is on a wheel mouse, not touch pad
-// // real meaning: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent#Delta_modes
-// if(support.isFirefox && e.deltaMode == 1) {
-// evt.deltaX *= options.firefoxMultiplier;
-// evt.deltaY *= options.firefoxMultiplier;
-// }
-//
-// evt.deltaX *= options.mouseMultiplier;
-// evt.deltaY *= options.mouseMultiplier;
-//
-// this._notify(e);
-// };
-//
-// VirtualScroll.prototype._onMouseWheel = function(e) {
-// if (this.options.limitInertia && this._lethargy.check(e) === false) return;
-//
-// var evt = this._event;
-//
-// // In Safari, IE and in Chrome if 'wheel' isn't defined
-// evt.deltaX = (e.wheelDeltaX) ? e.wheelDeltaX : 0;
-// evt.deltaY = (e.wheelDeltaY) ? e.wheelDeltaY : e.wheelDelta;
-//
-// this._notify(e);
-// };
-//
-// VirtualScroll.prototype._onTouchStart = function(e) {
-// var t = (e.targetTouches) ? e.targetTouches[0] : e;
-// this.touchStartX = t.pageX;
-// this.touchStartY = t.pageY;
-// };
-//
-// VirtualScroll.prototype._onTouchMove = function(e) {
-// var options = this.options;
-// if(options.preventTouch
-// && !e.target.classList.contains(options.unpreventTouchClass)) {
-// e.preventDefault();
-// }
-//
-// var evt = this._event;
-//
-// var t = (e.targetTouches) ? e.targetTouches[0] : e;
-//
-// evt.deltaX = (t.pageX - this.touchStartX) * options.touchMultiplier;
-// evt.deltaY = (t.pageY - this.touchStartY) * options.touchMultiplier;
-//
-// this.touchStartX = t.pageX;
-// this.touchStartY = t.pageY;
-//
-// this._notify(e);
-// };
-//
-// VirtualScroll.prototype._onKeyDown = function(e) {
-// var evt = this._event;
-// evt.deltaX = evt.deltaY = 0;
-//
-// switch(e.keyCode) {
-// case keyCodes.LEFT:
-// case keyCodes.UP:
-// evt.deltaY = this.options.keyStep;
-// break;
-//
-// case keyCodes.RIGHT:
-// case keyCodes.DOWN:
-// evt.deltaY = - this.options.keyStep;
-// break;
-//
-// default:
-// return;
-// }
-//
-// this._notify(e);
-// };
-//
-// VirtualScroll.prototype._bind = function() {
-// if(support.hasWheelEvent) this.el.addEventListener('wheel', this._onWheel);
-// if(support.hasMouseWheelEvent) this.el.addEventListener('mousewheel', this._onMouseWheel);
-//
-// if(support.hasTouch) {
-// this.el.addEventListener('touchstart', this._onTouchStart);
-// this.el.addEventListener('touchmove', this._onTouchMove);
-// }
-//
-// if(support.hasPointer && support.hasTouchWin) {
-// this.bodyTouchAction = document.body.style.msTouchAction;
-// document.body.style.msTouchAction = 'none';
-// this.el.addEventListener('MSPointerDown', this._onTouchStart, true);
-// this.el.addEventListener('MSPointerMove', this._onTouchMove, true);
-// }
-//
-// if(support.hasKeyDown) document.addEventListener('keydown', this._onKeyDown);
-// };
-//
-// VirtualScroll.prototype._unbind = function() {
-// if(support.hasWheelEvent) this.el.removeEventListener('wheel', this._onWheel);
-// if(support.hasMouseWheelEvent) this.el.removeEventListener('mousewheel', this._onMouseWheel);
-//
-// if(support.hasTouch) {
-// this.el.removeEventListener('touchstart', this._onTouchStart);
-// this.el.removeEventListener('touchmove', this._onTouchMove);
-// }
-//
-// if(support.hasPointer && support.hasTouchWin) {
-// document.body.style.msTouchAction = this.bodyTouchAction;
-// this.el.removeEventListener('MSPointerDown', this._onTouchStart, true);
-// this.el.removeEventListener('MSPointerMove', this._onTouchMove, true);
-// }
-//
-// if(support.hasKeyDown) document.removeEventListener('keydown', this._onKeyDown);
-// };
-//
-// VirtualScroll.prototype.on = function(cb, ctx) {
-// this._emitter.on(EVT_ID, cb, ctx);
-//
-// var events = this._emitter.e;
-// if (events && events[EVT_ID] && events[EVT_ID].length === 1) this._bind();
-// };
-//
-// VirtualScroll.prototype.off = function(cb, ctx) {
-// this._emitter.off(EVT_ID, cb, ctx);
-//
-// var events = this._emitter.e;
-// if (!events[EVT_ID] || events[EVT_ID].length <= 0) this._unbind();
-// };
-//
-// VirtualScroll.prototype.reset = function() {
-// var evt = this._event;
-// evt.x = 0;
-// evt.y = 0;
-// };
-//
-// VirtualScroll.prototype.destroy = function() {
-// this._emitter.off();
-// this._unbind();
-// };
+///
+
+import GeminiScrollbar from 'gemini-scrollbar';
+import coreModule from 'app/core/core_module';
+import _ from 'lodash';
+
+export function geminiScrollbar() {
+ return {
+ restrict: 'A',
+ link: function(scope, elem, attrs) {
+ var myScrollbar = new GeminiScrollbar({
+ autoshow: false,
+ element: elem[0]
+ }).create();
+ }
+ };
+}
+
+coreModule.directive('geminiScrollbar', geminiScrollbar);
diff --git a/public/app/core/core.ts b/public/app/core/core.ts
index 92db42be8c4..fed63f37955 100644
--- a/public/app/core/core.ts
+++ b/public/app/core/core.ts
@@ -51,6 +51,7 @@ import {JsonExplorer} from './components/json_explorer/json_explorer';
import {NavModelSrv, NavModel} from './nav_model_srv';
import {userPicker} from './components/user_picker';
import {userGroupPicker} from './components/user_group_picker';
+import {geminiScrollbar} from './components/scroll/scroll';
export {
arrayJoin,
@@ -81,4 +82,5 @@ export {
NavModel,
userPicker,
userGroupPicker,
+ geminiScrollbar,
};
diff --git a/public/app/features/dashboard/dashnav/dashnav.html b/public/app/features/dashboard/dashnav/dashnav.html
index 9fe6cfb9a1e..e8114e0298f 100644
--- a/public/app/features/dashboard/dashnav/dashnav.html
+++ b/public/app/features/dashboard/dashnav/dashnav.html
@@ -81,3 +81,4 @@
+
diff --git a/public/app/headers/common.d.ts b/public/app/headers/common.d.ts
index 9ea5e96654d..ec80c01407d 100644
--- a/public/app/headers/common.d.ts
+++ b/public/app/headers/common.d.ts
@@ -72,3 +72,8 @@ declare module 'd3' {
var d3: any;
export default d3;
}
+
+declare module 'gemini-scrollbar' {
+ var d3: any;
+ export default d3;
+}
diff --git a/public/app/system.conf.js b/public/app/system.conf.js
index 816cba4fb42..0a89f6f58b9 100644
--- a/public/app/system.conf.js
+++ b/public/app/system.conf.js
@@ -2,7 +2,7 @@ System.config({
defaultJSExtenions: true,
baseURL: 'public',
paths: {
- 'virtual-scroll': 'vendor/npm/virtual-scroll/src/index.js',
+ 'gemini-scrollbar': 'vendor/npm/gemini-scrollbar/index.js',
'mousetrap': 'vendor/npm/mousetrap/mousetrap.js',
'remarkable': 'vendor/npm/remarkable/dist/remarkable.js',
'tether': 'vendor/npm/tether/dist/js/tether.js',
@@ -56,10 +56,6 @@ System.config({
},
meta: {
- 'vendor/npm/virtual-scroll/src/indx.js': {
- format: 'cjs',
- exports: 'VirtualScroll',
- },
'vendor/angular/angular.js': {
format: 'global',
deps: ['jquery'],
diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss
index f22036dd266..e965baf9cbe 100644
--- a/public/sass/_grafana.scss
+++ b/public/sass/_grafana.scss
@@ -31,6 +31,7 @@
@import "layout/page";
// COMPONENTS
+@import "components/scrollbar";
@import "components/cards";
@import "components/buttons";
@import "components/navs";
diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss
new file mode 100644
index 00000000000..b16acb75475
--- /dev/null
+++ b/public/sass/components/_scrollbar.scss
@@ -0,0 +1,148 @@
+/**
+ * gemini-scrollbar
+ * @version 1.5.2
+ * @link http://noeldelgado.github.io/gemini-scrollbar/
+ * @license MIT
+ */
+
+/* disable selection while dragging */
+.gm-scrollbar-disable-selection {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/* fallback for native floating scrollbars */
+.gm-prevented {
+ -webkit-overflow-scrolling: touch;
+}
+.gm-prevented > .gm-scrollbar {
+ display: none;
+}
+
+/* actual gemini-scrollbar styles */
+.gm-scrollbar-container {
+ position: relative;
+ overflow: hidden!important;
+ width: 100%;
+ height: 100%;
+}
+
+.gm-scrollbar {
+ position: absolute;
+ right: 2px;
+ bottom: 2px;
+ z-index: 1;
+ border-radius: 3px;
+}
+
+.gm-scrollbar.-vertical {
+ width: 6px;
+ top: 2px;
+}
+
+.gm-scrollbar.-horizontal {
+ height: 6px;
+ left: 2px;
+}
+
+.gm-scrollbar .thumb {
+ position: relative;
+ display: block;
+ width: 0;
+ height: 0;
+ cursor: pointer;
+ border-radius: inherit;
+ background-color: lighten($body-bg, 10%);
+ transform: translate3d(0,0,0);
+}
+
+.gm-scrollbar .thumb:hover,
+.gm-scrollbar .thumb:active {
+ background-color: lighten($body-bg, 20%);
+}
+
+.gm-scrollbar.-vertical .thumb {
+ width: 100%;
+}
+
+.gm-scrollbar.-horizontal .thumb {
+ height: 100%;
+}
+
+.gm-scrollbar-container .gm-scroll-view {
+ width: 100%;
+ height: 100%;
+ overflow: scroll;
+ transform: translate3d(0,0,0);
+ -webkit-overflow-scrolling: touch;
+}
+
+/* @option: autoshow */
+.gm-scrollbar-container.gm-autoshow .gm-scrollbar {
+ opacity: 0;
+ transition: opacity 120ms ease-out;
+}
+.gm-scrollbar-container.gm-autoshow:hover > .gm-scrollbar,
+.gm-scrollbar-container.gm-autoshow:active > .gm-scrollbar,
+.gm-scrollbar-container.gm-autoshow:focus > .gm-scrollbar {
+ opacity: 1;
+ transition: opacity 340ms ease-out;
+}
+
+.gm-resize-trigger {
+ position: absolute;
+ display: block;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ overflow: hidden;
+ pointer-events: none;
+ z-index: -1;
+ opacity: 0;
+}
+
+
+// // Srollbars
+// //
+//
+// ::-webkit-scrollbar {
+// width: 8px;
+// height: 8px;
+// }
+//
+// ::-webkit-scrollbar:hover {
+// height: 8px;
+// }
+//
+// ::-webkit-scrollbar-button:start:decrement,
+// ::-webkit-scrollbar-button:end:increment { display: none; }
+// ::-webkit-scrollbar-button:horizontal:decrement { display: none; }
+// ::-webkit-scrollbar-button:horizontal:increment { display: none; }
+// ::-webkit-scrollbar-button:vertical:decrement { display: none; }
+// ::-webkit-scrollbar-button:vertical:increment { display: none; }
+// ::-webkit-scrollbar-button:horizontal:decrement:active { background-image: none; }
+// ::-webkit-scrollbar-button:horizontal:increment:active { background-image: none; }
+// ::-webkit-scrollbar-button:vertical:decrement:active { background-image: none; }
+// ::-webkit-scrollbar-button:vertical:increment:active {background-image: none; }
+// ::-webkit-scrollbar-track-piece { background-color: transparent; }
+//
+// ::-webkit-scrollbar-thumb:vertical {
+// height: 50px;
+// background: -webkit-gradient(linear, left top, right top, color-stop(0%, $scrollbarBackground), color-stop(100%, $scrollbarBackground2));
+// border: 1px solid $scrollbarBorder;
+// border-top: 1px solid $scrollbarBorder;
+// border-left: 1px solid $scrollbarBorder;
+// }
+//
+// ::-webkit-scrollbar-thumb:horizontal {
+// width: 50px;
+// background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, $scrollbarBackground), color-stop(100%, $scrollbarBackground2));
+// border: 1px solid $scrollbarBorder;
+// border-top: 1px solid $scrollbarBorder;
+// border-left: 1px solid $scrollbarBorder;
+// }
diff --git a/public/sass/layout/_page.scss b/public/sass/layout/_page.scss
index d823a8e457f..199bf876628 100644
--- a/public/sass/layout/_page.scss
+++ b/public/sass/layout/_page.scss
@@ -26,9 +26,6 @@
overflow: auto;
}
-.page-header-inner {
-}
-
.page-body {
@include media-breakpoint-up(md) {
display: flex;
diff --git a/tasks/options/copy.js b/tasks/options/copy.js
index 59921576da3..ba8b70f8674 100644
--- a/tasks/options/copy.js
+++ b/tasks/options/copy.js
@@ -32,7 +32,7 @@ module.exports = function(config) {
'tether-drop/**/*',
'remarkable/dist/*',
'remarkable/dist/*',
- 'virtual-scroll/**/*',
+ 'gemini-scrollbar/*.js',
'mousetrap/**/*',
],
dest: '<%= srcDir %>/vendor/npm'
diff --git a/tasks/options/watch.js b/tasks/options/watch.js
index f7bea8af721..c3449524243 100644
--- a/tasks/options/watch.js
+++ b/tasks/options/watch.js
@@ -24,6 +24,7 @@ module.exports = function(config, grunt) {
gaze([
config.srcDir + '/sass/**/*',
config.srcDir + '/app/**/*',
+ config.srcDir + '/vendor/npm/gemini-scrollbar/*.js',
], function(err, watcher) {
console.log('Gaze watchers setup');
diff --git a/yarn.lock b/yarn.lock
index a9100c88e10..db2f5db1f53 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1601,6 +1601,10 @@ gaze@^1.0.0, gaze@^1.1.2:
dependencies:
globule "^1.0.0"
+gemini-scrollbar@^1.5.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/gemini-scrollbar/-/gemini-scrollbar-1.5.2.tgz#06528e49afe155c45ce9f7c9ee754a9e6f4ed341"
+
generate-function@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74"
@@ -1926,12 +1930,12 @@ grunt-sass-lint@^0.2.2:
dependencies:
sass-lint "^1.10.2"
-grunt-sass@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/grunt-sass/-/grunt-sass-1.2.1.tgz#fb87b6caac46fb32d45177fd2e4b6ff7468c1919"
+grunt-sass@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/grunt-sass/-/grunt-sass-2.0.0.tgz#9074cf9d7b4592e20f7788caa727b8f9aa06b60a"
dependencies:
each-async "^1.0.0"
- node-sass "^3.7.0"
+ node-sass "^4.0.0"
object-assign "^4.0.1"
grunt-string-replace@~1.3.1:
@@ -2816,6 +2820,10 @@ lodash.keys@^3.0.0:
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
+lodash.mergewith@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
+
lodash@3.7.x:
version "3.7.0"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.7.0.tgz#3678bd8ab995057c07ade836ed2ef087da811d45"
@@ -3151,9 +3159,9 @@ node-pre-gyp@^0.6.29:
tar "~2.2.1"
tar-pack "~3.3.0"
-node-sass@^3.7.0:
- version "3.13.1"
- resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-3.13.1.tgz#7240fbbff2396304b4223527ed3020589c004fc2"
+node-sass@^4.0.0:
+ version "4.5.3"
+ resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568"
dependencies:
async-foreach "^0.1.3"
chalk "^1.1.1"
@@ -3164,13 +3172,15 @@ node-sass@^3.7.0:
in-publish "^2.0.0"
lodash.assign "^4.2.0"
lodash.clonedeep "^4.3.2"
+ lodash.mergewith "^4.6.0"
meow "^3.7.0"
mkdirp "^0.5.1"
nan "^2.3.2"
node-gyp "^3.3.1"
npmlog "^4.0.0"
- request "^2.61.0"
+ request "^2.79.0"
sass-graph "^2.1.1"
+ stdout-stream "^1.4.0"
"nomnom@>= 1.5.x":
version "1.8.1"
@@ -3627,7 +3637,7 @@ readable-stream@1.1:
isarray "0.0.1"
string_decoder "~0.10.x"
-readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5:
+readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5:
version "2.2.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e"
dependencies:
@@ -3745,7 +3755,7 @@ request-progress@~2.0.1:
dependencies:
throttleit "^1.0.0"
-request@2, request@^2.61.0, request@^2.79.0, request@~2.79.0:
+request@2, request@^2.79.0, request@~2.79.0:
version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
dependencies:
@@ -4117,6 +4127,12 @@ statuses@1, "statuses@>= 1.3.1 < 2", statuses@~1.3.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
+stdout-stream@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b"
+ dependencies:
+ readable-stream "^2.0.1"
+
stream-buffers@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"