mirror of
https://github.com/grafana/grafana.git
synced 2025-12-21 12:04:45 +08:00
Compare commits
11 Commits
docs/add-t
...
bogdan/dyn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
000f779e95 | ||
|
|
dd30415c2f | ||
|
|
fd8e344e67 | ||
|
|
4ae0ad15e6 | ||
|
|
11ab1ca599 | ||
|
|
b800eb94b0 | ||
|
|
e003775d4e | ||
|
|
5ed20e7f36 | ||
|
|
469883b926 | ||
|
|
6cf2d94495 | ||
|
|
42bbaf7286 |
@@ -346,6 +346,8 @@ export function PanelChrome({
|
|||||||
onMouseMove={onMouseMove}
|
onMouseMove={onMouseMove}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
draggable={true}
|
||||||
|
unselectable="on"
|
||||||
>
|
>
|
||||||
<div className={styles.loadingBarContainer}>
|
<div className={styles.loadingBarContainer}>
|
||||||
{loadingState === LoadingState.Loading ? (
|
{loadingState === LoadingState.Loading ? (
|
||||||
@@ -381,6 +383,7 @@ export function PanelChrome({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{hasHeader && (
|
{hasHeader && (
|
||||||
|
/* eslint-disable react/no-unknown-property */
|
||||||
<div
|
<div
|
||||||
className={cx(styles.headerContainer, dragClass)}
|
className={cx(styles.headerContainer, dragClass)}
|
||||||
style={headerStyles}
|
style={headerStyles}
|
||||||
@@ -388,7 +391,8 @@ export function PanelChrome({
|
|||||||
onPointerDown={onPointerDown}
|
onPointerDown={onPointerDown}
|
||||||
onMouseEnter={isSelectable ? onHeaderEnter : undefined}
|
onMouseEnter={isSelectable ? onHeaderEnter : undefined}
|
||||||
onMouseLeave={isSelectable ? onHeaderLeave : undefined}
|
onMouseLeave={isSelectable ? onHeaderLeave : undefined}
|
||||||
onPointerUp={onPointerUp}
|
// onPointerUp={onPointerUp}
|
||||||
|
onDragStart={(evt) => evt.dataTransfer.setData('text/plain', '')}
|
||||||
>
|
>
|
||||||
{statusMessage && (
|
{statusMessage && (
|
||||||
<div className={dragClassCancel}>
|
<div className={dragClassCancel}>
|
||||||
|
|||||||
@@ -1,115 +1,40 @@
|
|||||||
import { PointerEvent as ReactPointerEvent } from 'react';
|
import { PointerEvent as ReactPointerEvent } from 'react';
|
||||||
|
|
||||||
import { logWarning } from '@grafana/runtime';
|
import { sceneGraph, SceneObjectBase, SceneObjectState } from '@grafana/scenes';
|
||||||
import {
|
|
||||||
sceneGraph,
|
|
||||||
SceneObjectBase,
|
|
||||||
SceneObjectRef,
|
|
||||||
SceneObjectState,
|
|
||||||
VizPanel,
|
|
||||||
SceneGridItemLike,
|
|
||||||
} from '@grafana/scenes';
|
|
||||||
import { createPointerDistance } from '@grafana/ui';
|
import { createPointerDistance } from '@grafana/ui';
|
||||||
|
|
||||||
import { DashboardScene } from './DashboardScene';
|
import { DashboardScene } from './DashboardScene';
|
||||||
import { DashboardDropTarget, isDashboardDropTarget } from './types/DashboardDropTarget';
|
import { DashboardLayoutGrid, isDashboardLayoutGrid } from './types/DashboardLayoutGrid';
|
||||||
|
import { DashboardLayoutItem } from './types/DashboardLayoutItem';
|
||||||
|
import { isDashboardLayoutManager } from './types/DashboardLayoutManager';
|
||||||
|
|
||||||
interface DashboardLayoutOrchestratorState extends SceneObjectState {
|
interface DashboardLayoutOrchestratorState extends SceneObjectState {}
|
||||||
draggingGridItem?: SceneObjectRef<SceneGridItemLike>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayoutOrchestratorState> {
|
export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayoutOrchestratorState> {
|
||||||
private _sourceDropTarget: DashboardDropTarget | null = null;
|
private _sourceGrid: DashboardLayoutGrid | null = null;
|
||||||
private _lastDropTarget: DashboardDropTarget | null = null;
|
private _currentGrid: DashboardLayoutGrid | null = null;
|
||||||
|
private _layoutItem: DashboardLayoutItem | null = null;
|
||||||
private _pointerDistance = createPointerDistance();
|
private _pointerDistance = createPointerDistance();
|
||||||
private _isSelectedObject = false;
|
private _isSelectedObject = false;
|
||||||
|
private _grids: DashboardLayoutGrid[] = [];
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super({});
|
super({});
|
||||||
|
|
||||||
this._onPointerMove = this._onPointerMove.bind(this);
|
this._onPointerMove = this._onPointerMove.bind(this);
|
||||||
this._stopDraggingSync = this._stopDraggingSync.bind(this);
|
this._onPointerUp = this._onPointerUp.bind(this);
|
||||||
|
|
||||||
this.addActivationHandler(() => this._activationHandler());
|
this.addActivationHandler(() => this._activationHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
private _activationHandler() {
|
private _activationHandler() {
|
||||||
return () => {
|
return () => {
|
||||||
document.body.removeEventListener('pointermove', this._onPointerMove);
|
window.removeEventListener('pointermove', this._onPointerMove);
|
||||||
document.body.removeEventListener('pointerup', this._stopDraggingSync);
|
window.removeEventListener('pointerup', this._onPointerUp);
|
||||||
|
document.body.classList.remove('dashboard-draggable-transparent-selection');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public startDraggingSync(evt: ReactPointerEvent, gridItem: SceneGridItemLike): void {
|
|
||||||
this._pointerDistance.set(evt);
|
|
||||||
this._isSelectedObject = false;
|
|
||||||
|
|
||||||
const dropTarget = sceneGraph.findObject(gridItem, 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({ draggingGridItem: gridItem.getRef() });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _stopDraggingSync(_evt: PointerEvent) {
|
|
||||||
const gridItem = this.state.draggingGridItem?.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(() => {
|
|
||||||
if (gridItem) {
|
|
||||||
// Always use grid item dragging
|
|
||||||
this._sourceDropTarget?.draggedGridItemOutside?.(gridItem);
|
|
||||||
this._lastDropTarget?.draggedGridItemInside?.(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._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 {
|
private _getDashboard(): DashboardScene {
|
||||||
if (!(this.parent instanceof DashboardScene)) {
|
if (!(this.parent instanceof DashboardScene)) {
|
||||||
throw new Error('Parent is not a DashboardScene');
|
throw new Error('Parent is not a DashboardScene');
|
||||||
@@ -118,30 +43,95 @@ export class DashboardLayoutOrchestrator extends SceneObjectBase<DashboardLayout
|
|||||||
return this.parent;
|
return this.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getDropTargetUnderMouse(evt: MouseEvent): DashboardDropTarget | null {
|
private _findAllGrids(): DashboardLayoutGrid[] {
|
||||||
const elementsUnderPoint = document.elementsFromPoint(evt.clientX, evt.clientY);
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
const cursorIsInSourceTarget = elementsUnderPoint.some(
|
return sceneGraph.findAllObjects(
|
||||||
(el) => el.getAttribute('data-dashboard-drop-target-key') === this._sourceDropTarget?.state.key
|
this._getDashboard(),
|
||||||
);
|
(obj) => isDashboardLayoutManager(obj) && isDashboardLayoutGrid(obj)
|
||||||
|
) as DashboardLayoutGrid[];
|
||||||
|
}
|
||||||
|
|
||||||
if (cursorIsInSourceTarget) {
|
private _getCurrentGrid(evt: MouseEvent): DashboardLayoutGrid | null {
|
||||||
return null;
|
const key = document
|
||||||
}
|
.elementsFromPoint(evt.clientX, evt.clientY)
|
||||||
|
.reverse()
|
||||||
const key = elementsUnderPoint
|
?.find((element) => element.getAttribute('data-grid-manager-key'))
|
||||||
?.find((element) => element.getAttribute('data-dashboard-drop-target-key'))
|
?.getAttribute('data-grid-manager-key');
|
||||||
?.getAttribute('data-dashboard-drop-target-key');
|
|
||||||
|
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sceneObject = sceneGraph.findByKey(this._getDashboard(), key);
|
const grid = this._grids.find((grid) => grid.state.key === key);
|
||||||
|
|
||||||
if (!sceneObject || !isDashboardDropTarget(sceneObject)) {
|
if (!grid) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sceneObject;
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public startDragging(evt: ReactPointerEvent, layoutItem: DashboardLayoutItem, layoutGrid: DashboardLayoutGrid) {
|
||||||
|
console.log('started');
|
||||||
|
|
||||||
|
this._pointerDistance.set(evt);
|
||||||
|
|
||||||
|
this._isSelectedObject = false;
|
||||||
|
this._sourceGrid = layoutGrid;
|
||||||
|
this._currentGrid = layoutGrid;
|
||||||
|
this._layoutItem = layoutItem;
|
||||||
|
|
||||||
|
window.addEventListener('pointermove', this._onPointerMove);
|
||||||
|
window.addEventListener('pointerup', this._onPointerUp);
|
||||||
|
document.body.classList.add('dashboard-draggable-transparent-selection');
|
||||||
|
|
||||||
|
this._grids = this._findAllGrids();
|
||||||
|
|
||||||
|
this._grids.forEach((grid) =>
|
||||||
|
grid.onDragStart?.(this._sourceGrid!, this._layoutItem!, evt.nativeEvent)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onPointerMove(evt: PointerEvent) {
|
||||||
|
// Select the panel if needed
|
||||||
|
if (!this._isSelectedObject && this._pointerDistance.check(evt)) {
|
||||||
|
this._isSelectedObject = true;
|
||||||
|
|
||||||
|
this._getDashboard().state.editPane.selectObject(
|
||||||
|
this._layoutItem!.getElementBody()!,
|
||||||
|
this._layoutItem!.getElementBody()!.state.key!,
|
||||||
|
{
|
||||||
|
force: true,
|
||||||
|
multi: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._currentGrid = this._getCurrentGrid(evt) ?? this._currentGrid;
|
||||||
|
|
||||||
|
this._grids.forEach((grid) => grid.onDrag?.(this._sourceGrid!, this._currentGrid!, this._layoutItem!, evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onPointerUp(evt: PointerEvent) {
|
||||||
|
console.error('should stop!');
|
||||||
|
|
||||||
|
window.removeEventListener('pointermove', this._onPointerMove);
|
||||||
|
window.removeEventListener('pointerup', this._onPointerUp);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._grids.forEach((obj) => obj.onDragStop?.(this._sourceGrid!, this._currentGrid!, this._layoutItem!, evt));
|
||||||
|
|
||||||
|
this._isSelectedObject = false;
|
||||||
|
this._sourceGrid = null;
|
||||||
|
this._currentGrid = null;
|
||||||
|
this._layoutItem = null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ export class AutoGridItem extends SceneObjectBase<AutoGridItemState> implements
|
|||||||
this.setState({ body });
|
this.setState({ body });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getElementBody(): VizPanel {
|
||||||
|
return this.state.body;
|
||||||
|
}
|
||||||
|
|
||||||
public performRepeat() {
|
public performRepeat() {
|
||||||
if (!this.state.variableName || sceneGraph.hasVariableDependencyInLoadingState(this)) {
|
if (!this.state.variableName || sceneGraph.hasVariableDependencyInLoadingState(this)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { createRef, CSSProperties, PointerEvent as ReactPointerEvent } from 'react';
|
import { createRef, CSSProperties, PointerEvent as ReactPointerEvent } from 'react';
|
||||||
|
|
||||||
import { SceneLayout, SceneObjectBase, SceneObjectState, VizPanel, SceneGridItemLike } from '@grafana/scenes';
|
import { SceneLayout, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes';
|
||||||
|
|
||||||
import { isRepeatCloneOrChildOf } from '../../utils/clone';
|
import { isRepeatCloneOrChildOf } from '../../utils/clone';
|
||||||
import { getLayoutOrchestratorFor } from '../../utils/utils';
|
import { getLayoutOrchestratorFor } from '../../utils/utils';
|
||||||
|
import { DashboardLayoutGrid } from '../types/DashboardLayoutGrid';
|
||||||
|
import { DashboardLayoutItem } from '../types/DashboardLayoutItem';
|
||||||
|
|
||||||
import { AutoGridItem } from './AutoGridItem';
|
import { AutoGridItem } from './AutoGridItem';
|
||||||
|
import { AutoGridLayoutManager } from './AutoGridLayoutManager';
|
||||||
import { AutoGridLayoutRenderer } from './AutoGridLayoutRenderer';
|
import { AutoGridLayoutRenderer } from './AutoGridLayoutRenderer';
|
||||||
import { DRAGGED_ITEM_HEIGHT, DRAGGED_ITEM_LEFT, DRAGGED_ITEM_TOP, DRAGGED_ITEM_WIDTH } from './const';
|
import { DRAGGED_ITEM_HEIGHT, DRAGGED_ITEM_LEFT, DRAGGED_ITEM_TOP, DRAGGED_ITEM_WIDTH } from './const';
|
||||||
|
|
||||||
@@ -58,7 +61,7 @@ export class AutoGridLayout extends SceneObjectBase<AutoGridLayoutState> impleme
|
|||||||
public static Component = AutoGridLayoutRenderer;
|
public static Component = AutoGridLayoutRenderer;
|
||||||
|
|
||||||
public containerRef = createRef<HTMLDivElement>();
|
public containerRef = createRef<HTMLDivElement>();
|
||||||
private _draggedGridItem: AutoGridItem | null = null;
|
private _draggingGridItem: AutoGridItem | null = null;
|
||||||
private _initialGridItemPosition: {
|
private _initialGridItemPosition: {
|
||||||
pageX: number;
|
pageX: number;
|
||||||
pageY: number;
|
pageY: number;
|
||||||
@@ -75,21 +78,6 @@ export class AutoGridLayout extends SceneObjectBase<AutoGridLayoutState> impleme
|
|||||||
children: state.children ?? [],
|
children: state.children ?? [],
|
||||||
...state,
|
...state,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._onDragStart = this._onDragStart.bind(this);
|
|
||||||
this._onDragEnd = this._onDragEnd.bind(this);
|
|
||||||
this._onDrag = this._onDrag.bind(this);
|
|
||||||
|
|
||||||
this.addActivationHandler(() => this._activationHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
private _activationHandler() {
|
|
||||||
return () => {
|
|
||||||
this._resetPanelPositionAndSize();
|
|
||||||
document.body.removeEventListener('pointermove', this._onDrag);
|
|
||||||
document.body.removeEventListener('pointerup', this._onDragEnd);
|
|
||||||
document.body.classList.remove('dashboard-draggable-transparent-selection');
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public isDraggable(): boolean {
|
public isDraggable(): boolean {
|
||||||
@@ -113,78 +101,92 @@ export class AutoGridLayout extends SceneObjectBase<AutoGridLayoutState> impleme
|
|||||||
onDragStart: (evt: ReactPointerEvent, panel: VizPanel) => {
|
onDragStart: (evt: ReactPointerEvent, panel: VizPanel) => {
|
||||||
const gridItem = panel.parent;
|
const gridItem = panel.parent;
|
||||||
if (gridItem instanceof AutoGridItem) {
|
if (gridItem instanceof AutoGridItem) {
|
||||||
this._onDragStart(evt, gridItem);
|
getLayoutOrchestratorFor(this)?.startDragging(evt, gridItem, this._getLayoutManager());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _canDrag(evt: ReactPointerEvent): boolean {
|
// private _canDrag(evt: PointerEvent): boolean {
|
||||||
if (!this.isDraggable()) {
|
// if (!this.isDraggable()) {
|
||||||
return false;
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!(evt.target instanceof Element)) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return !!evt.target.closest(`.${this.getDragClass()}`) && !evt.target.closest(`.${this.getDragClassCancel()}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
private _getLayoutManager(): AutoGridLayoutManager {
|
||||||
|
if (!(this.parent instanceof AutoGridLayoutManager)) {
|
||||||
|
throw new Error('Parent of AutoGridLayout must be AutoGridLayoutManager');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(evt.target instanceof Element)) {
|
return this.parent;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!evt.target.closest(`.${this.getDragClass()}`) && !evt.target.closest(`.${this.getDragClassCancel()}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start inside dragging
|
public onDragStart(sourceGrid: DashboardLayoutGrid, layoutItem: DashboardLayoutItem, evt: PointerEvent) {
|
||||||
private _onDragStart(evt: ReactPointerEvent, gridItem: SceneGridItemLike) {
|
if (sourceGrid === this._getLayoutManager() && layoutItem instanceof AutoGridItem) {
|
||||||
if (!this._canDrag(evt)) {
|
this._draggingGridItem = layoutItem;
|
||||||
return;
|
|
||||||
|
const { top, left, width, height } = this._draggingGridItem!.getBoundingBox();
|
||||||
|
this._initialGridItemPosition = { pageX: evt.pageX, pageY: evt.pageY, top, left: left };
|
||||||
|
this._updatePanelSize(width, height);
|
||||||
|
this._updatePanelPosition(top, left);
|
||||||
|
|
||||||
|
this.setState({ draggingKey: this._draggingGridItem!.state.key });
|
||||||
}
|
}
|
||||||
|
|
||||||
evt.preventDefault();
|
|
||||||
evt.stopPropagation();
|
|
||||||
|
|
||||||
if (!(gridItem instanceof AutoGridItem)) {
|
|
||||||
throw new Error('Dragging wrong item');
|
|
||||||
}
|
|
||||||
|
|
||||||
this._draggedGridItem = gridItem;
|
|
||||||
|
|
||||||
const { top, left, width, height } = this._draggedGridItem.getBoundingBox();
|
|
||||||
this._initialGridItemPosition = { pageX: evt.pageX, pageY: evt.pageY, top, left: left };
|
|
||||||
this._updatePanelSize(width, height);
|
|
||||||
this._updatePanelPosition(top, left);
|
|
||||||
|
|
||||||
this.setState({ draggingKey: this._draggedGridItem.state.key });
|
|
||||||
|
|
||||||
document.body.addEventListener('pointermove', this._onDrag);
|
|
||||||
document.body.addEventListener('pointerup', this._onDragEnd);
|
|
||||||
document.body.classList.add('dashboard-draggable-transparent-selection');
|
|
||||||
|
|
||||||
getLayoutOrchestratorFor(this)?.startDraggingSync(evt, this._draggedGridItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop inside dragging
|
public onDrag(
|
||||||
private _onDragEnd() {
|
sourceGrid: DashboardLayoutGrid,
|
||||||
window.getSelection()?.removeAllRanges();
|
targetGrid: DashboardLayoutGrid,
|
||||||
|
layoutItem: DashboardLayoutItem,
|
||||||
|
evt: PointerEvent
|
||||||
|
) {
|
||||||
|
const layoutManager = this._getLayoutManager();
|
||||||
|
|
||||||
this._draggedGridItem = null;
|
if (targetGrid === layoutManager) {
|
||||||
this._initialGridItemPosition = null;
|
if (this._draggingGridItem) {
|
||||||
this._resetPanelPositionAndSize();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ draggingKey: undefined });
|
if (sourceGrid === layoutManager) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
this._draggingGridItem = layoutItem as AutoGridItem;
|
||||||
|
|
||||||
document.body.removeEventListener('pointermove', this._onDrag);
|
const { top, left, width, height } = this._draggingGridItem!.getBoundingBox();
|
||||||
document.body.removeEventListener('pointerup', this._onDragEnd);
|
this._updatePanelSize(width, height);
|
||||||
document.body.classList.remove('dashboard-draggable-transparent-selection');
|
this._updatePanelPosition(top, left);
|
||||||
}
|
|
||||||
|
|
||||||
// Handle inside drag moves
|
if (this.state.draggingKey !== this._draggingGridItem!.state.key) {
|
||||||
private _onDrag(evt: PointerEvent) {
|
this.setState({ draggingKey: this._draggingGridItem!.state.key });
|
||||||
if (!this._draggedGridItem || !this._initialGridItemPosition) {
|
}
|
||||||
this._onDragEnd();
|
} else {
|
||||||
return;
|
if (layoutItem instanceof AutoGridItem) {
|
||||||
|
this._draggingGridItem = layoutItem.clone();
|
||||||
|
this.setState({ children: [...this.state.children, this._draggingGridItem!], draggingKey: this._draggingGridItem!.state.key });
|
||||||
|
} else {
|
||||||
|
this._draggingGridItem = new AutoGridItem({ body: layoutItem.getElementBody().clone() });
|
||||||
|
this.setState({ children: [...this.state.children, this._draggingGridItem!], draggingKey: this._draggingGridItem!.state.key });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this._draggingGridItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourceGrid !== layoutManager) {
|
||||||
|
this.setState({ children: this.state.children.filter((child) => child !== this._draggingGridItem!), draggingKey: undefined });
|
||||||
|
this._draggingGridItem = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updatePanelPosition(
|
this._updatePanelPosition(
|
||||||
this._initialGridItemPosition.top + (evt.pageY - this._initialGridItemPosition.pageY),
|
this._initialGridItemPosition!.top + (evt.pageY - this._initialGridItemPosition!.pageY),
|
||||||
this._initialGridItemPosition.left + (evt.pageX - this._initialGridItemPosition.pageX)
|
this._initialGridItemPosition!.left + (evt.pageX - this._initialGridItemPosition!.pageX)
|
||||||
);
|
);
|
||||||
|
|
||||||
const dropTargetGridItemKey = document
|
const dropTargetGridItemKey = document
|
||||||
@@ -192,7 +194,7 @@ export class AutoGridLayout extends SceneObjectBase<AutoGridLayoutState> impleme
|
|||||||
?.find((element) => {
|
?.find((element) => {
|
||||||
const key = element.getAttribute('data-auto-grid-item-drop-target');
|
const key = element.getAttribute('data-auto-grid-item-drop-target');
|
||||||
|
|
||||||
return !!key && key !== this._draggedGridItem!.state.key;
|
return !!key && key !== this._draggingGridItem!.state.key;
|
||||||
})
|
})
|
||||||
?.getAttribute('data-auto-grid-item-drop-target');
|
?.getAttribute('data-auto-grid-item-drop-target');
|
||||||
|
|
||||||
@@ -201,10 +203,35 @@ export class AutoGridLayout extends SceneObjectBase<AutoGridLayoutState> impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onDragStop(sourceGrid: DashboardLayoutGrid,
|
||||||
|
targetGrid: DashboardLayoutGrid,
|
||||||
|
layoutItem: DashboardLayoutItem,
|
||||||
|
evt: PointerEvent) {
|
||||||
|
if (targetGrid !== this._getLayoutManager()) {
|
||||||
|
this.setState({ children: this.state.children.filter((child) => child !== this._draggingGridItem!) });
|
||||||
|
}
|
||||||
|
|
||||||
|
this._draggingGridItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public onDragStop() {
|
||||||
|
// window.getSelection()?.removeAllRanges();
|
||||||
|
//
|
||||||
|
// this._draggedGridItem = null;
|
||||||
|
// this._initialGridItemPosition = null;
|
||||||
|
// this._resetPanelPositionAndSize();
|
||||||
|
//
|
||||||
|
// this.setState({ draggingKey: undefined });
|
||||||
|
//
|
||||||
|
// document.body.removeEventListener('pointermove', this._onDrag);
|
||||||
|
// document.body.removeEventListener('pointerup', this._onDragEnd);
|
||||||
|
// document.body.classList.remove('dashboard-draggable-transparent-selection');
|
||||||
|
// }
|
||||||
|
|
||||||
// Handle dragging an item from the same grid over another item from the same grid
|
// Handle dragging an item from the same grid over another item from the same grid
|
||||||
private _onDragOverItem(key: string) {
|
private _onDragOverItem(key: string) {
|
||||||
const children = [...this.state.children];
|
const children = [...this.state.children];
|
||||||
const draggedIdx = children.findIndex((child) => child === this._draggedGridItem);
|
const draggedIdx = children.findIndex((child) => child === this._draggingGridItem);
|
||||||
const draggedOverIdx = children.findIndex((child) => child.state.key === key);
|
const draggedOverIdx = children.findIndex((child) => child.state.key === key);
|
||||||
|
|
||||||
if (draggedIdx === -1 || draggedOverIdx === -1) {
|
if (draggedIdx === -1 || draggedOverIdx === -1) {
|
||||||
@@ -212,7 +239,7 @@ export class AutoGridLayout extends SceneObjectBase<AutoGridLayoutState> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
children.splice(draggedIdx, 1);
|
children.splice(draggedIdx, 1);
|
||||||
children.splice(draggedOverIdx, 0, this._draggedGridItem!);
|
children.splice(draggedOverIdx, 0, this._draggingGridItem!);
|
||||||
|
|
||||||
this.setState({ children });
|
this.setState({ children });
|
||||||
}
|
}
|
||||||
@@ -227,18 +254,18 @@ export class AutoGridLayout extends SceneObjectBase<AutoGridLayoutState> impleme
|
|||||||
this._setContainerStyle(DRAGGED_ITEM_HEIGHT, `${Math.floor(height)}px`);
|
this._setContainerStyle(DRAGGED_ITEM_HEIGHT, `${Math.floor(height)}px`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resetPanelPositionAndSize() {
|
// private _resetPanelPositionAndSize() {
|
||||||
this._removeContainerStyle(DRAGGED_ITEM_TOP);
|
// this._removeContainerStyle(DRAGGED_ITEM_TOP);
|
||||||
this._removeContainerStyle(DRAGGED_ITEM_LEFT);
|
// this._removeContainerStyle(DRAGGED_ITEM_LEFT);
|
||||||
this._removeContainerStyle(DRAGGED_ITEM_WIDTH);
|
// this._removeContainerStyle(DRAGGED_ITEM_WIDTH);
|
||||||
this._removeContainerStyle(DRAGGED_ITEM_HEIGHT);
|
// this._removeContainerStyle(DRAGGED_ITEM_HEIGHT);
|
||||||
}
|
// }
|
||||||
|
|
||||||
private _setContainerStyle(name: string, value: string) {
|
private _setContainerStyle(name: string, value: string) {
|
||||||
this.containerRef.current?.style.setProperty(name, value);
|
this.containerRef.current?.style.setProperty(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _removeContainerStyle(name: string) {
|
// private _removeContainerStyle(name: string) {
|
||||||
this.containerRef.current?.style.removeProperty(name);
|
// this.containerRef.current?.style.removeProperty(name);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
import { DashboardGridItem } from '../layout-default/DashboardGridItem';
|
import { DashboardGridItem } from '../layout-default/DashboardGridItem';
|
||||||
import { clearClipboard, getAutoGridItemFromClipboard } from '../layouts-shared/paste';
|
import { clearClipboard, getAutoGridItemFromClipboard } from '../layouts-shared/paste';
|
||||||
import { DashboardLayoutGrid } from '../types/DashboardLayoutGrid';
|
import { DashboardLayoutGrid } from '../types/DashboardLayoutGrid';
|
||||||
|
import { DashboardLayoutItem } from '../types/DashboardLayoutItem';
|
||||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||||
import { LayoutRegistryItem } from '../types/LayoutRegistryItem';
|
import { LayoutRegistryItem } from '../types/LayoutRegistryItem';
|
||||||
|
|
||||||
@@ -359,6 +360,28 @@ export class AutoGridLayoutManager extends SceneObjectBase<AutoGridLayoutManager
|
|||||||
|
|
||||||
this.state.layout.setState({ children: [...this.state.layout.state.children, gridItem] });
|
this.state.layout.setState({ children: [...this.state.layout.state.children, gridItem] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onDragStart(sourceGrid: DashboardLayoutGrid, layoutItem: DashboardLayoutItem, evt: PointerEvent) {
|
||||||
|
this.state.layout.onDragStart(sourceGrid, layoutItem, evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDrag(
|
||||||
|
sourceGrid: DashboardLayoutGrid,
|
||||||
|
targetGrid: DashboardLayoutGrid,
|
||||||
|
layoutItem: DashboardLayoutItem,
|
||||||
|
evt: PointerEvent,
|
||||||
|
) {
|
||||||
|
this.state.layout.onDrag(sourceGrid, targetGrid, layoutItem, evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDragStop(
|
||||||
|
sourceGrid: DashboardLayoutGrid,
|
||||||
|
targetGrid: DashboardLayoutGrid,
|
||||||
|
layoutItem: DashboardLayoutItem,
|
||||||
|
evt: PointerEvent,
|
||||||
|
) {
|
||||||
|
// this.state.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function AutoGridLayoutManagerRenderer({ model }: SceneComponentProps<AutoGridLayoutManager>) {
|
function AutoGridLayoutManagerRenderer({ model }: SceneComponentProps<AutoGridLayoutManager>) {
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ export function AutoGridLayoutRenderer({ model }: SceneComponentProps<AutoGridLa
|
|||||||
<div
|
<div
|
||||||
className={cx(styles.container, fillScreen && styles.containerFillScreen, isEditing && styles.containerEditing)}
|
className={cx(styles.container, fillScreen && styles.containerFillScreen, isEditing && styles.containerEditing)}
|
||||||
ref={model.containerRef}
|
ref={model.containerRef}
|
||||||
|
data-grid-manager-key={
|
||||||
|
// We're using the manager key here as the manager doesn't have a wrapper element for the layout
|
||||||
|
model.parent!.state.key
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{children.map((item) => (
|
{children.map((item) => (
|
||||||
<item.Component key={item.state.key} model={item} />
|
<item.Component key={item.state.key} model={item} />
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ export class DashboardGridItem
|
|||||||
this.setState({ body });
|
this.setState({ body });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getElementBody(): VizPanel {
|
||||||
|
return this.state.body;
|
||||||
|
}
|
||||||
|
|
||||||
public handleEditChange() {
|
public handleEditChange() {
|
||||||
this._prevRepeatValues = undefined;
|
this._prevRepeatValues = undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import {
|
|||||||
SceneComponentProps,
|
SceneComponentProps,
|
||||||
SceneGridItemLike,
|
SceneGridItemLike,
|
||||||
useSceneObjectState,
|
useSceneObjectState,
|
||||||
SceneGridLayoutDragStartEvent,
|
|
||||||
SceneObject,
|
SceneObject,
|
||||||
|
SceneGridLayoutDragStartEvent,
|
||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
||||||
import { useStyles2 } from '@grafana/ui';
|
import { useStyles2 } from '@grafana/ui';
|
||||||
@@ -39,8 +39,8 @@ import {
|
|||||||
getVizPanelKeyForPanelId,
|
getVizPanelKeyForPanelId,
|
||||||
getGridItemKeyForPanelId,
|
getGridItemKeyForPanelId,
|
||||||
useDashboard,
|
useDashboard,
|
||||||
getLayoutOrchestratorFor,
|
|
||||||
getDashboardSceneFor,
|
getDashboardSceneFor,
|
||||||
|
getLayoutOrchestratorFor,
|
||||||
} from '../../utils/utils';
|
} from '../../utils/utils';
|
||||||
import { useSoloPanelContext } from '../SoloPanelContext';
|
import { useSoloPanelContext } from '../SoloPanelContext';
|
||||||
import { AutoGridItem } from '../layout-auto-grid/AutoGridItem';
|
import { AutoGridItem } from '../layout-auto-grid/AutoGridItem';
|
||||||
@@ -49,6 +49,7 @@ import { clearClipboard, getDashboardGridItemFromClipboard } from '../layouts-sh
|
|||||||
import { dashboardCanvasAddButtonHoverStyles } from '../layouts-shared/styles';
|
import { dashboardCanvasAddButtonHoverStyles } from '../layouts-shared/styles';
|
||||||
import { getIsLazy } from '../layouts-shared/utils';
|
import { getIsLazy } from '../layouts-shared/utils';
|
||||||
import { DashboardLayoutGrid } from '../types/DashboardLayoutGrid';
|
import { DashboardLayoutGrid } from '../types/DashboardLayoutGrid';
|
||||||
|
import { DashboardLayoutItem } from '../types/DashboardLayoutItem';
|
||||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||||
import { LayoutRegistryItem } from '../types/LayoutRegistryItem';
|
import { LayoutRegistryItem } from '../types/LayoutRegistryItem';
|
||||||
|
|
||||||
@@ -88,6 +89,8 @@ export class DefaultGridLayoutManager
|
|||||||
|
|
||||||
public readonly descriptor = DefaultGridLayoutManager.descriptor;
|
public readonly descriptor = DefaultGridLayoutManager.descriptor;
|
||||||
|
|
||||||
|
private _draggingGridItem: DashboardLayoutItem | undefined;
|
||||||
|
|
||||||
public constructor(state: DefaultGridLayoutManagerState) {
|
public constructor(state: DefaultGridLayoutManagerState) {
|
||||||
super(state);
|
super(state);
|
||||||
|
|
||||||
@@ -128,7 +131,7 @@ export class DefaultGridLayoutManager
|
|||||||
this.subscribeToEvent(SceneGridLayoutDragStartEvent, ({ payload: { evt, panel } }) => {
|
this.subscribeToEvent(SceneGridLayoutDragStartEvent, ({ payload: { evt, panel } }) => {
|
||||||
const gridItem = panel.parent;
|
const gridItem = panel.parent;
|
||||||
if (gridItem instanceof DashboardGridItem) {
|
if (gridItem instanceof DashboardGridItem) {
|
||||||
getLayoutOrchestratorFor(this)?.startDraggingSync(evt, gridItem);
|
getLayoutOrchestratorFor(this)?.startDragging(evt, gridItem, this);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -364,7 +367,10 @@ export class DefaultGridLayoutManager
|
|||||||
const panels: VizPanel[] = [];
|
const panels: VizPanel[] = [];
|
||||||
|
|
||||||
this.state.grid.forEachChild((child) => {
|
this.state.grid.forEachChild((child) => {
|
||||||
if (!(child instanceof DashboardGridItem) && !(child instanceof SceneGridRow)) {
|
if (
|
||||||
|
!(child instanceof DashboardGridItem) &&
|
||||||
|
!(child instanceof SceneGridRow)
|
||||||
|
) {
|
||||||
throw new Error('Child is not a DashboardGridItem or SceneGridRow, invalid scene');
|
throw new Error('Child is not a DashboardGridItem or SceneGridRow, invalid scene');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,6 +569,37 @@ export class DefaultGridLayoutManager
|
|||||||
this.state.grid.setState({ children: [...this.state.grid.state.children, gridItem] });
|
this.state.grid.setState({ children: [...this.state.grid.state.children, gridItem] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onDragStart(sourceGrid: DashboardLayoutGrid, layoutItem: DashboardLayoutItem) {
|
||||||
|
if (sourceGrid === this) {
|
||||||
|
this._draggingGridItem = layoutItem;
|
||||||
|
} else {
|
||||||
|
if (layoutItem instanceof DashboardGridItem) {
|
||||||
|
this._draggingGridItem = layoutItem.clone();
|
||||||
|
} else if (layoutItem instanceof AutoGridItem) {
|
||||||
|
this._draggingGridItem = new DashboardGridItem({
|
||||||
|
width: NEW_PANEL_WIDTH,
|
||||||
|
height: NEW_PANEL_HEIGHT,
|
||||||
|
body: layoutItem.state.body.clone(),
|
||||||
|
variableName: layoutItem.state.variableName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.grid.setPlaceholder(this._draggingGridItem!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDragStop(sourceGrid: DashboardLayoutGrid, targetGrid: DashboardLayoutGrid, layoutItem: DashboardLayoutItem) {
|
||||||
|
if (sourceGrid !== this && targetGrid === this) {
|
||||||
|
const panel = layoutItem.getElementBody();
|
||||||
|
panel.clearParent();
|
||||||
|
this._draggingGridItem?.setElementBody(panel);
|
||||||
|
} else if (sourceGrid === this && targetGrid !== this) {
|
||||||
|
this.state.grid.setState({ children: this.state.grid.state.children.filter((child) => child !== layoutItem) });
|
||||||
|
}
|
||||||
|
|
||||||
|
this._draggingGridItem = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
public static createFromLayout(currentLayout: DashboardLayoutManager): DefaultGridLayoutManager {
|
public static createFromLayout(currentLayout: DashboardLayoutManager): DefaultGridLayoutManager {
|
||||||
const panels = currentLayout.getVizPanels();
|
const panels = currentLayout.getVizPanels();
|
||||||
const isLazy = getIsLazy(getDashboardSceneFor(currentLayout).state.preload)!;
|
const isLazy = getIsLazy(getDashboardSceneFor(currentLayout).state.preload)!;
|
||||||
@@ -657,7 +694,10 @@ function DefaultGridLayoutManagerRenderer({ model }: SceneComponentProps<Default
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(styles.container, isEditing && styles.containerEditing)}>
|
<div
|
||||||
|
className={cx(styles.container, isEditing && styles.containerEditing)}
|
||||||
|
data-grid-manager-key={model.state.key!}
|
||||||
|
>
|
||||||
{model.state.grid.Component && <model.state.grid.Component model={model.state.grid} />}
|
{model.state.grid.Component && <model.state.grid.Component model={model.state.grid} />}
|
||||||
{showCanvasActions && (
|
{showCanvasActions && (
|
||||||
<div className={styles.actionsWrapper}>
|
<div className={styles.actionsWrapper}>
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { t } from '@grafana/i18n';
|
import { t } from '@grafana/i18n';
|
||||||
import { logWarning } from '@grafana/runtime';
|
import { sceneGraph, SceneObject, SceneObjectBase, SceneObjectState, VariableDependencyConfig } from '@grafana/scenes';
|
||||||
import {
|
|
||||||
sceneGraph,
|
|
||||||
SceneObject,
|
|
||||||
SceneObjectBase,
|
|
||||||
SceneObjectState,
|
|
||||||
VariableDependencyConfig,
|
|
||||||
SceneGridItemLike,
|
|
||||||
SceneGridLayout,
|
|
||||||
} from '@grafana/scenes';
|
|
||||||
import { RowsLayoutRowKind } from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
import { RowsLayoutRowKind } from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
||||||
import { appEvents } from 'app/core/app_events';
|
import { appEvents } from 'app/core/app_events';
|
||||||
import { LS_ROW_COPY_KEY } from 'app/core/constants';
|
import { LS_ROW_COPY_KEY } from 'app/core/constants';
|
||||||
@@ -22,15 +13,10 @@ import { ConditionalRenderingGroup } from '../../conditional-rendering/group/Con
|
|||||||
import { serializeRow } from '../../serialization/layoutSerializers/RowsLayoutSerializer';
|
import { serializeRow } from '../../serialization/layoutSerializers/RowsLayoutSerializer';
|
||||||
import { getElements } from '../../serialization/layoutSerializers/utils';
|
import { getElements } from '../../serialization/layoutSerializers/utils';
|
||||||
import { getDashboardSceneFor } from '../../utils/utils';
|
import { getDashboardSceneFor } from '../../utils/utils';
|
||||||
import { AutoGridItem } from '../layout-auto-grid/AutoGridItem';
|
|
||||||
import { AutoGridLayout } from '../layout-auto-grid/AutoGridLayout';
|
|
||||||
import { AutoGridLayoutManager } from '../layout-auto-grid/AutoGridLayoutManager';
|
import { AutoGridLayoutManager } from '../layout-auto-grid/AutoGridLayoutManager';
|
||||||
import { DashboardGridItem } from '../layout-default/DashboardGridItem';
|
|
||||||
import { clearClipboard } from '../layouts-shared/paste';
|
import { clearClipboard } from '../layouts-shared/paste';
|
||||||
import { scrollCanvasElementIntoView } from '../layouts-shared/scrollCanvasElementIntoView';
|
import { scrollCanvasElementIntoView } from '../layouts-shared/scrollCanvasElementIntoView';
|
||||||
import { BulkActionElement } from '../types/BulkActionElement';
|
import { BulkActionElement } from '../types/BulkActionElement';
|
||||||
import { DashboardDropTarget } from '../types/DashboardDropTarget';
|
|
||||||
import { isDashboardLayoutGrid } from '../types/DashboardLayoutGrid';
|
|
||||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||||
import { EditableDashboardElement, EditableDashboardElementInfo } from '../types/EditableDashboardElement';
|
import { EditableDashboardElement, EditableDashboardElementInfo } from '../types/EditableDashboardElement';
|
||||||
import { LayoutParent } from '../types/LayoutParent';
|
import { LayoutParent } from '../types/LayoutParent';
|
||||||
@@ -56,7 +42,7 @@ export interface RowItemState extends SceneObjectState {
|
|||||||
|
|
||||||
export class RowItem
|
export class RowItem
|
||||||
extends SceneObjectBase<RowItemState>
|
extends SceneObjectBase<RowItemState>
|
||||||
implements LayoutParent, BulkActionElement, EditableDashboardElement, DashboardDropTarget
|
implements LayoutParent, BulkActionElement, EditableDashboardElement
|
||||||
{
|
{
|
||||||
public static Component = RowItemRenderer;
|
public static Component = RowItemRenderer;
|
||||||
|
|
||||||
@@ -65,7 +51,6 @@ export class RowItem
|
|||||||
});
|
});
|
||||||
|
|
||||||
public readonly isEditableDashboardElement = true;
|
public readonly isEditableDashboardElement = true;
|
||||||
public readonly isDashboardDropTarget = true;
|
|
||||||
public containerRef: React.MutableRefObject<HTMLDivElement | null> = React.createRef<HTMLDivElement>();
|
public containerRef: React.MutableRefObject<HTMLDivElement | null> = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
public constructor(state?: Partial<RowItemState>) {
|
public constructor(state?: Partial<RowItemState>) {
|
||||||
@@ -168,44 +153,6 @@ export class RowItem
|
|||||||
store.set(LS_ROW_COPY_KEY, JSON.stringify({ elements, row: this.serialize() }));
|
store.set(LS_ROW_COPY_KEY, JSON.stringify({ elements, row: this.serialize() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
public setIsDropTarget(isDropTarget: boolean) {
|
|
||||||
if (!!this.state.isDropTarget !== isDropTarget) {
|
|
||||||
this.setState({ isDropTarget });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public draggedGridItemOutside?(gridItem: SceneGridItemLike): void {
|
|
||||||
// Remove from source layout
|
|
||||||
if (gridItem instanceof DashboardGridItem || gridItem instanceof AutoGridItem) {
|
|
||||||
const layout = gridItem.parent;
|
|
||||||
if (gridItem instanceof DashboardGridItem && layout instanceof SceneGridLayout) {
|
|
||||||
const newChildren = layout.state.children.filter((child) => child !== gridItem);
|
|
||||||
layout.setState({ children: newChildren });
|
|
||||||
} else if (gridItem instanceof AutoGridItem && layout instanceof AutoGridLayout) {
|
|
||||||
const newChildren = layout.state.children.filter((child) => child !== gridItem);
|
|
||||||
layout.setState({ children: newChildren });
|
|
||||||
} else {
|
|
||||||
const warningMessage = 'Grid item has unexpected parent type';
|
|
||||||
console.warn(warningMessage);
|
|
||||||
logWarning(warningMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setIsDropTarget(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public draggedGridItemInside(gridItem: SceneGridItemLike): void {
|
|
||||||
const layout = this.getLayout();
|
|
||||||
|
|
||||||
if (isDashboardLayoutGrid(layout)) {
|
|
||||||
layout.addGridItem(gridItem);
|
|
||||||
} else {
|
|
||||||
const warningMessage = 'Layout manager does not support addGridItem';
|
|
||||||
console.warn(warningMessage);
|
|
||||||
logWarning(warningMessage);
|
|
||||||
}
|
|
||||||
this.setIsDropTarget(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onChangeTitle(title: string) {
|
public onChangeTitle(title: string) {
|
||||||
this.setState({ title });
|
this.setState({ title });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { useSoloPanelContext } from '../SoloPanelContext';
|
|||||||
import { RowItem } from './RowItem';
|
import { RowItem } from './RowItem';
|
||||||
|
|
||||||
export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
|
export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
|
||||||
const { layout, collapse: isCollapsed, fillScreen, hideHeader: isHeaderHidden, isDropTarget, key } = model.useState();
|
const { layout, collapse: isCollapsed, fillScreen, hideHeader: isHeaderHidden, key } = model.useState();
|
||||||
const isClone = isRepeatCloneOrChildOf(model);
|
const isClone = isRepeatCloneOrChildOf(model);
|
||||||
const { isEditing } = useDashboardState(model);
|
const { isEditing } = useDashboardState(model);
|
||||||
const [isConditionallyHidden, conditionalRenderingClass, conditionalRenderingOverlay] =
|
const [isConditionallyHidden, conditionalRenderingClass, conditionalRenderingOverlay] =
|
||||||
@@ -82,7 +82,6 @@ export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
|
|||||||
dragProvided.innerRef(ref);
|
dragProvided.innerRef(ref);
|
||||||
model.containerRef.current = ref;
|
model.containerRef.current = ref;
|
||||||
}}
|
}}
|
||||||
data-dashboard-drop-target-key={model.state.key}
|
|
||||||
className={cx(
|
className={cx(
|
||||||
styles.wrapper,
|
styles.wrapper,
|
||||||
!isCollapsed && styles.wrapperNotCollapsed,
|
!isCollapsed && styles.wrapperNotCollapsed,
|
||||||
@@ -91,8 +90,7 @@ export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
|
|||||||
shouldGrow && styles.wrapperGrow,
|
shouldGrow && styles.wrapperGrow,
|
||||||
conditionalRenderingClass,
|
conditionalRenderingClass,
|
||||||
!isClone && isSelected && 'dashboard-selected-element',
|
!isClone && isSelected && 'dashboard-selected-element',
|
||||||
!isClone && !isSelected && selectableHighlight && 'dashboard-selectable-element',
|
!isClone && !isSelected && selectableHighlight && 'dashboard-selectable-element'
|
||||||
isDropTarget && 'dashboard-drop-target'
|
|
||||||
)}
|
)}
|
||||||
onPointerDown={(evt) => {
|
onPointerDown={(evt) => {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { t } from '@grafana/i18n';
|
import { t } from '@grafana/i18n';
|
||||||
import { logWarning } from '@grafana/runtime';
|
import { SceneObjectState, SceneObjectBase, sceneGraph, VariableDependencyConfig, SceneObject } from '@grafana/scenes';
|
||||||
import {
|
|
||||||
SceneObjectState,
|
|
||||||
SceneObjectBase,
|
|
||||||
sceneGraph,
|
|
||||||
VariableDependencyConfig,
|
|
||||||
SceneObject,
|
|
||||||
SceneGridItemLike,
|
|
||||||
SceneGridLayout,
|
|
||||||
} from '@grafana/scenes';
|
|
||||||
import { TabsLayoutTabKind } from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
import { TabsLayoutTabKind } from '@grafana/schema/dist/esm/schema/dashboard/v2';
|
||||||
import { appEvents } from 'app/core/app_events';
|
import { appEvents } from 'app/core/app_events';
|
||||||
import { LS_TAB_COPY_KEY } from 'app/core/constants';
|
import { LS_TAB_COPY_KEY } from 'app/core/constants';
|
||||||
@@ -22,15 +13,10 @@ import { ConditionalRenderingGroup } from '../../conditional-rendering/group/Con
|
|||||||
import { serializeTab } from '../../serialization/layoutSerializers/TabsLayoutSerializer';
|
import { serializeTab } from '../../serialization/layoutSerializers/TabsLayoutSerializer';
|
||||||
import { getElements } from '../../serialization/layoutSerializers/utils';
|
import { getElements } from '../../serialization/layoutSerializers/utils';
|
||||||
import { getDashboardSceneFor } from '../../utils/utils';
|
import { getDashboardSceneFor } from '../../utils/utils';
|
||||||
import { AutoGridItem } from '../layout-auto-grid/AutoGridItem';
|
|
||||||
import { AutoGridLayout } from '../layout-auto-grid/AutoGridLayout';
|
|
||||||
import { AutoGridLayoutManager } from '../layout-auto-grid/AutoGridLayoutManager';
|
import { AutoGridLayoutManager } from '../layout-auto-grid/AutoGridLayoutManager';
|
||||||
import { DashboardGridItem } from '../layout-default/DashboardGridItem';
|
|
||||||
import { clearClipboard } from '../layouts-shared/paste';
|
import { clearClipboard } from '../layouts-shared/paste';
|
||||||
import { scrollCanvasElementIntoView } from '../layouts-shared/scrollCanvasElementIntoView';
|
import { scrollCanvasElementIntoView } from '../layouts-shared/scrollCanvasElementIntoView';
|
||||||
import { BulkActionElement } from '../types/BulkActionElement';
|
import { BulkActionElement } from '../types/BulkActionElement';
|
||||||
import { DashboardDropTarget } from '../types/DashboardDropTarget';
|
|
||||||
import { isDashboardLayoutGrid } from '../types/DashboardLayoutGrid';
|
|
||||||
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
|
||||||
import { EditableDashboardElement, EditableDashboardElementInfo } from '../types/EditableDashboardElement';
|
import { EditableDashboardElement, EditableDashboardElementInfo } from '../types/EditableDashboardElement';
|
||||||
import { LayoutParent } from '../types/LayoutParent';
|
import { LayoutParent } from '../types/LayoutParent';
|
||||||
@@ -43,7 +29,6 @@ import { TabsLayoutManager } from './TabsLayoutManager';
|
|||||||
export interface TabItemState extends SceneObjectState {
|
export interface TabItemState extends SceneObjectState {
|
||||||
layout: DashboardLayoutManager;
|
layout: DashboardLayoutManager;
|
||||||
title?: string;
|
title?: string;
|
||||||
isDropTarget?: boolean;
|
|
||||||
conditionalRendering?: ConditionalRenderingGroup;
|
conditionalRendering?: ConditionalRenderingGroup;
|
||||||
repeatByVariable?: string;
|
repeatByVariable?: string;
|
||||||
repeatedTabs?: TabItem[];
|
repeatedTabs?: TabItem[];
|
||||||
@@ -53,7 +38,7 @@ export interface TabItemState extends SceneObjectState {
|
|||||||
|
|
||||||
export class TabItem
|
export class TabItem
|
||||||
extends SceneObjectBase<TabItemState>
|
extends SceneObjectBase<TabItemState>
|
||||||
implements LayoutParent, BulkActionElement, EditableDashboardElement, DashboardDropTarget
|
implements LayoutParent, BulkActionElement, EditableDashboardElement
|
||||||
{
|
{
|
||||||
public static Component = TabItemRenderer;
|
public static Component = TabItemRenderer;
|
||||||
|
|
||||||
@@ -62,7 +47,6 @@ export class TabItem
|
|||||||
});
|
});
|
||||||
|
|
||||||
public readonly isEditableDashboardElement = true;
|
public readonly isEditableDashboardElement = true;
|
||||||
public readonly isDashboardDropTarget = true;
|
|
||||||
|
|
||||||
public containerRef = React.createRef<HTMLDivElement>();
|
public containerRef = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
@@ -186,49 +170,6 @@ export class TabItem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setIsDropTarget(isDropTarget: boolean) {
|
|
||||||
if (!!this.state.isDropTarget !== isDropTarget) {
|
|
||||||
this.setState({ isDropTarget });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public draggedGridItemOutside?(gridItem: SceneGridItemLike): void {
|
|
||||||
// Remove from source layout
|
|
||||||
if (gridItem instanceof DashboardGridItem || gridItem instanceof AutoGridItem) {
|
|
||||||
const layout = gridItem.parent;
|
|
||||||
if (gridItem instanceof DashboardGridItem && layout instanceof SceneGridLayout) {
|
|
||||||
const newChildren = layout.state.children.filter((child) => child !== gridItem);
|
|
||||||
layout.setState({ children: newChildren });
|
|
||||||
} else if (gridItem instanceof AutoGridItem && layout instanceof AutoGridLayout) {
|
|
||||||
const newChildren = layout.state.children.filter((child) => child !== gridItem);
|
|
||||||
layout.setState({ children: newChildren });
|
|
||||||
} else {
|
|
||||||
const warningMessage = 'Grid item has unexpected parent type';
|
|
||||||
console.warn(warningMessage);
|
|
||||||
logWarning(warningMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setIsDropTarget(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public draggedGridItemInside(gridItem: SceneGridItemLike): void {
|
|
||||||
const layout = this.getLayout();
|
|
||||||
|
|
||||||
if (isDashboardLayoutGrid(layout)) {
|
|
||||||
layout.addGridItem(gridItem);
|
|
||||||
} else {
|
|
||||||
const warningMessage = 'Layout manager does not support addGridItem';
|
|
||||||
console.warn(warningMessage);
|
|
||||||
logWarning(warningMessage);
|
|
||||||
}
|
|
||||||
this.setIsDropTarget(false);
|
|
||||||
|
|
||||||
const parentLayout = this.getParentLayout();
|
|
||||||
if (parentLayout.state.currentTabSlug !== this.getSlug()) {
|
|
||||||
parentLayout.setState({ currentTabSlug: this.getSlug() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getParentLayout(): TabsLayoutManager {
|
public getParentLayout(): TabsLayoutManager {
|
||||||
return sceneGraph.getAncestor(this, TabsLayoutManager);
|
return sceneGraph.getAncestor(this, TabsLayoutManager);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { useSoloPanelContext } from '../SoloPanelContext';
|
|||||||
import { TabItem } from './TabItem';
|
import { TabItem } from './TabItem';
|
||||||
|
|
||||||
export function TabItemRenderer({ model }: SceneComponentProps<TabItem>) {
|
export function TabItemRenderer({ model }: SceneComponentProps<TabItem>) {
|
||||||
const { title, key, isDropTarget, layout } = model.useState();
|
const { title, key, layout } = model.useState();
|
||||||
const parentLayout = model.getParentLayout();
|
const parentLayout = model.getParentLayout();
|
||||||
const { currentTabSlug } = parentLayout.useState();
|
const { currentTabSlug } = parentLayout.useState();
|
||||||
const titleInterpolated = sceneGraph.interpolate(model, title, undefined, 'text');
|
const titleInterpolated = sceneGraph.interpolate(model, title, undefined, 'text');
|
||||||
@@ -67,8 +67,7 @@ export function TabItemRenderer({ model }: SceneComponentProps<TabItem>) {
|
|||||||
className={cx(
|
className={cx(
|
||||||
isConditionallyHidden && styles.hidden,
|
isConditionallyHidden && styles.hidden,
|
||||||
isSelected && 'dashboard-selected-element',
|
isSelected && 'dashboard-selected-element',
|
||||||
isSelectable && !isSelected && 'dashboard-selectable-element',
|
isSelectable && !isSelected && 'dashboard-selectable-element'
|
||||||
isDropTarget && 'dashboard-drop-target'
|
|
||||||
)}
|
)}
|
||||||
active={isActive}
|
active={isActive}
|
||||||
title={titleInterpolated}
|
title={titleInterpolated}
|
||||||
@@ -89,7 +88,6 @@ export function TabItemRenderer({ model }: SceneComponentProps<TabItem>) {
|
|||||||
onSelect?.(evt);
|
onSelect?.(evt);
|
||||||
}}
|
}}
|
||||||
label={titleInterpolated}
|
label={titleInterpolated}
|
||||||
data-dashboard-drop-target-key={model.state.key}
|
|
||||||
{...titleCollisionProps}
|
{...titleCollisionProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,15 +112,12 @@ interface TabItemLayoutRendererProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TabItemLayoutRenderer({ tab, isEditing }: TabItemLayoutRendererProps) {
|
export function TabItemLayoutRenderer({ tab, isEditing }: TabItemLayoutRendererProps) {
|
||||||
const { layout, key } = tab.useState();
|
const { layout } = tab.useState();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const [_, conditionalRenderingClass, conditionalRenderingOverlay] = useIsConditionallyHidden(tab);
|
const [_, conditionalRenderingClass, conditionalRenderingOverlay] = useIsConditionallyHidden(tab);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabContent
|
<TabContent className={cx(styles.tabContentContainer, isEditing && conditionalRenderingClass)}>
|
||||||
className={cx(styles.tabContentContainer, isEditing && conditionalRenderingClass)}
|
|
||||||
data-dashboard-drop-target-key={key}
|
|
||||||
>
|
|
||||||
<layout.Component model={layout} />
|
<layout.Component model={layout} />
|
||||||
{isEditing && conditionalRenderingOverlay}
|
{isEditing && conditionalRenderingOverlay}
|
||||||
</TabContent>
|
</TabContent>
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { SceneObject, SceneGridItemLike } from '@grafana/scenes';
|
|
||||||
|
|
||||||
export interface DashboardDropTarget extends SceneObject {
|
|
||||||
isDashboardDropTarget: Readonly<true>;
|
|
||||||
setIsDropTarget?(isDropTarget: boolean): void;
|
|
||||||
draggedGridItemOutside?(gridItem: SceneGridItemLike): void;
|
|
||||||
draggedGridItemInside?(gridItem: SceneGridItemLike): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isDashboardDropTarget(scene: SceneObject): scene is DashboardDropTarget {
|
|
||||||
return 'isDashboardDropTarget' in scene && scene.isDashboardDropTarget === true;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { SceneGridItemLike } from '@grafana/scenes';
|
import { SceneGridItemLike } from '@grafana/scenes';
|
||||||
|
|
||||||
|
import { DashboardLayoutItem } from './DashboardLayoutItem';
|
||||||
import { DashboardLayoutManager } from './DashboardLayoutManager';
|
import { DashboardLayoutManager } from './DashboardLayoutManager';
|
||||||
|
|
||||||
export interface DashboardLayoutGrid extends DashboardLayoutManager {
|
export interface DashboardLayoutGrid extends DashboardLayoutManager {
|
||||||
@@ -11,6 +12,29 @@ export interface DashboardLayoutGrid extends DashboardLayoutManager {
|
|||||||
* Add a grid item to the layout
|
* Add a grid item to the layout
|
||||||
*/
|
*/
|
||||||
addGridItem(gridItem: SceneGridItemLike): void;
|
addGridItem(gridItem: SceneGridItemLike): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the synchronization of the orchestrator with the grid drag
|
||||||
|
*/
|
||||||
|
onDragStart?(sourceGrid: DashboardLayoutGrid, layoutItem: DashboardLayoutItem, evt: PointerEvent): void;
|
||||||
|
|
||||||
|
onDragStop?(
|
||||||
|
sourceGrid: DashboardLayoutGrid,
|
||||||
|
targetGrid: DashboardLayoutGrid,
|
||||||
|
layoutItem: DashboardLayoutItem,
|
||||||
|
evt: PointerEvent
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the grid as the current drop target
|
||||||
|
* Useful for toggling between inner drag and outer drag
|
||||||
|
*/
|
||||||
|
onDrag?(
|
||||||
|
sourceGrid: DashboardLayoutGrid,
|
||||||
|
targetGrid: DashboardLayoutGrid,
|
||||||
|
layoutItem: DashboardLayoutItem,
|
||||||
|
evt: PointerEvent
|
||||||
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDashboardLayoutGrid(obj: DashboardLayoutManager): obj is DashboardLayoutGrid {
|
export function isDashboardLayoutGrid(obj: DashboardLayoutManager): obj is DashboardLayoutGrid {
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ export interface DashboardLayoutItem extends SceneObject {
|
|||||||
* Change inner body / viz panel
|
* Change inner body / viz panel
|
||||||
*/
|
*/
|
||||||
setElementBody(body: VizPanel): void;
|
setElementBody(body: VizPanel): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access inner body / viz panel
|
||||||
|
*/
|
||||||
|
getElementBody(): VizPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDashboardLayoutItem(obj: SceneObject): obj is DashboardLayoutItem {
|
export function isDashboardLayoutItem(obj: SceneObject): obj is DashboardLayoutItem {
|
||||||
|
|||||||
28
yarn.lock
28
yarn.lock
@@ -3642,7 +3642,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@grafana/scenes@npm:6.46.0, @grafana/scenes@npm:^6.46.0":
|
"@grafana/scenes@npm:6.46.0":
|
||||||
version: 6.46.0
|
version: 6.46.0
|
||||||
resolution: "@grafana/scenes@npm:6.46.0"
|
resolution: "@grafana/scenes@npm:6.46.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3668,6 +3668,32 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@grafana/scenes@npm:^6.46.0":
|
||||||
|
version: 6.48.0
|
||||||
|
resolution: "@grafana/scenes@npm:6.48.0"
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/react": "npm:^0.26.16"
|
||||||
|
"@leeoniya/ufuzzy": "npm:^1.0.16"
|
||||||
|
"@tanstack/react-virtual": "npm:^3.9.0"
|
||||||
|
i18next-parser: "npm:9.3.0"
|
||||||
|
react-grid-layout: "npm:1.3.4"
|
||||||
|
react-use: "npm:17.5.0"
|
||||||
|
react-virtualized-auto-sizer: "npm:1.0.24"
|
||||||
|
uuid: "npm:^9.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
"@grafana/data": ">=10.4"
|
||||||
|
"@grafana/e2e-selectors": ">=10.4"
|
||||||
|
"@grafana/i18n": "*"
|
||||||
|
"@grafana/runtime": ">=10.4"
|
||||||
|
"@grafana/schema": ">=10.4"
|
||||||
|
"@grafana/ui": ">=10.4"
|
||||||
|
react: ^18.0.0
|
||||||
|
react-dom: ^18.0.0
|
||||||
|
react-router-dom: ^6.28.0
|
||||||
|
checksum: 10/28cd64ea3c4faf87173ea71ffc136a7a525c33ec2e263ab2a98df718e3968ed7b7a12ecf0f309400af78e3c3269ae6048da8113412645a361a3e4925f9a2a810
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@grafana/schema@npm:12.4.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema":
|
"@grafana/schema@npm:12.4.0-pre, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@grafana/schema@workspace:packages/grafana-schema"
|
resolution: "@grafana/schema@workspace:packages/grafana-schema"
|
||||||
|
|||||||
Reference in New Issue
Block a user