mirror of
https://github.com/grafana/grafana.git
synced 2024-11-26 02:40:26 -06:00
New panel editor: Persist panel editor ui state (#22210)
This commit is contained in:
parent
21b53b7d79
commit
131a3248ba
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { GrafanaTheme, FieldConfigSource, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
|
||||
import { stylesFactory, Forms, CustomScrollbar, selectThemeVariant, Icon } from '@grafana/ui';
|
||||
import { FieldConfigSource, GrafanaTheme, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
|
||||
import { CustomScrollbar, Forms, Icon, selectThemeVariant, stylesFactory } from '@grafana/ui';
|
||||
import { css, cx } from 'emotion';
|
||||
import config from 'app/core/config';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
@ -11,7 +11,7 @@ import { DashboardPanel } from '../../dashgrid/DashboardPanel';
|
||||
|
||||
import SplitPane from 'react-split-pane';
|
||||
import { StoreState } from '../../../../types/store';
|
||||
import { connect, MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { updateLocation } from '../../../../core/reducers/location';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
import { PanelTitle } from './PanelTitle';
|
||||
@ -20,13 +20,18 @@ import { PanelEditorTabs } from './PanelEditorTabs';
|
||||
import { DashNavTimeControls } from '../DashNav/DashNavTimeControls';
|
||||
import { LocationState } from 'app/types';
|
||||
import { calculatePanelSize } from './utils';
|
||||
import { initPanelEditor, panelEditorCleanUp } from './state/actions';
|
||||
import { setDisplayMode, toggleOptionsView, setDiscardChanges } from './state/reducers';
|
||||
import { initPanelEditor, panelEditorCleanUp, updatePanelEditorUIState } from './state/actions';
|
||||
import { PanelEditorUIState, setDiscardChanges } from './state/reducers';
|
||||
import { FieldConfigEditor } from './FieldConfigEditor';
|
||||
import { OptionsGroup } from './OptionsGroup';
|
||||
import { getPanelEditorTabs } from './state/selectors';
|
||||
import { getPanelStateById } from '../../state/selectors';
|
||||
|
||||
enum Pane {
|
||||
Right,
|
||||
Top,
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
dashboard: DashboardModel;
|
||||
sourcePanel: PanelModel;
|
||||
@ -37,19 +42,17 @@ interface ConnectedProps {
|
||||
plugin?: PanelPlugin;
|
||||
panel: PanelModel;
|
||||
data: PanelData;
|
||||
mode: DisplayMode;
|
||||
isPanelOptionsVisible: boolean;
|
||||
initDone: boolean;
|
||||
tabs: PanelEditorTab[];
|
||||
uiState: PanelEditorUIState;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
updateLocation: typeof updateLocation;
|
||||
initPanelEditor: typeof initPanelEditor;
|
||||
panelEditorCleanUp: typeof panelEditorCleanUp;
|
||||
setDisplayMode: typeof setDisplayMode;
|
||||
toggleOptionsView: typeof toggleOptionsView;
|
||||
setDiscardChanges: typeof setDiscardChanges;
|
||||
updatePanelEditorUIState: typeof updatePanelEditorUIState;
|
||||
}
|
||||
|
||||
type Props = OwnProps & ConnectedProps & DispatchProps;
|
||||
@ -141,8 +144,13 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
return <div>No editor (angular?)</div>;
|
||||
}
|
||||
|
||||
onDragFinished = () => {
|
||||
onDragFinished = (pane: Pane, size: number) => {
|
||||
document.body.style.cursor = 'auto';
|
||||
const targetPane = pane === Pane.Top ? 'topPaneSize' : 'rightPaneSize';
|
||||
const { updatePanelEditorUIState } = this.props;
|
||||
updatePanelEditorUIState({
|
||||
[targetPane]: size,
|
||||
});
|
||||
};
|
||||
|
||||
onDragStarted = () => {
|
||||
@ -155,26 +163,31 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
};
|
||||
|
||||
onDiplayModeChange = (mode: SelectableValue<DisplayMode>) => {
|
||||
this.props.setDisplayMode(mode.value);
|
||||
const { updatePanelEditorUIState } = this.props;
|
||||
updatePanelEditorUIState({
|
||||
mode: mode.value,
|
||||
});
|
||||
};
|
||||
|
||||
onTogglePanelOptions = () => {
|
||||
this.props.toggleOptionsView();
|
||||
const { uiState, updatePanelEditorUIState } = this.props;
|
||||
updatePanelEditorUIState({ isPanelOptionsVisible: !uiState.isPanelOptionsVisible });
|
||||
};
|
||||
|
||||
renderHorizontalSplit(styles: any) {
|
||||
const { dashboard, panel, mode, tabs, data } = this.props;
|
||||
const { dashboard, panel, tabs, data, uiState } = this.props;
|
||||
|
||||
return (
|
||||
<SplitPane
|
||||
split="horizontal"
|
||||
minSize={50}
|
||||
primary="first"
|
||||
defaultSize="45%"
|
||||
/* Use persisted state for default size */
|
||||
defaultSize={uiState.topPaneSize}
|
||||
pane2Style={{ minHeight: 0 }}
|
||||
resizerClassName={styles.resizerH}
|
||||
onDragStarted={this.onDragStarted}
|
||||
onDragFinished={this.onDragFinished}
|
||||
onDragFinished={size => this.onDragFinished(Pane.Top, size)}
|
||||
>
|
||||
<div className={styles.panelWrapper}>
|
||||
<AutoSizer>
|
||||
@ -184,7 +197,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
}
|
||||
return (
|
||||
<div className={styles.centeringContainer} style={{ width, height }}>
|
||||
<div style={calculatePanelSize(mode, width, height, panel)}>
|
||||
<div style={calculatePanelSize(uiState.mode, width, height, panel)}>
|
||||
<DashboardPanel
|
||||
dashboard={dashboard}
|
||||
panel={panel}
|
||||
@ -207,7 +220,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dashboard, location, panel, mode, isPanelOptionsVisible, initDone } = this.props;
|
||||
const { dashboard, location, panel, uiState, initDone } = this.props;
|
||||
const styles = getStyles(config.theme);
|
||||
|
||||
if (!initDone) {
|
||||
@ -234,7 +247,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
</div>
|
||||
<div className={styles.toolbarItem}>
|
||||
<Forms.Select
|
||||
value={displayModes.find(v => v.value === mode)}
|
||||
value={displayModes.find(v => v.value === uiState.mode)}
|
||||
options={displayModes}
|
||||
onChange={this.onDiplayModeChange}
|
||||
/>
|
||||
@ -253,15 +266,16 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.editorBody}>
|
||||
{isPanelOptionsVisible ? (
|
||||
{uiState.isPanelOptionsVisible ? (
|
||||
<SplitPane
|
||||
split="vertical"
|
||||
minSize={100}
|
||||
primary="second"
|
||||
defaultSize={350}
|
||||
/* Use persisted state for default size */
|
||||
defaultSize={uiState.rightPaneSize}
|
||||
resizerClassName={styles.resizerV}
|
||||
onDragStarted={() => (document.body.style.cursor = 'col-resize')}
|
||||
onDragFinished={this.onDragFinished}
|
||||
onDragFinished={size => this.onDragFinished(Pane.Right, size)}
|
||||
>
|
||||
{this.renderHorizontalSplit(styles)}
|
||||
<div className={styles.panelOptionsPane}>
|
||||
@ -292,11 +306,10 @@ const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (
|
||||
location: state.location,
|
||||
plugin: plugin,
|
||||
panel: state.panelEditorNew.getPanel(),
|
||||
mode: state.panelEditorNew.mode,
|
||||
isPanelOptionsVisible: state.panelEditorNew.isPanelOptionsVisible,
|
||||
data: state.panelEditorNew.getData(),
|
||||
initDone: state.panelEditorNew.initDone,
|
||||
tabs: getPanelEditorTabs(state.location, plugin),
|
||||
uiState: state.panelEditorNew.ui,
|
||||
};
|
||||
};
|
||||
|
||||
@ -304,9 +317,8 @@ const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
|
||||
updateLocation,
|
||||
initPanelEditor,
|
||||
panelEditorCleanUp,
|
||||
setDisplayMode,
|
||||
toggleOptionsView,
|
||||
setDiscardChanges,
|
||||
updatePanelEditorUIState,
|
||||
};
|
||||
|
||||
export const PanelEditor = connect(mapStateToProps, mapDispatchToProps)(PanelEditorUnconnected);
|
||||
|
@ -1,7 +1,15 @@
|
||||
import { PanelModel, DashboardModel } from '../../../state';
|
||||
import { PanelData } from '@grafana/data';
|
||||
import { ThunkResult } from 'app/types';
|
||||
import { setEditorPanelData, updateEditorInitState, closeCompleted } from './reducers';
|
||||
import {
|
||||
setEditorPanelData,
|
||||
updateEditorInitState,
|
||||
closeCompleted,
|
||||
PanelEditorUIState,
|
||||
setPanelEditorUIState,
|
||||
PANEL_EDITOR_UI_STATE_STORAGE_KEY,
|
||||
} from './reducers';
|
||||
import store from '../../../../../core/store';
|
||||
|
||||
export function initPanelEditor(sourcePanel: PanelModel, dashboard: DashboardModel): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
@ -45,3 +53,11 @@ export function panelEditorCleanUp(): ThunkResult<void> {
|
||||
dispatch(closeCompleted());
|
||||
};
|
||||
}
|
||||
|
||||
export function updatePanelEditorUIState(uiState: Partial<PanelEditorUIState>): ThunkResult<void> {
|
||||
return (dispatch, getStore) => {
|
||||
const nextState = { ...getStore().panelEditorNew.ui, ...uiState };
|
||||
dispatch(setPanelEditorUIState(nextState));
|
||||
store.setObject(PANEL_EDITOR_UI_STATE_STORAGE_KEY, nextState);
|
||||
};
|
||||
}
|
||||
|
@ -3,6 +3,25 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { PanelModel } from '../../../state/PanelModel';
|
||||
import { PanelData, LoadingState, DefaultTimeRange } from '@grafana/data';
|
||||
import { DisplayMode } from '../types';
|
||||
import store from '../../../../../core/store';
|
||||
|
||||
export const PANEL_EDITOR_UI_STATE_STORAGE_KEY = 'grafana.dashboard.editor.ui';
|
||||
|
||||
export const DEFAULT_PANEL_EDITOR_UI_STATE: PanelEditorUIState = {
|
||||
isPanelOptionsVisible: true,
|
||||
rightPaneSize: 350,
|
||||
topPaneSize: '45%',
|
||||
mode: DisplayMode.Fill,
|
||||
};
|
||||
|
||||
export interface PanelEditorUIState {
|
||||
isPanelOptionsVisible: boolean;
|
||||
// annotating as number or string since size can be expressed as static value or percentage
|
||||
rightPaneSize: number | string;
|
||||
// annotating as number or string since size can be expressed as static value or percentage
|
||||
topPaneSize: number | string;
|
||||
mode: DisplayMode;
|
||||
}
|
||||
|
||||
export interface PanelEditorStateNew {
|
||||
/* These are functions as they are mutaded later on and redux toolkit will Object.freeze state so
|
||||
@ -10,12 +29,11 @@ export interface PanelEditorStateNew {
|
||||
getSourcePanel: () => PanelModel;
|
||||
getPanel: () => PanelModel;
|
||||
getData: () => PanelData;
|
||||
mode: DisplayMode;
|
||||
isPanelOptionsVisible: boolean;
|
||||
querySubscription?: Unsubscribable;
|
||||
initDone: boolean;
|
||||
shouldDiscardChanges: boolean;
|
||||
isOpen: boolean;
|
||||
ui: PanelEditorUIState;
|
||||
}
|
||||
|
||||
export const initialState: PanelEditorStateNew = {
|
||||
@ -26,11 +44,13 @@ export const initialState: PanelEditorStateNew = {
|
||||
series: [],
|
||||
timeRange: DefaultTimeRange,
|
||||
}),
|
||||
isPanelOptionsVisible: true,
|
||||
mode: DisplayMode.Fill,
|
||||
initDone: false,
|
||||
shouldDiscardChanges: false,
|
||||
isOpen: false,
|
||||
ui: {
|
||||
...DEFAULT_PANEL_EDITOR_UI_STATE,
|
||||
...store.getObject(PANEL_EDITOR_UI_STATE_STORAGE_KEY, DEFAULT_PANEL_EDITOR_UI_STATE),
|
||||
},
|
||||
};
|
||||
|
||||
interface InitEditorPayload {
|
||||
@ -53,15 +73,12 @@ const pluginsSlice = createSlice({
|
||||
setEditorPanelData: (state, action: PayloadAction<PanelData>) => {
|
||||
state.getData = () => action.payload;
|
||||
},
|
||||
toggleOptionsView: state => {
|
||||
state.isPanelOptionsVisible = !state.isPanelOptionsVisible;
|
||||
},
|
||||
setDisplayMode: (state, action: PayloadAction<DisplayMode>) => {
|
||||
state.mode = action.payload;
|
||||
},
|
||||
setDiscardChanges: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldDiscardChanges = action.payload;
|
||||
},
|
||||
setPanelEditorUIState: (state, action: PayloadAction<Partial<PanelEditorUIState>>) => {
|
||||
state.ui = { ...state.ui, ...action.payload };
|
||||
},
|
||||
closeCompleted: state => {
|
||||
state.isOpen = false;
|
||||
state.initDone = false;
|
||||
@ -72,10 +89,9 @@ const pluginsSlice = createSlice({
|
||||
export const {
|
||||
updateEditorInitState,
|
||||
setEditorPanelData,
|
||||
toggleOptionsView,
|
||||
setDisplayMode,
|
||||
setDiscardChanges,
|
||||
closeCompleted,
|
||||
setPanelEditorUIState,
|
||||
} = pluginsSlice.actions;
|
||||
|
||||
export const panelEditorReducerNew = pluginsSlice.reducer;
|
||||
|
Loading…
Reference in New Issue
Block a user