Allow to specify default size for each visualization type

This commit is contained in:
Levko Kravets 2017-11-12 20:15:21 +02:00
parent 457b8d9d8a
commit b26284933c
8 changed files with 117 additions and 71 deletions

View File

@ -321,7 +321,7 @@ to add those CSS styles here. */
}
pivot-table-renderer > table, grid-renderer > div, visualization-renderer > div {
overflow: auto;
/*overflow: auto;*/
}
counter-renderer {

View File

@ -8,7 +8,7 @@ const AddWidgetDialog = {
close: '&',
dismiss: '&',
},
controller($sce, toastr, Query, Widget, dashboardGridOptions) {
controller($sce, toastr, Query, Widget) {
'ngInject';
this.dashboard = this.resolve.dashboard;
@ -58,74 +58,45 @@ const AddWidgetDialog = {
this.saveWidget = () => {
this.saveInProgress = true;
const width = dashboardGridOptions.defaultSizeX;
// Find first free row for each column
const bottomLine = _.chain(this.dashboard.widgets)
.map((w) => {
const options = _.extend({}, w.options);
const position = _.extend({ row: 0, sizeY: 0 }, options.position);
return {
left: position.col,
top: position.row,
right: position.col + position.sizeX,
bottom: position.row + position.sizeY,
width: position.sizeX,
height: position.sizeY,
};
})
.reduce((result, item) => {
const from = Math.max(item.left, 0);
const to = Math.min(item.right, result.length + 1);
for (let i = from; i < to; i += 1) {
result[i] = Math.max(result[i], item.bottom);
}
return result;
}, _.map(new Array(dashboardGridOptions.columns), _.constant(0)))
.value();
// Go through columns, pick them by count necessary to hold new block,
// and calculate bottom-most free row per group.
// Choose group with the top-most free row (comparing to other groups)
const position = _.chain(_.range(0, dashboardGridOptions.columns - width + 1))
.map(col => ({
col,
row: _.chain(bottomLine)
.slice(col, col + width)
.max()
.value(),
}))
.sortBy('row')
.first()
.value();
const widget = new Widget({
visualization_id: this.selectedVis && this.selectedVis.id,
dashboard_id: this.dashboard.id,
options: {
isHidden: this.isTextBox() && this.isHidden,
position: {
// Place new widget below all others
col: position.col,
row: position.row,
sizeX: width,
// Auto-height by default
sizeY: -1,
},
position: {},
},
text: this.text,
});
widget.$save().then((response) => {
// update dashboard layout
this.dashboard.version = response.version;
this.dashboard.widgets.push(new Widget(response.widget));
this.close();
}).catch(() => {
toastr.error('Widget can not be added');
}).finally(() => {
this.saveInProgress = false;
});
widget.$save()
.then((response) => {
// update dashboard layout
this.dashboard.version = response.version;
response.widget.options = _.extend(
{},
response.widget.options,
{ position: {} },
);
const w = new Widget(response.widget);
const position = this.dashboard.calculateNewWidgetPosition(w);
w.options.position.col = position.col;
w.options.position.row = position.row;
// Save it with new position
return w.$save().then(() => w);
})
.then((w) => {
this.dashboard.widgets.push(w);
this.close();
})
.catch(() => {
toastr.error('Widget can not be added');
})
.finally(() => {
this.saveInProgress = false;
});
};
},
};

View File

@ -1,5 +1,5 @@
dynamic-table > div {
overflow: auto;
/*overflow: auto;*/
}
th.sortable-column {

View File

@ -1,18 +1,18 @@
import { map, flatten, extend, isArray } from 'underscore';
import * as _ from 'underscore';
function Dashboard($resource, $http, currentUser, Widget, dashboardGridOptions) {
function prepareDashboardWidgets(widgets) {
if (isArray(widgets) && (widgets.length > 0) && isArray(widgets[0])) {
if (_.isArray(widgets) && (widgets.length > 0) && _.isArray(widgets[0])) {
// Dashboard v1 processing
// v1 dashboard has two columns, and widget can occupy one of them or both;
// this means, that there can be at most two widgets per row.
// Here we will map gridster columns and rows to v1-style grid
const dashboardV1ColumnSize = Math.round(dashboardGridOptions.columns / 2);
widgets = map(
widgets = _.map(
widgets,
(row, rowIndex) => map(row, (widget, widgetIndex) => {
(row, rowIndex) => _.map(row, (widget, widgetIndex) => {
widget.options = widget.options || {};
widget.options.position = extend({}, {
widget.options.position = _.extend({}, {
row: rowIndex,
col: widgetIndex * dashboardV1ColumnSize,
sizeX: dashboardV1ColumnSize * widget.width,
@ -23,7 +23,7 @@ function Dashboard($resource, $http, currentUser, Widget, dashboardGridOptions)
);
}
return map(flatten(widgets), widget => new Widget(widget));
return _.map(_.flatten(widgets), widget => new Widget(widget));
}
function transformSingle(dashboard) {
@ -32,7 +32,7 @@ function Dashboard($resource, $http, currentUser, Widget, dashboardGridOptions)
}
const transform = $http.defaults.transformResponse.concat((data) => {
if (isArray(data)) {
if (_.isArray(data)) {
data.forEach(transformSingle);
} else {
transformSingle(data);
@ -56,6 +56,52 @@ function Dashboard($resource, $http, currentUser, Widget, dashboardGridOptions)
return currentUser.canEdit(this) || this.can_edit;
};
resource.prototype.calculateNewWidgetPosition = function calculateNewWidgetPosition(widget) {
const width = (_.extend(
{ sizeX: dashboardGridOptions.defaultSizeX },
_.extend({}, widget.options).position,
)).sizeX;
// Find first free row for each column
const bottomLine = _.chain(this.widgets)
.map((w) => {
const options = _.extend({}, w.options);
const position = _.extend({ row: 0, sizeY: 0 }, options.position);
return {
left: position.col,
top: position.row,
right: position.col + position.sizeX,
bottom: position.row + position.sizeY,
width: position.sizeX,
height: position.sizeY,
};
})
.reduce((result, item) => {
const from = Math.max(item.left, 0);
const to = Math.min(item.right, result.length + 1);
for (let i = from; i < to; i += 1) {
result[i] = Math.max(result[i], item.bottom);
}
return result;
}, _.map(new Array(dashboardGridOptions.columns), _.constant(0)))
.value();
// Go through columns, pick them by count necessary to hold new block,
// and calculate bottom-most free row per group.
// Choose group with the top-most free row (comparing to other groups)
return _.chain(_.range(0, dashboardGridOptions.columns - width + 1))
.map(col => ({
col,
row: _.chain(bottomLine)
.slice(col, col + width)
.max()
.value(),
}))
.sortBy('row')
.first()
.value();
};
resource.prepareDashboardWidgets = prepareDashboardWidgets;
return resource;

View File

@ -35,7 +35,10 @@ function Widget($resource, $http, Query, Visualization, dashboardGridOptions) {
function WidgetConstructor(widget) {
widget.width = 1; // Backward compatibility, user on back-end
const visualizationOptions = {};
const visualizationOptions = {
sizeX: Math.round(dashboardGridOptions.columns / 2),
sizeY: -1, // auto-height
};
const visualization = widget.visualization ?
Visualization.visualizations[widget.visualization.type] : null;
if (isObject(visualization)) {
@ -65,6 +68,16 @@ function Widget($resource, $http, Query, Visualization, dashboardGridOptions) {
if (isFinite(maxRows) && (maxRows >= 0)) {
visualizationOptions.maxSizeY = maxRows;
}
// Default dimensions
const defaultWidth = parseInt(options.defaultColumns, 10);
if (isFinite(defaultWidth) && (defaultWidth > 0)) {
visualizationOptions.sizeX = defaultWidth;
}
const defaultHeight = parseInt(options.defaultRows, 10);
if (isFinite(defaultHeight) && (defaultHeight > 0)) {
visualizationOptions.sizeY = defaultHeight;
}
}
widget.options = widget.options || {};

View File

@ -122,6 +122,8 @@ export default function init(ngModule) {
stringDecimal: 0,
stringDecChar: '.',
stringThouSep: ',',
defaultColumns: 2,
defaultRows: 6,
};
VisualizationProvider.registerVisualization({

View File

@ -77,11 +77,17 @@ function GridRenderer(clientConfig) {
export default function init(ngModule) {
ngModule.config((VisualizationProvider) => {
const defaultOptions = {
defaultRows: 15,
defaultColumns: 4,
};
VisualizationProvider.registerVisualization({
type: 'TABLE',
name: 'Table',
renderTemplate: '<grid-renderer options="visualization.options" query-result="queryResult"></grid-renderer>',
skipTypes: true,
defaultOptions,
});
});
ngModule.directive('gridRenderer', GridRenderer);

View File

@ -123,6 +123,10 @@ var config = {
]
},
devtool: 'cheap-eval-module-source-map',
stats: {
modules: false,
chunkModules: false,
},
devServer: {
inline: true,
historyApiFallback: true,
@ -134,7 +138,11 @@ var config = {
target: redashBackend + '/',
changeOrigin: true,
secure: false
}]
}],
stats: {
modules: false,
chunkModules: false,
},
}
};