grafana/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItem.tsx

265 lines
7.9 KiB
TypeScript
Raw Normal View History

import { isEqual } from 'lodash';
Scenes: Implement drag and drop support for SceneCSSGridLayout (#99386) * Draft: Move css grid stuff to main * Scenes: Implement drag and drop support for SceneCSSGridLayout * Fix some nits * WIP Refactor * Added a comment * Add orchestrator to v2schema and fix error (#100964) * Add orchestrator to v2schema and fix error * Display placeholder directly when starting to drag --------- Co-authored-by: kay delaney <kay@grafana.com> * Fix merge issue * Fix panel drag offset and remove console.logs * Fix small nit * Fix issue where layout options weren't refreshed on changing layout * Return empty array from useEditPaneOptions if dashboard body isn't LayoutOrchestrator * Expect layoutOrchestrator when serializing scene * Fix tests to expect orchestrator instead of layoutManager * Fix tests in transformSaveModelSchemaV2ToScene.test.ts * Fix tests in transformSceneToSaveModelSchemaV2.test.ts * More test fixes * fix lint issues * Small fixes * default to adding layout orchestrator? * Empty commit * delete artifactspage.go * remove artifactspage.tmpl.html * betterer * WIP refactor, not ready for review * Slightly fix placeholder behavior. still broken though * Fixed some visual glitches. Still very buggy * Fix layout bugginess when initiating dragging * more WIP * Fix some broken logic * clean up * Move LayoutOrchestrator to dashboard state * More cleanup * Fix misaligned placeholders after changing layout options or resizing browser * Fix issue with dragging vs selection * Fix scroll position jumping when dragging in vertically-oriented grid * Fix import order errors * Remove '!' from layoutOrchestrator references * Add LazyLoader support * Dynamic Dashboards: Responsive Grid drag and drop minor fixes (#102430) Changes --------- Co-authored-by: Oscar Kilhed <oscar.kilhed@grafana.com> Co-authored-by: Bogdan Matei <bogdan.matei@grafana.com>
2025-03-19 19:53:58 +08:00
import { createRef } from 'react';
import { Unsubscribable } from 'rxjs';
import {
VizPanel,
SceneObjectBase,
SceneGridLayout,
SceneVariableSet,
SceneGridItemStateLike,
SceneGridItemLike,
sceneGraph,
MultiValueVariable,
LocalValueVariable,
CustomVariable,
VizPanelState,
VariableValueSingle,
} from '@grafana/scenes';
import { GRID_COLUMN_COUNT } from 'app/core/constants';
import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor';
import { getCloneKey } from '../../utils/clone';
import { getMultiVariableValues } from '../../utils/utils';
Scenes: Implement drag and drop support for SceneCSSGridLayout (#99386) * Draft: Move css grid stuff to main * Scenes: Implement drag and drop support for SceneCSSGridLayout * Fix some nits * WIP Refactor * Added a comment * Add orchestrator to v2schema and fix error (#100964) * Add orchestrator to v2schema and fix error * Display placeholder directly when starting to drag --------- Co-authored-by: kay delaney <kay@grafana.com> * Fix merge issue * Fix panel drag offset and remove console.logs * Fix small nit * Fix issue where layout options weren't refreshed on changing layout * Return empty array from useEditPaneOptions if dashboard body isn't LayoutOrchestrator * Expect layoutOrchestrator when serializing scene * Fix tests to expect orchestrator instead of layoutManager * Fix tests in transformSaveModelSchemaV2ToScene.test.ts * Fix tests in transformSceneToSaveModelSchemaV2.test.ts * More test fixes * fix lint issues * Small fixes * default to adding layout orchestrator? * Empty commit * delete artifactspage.go * remove artifactspage.tmpl.html * betterer * WIP refactor, not ready for review * Slightly fix placeholder behavior. still broken though * Fixed some visual glitches. Still very buggy * Fix layout bugginess when initiating dragging * more WIP * Fix some broken logic * clean up * Move LayoutOrchestrator to dashboard state * More cleanup * Fix misaligned placeholders after changing layout options or resizing browser * Fix issue with dragging vs selection * Fix scroll position jumping when dragging in vertically-oriented grid * Fix import order errors * Remove '!' from layoutOrchestrator references * Add LazyLoader support * Dynamic Dashboards: Responsive Grid drag and drop minor fixes (#102430) Changes --------- Co-authored-by: Oscar Kilhed <oscar.kilhed@grafana.com> Co-authored-by: Bogdan Matei <bogdan.matei@grafana.com>
2025-03-19 19:53:58 +08:00
import { Point, Rect } from '../layout-manager/utils';
import { DashboardLayoutItem, IntermediateLayoutItem } from '../types/DashboardLayoutItem';
import { DashboardRepeatsProcessedEvent } from '../types/DashboardRepeatsProcessedEvent';
import { getDashboardGridItemOptions } from './DashboardGridItemEditor';
import { DashboardGridItemRenderer } from './DashboardGridItemRenderer';
import { DashboardGridItemVariableDependencyHandler } from './DashboardGridItemVariableDependencyHandler';
export interface DashboardGridItemState extends SceneGridItemStateLike {
body: VizPanel;
repeatedPanels?: VizPanel[];
variableName?: string;
itemHeight?: number;
repeatDirection?: RepeatDirection;
maxPerRow?: number;
}
export type RepeatDirection = 'v' | 'h';
export class DashboardGridItem
extends SceneObjectBase<DashboardGridItemState>
implements SceneGridItemLike, DashboardLayoutItem
{
public static Component = DashboardGridItemRenderer;
protected _variableDependency = new DashboardGridItemVariableDependencyHandler(this);
public readonly isDashboardLayoutItem = true;
private _prevRepeatValues?: VariableValueSingle[];
private _gridSizeSub: Unsubscribable | undefined;
Scenes: Implement drag and drop support for SceneCSSGridLayout (#99386) * Draft: Move css grid stuff to main * Scenes: Implement drag and drop support for SceneCSSGridLayout * Fix some nits * WIP Refactor * Added a comment * Add orchestrator to v2schema and fix error (#100964) * Add orchestrator to v2schema and fix error * Display placeholder directly when starting to drag --------- Co-authored-by: kay delaney <kay@grafana.com> * Fix merge issue * Fix panel drag offset and remove console.logs * Fix small nit * Fix issue where layout options weren't refreshed on changing layout * Return empty array from useEditPaneOptions if dashboard body isn't LayoutOrchestrator * Expect layoutOrchestrator when serializing scene * Fix tests to expect orchestrator instead of layoutManager * Fix tests in transformSaveModelSchemaV2ToScene.test.ts * Fix tests in transformSceneToSaveModelSchemaV2.test.ts * More test fixes * fix lint issues * Small fixes * default to adding layout orchestrator? * Empty commit * delete artifactspage.go * remove artifactspage.tmpl.html * betterer * WIP refactor, not ready for review * Slightly fix placeholder behavior. still broken though * Fixed some visual glitches. Still very buggy * Fix layout bugginess when initiating dragging * more WIP * Fix some broken logic * clean up * Move LayoutOrchestrator to dashboard state * More cleanup * Fix misaligned placeholders after changing layout options or resizing browser * Fix issue with dragging vs selection * Fix scroll position jumping when dragging in vertically-oriented grid * Fix import order errors * Remove '!' from layoutOrchestrator references * Add LazyLoader support * Dynamic Dashboards: Responsive Grid drag and drop minor fixes (#102430) Changes --------- Co-authored-by: Oscar Kilhed <oscar.kilhed@grafana.com> Co-authored-by: Bogdan Matei <bogdan.matei@grafana.com>
2025-03-19 19:53:58 +08:00
public containerRef = createRef<HTMLElement>();
public constructor(state: DashboardGridItemState) {
super(state);
this.addActivationHandler(() => this.handleVariableName());
}
private _handleGridResize(newState: DashboardGridItemState, prevState: DashboardGridItemState) {
const itemCount = this.state.repeatedPanels?.length ?? 1;
const stateChange: Partial<DashboardGridItemState> = {};
if (newState.height === prevState.height) {
return;
}
if (this.getRepeatDirection() === 'v') {
stateChange.itemHeight = Math.ceil(newState.height! / itemCount);
} else {
const rowCount = Math.ceil(itemCount / this.getMaxPerRow());
stateChange.itemHeight = Math.ceil(newState.height! / rowCount);
}
if (stateChange.itemHeight !== this.state.itemHeight) {
this.setState(stateChange);
}
}
public getClassName(): string {
return this.state.variableName ? 'panel-repeater-grid-item' : '';
}
public getOptions(): OptionsPaneCategoryDescriptor {
return getDashboardGridItemOptions(this);
}
public editingStarted() {
if (!this.state.variableName) {
return;
}
if (this.state.repeatedPanels?.length ?? 0 > 1) {
this.state.body.setState({
$variables: this.state.repeatedPanels![0].state.$variables?.clone(),
$data: this.state.repeatedPanels![0].state.$data?.clone(),
});
}
}
public editingCompleted(withChanges: boolean) {
if (withChanges) {
this._prevRepeatValues = undefined;
}
if (this.state.variableName && this.state.repeatDirection === 'h' && this.state.width !== GRID_COLUMN_COUNT) {
this.setState({ width: GRID_COLUMN_COUNT });
}
}
public performRepeat() {
if (!this.state.variableName || sceneGraph.hasVariableDependencyInLoadingState(this)) {
return;
}
const variable =
sceneGraph.lookupVariable(this.state.variableName, this) ??
new CustomVariable({
name: '_____default_sys_repeat_var_____',
options: [],
value: '',
text: '',
query: 'A',
});
if (!(variable instanceof MultiValueVariable)) {
console.error('DashboardGridItem: Variable is not a MultiValueVariable');
return;
}
const { values, texts } = getMultiVariableValues(variable);
if (isEqual(this._prevRepeatValues, values)) {
return;
}
const panelToRepeat = this.state.body;
const repeatedPanels: VizPanel[] = [];
// when variable has no options (due to error or similar) it will not render any panels at all
// adding a placeholder in this case so that there is at least empty panel that can display error
const emptyVariablePlaceholderOption = {
values: [''],
texts: variable.hasAllValue() ? ['All'] : ['None'],
};
const variableValues = values.length ? values : emptyVariablePlaceholderOption.values;
const variableTexts = texts.length ? texts : emptyVariablePlaceholderOption.texts;
// Loop through variable values and create repeats
for (let index = 0; index < variableValues.length; index++) {
const cloneState: Partial<VizPanelState> = {
$variables: new SceneVariableSet({
variables: [
new LocalValueVariable({
name: variable.state.name,
value: variableValues[index],
text: String(variableTexts[index]),
isMulti: variable.state.isMulti,
includeAll: variable.state.includeAll,
}),
],
}),
key: getCloneKey(panelToRepeat.state.key!, index),
};
const clone = panelToRepeat.clone(cloneState);
repeatedPanels.push(clone);
}
const direction = this.getRepeatDirection();
const stateChange: Partial<DashboardGridItemState> = { repeatedPanels: repeatedPanels };
const itemHeight = this.state.itemHeight ?? 10;
const prevHeight = this.state.height;
const maxPerRow = this.getMaxPerRow();
if (direction === 'h') {
const rowCount = Math.ceil(repeatedPanels.length / maxPerRow);
stateChange.height = rowCount * itemHeight;
} else {
stateChange.height = repeatedPanels.length * itemHeight;
}
this.setState(stateChange);
if (prevHeight !== this.state.height) {
const layout = sceneGraph.getLayout(this);
if (layout instanceof SceneGridLayout) {
layout.forceRender();
}
}
this._prevRepeatValues = values;
this.publishEvent(new DashboardRepeatsProcessedEvent({ source: this }), true);
}
public handleVariableName() {
if (this.state.variableName) {
if (!this._gridSizeSub) {
this._gridSizeSub = this.subscribeToState((newState, prevState) => this._handleGridResize(newState, prevState));
this._subs.add(this._gridSizeSub);
}
} else {
if (this._gridSizeSub) {
this._gridSizeSub.unsubscribe();
this._subs.remove(this._gridSizeSub);
this._gridSizeSub = undefined;
}
}
this.performRepeat();
}
public setRepeatByVariable(variableName: string | undefined) {
const stateUpdate: Partial<DashboardGridItemState> = { variableName };
if (variableName && !this.state.repeatDirection) {
stateUpdate.repeatDirection = 'h';
}
if (this.state.body.state.$variables) {
this.state.body.setState({ $variables: undefined });
}
this.setState(stateUpdate);
}
public getMaxPerRow(): number {
return this.state.maxPerRow ?? 4;
}
public setMaxPerRow(maxPerRow: number | undefined) {
this.setState({ maxPerRow });
}
public getRepeatDirection(): RepeatDirection {
return this.state.repeatDirection === 'v' ? 'v' : 'h';
}
public setRepeatDirection(repeatDirection: RepeatDirection) {
this.setState({ repeatDirection });
}
public isRepeated(): boolean {
return this.state.variableName !== undefined;
}
Scenes: Implement drag and drop support for SceneCSSGridLayout (#99386) * Draft: Move css grid stuff to main * Scenes: Implement drag and drop support for SceneCSSGridLayout * Fix some nits * WIP Refactor * Added a comment * Add orchestrator to v2schema and fix error (#100964) * Add orchestrator to v2schema and fix error * Display placeholder directly when starting to drag --------- Co-authored-by: kay delaney <kay@grafana.com> * Fix merge issue * Fix panel drag offset and remove console.logs * Fix small nit * Fix issue where layout options weren't refreshed on changing layout * Return empty array from useEditPaneOptions if dashboard body isn't LayoutOrchestrator * Expect layoutOrchestrator when serializing scene * Fix tests to expect orchestrator instead of layoutManager * Fix tests in transformSaveModelSchemaV2ToScene.test.ts * Fix tests in transformSceneToSaveModelSchemaV2.test.ts * More test fixes * fix lint issues * Small fixes * default to adding layout orchestrator? * Empty commit * delete artifactspage.go * remove artifactspage.tmpl.html * betterer * WIP refactor, not ready for review * Slightly fix placeholder behavior. still broken though * Fixed some visual glitches. Still very buggy * Fix layout bugginess when initiating dragging * more WIP * Fix some broken logic * clean up * Move LayoutOrchestrator to dashboard state * More cleanup * Fix misaligned placeholders after changing layout options or resizing browser * Fix issue with dragging vs selection * Fix scroll position jumping when dragging in vertically-oriented grid * Fix import order errors * Remove '!' from layoutOrchestrator references * Add LazyLoader support * Dynamic Dashboards: Responsive Grid drag and drop minor fixes (#102430) Changes --------- Co-authored-by: Oscar Kilhed <oscar.kilhed@grafana.com> Co-authored-by: Bogdan Matei <bogdan.matei@grafana.com>
2025-03-19 19:53:58 +08:00
public toIntermediate(): IntermediateLayoutItem {
throw new Error('Method not implemented.');
}
public distanceToPoint?(point: Point): number {
throw new Error('Method not implemented.');
}
public boundingBox?(): Rect | undefined {
throw new Error('Method not implemented.');
}
}