Skip to content

Commit 521a7a7

Browse files
committed
WiP property editor for stroke/fill for base ocif node-types
1 parent fa95ab9 commit 521a7a7

File tree

4 files changed

+252
-12
lines changed

4 files changed

+252
-12
lines changed

apps/vps-web/src/app/custom-nodes/classes/base-rect-node-class.tsx

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getCurrentOCIF } from '@devhelpr/app-canvas';
12
import {
23
createJSXElement,
34
FlowNode,
@@ -6,8 +7,12 @@ import {
67
IRectNodeComponent,
78
Rect,
89
IFlowCanvasBase,
10+
createElement,
11+
renderElement,
912
} from '@devhelpr/visual-programming-system';
1013
import { NodeInfo, RunCounter } from '@devhelpr/web-flow-executor';
14+
import { CorePropertiesSetupEditor } from './core-properties-settings-editor';
15+
import './style.css';
1116

1217
export type CreateRunCounterContext = (
1318
isRunViaRunButton: boolean,
@@ -22,9 +27,6 @@ export class BaseRectNode {
2227
id: string;
2328
node: IRectNodeComponent<NodeInfo> | undefined = undefined;
2429
updated: () => void;
25-
getSettingsPopup:
26-
| ((popupContainer: HTMLElement) => IDOMElement | undefined)
27-
| undefined = undefined;
2830

2931
rectInstance: Rect<NodeInfo> | undefined;
3032

@@ -47,6 +49,85 @@ export class BaseRectNode {
4749
this.updated = updated;
4850
this.node = node;
4951
}
52+
53+
getSettingsPopup:
54+
| ((popupContainer: HTMLElement) => IDOMElement | undefined)
55+
| undefined = (popupContainer: HTMLElement) => {
56+
const popupInstance = createElement(
57+
'div',
58+
{ class: 'max-h-[380px] h-[fit-content] p-3 pb-6' },
59+
popupContainer,
60+
undefined
61+
);
62+
const panel = popupInstance?.domElement as HTMLDivElement;
63+
if (panel) {
64+
panel.className = 'control-panel';
65+
66+
const controlsContainer = document.createElement('div');
67+
controlsContainer.className = 'controls-container';
68+
const nodeInfo = this.node?.nodeInfo as any;
69+
renderElement(
70+
<CorePropertiesSetupEditor
71+
strokeColor={nodeInfo?.strokeColor ?? '#000000'}
72+
fillColor={nodeInfo?.fillColor ?? '#ffffff'}
73+
onStrokeColorChange={(color: string) => {
74+
console.log('onStrokeColorChange', color);
75+
if (this.rectElement) {
76+
this.rectElement.style.borderColor = color;
77+
this.rectElement.style.color = color;
78+
if (this.node?.nodeInfo) {
79+
(this.node.nodeInfo as any).strokeColor = color;
80+
}
81+
}
82+
const ocif = getCurrentOCIF();
83+
if (ocif) {
84+
const node = ocif.nodes.find((n: any) => n.id === this.id);
85+
if (node) {
86+
const extension = node.data.find(
87+
(dataItem: any) =>
88+
dataItem.type === '@ocwg/node/rect' ||
89+
dataItem.type === '@ocwg/node/oval'
90+
);
91+
if (extension) {
92+
extension.strokeColor = color;
93+
}
94+
}
95+
}
96+
this.updated();
97+
}}
98+
onFillColorChange={(color: string) => {
99+
console.log('onFillColorChange', color);
100+
if (this.rectElement) {
101+
this.rectElement.style.backgroundColor = color;
102+
if (this.node?.nodeInfo) {
103+
(this.node.nodeInfo as any).fillColor = color;
104+
}
105+
}
106+
107+
const ocif = getCurrentOCIF();
108+
if (ocif) {
109+
const node = ocif.nodes.find((n: any) => n.id === this.id);
110+
if (node) {
111+
const extension = node.data.find(
112+
(dataItem: any) =>
113+
dataItem.type === '@ocwg/node/rect' ||
114+
dataItem.type === '@ocwg/node/oval'
115+
);
116+
if (extension) {
117+
extension.fillColor = color;
118+
}
119+
}
120+
}
121+
this.updated();
122+
}}
123+
/>,
124+
controlsContainer
125+
);
126+
panel.appendChild(controlsContainer);
127+
}
128+
return popupInstance;
129+
};
130+
50131
compute = (
51132
input: string,
52133
_loopIndex?: number,
@@ -100,6 +181,23 @@ export class BaseRectNode {
100181
const value = (event?.target as HTMLTextAreaElement)?.value;
101182

102183
(this.node.nodeInfo as any).text = value;
184+
const ocif = getCurrentOCIF();
185+
if (ocif) {
186+
const node = ocif.nodes.find((n: any) => n.id === this.id);
187+
if (node && node.resource) {
188+
const resource = ocif.resources.find(
189+
(r: any) => r.id === node.resource
190+
);
191+
if (resource) {
192+
const textRepresentation = resource.representations.find(
193+
(r: any) => r['mime-type'] === 'text/plain'
194+
);
195+
if (textRepresentation) {
196+
textRepresentation.content = value;
197+
}
198+
}
199+
}
200+
}
103201
const textarea = event.target as HTMLTextAreaElement;
104202
textarea.style.height = 'auto';
105203
textarea.style.height = textarea.scrollHeight + 'px';
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { createJSXElement } from '@devhelpr/visual-programming-system';
2+
3+
const COLOR_PRESETS: [number, number, number][] = [
4+
// Reds & Pinks
5+
[214, 39, 40], // d3 red (darker)
6+
[239, 68, 68], // red-500
7+
[236, 72, 153], // pink-500
8+
9+
// Oranges & Browns
10+
[140, 86, 75], // d3 brown
11+
[255, 127, 14], // d3 orange (bright)
12+
[249, 115, 22], // orange-500
13+
[245, 158, 11], // amber-500
14+
15+
// Greens
16+
[44, 160, 44], // d3 green (darker)
17+
[34, 197, 94], // green-500
18+
[20, 184, 166], // teal-500
19+
20+
// Blues & Cyans
21+
[31, 119, 180], // d3 blue (darker)
22+
[59, 130, 246], // blue-500
23+
[99, 102, 241], // indigo-500
24+
[6, 182, 212], // cyan-500
25+
26+
// Purples
27+
[168, 85, 247], // purple-500
28+
[148, 103, 189], // d3 purple
29+
];
30+
31+
export interface CorePropertiesSetupEditorProps {
32+
strokeColor: string;
33+
fillColor: string;
34+
onStrokeColorChange: (color: string) => void;
35+
onFillColorChange: (color: string) => void;
36+
}
37+
export const CorePropertiesSetupEditor = (
38+
props: CorePropertiesSetupEditorProps
39+
) => {
40+
let strokeColorElement: HTMLInputElement | null = null;
41+
let fillColorElement: HTMLInputElement | null = null;
42+
const onStrokeColorInputChange = (event: MouseEvent) => {
43+
const color = (event.target as HTMLButtonElement).value;
44+
props.onStrokeColorChange(color);
45+
};
46+
const onStrokeColorChange = (event: MouseEvent) => {
47+
const colorIndex = (event.target as HTMLInputElement).getAttribute(
48+
'data-color-index'
49+
);
50+
if (!colorIndex) return;
51+
const color = COLOR_PRESETS[parseInt(colorIndex)];
52+
const colorHex = `#${color[0].toString(16).padStart(2, '0')}${color[1]
53+
.toString(16)
54+
.padStart(2, '0')}${color[2].toString(16).padStart(2, '0')}`;
55+
props.onStrokeColorChange(colorHex);
56+
57+
if (strokeColorElement) {
58+
(strokeColorElement as HTMLInputElement).value = colorHex;
59+
}
60+
};
61+
62+
const onFillColorInputChange = (event: MouseEvent) => {
63+
const color = (event.target as HTMLButtonElement).value;
64+
props.onFillColorChange(color);
65+
};
66+
const onFillColorChange = (event: MouseEvent) => {
67+
const colorIndex = (event.target as HTMLInputElement).getAttribute(
68+
'data-color-index'
69+
);
70+
if (!colorIndex) return;
71+
const color = COLOR_PRESETS[parseInt(colorIndex)];
72+
const colorHex = `#${color[0].toString(16).padStart(2, '0')}${color[1]
73+
.toString(16)
74+
.padStart(2, '0')}${color[2].toString(16).padStart(2, '0')}`;
75+
props.onFillColorChange(colorHex);
76+
77+
if (fillColorElement) {
78+
(fillColorElement as HTMLInputElement).value = colorHex;
79+
}
80+
};
81+
82+
return (
83+
<element:Fragment>
84+
<div class="control-group">
85+
<label>Border color</label>
86+
<input
87+
type="color"
88+
id="strokeColorPicker"
89+
value={props.strokeColor}
90+
input={onStrokeColorInputChange}
91+
getElement={(element: HTMLInputElement) => {
92+
strokeColorElement = element;
93+
}}
94+
/>
95+
<div class="color-presets">
96+
{COLOR_PRESETS.map((color, index) => (
97+
<button
98+
data-control="strokeColorPicker"
99+
class="color-preset-button"
100+
style={`background-color: rgb(${color[0]}, ${color[1]}, ${color[2]})`}
101+
data-color-index={`${index}`}
102+
title={`Color ${index + 1}`}
103+
click={onStrokeColorChange}
104+
></button>
105+
))}
106+
</div>
107+
</div>
108+
<div class="control-group">
109+
<label>Fill color</label>
110+
<input
111+
type="color"
112+
id="fillColorPicker"
113+
value={props.fillColor}
114+
onInput={onFillColorInputChange}
115+
getElement={(element: HTMLInputElement) => {
116+
fillColorElement = element;
117+
}}
118+
/>
119+
<div class="color-presets">
120+
{COLOR_PRESETS.map((color, index) => (
121+
<button
122+
data-control="fillColorPicker"
123+
class="color-preset-button"
124+
style={`background-color: rgb(${color[0]}, ${color[1]}, ${color[2]})`}
125+
data-color-index={`${index}`}
126+
title={`Color ${index + 1}`}
127+
click={onFillColorChange}
128+
></button>
129+
))}
130+
</div>
131+
</div>
132+
</element:Fragment>
133+
);
134+
};

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
IThumbNodeComponent,
66
GetNodeTaskFactory,
77
ElementNodeMap,
8+
ThumbType,
89
} from '@devhelpr/visual-programming-system';
910
import { Exporter } from './Exporter';
1011

@@ -192,7 +193,7 @@ export class OCWGExporter extends BaseExporter<OCWGFile, OCWGInfo> {
192193
});
193194
if (nodeTask) {
194195
nodeTask.thumbs?.forEach((thumb) => {
195-
if (thumb.name) {
196+
if (thumb.name && thumb.thumbType !== ThumbType.Center) {
196197
ports.push(thumb.name);
197198
}
198199
});
@@ -210,19 +211,25 @@ export class OCWGExporter extends BaseExporter<OCWGFile, OCWGInfo> {
210211
delete clonedNodeInfo.fillColor;
211212
delete clonedNodeInfo.strokeColor;
212213
delete clonedNodeInfo.strokeWidth;
214+
213215
const ocwgNode: OCWGNode = {
214216
id: `${node.id}`,
215217
position: [node.x, node.y],
216218
size: [node.width ?? 0, node.height ?? 0],
217-
data: [
218-
{
219-
...clonedNodeInfo,
220-
type: nodeInfoPropertyName,
221-
nodeType: nodeInfo.type,
222-
},
223-
...portsNode,
224-
],
219+
data: [...portsNode],
225220
};
221+
222+
if (
223+
ocwgNode.data &&
224+
nodeInfo.type !== 'rect-node' &&
225+
nodeInfo.type !== 'oval-node'
226+
) {
227+
ocwgNode.data.push({
228+
...clonedNodeInfo,
229+
type: nodeInfoPropertyName,
230+
nodeType: nodeInfo.type,
231+
});
232+
}
226233
if (this.file?.nodes) {
227234
this.file.nodes.push(ocwgNode);
228235
}

libs/app-canvas/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export {
1010
ocifArrow,
1111
ocifToCodeFlowCanvas,
1212
} from './app/consts/ocif';
13+
export { getCurrentOCIF } from './app/importers/ocif-importer';

0 commit comments

Comments
 (0)