The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/dashboard-scene/scene/DashboardLayoutOrchestrator...

129 lines
4.2 KiB

import { PointerEvent as ReactPointerEvent } from 'react';
import { sceneGraph, SceneObjectBase, SceneObjectRef, SceneObjectState, VizPanel } from '@grafana/scenes';
import { createPointerDistance } from '@grafana/ui';
import { DashboardScene } from './DashboardScene';
import { DashboardDropTarget, isDashboardDropTarget } from './types/DashboardDropTarget';
interface DashboardLayoutOrchestratorState extends SceneObjectState {
draggingPanel?: SceneObjectRef<VizPanel>;
}
export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayoutOrchestratorState> {
private _sourceDropTarget: DashboardDropTarget | null = null;
private _lastDropTarget: DashboardDropTarget | null = null;
private _pointerDistance = createPointerDistance();
private _isSelectedObject = false;
public constructor() {
super({});
this._onPointerMove = this._onPointerMove.bind(this);
this._stopDraggingSync = this._stopDraggingSync.bind(this);
this.addActivationHandler(() => this._activationHandler());
}
private _activationHandler() {
return () => {
document.body.removeEventListener('pointermove', this._onPointerMove);
document.body.removeEventListener('pointerup', this._stopDraggingSync);
};
}
public startDraggingSync(evt: ReactPointerEvent, panel: VizPanel): void {
this._pointerDistance.set(evt);
this._isSelectedObject = false;
const dropTarget = sceneGraph.findObject(panel, isDashboardDropTarget);
if (!dropTarget || !isDashboardDropTarget(dropTarget)) {
return;
}
this._sourceDropTarget = dropTarget;
this._lastDropTarget = dropTarget;
document.body.addEventListener('pointermove', this._onPointerMove);
document.body.addEventListener('pointerup', this._stopDraggingSync);
this.setState({ draggingPanel: panel.getRef() });
}
private _stopDraggingSync(_evt: PointerEvent) {
const panel = this.state.draggingPanel?.resolve();
if (this._sourceDropTarget !== this._lastDropTarget) {
// Wrapped in setTimeout to ensure that any event handlers are called
// Useful for allowing react-grid-layout to remove placeholders, etc.
setTimeout(() => {
this._sourceDropTarget?.draggedPanelOutside?.(panel!);
this._lastDropTarget?.draggedPanelInside?.(panel!);
});
}
document.body.removeEventListener('pointermove', this._onPointerMove);
document.body.removeEventListener('pointerup', this._stopDraggingSync);
this.setState({ draggingPanel: undefined });
}
private _onPointerMove(evt: PointerEvent) {
if (!this._isSelectedObject && this.state.draggingPanel && this._pointerDistance.check(evt)) {
this._isSelectedObject = true;
const panel = this.state.draggingPanel?.resolve();
this._getDashboard().state.editPane.selectObject(panel, panel.state.key!, { force: true, multi: false });
}
const dropTarget = this._getDropTargetUnderMouse(evt) ?? this._sourceDropTarget;
if (!dropTarget) {
return;
}
if (dropTarget !== this._lastDropTarget) {
this._lastDropTarget?.setIsDropTarget?.(false);
this._lastDropTarget = dropTarget;
if (dropTarget !== this._sourceDropTarget) {
dropTarget.setIsDropTarget?.(true);
}
}
}
private _getDashboard(): DashboardScene {
if (!(this.parent instanceof DashboardScene)) {
throw new Error('Parent is not a DashboardScene');
}
return this.parent;
}
private _getDropTargetUnderMouse(evt: MouseEvent): DashboardDropTarget | null {
const elementsUnderPoint = document.elementsFromPoint(evt.clientX, evt.clientY);
const cursorIsInSourceTarget = elementsUnderPoint.some(
(el) => el.getAttribute('data-dashboard-drop-target-key') === this._sourceDropTarget?.state.key
);
if (cursorIsInSourceTarget) {
return null;
}
const key = elementsUnderPoint
?.find((element) => element.getAttribute('data-dashboard-drop-target-key'))
?.getAttribute('data-dashboard-drop-target-key');
if (!key) {
return null;
}
const sceneObject = sceneGraph.findByKey(this._getDashboard(), key);
if (!sceneObject || !isDashboardDropTarget(sceneObject)) {
return null;
}
return sceneObject;
}
}