mirror of
https://github.com/projectstorm/react-diagrams.git
synced 2025-08-18 19:20:42 +08:00
fixed a bug with serializing and added deserializing capablity
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -25,6 +25,7 @@ window.onload = () => {
|
||||
engine.registerNodeFactory(new SRD.DefaultNodeFactory());
|
||||
engine.registerLinkFactory(new SRD.DefaultLinkFactory());
|
||||
|
||||
|
||||
//2) setup the diagram model
|
||||
var model = new SRD.DiagramModel();
|
||||
|
||||
@ -56,6 +57,21 @@ window.onload = () => {
|
||||
//6) render the diagram!
|
||||
ReactDOM.render(React.createElement(SRD.DiagramWidget,{diagramEngine: engine}), document.body);
|
||||
|
||||
console.log(JSON.stringify(model.serialize()));
|
||||
|
||||
//!------------- SERIALIZING / DESERIALIZING ------------
|
||||
|
||||
//we need this to help the system know what models to create form the JSON
|
||||
engine.registerInstanceFactory(new SRD.DefaultNodeInstanceFactory());
|
||||
engine.registerInstanceFactory(new SRD.DefaultPortInstanceFactory());
|
||||
|
||||
//serialize the model
|
||||
var str = JSON.stringify(model.serializeDiagram());
|
||||
console.log(str);
|
||||
|
||||
//deserialize the model
|
||||
var model2 = new SRD.DiagramModel();
|
||||
model2.deSerializeDiagram(str,engine);
|
||||
|
||||
//re-render the model
|
||||
ReactDOM.render(React.createElement(SRD.DiagramWidget,{diagramEngine: engine}), document.body);
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1789
dist/main.js
vendored
1789
dist/main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/main.js.map
vendored
2
dist/main.js.map
vendored
File diff suppressed because one or more lines are too long
10
dist/src/AbstractInstanceFactory.d.ts
vendored
Normal file
10
dist/src/AbstractInstanceFactory.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
import { BaseEntity, BaseListener } from "./BaseEntity";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export declare abstract class AbstractInstanceFactory<T extends BaseEntity<BaseListener>> {
|
||||
className: string;
|
||||
constructor(className: string);
|
||||
getName(): string;
|
||||
abstract getInstance(): T;
|
||||
}
|
3
dist/src/BaseEntity.d.ts
vendored
3
dist/src/BaseEntity.d.ts
vendored
@ -3,7 +3,7 @@
|
||||
*/
|
||||
export declare class BaseListener {
|
||||
}
|
||||
export declare class BaseEnity<T extends BaseListener> {
|
||||
export declare class BaseEntity<T extends BaseListener> {
|
||||
listeners: {
|
||||
[s: string]: T;
|
||||
};
|
||||
@ -11,6 +11,7 @@ export declare class BaseEnity<T extends BaseListener> {
|
||||
constructor();
|
||||
getID(): string;
|
||||
clearListeners(): void;
|
||||
deSerialize(data: any): void;
|
||||
serialize(): {
|
||||
id: string;
|
||||
};
|
||||
|
10
dist/src/Common.d.ts
vendored
10
dist/src/Common.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import { BaseEnity, BaseListener } from "./BaseEntity";
|
||||
import { BaseEntity, BaseListener } from "./BaseEntity";
|
||||
export interface BaseModelListener extends BaseListener {
|
||||
selectionChanged?(): any;
|
||||
entityRemoved?(): any;
|
||||
@ -6,9 +6,10 @@ export interface BaseModelListener extends BaseListener {
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export declare class BaseModel extends BaseEnity<BaseModelListener> {
|
||||
export declare class BaseModel extends BaseEntity<BaseModelListener> {
|
||||
selected: boolean;
|
||||
constructor();
|
||||
deSerialize(ob: any): void;
|
||||
serialize(): {
|
||||
id: string;
|
||||
} & {
|
||||
@ -28,6 +29,7 @@ export declare class PointModel extends BaseModel {
|
||||
x: number;
|
||||
y: number;
|
||||
});
|
||||
deSerialize(ob: any): void;
|
||||
serialize(): {
|
||||
id: string;
|
||||
} & {
|
||||
@ -53,6 +55,7 @@ export declare class LinkModel extends BaseModel {
|
||||
points: PointModel[];
|
||||
extras: {};
|
||||
constructor();
|
||||
deSerialize(ob: any): void;
|
||||
serialize(): {
|
||||
id: string;
|
||||
} & {
|
||||
@ -62,6 +65,8 @@ export declare class LinkModel extends BaseModel {
|
||||
type: string;
|
||||
source: string;
|
||||
sourcePort: string;
|
||||
target: string;
|
||||
targetPort: string;
|
||||
points: ({
|
||||
id: string;
|
||||
} & {
|
||||
@ -124,6 +129,7 @@ export declare class NodeModel extends BaseModel {
|
||||
[s: string]: PortModel;
|
||||
};
|
||||
constructor(nodeType?: string);
|
||||
deSerialize(ob: any): void;
|
||||
serialize(): {
|
||||
id: string;
|
||||
} & {
|
||||
|
10
dist/src/DiagramEngine.d.ts
vendored
10
dist/src/DiagramEngine.d.ts
vendored
@ -1,8 +1,9 @@
|
||||
/// <reference types="react" />
|
||||
import { NodeWidgetFactory, LinkWidgetFactory } from "./WidgetFactories";
|
||||
import { LinkModel, NodeModel, BaseModel, PortModel } from "./Common";
|
||||
import { BaseEnity, BaseListener } from "./BaseEntity";
|
||||
import { BaseEntity, BaseListener } from "./BaseEntity";
|
||||
import { DiagramModel } from "./DiagramModel";
|
||||
import { AbstractInstanceFactory } from "./AbstractInstanceFactory";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
@ -13,13 +14,16 @@ export interface DiagramEngineListener extends BaseListener {
|
||||
/**
|
||||
* Passed as a parameter to the DiagramWidget
|
||||
*/
|
||||
export declare class DiagramEngine extends BaseEnity<DiagramEngineListener> {
|
||||
export declare class DiagramEngine extends BaseEntity<DiagramEngineListener> {
|
||||
nodeFactories: {
|
||||
[s: string]: NodeWidgetFactory;
|
||||
};
|
||||
linkFactories: {
|
||||
[s: string]: LinkWidgetFactory;
|
||||
};
|
||||
instanceFactories: {
|
||||
[s: string]: AbstractInstanceFactory<BaseEntity<BaseListener>>;
|
||||
};
|
||||
diagramModel: DiagramModel;
|
||||
canvas: Element;
|
||||
paintableWidgets: {};
|
||||
@ -36,6 +40,8 @@ export declare class DiagramEngine extends BaseEnity<DiagramEngineListener> {
|
||||
getLinkFactories(): {
|
||||
[s: string]: LinkWidgetFactory;
|
||||
};
|
||||
getInstanceFactory(className: string): AbstractInstanceFactory<BaseEntity<BaseListener>>;
|
||||
registerInstanceFactory(factory: AbstractInstanceFactory<BaseEntity<BaseListener>>): void;
|
||||
registerNodeFactory(factory: NodeWidgetFactory): void;
|
||||
registerLinkFactory(factory: LinkWidgetFactory): void;
|
||||
getFactoryForNode(node: NodeModel): NodeWidgetFactory | null;
|
||||
|
11
dist/src/DiagramModel.d.ts
vendored
11
dist/src/DiagramModel.d.ts
vendored
@ -1,5 +1,6 @@
|
||||
import { LinkModel, NodeModel, BaseModel } from "./Common";
|
||||
import { BaseListener, BaseEnity } from "./BaseEntity";
|
||||
import { BaseListener, BaseEntity } from "./BaseEntity";
|
||||
import { DiagramEngine } from "./DiagramEngine";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*
|
||||
@ -12,7 +13,7 @@ export interface DiagramListener extends BaseListener {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export declare class DiagramModel extends BaseEnity<DiagramListener> {
|
||||
export declare class DiagramModel extends BaseEntity<DiagramListener> {
|
||||
links: {
|
||||
[s: string]: LinkModel;
|
||||
};
|
||||
@ -23,8 +24,8 @@ export declare class DiagramModel extends BaseEnity<DiagramListener> {
|
||||
offsetY: number;
|
||||
zoom: number;
|
||||
constructor();
|
||||
deSerialize(object: any): void;
|
||||
serialize(): {
|
||||
deSerializeDiagram(object: any, diagramEngine: DiagramEngine): void;
|
||||
serializeDiagram(): {
|
||||
id: string;
|
||||
} & {
|
||||
offsetX: number;
|
||||
@ -39,6 +40,8 @@ export declare class DiagramModel extends BaseEnity<DiagramListener> {
|
||||
type: string;
|
||||
source: string;
|
||||
sourcePort: string;
|
||||
target: string;
|
||||
targetPort: string;
|
||||
points: ({
|
||||
id: string;
|
||||
} & {
|
||||
|
1
dist/src/defaults/DefaultNodeFactory.d.ts
vendored
1
dist/src/defaults/DefaultNodeFactory.d.ts
vendored
@ -7,6 +7,5 @@ import { DiagramEngine } from "../DiagramEngine";
|
||||
*/
|
||||
export declare class DefaultNodeFactory extends NodeWidgetFactory {
|
||||
constructor();
|
||||
generateModel(): void;
|
||||
generateReactWidget(diagramEngine: DiagramEngine, node: DefaultNodeModel): JSX.Element;
|
||||
}
|
||||
|
5
dist/src/defaults/DefaultNodeModel.d.ts
vendored
5
dist/src/defaults/DefaultNodeModel.d.ts
vendored
@ -1,5 +1,10 @@
|
||||
import { NodeModel } from "../Common";
|
||||
import { DefaultPortModel } from "./DefaultPortModel";
|
||||
import { AbstractInstanceFactory } from "../AbstractInstanceFactory";
|
||||
export declare class DefaultNodeInstanceFactory extends AbstractInstanceFactory<DefaultNodeModel> {
|
||||
constructor();
|
||||
getInstance(): DefaultNodeModel;
|
||||
}
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
|
5
dist/src/defaults/DefaultPortModel.d.ts
vendored
5
dist/src/defaults/DefaultPortModel.d.ts
vendored
@ -1,4 +1,9 @@
|
||||
import { PortModel } from "../Common";
|
||||
import { AbstractInstanceFactory } from "../AbstractInstanceFactory";
|
||||
export declare class DefaultPortInstanceFactory extends AbstractInstanceFactory<DefaultPortModel> {
|
||||
constructor();
|
||||
getInstance(): DefaultPortModel;
|
||||
}
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
|
1
dist/src/main.d.ts
vendored
1
dist/src/main.d.ts
vendored
@ -13,6 +13,7 @@ export * from "./DiagramEngine";
|
||||
export * from "./DiagramModel";
|
||||
export * from "./BaseEntity";
|
||||
export * from "./Common";
|
||||
export * from "./AbstractInstanceFactory";
|
||||
export * from "./widgets/DiagramWidget";
|
||||
export * from "./widgets/LinkLayerWidget";
|
||||
export * from "./widgets/LinkWidget";
|
||||
|
19
src/AbstractInstanceFactory.ts
Normal file
19
src/AbstractInstanceFactory.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {BaseEntity, BaseListener} from "./BaseEntity";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export abstract class AbstractInstanceFactory<T extends BaseEntity<BaseListener>>{
|
||||
|
||||
className: string;
|
||||
|
||||
constructor(className: string){
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
getName(){
|
||||
return this.className;
|
||||
}
|
||||
|
||||
abstract getInstance(): T;
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import {Toolkit} from "./Toolkit";
|
||||
import {AbstractInstanceFactory} from "./AbstractInstanceFactory";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
@ -6,7 +7,7 @@ export class BaseListener{
|
||||
|
||||
}
|
||||
|
||||
export class BaseEnity<T extends BaseListener>{
|
||||
export class BaseEntity<T extends BaseListener>{
|
||||
|
||||
public listeners:{[s: string]: T};
|
||||
public id: string;
|
||||
@ -24,6 +25,10 @@ export class BaseEnity<T extends BaseListener>{
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
public deSerialize(data){
|
||||
this.id = data.id;
|
||||
}
|
||||
|
||||
public serialize(){
|
||||
return {
|
||||
id: this.id,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Toolkit} from "./Toolkit";
|
||||
import {BaseEnity, BaseListener} from "./BaseEntity";
|
||||
import {BaseEntity, BaseListener} from "./BaseEntity";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export interface BaseModelListener extends BaseListener{
|
||||
@ -12,16 +12,20 @@ export interface BaseModelListener extends BaseListener{
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export class BaseModel extends BaseEnity<BaseModelListener>{
|
||||
export class BaseModel extends BaseEntity<BaseModelListener>{
|
||||
|
||||
selected: boolean;
|
||||
|
||||
constructor(){
|
||||
super();
|
||||
this.id = Toolkit.UID();
|
||||
this.selected = false;
|
||||
}
|
||||
|
||||
deSerialize(ob){
|
||||
super.deSerialize(ob);
|
||||
this.selected = ob.selected;
|
||||
}
|
||||
|
||||
serialize(){
|
||||
return _.merge(super.serialize(),{
|
||||
_class: this.constructor.name,
|
||||
@ -69,6 +73,12 @@ export class PointModel extends BaseModel{
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
deSerialize(ob){
|
||||
super.deSerialize(ob);
|
||||
this.x = ob.x;
|
||||
this.y = ob.y;
|
||||
}
|
||||
|
||||
serialize(){
|
||||
return _.extend(super.serialize(),{
|
||||
x: this.x,
|
||||
@ -123,11 +133,23 @@ export class LinkModel extends BaseModel{
|
||||
this.targetPort = null;
|
||||
}
|
||||
|
||||
deSerialize(ob){
|
||||
super.deSerialize(ob);
|
||||
this.linkType = ob.type;
|
||||
this.points = _.map(ob.points,(point: {x,y}) => {
|
||||
var p = new PointModel(this, {x: point.x,y:point.y});
|
||||
p.deSerialize(point);
|
||||
return p;
|
||||
})
|
||||
}
|
||||
|
||||
serialize(){
|
||||
return _.merge(super.serialize(),{
|
||||
type: this.linkType,
|
||||
source: this.sourcePort ? this.sourcePort.getParent().id:null,
|
||||
sourcePort: this.sourcePort ? this.sourcePort.id:null,
|
||||
target: this.targetPort ? this.targetPort.getParent().id:null,
|
||||
targetPort: this.targetPort ? this.targetPort.id:null,
|
||||
points: _.map(this.points,(point) => {
|
||||
return point.serialize();
|
||||
}),
|
||||
@ -274,6 +296,14 @@ export class NodeModel extends BaseModel{
|
||||
this.ports = {};
|
||||
}
|
||||
|
||||
deSerialize(ob){
|
||||
super.deSerialize(ob);
|
||||
this.nodeType = ob.type;
|
||||
this.x = ob.x;
|
||||
this.y = ob.y;
|
||||
this.extras = ob.extras;
|
||||
}
|
||||
|
||||
serialize(){
|
||||
return _.merge(super.serialize(),{
|
||||
type: this.nodeType,
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {NodeWidgetFactory, LinkWidgetFactory} from "./WidgetFactories";
|
||||
import {LinkModel, NodeModel, BaseModel, PortModel, PointModel} from "./Common";
|
||||
import {BaseEnity, BaseListener} from "./BaseEntity";
|
||||
import {BaseEntity, BaseListener} from "./BaseEntity";
|
||||
import {DiagramModel} from "./DiagramModel";
|
||||
import * as React from "react";
|
||||
import {AbstractInstanceFactory} from "./AbstractInstanceFactory";
|
||||
import * as _ from "lodash";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
@ -17,10 +17,12 @@ export interface DiagramEngineListener extends BaseListener{
|
||||
/**
|
||||
* Passed as a parameter to the DiagramWidget
|
||||
*/
|
||||
export class DiagramEngine extends BaseEnity<DiagramEngineListener>{
|
||||
export class DiagramEngine extends BaseEntity<DiagramEngineListener>{
|
||||
|
||||
nodeFactories: {[s: string]:NodeWidgetFactory};
|
||||
linkFactories: {[s: string]:LinkWidgetFactory};
|
||||
instanceFactories: {[s: string]: AbstractInstanceFactory<BaseEntity<BaseListener>>};
|
||||
|
||||
diagramModel: DiagramModel;
|
||||
canvas: Element;
|
||||
paintableWidgets: {};
|
||||
@ -30,6 +32,7 @@ export class DiagramEngine extends BaseEnity<DiagramEngineListener>{
|
||||
this.diagramModel = new DiagramModel();
|
||||
this.nodeFactories = {};
|
||||
this.linkFactories = {};
|
||||
this.instanceFactories = {};
|
||||
this.canvas = null;
|
||||
this.paintableWidgets = null;
|
||||
}
|
||||
@ -88,6 +91,14 @@ export class DiagramEngine extends BaseEnity<DiagramEngineListener>{
|
||||
return this.linkFactories;
|
||||
}
|
||||
|
||||
getInstanceFactory(className: string): AbstractInstanceFactory<BaseEntity<BaseListener>>{
|
||||
return this.instanceFactories[className];
|
||||
}
|
||||
|
||||
registerInstanceFactory(factory: AbstractInstanceFactory<BaseEntity<BaseListener>>){
|
||||
this.instanceFactories[factory.getName()] = factory;
|
||||
}
|
||||
|
||||
registerNodeFactory(factory: NodeWidgetFactory){
|
||||
this.nodeFactories[factory.getType()] = factory;
|
||||
this.itterateListeners((listener) => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {LinkModel, NodeModel, BaseModel} from "./Common";
|
||||
import {BaseListener, BaseEnity} from "./BaseEntity";
|
||||
import {LinkModel, NodeModel, BaseModel, PortModel} from "./Common";
|
||||
import {BaseListener, BaseEntity} from "./BaseEntity";
|
||||
import * as _ from "lodash";
|
||||
import {DiagramEngine} from "./DiagramEngine";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*
|
||||
@ -17,7 +18,7 @@ export interface DiagramListener extends BaseListener{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class DiagramModel extends BaseEnity<DiagramListener>{
|
||||
export class DiagramModel extends BaseEntity<DiagramListener>{
|
||||
|
||||
//models
|
||||
links: {[s:string] : LinkModel};
|
||||
@ -39,13 +40,46 @@ export class DiagramModel extends BaseEnity<DiagramListener>{
|
||||
this.zoom = 100;
|
||||
}
|
||||
|
||||
deSerialize(object: any){
|
||||
deSerializeDiagram(object: any, diagramEngine: DiagramEngine){
|
||||
this.deSerialize(object);
|
||||
|
||||
this.offsetX = object.offsetX;
|
||||
this.offsetY = object.offsetY;
|
||||
this.zoom = object.zoom;
|
||||
|
||||
//deserialize nodes
|
||||
_.forEach(object.nodes,(node) => {
|
||||
let nodeOb = diagramEngine.getInstanceFactory(node._class).getInstance() as NodeModel;
|
||||
nodeOb.deSerialize(node);
|
||||
|
||||
//deserialize ports
|
||||
_.forEach(node.ports,(port) => {
|
||||
let portOb = diagramEngine.getInstanceFactory(port._class).getInstance() as PortModel;
|
||||
portOb.deSerialize(port);
|
||||
nodeOb.addPort(portOb);
|
||||
});
|
||||
|
||||
this.addNode(nodeOb);
|
||||
});
|
||||
|
||||
_.forEach(object.links,(link) => {
|
||||
let linkOb = diagramEngine.getInstanceFactory(link._class).getInstance() as LinkModel;
|
||||
linkOb.deSerialize(link);
|
||||
|
||||
if(link.target){
|
||||
this.getNode(link.target).getPort(link.targetPort).addLink(linkOb);
|
||||
}
|
||||
|
||||
serialize(){
|
||||
return _.merge(super.serialize(),{
|
||||
if(link.source){
|
||||
this.getNode(link.source).getPort(link.sourcePort).addLink(linkOb);
|
||||
}
|
||||
|
||||
this.addLink(linkOb);
|
||||
});
|
||||
}
|
||||
|
||||
serializeDiagram(){
|
||||
return _.merge(this.serialize(),{
|
||||
offsetX: this.offsetX,
|
||||
offsetY: this.offsetY,
|
||||
zoom: this.zoom,
|
||||
|
@ -12,10 +12,6 @@ export class DefaultNodeFactory extends NodeWidgetFactory{
|
||||
super("default");
|
||||
}
|
||||
|
||||
generateModel(){
|
||||
|
||||
}
|
||||
|
||||
generateReactWidget(diagramEngine:DiagramEngine,node: DefaultNodeModel): JSX.Element{
|
||||
return React.createElement(DefaultNodeWidget,{
|
||||
node: node,
|
||||
|
@ -1,6 +1,20 @@
|
||||
import {NodeModel} from "../Common";
|
||||
import {DefaultPortModel} from "./DefaultPortModel";
|
||||
import * as _ from "lodash";
|
||||
|
||||
import {AbstractInstanceFactory} from "../AbstractInstanceFactory";
|
||||
|
||||
export class DefaultNodeInstanceFactory extends AbstractInstanceFactory<DefaultNodeModel>{
|
||||
|
||||
constructor(){
|
||||
super("DefaultNodeModel");
|
||||
}
|
||||
|
||||
getInstance(){
|
||||
return new DefaultNodeModel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
|
@ -1,5 +1,18 @@
|
||||
import {PortModel} from "../Common";
|
||||
import * as _ from "lodash";
|
||||
import {AbstractInstanceFactory} from "../AbstractInstanceFactory";
|
||||
|
||||
export class DefaultPortInstanceFactory extends AbstractInstanceFactory<DefaultPortModel>{
|
||||
|
||||
constructor(){
|
||||
super("DefaultPortModel");
|
||||
}
|
||||
|
||||
getInstance(){
|
||||
return new DefaultPortModel(true,"unknown");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
@ -14,7 +27,7 @@ export class DefaultPortModel extends PortModel{
|
||||
}
|
||||
|
||||
serialize(){
|
||||
return _.extend(super.serialize(),{
|
||||
return _.merge(super.serialize(),{
|
||||
in: this.in,
|
||||
label: this.label,
|
||||
});
|
||||
|
@ -17,6 +17,7 @@ export * from "./DiagramEngine";
|
||||
export * from "./DiagramModel";
|
||||
export * from "./BaseEntity";
|
||||
export * from "./Common";
|
||||
export * from "./AbstractInstanceFactory";
|
||||
|
||||
export * from "./widgets/DiagramWidget";
|
||||
export * from "./widgets/LinkLayerWidget";
|
||||
|
Reference in New Issue
Block a user