2020-02-11 06:48:36 -06:00
|
|
|
import React, { PureComponent } from 'react';
|
2020-04-04 07:25:06 -05:00
|
|
|
import { FieldConfigSource, GrafanaTheme, PanelData, PanelPlugin } from '@grafana/data';
|
2020-04-14 11:52:56 -05:00
|
|
|
import { Button, stylesFactory, Icon, RadioButtonGroup } from '@grafana/ui';
|
2020-02-08 05:01:10 -06:00
|
|
|
import { css, cx } from 'emotion';
|
2019-12-16 02:18:48 -06:00
|
|
|
import config from 'app/core/config';
|
2020-02-09 08:39:46 -06:00
|
|
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
2019-12-16 02:18:48 -06:00
|
|
|
|
|
|
|
import { PanelModel } from '../../state/PanelModel';
|
|
|
|
import { DashboardModel } from '../../state/DashboardModel';
|
|
|
|
import { DashboardPanel } from '../../dashgrid/DashboardPanel';
|
2020-02-09 08:39:46 -06:00
|
|
|
|
2020-02-08 05:01:10 -06:00
|
|
|
import SplitPane from 'react-split-pane';
|
2020-02-07 07:59:04 -06:00
|
|
|
import { StoreState } from '../../../../types/store';
|
2020-02-15 13:31:11 -06:00
|
|
|
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
2020-02-07 07:59:04 -06:00
|
|
|
import { updateLocation } from '../../../../core/reducers/location';
|
2020-02-08 06:23:16 -06:00
|
|
|
import { Unsubscribable } from 'rxjs';
|
2020-02-12 03:42:57 -06:00
|
|
|
import { DisplayMode, displayModes, PanelEditorTab } from './types';
|
2020-02-09 08:39:46 -06:00
|
|
|
import { PanelEditorTabs } from './PanelEditorTabs';
|
2020-02-09 11:48:14 -06:00
|
|
|
import { DashNavTimeControls } from '../DashNav/DashNavTimeControls';
|
2020-04-18 11:00:54 -05:00
|
|
|
import { LocationState, CoreEvents } from 'app/types';
|
2020-02-11 06:48:36 -06:00
|
|
|
import { calculatePanelSize } from './utils';
|
2020-02-15 13:31:11 -06:00
|
|
|
import { initPanelEditor, panelEditorCleanUp, updatePanelEditorUIState } from './state/actions';
|
|
|
|
import { PanelEditorUIState, setDiscardChanges } from './state/reducers';
|
2020-02-12 03:42:57 -06:00
|
|
|
import { getPanelEditorTabs } from './state/selectors';
|
2020-02-13 09:06:45 -06:00
|
|
|
import { getPanelStateById } from '../../state/selectors';
|
2020-03-20 09:15:04 -05:00
|
|
|
import { OptionsPaneContent } from './OptionsPaneContent';
|
2020-03-30 07:39:18 -05:00
|
|
|
import { DashNavButton } from 'app/features/dashboard/components/DashNav/DashNavButton';
|
2020-04-02 06:56:20 -05:00
|
|
|
import { VariableModel } from 'app/features/templating/types';
|
|
|
|
import { getVariables } from 'app/features/variables/state/selectors';
|
|
|
|
import { SubMenuItems } from 'app/features/dashboard/components/SubMenu/SubMenuItems';
|
2020-04-11 09:07:18 -05:00
|
|
|
import { BackButton } from 'app/core/components/BackButton/BackButton';
|
2020-04-18 11:00:54 -05:00
|
|
|
import { appEvents } from 'app/core/core';
|
|
|
|
import { SaveDashboardModalProxy } from '../SaveDashboard/SaveDashboardModalProxy';
|
2019-12-16 02:18:48 -06:00
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
interface OwnProps {
|
2019-12-16 02:18:48 -06:00
|
|
|
dashboard: DashboardModel;
|
2020-02-08 11:29:09 -06:00
|
|
|
sourcePanel: PanelModel;
|
2019-12-16 02:18:48 -06:00
|
|
|
}
|
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
interface ConnectedProps {
|
|
|
|
location: LocationState;
|
|
|
|
plugin?: PanelPlugin;
|
2020-02-08 11:29:09 -06:00
|
|
|
panel: PanelModel;
|
2020-02-08 06:23:16 -06:00
|
|
|
data: PanelData;
|
2020-02-11 07:57:16 -06:00
|
|
|
initDone: boolean;
|
2020-02-12 03:42:57 -06:00
|
|
|
tabs: PanelEditorTab[];
|
2020-02-15 13:31:11 -06:00
|
|
|
uiState: PanelEditorUIState;
|
2020-04-02 06:56:20 -05:00
|
|
|
variables: VariableModel[];
|
2019-12-16 02:18:48 -06:00
|
|
|
}
|
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
interface DispatchProps {
|
|
|
|
updateLocation: typeof updateLocation;
|
|
|
|
initPanelEditor: typeof initPanelEditor;
|
|
|
|
panelEditorCleanUp: typeof panelEditorCleanUp;
|
|
|
|
setDiscardChanges: typeof setDiscardChanges;
|
2020-02-15 13:31:11 -06:00
|
|
|
updatePanelEditorUIState: typeof updatePanelEditorUIState;
|
2020-02-11 07:57:16 -06:00
|
|
|
}
|
2020-02-08 11:29:09 -06:00
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
type Props = OwnProps & ConnectedProps & DispatchProps;
|
2020-02-09 11:48:14 -06:00
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
export class PanelEditorUnconnected extends PureComponent<Props> {
|
|
|
|
querySubscription: Unsubscribable;
|
2020-02-09 11:48:14 -06:00
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
componentDidMount() {
|
|
|
|
this.props.initPanelEditor(this.props.sourcePanel, this.props.dashboard);
|
2020-02-08 06:23:16 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
2020-02-11 07:57:16 -06:00
|
|
|
this.props.panelEditorCleanUp();
|
2020-02-07 07:59:04 -06:00
|
|
|
}
|
2019-12-16 02:18:48 -06:00
|
|
|
|
2020-02-07 07:59:04 -06:00
|
|
|
onPanelExit = () => {
|
2020-02-11 07:57:16 -06:00
|
|
|
this.props.updateLocation({
|
2020-04-04 08:44:55 -05:00
|
|
|
query: { editPanel: null, tab: null },
|
2020-02-07 07:59:04 -06:00
|
|
|
partial: true,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
onDiscard = () => {
|
2020-02-11 07:57:16 -06:00
|
|
|
this.props.setDiscardChanges(true);
|
2020-02-07 07:59:04 -06:00
|
|
|
this.props.updateLocation({
|
2020-04-04 08:44:55 -05:00
|
|
|
query: { editPanel: null, tab: null },
|
2020-02-07 07:59:04 -06:00
|
|
|
partial: true,
|
|
|
|
});
|
|
|
|
};
|
2019-12-16 02:18:48 -06:00
|
|
|
|
2020-04-18 11:00:54 -05:00
|
|
|
onOpenDashboardSettings = () => {
|
|
|
|
this.props.updateLocation({ query: { editview: 'settings' }, partial: true });
|
|
|
|
};
|
|
|
|
|
|
|
|
onSaveDashboard = () => {
|
|
|
|
appEvents.emit(CoreEvents.showModalReact, {
|
|
|
|
component: SaveDashboardModalProxy,
|
|
|
|
props: { dashboard: this.props.dashboard },
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-02-12 03:42:57 -06:00
|
|
|
onChangeTab = (tab: PanelEditorTab) => {
|
|
|
|
this.props.updateLocation({ query: { tab: tab.id }, partial: true });
|
|
|
|
};
|
|
|
|
|
2020-03-19 05:50:31 -05:00
|
|
|
onFieldConfigChange = (config: FieldConfigSource) => {
|
2020-02-11 07:57:16 -06:00
|
|
|
const { panel } = this.props;
|
2020-03-19 05:50:31 -05:00
|
|
|
|
|
|
|
panel.updateFieldConfig({
|
|
|
|
...config,
|
2020-02-08 11:29:09 -06:00
|
|
|
});
|
|
|
|
this.forceUpdate();
|
|
|
|
};
|
|
|
|
|
|
|
|
onPanelOptionsChanged = (options: any) => {
|
2020-02-11 07:57:16 -06:00
|
|
|
this.props.panel.updateOptions(options);
|
2020-02-08 11:29:09 -06:00
|
|
|
this.forceUpdate();
|
|
|
|
};
|
|
|
|
|
2020-03-20 09:15:04 -05:00
|
|
|
onPanelConfigChanged = (configKey: string, value: any) => {
|
|
|
|
// @ts-ignore
|
|
|
|
this.props.panel[configKey] = value;
|
|
|
|
this.props.panel.render();
|
|
|
|
this.forceUpdate();
|
|
|
|
};
|
2020-02-08 11:29:09 -06:00
|
|
|
|
2020-04-18 11:00:54 -05:00
|
|
|
onDragFinished = (pane: Pane, size?: number) => {
|
2020-02-08 05:01:10 -06:00
|
|
|
document.body.style.cursor = 'auto';
|
2020-04-18 11:00:54 -05:00
|
|
|
|
|
|
|
// When the drag handle is just clicked size is undefined
|
|
|
|
if (!size) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-15 13:31:11 -06:00
|
|
|
const targetPane = pane === Pane.Top ? 'topPaneSize' : 'rightPaneSize';
|
|
|
|
const { updatePanelEditorUIState } = this.props;
|
|
|
|
updatePanelEditorUIState({
|
|
|
|
[targetPane]: size,
|
|
|
|
});
|
2020-02-12 03:42:57 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
onDragStarted = () => {
|
|
|
|
document.body.style.cursor = 'row-resize';
|
2020-02-08 05:01:10 -06:00
|
|
|
};
|
|
|
|
|
2020-04-04 07:25:06 -05:00
|
|
|
onDiplayModeChange = (mode: DisplayMode) => {
|
2020-02-15 13:31:11 -06:00
|
|
|
const { updatePanelEditorUIState } = this.props;
|
|
|
|
updatePanelEditorUIState({
|
2020-04-04 07:25:06 -05:00
|
|
|
mode: mode,
|
2020-02-15 13:31:11 -06:00
|
|
|
});
|
2020-02-09 08:39:46 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
onTogglePanelOptions = () => {
|
2020-02-15 13:31:11 -06:00
|
|
|
const { uiState, updatePanelEditorUIState } = this.props;
|
|
|
|
updatePanelEditorUIState({ isPanelOptionsVisible: !uiState.isPanelOptionsVisible });
|
2020-02-09 08:39:46 -06:00
|
|
|
};
|
|
|
|
|
2020-04-23 13:36:42 -05:00
|
|
|
renderPanel = (styles: EditorStyles) => {
|
|
|
|
const { dashboard, panel, tabs, uiState } = this.props;
|
|
|
|
return (
|
|
|
|
<div className={cx(styles.mainPaneWrapper, tabs.length === 0 && styles.mainPaneWrapperNoTabs)}>
|
|
|
|
{this.renderPanelToolbar(styles)}
|
|
|
|
<div className={styles.panelWrapper}>
|
|
|
|
<AutoSizer>
|
|
|
|
{({ width, height }) => {
|
|
|
|
if (width < 3 || height < 3) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div className={styles.centeringContainer} style={{ width, height }}>
|
|
|
|
<div style={calculatePanelSize(uiState.mode, width, height, panel)}>
|
|
|
|
<DashboardPanel
|
|
|
|
dashboard={dashboard}
|
|
|
|
panel={panel}
|
|
|
|
isEditing={true}
|
|
|
|
isViewing={false}
|
|
|
|
isInView={true}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}}
|
|
|
|
</AutoSizer>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
2020-04-02 06:56:20 -05:00
|
|
|
renderHorizontalSplit(styles: EditorStyles) {
|
2020-02-15 13:31:11 -06:00
|
|
|
const { dashboard, panel, tabs, data, uiState } = this.props;
|
2020-04-23 13:36:42 -05:00
|
|
|
return tabs.length > 0 ? (
|
2020-02-09 08:39:46 -06:00
|
|
|
<SplitPane
|
|
|
|
split="horizontal"
|
|
|
|
minSize={50}
|
2020-02-12 03:42:57 -06:00
|
|
|
primary="first"
|
2020-02-15 13:31:11 -06:00
|
|
|
/* Use persisted state for default size */
|
|
|
|
defaultSize={uiState.topPaneSize}
|
2020-02-12 03:42:57 -06:00
|
|
|
pane2Style={{ minHeight: 0 }}
|
2020-02-09 08:39:46 -06:00
|
|
|
resizerClassName={styles.resizerH}
|
2020-02-12 03:42:57 -06:00
|
|
|
onDragStarted={this.onDragStarted}
|
2020-02-15 13:31:11 -06:00
|
|
|
onDragFinished={size => this.onDragFinished(Pane.Top, size)}
|
2020-02-09 08:39:46 -06:00
|
|
|
>
|
2020-04-23 13:36:42 -05:00
|
|
|
{this.renderPanel(styles)}
|
2020-02-25 10:58:20 -06:00
|
|
|
<div className={styles.tabsWrapper}>
|
2020-02-12 03:42:57 -06:00
|
|
|
<PanelEditorTabs panel={panel} dashboard={dashboard} tabs={tabs} onChangeTab={this.onChangeTab} data={data} />
|
2020-02-09 08:39:46 -06:00
|
|
|
</div>
|
|
|
|
</SplitPane>
|
2020-04-23 13:36:42 -05:00
|
|
|
) : (
|
|
|
|
this.renderPanel(styles)
|
2020-02-09 03:50:58 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-02 06:56:20 -05:00
|
|
|
renderTemplateVariables(styles: EditorStyles) {
|
|
|
|
const { variables } = this.props;
|
|
|
|
|
|
|
|
if (!variables.length) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={styles.variablesWrapper}>
|
|
|
|
<SubMenuItems variables={variables} />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-04 07:25:06 -05:00
|
|
|
renderPanelToolbar(styles: EditorStyles) {
|
2020-03-27 00:40:14 -05:00
|
|
|
const { dashboard, location, uiState } = this.props;
|
2020-02-25 10:58:20 -06:00
|
|
|
|
|
|
|
return (
|
2020-04-04 07:25:06 -05:00
|
|
|
<div className={styles.panelToolbar}>
|
|
|
|
{this.renderTemplateVariables(styles)}
|
|
|
|
<div className="flex-grow-1" />
|
|
|
|
<div className={styles.toolbarItem}>
|
2020-04-14 11:52:56 -05:00
|
|
|
<RadioButtonGroup value={uiState.mode} options={displayModes} onChange={this.onDiplayModeChange} />
|
2020-04-04 07:25:06 -05:00
|
|
|
</div>
|
|
|
|
<div className={styles.toolbarItem}>
|
|
|
|
<DashNavTimeControls dashboard={dashboard} location={location} updateLocation={updateLocation} />
|
|
|
|
</div>
|
|
|
|
{!uiState.isPanelOptionsVisible && (
|
|
|
|
<div className={styles.toolbarItem}>
|
|
|
|
<DashNavButton onClick={this.onTogglePanelOptions} tooltip="Open options pane" classSuffix="close-options">
|
@grafana/ui: Create Icon component and replace part of the icons (#23402)
* Part1: Unicons implementation (#23197)
* Create a new Icon component
* Update icons in main sidebar
* Update icons in Useful links and in react components on main site
* Update icons in Useful links and in main top navigation
* Adjust sizing
* Update panel navigation and timepicker
* Update icons in Panel menu
* NewPanelEditor: Fixed so that test alert rule works in new edit mode (#23179)
* Update icons in add panel widget
* Resolve merge conflict
* Fix part of the test errors and type errors
* Fix storybook errors
* Update getAvailableIcons import in storybook knobs
* Fix import path
* Fix SyntaxError: Cannot use import statement outside a module in test environment error
* Remove dynamic imports
* Remove types as using @ts-ignore
* Update snapshot test
* Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax
* Remove color prop from icon, remove color implemetation in mono icons
* Update navbar styling
* Move toPascalCase to utils/string
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
* Resolve type errors resulted from merge
* Part2: Unicons implementation (#23266)
* Create a new Icon component
* Update icons in main sidebar
* Update icons in Useful links and in react components on main site
* Update icons in Useful links and in main top navigation
* Adjust sizing
* Update panel navigation and timepicker
* Update icons in Panel menu
* Update icons in add panel widget
* Resolve merge conflict
* Fix part of the test errors and type errors
* Fix storybook errors
* Update getAvailableIcons import in storybook knobs
* Fix import path
* Fix SyntaxError: Cannot use import statement outside a module in test environment error
* Remove dynamic imports
* Remove types as using @ts-ignore
* Update snapshot test
* Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax
* Implment icons in Tabs
* Implement icons in search items and empty list
* Update buttons
* Update button-related snapshot tests
* Update icons in modals and page headers
* Create anfular wrapper and update all icons on search screen
* Update sizing, remove colors, update snapshot tests
* Remove color prop from icon, remove color implemetation in mono icons
* Remove color props from monochrome icons
* Complete update of icons for search screen
* Update icons for infor tooltips, playlist, permissions
* Support temporarly font awesome icons used in enterprise grafana
* Part1: Unicons implementation (#23197)
* Create a new Icon component
* Update icons in main sidebar
* Update icons in Useful links and in react components on main site
* Update icons in Useful links and in main top navigation
* Adjust sizing
* Update panel navigation and timepicker
* Update icons in Panel menu
* NewPanelEditor: Fixed so that test alert rule works in new edit mode (#23179)
* Update icons in add panel widget
* Resolve merge conflict
* Fix part of the test errors and type errors
* Fix storybook errors
* Update getAvailableIcons import in storybook knobs
* Fix import path
* Fix SyntaxError: Cannot use import statement outside a module in test environment error
* Remove dynamic imports
* Remove types as using @ts-ignore
* Update snapshot test
* Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax
* Remove color prop from icon, remove color implemetation in mono icons
* Update navbar styling
* Move toPascalCase to utils/string
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
* Icons update
* Add optional chaining to for isFontAwesome variable
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
* Part3: Unicons implementation (#23356)
* Create a new Icon component
* Update icons in main sidebar
* Update icons in Useful links and in react components on main site
* Update icons in Useful links and in main top navigation
* Adjust sizing
* Update panel navigation and timepicker
* Update icons in Panel menu
* Update icons in add panel widget
* Resolve merge conflict
* Fix part of the test errors and type errors
* Fix storybook errors
* Update getAvailableIcons import in storybook knobs
* Fix import path
* Fix SyntaxError: Cannot use import statement outside a module in test environment error
* Remove dynamic imports
* Remove types as using @ts-ignore
* Update snapshot test
* Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax
* Implment icons in Tabs
* Implement icons in search items and empty list
* Update buttons
* Update button-related snapshot tests
* Update icons in modals and page headers
* Create anfular wrapper and update all icons on search screen
* Update sizing, remove colors, update snapshot tests
* Remove color prop from icon, remove color implemetation in mono icons
* Remove color props from monochrome icons
* Complete update of icons for search screen
* Update icons for infor tooltips, playlist, permissions
* Support temporarly font awesome icons used in enterprise grafana
* Part1: Unicons implementation (#23197)
* Create a new Icon component
* Update icons in main sidebar
* Update icons in Useful links and in react components on main site
* Update icons in Useful links and in main top navigation
* Adjust sizing
* Update panel navigation and timepicker
* Update icons in Panel menu
* NewPanelEditor: Fixed so that test alert rule works in new edit mode (#23179)
* Update icons in add panel widget
* Resolve merge conflict
* Fix part of the test errors and type errors
* Fix storybook errors
* Update getAvailableIcons import in storybook knobs
* Fix import path
* Fix SyntaxError: Cannot use import statement outside a module in test environment error
* Remove dynamic imports
* Remove types as using @ts-ignore
* Update snapshot test
* Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax
* Remove color prop from icon, remove color implemetation in mono icons
* Update navbar styling
* Move toPascalCase to utils/string
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
* Update icons in Explore
* Update icons in alerting
* Update + and x buttons
* Update icons in configurations and settings
* Update close icons
* Update icons in rich history
* Update alert messages
* Add optional chaining to for isFontAwesome variable
* Remove icon mock, set up jest.config
* Fix navbar plus icon
* Fir enable-bacground to enableBackgournd
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
* Merge remote branch origin master to icons-unicons
* Revert "Merge remote branch origin master to icons-unicons"
This reverts commit 3f25d50a39a940883fefe73ce51219139c1ed37f.
* Size-up dashnav icons
* Fix alerting icons, panel headers, update tests
* Fix typecheck error
* Adjustments - add panel icon, spacing
* Set TerserPlugin sourceMap to false to prevent running out of memory when publishing storybook
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
2020-04-08 07:33:31 -05:00
|
|
|
<Icon name="angle-left" /> <span style={{ paddingLeft: '6px' }}>Show options</span>
|
2020-04-04 07:25:06 -05:00
|
|
|
</DashNavButton>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
editorToolbar(styles: EditorStyles) {
|
|
|
|
const { dashboard } = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={styles.editorToolbar}>
|
2020-02-25 10:58:20 -06:00
|
|
|
<div className={styles.toolbarLeft}>
|
2020-04-11 09:07:18 -05:00
|
|
|
<BackButton onClick={this.onPanelExit} surface="panel" />
|
2020-03-27 00:40:14 -05:00
|
|
|
<span className={styles.editorTitle}>{dashboard.title} / Edit Panel</span>
|
2020-02-25 10:58:20 -06:00
|
|
|
</div>
|
|
|
|
<div className={styles.toolbarLeft}>
|
|
|
|
<div className={styles.toolbarItem}>
|
2020-04-18 11:00:54 -05:00
|
|
|
<Button
|
|
|
|
icon="cog"
|
|
|
|
onClick={this.onOpenDashboardSettings}
|
|
|
|
variant="secondary"
|
|
|
|
title="Open dashboad settings"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div className={styles.toolbarItem}>
|
|
|
|
<Button onClick={this.onDiscard} variant="secondary" title="Undo all changes">
|
|
|
|
Discard
|
2020-04-04 07:25:06 -05:00
|
|
|
</Button>
|
2020-02-25 10:58:20 -06:00
|
|
|
</div>
|
|
|
|
<div className={styles.toolbarItem}>
|
2020-04-18 11:00:54 -05:00
|
|
|
<Button onClick={this.onSaveDashboard} variant="secondary" title="Apply changes and save dashboard">
|
|
|
|
Save
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
<div className={styles.toolbarItem}>
|
|
|
|
<Button onClick={this.onPanelExit} title="Apply changes and go back to dashboard">
|
|
|
|
Apply
|
|
|
|
</Button>
|
2020-02-25 10:58:20 -06:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-02 06:56:20 -05:00
|
|
|
renderOptionsPane() {
|
2020-04-22 12:21:48 -05:00
|
|
|
const { plugin, dashboard, panel, uiState } = this.props;
|
2020-03-20 09:15:04 -05:00
|
|
|
|
|
|
|
if (!plugin) {
|
2020-03-28 17:11:50 -05:00
|
|
|
return <div />;
|
2020-03-20 09:15:04 -05:00
|
|
|
}
|
2020-02-28 04:04:40 -06:00
|
|
|
|
2020-02-25 10:58:20 -06:00
|
|
|
return (
|
2020-03-20 09:15:04 -05:00
|
|
|
<OptionsPaneContent
|
|
|
|
plugin={plugin}
|
|
|
|
dashboard={dashboard}
|
|
|
|
panel={panel}
|
2020-04-05 07:09:43 -05:00
|
|
|
width={uiState.rightPaneSize as number}
|
2020-03-30 07:39:18 -05:00
|
|
|
onClose={this.onTogglePanelOptions}
|
2020-03-20 09:15:04 -05:00
|
|
|
onFieldConfigsChange={this.onFieldConfigChange}
|
|
|
|
onPanelOptionsChanged={this.onPanelOptionsChanged}
|
|
|
|
onPanelConfigChange={this.onPanelConfigChanged}
|
|
|
|
/>
|
2020-02-25 10:58:20 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-02 06:56:20 -05:00
|
|
|
renderWithOptionsPane(styles: EditorStyles) {
|
2020-02-25 10:58:20 -06:00
|
|
|
const { uiState } = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<SplitPane
|
|
|
|
split="vertical"
|
|
|
|
minSize={100}
|
|
|
|
primary="second"
|
|
|
|
/* Use persisted state for default size */
|
|
|
|
defaultSize={uiState.rightPaneSize}
|
|
|
|
resizerClassName={styles.resizerV}
|
|
|
|
onDragStarted={() => (document.body.style.cursor = 'col-resize')}
|
|
|
|
onDragFinished={size => this.onDragFinished(Pane.Right, size)}
|
|
|
|
>
|
|
|
|
{this.renderHorizontalSplit(styles)}
|
2020-04-02 06:56:20 -05:00
|
|
|
{this.renderOptionsPane()}
|
2020-02-25 10:58:20 -06:00
|
|
|
</SplitPane>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-12-16 02:18:48 -06:00
|
|
|
render() {
|
2020-02-25 10:58:20 -06:00
|
|
|
const { initDone, uiState } = this.props;
|
2020-03-30 07:39:18 -05:00
|
|
|
const styles = getStyles(config.theme, this.props);
|
2019-12-16 02:18:48 -06:00
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
if (!initDone) {
|
2019-12-16 02:18:48 -06:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2020-04-17 11:04:57 -05:00
|
|
|
<div className={styles.wrapper}>
|
|
|
|
{this.editorToolbar(styles)}
|
|
|
|
<div className={styles.verticalSplitPanesWrapper}>
|
|
|
|
{uiState.isPanelOptionsVisible ? this.renderWithOptionsPane(styles) : this.renderHorizontalSplit(styles)}
|
2020-02-08 12:31:17 -06:00
|
|
|
</div>
|
2020-04-17 11:04:57 -05:00
|
|
|
</div>
|
2019-12-16 02:18:48 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-02-07 07:59:04 -06:00
|
|
|
|
2020-02-12 03:42:57 -06:00
|
|
|
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state, props) => {
|
2020-04-20 01:47:25 -05:00
|
|
|
const panel = state.panelEditor.getPanel();
|
2020-02-13 09:06:45 -06:00
|
|
|
const { plugin } = getPanelStateById(state.dashboard, panel.id);
|
2020-02-12 03:42:57 -06:00
|
|
|
|
|
|
|
return {
|
|
|
|
location: state.location,
|
|
|
|
plugin: plugin,
|
2020-04-20 01:47:25 -05:00
|
|
|
panel: state.panelEditor.getPanel(),
|
|
|
|
data: state.panelEditor.getData(),
|
|
|
|
initDone: state.panelEditor.initDone,
|
2020-02-12 03:42:57 -06:00
|
|
|
tabs: getPanelEditorTabs(state.location, plugin),
|
2020-04-20 01:47:25 -05:00
|
|
|
uiState: state.panelEditor.ui,
|
2020-04-02 06:56:20 -05:00
|
|
|
variables: getVariables(state),
|
2020-02-12 03:42:57 -06:00
|
|
|
};
|
|
|
|
};
|
2020-02-07 07:59:04 -06:00
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
|
2020-02-07 07:59:04 -06:00
|
|
|
updateLocation,
|
2020-02-11 07:57:16 -06:00
|
|
|
initPanelEditor,
|
|
|
|
panelEditorCleanUp,
|
|
|
|
setDiscardChanges,
|
2020-02-15 13:31:11 -06:00
|
|
|
updatePanelEditorUIState,
|
2020-02-07 07:59:04 -06:00
|
|
|
};
|
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
export const PanelEditor = connect(mapStateToProps, mapDispatchToProps)(PanelEditorUnconnected);
|
|
|
|
|
2020-04-05 07:09:43 -05:00
|
|
|
enum Pane {
|
|
|
|
Right,
|
|
|
|
Top,
|
|
|
|
}
|
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
/*
|
|
|
|
* Styles
|
|
|
|
*/
|
2020-04-18 11:00:54 -05:00
|
|
|
export const getStyles = stylesFactory((theme: GrafanaTheme, props: Props) => {
|
2020-03-30 07:39:18 -05:00
|
|
|
const { uiState } = props;
|
2020-04-14 04:32:14 -05:00
|
|
|
const handleColor = theme.palette.blue95;
|
2020-04-23 13:36:42 -05:00
|
|
|
const paneSpacing = theme.spacing.md;
|
2020-02-11 07:57:16 -06:00
|
|
|
|
|
|
|
const resizer = css`
|
|
|
|
font-style: italic;
|
2020-02-12 03:42:57 -06:00
|
|
|
background: transparent;
|
|
|
|
border-top: 0;
|
|
|
|
border-right: 0;
|
|
|
|
border-bottom: 0;
|
|
|
|
border-left: 0;
|
|
|
|
border-color: transparent;
|
|
|
|
border-style: solid;
|
|
|
|
transition: 0.2s border-color ease-in-out;
|
|
|
|
|
2020-02-11 07:57:16 -06:00
|
|
|
&:hover {
|
2020-02-12 03:42:57 -06:00
|
|
|
border-color: ${handleColor};
|
2020-02-11 07:57:16 -06:00
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
return {
|
|
|
|
wrapper: css`
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
position: fixed;
|
2020-04-18 11:00:54 -05:00
|
|
|
z-index: ${theme.zIndex.sidemenu};
|
2020-02-11 07:57:16 -06:00
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
right: 0;
|
|
|
|
bottom: 0;
|
2020-04-12 08:05:49 -05:00
|
|
|
background: ${theme.colors.dashboardBg};
|
2020-02-25 10:58:20 -06:00
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
`,
|
2020-04-04 07:25:06 -05:00
|
|
|
verticalSplitPanesWrapper: css`
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
|
|
|
position: relative;
|
|
|
|
`,
|
2020-03-30 07:39:18 -05:00
|
|
|
mainPaneWrapper: css`
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
height: 100%;
|
2020-02-25 10:58:20 -06:00
|
|
|
width: 100%;
|
2020-04-23 13:36:42 -05:00
|
|
|
padding-right: ${uiState.isPanelOptionsVisible ? 0 : paneSpacing};
|
|
|
|
`,
|
|
|
|
mainPaneWrapperNoTabs: css`
|
|
|
|
padding-bottom: ${paneSpacing};
|
2020-02-11 07:57:16 -06:00
|
|
|
`,
|
2020-04-02 06:56:20 -05:00
|
|
|
variablesWrapper: css`
|
2020-04-04 07:25:06 -05:00
|
|
|
display: flex;
|
2020-04-02 06:56:20 -05:00
|
|
|
`,
|
2020-02-11 07:57:16 -06:00
|
|
|
panelWrapper: css`
|
2020-03-30 07:39:18 -05:00
|
|
|
flex: 1 1 0;
|
|
|
|
min-height: 0;
|
2020-02-11 07:57:16 -06:00
|
|
|
width: 100%;
|
2020-04-23 13:36:42 -05:00
|
|
|
padding-left: ${paneSpacing};
|
2020-02-11 07:57:16 -06:00
|
|
|
`,
|
|
|
|
resizerV: cx(
|
|
|
|
resizer,
|
|
|
|
css`
|
|
|
|
cursor: col-resize;
|
2020-04-23 13:36:42 -05:00
|
|
|
width: ${paneSpacing};
|
2020-02-12 03:42:57 -06:00
|
|
|
border-right-width: 1px;
|
2020-04-10 09:37:26 -05:00
|
|
|
margin-top: 18px;
|
2020-02-11 07:57:16 -06:00
|
|
|
`
|
|
|
|
),
|
|
|
|
resizerH: cx(
|
|
|
|
resizer,
|
|
|
|
css`
|
2020-04-23 13:36:42 -05:00
|
|
|
height: ${paneSpacing};
|
2020-02-11 07:57:16 -06:00
|
|
|
cursor: row-resize;
|
2020-02-12 03:42:57 -06:00
|
|
|
position: relative;
|
2020-04-10 09:37:26 -05:00
|
|
|
top: 0px;
|
2020-02-12 03:42:57 -06:00
|
|
|
z-index: 1;
|
|
|
|
border-top-width: 1px;
|
2020-04-23 13:36:42 -05:00
|
|
|
margin-left: ${paneSpacing};
|
2020-02-11 07:57:16 -06:00
|
|
|
`
|
|
|
|
),
|
2020-02-25 10:58:20 -06:00
|
|
|
tabsWrapper: css`
|
2020-02-11 07:57:16 -06:00
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
2020-02-12 03:42:57 -06:00
|
|
|
`,
|
2020-04-04 07:25:06 -05:00
|
|
|
editorToolbar: css`
|
2020-02-11 07:57:16 -06:00
|
|
|
display: flex;
|
2020-02-25 10:58:20 -06:00
|
|
|
padding: ${theme.spacing.sm};
|
2020-04-04 07:25:06 -05:00
|
|
|
background: ${theme.colors.panelBg};
|
|
|
|
justify-content: space-between;
|
|
|
|
border-bottom: 1px solid ${theme.colors.panelBorder};
|
|
|
|
`,
|
|
|
|
panelToolbar: css`
|
|
|
|
display: flex;
|
2020-04-23 13:36:42 -05:00
|
|
|
padding: ${paneSpacing} 0 ${paneSpacing} ${paneSpacing};
|
2020-02-11 07:57:16 -06:00
|
|
|
justify-content: space-between;
|
2020-04-04 07:25:06 -05:00
|
|
|
flex-wrap: wrap;
|
2020-02-11 07:57:16 -06:00
|
|
|
`,
|
|
|
|
toolbarLeft: css`
|
2020-02-17 00:28:00 -06:00
|
|
|
padding-left: ${theme.spacing.sm};
|
2020-02-11 07:57:16 -06:00
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
`,
|
2020-02-12 03:42:57 -06:00
|
|
|
toolbarItem: css`
|
|
|
|
margin-right: ${theme.spacing.sm};
|
2020-02-25 10:58:20 -06:00
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
margin-right: 0;
|
|
|
|
}
|
2020-02-12 03:42:57 -06:00
|
|
|
`,
|
2020-02-11 07:57:16 -06:00
|
|
|
centeringContainer: css`
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
`,
|
2020-03-27 00:40:14 -05:00
|
|
|
editorTitle: css`
|
|
|
|
font-size: ${theme.typography.size.lg};
|
|
|
|
padding-left: ${theme.spacing.md};
|
|
|
|
`,
|
2020-02-11 07:57:16 -06:00
|
|
|
};
|
|
|
|
});
|
2020-04-02 06:56:20 -05:00
|
|
|
|
|
|
|
type EditorStyles = ReturnType<typeof getStyles>;
|