mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Canvas: Refactor group to frame (#48671)
This commit is contained in:
parent
0d60b1ce0a
commit
ff38f24044
@ -16,7 +16,7 @@ type LayerDragDropListProps<T extends LayerElement> = {
|
|||||||
onSelect: (element: T) => any;
|
onSelect: (element: T) => any;
|
||||||
onDelete: (element: T) => any;
|
onDelete: (element: T) => any;
|
||||||
onDuplicate?: (element: T) => any;
|
onDuplicate?: (element: T) => any;
|
||||||
isGroup?: (element: T) => boolean;
|
isFrame?: (element: T) => boolean;
|
||||||
selection?: string[]; // list of unique ids (names)
|
selection?: string[]; // list of unique ids (names)
|
||||||
excludeBaseLayer?: boolean;
|
excludeBaseLayer?: boolean;
|
||||||
onNameChange: (element: T, newName: string) => any;
|
onNameChange: (element: T, newName: string) => any;
|
||||||
@ -30,7 +30,7 @@ export const LayerDragDropList = <T extends LayerElement>({
|
|||||||
onSelect,
|
onSelect,
|
||||||
onDelete,
|
onDelete,
|
||||||
onDuplicate,
|
onDuplicate,
|
||||||
isGroup,
|
isFrame,
|
||||||
selection,
|
selection,
|
||||||
excludeBaseLayer,
|
excludeBaseLayer,
|
||||||
onNameChange,
|
onNameChange,
|
||||||
@ -74,7 +74,7 @@ export const LayerDragDropList = <T extends LayerElement>({
|
|||||||
/>
|
/>
|
||||||
<div className={style.textWrapper}> {getLayerInfo(element)}</div>
|
<div className={style.textWrapper}> {getLayerInfo(element)}</div>
|
||||||
|
|
||||||
{!isGroup!(element) && (
|
{!isFrame!(element) && (
|
||||||
<>
|
<>
|
||||||
{onDuplicate ? (
|
{onDuplicate ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
6
public/app/features/canvas/frame.ts
Normal file
6
public/app/features/canvas/frame.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { CanvasElementOptions } from './element';
|
||||||
|
|
||||||
|
export interface CanvasFrameOptions extends CanvasElementOptions {
|
||||||
|
type: 'frame';
|
||||||
|
elements: CanvasElementOptions[];
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
import { CanvasElementOptions } from './element';
|
|
||||||
|
|
||||||
export interface CanvasGroupOptions extends CanvasElementOptions {
|
|
||||||
type: 'group';
|
|
||||||
elements: CanvasElementOptions[];
|
|
||||||
// layout? // absolute, list, grid?
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
export * from './types';
|
export * from './types';
|
||||||
export * from './element';
|
export * from './element';
|
||||||
export { CanvasGroupOptions } from './group';
|
export { CanvasFrameOptions } from './frame';
|
||||||
export * from './registry';
|
export * from './registry';
|
||||||
|
@ -13,7 +13,7 @@ import { DimensionContext } from 'app/features/dimensions';
|
|||||||
|
|
||||||
import { HorizontalConstraint, Placement, VerticalConstraint } from '../types';
|
import { HorizontalConstraint, Placement, VerticalConstraint } from '../types';
|
||||||
|
|
||||||
import { GroupState } from './group';
|
import { FrameState } from './frame';
|
||||||
import { RootElement } from './root';
|
import { RootElement } from './root';
|
||||||
import { Scene } from './scene';
|
import { Scene } from './scene';
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ export class ElementState implements LayerElement {
|
|||||||
// Calculated
|
// Calculated
|
||||||
data?: any; // depends on the type
|
data?: any; // depends on the type
|
||||||
|
|
||||||
constructor(public item: CanvasElementItem, public options: CanvasElementOptions, public parent?: GroupState) {
|
constructor(public item: CanvasElementItem, public options: CanvasElementOptions, public parent?: FrameState) {
|
||||||
const fallbackName = `Element ${Date.now()}`;
|
const fallbackName = `Element ${Date.now()}`;
|
||||||
if (!options) {
|
if (!options) {
|
||||||
this.options = { type: item.id, name: fallbackName };
|
this.options = { type: item.id, name: fallbackName };
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { CanvasGroupOptions, canvasElementRegistry } from 'app/features/canvas';
|
import { CanvasFrameOptions, canvasElementRegistry } from 'app/features/canvas';
|
||||||
import { notFoundItem } from 'app/features/canvas/elements/notFound';
|
import { notFoundItem } from 'app/features/canvas/elements/notFound';
|
||||||
import { DimensionContext } from 'app/features/dimensions';
|
import { DimensionContext } from 'app/features/dimensions';
|
||||||
import { LayerActionID } from 'app/plugins/panel/canvas/types';
|
import { LayerActionID } from 'app/plugins/panel/canvas/types';
|
||||||
@ -13,10 +13,10 @@ import { ElementState } from './element';
|
|||||||
import { RootElement } from './root';
|
import { RootElement } from './root';
|
||||||
import { Scene } from './scene';
|
import { Scene } from './scene';
|
||||||
|
|
||||||
export const groupItemDummy: CanvasElementItem = {
|
export const frameItemDummy: CanvasElementItem = {
|
||||||
id: 'group',
|
id: 'frame',
|
||||||
name: 'Group',
|
name: 'Frame',
|
||||||
description: 'Group',
|
description: 'Frame',
|
||||||
|
|
||||||
getNewOptions: () => ({
|
getNewOptions: () => ({
|
||||||
config: {},
|
config: {},
|
||||||
@ -24,16 +24,16 @@ export const groupItemDummy: CanvasElementItem = {
|
|||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
display: () => {
|
display: () => {
|
||||||
return <div>GROUP!</div>;
|
return <div>FRAME!</div>;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export class GroupState extends ElementState {
|
export class FrameState extends ElementState {
|
||||||
elements: ElementState[] = [];
|
elements: ElementState[] = [];
|
||||||
scene: Scene;
|
scene: Scene;
|
||||||
|
|
||||||
constructor(public options: CanvasGroupOptions, scene: Scene, public parent?: GroupState) {
|
constructor(public options: CanvasFrameOptions, scene: Scene, public parent?: FrameState) {
|
||||||
super(groupItemDummy, options, parent);
|
super(frameItemDummy, options, parent);
|
||||||
|
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ export class GroupState extends ElementState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const c of elements) {
|
for (const c of elements) {
|
||||||
if (c.type === 'group') {
|
if (c.type === 'frame') {
|
||||||
this.elements.push(new GroupState(c as CanvasGroupOptions, scene, this));
|
this.elements.push(new FrameState(c as CanvasFrameOptions, scene, this));
|
||||||
} else {
|
} else {
|
||||||
const item = canvasElementRegistry.getIfExists(c.type) ?? notFoundItem;
|
const item = canvasElementRegistry.getIfExists(c.type) ?? notFoundItem;
|
||||||
this.elements.push(new ElementState(item, c, this));
|
this.elements.push(new ElementState(item, c, this));
|
||||||
@ -91,8 +91,8 @@ export class GroupState extends ElementState {
|
|||||||
this.reinitializeMoveable();
|
this.reinitializeMoveable();
|
||||||
break;
|
break;
|
||||||
case LayerActionID.Duplicate:
|
case LayerActionID.Duplicate:
|
||||||
if (element.item.id === 'group') {
|
if (element.item.id === 'frame') {
|
||||||
console.log('Can not duplicate groups (yet)', action, element);
|
console.log('Can not duplicate frames (yet)', action, element);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const opts = cloneDeep(element.options);
|
const opts = cloneDeep(element.options);
|
@ -1,12 +1,12 @@
|
|||||||
import React from 'react';
|
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';
|
import { Scene } from './scene';
|
||||||
|
|
||||||
export class RootElement extends GroupState {
|
export class RootElement extends FrameState {
|
||||||
constructor(public options: CanvasGroupOptions, public scene: Scene, private changeCallback: () => void) {
|
constructor(public options: CanvasFrameOptions, public scene: Scene, private changeCallback: () => void) {
|
||||||
super(options, scene);
|
super(options, scene);
|
||||||
|
|
||||||
this.sizeStyle = {
|
this.sizeStyle = {
|
||||||
@ -22,11 +22,11 @@ export class RootElement extends GroupState {
|
|||||||
// root type can not change
|
// root type can not change
|
||||||
onChange(options: CanvasElementOptions) {
|
onChange(options: CanvasElementOptions) {
|
||||||
this.revId++;
|
this.revId++;
|
||||||
this.options = { ...options } as CanvasGroupOptions;
|
this.options = { ...options } as CanvasFrameOptions;
|
||||||
this.changeCallback();
|
this.changeCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSaveModel(): CanvasGroupOptions {
|
getSaveModel(): CanvasFrameOptions {
|
||||||
const { placement, constraint, ...rest } = this.options;
|
const { placement, constraint, ...rest } = this.options;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -8,7 +8,7 @@ import Selecto from 'selecto';
|
|||||||
import { GrafanaTheme2, PanelData } from '@grafana/data';
|
import { GrafanaTheme2, PanelData } from '@grafana/data';
|
||||||
import { stylesFactory } from '@grafana/ui';
|
import { stylesFactory } from '@grafana/ui';
|
||||||
import { config } from 'app/core/config';
|
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 {
|
import {
|
||||||
ColorDimensionConfig,
|
ColorDimensionConfig,
|
||||||
ResourceDimensionConfig,
|
ResourceDimensionConfig,
|
||||||
@ -29,12 +29,12 @@ import { LayerActionID } from 'app/plugins/panel/canvas/types';
|
|||||||
import { Placement } from '../types';
|
import { Placement } from '../types';
|
||||||
|
|
||||||
import { ElementState } from './element';
|
import { ElementState } from './element';
|
||||||
import { GroupState } from './group';
|
import { FrameState } from './frame';
|
||||||
import { RootElement } from './root';
|
import { RootElement } from './root';
|
||||||
|
|
||||||
export interface SelectionParams {
|
export interface SelectionParams {
|
||||||
targets: Array<HTMLElement | SVGElement>;
|
targets: Array<HTMLElement | SVGElement>;
|
||||||
group?: GroupState;
|
frame?: FrameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Scene {
|
export class Scene {
|
||||||
@ -53,15 +53,15 @@ export class Scene {
|
|||||||
selecto?: Selecto;
|
selecto?: Selecto;
|
||||||
moveable?: Moveable;
|
moveable?: Moveable;
|
||||||
div?: HTMLDivElement;
|
div?: HTMLDivElement;
|
||||||
currentLayer?: GroupState;
|
currentLayer?: FrameState;
|
||||||
isEditingEnabled?: boolean;
|
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);
|
this.root = this.load(cfg, enableEditing);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNextElementName = (isGroup = false) => {
|
getNextElementName = (isFrame = false) => {
|
||||||
const label = isGroup ? 'Group' : 'Element';
|
const label = isFrame ? 'Frame' : 'Element';
|
||||||
let idx = this.byName.size + 1;
|
let idx = this.byName.size + 1;
|
||||||
|
|
||||||
const max = idx + 100;
|
const max = idx + 100;
|
||||||
@ -79,10 +79,10 @@ export class Scene {
|
|||||||
return !this.byName.has(v);
|
return !this.byName.has(v);
|
||||||
};
|
};
|
||||||
|
|
||||||
load(cfg: CanvasGroupOptions, enableEditing: boolean) {
|
load(cfg: CanvasFrameOptions, enableEditing: boolean) {
|
||||||
this.root = new RootElement(
|
this.root = new RootElement(
|
||||||
cfg ?? {
|
cfg ?? {
|
||||||
type: 'group',
|
type: 'frame',
|
||||||
elements: [DEFAULT_CANVAS_ELEMENT_CONFIG],
|
elements: [DEFAULT_CANVAS_ELEMENT_CONFIG],
|
||||||
},
|
},
|
||||||
this,
|
this,
|
||||||
@ -126,13 +126,13 @@ export class Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
groupSelection() {
|
frameSelection() {
|
||||||
this.selection.pipe(first()).subscribe((currentSelectedElements) => {
|
this.selection.pipe(first()).subscribe((currentSelectedElements) => {
|
||||||
const currentLayer = currentSelectedElements[0].parent!;
|
const currentLayer = currentSelectedElements[0].parent!;
|
||||||
|
|
||||||
const newLayer = new GroupState(
|
const newLayer = new FrameState(
|
||||||
{
|
{
|
||||||
type: 'group',
|
type: 'frame',
|
||||||
name: this.getNextElementName(true),
|
name: this.getNextElementName(true),
|
||||||
elements: [],
|
elements: [],
|
||||||
},
|
},
|
||||||
@ -140,18 +140,18 @@ export class Scene {
|
|||||||
currentSelectedElements[0].parent
|
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) => {
|
currentSelectedElements.forEach((element: ElementState) => {
|
||||||
const elementContainer = element.div?.getBoundingClientRect();
|
const elementContainer = element.div?.getBoundingClientRect();
|
||||||
element.setPlacementFromConstraint(elementContainer, groupPlacement as DOMRect);
|
element.setPlacementFromConstraint(elementContainer, framePlacement as DOMRect);
|
||||||
currentLayer.doAction(LayerActionID.Delete, element);
|
currentLayer.doAction(LayerActionID.Delete, element);
|
||||||
newLayer.doAction(LayerActionID.Duplicate, element, false, false);
|
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);
|
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 minTop = Infinity;
|
||||||
let minLeft = Infinity;
|
let minLeft = Infinity;
|
||||||
let maxRight = 0;
|
let maxRight = 0;
|
||||||
@ -204,7 +204,7 @@ export class Scene {
|
|||||||
this.selecto?.clickTarget(event, this.div);
|
this.selecto?.clickTarget(event, this.div);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCurrentLayer(newLayer: GroupState) {
|
updateCurrentLayer(newLayer: FrameState) {
|
||||||
this.currentLayer = newLayer;
|
this.currentLayer = newLayer;
|
||||||
this.clearCurrentSelection();
|
this.clearCurrentSelection();
|
||||||
this.save();
|
this.save();
|
||||||
@ -233,7 +233,7 @@ export class Scene {
|
|||||||
return currentElement;
|
return currentElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nestedElements = currentElement instanceof GroupState ? currentElement.elements : [];
|
const nestedElements = currentElement instanceof FrameState ? currentElement.elements : [];
|
||||||
for (const nestedElement of nestedElements) {
|
for (const nestedElement of nestedElements) {
|
||||||
stack.unshift(nestedElement);
|
stack.unshift(nestedElement);
|
||||||
}
|
}
|
||||||
@ -256,8 +256,8 @@ export class Scene {
|
|||||||
private updateSelection = (selection: SelectionParams) => {
|
private updateSelection = (selection: SelectionParams) => {
|
||||||
this.moveable!.target = selection.targets;
|
this.moveable!.target = selection.targets;
|
||||||
|
|
||||||
if (selection.group) {
|
if (selection.frame) {
|
||||||
this.selection.next([selection.group]);
|
this.selection.next([selection.frame]);
|
||||||
} else {
|
} else {
|
||||||
const s = selection.targets.map((t) => this.findElementByTarget(t)!);
|
const s = selection.targets.map((t) => this.findElementByTarget(t)!);
|
||||||
this.selection.next(s);
|
this.selection.next(s);
|
||||||
@ -275,7 +275,7 @@ export class Scene {
|
|||||||
targetElements.push(currentElement.div);
|
targetElements.push(currentElement.div);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nestedElements = currentElement instanceof GroupState ? currentElement.elements : [];
|
const nestedElements = currentElement instanceof FrameState ? currentElement.elements : [];
|
||||||
for (const nestedElement of nestedElements) {
|
for (const nestedElement of nestedElements) {
|
||||||
stack.unshift(nestedElement);
|
stack.unshift(nestedElement);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { Subscription } from 'rxjs';
|
|||||||
|
|
||||||
import { PanelProps } from '@grafana/data';
|
import { PanelProps } from '@grafana/data';
|
||||||
import { PanelContext, PanelContextRoot } from '@grafana/ui';
|
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 { ElementState } from 'app/features/canvas/runtime/element';
|
||||||
import { Scene } from 'app/features/canvas/runtime/scene';
|
import { Scene } from 'app/features/canvas/runtime/scene';
|
||||||
import { PanelEditEnteredEvent, PanelEditExitedEvent } from 'app/types/events';
|
import { PanelEditEnteredEvent, PanelEditExitedEvent } from 'app/types/events';
|
||||||
@ -85,7 +85,7 @@ export class CanvasPanel extends Component<Props, State> {
|
|||||||
|
|
||||||
// NOTE, all changes to the scene flow through this function
|
// NOTE, all changes to the scene flow through this function
|
||||||
// even the editor gets current state from the same scene instance!
|
// even the editor gets current state from the same scene instance!
|
||||||
onUpdateScene = (root: CanvasGroupOptions) => {
|
onUpdateScene = (root: CanvasFrameOptions) => {
|
||||||
const { onOptionsChange, options } = this.props;
|
const { onOptionsChange, options } = this.props;
|
||||||
onOptionsChange({
|
onOptionsChange({
|
||||||
...options,
|
...options,
|
||||||
|
@ -9,7 +9,7 @@ import { LayerDragDropList } from 'app/core/components/Layers/LayerDragDropList'
|
|||||||
import { CanvasElementOptions, canvasElementRegistry } from 'app/features/canvas';
|
import { CanvasElementOptions, canvasElementRegistry } from 'app/features/canvas';
|
||||||
import { notFoundItem } from 'app/features/canvas/elements/notFound';
|
import { notFoundItem } from 'app/features/canvas/elements/notFound';
|
||||||
import { ElementState } from 'app/features/canvas/runtime/element';
|
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 { SelectionParams } from 'app/features/canvas/runtime/scene';
|
||||||
import { ShowConfirmModalEvent } from 'app/types/events';
|
import { ShowConfirmModalEvent } from 'app/types/events';
|
||||||
|
|
||||||
@ -53,11 +53,11 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
if (settings?.scene) {
|
if (settings?.scene) {
|
||||||
try {
|
try {
|
||||||
let selection: SelectionParams = { targets: [] };
|
let selection: SelectionParams = { targets: [] };
|
||||||
if (item instanceof GroupState) {
|
if (item instanceof FrameState) {
|
||||||
const targetElements: HTMLDivElement[] = [];
|
const targetElements: HTMLDivElement[] = [];
|
||||||
targetElements.push(item?.div!);
|
targetElements.push(item?.div!);
|
||||||
selection.targets = targetElements;
|
selection.targets = targetElements;
|
||||||
selection.group = item;
|
selection.frame = item;
|
||||||
settings.scene.select(selection);
|
settings.scene.select(selection);
|
||||||
} else if (item instanceof ElementState) {
|
} else if (item instanceof ElementState) {
|
||||||
const targetElement = [item?.div!];
|
const targetElement = [item?.div!];
|
||||||
@ -115,7 +115,7 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private decoupleGroup = () => {
|
private decoupleFrame = () => {
|
||||||
const settings = this.props.item.settings;
|
const settings = this.props.item.settings;
|
||||||
|
|
||||||
if (!settings?.layer) {
|
if (!settings?.layer) {
|
||||||
@ -124,7 +124,7 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
|
|
||||||
const { layer } = settings;
|
const { layer } = settings;
|
||||||
|
|
||||||
this.deleteGroup();
|
this.deleteFrame();
|
||||||
layer.elements.forEach((element: ElementState) => {
|
layer.elements.forEach((element: ElementState) => {
|
||||||
const elementContainer = element.div?.getBoundingClientRect();
|
const elementContainer = element.div?.getBoundingClientRect();
|
||||||
element.setPlacementFromConstraint(elementContainer, layer.parent?.div?.getBoundingClientRect());
|
element.setPlacementFromConstraint(elementContainer, layer.parent?.div?.getBoundingClientRect());
|
||||||
@ -132,22 +132,22 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDecoupleGroup = () => {
|
private onDecoupleFrame = () => {
|
||||||
appEvents.publish(
|
appEvents.publish(
|
||||||
new ShowConfirmModalEvent({
|
new ShowConfirmModalEvent({
|
||||||
title: 'Decouple group',
|
title: 'Decouple frame',
|
||||||
text: `Are you sure you want to decouple this group?`,
|
text: `Are you sure you want to decouple this frame?`,
|
||||||
text2: 'This will remove the group and push nested elements in the next level up.',
|
text2: 'This will remove the frame and push nested elements in the next level up.',
|
||||||
confirmText: 'Yes',
|
confirmText: 'Yes',
|
||||||
yesText: 'Decouple',
|
yesText: 'Decouple',
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
this.decoupleGroup();
|
this.decoupleFrame();
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private deleteGroup = () => {
|
private deleteFrame = () => {
|
||||||
const settings = this.props.item.settings;
|
const settings = this.props.item.settings;
|
||||||
|
|
||||||
if (!settings?.layer) {
|
if (!settings?.layer) {
|
||||||
@ -164,26 +164,26 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
this.goUpLayer();
|
this.goUpLayer();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onGroupSelection = () => {
|
private onFrameSelection = () => {
|
||||||
const scene = this.getScene();
|
const scene = this.getScene();
|
||||||
if (scene) {
|
if (scene) {
|
||||||
scene.groupSelection();
|
scene.frameSelection();
|
||||||
} else {
|
} else {
|
||||||
console.warn('no scene!');
|
console.warn('no scene!');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDeleteGroup = () => {
|
private onDeleteFrame = () => {
|
||||||
appEvents.publish(
|
appEvents.publish(
|
||||||
new ShowConfirmModalEvent({
|
new ShowConfirmModalEvent({
|
||||||
title: 'Delete group',
|
title: 'Delete frame',
|
||||||
text: `Are you sure you want to delete this group?`,
|
text: `Are you sure you want to delete this frame?`,
|
||||||
text2: 'This will delete the group and all nested elements.',
|
text2: 'This will delete the frame and all nested elements.',
|
||||||
icon: 'trash-alt',
|
icon: 'trash-alt',
|
||||||
confirmText: 'Delete',
|
confirmText: 'Delete',
|
||||||
yesText: 'Delete',
|
yesText: 'Delete',
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
this.deleteGroup();
|
this.deleteFrame();
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -215,8 +215,8 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
element.onChange({ ...element.options, name });
|
element.onChange({ ...element.options, name });
|
||||||
};
|
};
|
||||||
|
|
||||||
const isGroup = (element: ElementState) => {
|
const isFrame = (element: ElementState) => {
|
||||||
return element instanceof GroupState;
|
return element instanceof FrameState;
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifyLayerNameUniqueness = (nameToVerify: string) => {
|
const verifyLayerNameUniqueness = (nameToVerify: string) => {
|
||||||
@ -231,16 +231,16 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
{!layer.isRoot() && (
|
{!layer.isRoot() && (
|
||||||
<>
|
<>
|
||||||
<Button icon="angle-up" size="sm" variant="secondary" onClick={this.goUpLayer}>
|
<Button icon="angle-up" size="sm" variant="secondary" onClick={this.goUpLayer}>
|
||||||
Go Up Level
|
Go up level
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="secondary" onClick={() => this.onSelect(layer)}>
|
<Button size="sm" variant="secondary" onClick={() => this.onSelect(layer)}>
|
||||||
Select Group
|
Select frame
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="secondary" onClick={() => this.onDecoupleGroup()}>
|
<Button size="sm" variant="secondary" onClick={() => this.onDecoupleFrame()}>
|
||||||
Decouple Group
|
Decouple frame
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="secondary" onClick={() => this.onDeleteGroup()}>
|
<Button size="sm" variant="secondary" onClick={() => this.onDeleteFrame()}>
|
||||||
Delete Group
|
Delete frame
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -252,7 +252,7 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
getLayerInfo={getLayerInfo}
|
getLayerInfo={getLayerInfo}
|
||||||
onNameChange={onNameChange}
|
onNameChange={onNameChange}
|
||||||
verifyLayerNameUniqueness={verifyLayerNameUniqueness}
|
verifyLayerNameUniqueness={verifyLayerNameUniqueness}
|
||||||
isGroup={isGroup}
|
isFrame={isFrame}
|
||||||
layers={layer.elements}
|
layers={layer.elements}
|
||||||
selection={selection}
|
selection={selection}
|
||||||
/>
|
/>
|
||||||
@ -266,12 +266,12 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
|||||||
/>
|
/>
|
||||||
{selection.length > 0 && (
|
{selection.length > 0 && (
|
||||||
<Button size="sm" variant="secondary" onClick={this.onClearSelection}>
|
<Button size="sm" variant="secondary" onClick={this.onClearSelection}>
|
||||||
Clear Selection
|
Clear selection
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{selection.length > 1 && (
|
{selection.length > 1 && (
|
||||||
<Button size="sm" variant="secondary" onClick={this.onGroupSelection}>
|
<Button size="sm" variant="secondary" onClick={this.onFrameSelection}>
|
||||||
Group items
|
Frame selection
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</HorizontalGroup>
|
</HorizontalGroup>
|
||||||
|
@ -2,7 +2,7 @@ import { get as lodashGet } from 'lodash';
|
|||||||
|
|
||||||
import { NestedPanelOptions, NestedValueAccess } from '@grafana/data/src/utils/OptionsUIBuilders';
|
import { NestedPanelOptions, NestedValueAccess } from '@grafana/data/src/utils/OptionsUIBuilders';
|
||||||
import { ElementState } from 'app/features/canvas/runtime/element';
|
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 { Scene } from 'app/features/canvas/runtime/scene';
|
||||||
import { setOptionImmutably } from 'app/features/dashboard/components/PanelEditor/utils';
|
import { setOptionImmutably } from 'app/features/dashboard/components/PanelEditor/utils';
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ import { optionBuilder } from './options';
|
|||||||
|
|
||||||
export interface LayerEditorProps {
|
export interface LayerEditorProps {
|
||||||
scene: Scene;
|
scene: Scene;
|
||||||
layer: GroupState;
|
layer: FrameState;
|
||||||
selected: ElementState[];
|
selected: ElementState[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,12 +22,12 @@ export function getLayerEditor(opts: InstanceState): NestedPanelOptions<LayerEdi
|
|||||||
const { selected, scene } = opts;
|
const { selected, scene } = opts;
|
||||||
|
|
||||||
if (!scene.currentLayer) {
|
if (!scene.currentLayer) {
|
||||||
scene.currentLayer = scene.root as GroupState;
|
scene.currentLayer = scene.root as FrameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
for (const element of selected) {
|
for (const element of selected) {
|
||||||
if (element instanceof GroupState) {
|
if (element instanceof FrameState) {
|
||||||
scene.currentLayer = element;
|
scene.currentLayer = element;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
// It is currenty hand written but will serve as the target for cuetsy
|
// It is currenty hand written but will serve as the target for cuetsy
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
import { CanvasGroupOptions, DEFAULT_CANVAS_ELEMENT_CONFIG } from 'app/features/canvas';
|
import { CanvasFrameOptions, DEFAULT_CANVAS_ELEMENT_CONFIG } from 'app/features/canvas';
|
||||||
|
|
||||||
export const modelVersion = Object.freeze([1, 0]);
|
export const modelVersion = Object.freeze([1, 0]);
|
||||||
|
|
||||||
export interface PanelOptions {
|
export interface PanelOptions {
|
||||||
inlineEditing: boolean;
|
inlineEditing: boolean;
|
||||||
root: CanvasGroupOptions;
|
root: CanvasFrameOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultPanelOptions: PanelOptions = {
|
export const defaultPanelOptions: PanelOptions = {
|
||||||
@ -20,5 +20,5 @@ export const defaultPanelOptions: PanelOptions = {
|
|||||||
...DEFAULT_CANVAS_ELEMENT_CONFIG,
|
...DEFAULT_CANVAS_ELEMENT_CONFIG,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as unknown as CanvasGroupOptions,
|
} as unknown as CanvasFrameOptions,
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { GroupState } from 'app/features/canvas/runtime/group';
|
import { FrameState } from 'app/features/canvas/runtime/frame';
|
||||||
|
|
||||||
import { CanvasPanel, InstanceState } from './CanvasPanel';
|
import { CanvasPanel, InstanceState } from './CanvasPanel';
|
||||||
import { getElementEditor } from './editor/elementEditor';
|
import { getElementEditor } from './editor/elementEditor';
|
||||||
@ -25,7 +25,7 @@ export const plugin = new PanelPlugin<PanelOptions>(CanvasPanel)
|
|||||||
const selection = state.selected;
|
const selection = state.selected;
|
||||||
if (selection?.length === 1) {
|
if (selection?.length === 1) {
|
||||||
const element = selection[0];
|
const element = selection[0];
|
||||||
if (!(element instanceof GroupState)) {
|
if (!(element instanceof FrameState)) {
|
||||||
builder.addNestedOptions(
|
builder.addNestedOptions(
|
||||||
getElementEditor({
|
getElementEditor({
|
||||||
category: [`Selected element (${element.options.name})`],
|
category: [`Selected element (${element.options.name})`],
|
||||||
|
Loading…
Reference in New Issue
Block a user