mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Canvas: use name as UID (#42696)
This commit is contained in:
parent
a345b06095
commit
2af5ad4764
@ -50,6 +50,7 @@ export const LayerDragDropList = <T extends LayerElement>({
|
||||
// reverse order
|
||||
const rows: any = [];
|
||||
const lastLayerIndex = excludeBaseLayer ? 1 : 0;
|
||||
const shouldRenderDragIconLengthThreshold = excludeBaseLayer ? 2 : 1;
|
||||
for (let i = layers.length - 1; i >= lastLayerIndex; i--) {
|
||||
const element = layers[i];
|
||||
const uid = element.getName();
|
||||
@ -91,7 +92,7 @@ export const LayerDragDropList = <T extends LayerElement>({
|
||||
onClick={() => onDelete(element)}
|
||||
surface="header"
|
||||
/>
|
||||
{layers.length > 2 && (
|
||||
{layers.length > shouldRenderDragIconLengthThreshold && (
|
||||
<Icon
|
||||
title="Drag and drop to reorder"
|
||||
name="draggabledots"
|
||||
|
@ -40,7 +40,7 @@ export const LayerName = ({ name, onChange, verifyLayerNameUniqueness }: LayerNa
|
||||
return;
|
||||
}
|
||||
|
||||
if (verifyLayerNameUniqueness && !verifyLayerNameUniqueness(newName)) {
|
||||
if (verifyLayerNameUniqueness && !verifyLayerNameUniqueness(newName) && newName !== name) {
|
||||
setValidationError('Layer name already exists');
|
||||
return;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { textBoxItem } from './elements/textBox';
|
||||
export const DEFAULT_CANVAS_ELEMENT_CONFIG: CanvasElementOptions = {
|
||||
...iconItem.getNewOptions(),
|
||||
type: iconItem.id,
|
||||
name: `Group ${Date.now()}.${Math.floor(Math.random() * 100)}`,
|
||||
name: `Element 1`,
|
||||
};
|
||||
|
||||
export const canvasElementRegistry = new Registry<CanvasElementItem>(() => [
|
||||
|
@ -13,12 +13,13 @@ import { DimensionContext } from 'app/features/dimensions';
|
||||
import { notFoundItem } from 'app/features/canvas/elements/notFound';
|
||||
import { GroupState } from './group';
|
||||
import { LayerElement } from 'app/core/components/Layers/types';
|
||||
import { Scene } from './scene';
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export class ElementState implements LayerElement {
|
||||
// UID necessary for moveable to work (for now)
|
||||
readonly UID = counter++;
|
||||
|
||||
revId = 0;
|
||||
sizeStyle: CSSProperties = {};
|
||||
dataStyle: CSSProperties = {};
|
||||
@ -36,17 +37,34 @@ export class ElementState implements LayerElement {
|
||||
placement: Placement;
|
||||
|
||||
constructor(public item: CanvasElementItem, public options: CanvasElementOptions, public parent?: GroupState) {
|
||||
const fallbackName = `Element ${Date.now()}`;
|
||||
if (!options) {
|
||||
this.options = { type: item.id, name: `Element ${this.UID}` };
|
||||
this.options = { type: item.id, name: fallbackName };
|
||||
}
|
||||
this.anchor = options.anchor ?? {};
|
||||
this.placement = options.placement ?? {};
|
||||
options.anchor = this.anchor;
|
||||
options.placement = this.placement;
|
||||
|
||||
const scene = this.getScene();
|
||||
if (!options.name) {
|
||||
options.name = `Element ${this.UID}`;
|
||||
const newName = scene?.getNextElementName();
|
||||
options.name = newName ?? fallbackName;
|
||||
}
|
||||
scene?.byName.set(options.name, this);
|
||||
}
|
||||
|
||||
private getScene(): Scene | undefined {
|
||||
let trav = this.parent;
|
||||
while (trav) {
|
||||
if (trav.isRoot()) {
|
||||
return trav.scene;
|
||||
break;
|
||||
}
|
||||
trav = trav.parent;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getName() {
|
||||
@ -106,8 +124,6 @@ export class ElementState implements LayerElement {
|
||||
|
||||
this.options.anchor = this.anchor;
|
||||
this.options.placement = this.placement;
|
||||
|
||||
// console.log('validate', this.UID, this.item.id, this.placement, this.anchor);
|
||||
}
|
||||
|
||||
// The parent size, need to set our own size based on offsets
|
||||
@ -191,6 +207,10 @@ export class ElementState implements LayerElement {
|
||||
this.item = canvasElementRegistry.getIfExists(options.type) ?? notFoundItem;
|
||||
}
|
||||
|
||||
// rename handling
|
||||
const oldName = this.options.name;
|
||||
const newName = options.name;
|
||||
|
||||
this.revId++;
|
||||
this.options = { ...options };
|
||||
let trav = this.parent;
|
||||
@ -202,6 +222,12 @@ export class ElementState implements LayerElement {
|
||||
trav.revId++;
|
||||
trav = trav.parent;
|
||||
}
|
||||
|
||||
const scene = this.getScene();
|
||||
if (oldName !== newName && scene) {
|
||||
scene.byName.delete(oldName);
|
||||
scene.byName.set(newName, this);
|
||||
}
|
||||
}
|
||||
|
||||
getSaveModel() {
|
||||
|
@ -103,6 +103,7 @@ export class GroupState extends ElementState {
|
||||
switch (action) {
|
||||
case LayerActionID.Delete:
|
||||
this.elements = this.elements.filter((e) => e !== element);
|
||||
this.scene.byName.delete(element.options.name);
|
||||
this.scene.save();
|
||||
this.reinitializeMoveable();
|
||||
break;
|
||||
@ -129,9 +130,10 @@ export class GroupState extends ElementState {
|
||||
copy.updateSize(element.width, element.height);
|
||||
copy.updateData(this.scene.context);
|
||||
if (updateName) {
|
||||
copy.options.name = `Element ${copy.UID} (duplicate)`;
|
||||
copy.options.name = this.scene.getNextElementName();
|
||||
}
|
||||
this.elements.push(copy);
|
||||
this.scene.byName.set(copy.options.name, copy);
|
||||
this.scene.save();
|
||||
this.reinitializeMoveable();
|
||||
break;
|
||||
|
@ -36,6 +36,7 @@ export class Scene {
|
||||
styles = getStyles(config.theme2);
|
||||
readonly selection = new ReplaySubject<ElementState[]>(1);
|
||||
readonly moved = new Subject<number>(); // called after resize/drag for editor updates
|
||||
readonly byName = new Map<string, ElementState>();
|
||||
root: RootElement;
|
||||
|
||||
revId = 0;
|
||||
@ -53,6 +54,25 @@ export class Scene {
|
||||
this.root = this.load(cfg, enableEditing);
|
||||
}
|
||||
|
||||
getNextElementName = (isGroup = false) => {
|
||||
const label = isGroup ? 'Group' : 'Element';
|
||||
let idx = this.byName.size + 1;
|
||||
|
||||
const max = idx + 100;
|
||||
while (true && idx < max) {
|
||||
const name = `${label} ${idx++}`;
|
||||
if (!this.byName.has(name)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
return `${label} ${Date.now()}`;
|
||||
};
|
||||
|
||||
canRename = (v: string) => {
|
||||
return !this.byName.has(v);
|
||||
};
|
||||
|
||||
load(cfg: CanvasGroupOptions, enableEditing: boolean) {
|
||||
this.root = new RootElement(
|
||||
cfg ?? {
|
||||
@ -103,7 +123,7 @@ export class Scene {
|
||||
const newLayer = new GroupState(
|
||||
{
|
||||
type: 'group',
|
||||
name: `Group ${Date.now()}.${Math.floor(Math.random() * 100)}`,
|
||||
name: this.getNextElementName(true),
|
||||
elements: [],
|
||||
},
|
||||
this,
|
||||
@ -111,12 +131,14 @@ export class Scene {
|
||||
);
|
||||
|
||||
currentSelectedElements.forEach((element: ElementState) => {
|
||||
newLayer.doAction(LayerActionID.Duplicate, element, false);
|
||||
currentLayer.doAction(LayerActionID.Delete, element);
|
||||
newLayer.doAction(LayerActionID.Duplicate, element, false);
|
||||
});
|
||||
|
||||
currentLayer.elements.push(newLayer);
|
||||
|
||||
this.byName.set(newLayer.getName(), newLayer);
|
||||
|
||||
this.save();
|
||||
});
|
||||
}
|
||||
@ -133,7 +155,6 @@ export class Scene {
|
||||
}
|
||||
|
||||
toggleAnchor(element: ElementState, k: keyof Anchor) {
|
||||
console.log('TODO, smarter toggle', element.UID, element.anchor, k);
|
||||
const { div } = element;
|
||||
if (!div) {
|
||||
console.log('Not ready');
|
||||
|
@ -126,10 +126,10 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
||||
|
||||
const { layer } = settings;
|
||||
|
||||
this.deleteGroup();
|
||||
layer.elements.forEach((element: ElementState) => {
|
||||
layer.parent?.doAction(LayerActionID.Duplicate, element, false);
|
||||
});
|
||||
this.deleteGroup();
|
||||
};
|
||||
|
||||
private onDecoupleGroup = () => {
|
||||
@ -156,7 +156,11 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
||||
|
||||
const { layer } = settings;
|
||||
|
||||
const scene = this.getScene();
|
||||
scene?.byName.delete(layer.getName());
|
||||
layer.elements.forEach((element) => scene?.byName.delete(element.getName()));
|
||||
layer.parent?.doAction(LayerActionID.Delete, layer);
|
||||
|
||||
this.goUpLayer();
|
||||
};
|
||||
|
||||
@ -215,6 +219,12 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
||||
return element instanceof GroupState;
|
||||
};
|
||||
|
||||
const verifyLayerNameUniqueness = (nameToVerify: string) => {
|
||||
const scene = this.getScene();
|
||||
|
||||
return Boolean(scene?.canRename(nameToVerify));
|
||||
};
|
||||
|
||||
const selection: string[] = settings.selected ? settings.selected.map((v) => v.getName()) : [];
|
||||
return (
|
||||
<>
|
||||
@ -241,6 +251,7 @@ export class LayerElementListEditor extends PureComponent<Props> {
|
||||
onDuplicate={onDuplicate}
|
||||
getLayerInfo={getLayerInfo}
|
||||
onNameChange={onNameChange}
|
||||
verifyLayerNameUniqueness={verifyLayerNameUniqueness}
|
||||
isGroup={isGroup}
|
||||
layers={layer.elements}
|
||||
selection={selection}
|
||||
|
@ -28,7 +28,7 @@ export const plugin = new PanelPlugin<PanelOptions>(CanvasPanel)
|
||||
if (!(element instanceof GroupState)) {
|
||||
builder.addNestedOptions(
|
||||
getElementEditor({
|
||||
category: [`Selected element (id: ${element.UID})`], // changing the ID forces reload
|
||||
category: [`Selected element (${element.options.name})`],
|
||||
element,
|
||||
scene: state.scene,
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user