UNPKG

kibana-with-account

Version:

kibana-with-account is develop based kibana project

237 lines (193 loc) 8.49 kB
define(function (require) { var _ = require('lodash'); var $ = require('jquery'); var Binder = require('ui/Binder'); require('gridster'); var app = require('ui/modules').get('app/dashboard'); app.directive('dashboardGrid', function ($compile, Notifier) { return { restrict: 'E', require: '^dashboardApp', // must inherit from the dashboardApp link: function ($scope, $el) { var notify = new Notifier(); var $container = $el; $el = $('<ul>').appendTo($container); var $window = $(window); var $body = $(document.body); var binder = new Binder($scope); // appState from controller var $state = $scope.state; var gridster; // defined in init() // number of columns to render var COLS = 12; // number of pixed between each column/row var SPACER = 10; // pixels used by all of the spacers (gridster puts have a spacer on the ends) var spacerSize = SPACER * COLS; // debounced layout function is safe to call as much as possible var safeLayout = _.debounce(layout, 200); function init() { $el.addClass('gridster'); gridster = $el.gridster({ max_cols: COLS, min_cols: COLS, autogenerate_stylesheet: false, resize: { enabled: true, stop: readGridsterChangeHandler }, draggable: { handle: '.panel-heading, .panel-title', stop: readGridsterChangeHandler } }).data('gridster'); // This is necessary to enable text selection within gridster elements // http://stackoverflow.com/questions/21561027/text-not-selectable-from-editable-div-which-is-draggable binder.jqOn($el, 'mousedown', function () { gridster.disable().disable_resize(); }); binder.jqOn($el, 'mouseup', function enableResize() { gridster.enable().enable_resize(); }); $scope.$watchCollection('state.panels', function (panels) { var currentPanels = gridster.$widgets.toArray().map(function (el) { return getPanelFor(el); }); // panels that are now missing from the panels array var removed = _.difference(currentPanels, panels); // panels that have been added var added = _.difference(panels, currentPanels); if (removed.length) removed.forEach(removePanel); if (added.length) added.forEach(addPanel); // ensure that every panel can be serialized now that we are done $state.panels.forEach(makePanelSerializeable); // alert interested parties that we have finished processing changes to the panels // TODO: change this from event based to calling a method on dashboardApp if (added.length || removed.length) $scope.$root.$broadcast('change:vis'); }); $scope.$on('$destroy', function () { $window.off('resize', safeLayout); if (!gridster) return; gridster.$widgets.each(function (i, el) { var panel = getPanelFor(el); removePanel(panel); // stop any animations panel.$el.stop(); // not that we will, but lets be safe makePanelSerializeable(panel); }); }); safeLayout(); $window.on('resize', safeLayout); $scope.$on('ready:vis', safeLayout); } // return the panel object for an element. // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // ALWAYS CALL makePanelSerializeable AFTER YOU ARE DONE WITH IT // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! function getPanelFor(el) { var $panel = el.jquery ? el : $(el); var panel = $panel.data('panel'); panel.$el = $panel; panel.$scope = $panel.data('$scope'); return panel; } // since the $el and $scope are circular structures, they need to be // removed from panel before it can be serialized (we also wouldn't // want them to show up in the url) function makePanelSerializeable(panel) { delete panel.$el; delete panel.$scope; } // tell gridster to remove the panel, and cleanup our metadata function removePanel(panel) { // remove from grister 'silently' (don't reorganize after) gridster.remove_widget(panel.$el); // destroy the scope panel.$scope.$destroy(); panel.$el.removeData('panel'); panel.$el.removeData('$scope'); } // tell gridster to add the panel, and create additional meatadata like $scope function addPanel(panel) { _.defaults(panel, { size_x: 3, size_y: 2 }); // ignore panels that don't have vis id's if (!panel.id) { // In the interest of backwards compat if (panel.visId) { panel.id = panel.visId; panel.type = 'visualization'; delete panel.visId; } else { throw new Error('missing object id on panel'); } } panel.$scope = $scope.$new(); panel.$scope.panel = panel; panel.$scope.parentUiState = $scope.uiState; panel.$el = $compile('<li><dashboard-panel></li>')(panel.$scope); // tell gridster to use the widget gridster.add_widget(panel.$el, panel.size_x, panel.size_y, panel.col, panel.row); // update size/col/etc. refreshPanelStats(panel); // stash the panel and it's scope in the element's data panel.$el.data('panel', panel); panel.$el.data('$scope', panel.$scope); } // ensure that the panel object has the latest size/pos info function refreshPanelStats(panel) { var data = panel.$el.coords().grid; panel.size_x = data.size_x; panel.size_y = data.size_y; panel.col = data.col; panel.row = data.row; } // when gridster tell us it made a change, update each of the panel objects function readGridsterChangeHandler(e, ui, $widget) { // ensure that our panel objects keep their size in sync gridster.$widgets.each(function (i, el) { var panel = getPanelFor(el); refreshPanelStats(panel); panel.$scope.$broadcast('resize'); makePanelSerializeable(panel); $scope.$root.$broadcast('change:vis'); }); } // calculate the position and sizing of the gridster el, and the columns within it // then tell gridster to "reflow" -- which is definitely not supported. // we may need to consider using a different library function reflowGridster() { // https://github.com/gcphost/gridster-responsive/blob/97fe43d4b312b409696b1d702e1afb6fbd3bba71/jquery.gridster.js#L1208-L1235 var g = gridster; g.options.widget_margins = [SPACER / 2, SPACER / 2]; g.options.widget_base_dimensions = [($container.width() - spacerSize) / COLS, 100]; g.min_widget_width = (g.options.widget_margins[0] * 2) + g.options.widget_base_dimensions[0]; g.min_widget_height = (g.options.widget_margins[1] * 2) + g.options.widget_base_dimensions[1]; // var serializedGrid = g.serialize(); g.$widgets.each(function (i, widget) { g.resize_widget($(widget)); }); g.generate_grid_and_stylesheet(); g.generate_stylesheet({ namespace: '.gridster' }); g.get_widgets_from_DOM(); // We can't call this method if the gridmap is empty. This was found // when the user double clicked the "New Dashboard" icon. See // https://github.com/elastic/kibana4/issues/390 if (gridster.gridmap.length > 0) g.set_dom_grid_height(); g.drag_api.set_limits(COLS * g.min_widget_width); } function layout() { var complete = notify.event('reflow dashboard'); reflowGridster(); readGridsterChangeHandler(); complete(); } init(); } }; }); });