mirror of
https://github.com/valitydev/redash.git
synced 2024-11-06 17:15:17 +00:00
Allow to specify default size for each visualization type
This commit is contained in:
parent
457b8d9d8a
commit
b26284933c
@ -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 {
|
||||
|
@ -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;
|
||||
});
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
dynamic-table > div {
|
||||
overflow: auto;
|
||||
/*overflow: auto;*/
|
||||
}
|
||||
|
||||
th.sortable-column {
|
||||
|
@ -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;
|
||||
|
@ -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 || {};
|
||||
|
@ -122,6 +122,8 @@ export default function init(ngModule) {
|
||||
stringDecimal: 0,
|
||||
stringDecChar: '.',
|
||||
stringThouSep: ',',
|
||||
defaultColumns: 2,
|
||||
defaultRows: 6,
|
||||
};
|
||||
|
||||
VisualizationProvider.registerVisualization({
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user