diff --git a/public/app/plugins/panel/canvas/editor/ConstraintSelectionBox.tsx b/public/app/plugins/panel/canvas/editor/ConstraintSelectionBox.tsx
new file mode 100644
index 00000000000..8c815bc5c98
--- /dev/null
+++ b/public/app/plugins/panel/canvas/editor/ConstraintSelectionBox.tsx
@@ -0,0 +1,183 @@
+import { css } from '@emotion/css';
+import React from 'react';
+
+import { GrafanaTheme2 } from '@grafana/data';
+import { useStyles2 } from '@grafana/ui';
+import { Constraint, HorizontalConstraint, VerticalConstraint } from 'app/features/canvas';
+
+interface Props {
+ onVerticalConstraintChange: (v: VerticalConstraint) => void;
+ onHorizontalConstraintChange: (h: HorizontalConstraint) => void;
+ currentConstraints: Constraint;
+}
+
+export const ConstraintSelectionBox = ({
+ onVerticalConstraintChange,
+ onHorizontalConstraintChange,
+ currentConstraints,
+}: Props) => {
+ const styles = useStyles2(getStyles(currentConstraints));
+
+ const onClickTopConstraint = () => {
+ onVerticalConstraintChange(VerticalConstraint.Top);
+ };
+
+ const onClickBottomConstraint = () => {
+ onVerticalConstraintChange(VerticalConstraint.Bottom);
+ };
+
+ const onClickVerticalCenterConstraint = () => {
+ onVerticalConstraintChange(VerticalConstraint.Center);
+ };
+
+ const onClickLeftConstraint = () => {
+ onHorizontalConstraintChange(HorizontalConstraint.Left);
+ };
+
+ const onClickRightConstraint = () => {
+ onHorizontalConstraintChange(HorizontalConstraint.Right);
+ };
+
+ const onClickHorizontalCenterConstraint = () => {
+ onHorizontalConstraintChange(HorizontalConstraint.Center);
+ };
+
+ return (
+
+ );
+};
+
+const getStyles = (currentConstraints: Constraint) => (theme: GrafanaTheme2) => {
+ const HOVER_COLOR = '#daebf7';
+ const HOVER_OPACITY = '0.6';
+ const SELECTED_COLOR = '#0d99ff';
+
+ const selectionBoxColor = theme.isDark ? '#ffffff' : '#000000';
+
+ return {
+ constraintHover: css`
+ &:hover {
+ fill: ${HOVER_COLOR};
+ fill-opacity: ${HOVER_OPACITY};
+ }
+ `,
+ topConstraint: css`
+ ${currentConstraints.vertical === VerticalConstraint.Top
+ ? `width: 92pt; x: 1085; fill: ${SELECTED_COLOR};`
+ : `fill: ${selectionBoxColor};`}
+ `,
+ bottomConstraint: css`
+ ${currentConstraints.vertical === VerticalConstraint.Bottom
+ ? `width: 92pt; x: 1085; fill: ${SELECTED_COLOR};`
+ : `fill: ${selectionBoxColor};`}
+ `,
+ leftConstraint: css`
+ ${currentConstraints.horizontal === HorizontalConstraint.Left
+ ? `height: 92pt; y: 1014; fill: ${SELECTED_COLOR};`
+ : `fill: ${selectionBoxColor};`}
+ `,
+ rightConstraint: css`
+ ${currentConstraints.horizontal === HorizontalConstraint.Right
+ ? `height: 92pt; y: 1014; fill: ${SELECTED_COLOR};`
+ : `fill: ${selectionBoxColor};`}
+ `,
+ horizontalCenterConstraint: css`
+ ${currentConstraints.horizontal === HorizontalConstraint.Center
+ ? `height: 92pt; y: 1014; fill: ${SELECTED_COLOR};`
+ : `fill: ${selectionBoxColor};`}
+ `,
+ verticalCenterConstraint: css`
+ ${currentConstraints.vertical === VerticalConstraint.Center
+ ? `width: 92pt; x: 1085; fill: ${SELECTED_COLOR};`
+ : `fill: ${selectionBoxColor};`}
+ `,
+ box: css`
+ fill: ${selectionBoxColor};
+ `,
+ };
+};
diff --git a/public/app/plugins/panel/canvas/editor/PlacementEditor.tsx b/public/app/plugins/panel/canvas/editor/PlacementEditor.tsx
index b64564f6dc4..7916ef11743 100644
--- a/public/app/plugins/panel/canvas/editor/PlacementEditor.tsx
+++ b/public/app/plugins/panel/canvas/editor/PlacementEditor.tsx
@@ -3,12 +3,13 @@ import { useObservable } from 'react-use';
import { Subject } from 'rxjs';
import { SelectableValue, StandardEditorProps } from '@grafana/data';
-import { Field, InlineField, InlineFieldRow, Select, VerticalGroup } from '@grafana/ui';
+import { Field, HorizontalGroup, InlineField, InlineFieldRow, Select, VerticalGroup } from '@grafana/ui';
import { HorizontalConstraint, Placement, VerticalConstraint } from 'app/features/canvas';
import { NumberInput } from 'app/features/dimensions/editors/NumberInput';
import { PanelOptions } from '../models.gen';
+import { ConstraintSelectionBox } from './ConstraintSelectionBox';
import { CanvasEditorOptions } from './elementEditor';
const places: Array = ['top', 'left', 'bottom', 'right', 'width', 'height'];
@@ -46,15 +47,23 @@ export const PlacementEditor: FC) => {
- element.options.constraint!.horizontal = h.value;
+ const onHorizontalConstraintSelect = (h: SelectableValue) => {
+ onHorizontalConstraintChange(h.value!);
+ };
+
+ const onHorizontalConstraintChange = (h: HorizontalConstraint) => {
+ element.options.constraint!.horizontal = h;
element.setPlacementFromConstraint();
settings.scene.revId++;
settings.scene.save(true);
};
- const onVerticalConstraintChange = (v: SelectableValue) => {
- element.options.constraint!.vertical = v.value;
+ const onVerticalConstraintSelect = (v: SelectableValue) => {
+ onVerticalConstraintChange(v.value!);
+ };
+
+ const onVerticalConstraintChange = (v: VerticalConstraint) => {
+ element.options.constraint!.vertical = v;
element.setPlacementFromConstraint();
settings.scene.revId++;
settings.scene.save(true);
@@ -68,14 +77,21 @@ export const PlacementEditor: FC
-
-
-
+
+
+
+
+