diff --git a/client/app/assets/less/inc/gridster.less b/client/app/assets/less/inc/gridster.less deleted file mode 100755 index fbfc20b23..000000000 --- a/client/app/assets/less/inc/gridster.less +++ /dev/null @@ -1,29 +0,0 @@ -.gridster .preview-holder { - border: none !important; - border-radius: 0 !important; - background: rgba(0, 0, 0, 0.5) !important; -} - -.gridster li .heading { - border: #ddd; - background-color: #f5f5f5; - padding: 5px; -} - -li.widget { - /*background-color:grey;*/ - border-width: 1px; - border-style: solid; - border-color: grey; - opacity: 0.7; - cursor: move; - &:hover { - opacity: 1.0 !important; - - -webkit-transition: opacity .6s; - -moz-transition: opacity .6s; - -o-transition: opacity .6s; - -ms-transition: opacity .6s; - transition: opacity .6s; - } -} diff --git a/client/app/assets/less/main.less b/client/app/assets/less/main.less index c0dbe77a8..de139f3f8 100644 --- a/client/app/assets/less/main.less +++ b/client/app/assets/less/main.less @@ -9,7 +9,6 @@ @import '~ui-select/dist/select.css'; @import '~angular-toastr/src/toastr'; @import '~angular-resizable/src/angular-resizable.css'; -@import '~angular-gridster/src/angular-gridster'; @import '~pace-progress/themes/blue/pace-theme-minimal.css'; @import '~material-design-iconic-font/dist/css/material-design-iconic-font.css'; @@ -51,7 +50,6 @@ @import 'inc/navbar'; @import 'inc/edit-in-place'; @import 'inc/growl'; -@import 'inc/gridster'; @import 'inc/flex'; @import 'inc/ace-editor'; @import 'inc/overlay'; diff --git a/client/app/components/dashboards/gridstack/gridstack.js b/client/app/components/dashboards/gridstack/gridstack.js index 8d3d93094..3d2a879ed 100644 --- a/client/app/components/dashboards/gridstack/gridstack.js +++ b/client/app/components/dashboards/gridstack/gridstack.js @@ -5,9 +5,6 @@ import 'jquery-ui/ui/widgets/droppable'; import 'jquery-ui/ui/widgets/resizable'; import 'gridstack/dist/gridstack.css'; -window.$ = window.jQuery = $; -window._ = _; - // eslint-disable-next-line import/first import gridstack from 'gridstack'; diff --git a/client/app/components/dashboards/gridstack/index.js b/client/app/components/dashboards/gridstack/index.js index 2ece843ae..2f364153e 100644 --- a/client/app/components/dashboards/gridstack/index.js +++ b/client/app/components/dashboards/gridstack/index.js @@ -59,13 +59,14 @@ function computeAutoHeight($element, grid, node, minHeight, maxHeight) { return Math.min(Math.max(minHeight, resultHeight), maxHeight); } -function gridstack() { +function gridstack($parse, dashboardGridOptions) { return { restrict: 'A', replace: false, scope: { editing: '=', batchUpdate: '=', // set by directive - for using in wrapper components + isOneColumnMode: '=', }, controller() { this.$el = null; @@ -205,16 +206,22 @@ function gridstack() { }; }, link: ($scope, $element, $attr, controller) => { + const batchUpdateAssignable = _.isFunction($parse($attr.batchUpdate).assign); + const isOneColumnModeAssignable = _.isFunction($parse($attr.batchUpdate).assign); + + let enablePolling = true; + $element.addClass('grid-stack'); $element.gridstack({ auto: false, - verticalMargin: 15, - cellHeight: 35, // real row height will be `cellHeight` + `verticalMargin` - width: 6, // columns + verticalMargin: dashboardGridOptions.margins, + // real row height will be `cellHeight` + `verticalMargin` + cellHeight: dashboardGridOptions.rowHeight - dashboardGridOptions.margins, + width: dashboardGridOptions.columns, // columns height: 0, // max rows (0 for unlimited) animate: true, float: false, - minWidth: 800, + minWidth: dashboardGridOptions.mobileBreakPoint, resizable: { handles: 'e, se, s, sw, w', start: (event, ui) => { @@ -253,25 +260,56 @@ function gridstack() { }); controller.$el = $element; - $element.on('change', (event, nodes) => { - nodes = _.isArray(nodes) ? nodes : []; - console.log('+', nodes.length); - _.each(nodes, (node) => { + // `change` events sometimes fire too frequently (for example, + // on initial rendering when all widgets add themselves to grid, grid + // will fire `change` event will _all_ items available at that moment). + // Collect changed items, and then delegate event with some delay + let changedNodes = {}; + const triggerChange = _.debounce(() => { + _.each(changedNodes, (node) => { if (node.el) { $(node.el).trigger('gridstack.changed', node); } }); + changedNodes = {}; + }); + + $element.on('change', (event, nodes) => { + nodes = _.isArray(nodes) ? nodes : []; + _.each(nodes, (node) => { + changedNodes[node.id] = node; + }); + triggerChange(); }); $scope.$watch('editing', (value) => { controller.setEditing(!!value); }); - $scope.batchUpdate = controller.batchUpdateWidgets; + if (batchUpdateAssignable) { + $scope.batchUpdate = controller.batchUpdateWidgets; + } $scope.$on('$destroy', () => { + enablePolling = false; controller.$el = null; }); + + function updateOneColumnMode() { + const grid = controller.grid(); + if (grid) { + $scope.isOneColumnMode = $element.hasClass(grid.opts.oneColumnModeClass); + $scope.$applyAsync(); + } + + if (enablePolling) { + setTimeout(updateOneColumnMode, 150); + } + } + + if (isOneColumnModeAssignable) { + updateOneColumnMode(); + } }, }; } diff --git a/client/app/config/dashboard-grid-options.js b/client/app/config/dashboard-grid-options.js index fffbdabb8..f30c918dc 100644 --- a/client/app/config/dashboard-grid-options.js +++ b/client/app/config/dashboard-grid-options.js @@ -1,33 +1,15 @@ const dashboardGridOptions = { - columns: 6, - pushing: true, - floating: true, - swapping: false, - width: 'auto', - colWidth: 'auto', - rowHeight: 50, - margins: [15, 15], - outerMargin: false, - sparse: false, - isMobile: false, + columns: 6, // grid columns count + rowHeight: 50, // grid row height (incl. bottom padding) + margins: 15, // widget margins mobileBreakPoint: 800, - mobileModeEnabled: true, - minColumns: 6, - minRows: 1, - maxRows: 1000, + // defaults for widgets defaultSizeX: 3, defaultSizeY: 3, minSizeX: 1, maxSizeX: 6, minSizeY: 1, maxSizeY: 1000, - resizable: { - enabled: false, - handles: ['n', 'e', 's', 'w', 'ne', 'se', 'sw', 'nw'], - }, - draggable: { - enabled: false, - }, }; export default function init(ngModule) { diff --git a/client/app/config/index.js b/client/app/config/index.js index 4f154b72a..239e80b51 100644 --- a/client/app/config/index.js +++ b/client/app/config/index.js @@ -17,7 +17,6 @@ import 'angular-moment'; import 'brace'; import 'angular-ui-ace'; import 'angular-resizable'; -import ngGridster from 'angular-gridster'; import { each, isFunction } from 'underscore'; import '@/lib/sortable'; @@ -52,7 +51,6 @@ const requirements = [ 'angularResizable', vsRepeat, 'ui.sortable', - ngGridster.name, ]; const ngModule = angular.module('app', requirements); diff --git a/client/app/config/styles.js b/client/app/config/styles.js index 468747588..40dbd2713 100644 --- a/client/app/config/styles.js +++ b/client/app/config/styles.js @@ -3,7 +3,6 @@ import 'font-awesome/css/font-awesome.css'; import 'ui-select/dist/select.css'; import 'angular-toastr/dist/angular-toastr.css'; import 'angular-resizable/src/angular-resizable.css'; -import 'angular-gridster/dist/angular-gridster.css'; import 'pace-progress/themes/blue/pace-theme-minimal.css'; import '@/assets/css/superflat_redash.css'; diff --git a/client/app/pages/dashboards/dashboard.html b/client/app/pages/dashboards/dashboard.html index 68cc3eaa6..74e04a8e8 100644 --- a/client/app/pages/dashboards/dashboard.html +++ b/client/app/pages/dashboards/dashboard.html @@ -85,7 +85,8 @@