mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
PanelEdit: Prevent the preview pane to be resized further than window height (#28370)
* use percentage on topPanel, limit resize * add relative size to rightPanel, resize split panes on resize * lock min size of options pane to min 300px, adding debounce * sorting imports * assigning the ref, remove debounce * set default uistate to number instead of string * revert go.sum and go.mod * fix go.mod
This commit is contained in:
@@ -1,38 +1,41 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { FieldConfigSource, GrafanaTheme, PanelPlugin } from '@grafana/data';
|
||||
import { Button, HorizontalGroup, Icon, RadioButtonGroup, stylesFactory } from '@grafana/ui';
|
||||
import { css, cx } from 'emotion';
|
||||
import config from 'app/core/config';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
import { PanelModel } from '../../state/PanelModel';
|
||||
import { DashboardModel } from '../../state/DashboardModel';
|
||||
import { DashboardPanel } from '../../dashgrid/DashboardPanel';
|
||||
|
||||
import SplitPane from 'react-split-pane';
|
||||
import { StoreState } from '../../../../types/store';
|
||||
import React, { createRef, MutableRefObject, PureComponent } from 'react';
|
||||
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { updateLocation } from '../../../../core/reducers/location';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import SplitPane from 'react-split-pane';
|
||||
import { css, cx } from 'emotion';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
import { DisplayMode, displayModes, PanelEditorTab } from './types';
|
||||
|
||||
import { FieldConfigSource, GrafanaTheme, PanelPlugin } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Button, HorizontalGroup, Icon, RadioButtonGroup, stylesFactory } from '@grafana/ui';
|
||||
|
||||
import config from 'app/core/config';
|
||||
import { appEvents } from 'app/core/core';
|
||||
import { calculatePanelSize } from './utils';
|
||||
|
||||
import { PanelEditorTabs } from './PanelEditorTabs';
|
||||
import { DashNavTimeControls } from '../DashNav/DashNavTimeControls';
|
||||
import { CoreEvents, LocationState } from 'app/types';
|
||||
import { calculatePanelSize } from './utils';
|
||||
import { initPanelEditor, panelEditorCleanUp, updatePanelEditorUIState } from './state/actions';
|
||||
import { PanelEditorUIState, setDiscardChanges } from './state/reducers';
|
||||
import { getPanelEditorTabs } from './state/selectors';
|
||||
import { getPanelStateById } from '../../state/selectors';
|
||||
import { OptionsPaneContent } from './OptionsPaneContent';
|
||||
import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
|
||||
import { DashNavButton } from 'app/features/dashboard/components/DashNav/DashNavButton';
|
||||
import { VariableModel } from 'app/features/variables/types';
|
||||
import { getVariables } from 'app/features/variables/state/selectors';
|
||||
import { SubMenuItems } from 'app/features/dashboard/components/SubMenu/SubMenuItems';
|
||||
import { BackButton } from 'app/core/components/BackButton/BackButton';
|
||||
import { appEvents } from 'app/core/core';
|
||||
import { SaveDashboardModalProxy } from '../SaveDashboard/SaveDashboardModalProxy';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { DashboardPanel } from '../../dashgrid/DashboardPanel';
|
||||
|
||||
import { initPanelEditor, panelEditorCleanUp, updatePanelEditorUIState } from './state/actions';
|
||||
|
||||
import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
|
||||
import { updateLocation } from 'app/core/reducers/location';
|
||||
import { PanelEditorUIState, setDiscardChanges } from './state/reducers';
|
||||
|
||||
import { getPanelEditorTabs } from './state/selectors';
|
||||
import { getPanelStateById } from '../../state/selectors';
|
||||
import { getVariables } from 'app/features/variables/state/selectors';
|
||||
|
||||
import { CoreEvents, LocationState, StoreState } from 'app/types';
|
||||
import { DisplayMode, displayModes, PanelEditorTab } from './types';
|
||||
import { VariableModel } from 'app/features/variables/types';
|
||||
import { DashboardModel, PanelModel } from '../../state';
|
||||
|
||||
interface OwnProps {
|
||||
dashboard: DashboardModel;
|
||||
@@ -62,15 +65,28 @@ type Props = OwnProps & ConnectedProps & DispatchProps;
|
||||
|
||||
export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
querySubscription: Unsubscribable;
|
||||
rafToken = createRef<number>();
|
||||
|
||||
componentDidMount() {
|
||||
this.props.initPanelEditor(this.props.sourcePanel, this.props.dashboard);
|
||||
|
||||
window.addEventListener('resize', this.updateSplitPaneSize);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.panelEditorCleanUp();
|
||||
window.removeEventListener('resize', this.updateSplitPaneSize);
|
||||
}
|
||||
|
||||
updateSplitPaneSize = () => {
|
||||
if (this.rafToken.current !== undefined) {
|
||||
window.cancelAnimationFrame(this.rafToken.current!);
|
||||
}
|
||||
(this.rafToken as MutableRefObject<number>).current = window.requestAnimationFrame(() => {
|
||||
this.forceUpdate();
|
||||
});
|
||||
};
|
||||
|
||||
onPanelExit = () => {
|
||||
this.props.updateLocation({
|
||||
query: { editPanel: null, tab: null },
|
||||
@@ -130,11 +146,16 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetPane = pane === Pane.Top ? 'topPaneSize' : 'rightPaneSize';
|
||||
const { updatePanelEditorUIState } = this.props;
|
||||
updatePanelEditorUIState({
|
||||
[targetPane]: size,
|
||||
});
|
||||
if (pane === Pane.Top) {
|
||||
updatePanelEditorUIState({
|
||||
topPaneSize: size / window.innerHeight,
|
||||
});
|
||||
} else {
|
||||
updatePanelEditorUIState({
|
||||
rightPaneSize: size / window.innerWidth,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onDragStarted = () => {
|
||||
@@ -186,11 +207,21 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
|
||||
renderHorizontalSplit(styles: EditorStyles) {
|
||||
const { dashboard, panel, tabs, uiState } = this.props;
|
||||
/*
|
||||
Guesstimate the height of the browser window minus
|
||||
panel toolbar and editor toolbar (~120px). This is to prevent resizing
|
||||
the preview window beyond the browser window.
|
||||
*/
|
||||
const windowHeight = window.innerHeight - 120;
|
||||
const size = uiState.topPaneSize >= 1 ? uiState.topPaneSize : (uiState.topPaneSize as number) * window.innerHeight;
|
||||
|
||||
return tabs.length > 0 ? (
|
||||
<SplitPane
|
||||
split="horizontal"
|
||||
minSize={200}
|
||||
maxSize={windowHeight}
|
||||
primary="first"
|
||||
size={size}
|
||||
/* Use persisted state for default size */
|
||||
defaultSize={uiState.topPaneSize}
|
||||
pane2Style={{ minHeight: 0 }}
|
||||
@@ -288,8 +319,8 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
renderOptionsPane() {
|
||||
const { plugin, dashboard, panel, uiState } = this.props;
|
||||
renderOptionsPane(width: number) {
|
||||
const { plugin, dashboard, panel } = this.props;
|
||||
|
||||
if (!plugin) {
|
||||
return <div />;
|
||||
@@ -300,7 +331,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
plugin={plugin}
|
||||
dashboard={dashboard}
|
||||
panel={panel}
|
||||
width={uiState.rightPaneSize as number}
|
||||
width={width}
|
||||
onClose={this.onTogglePanelOptions}
|
||||
onFieldConfigsChange={this.onFieldConfigChange}
|
||||
onPanelOptionsChanged={this.onPanelOptionsChanged}
|
||||
@@ -312,10 +343,21 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
renderWithOptionsPane(styles: EditorStyles) {
|
||||
const { uiState } = this.props;
|
||||
|
||||
// Limit options pane width to 90% of screen.
|
||||
const maxWidth = window.innerWidth * 0.9;
|
||||
|
||||
// Need to handle when width is relative. ie a percentage of the viewport
|
||||
const width =
|
||||
uiState.rightPaneSize <= 1
|
||||
? (uiState.rightPaneSize as number) * window.innerWidth
|
||||
: (uiState.rightPaneSize as number);
|
||||
|
||||
return (
|
||||
<SplitPane
|
||||
split="vertical"
|
||||
minSize={300}
|
||||
maxSize={maxWidth}
|
||||
size={width >= 300 ? width : 300}
|
||||
primary="second"
|
||||
/* Use persisted state for default size */
|
||||
defaultSize={uiState.rightPaneSize}
|
||||
@@ -324,7 +366,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
onDragFinished={size => this.onDragFinished(Pane.Right, size)}
|
||||
>
|
||||
{this.renderHorizontalSplit(styles)}
|
||||
{this.renderOptionsPane()}
|
||||
{this.renderOptionsPane(width)}
|
||||
</SplitPane>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export const PANEL_EDITOR_UI_STATE_STORAGE_KEY = 'grafana.dashboard.editor.ui';
|
||||
export const DEFAULT_PANEL_EDITOR_UI_STATE: PanelEditorUIState = {
|
||||
isPanelOptionsVisible: true,
|
||||
rightPaneSize: 400,
|
||||
topPaneSize: '45%',
|
||||
topPaneSize: 0.45,
|
||||
mode: DisplayMode.Fill,
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface PanelEditorUIState {
|
||||
}
|
||||
|
||||
export interface PanelEditorState {
|
||||
/* These are functions as they are mutaded later on and redux toolkit will Object.freeze state so
|
||||
/* These are functions as they are mutated later on and redux toolkit will Object.freeze state so
|
||||
* we need to store these using functions instead */
|
||||
getSourcePanel: () => PanelModel;
|
||||
getPanel: () => PanelModel;
|
||||
|
||||
Reference in New Issue
Block a user