mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 23:53:10 +08:00
Dashboard: Ungroup action to remove row or tab (#103370)
* Dashboard: Ungroup action to remove row or tab * Update
This commit is contained in:
@ -172,6 +172,7 @@ export const availableIconsIndex = {
|
||||
'layer-group': true,
|
||||
'layers-alt': true,
|
||||
layers: true,
|
||||
'layers-slash': true,
|
||||
'legend-hide': true,
|
||||
'legend-show': true,
|
||||
'library-panel': true,
|
||||
|
@ -16,7 +16,7 @@ import { DefaultGridLayoutManager } from '../layout-default/DefaultGridLayoutMan
|
||||
import { RowRepeaterBehavior } from '../layout-default/RowRepeaterBehavior';
|
||||
import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager';
|
||||
import { getRowFromClipboard } from '../layouts-shared/paste';
|
||||
import { generateUniqueTitle } from '../layouts-shared/utils';
|
||||
import { generateUniqueTitle, ungroupLayout } from '../layouts-shared/utils';
|
||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||
import { LayoutRegistryItem } from '../types/LayoutRegistryItem';
|
||||
|
||||
@ -134,8 +134,14 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
|
||||
}
|
||||
|
||||
public removeRow(row: RowItem) {
|
||||
// When removing last row replace ourselves with the inner row layout
|
||||
if (this.state.rows.length === 1) {
|
||||
ungroupLayout(this, row.state.layout);
|
||||
return;
|
||||
}
|
||||
|
||||
const rows = this.state.rows.filter((r) => r !== row);
|
||||
this.setState({ rows: rows.length === 0 ? [new RowItem()] : rows });
|
||||
this.setState({ rows });
|
||||
this.publishEvent(new ObjectRemovedFromCanvasEvent(row), true);
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ import { getDashboardSceneFor } from '../../utils/utils';
|
||||
import { RowItem } from '../layout-rows/RowItem';
|
||||
import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager';
|
||||
import { getTabFromClipboard } from '../layouts-shared/paste';
|
||||
import { generateUniqueTitle } from '../layouts-shared/utils';
|
||||
import { generateUniqueTitle, ungroupLayout } from '../layouts-shared/utils';
|
||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||
import { LayoutRegistryItem } from '../types/LayoutRegistryItem';
|
||||
|
||||
@ -152,9 +152,9 @@ export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> i
|
||||
}
|
||||
|
||||
public removeTab(tabToRemove: TabItem) {
|
||||
// Do not allow removing last tab (for now)
|
||||
// When removing last tab replace ourselves with the inner tab layout
|
||||
if (this.state.tabs.length === 1) {
|
||||
return;
|
||||
ungroupLayout(this, tabToRemove.state.layout);
|
||||
}
|
||||
|
||||
const currentTab = this.getCurrentTab();
|
||||
|
@ -4,11 +4,15 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Button, Dropdown, Menu, useStyles2 } from '@grafana/ui';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
|
||||
import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph';
|
||||
import { getDefaultVizPanel } from '../../utils/utils';
|
||||
import { DashboardScene } from '../DashboardScene';
|
||||
import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager';
|
||||
import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager';
|
||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||
|
||||
import { addNewRowTo, addNewTabTo } from './addNew';
|
||||
import { useClipboardState } from './useClipboardState';
|
||||
import { ungroupLayout } from './utils';
|
||||
|
||||
export interface Props {
|
||||
layoutManager: DashboardLayoutManager;
|
||||
@ -16,7 +20,6 @@ export interface Props {
|
||||
|
||||
export function CanvasGridAddActions({ layoutManager }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const { hasCopiedPanel } = useClipboardState();
|
||||
|
||||
return (
|
||||
<div className={cx(styles.addAction, 'dashboard-canvas-add-button')}>
|
||||
@ -52,22 +55,69 @@ export function CanvasGridAddActions({ layoutManager }: Props) {
|
||||
<Trans i18nKey="dashboard.canvas-actions.group-panels">Group panels</Trans>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
{hasCopiedPanel && layoutManager.pastePanel && (
|
||||
<Button
|
||||
variant="primary"
|
||||
fill="text"
|
||||
icon="layers"
|
||||
onClick={() => {
|
||||
layoutManager.pastePanel?.();
|
||||
}}
|
||||
>
|
||||
<Trans i18nKey="dashboard.canvas-actions.paste-panel">Paste panel</Trans>
|
||||
</Button>
|
||||
)}
|
||||
{}
|
||||
{renderUngroupAction(layoutManager)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderUngroupAction(layoutManager: DashboardLayoutManager) {
|
||||
const parent = layoutManager.parent;
|
||||
|
||||
if (parent instanceof DashboardScene) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parentLayout = dashboardSceneGraph.getLayoutManagerFor(layoutManager.parent!);
|
||||
|
||||
const onUngroup = () => {
|
||||
ungroupLayout(parentLayout, layoutManager);
|
||||
};
|
||||
|
||||
if (parentLayout instanceof TabsLayoutManager) {
|
||||
return <UngroupButtonTabs parentLayout={parentLayout} onClick={onUngroup} />;
|
||||
}
|
||||
|
||||
if (parentLayout instanceof RowsLayoutManager) {
|
||||
return <UngroupButtonRows parentLayout={parentLayout} onClick={onUngroup} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
interface UngroupButtonProps<T> {
|
||||
parentLayout: T;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
function UngroupButtonTabs({ parentLayout, onClick }: UngroupButtonProps<TabsLayoutManager>) {
|
||||
const { tabs } = parentLayout.useState();
|
||||
|
||||
if (tabs.length > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button variant="primary" fill="text" icon="layers-slash" onClick={onClick}>
|
||||
<Trans i18nKey="dashboard.canvas-actions.un-group-panels">Ungroup</Trans>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function UngroupButtonRows({ parentLayout, onClick }: UngroupButtonProps<RowsLayoutManager>) {
|
||||
const { rows } = parentLayout.useState();
|
||||
|
||||
if (rows.length > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button variant="primary" fill="text" icon="layers-slash" onClick={onClick}>
|
||||
<Trans i18nKey="dashboard.canvas-actions.un-group-panels">Ungroup</Trans>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
addAction: css({
|
||||
position: 'absolute',
|
||||
|
@ -3,6 +3,7 @@ import { useEffect, useRef } from 'react';
|
||||
import { SceneObject } from '@grafana/scenes';
|
||||
|
||||
import { DashboardLayoutManager, isDashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||
import { isLayoutParent } from '../types/LayoutParent';
|
||||
|
||||
export function findParentLayout(sceneObject: SceneObject): DashboardLayoutManager | null {
|
||||
let parent = sceneObject.parent;
|
||||
@ -66,3 +67,11 @@ export function generateUniqueTitle(title: string | undefined, existingTitles: S
|
||||
|
||||
return baseTitle;
|
||||
}
|
||||
|
||||
export function ungroupLayout(layout: DashboardLayoutManager, innerLayout: DashboardLayoutManager) {
|
||||
const layoutParent = layout.parent!;
|
||||
if (isLayoutParent(layoutParent)) {
|
||||
innerLayout.clearParent();
|
||||
layoutParent.switchLayout(innerLayout);
|
||||
}
|
||||
}
|
||||
|
@ -1508,9 +1508,9 @@
|
||||
"group-panels": "Group panels",
|
||||
"new-row": "New row",
|
||||
"new-tab": "New tab",
|
||||
"paste-panel": "Paste panel",
|
||||
"paste-row": "Paste row",
|
||||
"paste-tab": "Paste tab"
|
||||
"paste-tab": "Paste tab",
|
||||
"un-group-panels": "Ungroup"
|
||||
},
|
||||
"conditional-rendering": {
|
||||
"conditions": {
|
||||
|
Reference in New Issue
Block a user