Dashbord: Adding elements via buttons in canvas v2 (#102806)
This commit is contained in:
@@ -91,10 +91,13 @@ export function getDashboardGridStyles(theme: GrafanaTheme2) {
|
||||
},
|
||||
|
||||
'.dashboard-canvas-add-button': {
|
||||
opacity: 0,
|
||||
opacity: 0.5,
|
||||
transition: theme.transitions.create('opacity'),
|
||||
filter: `grayscale(100%)`,
|
||||
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
filter: 'unset',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ export class AutoGridLayoutManager
|
||||
vizPanel.clearParent();
|
||||
|
||||
this.state.layout.setState({
|
||||
children: [new AutoGridItem({ body: vizPanel }), ...this.state.layout.state.children],
|
||||
children: [...this.state.layout.state.children, new AutoGridItem({ body: vizPanel })],
|
||||
});
|
||||
|
||||
this.publishEvent(new NewObjectAddedToCanvasEvent(vizPanel), true);
|
||||
|
||||
+80
-8
@@ -1,24 +1,31 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { LazyLoader, SceneComponentProps } from '@grafana/scenes';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { LazyLoader, SceneComponentProps, sceneGraph } from '@grafana/scenes';
|
||||
import { Button, Dropdown, Menu, useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { getDashboardSceneFor } from '../../utils/utils';
|
||||
import { getDefaultVizPanel, useDashboardState } from '../../utils/utils';
|
||||
import { addNewRowTo, addNewTabTo } from '../layouts-shared/addNew';
|
||||
|
||||
import { AutoGridLayout, AutoGridLayoutState } from './ResponsiveGridLayout';
|
||||
import { AutoGridLayoutManager } from './ResponsiveGridLayoutManager';
|
||||
|
||||
export function AutoGridLayoutRenderer({ model }: SceneComponentProps<AutoGridLayout>) {
|
||||
const { children, isHidden, isLazy } = model.useState();
|
||||
const styles = useStyles2(getStyles, model.state);
|
||||
const { layoutOrchestrator } = getDashboardSceneFor(model).state;
|
||||
const { layoutOrchestrator, isEditing } = useDashboardState(model);
|
||||
const layoutManager = sceneGraph.getAncestor(model, AutoGridLayoutManager);
|
||||
const { fillScreen } = layoutManager.useState();
|
||||
|
||||
if (isHidden || !layoutOrchestrator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container} ref={(ref) => model.setRef(ref)}>
|
||||
<div
|
||||
className={cx(styles.container, fillScreen && styles.containerFillScreen, isEditing && styles.containerEditing)}
|
||||
>
|
||||
{children.map((item) =>
|
||||
isLazy ? (
|
||||
<LazyLoader key={item.state.key!} className={styles.container}>
|
||||
@@ -28,6 +35,47 @@ export function AutoGridLayoutRenderer({ model }: SceneComponentProps<AutoGridLa
|
||||
<item.Component key={item.state.key} model={item} />
|
||||
)
|
||||
)}
|
||||
{isEditing && (
|
||||
<div className={cx(styles.addAction, 'dashboard-canvas-add-button')}>
|
||||
<Button
|
||||
variant="primary"
|
||||
fill="text"
|
||||
icon="plus"
|
||||
onClick={() => layoutManager.addPanel(getDefaultVizPanel())}
|
||||
>
|
||||
<Trans i18nKey="dashboard.canvas-actions.add-panel">Add panel</Trans>
|
||||
</Button>
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
icon="list-ul"
|
||||
label={t('dashboard.canvas-actions.group-into-row', 'Group into row')}
|
||||
onClick={() => {
|
||||
addNewRowTo(layoutManager);
|
||||
}}
|
||||
></Menu.Item>
|
||||
<Menu.Item
|
||||
icon="layers"
|
||||
label={t('dashboard.canvas-actions.group-into-tab', 'Group into tab')}
|
||||
onClick={() => {
|
||||
addNewTabTo(layoutManager);
|
||||
}}
|
||||
></Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant="primary"
|
||||
fill="text"
|
||||
icon="layers"
|
||||
onClick={() => layoutManager.addPanel(getDefaultVizPanel())}
|
||||
>
|
||||
<Trans i18nKey="dashboard.canvas-actions.group-panels">Group panels</Trans>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -44,8 +92,6 @@ const getStyles = (theme: GrafanaTheme2, state: AutoGridLayoutState) => ({
|
||||
justifyItems: state.justifyItems || 'unset',
|
||||
alignItems: state.alignItems || 'unset',
|
||||
justifyContent: state.justifyContent || 'unset',
|
||||
flexGrow: 1,
|
||||
|
||||
[theme.breakpoints.down('md')]: state.md
|
||||
? {
|
||||
gridTemplateRows: state.md.templateRows,
|
||||
@@ -57,6 +103,20 @@ const getStyles = (theme: GrafanaTheme2, state: AutoGridLayoutState) => ({
|
||||
justifyContent: state.md.justifyContent,
|
||||
}
|
||||
: undefined,
|
||||
// Show add action when hovering over the grid
|
||||
'&:hover': {
|
||||
'.dashboard-canvas-add-button': {
|
||||
opacity: 1,
|
||||
filter: 'unset',
|
||||
},
|
||||
},
|
||||
}),
|
||||
containerFillScreen: css({
|
||||
flexGrow: 1,
|
||||
}),
|
||||
containerEditing: css({
|
||||
paddingBottom: theme.spacing(5),
|
||||
position: 'relative',
|
||||
}),
|
||||
wrapper: css({
|
||||
display: 'grid',
|
||||
@@ -64,6 +124,18 @@ const getStyles = (theme: GrafanaTheme2, state: AutoGridLayoutState) => ({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}),
|
||||
addAction: css({
|
||||
position: 'absolute',
|
||||
padding: theme.spacing(1, 0),
|
||||
height: theme.spacing(5),
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
opacity: 0,
|
||||
[theme.transitions.handleMotion('no-preference', 'reduce')]: {
|
||||
transition: theme.transitions.create('opacity'),
|
||||
},
|
||||
}),
|
||||
dragging: css({
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
|
||||
@@ -120,30 +120,6 @@ export class RowItem
|
||||
this.getLayout().addPanel(panel);
|
||||
}
|
||||
|
||||
public onAddRowAbove() {
|
||||
this._getParentLayout().addRowAbove(this);
|
||||
}
|
||||
|
||||
public onAddRowBelow() {
|
||||
this._getParentLayout().addRowBelow(this);
|
||||
}
|
||||
|
||||
public onMoveUp() {
|
||||
this._getParentLayout().moveRowUp(this);
|
||||
}
|
||||
|
||||
public onMoveDown() {
|
||||
this._getParentLayout().moveRowDown(this);
|
||||
}
|
||||
|
||||
public isFirstRow(): boolean {
|
||||
return this._getParentLayout().isFirstRow(this);
|
||||
}
|
||||
|
||||
public isLastRow(): boolean {
|
||||
return this._getParentLayout().isLastRow(this);
|
||||
}
|
||||
|
||||
public setIsDropTarget(isDropTarget: boolean) {
|
||||
if (!!this.state.isDropTarget !== isDropTarget) {
|
||||
this.setState({ isDropTarget });
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Button, Dropdown, Menu, ToolbarButtonRow, useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { RowItem } from './RowItem';
|
||||
|
||||
interface RowItemMenuProps {
|
||||
model: RowItem;
|
||||
}
|
||||
|
||||
export function RowItemMenu({ model }: RowItemMenuProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<ToolbarButtonRow className={cx(styles.container, 'dashboard-canvas-add-button')}>
|
||||
<Dropdown
|
||||
placement="bottom-end"
|
||||
overlay={() => (
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
ariaLabel={t('dashboard.rows-layout.row.menu.add-panel', 'Panel')}
|
||||
label={t('dashboard.rows-layout.row.menu.add-panel', 'Panel')}
|
||||
onClick={() => model.onAddPanel()}
|
||||
/>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
ariaLabel={t('dashboard.rows-layout.row.menu.add-row-above', 'Row above')}
|
||||
label={t('dashboard.rows-layout.row.menu.add-row-above', 'Row above')}
|
||||
onClick={() => model.onAddRowAbove()}
|
||||
/>
|
||||
<Menu.Item
|
||||
ariaLabel={t('dashboard.rows-layout.row.menu.add-row-below', 'Row below')}
|
||||
label={t('dashboard.rows-layout.row.menu.add-row-below', 'Row below')}
|
||||
onClick={() => model.onAddRowBelow()}
|
||||
/>
|
||||
</Menu>
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
aria-label={t('dashboard.rows-layout.row.menu.add', 'Add row')}
|
||||
title={t('dashboard.rows-layout.row.menu.add', 'Add row')}
|
||||
tooltip={t('dashboard.rows-layout.row.menu.add', 'Add row')}
|
||||
icon="plus"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
>
|
||||
<Trans i18nKey="grafana-ui.tags-input.add">Add</Trans>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<Dropdown
|
||||
placement="bottom-end"
|
||||
overlay={() => (
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
aria-label={t('dashboard.rows-layout.row.menu.move-up', 'Move row up')}
|
||||
label={t('dashboard.rows-layout.row.menu.move-up', 'Move row up')}
|
||||
onClick={() => model.onMoveUp()}
|
||||
disabled={model.isFirstRow()}
|
||||
/>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
aria-label={t('dashboard.rows-layout.row.menu.move-down', 'Move row down')}
|
||||
label={t('dashboard.rows-layout.row.menu.move-down', 'Move row down')}
|
||||
onClick={() => model.onMoveDown()}
|
||||
disabled={model.isLastRow()}
|
||||
/>
|
||||
</Menu>
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
aria-label={t('dashboard.rows-layout.row.menu.move-row', 'Move row')}
|
||||
title={t('dashboard.rows-layout.row.menu.move-row', 'Move row')}
|
||||
tooltip={t('dashboard.rows-layout.row.menu.move-row', 'Move row')}
|
||||
icon="arrows-v"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
/>
|
||||
</Dropdown>
|
||||
</ToolbarButtonRow>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
container: css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'flex-end',
|
||||
gap: theme.spacing(1),
|
||||
}),
|
||||
});
|
||||
@@ -14,9 +14,9 @@ import {
|
||||
useInterpolatedTitle,
|
||||
useIsConditionallyHidden,
|
||||
} from '../../utils/utils';
|
||||
import { DashboardScene } from '../DashboardScene';
|
||||
|
||||
import { RowItem } from './RowItem';
|
||||
import { RowItemMenu } from './RowItemMenu';
|
||||
|
||||
export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
|
||||
const { layout, collapse: isCollapsed, fillScreen, hideHeader: isHeaderHidden, isDropTarget } = model.useState();
|
||||
@@ -27,6 +27,7 @@ export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
|
||||
const title = useInterpolatedTitle(model);
|
||||
const styles = useStyles2(getStyles);
|
||||
const clearStyles = useStyles2(clearButtonStyles);
|
||||
const isTopLevel = model.parent?.parent instanceof DashboardScene;
|
||||
|
||||
const shouldGrow = !isCollapsed && fillScreen;
|
||||
const isHidden = isConditionallyHidden && !isEditing;
|
||||
@@ -83,7 +84,14 @@ export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
|
||||
data-testid={selectors.components.DashboardRow.title(title!)}
|
||||
>
|
||||
<Icon name={isCollapsed ? 'angle-right' : 'angle-down'} />
|
||||
<span className={cx(styles.rowTitle, isHeaderHidden && styles.rowTitleHidden)} role="heading">
|
||||
<span
|
||||
className={cx(
|
||||
styles.rowTitle,
|
||||
isHeaderHidden && styles.rowTitleHidden,
|
||||
!isTopLevel && styles.rowTitleNested
|
||||
)}
|
||||
role="heading"
|
||||
>
|
||||
{title}
|
||||
{isHeaderHidden && (
|
||||
<Tooltip
|
||||
@@ -94,7 +102,6 @@ export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
{!isClone && isEditing && <RowItemMenu model={model} />}
|
||||
</div>
|
||||
)}
|
||||
{!isCollapsed && <layout.Component model={layout} />}
|
||||
@@ -108,7 +115,7 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
padding: theme.spacing(0.5),
|
||||
padding: theme.spacing(0.5, 0.5, 0.5, 0),
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing(1),
|
||||
}),
|
||||
@@ -137,11 +144,28 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
rowTitleHidden: css({
|
||||
textDecoration: 'line-through',
|
||||
}),
|
||||
rowTitleNested: css({
|
||||
fontSize: theme.typography.body.fontSize,
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
}),
|
||||
wrapper: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
minHeight: '100px',
|
||||
'> div:nth-child(2)': {
|
||||
marginLeft: theme.spacing(3),
|
||||
position: 'relative',
|
||||
'&:before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: `-8px`,
|
||||
bottom: 0,
|
||||
left: '-16px',
|
||||
width: '1px',
|
||||
backgroundColor: theme.colors.border.weak,
|
||||
},
|
||||
},
|
||||
}),
|
||||
wrapperEditing: css({
|
||||
padding: theme.spacing(0.5),
|
||||
|
||||
+12
-1
@@ -2,12 +2,16 @@ import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { SceneComponentProps } from '@grafana/scenes';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { Button, useStyles2 } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
|
||||
import { useDashboardState } from '../../utils/utils';
|
||||
|
||||
import { RowsLayoutManager } from './RowsLayoutManager';
|
||||
|
||||
export function RowLayoutManagerRenderer({ model }: SceneComponentProps<RowsLayoutManager>) {
|
||||
const { rows } = model.useState();
|
||||
const { isEditing } = useDashboardState(model);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
@@ -15,6 +19,13 @@ export function RowLayoutManagerRenderer({ model }: SceneComponentProps<RowsLayo
|
||||
{rows.map((row) => (
|
||||
<row.Component model={row} key={row.state.key!} />
|
||||
))}
|
||||
{isEditing && (
|
||||
<div className="dashboard-canvas-add-button">
|
||||
<Button icon="plus" variant="primary" fill="text" onClick={() => model.addNewRow()}>
|
||||
<Trans i18nKey="dashboard.canvas-actions.new-row">New row</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,28 +106,8 @@ export class TabItem
|
||||
this.getLayout().addPanel(panel);
|
||||
}
|
||||
|
||||
public onAddTabBefore() {
|
||||
this._getParentLayout().addTabBefore(this);
|
||||
}
|
||||
|
||||
public onAddTabAfter() {
|
||||
this._getParentLayout().addTabAfter(this);
|
||||
}
|
||||
|
||||
public onMoveLeft() {
|
||||
this._getParentLayout().moveTabLeft(this);
|
||||
}
|
||||
|
||||
public onMoveRight() {
|
||||
this._getParentLayout().moveTabRight(this);
|
||||
}
|
||||
|
||||
public isFirstTab(): boolean {
|
||||
return this._getParentLayout().isFirstTab(this);
|
||||
}
|
||||
|
||||
public isLastTab(): boolean {
|
||||
return this._getParentLayout().isLastTab(this);
|
||||
public onAddTab() {
|
||||
this._getParentLayout().addNewTab();
|
||||
}
|
||||
|
||||
public onChangeTitle(title: string) {
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Button, Dropdown, Menu, ToolbarButtonRow, useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { TabItem } from './TabItem';
|
||||
|
||||
interface Props {
|
||||
model: TabItem;
|
||||
}
|
||||
|
||||
export function TabItemMenu({ model }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<ToolbarButtonRow className={styles.container}>
|
||||
<Dropdown
|
||||
placement="bottom-end"
|
||||
overlay={() => (
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
ariaLabel={t('dashboard.tabs-layout.tab.menu.add-panel', 'Panel')}
|
||||
label={t('dashboard.tabs-layout.tab.menu.add-panel', 'Panel')}
|
||||
onClick={() => model.onAddPanel()}
|
||||
/>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
ariaLabel={t('dashboard.tabs-layout.tab.menu.add-tab-before', 'Tab before')}
|
||||
label={t('dashboard.tabs-layout.tab.menu.add-tab-above', 'Tab before')}
|
||||
onClick={() => model.onAddTabBefore()}
|
||||
/>
|
||||
<Menu.Item
|
||||
ariaLabel={t('dashboard.tabs-layout.tab.menu.add-tab-after', 'Tab after')}
|
||||
label={t('dashboard.tabs-layout.tab.menu.add-tab-after', 'Tab after')}
|
||||
onClick={() => model.onAddTabAfter()}
|
||||
/>
|
||||
</Menu>
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
aria-label={t('dashboard.tabs-layout.tab.menu.add', 'Add tab')}
|
||||
title={t('dashboard.tabs-layout.tab.menu.add', 'Add tab')}
|
||||
tooltip={t('dashboard.tabs-layout.tab.menu.add', 'Add tab')}
|
||||
icon="plus"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
>
|
||||
<Trans i18nKey="grafana-ui.tags-input.add">Add</Trans>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<Dropdown
|
||||
placement="bottom-end"
|
||||
overlay={() => (
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
aria-label={t('dashboard.tabs-layout.tab.menu.move-left', 'Move tab left')}
|
||||
label={t('dashboard.tabs-layout.tab.menu.move-left', 'Move tab left')}
|
||||
onClick={() => model.onMoveLeft()}
|
||||
/>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
aria-label={t('dashboard.tabs-layout.tab.menu.move-right', 'Move tab right')}
|
||||
label={t('dashboard.tabs-layout.tab.menu.move-right', 'Move tab right')}
|
||||
onClick={() => model.onMoveRight()}
|
||||
/>
|
||||
</Menu>
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
aria-label={t('dashboard.tabs-layout.menu.move-tab', 'Move tab')}
|
||||
title={t('dashboard.tabs-layout.menu.move-tab', 'Move tab')}
|
||||
tooltip={t('dashboard.tabs-layout.menu.move-tab', 'Move tab')}
|
||||
icon="arrows-h"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
/>
|
||||
</Dropdown>
|
||||
</ToolbarButtonRow>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
container: css({
|
||||
gap: theme.spacing(1),
|
||||
flexShrink: 0,
|
||||
}),
|
||||
};
|
||||
}
|
||||
+17
-4
@@ -3,11 +3,11 @@ import { Fragment } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { SceneComponentProps } from '@grafana/scenes';
|
||||
import { TabContent, TabsBar, useStyles2 } from '@grafana/ui';
|
||||
import { Button, TabContent, TabsBar, useStyles2 } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
|
||||
import { getDashboardSceneFor } from '../../utils/utils';
|
||||
|
||||
import { TabItemMenu } from './TabItemMenu';
|
||||
import { TabsLayoutManager } from './TabsLayoutManager';
|
||||
|
||||
export function TabsLayoutManagerRenderer({ model }: SceneComponentProps<TabsLayoutManager>) {
|
||||
@@ -29,7 +29,13 @@ export function TabsLayoutManagerRenderer({ model }: SceneComponentProps<TabsLay
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
{isEditing && <TabItemMenu model={currentTab} />}
|
||||
{isEditing && (
|
||||
<div className="dashboard-canvas-add-button">
|
||||
<Button icon="plus" variant="primary" fill="text" onClick={() => model.addNewTab()}>
|
||||
<Trans i18nKey="dashboard.canvas-actions.new-tab">New tab</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TabsBar>
|
||||
<TabContent className={styles.tabContentContainer}>
|
||||
@@ -47,11 +53,17 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
}),
|
||||
tabsBar: css({
|
||||
overflow: 'hidden',
|
||||
'&:hover': {
|
||||
'.dashboard-canvas-add-button': {
|
||||
filter: 'unset',
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
tabsRow: css({
|
||||
justifyContent: 'space-between',
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
}),
|
||||
tabsContainer: css({
|
||||
display: 'flex',
|
||||
@@ -65,6 +77,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
tabContentContainer: css({
|
||||
backgroundColor: 'transparent',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
paddingTop: theme.spacing(1),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
import { sceneGraph, SceneGridRow } from '@grafana/scenes';
|
||||
import { SceneGridRow } from '@grafana/scenes';
|
||||
|
||||
import { NewObjectAddedToCanvasEvent } from '../../edit-pane/shared';
|
||||
import { DefaultGridLayoutManager } from '../layout-default/DefaultGridLayoutManager';
|
||||
@@ -16,12 +16,6 @@ export function addNewTabTo(layout: DashboardLayoutManager): TabItem {
|
||||
throw new Error('Parent layout is not a LayoutParent');
|
||||
}
|
||||
|
||||
// If layout parent is tab item we add new tab after it rather than create a nested tab
|
||||
if (layoutParent instanceof TabItem) {
|
||||
const tabsLayout = sceneGraph.getAncestor(layoutParent, TabsLayoutManager);
|
||||
return tabsLayout.addTabAfter(layoutParent);
|
||||
}
|
||||
|
||||
if (layout instanceof TabsLayoutManager) {
|
||||
return layout.addNewTab();
|
||||
}
|
||||
@@ -50,17 +44,6 @@ export function addNewRowTo(layout: DashboardLayoutManager): RowItem | SceneGrid
|
||||
}
|
||||
}
|
||||
|
||||
const layoutParent = layout.parent!;
|
||||
if (!isLayoutParent(layoutParent)) {
|
||||
throw new Error('Parent layout is not a LayoutParent');
|
||||
}
|
||||
|
||||
// If adding we are adding a row to a row we add it below the current row
|
||||
if (layoutParent instanceof RowItem) {
|
||||
const rowsLayout = sceneGraph.getAncestor(layoutParent, RowsLayoutManager);
|
||||
return rowsLayout.addRowBelow(layoutParent);
|
||||
}
|
||||
|
||||
if (layout instanceof RowsLayoutManager) {
|
||||
return layout.addNewRow();
|
||||
}
|
||||
@@ -70,6 +53,11 @@ export function addNewRowTo(layout: DashboardLayoutManager): RowItem | SceneGrid
|
||||
return addNewRowTo(currentTab.state.layout);
|
||||
}
|
||||
|
||||
const layoutParent = layout.parent!;
|
||||
if (!isLayoutParent(layoutParent)) {
|
||||
throw new Error('Parent layout is not a LayoutParent');
|
||||
}
|
||||
|
||||
// If we want to add a row and current layout is custom grid or auto we migrate to rows layout
|
||||
// And wrap current layout in a row
|
||||
|
||||
|
||||
@@ -1372,6 +1372,14 @@
|
||||
"min-width-error": "A number between 50 and 2000 is required"
|
||||
}
|
||||
},
|
||||
"canvas-actions": {
|
||||
"add-panel": "Add panel",
|
||||
"group-into-row": "Group into row",
|
||||
"group-into-tab": "Group into tab",
|
||||
"group-panels": "Group panels",
|
||||
"new-row": "New row",
|
||||
"new-tab": "New tab"
|
||||
},
|
||||
"conditional-rendering": {
|
||||
"data": {
|
||||
"disable": "Disable",
|
||||
@@ -1612,15 +1620,6 @@
|
||||
"row": {
|
||||
"collapse": "Collapse row",
|
||||
"expand": "Expand row",
|
||||
"menu": {
|
||||
"add": "Add row",
|
||||
"add-panel": "Panel",
|
||||
"add-row-above": "Row above",
|
||||
"add-row-below": "Row below",
|
||||
"move-down": "Move row down",
|
||||
"move-row": "Move row",
|
||||
"move-up": "Move row up"
|
||||
},
|
||||
"new": "New row",
|
||||
"repeat": {
|
||||
"learn-more": "Learn more",
|
||||
@@ -1645,20 +1644,8 @@
|
||||
},
|
||||
"tabs-layout": {
|
||||
"description": "Organize panels into horizontal tabs",
|
||||
"menu": {
|
||||
"move-tab": "Move tab"
|
||||
},
|
||||
"name": "Tabs",
|
||||
"tab": {
|
||||
"menu": {
|
||||
"add": "Add tab",
|
||||
"add-panel": "Panel",
|
||||
"add-tab-above": "Tab before",
|
||||
"add-tab-after": "Tab after",
|
||||
"add-tab-before": "Tab before",
|
||||
"move-left": "Move tab left",
|
||||
"move-right": "Move tab right"
|
||||
},
|
||||
"new": "New tab"
|
||||
},
|
||||
"tab-options": {
|
||||
|
||||
Reference in New Issue
Block a user