mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
47853 canvas constraint visualizations (#49206)
Co-authored-by: Adela Almasan <adela.almasan@grafana.com>
This commit is contained in:
parent
3b017e0fb1
commit
35ea67c210
200
public/app/features/canvas/runtime/ables.tsx
Normal file
200
public/app/features/canvas/runtime/ables.tsx
Normal file
@ -0,0 +1,200 @@
|
||||
import { MoveableManagerInterface, Renderer } from 'moveable';
|
||||
|
||||
import { HorizontalConstraint, VerticalConstraint } from '../types';
|
||||
|
||||
import { Scene } from './scene';
|
||||
|
||||
export const dimensionViewable = {
|
||||
name: 'dimensionViewable',
|
||||
props: {},
|
||||
events: {},
|
||||
render(moveable: MoveableManagerInterface<any, any>, React: Renderer) {
|
||||
const rect = moveable.getRect();
|
||||
return (
|
||||
<div
|
||||
key={'dimension-viewable'}
|
||||
className={'moveable-dimension'}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: `${rect.width / 2}px`,
|
||||
top: `${rect.height + 20}px`,
|
||||
background: '#4af',
|
||||
borderRadius: '2px',
|
||||
padding: '2px 4px',
|
||||
color: 'white',
|
||||
fontSize: '13px',
|
||||
whiteSpace: 'nowrap',
|
||||
fontWeight: 'bold',
|
||||
willChange: 'transform',
|
||||
transform: 'translate(-50%, 0px)',
|
||||
zIndex: 100,
|
||||
}}
|
||||
>
|
||||
{Math.round(rect.offsetWidth)} x {Math.round(rect.offsetHeight)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const constraintViewable = (scene: Scene) => ({
|
||||
name: 'constraintViewable',
|
||||
props: {},
|
||||
events: {},
|
||||
render(moveable: MoveableManagerInterface<any, any>, React: Renderer) {
|
||||
const rect = moveable.getRect();
|
||||
const targetElement = scene.findElementByTarget(moveable.state.target);
|
||||
|
||||
// If target is currently in motion or selection is more than 1 element don't display constraint visualizations
|
||||
if (
|
||||
targetElement?.isMoving ||
|
||||
(scene.selecto?.getSelectedTargets() && scene.selecto?.getSelectedTargets().length > 1)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let verticalConstraintVisualization = null;
|
||||
let horizontalConstraintVisualization = null;
|
||||
|
||||
const constraint = targetElement?.options.constraint ?? {};
|
||||
|
||||
const borderStyle = '1px dashed #4af';
|
||||
|
||||
const centerIndicatorLineOne = React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: `${rect.width / 2}px`,
|
||||
top: `${rect.height / 2 - rect.height / 16}px`,
|
||||
borderLeft: borderStyle,
|
||||
height: `${rect.height / 8}px`,
|
||||
transform: 'rotate(45deg)',
|
||||
},
|
||||
});
|
||||
|
||||
const centerIndicatorLineTwo = React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: `${rect.width / 2}px`,
|
||||
top: `${rect.height / 2 - rect.height / 16}px`,
|
||||
borderLeft: borderStyle,
|
||||
height: `${rect.height / 8}px`,
|
||||
transform: 'rotate(-45deg)',
|
||||
},
|
||||
});
|
||||
|
||||
const centerIndicator = React.createElement('div', {}, [centerIndicatorLineOne, centerIndicatorLineTwo]);
|
||||
|
||||
const verticalConstraintTop = React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: `${rect.width / 2}px`,
|
||||
bottom: '0px',
|
||||
borderLeft: borderStyle,
|
||||
height: '100vh',
|
||||
},
|
||||
});
|
||||
|
||||
const verticalConstraintBottom = React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: `${rect.width / 2}px`,
|
||||
top: `${rect.height}px`,
|
||||
borderLeft: borderStyle,
|
||||
height: '100vh',
|
||||
},
|
||||
});
|
||||
|
||||
const verticalConstraintTopBottom = React.createElement('div', {}, [
|
||||
verticalConstraintTop,
|
||||
verticalConstraintBottom,
|
||||
]);
|
||||
|
||||
const verticalConstraintCenterLine = React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: `${rect.width / 2}px`,
|
||||
top: `${rect.height / 4}px`,
|
||||
borderLeft: borderStyle,
|
||||
height: `${rect.height / 2}px`,
|
||||
},
|
||||
});
|
||||
|
||||
const verticalConstraintCenter = React.createElement('div', {}, [verticalConstraintCenterLine, centerIndicator]);
|
||||
|
||||
switch (constraint.vertical) {
|
||||
case VerticalConstraint.Top:
|
||||
verticalConstraintVisualization = verticalConstraintTop;
|
||||
break;
|
||||
case VerticalConstraint.Bottom:
|
||||
verticalConstraintVisualization = verticalConstraintBottom;
|
||||
break;
|
||||
case VerticalConstraint.TopBottom:
|
||||
verticalConstraintVisualization = verticalConstraintTopBottom;
|
||||
break;
|
||||
case VerticalConstraint.Center:
|
||||
verticalConstraintVisualization = verticalConstraintCenter;
|
||||
break;
|
||||
}
|
||||
|
||||
const horizontalConstraintLeft = React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
right: '0px',
|
||||
top: `${rect.height / 2}px`,
|
||||
borderTop: borderStyle,
|
||||
width: '100vw',
|
||||
},
|
||||
});
|
||||
|
||||
const horizontalConstraintRight = React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: `${rect.width}px`,
|
||||
top: `${rect.height / 2}px`,
|
||||
borderTop: borderStyle,
|
||||
width: '100vw',
|
||||
},
|
||||
});
|
||||
|
||||
const horizontalConstraintLeftRight = React.createElement('div', {}, [
|
||||
horizontalConstraintLeft,
|
||||
horizontalConstraintRight,
|
||||
]);
|
||||
|
||||
const horizontalConstraintCenterLine = React.createElement('div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: `${rect.width / 4}px`,
|
||||
top: `${rect.height / 2}px`,
|
||||
borderTop: borderStyle,
|
||||
width: `${rect.width / 2}px`,
|
||||
},
|
||||
});
|
||||
|
||||
const horizontalConstraintCenter = React.createElement('div', {}, [
|
||||
horizontalConstraintCenterLine,
|
||||
centerIndicator,
|
||||
]);
|
||||
|
||||
switch (constraint.horizontal) {
|
||||
case HorizontalConstraint.Left:
|
||||
horizontalConstraintVisualization = horizontalConstraintLeft;
|
||||
break;
|
||||
case HorizontalConstraint.Right:
|
||||
horizontalConstraintVisualization = horizontalConstraintRight;
|
||||
break;
|
||||
case HorizontalConstraint.LeftRight:
|
||||
horizontalConstraintVisualization = horizontalConstraintLeftRight;
|
||||
break;
|
||||
case HorizontalConstraint.Center:
|
||||
horizontalConstraintVisualization = horizontalConstraintCenter;
|
||||
break;
|
||||
}
|
||||
|
||||
const constraintVisualization = React.createElement('div', {}, [
|
||||
verticalConstraintVisualization,
|
||||
horizontalConstraintVisualization,
|
||||
]);
|
||||
|
||||
return constraintVisualization;
|
||||
},
|
||||
});
|
@ -26,6 +26,9 @@ export class ElementState implements LayerElement {
|
||||
sizeStyle: CSSProperties = {};
|
||||
dataStyle: CSSProperties = {};
|
||||
|
||||
// Determine whether or not element is in motion or not (via moveable)
|
||||
isMoving = false;
|
||||
|
||||
// Filled in by ref
|
||||
div?: HTMLDivElement;
|
||||
|
||||
|
@ -11,23 +11,24 @@ import { config } from 'app/core/config';
|
||||
import { CanvasFrameOptions, DEFAULT_CANVAS_ELEMENT_CONFIG } from 'app/features/canvas';
|
||||
import {
|
||||
ColorDimensionConfig,
|
||||
DimensionContext,
|
||||
ResourceDimensionConfig,
|
||||
ScalarDimensionConfig,
|
||||
ScaleDimensionConfig,
|
||||
TextDimensionConfig,
|
||||
DimensionContext,
|
||||
ScalarDimensionConfig,
|
||||
} from 'app/features/dimensions';
|
||||
import {
|
||||
getColorDimensionFromData,
|
||||
getScaleDimensionFromData,
|
||||
getResourceDimensionFromData,
|
||||
getTextDimensionFromData,
|
||||
getScalarDimensionFromData,
|
||||
getScaleDimensionFromData,
|
||||
getTextDimensionFromData,
|
||||
} from 'app/features/dimensions/utils';
|
||||
import { LayerActionID } from 'app/plugins/panel/canvas/types';
|
||||
|
||||
import { Placement } from '../types';
|
||||
|
||||
import { constraintViewable, dimensionViewable } from './ables';
|
||||
import { ElementState } from './element';
|
||||
import { FrameState } from './frame';
|
||||
import { RootElement } from './root';
|
||||
@ -224,7 +225,7 @@ export class Scene {
|
||||
}
|
||||
};
|
||||
|
||||
private findElementByTarget = (target: HTMLElement | SVGElement): ElementState | undefined => {
|
||||
findElementByTarget = (target: HTMLElement | SVGElement): ElementState | undefined => {
|
||||
// We will probably want to add memoization to this as we are calling on drag / resize
|
||||
|
||||
const stack = [...this.root.elements];
|
||||
@ -307,11 +308,22 @@ export class Scene {
|
||||
this.moveable = new Moveable(this.div!, {
|
||||
draggable: allowChanges,
|
||||
resizable: allowChanges,
|
||||
ables: [dimensionViewable, constraintViewable(this)],
|
||||
props: {
|
||||
dimensionViewable: allowChanges,
|
||||
constraintViewable: allowChanges,
|
||||
},
|
||||
origin: false,
|
||||
})
|
||||
.on('clickGroup', (event) => {
|
||||
this.selecto!.clickTarget(event.inputEvent, event.inputTarget);
|
||||
})
|
||||
.on('dragStart', (event) => {
|
||||
const targetedElement = this.findElementByTarget(event.target);
|
||||
if (targetedElement) {
|
||||
targetedElement.isMoving = true;
|
||||
}
|
||||
})
|
||||
.on('drag', (event) => {
|
||||
const targetedElement = this.findElementByTarget(event.target);
|
||||
targetedElement!.applyDrag(event);
|
||||
@ -325,7 +337,8 @@ export class Scene {
|
||||
.on('dragEnd', (event) => {
|
||||
const targetedElement = this.findElementByTarget(event.target);
|
||||
if (targetedElement) {
|
||||
targetedElement?.setPlacementFromConstraint();
|
||||
targetedElement.setPlacementFromConstraint();
|
||||
targetedElement.isMoving = false;
|
||||
}
|
||||
|
||||
this.moved.next(Date.now());
|
||||
@ -389,10 +402,4 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => ({
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
`,
|
||||
|
||||
toolbar: css`
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
margin: 10px;
|
||||
`,
|
||||
}));
|
||||
|
@ -147,22 +147,26 @@ const getStyles = (currentConstraints: Constraint) => (theme: GrafanaTheme2) =>
|
||||
}
|
||||
`,
|
||||
topConstraint: css`
|
||||
${currentConstraints.vertical === VerticalConstraint.Top
|
||||
${currentConstraints.vertical === VerticalConstraint.Top ||
|
||||
currentConstraints.vertical === VerticalConstraint.TopBottom
|
||||
? `width: 92pt; x: 1085; fill: ${SELECTED_COLOR};`
|
||||
: `fill: ${selectionBoxColor};`}
|
||||
`,
|
||||
bottomConstraint: css`
|
||||
${currentConstraints.vertical === VerticalConstraint.Bottom
|
||||
${currentConstraints.vertical === VerticalConstraint.Bottom ||
|
||||
currentConstraints.vertical === VerticalConstraint.TopBottom
|
||||
? `width: 92pt; x: 1085; fill: ${SELECTED_COLOR};`
|
||||
: `fill: ${selectionBoxColor};`}
|
||||
`,
|
||||
leftConstraint: css`
|
||||
${currentConstraints.horizontal === HorizontalConstraint.Left
|
||||
${currentConstraints.horizontal === HorizontalConstraint.Left ||
|
||||
currentConstraints.horizontal === HorizontalConstraint.LeftRight
|
||||
? `height: 92pt; y: 1014; fill: ${SELECTED_COLOR};`
|
||||
: `fill: ${selectionBoxColor};`}
|
||||
`,
|
||||
rightConstraint: css`
|
||||
${currentConstraints.horizontal === HorizontalConstraint.Right
|
||||
${currentConstraints.horizontal === HorizontalConstraint.Right ||
|
||||
currentConstraints.horizontal === HorizontalConstraint.LeftRight
|
||||
? `height: 92pt; y: 1014; fill: ${SELECTED_COLOR};`
|
||||
: `fill: ${selectionBoxColor};`}
|
||||
`,
|
||||
|
Loading…
Reference in New Issue
Block a user