Skip to content

Commit

Permalink
#335 Better dashboard editor
Browse files Browse the repository at this point in the history
  • Loading branch information
kravets-levko committed Nov 6, 2017
1 parent 900d558 commit 6a7d229
Show file tree
Hide file tree
Showing 18 changed files with 342 additions and 118 deletions.
42 changes: 40 additions & 2 deletions client/app/assets/css/superflat_redash.css
Original file line number Diff line number Diff line change
Expand Up @@ -6209,7 +6209,7 @@ button.close {
/* --------------------------------------------------------
Container
-----------------------------------------------------------*/
/* --------------------------------------------------------
/* --------------------------------------------------------
Template Variables
-----------------------------------------------------------*/
/* --------------------------------------------------------
Expand Down Expand Up @@ -6259,7 +6259,7 @@ button.close {
Carousel
-----------------------------------------------------------*/
/* --------------------------------------------------------
Modal
Modal
-----------------------------------------------------------*/
/* --------------------------------------------------------
Tooltips
Expand Down Expand Up @@ -7913,6 +7913,44 @@ fieldset[disabled] .btn-inverse.active {
position: relative;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.07);
}

.dashboard-wrapper {
margin: -7px;
}
.dashboard-wrapper .tile {
position: absolute;
left: 7px;
top: 7px;
right: 7px;
bottom: 7px;
overflow: auto;
margin: 0;
padding: 0 0 40px 0;
}
.dashboard-wrapper.gridster-mobile {
margin: 0;
}
.dashboard-wrapper.gridster-mobile .gridster-item {
margin-left: 0 !important;
margin-right: 0 !important;
}
.dashboard-wrapper.gridster-mobile .tile {
display: block;
position: static;
height: auto;
margin-bottom: 15px;
}

.dashboard-widget-bottom-panel {
line-height: 28px;
}
.dashboard-wrapper .dashboard-widget-bottom-panel {
position: absolute;
left: 0;
right: 0;
bottom: 0;
}

.tile[class*="bg-"] {
color: #fff;
}
Expand Down
6 changes: 2 additions & 4 deletions client/app/components/dashboards/add-widget-dialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ <h4 class="modal-title">Add Widget</h4>
</div>
</div>

<div class="form-group">
<label for="">Widget Size</label>
<select class="form-control" ng-model="$ctrl.widgetSize"
ng-options="c.value as c.name for c in $ctrl.widgetSizes"></select>
<div class="form-group" ng-if="$ctrl.isTextBox()">
<label><input type="checkbox" ng-model="$ctrl.isHidden">&nbsp;Hidden</label>
</div>
</div>

Expand Down
53 changes: 30 additions & 23 deletions client/app/components/dashboards/add-widget-dialog.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as _ from 'underscore';
import template from './add-widget-dialog.html';

const AddWidgetDialog = {
Expand All @@ -12,21 +13,13 @@ const AddWidgetDialog = {

this.dashboard = this.resolve.dashboard;
this.saveInProgress = false;
this.widgetSize = 1;
this.selectedVis = null;
this.query = {};
this.selected_query = undefined;
this.text = '';
this.existing_text = '';
this.new_text = '';
this.widgetSizes = [{
name: 'Regular',
value: 1,
}, {
name: 'Double',
value: 2,
}];

this.isHidden = false;
this.type = 'visualization';

this.trustAsHtml = html => $sce.trustAsHtml(html);
Expand All @@ -35,11 +28,6 @@ const AddWidgetDialog = {

this.setType = (type) => {
this.type = type;
if (type === 'textbox') {
this.widgetSizes.push({ name: 'Hidden', value: 0 });
} else if (this.widgetSizes.length > 2) {
this.widgetSizes.pop();
}
};

this.onQuerySelect = () => {
Expand Down Expand Up @@ -72,21 +60,40 @@ const AddWidgetDialog = {
const widget = new Widget({
visualization_id: this.selectedVis && this.selectedVis.id,
dashboard_id: this.dashboard.id,
options: {},
width: this.widgetSize,
options: {
isHidden: this.isTextBox() && this.isHidden,
position: {
// Place new widget below all others
col: 0,
row: _.chain(this.dashboard.widgets)
.map((w) => {
let temp = _.extend({}, w.options);
temp = _.extend({}, temp.position);
const row = parseInt(temp.row, 10);
const height = parseInt(temp.sizeY, 10);

let result = 0;
if (isFinite(row)) {
result += row;
}
if (isFinite(height)) {
result += height;
}
return result;
})
.max()
.value(),
// Auto-height by default
sizeY: -1,
},
},
text: this.text,
});

widget.$save().then((response) => {
// update dashboard layout
this.dashboard.layout = response.layout;
this.dashboard.version = response.version;
const newWidget = new Widget(response.widget);
if (response.new_row) {
this.dashboard.widgets.push([newWidget]);
} else {
this.dashboard.widgets[this.dashboard.widgets.length - 1].push(newWidget);
}
this.dashboard.widgets.push(new Widget(response.widget));
this.close();
}).catch(() => {
toastr.error('Widget can not be added');
Expand Down
8 changes: 0 additions & 8 deletions client/app/components/dashboards/edit-dashboard-dialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,6 @@ <h4 class="modal-title">Edit: {{$ctrl.dashboard.name}}</h4>
Use Dashboard Level Filters
</label>
</p>

<div gridster="$ctrl.gridsterOptions" ng-if="$ctrl.items | notEmpty">
<ul>
<li gridster-item="item" ng-repeat="item in $ctrl.items" class="widget panel panel-default gs-w">
<div class="heading">{{item.name}}</div>
</li>
</ul>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" ng-disabled="$ctrl.saveInProgress" ng-click="$ctrl.dismiss()">Close</button>
Expand Down
32 changes: 1 addition & 31 deletions client/app/components/dashboards/edit-dashboard-dialog.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isEmpty, sortBy } from 'underscore';
import { isEmpty } from 'underscore';
import template from './edit-dashboard-dialog.html';

const EditDashboardDialog = {
Expand Down Expand Up @@ -28,47 +28,17 @@ const EditDashboardDialog = {
},
};

this.items = [];

if (this.dashboard.widgets) {
this.dashboard.widgets.forEach((row, rowIndex) => {
row.forEach((widget, colIndex) => {
this.items.push({
id: widget.id,
col: colIndex,
row: rowIndex,
sizeY: 1,
sizeX: widget.width,
name: widget.getName(), // visualization.query.name
});
});
});
}

this.isFormValid = () => !isEmpty(this.dashboard.name);

this.saveDashboard = () => {
this.saveInProgress = true;

if (this.dashboard.id) {
const layout = [];
const sortedItems = sortBy(this.items, item => item.row * 10 + item.col);

sortedItems.forEach((item) => {
layout[item.row] = layout[item.row] || [];
if (item.col > 0 && layout[item.row][item.col - 1] === undefined) {
layout[item.row][item.col - 1] = item.id;
} else {
layout[item.row][item.col] = item.id;
}
});

const request = {
slug: this.dashboard.id,
name: this.dashboard.name,
version: this.dashboard.version,
dashboard_filters_enabled: this.dashboard.dashboard_filters_enabled,
layout: JSON.stringify(layout),
};

Dashboard.save(request, (dashboard) => {
Expand Down
4 changes: 2 additions & 2 deletions client/app/components/dashboards/widget.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="col-lg-{{$ctrl.widget.width | colWidth}}">
<div>
<div class="tile" ng-if="$ctrl.type=='visualization'">
<div class="t-header widget">
<div class="th-title">
Expand Down Expand Up @@ -39,7 +39,7 @@
</div>
</div>

<div class="p-5 clearfix" style="line-height:28px;">
<div class="dashboard-widget-bottom-panel p-5 clearfix">
<span class="small hidden-print">Updated: <span am-time-ago="$ctrl.queryResult.getUpdatedAt()"></span></span>
<span class="visible-print">
Updated: {{$ctrl.queryResult.getUpdatedAt() | dateTime}}
Expand Down
10 changes: 2 additions & 8 deletions client/app/components/dashboards/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser)

this.localParametersDefs = () => {
if (!this.localParameters) {
this.localParameters = this.widget.query.getParametersDefs().filter(p => !p.global);
this.localParameters = this.widget.getQuery().getParametersDefs().filter(p => !p.global);
}
return this.localParameters;
};
Expand All @@ -60,14 +60,8 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser)
Events.record('delete', 'widget', this.widget.id);

this.widget.$delete((response) => {
this.dashboard.widgets =
this.dashboard.widgets.map(row => row.filter(widget => widget.id !== undefined));

this.dashboard.widgets = this.dashboard.widgets.filter(row => row.length > 0);

this.dashboard.layout = response.layout;
this.dashboard.widgets = this.dashboard.widgets.filter(widget => widget.id !== undefined);
this.dashboard.version = response.version;

if (this.deleted) {
this.deleted({});
}
Expand Down
34 changes: 34 additions & 0 deletions client/app/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,42 @@ const requirements = [
ngGridster.name,
];

const dashboardGridOptions = {
columns: 6,
pushing: true,
floating: true,
swapping: true,
width: 'auto',
colWidth: 'auto',
rowHeight: 100,
margins: [0, 0],
outerMargin: false,
sparse: false,
isMobile: false,
mobileBreakPoint: 1200,
mobileModeEnabled: true,
minColumns: 1,
minRows: 1,
maxRows: 100,
defaultSizeX: 3,
defaultSizeY: 2,
minSizeX: 1,
maxSizeX: null,
minSizeY: 1,
maxSizeY: null,
resizable: {
enabled: true,
handles: ['n', 'e', 's', 'w', 'ne', 'se', 'sw', 'nw'],
},
draggable: {
enabled: true, // whether dragging items is supported
},
};

const ngModule = angular.module('app', requirements);

ngModule.constant('dashboardGridOptions', dashboardGridOptions);

function registerAll(context) {
const modules = context
.keys()
Expand Down
42 changes: 42 additions & 0 deletions client/app/directives/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import debug from 'debug';

const logger = debug('redash:directives');

const requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame;

function compareTo() {
return {
require: 'ngModel',
Expand Down Expand Up @@ -70,9 +75,46 @@ function title($rootScope, Title) {
};
}

function gridsterAutoHeight($timeout) {
return {
restrict: 'A',
require: 'gridsterItem',
link($scope, $element, attr, controller) {
let destroyed = false;

function updateHeight() {
const paddings = 15;
const element = $element[0].querySelector(attr.gridsterAutoHeight);
if (element) {
if (element.scrollHeight > element.offsetHeight) {
let h = element.scrollHeight;
h = Math.ceil((h + paddings) / controller.gridster.curRowHeight);
$timeout(() => {
controller.sizeY = h;
});
}
}

if (!destroyed) {
requestAnimationFrame(updateHeight);
}
}

if (controller.sizeY < 0) {
updateHeight();

$scope.$on('$destroy', () => {
destroyed = true;
});
}
},
};
}

export default function init(ngModule) {
ngModule.factory('Title', TitleService);
ngModule.directive('title', title);
ngModule.directive('compareTo', compareTo);
ngModule.directive('autofocus', autofocus);
ngModule.directive('gridsterAutoHeight', gridsterAutoHeight);
}
Loading

3 comments on commit 6a7d229

@vabanin
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @kravets-levko

I very excited about your work for refinement dashboard displaying and layout management. Thank you!

I found following little issues:

  • pivot table visualizations can't be scrolled now in widgets of dashboards. This is very inconvenient for wide or long pivot tables. In my fork of redash I fixed it by commenting all lines in method this.getWidgetStyles in client/app/components/dashboards/widget.js

  • it seems that appeared some length-limit for dashboard page - I place more than 10 full-wide visualizations in one dashboard and can't make a correct place for last visualizations, they are overlapped.

Thank you.

@kravets-levko
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @vabanin! Can you please create an issue - it's hard to track such comments for commits. Thanks! And thanks a lot for your help with testing!

@vabanin
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kravets-levko

Ok! I've created #2108

Please sign in to comment.