Skip to content

Commit bbb705c

Browse files
committed
new rect-node type for ocif compatibilty and fixes for handling (re)sizing and storage
1 parent 9425466 commit bbb705c

File tree

20 files changed

+309
-38
lines changed

20 files changed

+309
-38
lines changed

apps/vps-web/public/test.ocif.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
"position": [100, 100],
77
"resource": "r1"
88
},
9-
109
{
1110
"id": "n2",
1211
"position": [100, 100],
12+
1313
"data": [
1414
{
1515
"type": "circle-node",
@@ -20,12 +20,13 @@
2020
{
2121
"id": "n3",
2222
"position": [200, 200],
23+
"size": [200, 200],
2324
"resource": "r1",
2425
"data": [
2526
{
2627
"type": "rect-node",
2728
"strokeColor": "red",
28-
"strokeWidth": 2,
29+
"strokeWidth": "2px",
2930
"fillColor": "darkred"
3031
}
3132
]
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {
2+
createJSXElement,
3+
FlowNode,
4+
IComputeResult,
5+
} from '@devhelpr/visual-programming-system';
6+
import { NodeInfo } from '@devhelpr/web-flow-executor';
7+
8+
export class RectNode {
9+
nodeRenderElement: HTMLElement | undefined = undefined;
10+
rectElement: HTMLElement | undefined = undefined;
11+
id: string;
12+
constructor(id: string) {
13+
this.id = id;
14+
}
15+
compute = (
16+
input: string,
17+
_loopIndex?: number,
18+
_payload?: any
19+
): Promise<IComputeResult> => {
20+
return new Promise<IComputeResult>((resolve) => {
21+
resolve({
22+
result: input,
23+
output: input,
24+
followPath: undefined,
25+
});
26+
});
27+
};
28+
/*
29+
w-min h-min
30+
min-w-min min-h-min
31+
p-0
32+
flex items-center justify-center
33+
*/
34+
render = (node: FlowNode<NodeInfo>) => {
35+
const nodeInfo = node.nodeInfo as any;
36+
console.log('render rect-node', node.width, node.height);
37+
return (
38+
<div>
39+
<div
40+
getElement={(element: HTMLElement) => {
41+
this.rectElement = element;
42+
}}
43+
class={`rounded`}
44+
style={`width:${node.width ?? 50}px;height:${
45+
node.height ?? 50
46+
}px;background:${nodeInfo?.fillColor ?? 'black'};border: ${
47+
nodeInfo?.strokeWidth ?? '2px'
48+
} ${nodeInfo?.strokeColor ?? 'white'} solid`}
49+
></div>
50+
</div>
51+
);
52+
};
53+
54+
setSize = (width: number, height: number) => {
55+
if (this.rectElement) {
56+
this.rectElement.style.width = `${width}px`;
57+
this.rectElement.style.height = `${height}px`;
58+
}
59+
};
60+
}

apps/vps-web/src/app/custom-nodes/mermaid.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
createJSXElement,
33
FormField,
4-
IComputeResult,
54
InitialValues,
65
IRectNodeComponent,
76
NodeTask,
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import {
2+
createJSXElement,
3+
FlowNode,
4+
FormField,
5+
InitialValues,
6+
IRectNodeComponent,
7+
NodeTask,
8+
Rect,
9+
renderElement,
10+
Theme,
11+
visualNodeFactory,
12+
} from '@devhelpr/visual-programming-system';
13+
import { NodeInfo } from '@devhelpr/web-flow-executor';
14+
import { RectNode } from './classes/rect-node-class';
15+
16+
const category = 'Default';
17+
const fieldName = 'rect-input';
18+
const nodeTitle = 'Rect node';
19+
export const rectNodeName = 'rect-node';
20+
const familyName = 'flow-canvas';
21+
22+
export const getRectNode =
23+
() =>
24+
(
25+
_updated: () => void,
26+
_theme?: Theme,
27+
flowNode?: FlowNode<NodeInfo>
28+
): NodeTask<NodeInfo> => {
29+
let node: IRectNodeComponent<NodeInfo>;
30+
let rect: Rect<NodeInfo> | undefined;
31+
let rectNode: RectNode;
32+
let nodeRenderElement: HTMLElement | null = null;
33+
const initializeCompute = () => {
34+
return;
35+
};
36+
const computeAsync = (input: string, loopIndex?: number, payload?: any) => {
37+
return rectNode.compute(input, loopIndex, payload).then((result) => {
38+
if (rect && rect.resize) {
39+
rect.resize(
40+
undefined,
41+
true,
42+
'.child-node-wrapper > *:first-child',
43+
true
44+
);
45+
}
46+
return result;
47+
});
48+
};
49+
50+
return visualNodeFactory(
51+
rectNodeName,
52+
nodeTitle,
53+
familyName,
54+
fieldName,
55+
computeAsync,
56+
initializeCompute,
57+
false,
58+
flowNode?.width ?? 10,
59+
flowNode?.height ?? 10,
60+
[],
61+
(_values?: InitialValues): FormField[] => {
62+
return [];
63+
},
64+
(nodeInstance) => {
65+
if (!flowNode) {
66+
return;
67+
}
68+
rect = nodeInstance.rect;
69+
node = nodeInstance.node as IRectNodeComponent<NodeInfo>;
70+
71+
rectNode = new RectNode(node.id);
72+
73+
const childNodeWrapper = (nodeRenderElement = (
74+
rect?.nodeComponent?.domElement as HTMLElement
75+
).querySelector('.child-node-wrapper'));
76+
const childNodeInstance =
77+
childNodeWrapper?.querySelector('.child-instance');
78+
if (childNodeInstance) {
79+
childNodeInstance.remove();
80+
}
81+
renderElement(rectNode.render(flowNode), childNodeWrapper);
82+
nodeRenderElement = (
83+
rect?.nodeComponent?.domElement as HTMLElement
84+
).querySelector('.child-node-wrapper > *:first-child');
85+
if (nodeRenderElement) {
86+
rectNode.nodeRenderElement = nodeRenderElement;
87+
const resizeObserver = new ResizeObserver(() => {
88+
if (rect && rect.resize) {
89+
console.log('RECT RESIZE via observer');
90+
// problem with manual resizing is partially solved when this is commented
91+
// node content is not resized though...
92+
if (node.isSettingSize) {
93+
node.isSettingSize = false;
94+
rectNode.setSize(node.width ?? 10, node.height ?? 10);
95+
return;
96+
}
97+
rect.resize(
98+
undefined,
99+
true,
100+
'.child-node-wrapper > *:first-child',
101+
true
102+
);
103+
}
104+
});
105+
resizeObserver.observe(nodeRenderElement);
106+
if (node?.nodeInfo) {
107+
node.nodeInfo.delete = () => {
108+
if (nodeRenderElement) {
109+
resizeObserver.unobserve(nodeRenderElement);
110+
}
111+
resizeObserver.disconnect();
112+
};
113+
}
114+
}
115+
// if (rect && rect.resize) {
116+
// rect.resize(undefined, undefined, undefined, true);
117+
// }
118+
},
119+
{
120+
category,
121+
hasStaticWidthHeight: true,
122+
hasCustomStyling: true,
123+
customClassName: 'custom-rect-node',
124+
childNodeWrapperClass: 'child-node-wrapper',
125+
centerToYPositionThumb: false,
126+
skipDetermineSizeOnInit: true,
127+
},
128+
<div class="child-instance"></div>,
129+
true
130+
);
131+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { RegisterNodeFactoryFunction } from '@devhelpr/web-flow-executor';
22
import { mermaidNodeName, getMermaidNode } from './mermaid';
3+
import { rectNodeName, getRectNode } from './rect-node';
34

45
export const registerNodes = (
56
registerNodeFactory: RegisterNodeFactoryFunction
67
) => {
78
//registerNodeFactory('test-external-node', getExternalTestNode());
89
registerNodeFactory(mermaidNodeName, getMermaidNode());
10+
registerNodeFactory(rectNodeName, getRectNode());
911
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const ocifSchema = 'https://canvasprotocol.org/ocif/';
2+
export const ocifVersion = '0.2';
3+
export const ocifRelationEdge = '@ocwg/rel/edge';
4+
export const ocifRelationGroup = '@ocwg/rel/group';

libs/app-canvas/src/app/exporters/export-ocwg.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { OCWGFile, OCWGNode } from './ocwg/ocwg-schema';
1313
import { ocwgEmptyFile } from './ocwg/ocwg-empty-file';
1414
import { NodeInfo } from '@devhelpr/web-flow-executor';
1515
import { getCurrentOCIF } from '../importers/ocif-importer';
16+
import { ocifRelationGroup } from '../consts/ocif';
1617

1718
interface OCWGInfo {
1819
index: number;
@@ -91,6 +92,7 @@ export class OCWGExporter extends BaseExporter<OCWGFile, OCWGInfo> {
9192
(n) => n.id === node.id
9293
);
9394
if (codeFlowCanvasNode && codeFlowCanvasNode.data) {
95+
console.log('export ocif node', codeFlowCanvasNode);
9496
node.data?.forEach((d: any) => {
9597
if (!codeFlowCanvasNode.data) {
9698
return;
@@ -195,13 +197,21 @@ export class OCWGExporter extends BaseExporter<OCWGFile, OCWGInfo> {
195197
}
196198
if (parentId && this.file) {
197199
const relation = this.file.relations.find((r) => r.id === parentId);
198-
if (relation && relation.type === '@ocwg/rel/group') {
199-
relation.members.push(ocwgNode.id);
200+
if (
201+
relation &&
202+
relation.data.length > 0 &&
203+
relation.data[0].type === ocifRelationGroup
204+
) {
205+
relation.data[0].members.push(ocwgNode.id);
200206
} else {
201207
const relation = {
202-
type: '@ocwg/rel/group' as const,
203-
members: [ocwgNode.id],
204208
id: parentId,
209+
data: [
210+
{
211+
type: '@ocwg/rel/group' as const,
212+
members: [ocwgNode.id],
213+
},
214+
],
205215
};
206216
this.file.relations.push(relation);
207217
}
@@ -249,11 +259,15 @@ export class OCWGExporter extends BaseExporter<OCWGFile, OCWGInfo> {
249259
if (this.file?.relations) {
250260
{
251261
const relation = {
252-
type: '@ocwg/rel/edge' as const,
253262
id: `${node.id}-edge`,
254-
from: `${node.startNode.id}`,
255-
to: `${node.endNode.id}`,
256-
directed: true,
263+
data: [
264+
{
265+
type: '@ocwg/rel/edge' as const,
266+
from: `${node.startNode.id}`,
267+
to: `${node.endNode.id}`,
268+
directed: true,
269+
},
270+
],
257271
};
258272
this.file.relations.push(relation);
259273
}
@@ -295,10 +309,14 @@ export class OCWGExporter extends BaseExporter<OCWGFile, OCWGInfo> {
295309
{
296310
const relation = {
297311
id: `${node.id}-edge`,
298-
type: '@ocwg/rel/edge' as const,
299-
directed: true,
300-
from: `${node.startNode.id}`,
301-
to: `${node.endNode.id}`,
312+
data: [
313+
{
314+
type: '@ocwg/rel/edge' as const,
315+
directed: true,
316+
from: `${node.startNode.id}`,
317+
to: `${node.endNode.id}`,
318+
},
319+
],
302320
};
303321
this.file.relations.push(relation);
304322
}

libs/app-canvas/src/app/exporters/ocwg/ocwg-empty-file.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { ocifSchema, ocifVersion } from '../../consts/ocif';
12
import { OCWGFile } from './ocwg-schema';
23

34
export const ocwgEmptyFile: OCWGFile = {
4-
ocif: 'https://canvasprotocol.org/ocif/0.2',
5+
ocif: `${ocifSchema}${ocifVersion}`,
56
nodes: [],
67
relations: [],
78
resources: [],

libs/app-canvas/src/app/exporters/ocwg/ocwg-schema.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ocifRelationEdge, ocifRelationGroup } from '../../consts/ocif';
2+
13
export type OCWGNode = {
24
id: string;
35
position?: number[];
@@ -9,18 +11,22 @@ export type OCWGNode = {
911
};
1012

1113
export type OCWGEdge = {
12-
type: '@ocwg/rel/edge';
1314
id: string;
14-
directed?: boolean;
15-
from: string;
16-
to: string;
15+
data: {
16+
type: typeof ocifRelationEdge;
17+
directed?: boolean;
18+
from: string;
19+
to: string;
20+
}[];
1721
};
1822
export type OCWGRelation = OCWGGroup | OCWGEdge;
1923

2024
export type OCWGGroup = {
21-
type: '@ocwg/rel/group';
22-
members: Array<string>;
2325
id: string;
26+
data: {
27+
type: typeof ocifRelationGroup;
28+
members: Array<string>;
29+
}[];
2430
};
2531
export type OCWGFile = {
2632
ocif: string;

libs/app-canvas/src/app/flow-app.element.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,9 @@ export class FlowAppElement extends AppElement<NodeInfo> {
10921092
console.log('nodesList', nodesList);
10931093
const compositions = getSerializeCompositions() || {};
10941094
console.log('compositions', compositions);
1095+
1096+
1097+
// TODO : store ocif here as well.. if it's available
10951098
const flow: Flow<NodeInfo> = {
10961099
schemaType: 'flow',
10971100
schemaVersion: '0.0.1',

0 commit comments

Comments
 (0)