, cell: GridCell, maxY: number) {
+ // If we are at maxY we are on a new row, return default new row panel dimensions
+ if (cell.y >= maxY) {
+ cell.width = NEW_PANEL_WIDTH;
+ cell.height = NEW_PANEL_HEIGHT;
+ return cell;
+ }
+
+ // Expand width
+ for (let x = cell.x + cell.width + 1; x <= GRID_COLUMN_COUNT; x++) {
+ let fits = true;
+
+ for (let y = cell.y; y < cell.y + cell.height; y++) {
+ const key = `${x},${y}`;
+ if (occupied.has(key)) {
+ fits = false;
+ break;
+ }
+ }
+
+ if (fits) {
+ cell.width = x - cell.x;
+ } else {
+ break;
+ }
+ }
+
+ // try to expand y
+ for (let y = cell.y + cell.height + 1; y <= maxY; y++) {
+ let fits = true;
+
+ // Some max panel height
+ if (cell.height >= 20) {
+ break;
+ }
+
+ for (let x = cell.x; x < cell.x + cell.width; x++) {
+ const key = `${x},${y}`;
+ if (occupied.has(key)) {
+ fits = false;
+ break;
+ }
+ }
+
+ if (fits) {
+ cell.height = y - cell.y;
+ } else {
+ break;
+ }
+ }
+
+ return cell;
+}
diff --git a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutRenderer.tsx
index 0721f1fe704..03323b93a22 100644
--- a/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutRenderer.tsx
+++ b/public/app/features/dashboard-scene/scene/layout-responsive-grid/ResponsiveGridLayoutRenderer.tsx
@@ -2,11 +2,10 @@ import { css, cx } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { LazyLoader, SceneComponentProps, sceneGraph } from '@grafana/scenes';
-import { Button, Dropdown, Menu, useStyles2 } from '@grafana/ui';
-import { t, Trans } from 'app/core/internationalization';
+import { useStyles2 } from '@grafana/ui';
-import { getDefaultVizPanel, useDashboardState } from '../../utils/utils';
-import { addNewRowTo, addNewTabTo } from '../layouts-shared/addNew';
+import { useDashboardState } from '../../utils/utils';
+import { CanvasGridAddActions } from '../layouts-shared/CanvasGridAddActions';
import { AutoGridLayout, AutoGridLayoutState } from './ResponsiveGridLayout';
import { AutoGridLayoutManager } from './ResponsiveGridLayoutManager';
@@ -36,47 +35,7 @@ export function AutoGridLayoutRenderer({ model }: SceneComponentProps
)
)}
- {isEditing && (
-
-
-
- {
- addNewRowTo(layoutManager);
- }}
- >
- {
- addNewTabTo(layoutManager);
- }}
- >
-
- }
- >
-
-
-
- )}
+ {isEditing && }
);
}
@@ -125,18 +84,6 @@ 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,
diff --git a/public/app/features/dashboard-scene/scene/layouts-shared/CanvasGridAddActions.tsx b/public/app/features/dashboard-scene/scene/layouts-shared/CanvasGridAddActions.tsx
new file mode 100644
index 00000000000..c6abc6a30fd
--- /dev/null
+++ b/public/app/features/dashboard-scene/scene/layouts-shared/CanvasGridAddActions.tsx
@@ -0,0 +1,70 @@
+import { css, cx } from '@emotion/css';
+
+import { GrafanaTheme2 } from '@grafana/data';
+import { Button, Dropdown, Menu, useStyles2 } from '@grafana/ui';
+import { t, Trans } from 'app/core/internationalization';
+
+import { getDefaultVizPanel } from '../../utils/utils';
+import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
+
+import { addNewRowTo, addNewTabTo } from './addNew';
+
+export interface Props {
+ layoutManager: DashboardLayoutManager;
+}
+
+export function CanvasGridAddActions({ layoutManager }: Props) {
+ const styles = useStyles2(getStyles);
+
+ return (
+
+
+
+ {
+ addNewRowTo(layoutManager);
+ }}
+ >
+ {
+ addNewTabTo(layoutManager);
+ }}
+ >
+
+ }
+ >
+
+
+
+ );
+}
+
+const getStyles = (theme: GrafanaTheme2) => ({
+ 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'),
+ },
+ }),
+});