From ff38f24044ec3462e014d96ebb3b0ba64f300bac Mon Sep 17 00:00:00 2001 From: Nathan Marrs Date: Tue, 3 May 2022 22:58:00 -0700 Subject: [PATCH] Canvas: Refactor group to frame (#48671) --- .../components/Layers/LayerDragDropList.tsx | 6 +- public/app/features/canvas/frame.ts | 6 ++ public/app/features/canvas/group.ts | 7 --- public/app/features/canvas/index.ts | 2 +- .../app/features/canvas/runtime/element.tsx | 4 +- .../canvas/runtime/{group.tsx => frame.tsx} | 26 ++++---- public/app/features/canvas/runtime/root.tsx | 12 ++-- public/app/features/canvas/runtime/scene.tsx | 44 +++++++------- .../app/plugins/panel/canvas/CanvasPanel.tsx | 4 +- .../canvas/editor/LayerElementListEditor.tsx | 60 +++++++++---------- .../panel/canvas/editor/layerEditor.tsx | 8 +-- public/app/plugins/panel/canvas/models.gen.ts | 6 +- public/app/plugins/panel/canvas/module.tsx | 4 +- 13 files changed, 94 insertions(+), 95 deletions(-) create mode 100644 public/app/features/canvas/frame.ts delete mode 100644 public/app/features/canvas/group.ts rename public/app/features/canvas/runtime/{group.tsx => frame.tsx} (88%) diff --git a/public/app/core/components/Layers/LayerDragDropList.tsx b/public/app/core/components/Layers/LayerDragDropList.tsx index d4ceae1dc90..e3ee75bf669 100644 --- a/public/app/core/components/Layers/LayerDragDropList.tsx +++ b/public/app/core/components/Layers/LayerDragDropList.tsx @@ -16,7 +16,7 @@ type LayerDragDropListProps = { onSelect: (element: T) => any; onDelete: (element: T) => any; onDuplicate?: (element: T) => any; - isGroup?: (element: T) => boolean; + isFrame?: (element: T) => boolean; selection?: string[]; // list of unique ids (names) excludeBaseLayer?: boolean; onNameChange: (element: T, newName: string) => any; @@ -30,7 +30,7 @@ export const LayerDragDropList = ({ onSelect, onDelete, onDuplicate, - isGroup, + isFrame, selection, excludeBaseLayer, onNameChange, @@ -74,7 +74,7 @@ export const LayerDragDropList = ({ />
  {getLayerInfo(element)}
- {!isGroup!(element) && ( + {!isFrame!(element) && ( <> {onDuplicate ? ( ({ config: {}, @@ -24,16 +24,16 @@ export const groupItemDummy: CanvasElementItem = { // eslint-disable-next-line react/display-name display: () => { - return
GROUP!
; + return
FRAME!
; }, }; -export class GroupState extends ElementState { +export class FrameState extends ElementState { elements: ElementState[] = []; scene: Scene; - constructor(public options: CanvasGroupOptions, scene: Scene, public parent?: GroupState) { - super(groupItemDummy, options, parent); + constructor(public options: CanvasFrameOptions, scene: Scene, public parent?: FrameState) { + super(frameItemDummy, options, parent); this.scene = scene; @@ -44,8 +44,8 @@ export class GroupState extends ElementState { } for (const c of elements) { - if (c.type === 'group') { - this.elements.push(new GroupState(c as CanvasGroupOptions, scene, this)); + if (c.type === 'frame') { + this.elements.push(new FrameState(c as CanvasFrameOptions, scene, this)); } else { const item = canvasElementRegistry.getIfExists(c.type) ?? notFoundItem; this.elements.push(new ElementState(item, c, this)); @@ -91,8 +91,8 @@ export class GroupState extends ElementState { this.reinitializeMoveable(); break; case LayerActionID.Duplicate: - if (element.item.id === 'group') { - console.log('Can not duplicate groups (yet)', action, element); + if (element.item.id === 'frame') { + console.log('Can not duplicate frames (yet)', action, element); return; } const opts = cloneDeep(element.options); diff --git a/public/app/features/canvas/runtime/root.tsx b/public/app/features/canvas/runtime/root.tsx index 47b1339356d..1ac9acbc9e9 100644 --- a/public/app/features/canvas/runtime/root.tsx +++ b/public/app/features/canvas/runtime/root.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { CanvasGroupOptions, CanvasElementOptions } from 'app/features/canvas'; +import { CanvasFrameOptions, CanvasElementOptions } from 'app/features/canvas'; -import { GroupState } from './group'; +import { FrameState } from './frame'; import { Scene } from './scene'; -export class RootElement extends GroupState { - constructor(public options: CanvasGroupOptions, public scene: Scene, private changeCallback: () => void) { +export class RootElement extends FrameState { + constructor(public options: CanvasFrameOptions, public scene: Scene, private changeCallback: () => void) { super(options, scene); this.sizeStyle = { @@ -22,11 +22,11 @@ export class RootElement extends GroupState { // root type can not change onChange(options: CanvasElementOptions) { this.revId++; - this.options = { ...options } as CanvasGroupOptions; + this.options = { ...options } as CanvasFrameOptions; this.changeCallback(); } - getSaveModel(): CanvasGroupOptions { + getSaveModel(): CanvasFrameOptions { const { placement, constraint, ...rest } = this.options; return { diff --git a/public/app/features/canvas/runtime/scene.tsx b/public/app/features/canvas/runtime/scene.tsx index edfcb5689f2..0b951cb2bc6 100644 --- a/public/app/features/canvas/runtime/scene.tsx +++ b/public/app/features/canvas/runtime/scene.tsx @@ -8,7 +8,7 @@ import Selecto from 'selecto'; import { GrafanaTheme2, PanelData } from '@grafana/data'; import { stylesFactory } from '@grafana/ui'; import { config } from 'app/core/config'; -import { CanvasGroupOptions, DEFAULT_CANVAS_ELEMENT_CONFIG } from 'app/features/canvas'; +import { CanvasFrameOptions, DEFAULT_CANVAS_ELEMENT_CONFIG } from 'app/features/canvas'; import { ColorDimensionConfig, ResourceDimensionConfig, @@ -29,12 +29,12 @@ import { LayerActionID } from 'app/plugins/panel/canvas/types'; import { Placement } from '../types'; import { ElementState } from './element'; -import { GroupState } from './group'; +import { FrameState } from './frame'; import { RootElement } from './root'; export interface SelectionParams { targets: Array; - group?: GroupState; + frame?: FrameState; } export class Scene { @@ -53,15 +53,15 @@ export class Scene { selecto?: Selecto; moveable?: Moveable; div?: HTMLDivElement; - currentLayer?: GroupState; + currentLayer?: FrameState; isEditingEnabled?: boolean; - constructor(cfg: CanvasGroupOptions, enableEditing: boolean, public onSave: (cfg: CanvasGroupOptions) => void) { + constructor(cfg: CanvasFrameOptions, enableEditing: boolean, public onSave: (cfg: CanvasFrameOptions) => void) { this.root = this.load(cfg, enableEditing); } - getNextElementName = (isGroup = false) => { - const label = isGroup ? 'Group' : 'Element'; + getNextElementName = (isFrame = false) => { + const label = isFrame ? 'Frame' : 'Element'; let idx = this.byName.size + 1; const max = idx + 100; @@ -79,10 +79,10 @@ export class Scene { return !this.byName.has(v); }; - load(cfg: CanvasGroupOptions, enableEditing: boolean) { + load(cfg: CanvasFrameOptions, enableEditing: boolean) { this.root = new RootElement( cfg ?? { - type: 'group', + type: 'frame', elements: [DEFAULT_CANVAS_ELEMENT_CONFIG], }, this, @@ -126,13 +126,13 @@ export class Scene { } } - groupSelection() { + frameSelection() { this.selection.pipe(first()).subscribe((currentSelectedElements) => { const currentLayer = currentSelectedElements[0].parent!; - const newLayer = new GroupState( + const newLayer = new FrameState( { - type: 'group', + type: 'frame', name: this.getNextElementName(true), elements: [], }, @@ -140,18 +140,18 @@ export class Scene { currentSelectedElements[0].parent ); - const groupPlacement = this.generateGroupContainer(currentSelectedElements); + const framePlacement = this.generateFrameContainer(currentSelectedElements); - newLayer.options.placement = groupPlacement; + newLayer.options.placement = framePlacement; currentSelectedElements.forEach((element: ElementState) => { const elementContainer = element.div?.getBoundingClientRect(); - element.setPlacementFromConstraint(elementContainer, groupPlacement as DOMRect); + element.setPlacementFromConstraint(elementContainer, framePlacement as DOMRect); currentLayer.doAction(LayerActionID.Delete, element); newLayer.doAction(LayerActionID.Duplicate, element, false, false); }); - newLayer.setPlacementFromConstraint(groupPlacement as DOMRect, currentLayer.div?.getBoundingClientRect()); + newLayer.setPlacementFromConstraint(framePlacement as DOMRect, currentLayer.div?.getBoundingClientRect()); currentLayer.elements.push(newLayer); @@ -161,7 +161,7 @@ export class Scene { }); } - private generateGroupContainer = (elements: ElementState[]): Placement => { + private generateFrameContainer = (elements: ElementState[]): Placement => { let minTop = Infinity; let minLeft = Infinity; let maxRight = 0; @@ -204,7 +204,7 @@ export class Scene { this.selecto?.clickTarget(event, this.div); } - updateCurrentLayer(newLayer: GroupState) { + updateCurrentLayer(newLayer: FrameState) { this.currentLayer = newLayer; this.clearCurrentSelection(); this.save(); @@ -233,7 +233,7 @@ export class Scene { return currentElement; } - const nestedElements = currentElement instanceof GroupState ? currentElement.elements : []; + const nestedElements = currentElement instanceof FrameState ? currentElement.elements : []; for (const nestedElement of nestedElements) { stack.unshift(nestedElement); } @@ -256,8 +256,8 @@ export class Scene { private updateSelection = (selection: SelectionParams) => { this.moveable!.target = selection.targets; - if (selection.group) { - this.selection.next([selection.group]); + if (selection.frame) { + this.selection.next([selection.frame]); } else { const s = selection.targets.map((t) => this.findElementByTarget(t)!); this.selection.next(s); @@ -275,7 +275,7 @@ export class Scene { targetElements.push(currentElement.div); } - const nestedElements = currentElement instanceof GroupState ? currentElement.elements : []; + const nestedElements = currentElement instanceof FrameState ? currentElement.elements : []; for (const nestedElement of nestedElements) { stack.unshift(nestedElement); } diff --git a/public/app/plugins/panel/canvas/CanvasPanel.tsx b/public/app/plugins/panel/canvas/CanvasPanel.tsx index 9e3b741d681..6b1c52468e9 100644 --- a/public/app/plugins/panel/canvas/CanvasPanel.tsx +++ b/public/app/plugins/panel/canvas/CanvasPanel.tsx @@ -3,7 +3,7 @@ import { Subscription } from 'rxjs'; import { PanelProps } from '@grafana/data'; import { PanelContext, PanelContextRoot } from '@grafana/ui'; -import { CanvasGroupOptions } from 'app/features/canvas'; +import { CanvasFrameOptions } from 'app/features/canvas'; import { ElementState } from 'app/features/canvas/runtime/element'; import { Scene } from 'app/features/canvas/runtime/scene'; import { PanelEditEnteredEvent, PanelEditExitedEvent } from 'app/types/events'; @@ -85,7 +85,7 @@ export class CanvasPanel extends Component { // NOTE, all changes to the scene flow through this function // even the editor gets current state from the same scene instance! - onUpdateScene = (root: CanvasGroupOptions) => { + onUpdateScene = (root: CanvasFrameOptions) => { const { onOptionsChange, options } = this.props; onOptionsChange({ ...options, diff --git a/public/app/plugins/panel/canvas/editor/LayerElementListEditor.tsx b/public/app/plugins/panel/canvas/editor/LayerElementListEditor.tsx index 809495898b5..36ed1cc2943 100644 --- a/public/app/plugins/panel/canvas/editor/LayerElementListEditor.tsx +++ b/public/app/plugins/panel/canvas/editor/LayerElementListEditor.tsx @@ -9,7 +9,7 @@ import { LayerDragDropList } from 'app/core/components/Layers/LayerDragDropList' import { CanvasElementOptions, canvasElementRegistry } from 'app/features/canvas'; import { notFoundItem } from 'app/features/canvas/elements/notFound'; import { ElementState } from 'app/features/canvas/runtime/element'; -import { GroupState } from 'app/features/canvas/runtime/group'; +import { FrameState } from 'app/features/canvas/runtime/frame'; import { SelectionParams } from 'app/features/canvas/runtime/scene'; import { ShowConfirmModalEvent } from 'app/types/events'; @@ -53,11 +53,11 @@ export class LayerElementListEditor extends PureComponent { if (settings?.scene) { try { let selection: SelectionParams = { targets: [] }; - if (item instanceof GroupState) { + if (item instanceof FrameState) { const targetElements: HTMLDivElement[] = []; targetElements.push(item?.div!); selection.targets = targetElements; - selection.group = item; + selection.frame = item; settings.scene.select(selection); } else if (item instanceof ElementState) { const targetElement = [item?.div!]; @@ -115,7 +115,7 @@ export class LayerElementListEditor extends PureComponent { } }; - private decoupleGroup = () => { + private decoupleFrame = () => { const settings = this.props.item.settings; if (!settings?.layer) { @@ -124,7 +124,7 @@ export class LayerElementListEditor extends PureComponent { const { layer } = settings; - this.deleteGroup(); + this.deleteFrame(); layer.elements.forEach((element: ElementState) => { const elementContainer = element.div?.getBoundingClientRect(); element.setPlacementFromConstraint(elementContainer, layer.parent?.div?.getBoundingClientRect()); @@ -132,22 +132,22 @@ export class LayerElementListEditor extends PureComponent { }); }; - private onDecoupleGroup = () => { + private onDecoupleFrame = () => { appEvents.publish( new ShowConfirmModalEvent({ - title: 'Decouple group', - text: `Are you sure you want to decouple this group?`, - text2: 'This will remove the group and push nested elements in the next level up.', + title: 'Decouple frame', + text: `Are you sure you want to decouple this frame?`, + text2: 'This will remove the frame and push nested elements in the next level up.', confirmText: 'Yes', yesText: 'Decouple', onConfirm: async () => { - this.decoupleGroup(); + this.decoupleFrame(); }, }) ); }; - private deleteGroup = () => { + private deleteFrame = () => { const settings = this.props.item.settings; if (!settings?.layer) { @@ -164,26 +164,26 @@ export class LayerElementListEditor extends PureComponent { this.goUpLayer(); }; - private onGroupSelection = () => { + private onFrameSelection = () => { const scene = this.getScene(); if (scene) { - scene.groupSelection(); + scene.frameSelection(); } else { console.warn('no scene!'); } }; - private onDeleteGroup = () => { + private onDeleteFrame = () => { appEvents.publish( new ShowConfirmModalEvent({ - title: 'Delete group', - text: `Are you sure you want to delete this group?`, - text2: 'This will delete the group and all nested elements.', + title: 'Delete frame', + text: `Are you sure you want to delete this frame?`, + text2: 'This will delete the frame and all nested elements.', icon: 'trash-alt', confirmText: 'Delete', yesText: 'Delete', onConfirm: async () => { - this.deleteGroup(); + this.deleteFrame(); }, }) ); @@ -215,8 +215,8 @@ export class LayerElementListEditor extends PureComponent { element.onChange({ ...element.options, name }); }; - const isGroup = (element: ElementState) => { - return element instanceof GroupState; + const isFrame = (element: ElementState) => { + return element instanceof FrameState; }; const verifyLayerNameUniqueness = (nameToVerify: string) => { @@ -231,16 +231,16 @@ export class LayerElementListEditor extends PureComponent { {!layer.isRoot() && ( <> - - )} @@ -252,7 +252,7 @@ export class LayerElementListEditor extends PureComponent { getLayerInfo={getLayerInfo} onNameChange={onNameChange} verifyLayerNameUniqueness={verifyLayerNameUniqueness} - isGroup={isGroup} + isFrame={isFrame} layers={layer.elements} selection={selection} /> @@ -266,12 +266,12 @@ export class LayerElementListEditor extends PureComponent { /> {selection.length > 0 && ( )} {selection.length > 1 && ( - )} diff --git a/public/app/plugins/panel/canvas/editor/layerEditor.tsx b/public/app/plugins/panel/canvas/editor/layerEditor.tsx index dcf2ba29abe..cb5891f2fd3 100644 --- a/public/app/plugins/panel/canvas/editor/layerEditor.tsx +++ b/public/app/plugins/panel/canvas/editor/layerEditor.tsx @@ -2,7 +2,7 @@ import { get as lodashGet } from 'lodash'; import { NestedPanelOptions, NestedValueAccess } from '@grafana/data/src/utils/OptionsUIBuilders'; import { ElementState } from 'app/features/canvas/runtime/element'; -import { GroupState } from 'app/features/canvas/runtime/group'; +import { FrameState } from 'app/features/canvas/runtime/frame'; import { Scene } from 'app/features/canvas/runtime/scene'; import { setOptionImmutably } from 'app/features/dashboard/components/PanelEditor/utils'; @@ -14,7 +14,7 @@ import { optionBuilder } from './options'; export interface LayerEditorProps { scene: Scene; - layer: GroupState; + layer: FrameState; selected: ElementState[]; } @@ -22,12 +22,12 @@ export function getLayerEditor(opts: InstanceState): NestedPanelOptions(CanvasPanel) const selection = state.selected; if (selection?.length === 1) { const element = selection[0]; - if (!(element instanceof GroupState)) { + if (!(element instanceof FrameState)) { builder.addNestedOptions( getElementEditor({ category: [`Selected element (${element.options.name})`],