Url state -> dashboard model state sync starting to work
This commit is contained in:
@@ -13,6 +13,9 @@ import { DashboardModel } from '../../state/DashboardModel';
|
|||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
dashboard: DashboardModel | null;
|
dashboard: DashboardModel | null;
|
||||||
|
editview: string;
|
||||||
|
isEditing: boolean;
|
||||||
|
isFullscreen: boolean;
|
||||||
updateLocation: typeof updateLocation;
|
updateLocation: typeof updateLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,5 @@
|
|||||||
<i class="fa fa-reply"></i>
|
<i class="fa fa-reply"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<dashboard-search></dashboard-search>
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import { hot } from 'react-hot-loader';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
// Services & Utils
|
||||||
|
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { LoadingPlaceholder } from '@grafana/ui';
|
import { LoadingPlaceholder } from '@grafana/ui';
|
||||||
import { DashboardGrid } from '../dashgrid/DashboardGrid';
|
import { DashboardGrid } from '../dashgrid/DashboardGrid';
|
||||||
@@ -14,34 +17,45 @@ import { DashboardSettings } from '../components/DashboardSettings';
|
|||||||
// Redux
|
// Redux
|
||||||
import { initDashboard } from '../state/initDashboard';
|
import { initDashboard } from '../state/initDashboard';
|
||||||
import { setDashboardModel } from '../state/actions';
|
import { setDashboardModel } from '../state/actions';
|
||||||
|
import { updateLocation } from 'app/core/actions';
|
||||||
|
import { notifyApp } from 'app/core/actions';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
import { DashboardLoadingState } from 'app/types/dashboard';
|
import { DashboardLoadingState } from 'app/types/dashboard';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
panelId: string;
|
|
||||||
urlUid?: string;
|
urlUid?: string;
|
||||||
urlSlug?: string;
|
urlSlug?: string;
|
||||||
urlType?: string;
|
urlType?: string;
|
||||||
editview: string;
|
editview?: string;
|
||||||
|
urlPanelId?: string;
|
||||||
$scope: any;
|
$scope: any;
|
||||||
$injector: any;
|
$injector: any;
|
||||||
initDashboard: typeof initDashboard;
|
urlEdit: boolean;
|
||||||
setDashboardModel: typeof setDashboardModel;
|
urlFullscreen: boolean;
|
||||||
loadingState: DashboardLoadingState;
|
loadingState: DashboardLoadingState;
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel;
|
||||||
|
initDashboard: typeof initDashboard;
|
||||||
|
setDashboardModel: typeof setDashboardModel;
|
||||||
|
notifyApp: typeof notifyApp;
|
||||||
|
updateLocation: typeof updateLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
isSettingsOpening: boolean;
|
isSettingsOpening: boolean;
|
||||||
|
isEditing: boolean;
|
||||||
|
isFullscreen: boolean;
|
||||||
|
fullscreenPanel: PanelModel | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DashboardPage extends PureComponent<Props, State> {
|
export class DashboardPage extends PureComponent<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
isSettingsOpening: false,
|
isSettingsOpening: false,
|
||||||
isSettingsOpen: false,
|
isEditing: false,
|
||||||
|
isFullscreen: false,
|
||||||
|
fullscreenPanel: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -55,30 +69,66 @@ export class DashboardPage extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
const { dashboard, editview } = this.props;
|
const { dashboard, editview, urlEdit, urlFullscreen, urlPanelId } = this.props;
|
||||||
|
|
||||||
// when dashboard has loaded subscribe to somme events
|
if (!dashboard) {
|
||||||
if (prevProps.dashboard === null && dashboard) {
|
return;
|
||||||
dashboard.events.on('view-mode-changed', this.onViewModeChanged);
|
|
||||||
|
|
||||||
// set initial fullscreen class state
|
|
||||||
this.setPanelFullscreenClass();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle animation states when opening dashboard settings
|
||||||
if (!prevProps.editview && editview) {
|
if (!prevProps.editview && editview) {
|
||||||
this.setState({ isSettingsOpening: true });
|
this.setState({ isSettingsOpening: true });
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.setState({ isSettingsOpening: false});
|
this.setState({ isSettingsOpening: false });
|
||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // when dashboard has loaded subscribe to somme events
|
||||||
|
// if (prevProps.dashboard === null) {
|
||||||
|
// // set initial fullscreen class state
|
||||||
|
// this.setPanelFullscreenClass();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Sync url state with model
|
||||||
|
if (urlFullscreen !== dashboard.meta.isFullscreen || urlEdit !== dashboard.meta.isEditing) {
|
||||||
|
// entering fullscreen/edit mode
|
||||||
|
if (urlPanelId) {
|
||||||
|
const panel = dashboard.getPanelById(parseInt(urlPanelId, 10));
|
||||||
|
|
||||||
|
if (panel) {
|
||||||
|
dashboard.setViewMode(panel, urlFullscreen, urlEdit);
|
||||||
|
this.setState({ isEditing: urlEdit, isFullscreen: urlFullscreen, fullscreenPanel: panel });
|
||||||
|
} else {
|
||||||
|
this.handleFullscreenPanelNotFound(urlPanelId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// handle leaving fullscreen mode
|
||||||
|
if (this.state.fullscreenPanel) {
|
||||||
|
dashboard.setViewMode(this.state.fullscreenPanel, urlFullscreen, urlEdit);
|
||||||
|
}
|
||||||
|
this.setState({ isEditing: urlEdit, isFullscreen: urlFullscreen, fullscreenPanel: null });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPanelFullscreenClass(urlFullscreen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onViewModeChanged = () => {
|
handleFullscreenPanelNotFound(urlPanelId: string) {
|
||||||
this.setPanelFullscreenClass();
|
// Panel not found
|
||||||
};
|
this.props.notifyApp(createErrorNotification(`Panel with id ${urlPanelId} not found`));
|
||||||
|
// Clear url state
|
||||||
|
this.props.updateLocation({
|
||||||
|
query: {
|
||||||
|
edit: null,
|
||||||
|
fullscreen: null,
|
||||||
|
panelId: null,
|
||||||
|
},
|
||||||
|
partial: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setPanelFullscreenClass() {
|
setPanelFullscreenClass(isFullscreen: boolean) {
|
||||||
$('body').toggleClass('panel-in-fullscreen', this.props.dashboard.meta.fullscreen === true);
|
$('body').toggleClass('panel-in-fullscreen', isFullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@@ -94,10 +144,11 @@ export class DashboardPage extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
renderDashboard() {
|
renderDashboard() {
|
||||||
const { dashboard, editview } = this.props;
|
const { dashboard, editview } = this.props;
|
||||||
|
const { isEditing, isFullscreen } = this.state;
|
||||||
|
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
'dashboard-container': true,
|
'dashboard-container': true,
|
||||||
'dashboard-container--has-submenu': dashboard.meta.submenuEnabled
|
'dashboard-container--has-submenu': dashboard.meta.submenuEnabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -105,7 +156,7 @@ export class DashboardPage extends PureComponent<Props, State> {
|
|||||||
{dashboard && editview && <DashboardSettings dashboard={dashboard} />}
|
{dashboard && editview && <DashboardSettings dashboard={dashboard} />}
|
||||||
|
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<DashboardGrid dashboard={dashboard} />
|
<DashboardGrid dashboard={dashboard} isEditing={isEditing} isFullscreen={isFullscreen} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -113,7 +164,7 @@ export class DashboardPage extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { dashboard, editview } = this.props;
|
const { dashboard, editview } = this.props;
|
||||||
const { isSettingsOpening } = this.state;
|
const { isSettingsOpening, isEditing, isFullscreen } = this.state;
|
||||||
|
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
'dashboard-page--settings-opening': isSettingsOpening,
|
'dashboard-page--settings-opening': isSettingsOpening,
|
||||||
@@ -122,7 +173,7 @@ export class DashboardPage extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<DashNav dashboard={dashboard} />
|
<DashNav dashboard={dashboard} isEditing={isEditing} isFullscreen={isFullscreen} editview={editview} />
|
||||||
{!dashboard && this.renderLoadingState()}
|
{!dashboard && this.renderLoadingState()}
|
||||||
{dashboard && this.renderDashboard()}
|
{dashboard && this.renderDashboard()}
|
||||||
</div>
|
</div>
|
||||||
@@ -130,19 +181,26 @@ export class DashboardPage extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: StoreState) => ({
|
const mapStateToProps = (state: StoreState) => {
|
||||||
urlUid: state.location.routeParams.uid,
|
console.log('state location', state.location.query);
|
||||||
urlSlug: state.location.routeParams.slug,
|
return {
|
||||||
urlType: state.location.routeParams.type,
|
urlUid: state.location.routeParams.uid,
|
||||||
panelId: state.location.query.panelId,
|
urlSlug: state.location.routeParams.slug,
|
||||||
editview: state.location.query.editview,
|
urlType: state.location.routeParams.type,
|
||||||
loadingState: state.dashboard.loadingState,
|
editview: state.location.query.editview,
|
||||||
dashboard: state.dashboard.model as DashboardModel,
|
urlPanelId: state.location.query.panelId,
|
||||||
});
|
urlFullscreen: state.location.query.fullscreen === true,
|
||||||
|
urlEdit: state.location.query.edit === true,
|
||||||
|
loadingState: state.dashboard.loadingState,
|
||||||
|
dashboard: state.dashboard.model as DashboardModel,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
initDashboard,
|
initDashboard,
|
||||||
setDashboardModel
|
setDashboardModel,
|
||||||
|
notifyApp,
|
||||||
|
updateLocation,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));
|
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import React from 'react';
|
// Libaries
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
import { hot } from 'react-hot-loader';
|
import { hot } from 'react-hot-loader';
|
||||||
import ReactGridLayout, { ItemCallback } from 'react-grid-layout';
|
import ReactGridLayout, { ItemCallback } from 'react-grid-layout';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import sizeMe from 'react-sizeme';
|
||||||
|
|
||||||
|
// Types
|
||||||
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants';
|
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants';
|
||||||
import { DashboardPanel } from './DashboardPanel';
|
import { DashboardPanel } from './DashboardPanel';
|
||||||
import { DashboardModel, PanelModel } from '../state';
|
import { DashboardModel, PanelModel } from '../state';
|
||||||
import classNames from 'classnames';
|
|
||||||
import sizeMe from 'react-sizeme';
|
|
||||||
|
|
||||||
let lastGridWidth = 1200;
|
let lastGridWidth = 1200;
|
||||||
let ignoreNextWidthChange = false;
|
let ignoreNextWidthChange = false;
|
||||||
@@ -76,19 +79,18 @@ function GridWrapper({
|
|||||||
|
|
||||||
const SizedReactLayoutGrid = sizeMe({ monitorWidth: true })(GridWrapper);
|
const SizedReactLayoutGrid = sizeMe({ monitorWidth: true })(GridWrapper);
|
||||||
|
|
||||||
export interface DashboardGridProps {
|
export interface Props {
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel;
|
||||||
|
isEditing: boolean;
|
||||||
|
isFullscreen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DashboardGrid extends React.Component<DashboardGridProps> {
|
export class DashboardGrid extends PureComponent<Props> {
|
||||||
gridToPanelMap: any;
|
gridToPanelMap: any;
|
||||||
panelMap: { [id: string]: PanelModel };
|
panelMap: { [id: string]: PanelModel };
|
||||||
|
|
||||||
constructor(props: DashboardGridProps) {
|
componentDidMount() {
|
||||||
super(props);
|
const { dashboard } = this.props;
|
||||||
|
|
||||||
// subscribe to dashboard events
|
|
||||||
const dashboard = this.props.dashboard;
|
|
||||||
dashboard.on('panel-added', this.triggerForceUpdate);
|
dashboard.on('panel-added', this.triggerForceUpdate);
|
||||||
dashboard.on('panel-removed', this.triggerForceUpdate);
|
dashboard.on('panel-removed', this.triggerForceUpdate);
|
||||||
dashboard.on('repeats-processed', this.triggerForceUpdate);
|
dashboard.on('repeats-processed', this.triggerForceUpdate);
|
||||||
@@ -97,6 +99,16 @@ export class DashboardGrid extends React.Component<DashboardGridProps> {
|
|||||||
dashboard.on('row-expanded', this.triggerForceUpdate);
|
dashboard.on('row-expanded', this.triggerForceUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { dashboard } = this.props;
|
||||||
|
dashboard.off('panel-added', this.triggerForceUpdate);
|
||||||
|
dashboard.off('panel-removed', this.triggerForceUpdate);
|
||||||
|
dashboard.off('repeats-processed', this.triggerForceUpdate);
|
||||||
|
dashboard.off('view-mode-changed', this.onViewModeChanged);
|
||||||
|
dashboard.off('row-collapsed', this.triggerForceUpdate);
|
||||||
|
dashboard.off('row-expanded', this.triggerForceUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
buildLayout() {
|
buildLayout() {
|
||||||
const layout = [];
|
const layout = [];
|
||||||
this.panelMap = {};
|
this.panelMap = {};
|
||||||
@@ -151,7 +163,6 @@ export class DashboardGrid extends React.Component<DashboardGridProps> {
|
|||||||
|
|
||||||
onViewModeChanged = () => {
|
onViewModeChanged = () => {
|
||||||
ignoreNextWidthChange = true;
|
ignoreNextWidthChange = true;
|
||||||
this.forceUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGridPos = (item: ReactGridLayout.Layout, layout: ReactGridLayout.Layout[]) => {
|
updateGridPos = (item: ReactGridLayout.Layout, layout: ReactGridLayout.Layout[]) => {
|
||||||
@@ -197,18 +208,20 @@ export class DashboardGrid extends React.Component<DashboardGridProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { dashboard, isFullscreen } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SizedReactLayoutGrid
|
<SizedReactLayoutGrid
|
||||||
className={classNames({ layout: true })}
|
className={classNames({ layout: true })}
|
||||||
layout={this.buildLayout()}
|
layout={this.buildLayout()}
|
||||||
isResizable={this.props.dashboard.meta.canEdit}
|
isResizable={dashboard.meta.canEdit}
|
||||||
isDraggable={this.props.dashboard.meta.canEdit}
|
isDraggable={dashboard.meta.canEdit}
|
||||||
onLayoutChange={this.onLayoutChange}
|
onLayoutChange={this.onLayoutChange}
|
||||||
onWidthChange={this.onWidthChange}
|
onWidthChange={this.onWidthChange}
|
||||||
onDragStop={this.onDragStop}
|
onDragStop={this.onDragStop}
|
||||||
onResize={this.onResize}
|
onResize={this.onResize}
|
||||||
onResizeStop={this.onResizeStop}
|
onResizeStop={this.onResizeStop}
|
||||||
isFullscreen={this.props.dashboard.meta.fullscreen}
|
isFullscreen={isFullscreen}
|
||||||
>
|
>
|
||||||
{this.renderPanels()}
|
{this.renderPanels()}
|
||||||
</SizedReactLayoutGrid>
|
</SizedReactLayoutGrid>
|
||||||
|
|||||||
@@ -98,8 +98,6 @@ export class DashboardViewStateSrv {
|
|||||||
if (fromRouteUpdated !== true) {
|
if (fromRouteUpdated !== true) {
|
||||||
this.$location.search(this.serializeToUrl());
|
this.$location.search(this.serializeToUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.syncState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCollapsedPanelRow(panelId) {
|
toggleCollapsedPanelRow(panelId) {
|
||||||
@@ -115,34 +113,6 @@ export class DashboardViewStateSrv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
syncState() {
|
|
||||||
if (this.state.fullscreen) {
|
|
||||||
const panel = this.dashboard.getPanelById(this.state.panelId);
|
|
||||||
|
|
||||||
if (!panel) {
|
|
||||||
this.state.fullscreen = null;
|
|
||||||
this.state.panelId = null;
|
|
||||||
this.state.edit = null;
|
|
||||||
|
|
||||||
this.update(this.state);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
appEvents.emit('alert-error', ['Error', 'Panel not found']);
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!panel.fullscreen) {
|
|
||||||
this.enterFullscreen(panel);
|
|
||||||
} else if (this.dashboard.meta.isEditing !== this.state.edit) {
|
|
||||||
this.dashboard.setViewMode(panel, this.state.fullscreen, this.state.edit);
|
|
||||||
}
|
|
||||||
} else if (this.fullscreenPanel) {
|
|
||||||
this.leaveFullscreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
leaveFullscreen() {
|
leaveFullscreen() {
|
||||||
const panel = this.fullscreenPanel;
|
const panel = this.fullscreenPanel;
|
||||||
|
|
||||||
|
|||||||
@@ -165,6 +165,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
|||||||
for (const drop of Drop.drops) {
|
for (const drop of Drop.drops) {
|
||||||
drop.destroy();
|
drop.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appEvents.emit('hide-dash-search');
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle kiosk mode
|
// handle kiosk mode
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { DashboardAcl } from './acl';
|
import { DashboardAcl } from './acl';
|
||||||
|
|
||||||
export interface MutableDashboard {
|
export interface MutableDashboard {
|
||||||
|
meta: {
|
||||||
|
fullscreen: boolean;
|
||||||
|
isEditing: boolean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum DashboardLoadingState {
|
export enum DashboardLoadingState {
|
||||||
|
|||||||
@@ -189,7 +189,7 @@
|
|||||||
<grafana-app class="grafana-app" ng-cloak>
|
<grafana-app class="grafana-app" ng-cloak>
|
||||||
<sidemenu class="sidemenu"></sidemenu>
|
<sidemenu class="sidemenu"></sidemenu>
|
||||||
<app-notifications-list class="page-alert-list"></app-notifications-list>
|
<app-notifications-list class="page-alert-list"></app-notifications-list>
|
||||||
|
<dashboard-search></dashboard-search>
|
||||||
|
|
||||||
<div class="main-view">
|
<div class="main-view">
|
||||||
<div class="scroll-canvas" page-scrollbar>
|
<div class="scroll-canvas" page-scrollbar>
|
||||||
|
|||||||
Reference in New Issue
Block a user