From 43cd24927f9c58760e331e41d14e25ebf777d076 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Thu, 1 Mar 2018 14:46:53 +0200 Subject: [PATCH 1/2] gridsterAutoHeight: disable auto-height when widget resized by user --- client/app/directives/gridster-auto-height.js | 44 ++++++++++++------- client/app/pages/dashboards/dashboard.js | 1 + client/app/services/widget.js | 4 ++ client/app/visualizations/table/index.js | 2 +- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/client/app/directives/gridster-auto-height.js b/client/app/directives/gridster-auto-height.js index 639786ec..4754c2a5 100644 --- a/client/app/directives/gridster-auto-height.js +++ b/client/app/directives/gridster-auto-height.js @@ -1,15 +1,32 @@ import * as _ from 'underscore'; import { requestAnimationFrame } from './utils'; -function gridsterAutoHeight($timeout) { +function gridsterAutoHeight($timeout, $parse) { return { restrict: 'A', require: 'gridsterItem', link($scope, $element, attr, controller) { - let destroyed = false; + let autoSized = true; + + const itemGetter = $parse(attr.gridsterItem); + + $scope.$watch(attr.gridsterItem, () => { + const item = _.extend({}, itemGetter($scope)); + if (!autoSized) { + item.autoHeight = false; + } + if (item.autoHeight) { + $element.addClass('gridster-auto-height-enabled'); + } else { + $element.removeClass('gridster-auto-height-enabled'); + } + autoSized = false; + }, true); function updateHeight() { - if (controller.gridster) { + const item = _.extend({}, itemGetter($scope)); + + if (controller.gridster && item.autoHeight) { const wrapper = $element[0]; // Query element, but keep selector order const element = _.chain(attr.gridsterAutoHeight.split(',')) @@ -31,25 +48,22 @@ function gridsterAutoHeight($timeout) { const additionalHeight = 100 + _.last(controller.gridster.margins); const contentsHeight = childrenBounds.bottom - childrenBounds.top; $timeout(() => { - controller.sizeY = Math.ceil((contentsHeight + additionalHeight) / + const sizeY = Math.ceil((contentsHeight + additionalHeight) / controller.gridster.curRowHeight); + if (controller.sizeY !== sizeY) { + autoSized = true; + controller.sizeY = sizeY; + } else { + autoSized = false; + } }); } - if (!destroyed) { - requestAnimationFrame(updateHeight); - } + requestAnimationFrame(updateHeight); } } - if (controller.sizeY < 0) { - $element.addClass('gridster-auto-height-enabled'); - updateHeight(); - - $scope.$on('$destroy', () => { - destroyed = true; - }); - } + updateHeight(); }, }; } diff --git a/client/app/pages/dashboards/dashboard.js b/client/app/pages/dashboards/dashboard.js index fd9b9a8d..41b617e0 100644 --- a/client/app/pages/dashboards/dashboard.js +++ b/client/app/pages/dashboards/dashboard.js @@ -242,6 +242,7 @@ function DashboardCtrl( // Clear saved data and save layout _.each(this.dashboard.widgets, (widget) => { widget.$savedPosition = undefined; + widget.options.position.autoHeight = false; }); saveDashboardLayout(); } else { diff --git a/client/app/services/widget.js b/client/app/services/widget.js index 1e5789fd..e4ba2f4d 100644 --- a/client/app/services/widget.js +++ b/client/app/services/widget.js @@ -106,6 +106,10 @@ function Widget($resource, $http, Query, Visualization, dashboardGridOptions) { pick(widget.options.position, ['col', 'row', 'sizeX', 'sizeY']), ); + if (widget.options.position.sizeY < 0) { + widget.options.position.autoHeight = true; + } + return new WidgetResource(widget); } diff --git a/client/app/visualizations/table/index.js b/client/app/visualizations/table/index.js index 3f87c386..60cf411c 100644 --- a/client/app/visualizations/table/index.js +++ b/client/app/visualizations/table/index.js @@ -20,7 +20,7 @@ const DISPLAY_AS_OPTIONS = [ const DEFAULT_OPTIONS = { itemsPerPage: 15, defaultRows: -1, - defaultColumns: 4, + defaultColumns: 3, minColumns: 2, }; From 9295a9d8fb140254e9055fe947838c6275080501 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Thu, 1 Mar 2018 16:02:49 +0200 Subject: [PATCH 2/2] Dashboard: save only changed widgets --- client/app/directives/gridster-auto-height.js | 11 ++- client/app/pages/dashboards/dashboard.js | 84 +++++++++++++------ client/app/services/widget.js | 2 +- 3 files changed, 66 insertions(+), 31 deletions(-) diff --git a/client/app/directives/gridster-auto-height.js b/client/app/directives/gridster-auto-height.js index 4754c2a5..1fd34924 100644 --- a/client/app/directives/gridster-auto-height.js +++ b/client/app/directives/gridster-auto-height.js @@ -10,10 +10,15 @@ function gridsterAutoHeight($timeout, $parse) { const itemGetter = $parse(attr.gridsterItem); - $scope.$watch(attr.gridsterItem, () => { + $scope.$watch(attr.gridsterItem, (newValue, oldValue) => { const item = _.extend({}, itemGetter($scope)); - if (!autoSized) { - item.autoHeight = false; + if (_.isObject(newValue) && _.isObject(oldValue)) { + if ((newValue.sizeY !== oldValue.sizeY) && !autoSized) { + item.autoHeight = false; + if (_.isFunction(itemGetter.assign)) { + itemGetter.assign($scope, item); + } + } } if (item.autoHeight) { $element.addClass('gridster-auto-height-enabled'); diff --git a/client/app/pages/dashboards/dashboard.js b/client/app/pages/dashboards/dashboard.js index 41b617e0..15554741 100644 --- a/client/app/pages/dashboards/dashboard.js +++ b/client/app/pages/dashboards/dashboard.js @@ -5,6 +5,30 @@ import template from './dashboard.html'; import shareDashboardTemplate from './share-dashboard.html'; import './dashboard.less'; +function isWidgetPositionChanged(oldPosition, newPosition) { + const fields = ['col', 'row', 'sizeX', 'sizeY', 'autoHeight']; + oldPosition = _.pick(oldPosition, fields); + newPosition = _.pick(newPosition, fields); + return !!_.find(fields, key => newPosition[key] !== oldPosition[key]); +} + +function collectWidgetPositions(widgets) { + return _.chain(widgets) + .map(widget => [widget.id, _.clone(widget.options.position)]) + .object() + .value(); +} + +function getWidgetsWithChangedPositions(widgets, savedPositions) { + return _.filter(widgets, (widget) => { + const savedPosition = savedPositions[widget.id]; + if (!_.isObject(savedPosition)) { + return true; + } + return isWidgetPositionChanged(savedPosition, widget.options.position); + }); +} + function DashboardCtrl( $rootScope, $routeParams, @@ -22,7 +46,11 @@ function DashboardCtrl( toastr, ) { this.saveInProgress = false; - const saveDashboardLayout = () => { + + // This variable should always be in sync with widgets + let savedWidgetPositions = {}; + + const saveDashboardLayout = (widgets) => { if (!this.dashboard.canEdit()) { return; } @@ -33,7 +61,7 @@ function DashboardCtrl( this.dashboardGridOptions.draggable.enabled = false; this.dashboardGridOptions.resizable.enabled = false; return $q - .all(_.map(this.dashboard.widgets, widget => widget.$save())) + .all(_.map(widgets, widget => widget.$save())) .then(() => { if (showMessages) { toastr.success('Changes saved.'); @@ -181,6 +209,8 @@ function DashboardCtrl( $location.search('edit', null); this.editLayout(true); } + + savedWidgetPositions = collectWidgetPositions(dashboard.widgets); }, (rejection) => { const statusGroup = Math.floor(rejection.status / 100); @@ -233,27 +263,26 @@ function DashboardCtrl( if (enableEditing) { if (!this.layoutEditing) { // Save current positions of widgets - _.each(this.dashboard.widgets, (widget) => { - widget.$savedPosition = _.clone(widget.options.position); - }); + savedWidgetPositions = collectWidgetPositions(this.dashboard.widgets); } } else { if (applyChanges) { - // Clear saved data and save layout - _.each(this.dashboard.widgets, (widget) => { - widget.$savedPosition = undefined; - widget.options.position.autoHeight = false; + const changedWidgets = getWidgetsWithChangedPositions( + this.dashboard.widgets, + savedWidgetPositions, + ); + saveDashboardLayout(changedWidgets).finally(() => { + savedWidgetPositions = collectWidgetPositions(this.dashboard.widgets); }); - saveDashboardLayout(); } else { // Revert changes _.each(this.dashboard.widgets, (widget) => { - if (_.isObject(widget.$savedPosition)) { - widget.options.position = widget.$savedPosition; + if (_.isObject(savedWidgetPositions[widget.id])) { + widget.options.position = savedWidgetPositions[widget.id]; } - widget.$savedPosition = undefined; }); } + savedWidgetPositions = collectWidgetPositions(this.dashboard.widgets); } this.layoutEditing = enableEditing; @@ -298,19 +327,14 @@ function DashboardCtrl( }) .result.then(() => { this.extractGlobalParameters(); - if (this.layoutEditing) { - // Save position of newly added widget (but not entire layout) - const widget = _.last(this.dashboard.widgets); - if (_.isObject(widget)) { - return widget.$save().then(() => { - if (this.layoutEditing) { - widget.$savedPosition = _.clone(widget.options.position); - } - }); - } - } else { - // Update entire layout - return saveDashboardLayout(); + // Save position of newly added widget (but not entire layout) + const widget = _.last(this.dashboard.widgets); + if (_.isObject(widget)) { + return widget.$save().then(() => { + if (this.layoutEditing) { + savedWidgetPositions[widget.id] = _.clone(widget.options.position); + } + }); } }); }; @@ -321,7 +345,13 @@ function DashboardCtrl( // We need to wait a bit for `angular-gridster` before it updates widgets, // and only then save new layout $timeout(() => { - saveDashboardLayout(); + const changedWidgets = getWidgetsWithChangedPositions( + this.dashboard.widgets, + savedWidgetPositions, + ); + saveDashboardLayout(changedWidgets).finally(() => { + savedWidgetPositions = collectWidgetPositions(this.dashboard.widgets); + }); }, 50); } }; diff --git a/client/app/services/widget.js b/client/app/services/widget.js index e4ba2f4d..779e9d79 100644 --- a/client/app/services/widget.js +++ b/client/app/services/widget.js @@ -103,7 +103,7 @@ function Widget($resource, $http, Query, Visualization, dashboardGridOptions) { widget.options.position = extend( {}, visualizationOptions, - pick(widget.options.position, ['col', 'row', 'sizeX', 'sizeY']), + pick(widget.options.position, ['col', 'row', 'sizeX', 'sizeY', 'autoHeight']), ); if (widget.options.position.sizeY < 0) {