Pushes
This commit is contained in:
@@ -346,6 +346,8 @@ export function PanelChrome({
|
||||
onMouseMove={onMouseMove}
|
||||
onMouseEnter={onMouseEnter}
|
||||
ref={ref}
|
||||
draggable={true}
|
||||
unselectable="on"
|
||||
>
|
||||
<div className={styles.loadingBarContainer}>
|
||||
{loadingState === LoadingState.Loading ? (
|
||||
@@ -390,8 +392,6 @@ export function PanelChrome({
|
||||
onMouseEnter={isSelectable ? onHeaderEnter : undefined}
|
||||
onMouseLeave={isSelectable ? onHeaderLeave : undefined}
|
||||
onPointerUp={onPointerUp}
|
||||
draggable={true}
|
||||
unselectable="on"
|
||||
onDragStart={(evt) => evt.dataTransfer.setData('text/plain', '')}
|
||||
>
|
||||
{statusMessage && (
|
||||
|
||||
@@ -1,27 +1,19 @@
|
||||
import { PointerEvent as ReactPointerEvent } from 'react';
|
||||
|
||||
import { logWarning } from '@grafana/runtime';
|
||||
import {
|
||||
sceneGraph,
|
||||
SceneObjectBase,
|
||||
SceneObjectRef,
|
||||
SceneObjectState,
|
||||
VizPanel,
|
||||
SceneGridItemLike,
|
||||
} from '@grafana/scenes';
|
||||
import { sceneGraph, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes';
|
||||
import { createPointerDistance } from '@grafana/ui';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
import { DashboardLayoutGrid, isDashboardLayoutGrid } from './types/DashboardLayoutGrid';
|
||||
import { DashboardLayoutItem } from './types/DashboardLayoutItem';
|
||||
import { isDashboardLayoutManager } from './types/DashboardLayoutManager';
|
||||
|
||||
interface DashboardLayoutOrchestratorState extends SceneObjectState {
|
||||
draggingGridItem?: SceneObjectRef<SceneGridItemLike>;
|
||||
}
|
||||
interface DashboardLayoutOrchestratorState extends SceneObjectState {}
|
||||
|
||||
export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayoutOrchestratorState> {
|
||||
private _sourceGrid: DashboardLayoutGrid | null = null;
|
||||
private _lastGrid: DashboardLayoutGrid | null = null;
|
||||
private _currentGrid: DashboardLayoutGrid | null = null;
|
||||
private _layoutItem: DashboardLayoutItem | null = null;
|
||||
private _pointerDistance = createPointerDistance();
|
||||
private _isSelectedObject = false;
|
||||
|
||||
@@ -38,79 +30,11 @@ export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayout
|
||||
return () => {
|
||||
document.body.removeEventListener('pointermove', this._onPointerMove);
|
||||
document.body.removeEventListener('pointerup', this._stopDraggingSync);
|
||||
document.body.classList.remove('dashboard-draggable-transparent-selection');
|
||||
};
|
||||
}
|
||||
|
||||
public startDraggingSync(evt: ReactPointerEvent, gridItem: SceneGridItemLike, layoutGrid: DashboardLayoutGrid): void {
|
||||
this._pointerDistance.set(evt);
|
||||
this._isSelectedObject = false;
|
||||
|
||||
(sceneGraph.findAllObjects(
|
||||
this._getDashboard(),
|
||||
(obj) => isDashboardLayoutManager(obj) && isDashboardLayoutGrid(obj)
|
||||
) as DashboardLayoutGrid[]).forEach((layout) => layout.startOrchestratorSync?.());
|
||||
|
||||
this._sourceGrid = layoutGrid;
|
||||
this._lastGrid = layoutGrid;
|
||||
|
||||
document.body.addEventListener('pointermove', this._onPointerMove);
|
||||
document.body.addEventListener('pointerup', this._stopDraggingSync);
|
||||
|
||||
this.setState({ draggingGridItem: gridItem.getRef() });
|
||||
}
|
||||
|
||||
private _stopDraggingSync(_evt: PointerEvent) {
|
||||
const gridItem = this.state.draggingGridItem?.resolve();
|
||||
|
||||
if (this._sourceGrid !== this._lastGrid) {
|
||||
// Wrapped in setTimeout to ensure that any event handlers are called
|
||||
// Useful for allowing react-grid-layout to remove placeholders, etc.
|
||||
setTimeout(() => {
|
||||
if (gridItem) {
|
||||
// Always use grid item dragging
|
||||
this._sourceGrid?.draggedItemOutside?.(gridItem);
|
||||
this._lastGrid?.draggedItemInside?.(gridItem);
|
||||
} else {
|
||||
const warningMessage = 'No grid item to drag';
|
||||
console.warn(warningMessage);
|
||||
logWarning(warningMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.body.removeEventListener('pointermove', this._onPointerMove);
|
||||
document.body.removeEventListener('pointerup', this._stopDraggingSync);
|
||||
|
||||
this.setState({ draggingGridItem: undefined });
|
||||
}
|
||||
|
||||
private _onPointerMove(evt: PointerEvent) {
|
||||
if (!this._isSelectedObject && this.state.draggingGridItem && this._pointerDistance.check(evt)) {
|
||||
this._isSelectedObject = true;
|
||||
const gridItem = this.state.draggingGridItem?.resolve();
|
||||
if (gridItem && 'state' in gridItem && 'body' in gridItem.state && gridItem.state.body instanceof VizPanel) {
|
||||
const panel = gridItem.state.body;
|
||||
this._getDashboard().state.editPane.selectObject(panel, panel.state.key!, { force: true, multi: false });
|
||||
}
|
||||
}
|
||||
|
||||
const dropTarget = this._getLayoutGridUnderMouse(evt) ?? this._sourceGrid;
|
||||
|
||||
if (!dropTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropTarget !== this._lastGrid) {
|
||||
this._lastGrid?.setIsDropTarget?.(false);
|
||||
this._lastGrid = dropTarget;
|
||||
|
||||
if (dropTarget !== this._sourceGrid) {
|
||||
dropTarget.setIsDropTarget?.(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getDashboard(): DashboardScene {
|
||||
public getDashboard(): DashboardScene {
|
||||
if (!(this.parent instanceof DashboardScene)) {
|
||||
throw new Error('Parent is not a DashboardScene');
|
||||
}
|
||||
@@ -118,10 +42,74 @@ export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayout
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
private _getLayoutGridUnderMouse(evt: MouseEvent): DashboardLayoutGrid | null {
|
||||
public startDraggingSync(evt: ReactPointerEvent, layoutItem: DashboardLayoutItem, layoutGrid: DashboardLayoutGrid) {
|
||||
this._pointerDistance.set(evt);
|
||||
|
||||
this._isSelectedObject = false;
|
||||
this._sourceGrid = layoutGrid;
|
||||
this._currentGrid = layoutGrid;
|
||||
this._layoutItem = layoutItem;
|
||||
|
||||
this._sourceGrid.setIsDropTarget?.(true, this._sourceGrid!, this._layoutItem!);
|
||||
|
||||
document.body.addEventListener('pointermove', this._onPointerMove);
|
||||
document.body.addEventListener('pointerup', this._stopDraggingSync);
|
||||
document.body.classList.add('dashboard-draggable-transparent-selection');
|
||||
}
|
||||
|
||||
private _onPointerMove(evt: PointerEvent) {
|
||||
this._selectVizPanelIfNeeded(evt);
|
||||
this._changeCurrentGridIfNeeded(evt);
|
||||
}
|
||||
|
||||
private _selectVizPanelIfNeeded(evt: PointerEvent) {
|
||||
if (!this._isSelectedObject && this._layoutItem && this._pointerDistance.check(evt)) {
|
||||
const panel = this._getVizPanelFromLayoutItem();
|
||||
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isSelectedObject = true;
|
||||
this.getDashboard().state.editPane.selectObject(panel, panel.state.key!, { force: true, multi: false });
|
||||
}
|
||||
}
|
||||
|
||||
private _getVizPanelFromLayoutItem(): VizPanel | null {
|
||||
if (
|
||||
this._layoutItem &&
|
||||
'state' in this._layoutItem &&
|
||||
'body' in this._layoutItem.state &&
|
||||
this._layoutItem.state.body instanceof VizPanel
|
||||
) {
|
||||
return this._layoutItem.state.body;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private _changeCurrentGridIfNeeded(evt: PointerEvent) {
|
||||
const currentGrid = this._getCurrentGrid(evt) ?? this._sourceGrid;
|
||||
|
||||
if (!currentGrid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentGrid !== this._currentGrid) {
|
||||
this._currentGrid?.setIsDropTarget?.(false, this._sourceGrid!, this._layoutItem!);
|
||||
this._currentGrid = currentGrid;
|
||||
|
||||
if (currentGrid !== this._sourceGrid) {
|
||||
currentGrid.setIsDropTarget?.(true, this._sourceGrid!, this._layoutItem!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getCurrentGrid(evt: MouseEvent): DashboardLayoutGrid | null {
|
||||
const elementsUnderPoint = document.elementsFromPoint(evt.clientX, evt.clientY);
|
||||
const cursorIsInSourceTarget = elementsUnderPoint.some((el) => el.getAttribute('data-grid-manager-key') ===
|
||||
this._sourceGrid?.state.key);
|
||||
const cursorIsInSourceTarget = elementsUnderPoint.some(
|
||||
(el) => el.getAttribute('data-grid-manager-key') === this._sourceGrid?.state.key
|
||||
);
|
||||
|
||||
if (cursorIsInSourceTarget) {
|
||||
return null;
|
||||
@@ -135,7 +123,7 @@ export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayout
|
||||
return null;
|
||||
}
|
||||
|
||||
const sceneObject = sceneGraph.findByKey(this._getDashboard(), key);
|
||||
const sceneObject = sceneGraph.findByKey(this.getDashboard(), key);
|
||||
|
||||
if (!sceneObject || !isDashboardLayoutManager(sceneObject) || !isDashboardLayoutGrid(sceneObject)) {
|
||||
return null;
|
||||
@@ -143,4 +131,28 @@ export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayout
|
||||
|
||||
return sceneObject;
|
||||
}
|
||||
|
||||
private _stopDraggingSync(_evt: PointerEvent) {
|
||||
document.body.removeEventListener('pointermove', this._onPointerMove);
|
||||
document.body.removeEventListener('pointerup', this._stopDraggingSync);
|
||||
document.body.classList.remove('dashboard-draggable-transparent-selection');
|
||||
|
||||
// Wrapped in setTimeout to ensure that any event handlers are called
|
||||
// Useful for allowing react-grid-layout to remove placeholders, etc.
|
||||
setTimeout(() => {
|
||||
if (!this._sourceGrid || !this._currentGrid || !this._layoutItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
sceneGraph.findAllObjects(this.getDashboard(), (obj) => isDashboardLayoutManager(obj) && isDashboardLayoutGrid(obj)).forEach((obj) => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
(obj as DashboardLayoutGrid).stopOrchestratorSync?.(this._sourceGrid!, this._currentGrid!, this._layoutItem!);
|
||||
});
|
||||
|
||||
this._isSelectedObject = false;
|
||||
this._sourceGrid = null;
|
||||
this._currentGrid = null;
|
||||
this._layoutItem = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ export class AutoGridLayout extends SceneObjectBase<AutoGridLayoutState> impleme
|
||||
this._resetPanelPositionAndSize();
|
||||
document.body.removeEventListener('pointermove', this._onDrag);
|
||||
document.body.removeEventListener('pointerup', this._onDragEnd);
|
||||
document.body.classList.remove('dashboard-draggable-transparent-selection');
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+35
-1
@@ -15,6 +15,8 @@ import {
|
||||
SceneGridItemLike,
|
||||
useSceneObjectState,
|
||||
SceneObject,
|
||||
SceneGridLayoutDragStartEvent,
|
||||
SceneGridPlaceholderItem
|
||||
} from '@grafana/scenes';
|
||||
import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
@@ -48,6 +50,7 @@ import { clearClipboard, getDashboardGridItemFromClipboard } from '../layouts-sh
|
||||
import { dashboardCanvasAddButtonHoverStyles } from '../layouts-shared/styles';
|
||||
import { getIsLazy } from '../layouts-shared/utils';
|
||||
import { DashboardLayoutGrid } from '../types/DashboardLayoutGrid';
|
||||
import { DashboardLayoutItem } from '../types/DashboardLayoutItem';
|
||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||
import { LayoutRegistryItem } from '../types/LayoutRegistryItem';
|
||||
|
||||
@@ -127,7 +130,8 @@ export class DefaultGridLayoutManager
|
||||
this.subscribeToEvent(SceneGridLayoutDragStartEvent, ({ payload: { evt, panel }}) => {
|
||||
const gridItem = panel.parent;
|
||||
if (gridItem instanceof DashboardGridItem) {
|
||||
getLayoutOrchestratorFor(this)?.startDraggingSync(evt, gridItem, this.state.grid);
|
||||
this.state.grid.setPlaceholderSize(gridItem.state.width ?? NEW_PANEL_WIDTH, gridItem.state.height ?? NEW_PANEL_HEIGHT);
|
||||
getLayoutOrchestratorFor(this)?.startDraggingSync(evt, gridItem, this);
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -562,6 +566,36 @@ export class DefaultGridLayoutManager
|
||||
this.state.grid.setState({ children: [...this.state.grid.state.children, gridItem] });
|
||||
}
|
||||
|
||||
public setIsDropTarget(flag: boolean, sourceGrid: DashboardLayoutGrid) {
|
||||
const newState = {
|
||||
isDragging: flag,
|
||||
isOutsideDragging: sourceGrid !== this,
|
||||
};
|
||||
|
||||
if (newState.isDragging !== this.state.grid.state.isDragging || newState.isOutsideDragging !== this.state.grid.state.isOutsideDragging) {
|
||||
this.state.grid.setState(newState);
|
||||
}
|
||||
}
|
||||
|
||||
public stopOrchestratorSync(sourceGrid: DashboardLayoutGrid, targetGrid: DashboardLayoutGrid, layoutItem: DashboardLayoutItem) {
|
||||
console.log('stop orchestrator sync');
|
||||
|
||||
const isSourceGrid = sourceGrid === this;
|
||||
const isTargetGrid = targetGrid === this;
|
||||
|
||||
if (!isSourceGrid && !isTargetGrid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSourceGrid && !isTargetGrid) {
|
||||
this.state.grid.setState({ children: this.state.grid.state.children.filter((child) => child !== layoutItem) });
|
||||
} else if (!isSourceGrid && isTargetGrid) {
|
||||
// From outside drag
|
||||
} else {
|
||||
// Inside drag
|
||||
}
|
||||
}
|
||||
|
||||
public static createFromLayout(currentLayout: DashboardLayoutManager): DefaultGridLayoutManager {
|
||||
const panels = currentLayout.getVizPanels();
|
||||
const isLazy = getIsLazy(getDashboardSceneFor(currentLayout).state.preload)!;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SceneGridItemLike } from '@grafana/scenes';
|
||||
|
||||
import { DashboardLayoutItem } from './DashboardLayoutItem';
|
||||
import { DashboardLayoutManager } from './DashboardLayoutManager';
|
||||
|
||||
export interface DashboardLayoutGrid extends DashboardLayoutManager {
|
||||
@@ -12,10 +13,18 @@ export interface DashboardLayoutGrid extends DashboardLayoutManager {
|
||||
*/
|
||||
addGridItem(gridItem: SceneGridItemLike): void;
|
||||
|
||||
/**
|
||||
* Start the synchronization of the orchestrator with the grid drag
|
||||
*/
|
||||
startOrchestratorSync?(): void;
|
||||
draggedItemOutside?(gridItem: SceneGridItemLike): void;
|
||||
draggedItemInside?(gridItem: SceneGridItemLike): void;
|
||||
setIsDropTarget?(flag: boolean): void;
|
||||
|
||||
stopOrchestratorSync?(sourceGrid: DashboardLayoutGrid, targetGrid: DashboardLayoutGrid, layoutItem: DashboardLayoutItem): void;
|
||||
|
||||
/**
|
||||
* Toggle the grid as the current drop target
|
||||
* Useful for toggling between inner drag and outer drag
|
||||
*/
|
||||
setIsDropTarget?(flag: boolean, sourceGrid: DashboardLayoutGrid, layoutItem: DashboardLayoutItem): void;
|
||||
}
|
||||
|
||||
export function isDashboardLayoutGrid(obj: DashboardLayoutManager): obj is DashboardLayoutGrid {
|
||||
|
||||
Reference in New Issue
Block a user