Dynamic dashboards: Duplicate tabs and rows (#102370)

Duplicate tabs and rows
This commit is contained in:
Oscar Kilhed
2025-03-20 12:39:48 +01:00
committed by GitHub
parent bc0c650cf4
commit 0f14bccc68
7 changed files with 86 additions and 0 deletions
@@ -186,6 +186,29 @@ export class DefaultGridLayoutManager
this.publishEvent(new NewObjectAddedToCanvasEvent(newPanel), true);
}
public duplicate(): DashboardLayoutManager {
const clone = this.clone({
key: undefined,
grid: this.state.grid.clone({
key: undefined,
children: this.state.grid.state.children.map((child) => {
if (child instanceof DashboardGridItem) {
return child.clone({
key: undefined,
body: child.state.body.clone({
key: getVizPanelKeyForPanelId(dashboardSceneGraph.getNextPanelId(child.state.body)),
}),
});
}
return child.clone({ key: undefined });
}),
}),
});
return clone;
}
public getVizPanels(): VizPanel[] {
const panels: VizPanel[] = [];
@@ -72,6 +72,27 @@ export class ResponsiveGridLayoutManager
this.publishEvent(new ObjectRemovedFromCanvasEvent(panel), true);
}
public duplicate(): DashboardLayoutManager {
return this.clone({
key: undefined,
layout: this.state.layout.clone({
key: undefined,
children: this.state.layout.state.children.map((child) => {
if (child instanceof ResponsiveGridItem) {
return child.clone({
key: undefined,
body: child.state.body.clone({
key: getVizPanelKeyForPanelId(dashboardSceneGraph.getNextPanelId(child.state.body)),
}),
});
}
return child.clone({ key: undefined });
}),
}),
});
}
public duplicatePanel(panel: VizPanel) {
const gridItem = panel.parent;
if (!(gridItem instanceof ResponsiveGridItem)) {
@@ -88,6 +88,14 @@ export class RowItem
return new RowItems(items.filter((item) => item instanceof RowItem));
}
public onDuplicate() {
this._getParentLayout().duplicateRow(this);
}
public duplicate(): RowItem {
return this.clone({ key: undefined, layout: this.getLayout().duplicate() });
}
public onAddPanel(panel = getDefaultVizPanel()) {
this.getLayout().addPanel(panel);
}
@@ -75,6 +75,17 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
throw new Error('Method not implemented.');
}
public duplicate(): DashboardLayoutManager {
const newRows = this.state.rows.map((row) => row.duplicate());
return this.clone({ rows: newRows, key: undefined });
}
public duplicateRow(row: RowItem) {
const newRow = row.duplicate();
this.setState({ rows: [...this.state.rows, newRow] });
this.publishEvent(new NewObjectAddedToCanvasEvent(newRow), true);
}
public addNewRow(): RowItem {
const row = new RowItem();
this.setState({ rows: [...this.state.rows, row] });
@@ -70,6 +70,14 @@ export class TabItem
return new TabItems(items.filter((item) => item instanceof TabItem));
}
public onDuplicate(): void {
this._getParentLayout().duplicateTab(this);
}
public duplicate(): TabItem {
return this.clone({ key: undefined, layout: this.getLayout().duplicate() });
}
public onAddPanel(panel = getDefaultVizPanel()) {
this.getLayout().addPanel(panel);
}
@@ -54,6 +54,16 @@ export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> i
});
}
public duplicate(): DashboardLayoutManager {
// Maybe not needed, depending on if we want nested tabs or tabs within rows
throw new Error('Method not implemented.');
}
public duplicateTab(tab: TabItem) {
const newTab = tab.duplicate();
this.setState({ tabs: [...this.state.tabs, newTab] });
}
public getUrlState() {
return { tab: this.state.currentTabIndex.toString() };
}
@@ -66,6 +66,11 @@ export interface DashboardLayoutManager<S = {}> extends SceneObject {
* @param isSource
*/
cloneLayout(ancestorKey: string, isSource: boolean): DashboardLayoutManager;
/**
* Duplicate, like clone but with new keys
*/
duplicate(): DashboardLayoutManager;
}
export interface LayoutManagerSerializer {