mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboards: Prevent rows nesting (#99246)
This commit is contained in:
parent
51b4ac50aa
commit
d2d6dd2e5f
@ -8,6 +8,7 @@ import {
|
||||
sceneGraph,
|
||||
sceneUtils,
|
||||
SceneComponentProps,
|
||||
SceneGridItemLike,
|
||||
} from '@grafana/scenes';
|
||||
import { GRID_COLUMN_COUNT } from 'app/core/constants';
|
||||
|
||||
@ -385,6 +386,28 @@ export class DefaultGridLayoutManager
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for preserving items positioning when switching layouts
|
||||
* @param gridItems
|
||||
* @returns
|
||||
*/
|
||||
public static fromGridItems(gridItems: SceneGridItemLike[]): DefaultGridLayoutManager {
|
||||
const children = gridItems.reduce<SceneGridItemLike[]>((acc, gridItem) => {
|
||||
gridItem.clearParent();
|
||||
acc.push(gridItem);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return new DefaultGridLayoutManager({
|
||||
grid: new SceneGridLayout({
|
||||
children,
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
public static Component = ({ model }: SceneComponentProps<DefaultGridLayoutManager>) => {
|
||||
return <model.state.grid.Component model={model.state.grid} />;
|
||||
};
|
||||
|
@ -3,7 +3,8 @@ import { SceneComponentProps, SceneCSSGridLayout, SceneObjectBase, SceneObjectSt
|
||||
import { Select } from '@grafana/ui';
|
||||
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
|
||||
|
||||
import { getPanelIdForVizPanel, getVizPanelKeyForPanelId } from '../../utils/utils';
|
||||
import { getDashboardSceneFor, getPanelIdForVizPanel, getVizPanelKeyForPanelId } from '../../utils/utils';
|
||||
import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager';
|
||||
import { DashboardLayoutManager, LayoutRegistryItem } from '../types';
|
||||
|
||||
import { ResponsiveGridItem } from './ResponsiveGridItem';
|
||||
@ -32,7 +33,9 @@ export class ResponsiveGridLayoutManager
|
||||
}
|
||||
|
||||
public addNewRow(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
const rowsLayout = RowsLayoutManager.createFromLayout(this);
|
||||
rowsLayout.addNewRow();
|
||||
getDashboardSceneFor(this).switchLayout(rowsLayout);
|
||||
}
|
||||
|
||||
public getNextPanelId(): number {
|
||||
|
@ -95,13 +95,13 @@ export class RowItem extends SceneObjectBase<RowItemState> implements LayoutPare
|
||||
};
|
||||
|
||||
public static Component = ({ model }: SceneComponentProps<RowItem>) => {
|
||||
const { layout, title, isCollapsed, height = 'expand', isHeaderHidden } = model.useState();
|
||||
const { layout, title, isCollapsed, height = 'expand', isHeaderHidden, key } = model.useState();
|
||||
const { isEditing, showHiddenElements } = getDashboardSceneFor(model).useState();
|
||||
const styles = useStyles2(getStyles);
|
||||
const titleInterpolated = sceneGraph.interpolate(model, title, undefined, 'text');
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const shouldGrow = !isCollapsed && height === 'expand';
|
||||
const { isSelected, onSelect } = useElementSelection(model.state.key);
|
||||
const { isSelected, onSelect } = useElementSelection(key);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -1,10 +1,20 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes';
|
||||
import {
|
||||
SceneComponentProps,
|
||||
sceneGraph,
|
||||
SceneGridItemLike,
|
||||
SceneGridRow,
|
||||
SceneObjectBase,
|
||||
SceneObjectState,
|
||||
VizPanel,
|
||||
} from '@grafana/scenes';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { DashboardScene } from '../DashboardScene';
|
||||
import { DashboardGridItem } from '../layout-default/DashboardGridItem';
|
||||
import { DefaultGridLayoutManager } from '../layout-default/DefaultGridLayoutManager';
|
||||
import { ResponsiveGridLayoutManager } from '../layout-responsive-grid/ResponsiveGridLayoutManager';
|
||||
import { DashboardLayoutManager, LayoutRegistryItem } from '../types';
|
||||
|
||||
@ -101,6 +111,50 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
|
||||
}
|
||||
|
||||
public static createFromLayout(layout: DashboardLayoutManager): RowsLayoutManager {
|
||||
if (layout instanceof DefaultGridLayoutManager) {
|
||||
const config: Array<{
|
||||
title?: string;
|
||||
isCollapsed?: boolean;
|
||||
children: SceneGridItemLike[];
|
||||
}> = [];
|
||||
let children: SceneGridItemLike[] | undefined;
|
||||
|
||||
layout.state.grid.forEachChild((child) => {
|
||||
if (!(child instanceof DashboardGridItem) && !(child instanceof SceneGridRow)) {
|
||||
throw new Error('Child is not a DashboardGridItem or SceneGridRow, invalid scene');
|
||||
}
|
||||
|
||||
if (child instanceof SceneGridRow) {
|
||||
if (!child.state.key?.includes('-clone-')) {
|
||||
config.push({
|
||||
title: child.state.title,
|
||||
isCollapsed: !!child.state.isCollapsed,
|
||||
children: child.state.children,
|
||||
});
|
||||
children = undefined;
|
||||
}
|
||||
} else {
|
||||
if (!children) {
|
||||
children = [];
|
||||
config.push({ children });
|
||||
}
|
||||
|
||||
children.push(child);
|
||||
}
|
||||
});
|
||||
|
||||
const rows = config.map(
|
||||
(rowConfig) =>
|
||||
new RowItem({
|
||||
title: rowConfig.title ?? 'Row title',
|
||||
isCollapsed: !!rowConfig.isCollapsed,
|
||||
layout: DefaultGridLayoutManager.fromGridItems(rowConfig.children),
|
||||
})
|
||||
);
|
||||
|
||||
return new RowsLayoutManager({ rows });
|
||||
}
|
||||
|
||||
const row = new RowItem({ layout: layout.clone(), title: 'Row title' });
|
||||
|
||||
return new RowsLayoutManager({ rows: [row] });
|
||||
|
@ -7,26 +7,38 @@ import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/Pan
|
||||
import { DashboardLayoutManager, isLayoutParent, LayoutRegistryItem } from '../types';
|
||||
|
||||
import { layoutRegistry } from './layoutRegistry';
|
||||
import { findParentLayout } from './utils';
|
||||
|
||||
export interface Props {
|
||||
layoutManager: DashboardLayoutManager;
|
||||
}
|
||||
|
||||
export function DashboardLayoutSelector({ layoutManager }: { layoutManager: DashboardLayoutManager }) {
|
||||
const layouts = layoutRegistry.list();
|
||||
const options = layouts.map((layout) => ({
|
||||
export function DashboardLayoutSelector({ layoutManager }: Props) {
|
||||
const options = useMemo(() => {
|
||||
const parentLayout = findParentLayout(layoutManager);
|
||||
const parentLayoutId = parentLayout?.getDescriptor().id;
|
||||
|
||||
return layoutRegistry
|
||||
.list()
|
||||
.filter((layout) => layout.id !== parentLayoutId)
|
||||
.map((layout) => ({
|
||||
label: layout.name,
|
||||
value: layout,
|
||||
}));
|
||||
}, [layoutManager]);
|
||||
|
||||
const currentLayoutId = layoutManager.getDescriptor().id;
|
||||
const currentLayoutOption = options.find((option) => option.value.id === currentLayoutId);
|
||||
const currentOption = options.find((option) => option.value.id === currentLayoutId);
|
||||
|
||||
return (
|
||||
<Select
|
||||
options={options}
|
||||
value={currentLayoutOption}
|
||||
onChange={(option) => changeLayoutTo(layoutManager, option.value!)}
|
||||
value={currentOption}
|
||||
onChange={(option) => {
|
||||
if (option.value?.id !== currentOption?.value.id) {
|
||||
changeLayoutTo(layoutManager, option.value!);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { SceneObject } from '@grafana/scenes';
|
||||
|
||||
import { DashboardLayoutManager, isDashboardLayoutManager } from '../types';
|
||||
|
||||
export function findParentLayout(sceneObject: SceneObject): DashboardLayoutManager | null {
|
||||
let parent = sceneObject.parent;
|
||||
|
||||
while (parent) {
|
||||
if (isDashboardLayoutManager(parent)) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
Loading…
Reference in New Issue
Block a user