UNPKG

@spalger/kibana

Version:

Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic

116 lines (97 loc) 3.47 kB
define(function (require) { var $ = require('jquery'); var _ = require('lodash'); var SCROLLER_HEIGHT = 20; require('ui/modules') .get('kibana') .directive('fixedScroll', function ($timeout) { return { restrict: 'A', link: function ($scope, $el) { var $window = $(window); var $scroller = $('<div class="fixed-scroll-scroller">').height(SCROLLER_HEIGHT); /** * Remove the listeners bound in listen() * @type {function} */ var unlisten = _.noop; /** * Listen for scroll events on the $scroller and the $el, sets unlisten() * * unlisten must be called before calling or listen() will throw an Error * * Since the browser emits "scroll" events after setting scrollLeft * the listeners also prevent tug-of-war * * @throws {Error} If unlisten was not called first * @return {undefined} */ function listen() { if (unlisten !== _.noop) { throw new Error('fixedScroll listeners were not cleaned up properly before re-listening!'); } var blockTo; function bind($from, $to) { function handler() { if (blockTo === $to) return (blockTo = null); $to.scrollLeft((blockTo = $from).scrollLeft()); } $from.on('scroll', handler); return function () { $from.off('scroll', handler); }; } unlisten = _.flow( bind($el, $scroller), bind($scroller, $el), function () { unlisten = _.noop; } ); } /** * Revert DOM changes and event listeners * @return {undefined} */ function cleanUp() { unlisten(); $scroller.detach(); $el.css('padding-bottom', 0); } /** * Modify the DOM and attach event listeners based on need. * Is called many times to re-setup, must be idempotent * @return {undefined} */ function setup() { cleanUp(); var containerWidth = $el.width(); var contentWidth = $el.prop('scrollWidth'); var containerHorizOverflow = contentWidth - containerWidth; var elTop = $el.offset().top - $window.scrollTop(); var elBottom = elTop + $el.height(); var windowVertOverflow = elBottom - $window.height(); var requireScroller = containerHorizOverflow > 0 && windowVertOverflow > 0; if (!requireScroller) return; // push the content away from the scroller $el.css('padding-bottom', SCROLLER_HEIGHT); // fill the scroller with a dummy element that mimics the content $scroller .width(containerWidth) .html($('<div>').css({ width: contentWidth, height: SCROLLER_HEIGHT })) .insertAfter($el); // listen for scroll events listen(); } // reset when the width or scrollWidth of the $el changes $scope.$watchMulti([ function () { return $el.prop('scrollWidth'); }, function () { return $el.width(); } ], setup); // cleanup when the scope is destroyed $scope.$on('$destroy', function () { cleanUp(); $scroller = $window = null; }); } }; }); });