import * as React from 'react';
import { useState, useEffect, RefObject } from "react";
import {
AbstractUIElementStore,
// Component,
ComponentMeta,
ComponentProps,
ComponentStoreDelegate,
makeLogger,
PComponent,
PropertyTree,
SizeObject,
JsObject,
ReactResizeDetector,
QualifiedValue
} from '@inductiveautomation/perspective-client';
import { bind } from 'bind-decorator';
// const objectScan = require('object-scan');
// const cleanDeep = require('clean-deep');
import Diagram, { useSchema, createSchema } from 'beautiful-react-diagrams';
import { NodeCoordinates } from 'beautiful-react-diagrams/@types/DiagramSchema';
// import { SchemaType } from 'beautiful-react-diagrams/shared/Types';
// import Diagram from 'beautiful-react-diagrams';
export const COMPONENT_TYPE = "rad.display.diagram";
const logger = makeLogger(COMPONENT_TYPE);
export interface KanoaDiagramProps {
diagramSchema: any;
test: string;
onChange?: any;
}
export class KanoaDiagramGatewayDelegate extends ComponentStoreDelegate {
/**
* Value that is set when a message should be pending.
*/
private diagram: typeof Diagram | null = null;
constructor(componentStore: AbstractUIElementStore) {
// Required initialization of super.
super(componentStore);
}
@bind
init(diagram: typeof Diagram) {
if (diagram) {
this.diagram = diagram;
}
}
@bind
doSomething() {
this.diagram;
}
/**
* Maps our delegate state to component props. Invoked by the components HOC
* Wrapper component in the ComponentStore and passed to the Perspective
* component in props, i.e. `this.props.props.delegate`. Will do so
* whenever this delegate notifies listeners of state changes.
*/
public fireDelegateEvent(eventName: string, eventObject: JsObject): void {
// log messages left intentionally
logger.info(() => `Firing ${eventName} event with message body ${eventObject}`);
}
// Used by our component to fire a message to the gateway.
/**
* Implements `handleEvent` of abstract class ComponentStoreDelegate.
*
* Will automatically be invoked whenever a message is sent down from the corresponding delegate
* on the backend. Here we map messages and their payloads to the appropriate handlers.
*/
handleEvent(eventName: string, eventObject: JsObject): void {
logger.info(() => `Received '${eventName}' event!`);
}
handleComponentResponseEvent(eventObject: JsObject): void {
logger.info(() => `Callback handling message with contents: ${JSON.stringify(eventObject)}`);
}
}
export function KanoaDiagram(props: ComponentProps<KanoaDiagramProps>) {
const diagramRef: RefObject<HTMLDivElement> = React.createRef();
const initialSchema = createSchema({
nodes: [
{ id: 'node-1', content: 'Node 1', coordinates: [250, 60], },
{ id: 'node-2', content: 'Node 2', coordinates: [100, 200], },
{ id: 'node-3', content: 'Node 3', coordinates: [250, 220], },
{ id: 'node-4', content: 'Node 4', coordinates: [400, 200], },
],
links: [
{ input: 'node-1', output: 'node-2' },
{ input: 'node-1', output: 'node-3' },
{ input: 'node-1', output: 'node-4' },
]
});
props.store.props.write('diagramSchema', new QualifiedValue(initialSchema));
const [schema, { addNode, removeNode, onChange }] = useSchema(initialSchema);
const [diagramSchema, setDiagramSchema] = useState(initialSchema);
useEffect(() => {
console.log("print 1: ", props.store.props.readObject("diagramSchema"))
console.log("print 2: ", props.props.diagramSchema);
props.store.props.write('diagramSchema', new QualifiedValue(diagramSchema));
props.store.props.write('test', 'successful PropertyTree write');
console.log("Node added");
console.log("print 3: ", props.store.props.readObject("diagramSchema"))
console.log("print 4: ",props.props.diagramSchema);
}, [diagramSchema])
const CustomRender = ({ id, content, data, inputs, outputs }) => (
<div>
<div role="button" style={{padding: '15px'}}>
{content}
</div>
<div style={{marginTop: '10px',display:'flex', justifyContent:'space-between'}}>
{inputs.map((port) => React.cloneElement(port))}
{outputs.map((port) => React.cloneElement(port))}
</div>
</div>
);
const UncontrolledDiagram = () => {
const deleteNodeFromSchema = (id) => {
const nodeToRemove = schema.nodes.find(node => node.id === id)!;
// removeNode(nodeToRemove);
const updatedSchema = removeNode(nodeToRemove)
console.log(updatedSchema);
console.log(schema)
props.store.props.write('diagramSchema', new QualifiedValue(schema));
props.store.props.write('diagramSchema', schema);
setDiagramSchema(schema);
props.store.props.write('diagramSchema', new QualifiedValue(diagramSchema));
};
const addNewNode = () => {
const coord: NodeCoordinates = [
schema.nodes[schema.nodes.length - 1].coordinates[0] + 100,
schema.nodes[schema.nodes.length - 1].coordinates[1],
];
const nextNode = {
id: `node-${schema.nodes.length+1}`,
content: `Node ${schema.nodes.length+1}`,
coordinates: coord,
render: CustomRender,
className: 'bi-diagram-node-default',
data: {onClick: deleteNodeFromSchema},
inputs: [{ id: `port-${Math.random()}`}],
outputs: [{ id: `port-${Math.random()}`}],
}!;
// addNode(nextNode);
addNode(nextNode);
setDiagramSchema(schema);
};
return (
<div style={{ height: '100%' }}>
<button onClick={addNewNode}>Add new node</button>
<Diagram schema={diagramSchema} onChange={onChange} />
</div>
);
};
return (
<div {...props.emit()}>
<div ref={diagramRef} />
<div>{JSON.stringify(diagramSchema)}</div>
<UncontrolledDiagram />
<ReactResizeDetector
handleHeight={ true }
handleWidth={ true }
refreshMode="debounce"
/>
</div>
);
}
// export class KanoaDiagram extends Component<ComponentProps<KanoaDiagramProps>, any> {
// private diagramRef: RefObject<HTMLDivElement> = React.createRef();
// render() {
// const { props, emit } = this.props;
// const initialSchema = createSchema({
// nodes: [
// { id: 'node-1', content: 'Node 1', coordinates: [250, 60], },
// { id: 'node-2', content: 'Node 2', coordinates: [100, 200], },
// { id: 'node-3', content: 'Node 3', coordinates: [250, 220], },
// { id: 'node-4', content: 'Node 4', coordinates: [400, 200], },
// ],
// links: [
// { input: 'node-1', output: 'node-2' },
// { input: 'node-1', output: 'node-3' },
// { input: 'node-1', output: 'node-4' },
// ]
// });
// props.schema = initialSchema;
// const CustomRender = ({ id, content, data, inputs, outputs }) => (
// <div>
// <div role="button" style={{padding: '15px'}}>
// {content}
// </div>
// <div style={{marginTop: '10px',display:'flex', justifyContent:'space-between'}}>
// {inputs.map((port) => React.cloneElement(port))}
// {outputs.map((port) => React.cloneElement(port))}
// </div>
// </div>
// );
// const UncontrolledDiagram = () => {
// // create diagrams schema
// const [schema, { addNode, removeNode, onChange }] = useSchema(initialSchema);
// const deleteNodeFromSchema = (id) => {
// const nodeToRemove = schema.nodes.find(node => node.id === id)!;
// removeNode(nodeToRemove);
// };
// const addNewNode = () => {
// const coord: NodeCoordinates = [
// schema.nodes[schema.nodes.length - 1].coordinates[0] + 100,
// schema.nodes[schema.nodes.length - 1].coordinates[1],
// ];
// const nextNode = {
// id: `node-${schema.nodes.length+1}`,
// content: `Node ${schema.nodes.length+1}`,
// coordinates: coord,
// render: CustomRender,
// className: 'bi-diagram-node-default',
// data: {onClick: deleteNodeFromSchema},
// inputs: [{ id: `port-${Math.random()}`}],
// outputs: [{ id: `port-${Math.random()}`}],
// }!;
// addNode(nextNode);
// };
// const onDiagramChange = () => {
// // this.setState({schema: onChange(schema)});
// props.schema = onChange(schema);
// console.log(props.schema);
// };
// return (
// <div style={{ height: '100%' }}>
// <button onClick={addNewNode}>Add new node</button>
// <Diagram schema={schema} onChange={onDiagramChange} />
// </div>
// );
// };
// return (
// <div {...emit()}>
// <div ref={this.diagramRef} />
// <div>{JSON.stringify(props)}</div>
// <UncontrolledDiagram />
// <ReactResizeDetector
// handleHeight={ true }
// handleWidth={ true }
// refreshMode="debounce"
// />
// </div>
// );
// }
// }
export class KanoaDiagramMeta implements ComponentMeta {
getComponentType(): string {
return COMPONENT_TYPE;
}
getViewComponent(): PComponent {
return KanoaDiagram;
}
getDefaultSize(): SizeObject {
return ({
width: 475,
height: 200
});
}
// createDelegate(component: AbstractUIElementStore): ComponentStoreDelegate | undefined {
// return new KanoaDiagramGatewayDelegate(component);
// }
getPropsReducer(tree: PropertyTree): KanoaDiagramProps {
return {
diagramSchema: tree.readObject("diagramSchema"),
test: tree.readString("nodes", "default"),
onChange: tree.read("onChange"),
};
}
}
Sure, right now I'm thinking if I need to implement a DesignDelegate. Hopefully the issue is limited purely to the tsx file. You can ignore the GatewayDelegate class, since I already commented out the createDelegate function in the Meta class.
Edit: some extra context: in the useEffect hook, print 3 is correctly reading updated values. print 4 is not @Benjamin_Furlani