blob: dad66e1adb4b12e743fa10d822d17838bed1a722 [file] [log] [blame]
import React from 'react';
import PropTypes from 'prop-types';
import { Responsive, WidthProvider } from 'react-grid-layout';
import GridCell from './GridCell';
import { getExploreUrl } from '../../explore/exploreUtils';
require('react-grid-layout/css/styles.css');
require('react-resizable/css/styles.css');
const ResponsiveReactGridLayout = WidthProvider(Responsive);
const propTypes = {
dashboard: PropTypes.object.isRequired,
datasources: PropTypes.object,
charts: PropTypes.object.isRequired,
filters: PropTypes.object,
timeout: PropTypes.number,
onChange: PropTypes.func,
getFormDataExtra: PropTypes.func,
fetchSlice: PropTypes.func,
saveSlice: PropTypes.func,
removeSlice: PropTypes.func,
removeChart: PropTypes.func,
updateDashboardLayout: PropTypes.func,
toggleExpandSlice: PropTypes.func,
addFilter: PropTypes.func,
getFilters: PropTypes.func,
clearFilter: PropTypes.func,
removeFilter: PropTypes.func,
editMode: PropTypes.bool.isRequired,
};
const defaultProps = {
onChange: () => ({}),
getFormDataExtra: () => ({}),
fetchSlice: () => ({}),
saveSlice: () => ({}),
removeSlice: () => ({}),
removeChart: () => ({}),
updateDashboardLayout: () => ({}),
toggleExpandSlice: () => ({}),
addFilter: () => ({}),
getFilters: () => ({}),
clearFilter: () => ({}),
removeFilter: () => ({}),
};
class GridLayout extends React.Component {
constructor(props) {
super(props);
this.onResizeStop = this.onResizeStop.bind(this);
this.onDragStop = this.onDragStop.bind(this);
this.forceRefresh = this.forceRefresh.bind(this);
this.removeSlice = this.removeSlice.bind(this);
this.updateSliceName = this.props.dashboard.dash_edit_perm ?
this.updateSliceName.bind(this) : null;
}
onResizeStop(layout) {
this.props.updateDashboardLayout(layout);
this.props.onChange();
}
onDragStop(layout) {
this.props.updateDashboardLayout(layout);
this.props.onChange();
}
getWidgetId(slice) {
return 'widget_' + slice.slice_id;
}
getWidgetHeight(slice) {
const widgetId = this.getWidgetId(slice);
if (!widgetId || !this.refs[widgetId]) {
return 400;
}
return this.refs[widgetId].offsetHeight;
}
getWidgetWidth(slice) {
const widgetId = this.getWidgetId(slice);
if (!widgetId || !this.refs[widgetId]) {
return 400;
}
return this.refs[widgetId].offsetWidth;
}
findSliceIndexById(sliceId) {
return this.props.dashboard.slices
.map(slice => (slice.slice_id)).indexOf(sliceId);
}
forceRefresh(sliceId) {
return this.props.fetchSlice(this.props.charts['slice_' + sliceId], true);
}
removeSlice(slice) {
if (!slice) {
return;
}
// remove slice dashbaord and charts
this.props.removeSlice(slice);
this.props.removeChart(this.props.charts['slice_' + slice.slice_id].chartKey);
this.props.onChange();
}
updateSliceName(sliceId, sliceName) {
const index = this.findSliceIndexById(sliceId);
if (index === -1) {
return;
}
const currentSlice = this.props.dashboard.slices[index];
if (currentSlice.slice_name === sliceName) {
return;
}
this.props.saveSlice(currentSlice, sliceName);
}
isExpanded(slice) {
return this.props.dashboard.metadata.expanded_slices &&
this.props.dashboard.metadata.expanded_slices[slice.slice_id];
}
render() {
const cells = this.props.dashboard.slices.map((slice) => {
const chartKey = `slice_${slice.slice_id}`;
const currentChart = this.props.charts[chartKey];
const queryResponse = currentChart.queryResponse || {};
return (
<div
id={'slice_' + slice.slice_id}
key={slice.slice_id}
data-slice-id={slice.slice_id}
className={`widget ${slice.form_data.viz_type}`}
ref={this.getWidgetId(slice)}
>
<GridCell
slice={slice}
chartKey={chartKey}
datasource={this.props.datasources[slice.form_data.datasource]}
filters={this.props.filters}
formData={this.props.getFormDataExtra(slice)}
timeout={this.props.timeout}
widgetHeight={this.getWidgetHeight(slice)}
widgetWidth={this.getWidgetWidth(slice)}
exploreChartUrl={getExploreUrl(this.props.getFormDataExtra(slice))}
exportCSVUrl={getExploreUrl(this.props.getFormDataExtra(slice), 'csv')}
isExpanded={!!this.isExpanded(slice)}
isLoading={currentChart.chartStatus === 'loading'}
isCached={queryResponse.is_cached}
cachedDttm={queryResponse.cached_dttm}
toggleExpandSlice={this.props.toggleExpandSlice}
forceRefresh={this.forceRefresh}
removeSlice={this.removeSlice}
updateSliceName={this.updateSliceName}
addFilter={this.props.addFilter}
getFilters={this.props.getFilters}
clearFilter={this.props.clearFilter}
removeFilter={this.props.removeFilter}
editMode={this.props.editMode}
/>
</div>);
});
return (
<ResponsiveReactGridLayout
className="layout"
layouts={{ lg: this.props.dashboard.layout }}
onResizeStop={this.onResizeStop}
onDragStop={this.onDragStop}
cols={{ lg: 12, md: 12, sm: 10, xs: 8, xxs: 6 }}
rowHeight={100}
autoSize
margin={[20, 20]}
useCSSTransforms
draggableHandle=".drag"
>
{cells}
</ResponsiveReactGridLayout>
);
}
}
GridLayout.propTypes = propTypes;
GridLayout.defaultProps = defaultProps;
export default GridLayout;