@linkdlab/funcnodes_react_flow 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/package copy.json +63 -0
- package/package.json +75 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +43 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/public/worker_manager +1 -0
- package/src/App.css +38 -0
- package/src/App.test.tsx +9 -0
- package/src/App.tsx +22 -0
- package/src/frontend/datarenderer/images.tsx +28 -0
- package/src/frontend/datarenderer/index.tsx +53 -0
- package/src/frontend/datarenderer/plotly.tsx +82 -0
- package/src/frontend/dialog.scss +88 -0
- package/src/frontend/dialog.tsx +70 -0
- package/src/frontend/edge.scss +15 -0
- package/src/frontend/edge.tsx +31 -0
- package/src/frontend/funcnodesreactflow.scss +63 -0
- package/src/frontend/funcnodesreactflow.tsx +283 -0
- package/src/frontend/header/header.scss +48 -0
- package/src/frontend/header/index.tsx +268 -0
- package/src/frontend/index.tsx +4 -0
- package/src/frontend/layout/htmlelements.scss +63 -0
- package/src/frontend/lib.scss +157 -0
- package/src/frontend/lib.tsx +198 -0
- package/src/frontend/node/index.tsx +3 -0
- package/src/frontend/node/io/default_input_renderer.tsx +327 -0
- package/src/frontend/node/io/default_output_render.tsx +26 -0
- package/src/frontend/node/io/handle_renderer.tsx +89 -0
- package/src/frontend/node/io/index.tsx +4 -0
- package/src/frontend/node/io/io.scss +91 -0
- package/src/frontend/node/io/io.tsx +114 -0
- package/src/frontend/node/io/nodeinput.tsx +125 -0
- package/src/frontend/node/io/nodeoutput.tsx +37 -0
- package/src/frontend/node/node.scss +265 -0
- package/src/frontend/node/node.tsx +208 -0
- package/src/frontend/nodecontextmenu.scss +18 -0
- package/src/frontend/utils/colorpicker.scss +37 -0
- package/src/frontend/utils/colorpicker.tsx +342 -0
- package/src/frontend/utils/jsondata.tsx +19 -0
- package/src/frontend/utils/table.scss +22 -0
- package/src/frontend/utils/table.tsx +159 -0
- package/src/funcnodes/funcnodesworker.ts +455 -0
- package/src/funcnodes/index.ts +4 -0
- package/src/funcnodes/websocketworker.ts +153 -0
- package/src/funcnodes/workermanager.ts +229 -0
- package/src/index.css +13 -0
- package/src/index.tsx +19 -0
- package/src/logo.svg +1 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/reportWebVitals.ts +15 -0
- package/src/setupTests.ts +5 -0
- package/src/state/edge.ts +35 -0
- package/src/state/fnrfzst.ts +440 -0
- package/src/state/index.ts +139 -0
- package/src/state/lib.ts +26 -0
- package/src/state/node.ts +118 -0
- package/src/state/nodespace.ts +151 -0
- package/src/state/reactflow.ts +65 -0
- package/src/types/lib.d.ts +16 -0
- package/src/types/node.d.ts +29 -0
- package/src/types/nodeio.d.ts +82 -0
- package/src/types/worker.d.ts +56 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useContext, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { FuncNodesContext } from "../..";
|
|
3
|
+
import * as Tooltip from "@radix-ui/react-tooltip";
|
|
4
|
+
import { Handle, Position } from "reactflow";
|
|
5
|
+
import { FuncNodesReactFlowZustandInterface } from "../../../state/fnrfzst";
|
|
6
|
+
import { HandleWithPreview, pick_best_io_type } from "./io";
|
|
7
|
+
import {
|
|
8
|
+
BooleanInput,
|
|
9
|
+
FloatInput,
|
|
10
|
+
IntegerInput,
|
|
11
|
+
StringInput,
|
|
12
|
+
SelectionInput,
|
|
13
|
+
ColorInput,
|
|
14
|
+
} from "./default_input_renderer";
|
|
15
|
+
|
|
16
|
+
const Inputrenderer: {
|
|
17
|
+
[key: string]: InputRendererType;
|
|
18
|
+
} = {};
|
|
19
|
+
|
|
20
|
+
const register_input_renderer = (type: string, renderer: InputRendererType) => {
|
|
21
|
+
Inputrenderer[type] = renderer;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
register_input_renderer("float", FloatInput);
|
|
25
|
+
register_input_renderer("int", IntegerInput);
|
|
26
|
+
register_input_renderer("bool", BooleanInput);
|
|
27
|
+
register_input_renderer("string", StringInput);
|
|
28
|
+
register_input_renderer("str", StringInput);
|
|
29
|
+
register_input_renderer("color", ColorInput);
|
|
30
|
+
register_input_renderer("select", SelectionInput);
|
|
31
|
+
register_input_renderer("enum", SelectionInput);
|
|
32
|
+
|
|
33
|
+
const INPUTCONVERTER: {
|
|
34
|
+
[key: string]: (v: any) => any;
|
|
35
|
+
} = {
|
|
36
|
+
"": (v: any) => v,
|
|
37
|
+
str_to_json: (v: any) => {
|
|
38
|
+
return JSON.parse(v);
|
|
39
|
+
},
|
|
40
|
+
str_to_list: (v: any) => {
|
|
41
|
+
try {
|
|
42
|
+
const a = JSON.parse(v);
|
|
43
|
+
if (Array.isArray(a)) return a;
|
|
44
|
+
return [a];
|
|
45
|
+
} catch (e) {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse("[" + v + "]");
|
|
48
|
+
} catch (e) {}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw new Error("Invalid list");
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const NodeInput = ({ io }: { io: IOType }) => {
|
|
56
|
+
const fnrf_zst: FuncNodesReactFlowZustandInterface =
|
|
57
|
+
useContext(FuncNodesContext);
|
|
58
|
+
const render: RenderOptions = fnrf_zst.render_options();
|
|
59
|
+
|
|
60
|
+
if (io.render_options === undefined) {
|
|
61
|
+
io.render_options = {};
|
|
62
|
+
}
|
|
63
|
+
if (io.render_options.set_default === undefined) {
|
|
64
|
+
io.render_options.set_default = true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const [typestring, otypestring] = pick_best_io_type(
|
|
68
|
+
io.type,
|
|
69
|
+
render.typemap || {}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const Input = typestring
|
|
73
|
+
? io.value_options?.options
|
|
74
|
+
? SelectionInput
|
|
75
|
+
: Inputrenderer[typestring]
|
|
76
|
+
: undefined;
|
|
77
|
+
const inputconverterf: (v: any) => any =
|
|
78
|
+
INPUTCONVERTER[(otypestring && render.inputconverter?.[otypestring]) ?? ""];
|
|
79
|
+
return (
|
|
80
|
+
<div className="nodeinput">
|
|
81
|
+
<HandleWithPreview
|
|
82
|
+
io={io}
|
|
83
|
+
typestring={typestring}
|
|
84
|
+
position={Position.Left}
|
|
85
|
+
type="target"
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
{Input && (
|
|
89
|
+
<div className="iovaluefield nodrag">
|
|
90
|
+
<Input io={io} inputconverter={inputconverterf} />
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
<div className="ioname">{io.name}</div>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export default NodeInput;
|
|
99
|
+
|
|
100
|
+
type Renderotion = ({ text }: { text: string }) => JSX.Element;
|
|
101
|
+
|
|
102
|
+
const renderoptions: { [key: string]: Renderotion } = {};
|
|
103
|
+
|
|
104
|
+
const Renderer = () => {
|
|
105
|
+
const [text, setText] = useState("");
|
|
106
|
+
const [render, setRender] = useState<Renderotion | undefined>(undefined);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<div>
|
|
110
|
+
<input
|
|
111
|
+
type="text"
|
|
112
|
+
value={text}
|
|
113
|
+
onChange={(e) => setText(e.target.value)}
|
|
114
|
+
/>
|
|
115
|
+
<select onChange={(e) => setRender(renderoptions[e.target.value])}>
|
|
116
|
+
{Object.keys(renderoptions).map((key) => (
|
|
117
|
+
<option key={key} value={key}>
|
|
118
|
+
{key}
|
|
119
|
+
</option>
|
|
120
|
+
))}
|
|
121
|
+
</select>
|
|
122
|
+
{render && render({ text })}
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useContext, useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { Handle, Position } from "reactflow";
|
|
4
|
+
import { FuncNodesContext } from "../..";
|
|
5
|
+
import { FuncNodesReactFlowZustandInterface } from "../../../state/fnrfzst";
|
|
6
|
+
import { HandleWithPreview, pick_best_io_type } from "./io";
|
|
7
|
+
import { InLineOutput } from "./default_output_render";
|
|
8
|
+
|
|
9
|
+
const NodeOutput = ({ io }: { io: IOType }) => {
|
|
10
|
+
const fnrf_zst: FuncNodesReactFlowZustandInterface =
|
|
11
|
+
useContext(FuncNodesContext);
|
|
12
|
+
const render: RenderOptions = fnrf_zst.render_options();
|
|
13
|
+
|
|
14
|
+
const [typestring, otypestring] = pick_best_io_type(
|
|
15
|
+
io.type,
|
|
16
|
+
render.typemap || {}
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="nodeoutput">
|
|
21
|
+
<HandleWithPreview
|
|
22
|
+
io={io}
|
|
23
|
+
typestring={typestring}
|
|
24
|
+
position={Position.Right}
|
|
25
|
+
type="source"
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<div className="ioname">{io.name}</div>
|
|
29
|
+
|
|
30
|
+
<div className="iovaluefield">
|
|
31
|
+
<InLineOutput io={io} />
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default NodeOutput;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--node_border_radius: 5px;
|
|
3
|
+
--handle_outer_radius: 4px;
|
|
4
|
+
--handle_inner_radius: 2px;
|
|
5
|
+
--handle_width: 10;
|
|
6
|
+
--handle_width_hover: 15;
|
|
7
|
+
--handle_overlap: 3;
|
|
8
|
+
--nodeinput_margin: 2;
|
|
9
|
+
--nodeio_shift: calc(var(--handle_overlap) - var(--nodeinput_margin));
|
|
10
|
+
--handle_source_color: hsl(190, 98%, 49%);
|
|
11
|
+
--handle_target_color: hsl(204, 98%, 51%);
|
|
12
|
+
--node_background: hsl(245, 51%, 42%);
|
|
13
|
+
--node_header_color: hsl(245, 51%, 22%);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.react-flow {
|
|
17
|
+
&__node {
|
|
18
|
+
padding: 0;
|
|
19
|
+
border-radius: var(--node_border_radius);
|
|
20
|
+
background-color: var(--node_background);
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
color: aliceblue;
|
|
24
|
+
box-sizing: content-box;
|
|
25
|
+
transform: (-50%, -50%);
|
|
26
|
+
border: 2px solid #ffffff00;
|
|
27
|
+
font-size: 0.5rem;
|
|
28
|
+
width: auto;
|
|
29
|
+
max-width: 200px;
|
|
30
|
+
min-width: 100px;
|
|
31
|
+
&.selected {
|
|
32
|
+
border-color: #ffffff99;
|
|
33
|
+
}
|
|
34
|
+
background-clip: content-box;
|
|
35
|
+
|
|
36
|
+
& * {
|
|
37
|
+
box-sizing: border-box;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&__handle {
|
|
42
|
+
height: calc(100% - 4px);
|
|
43
|
+
border-radius: 0;
|
|
44
|
+
width: calc(var(--handle_width) * 1px);
|
|
45
|
+
|
|
46
|
+
transition: left 0.2s ease-in-out, right 0.2s ease-in-out,
|
|
47
|
+
width 0.2s ease-in-out;
|
|
48
|
+
&:hover {
|
|
49
|
+
width: calc(var(--handle_width_hover) * 1px);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&.source {
|
|
53
|
+
background-color: var(--handle_source_color);
|
|
54
|
+
}
|
|
55
|
+
&.target {
|
|
56
|
+
background-color: var(--handle_target_color);
|
|
57
|
+
}
|
|
58
|
+
&-left {
|
|
59
|
+
border-radius: var(--handle_outer_radius) var(--handle_inner_radius)
|
|
60
|
+
var(--handle_inner_radius) var(--handle_outer_radius);
|
|
61
|
+
left: calc((var(--nodeio_shift) - var(--handle_width)) * 1px);
|
|
62
|
+
&:hover {
|
|
63
|
+
left: calc((var(--nodeio_shift) - var(--handle_width_hover)) * 1px);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
&-right {
|
|
67
|
+
border-radius: var(--handle_inner_radius) var(--handle_outer_radius)
|
|
68
|
+
var(--handle_outer_radius) var(--handle_inner_radius);
|
|
69
|
+
right: calc((var(--nodeio_shift) - var(--handle_width)) * 1px);
|
|
70
|
+
&:hover {
|
|
71
|
+
right: calc((var(--nodeio_shift) - var(--handle_width_hover)) * 1px);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
&-top {
|
|
75
|
+
border-radius: var(--handle_outer_radius) var(--handle_outer_radius)
|
|
76
|
+
var(--handle_inner_radius) var(--handle_inner_radius);
|
|
77
|
+
}
|
|
78
|
+
&-bottom {
|
|
79
|
+
border-radius: var(--handle_inner_radius) var(--handle_inner_radius)
|
|
80
|
+
var(--handle_outer_radius) var(--handle_outer_radius);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.innernode {
|
|
86
|
+
width: 100%;
|
|
87
|
+
height: 100%;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
display: flex;
|
|
90
|
+
// padding: 2px;
|
|
91
|
+
box-sizing: border-box;
|
|
92
|
+
|
|
93
|
+
&.intrigger {
|
|
94
|
+
& .nodeheader {
|
|
95
|
+
background-color: #abb408;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&.error {
|
|
100
|
+
& .nodeheader {
|
|
101
|
+
background-color: #ff0000;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
.nodeheader {
|
|
106
|
+
box-sizing: border-box;
|
|
107
|
+
background-color: var(--node_header_color);
|
|
108
|
+
width: 100%;
|
|
109
|
+
padding: 0.1rem;
|
|
110
|
+
border-radius: var(--node_border_radius) var(--node_border_radius) 0 0;
|
|
111
|
+
display: flex; /* Use flex layout */
|
|
112
|
+
align-items: center; /* Align items vertically in the center */
|
|
113
|
+
justify-content: space-between; /* Distribute space between items */
|
|
114
|
+
|
|
115
|
+
&_element {
|
|
116
|
+
display: flex; /* Enable flex within each child to center content */
|
|
117
|
+
align-items: center; /* Align items vertically in the center */
|
|
118
|
+
}
|
|
119
|
+
&_title {
|
|
120
|
+
flex-grow: 1; /* Allow the text div to grow and fill available space */
|
|
121
|
+
margin: 0 10px; /* Add some space around the text for aesthetics */
|
|
122
|
+
white-space: nowrap; /* Prevent wrapping */
|
|
123
|
+
overflow: hidden; /* Hide overflow */
|
|
124
|
+
text-overflow: ellipsis; /* Add ellipsis to overflow */
|
|
125
|
+
justify-content: center; /* Center the text horizontally */
|
|
126
|
+
&_text {
|
|
127
|
+
overflow: hidden;
|
|
128
|
+
text-overflow: ellipsis;
|
|
129
|
+
white-space: nowrap;
|
|
130
|
+
max-width: 100%; /* Ensure it does not overflow the parent */
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
& .triggerbutton {
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
|
|
137
|
+
&:hover {
|
|
138
|
+
color: #0cc3f5;
|
|
139
|
+
}
|
|
140
|
+
&:active {
|
|
141
|
+
color: #075d74;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.nodename_input {
|
|
147
|
+
// remove the default
|
|
148
|
+
border: 1px solid #000000;
|
|
149
|
+
border-radius: 2px;
|
|
150
|
+
background: none;
|
|
151
|
+
color: white;
|
|
152
|
+
text-align: center;
|
|
153
|
+
|
|
154
|
+
font-size: inherit;
|
|
155
|
+
margin: 2px;
|
|
156
|
+
box-sizing: border-box;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.nodefooter {
|
|
160
|
+
background-color: #4e0ba5;
|
|
161
|
+
width: 100%;
|
|
162
|
+
padding: 0.1rem;
|
|
163
|
+
border-radius: 0 0 var(--node_border_radius) var(--node_border_radius);
|
|
164
|
+
&:empty {
|
|
165
|
+
display: none;
|
|
166
|
+
}
|
|
167
|
+
& .nodeerror {
|
|
168
|
+
border: 1px solid #ff0000;
|
|
169
|
+
border-radius: 2px;
|
|
170
|
+
padding: 0.25rem;
|
|
171
|
+
background-color: #ff000075;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.nodebody {
|
|
176
|
+
flex-grow: 1;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.nodedatabody {
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.noderesizecontrol {
|
|
184
|
+
background: transparent !important;
|
|
185
|
+
border: none !important;
|
|
186
|
+
font-size: 0.5rem;
|
|
187
|
+
}
|
|
188
|
+
.noderesizeicon {
|
|
189
|
+
position: absolute;
|
|
190
|
+
bottom: 4px;
|
|
191
|
+
right: 4px;
|
|
192
|
+
font-size: 0.5rem;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.nodeio {
|
|
196
|
+
width: auto;
|
|
197
|
+
background-color: inherit;
|
|
198
|
+
padding: 0.1rem;
|
|
199
|
+
margin-top: 0.1rem;
|
|
200
|
+
margin-bottom: 0.1rem;
|
|
201
|
+
position: relative;
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-direction: row;
|
|
204
|
+
|
|
205
|
+
border: 1px solid #ffffff88;
|
|
206
|
+
border-radius: 3px;
|
|
207
|
+
box-sizing: border-box;
|
|
208
|
+
margin-left: calc(var(--nodeinput_margin) * 1px);
|
|
209
|
+
margin-right: calc(var(--nodeinput_margin) * 1px);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.nodeinput {
|
|
213
|
+
@extend .nodeio;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.nodeoutput {
|
|
217
|
+
@extend .nodeio;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.iovaluefield {
|
|
221
|
+
flex-grow: 1;
|
|
222
|
+
flex-shrink: 1;
|
|
223
|
+
overflow: hidden;
|
|
224
|
+
align-items: start;
|
|
225
|
+
justify-content: start;
|
|
226
|
+
display: flex;
|
|
227
|
+
margin: 0.2rem;
|
|
228
|
+
line-break: anywhere;
|
|
229
|
+
& > input {
|
|
230
|
+
width: 100%;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
.ioname {
|
|
234
|
+
flex-grow: 1;
|
|
235
|
+
flex-shrink: 1;
|
|
236
|
+
color: inherit;
|
|
237
|
+
margin: 0.2rem;
|
|
238
|
+
overflow: hidden;
|
|
239
|
+
min-width: 2rem;
|
|
240
|
+
|
|
241
|
+
.nodeinput & {
|
|
242
|
+
text-align: left;
|
|
243
|
+
}
|
|
244
|
+
.nodeoutput & {
|
|
245
|
+
text-align: right;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
input.nodedatainput {
|
|
250
|
+
&:focus {
|
|
251
|
+
outline: none;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.nodedatastyledelement {
|
|
256
|
+
background-color: var(--node_header_color);
|
|
257
|
+
|
|
258
|
+
font-size: 0.5rem;
|
|
259
|
+
height: 1.5rem;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
input.nodedatainput.styledinput,
|
|
263
|
+
select.nodedatainput.styleddropdown {
|
|
264
|
+
@extend .nodedatastyledelement;
|
|
265
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
RefObject,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from "react";
|
|
9
|
+
|
|
10
|
+
import { NodeStore } from "../../state/node";
|
|
11
|
+
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
|
12
|
+
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
13
|
+
import ExpandIcon from "@mui/icons-material/Expand";
|
|
14
|
+
import LanIcon from "@mui/icons-material/Lan";
|
|
15
|
+
import PlayCircleFilledIcon from "@mui/icons-material/PlayCircleFilled";
|
|
16
|
+
import "./node.scss";
|
|
17
|
+
import { FuncNodesReactFlowZustandInterface } from "../../state/fnrfzst";
|
|
18
|
+
import { FuncNodesContext } from "..";
|
|
19
|
+
import get_rendertype from "../datarenderer";
|
|
20
|
+
import CustomColorPicker, { HSLColorPicker } from "../utils/colorpicker";
|
|
21
|
+
import { NodeInput, NodeOutput } from "./io";
|
|
22
|
+
import { PreviewHandleDataRendererForIo } from "./io/handle_renderer";
|
|
23
|
+
import CustomDialog from "../dialog";
|
|
24
|
+
interface NodeHeaderProps {
|
|
25
|
+
node_data: NodeType;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const NodeHeader = ({ node_data }: NodeHeaderProps) => {
|
|
29
|
+
const fnrf_zst: FuncNodesReactFlowZustandInterface =
|
|
30
|
+
useContext(FuncNodesContext);
|
|
31
|
+
|
|
32
|
+
const clicktrigger = () => {
|
|
33
|
+
fnrf_zst.on_node_action({
|
|
34
|
+
type: "trigger",
|
|
35
|
+
from_remote: false,
|
|
36
|
+
id: node_data.id,
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="nodeheader">
|
|
42
|
+
<div className="nodeheader_element">
|
|
43
|
+
<PlayCircleFilledIcon
|
|
44
|
+
fontSize="inherit"
|
|
45
|
+
className="triggerbutton"
|
|
46
|
+
onClick={clicktrigger}
|
|
47
|
+
/>
|
|
48
|
+
<LanIcon fontSize="inherit" />
|
|
49
|
+
</div>
|
|
50
|
+
<div className="nodeheader_element nodeheader_title">
|
|
51
|
+
<div className="nodeheader_title_text">{node_data.node_name}</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div className="nodeheader_element">
|
|
54
|
+
<ExpandLessIcon fontSize="inherit" />
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
interface NodeBodyProps {
|
|
61
|
+
node_data: NodeType;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const NodeDataRenderer = ({ node_data }: { node_data: NodeType }) => {
|
|
65
|
+
const src = node_data.render_options?.data?.src;
|
|
66
|
+
|
|
67
|
+
const io: IOType | undefined = node_data.render_options?.data?.src
|
|
68
|
+
? node_data.io[node_data.render_options?.data?.src]
|
|
69
|
+
: undefined;
|
|
70
|
+
|
|
71
|
+
const pvhandle = io && PreviewHandleDataRendererForIo(io);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div className="nodrag">
|
|
75
|
+
{pvhandle && (
|
|
76
|
+
<CustomDialog
|
|
77
|
+
trigger={<div>{pvhandle({ io })}</div>}
|
|
78
|
+
onOpenChange={(open: boolean) => {
|
|
79
|
+
if (open) {
|
|
80
|
+
io?.try_get_full_value();
|
|
81
|
+
}
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
{pvhandle({ io })}
|
|
85
|
+
</CustomDialog>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const NodeBody = ({ node_data }: NodeBodyProps) => {
|
|
92
|
+
const inputs = Object.values(node_data.io).filter((io) => io.is_input);
|
|
93
|
+
const outputs = Object.values(node_data.io).filter((io) => !io.is_input);
|
|
94
|
+
|
|
95
|
+
if (node_data.render_options?.data?.src) {
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div className="nodebody">
|
|
100
|
+
{outputs.map((io) => (
|
|
101
|
+
<NodeOutput key={io.id} io={io} />
|
|
102
|
+
))}
|
|
103
|
+
<NodeDataRenderer node_data={node_data} />
|
|
104
|
+
{inputs.map((io) => (
|
|
105
|
+
<NodeInput key={io.id} io={io} />
|
|
106
|
+
))}
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const NodeName = ({ node_data }: { node_data: NodeType }) => {
|
|
112
|
+
const [name, setName] = useState(node_data.name);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
setName(node_data.name);
|
|
116
|
+
}, [node_data]);
|
|
117
|
+
|
|
118
|
+
const fnrf_zst: FuncNodesReactFlowZustandInterface =
|
|
119
|
+
useContext(FuncNodesContext);
|
|
120
|
+
|
|
121
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
122
|
+
setName(event.target.value);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const finalSetName = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
126
|
+
const new_name = e.target.value;
|
|
127
|
+
fnrf_zst.on_node_action({
|
|
128
|
+
type: "update",
|
|
129
|
+
from_remote: false,
|
|
130
|
+
id: node_data.id,
|
|
131
|
+
node: { name: new_name },
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
return (
|
|
135
|
+
<input
|
|
136
|
+
className="nodename_input"
|
|
137
|
+
value={name}
|
|
138
|
+
onChange={handleChange}
|
|
139
|
+
onBlur={finalSetName}
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const NodeFooter = ({ node_data }: { node_data: NodeType }) => {
|
|
145
|
+
return (
|
|
146
|
+
<div className="nodefooter">
|
|
147
|
+
{node_data.error && <div className="nodeerror">{node_data.error}</div>}
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* A generic function to deeply merge two objects of type T.
|
|
153
|
+
*
|
|
154
|
+
* @param {T} target - The target object to be merged.
|
|
155
|
+
* @param {DeepPartial<T>} source - The source object to merge into the target. All properties of this object are optional.
|
|
156
|
+
*
|
|
157
|
+
* @returns {Object} An object containing the merged object (new_obj) and a boolean indicating if there was a change (change).
|
|
158
|
+
*
|
|
159
|
+
* @throws {Type 'string' cannot be used to index type 'T'} This error is ignored using the @ts-ignore directive because we are dynamically accessing properties of a generic type T.
|
|
160
|
+
*/
|
|
161
|
+
const DefaultNode = ({ data }: { data: { UseNodeStore: NodeStore } }) => {
|
|
162
|
+
// Use the NodeStore to get the data for the node.
|
|
163
|
+
const storedata = data.UseNodeStore();
|
|
164
|
+
|
|
165
|
+
const collapsed = storedata.frontend.collapsed || true;
|
|
166
|
+
|
|
167
|
+
const [visualTrigger, setVisualTrigger] = useState(false);
|
|
168
|
+
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
let timeoutId: NodeJS.Timeout;
|
|
171
|
+
// When in_trigger becomes true, set visualTrigger to true immediately
|
|
172
|
+
if (storedata.in_trigger) {
|
|
173
|
+
setVisualTrigger(true);
|
|
174
|
+
} else if (visualTrigger) {
|
|
175
|
+
// When in_trigger becomes false, wait for a minimum duration before setting visualTrigger to false
|
|
176
|
+
timeoutId = setTimeout(() => setVisualTrigger(false), 200); // 50ms or any other duration you see fit
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return () => clearTimeout(timeoutId); // Cleanup timeout on component unmount or state changes
|
|
180
|
+
}, [storedata.in_trigger, visualTrigger]);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<>
|
|
184
|
+
{/* <NodeResizeControl
|
|
185
|
+
minWidth={100}
|
|
186
|
+
minHeight={100}
|
|
187
|
+
className="noderesizecontrol"
|
|
188
|
+
>
|
|
189
|
+
<ExpandIcon fontSize="inherit" className="noderesizeicon" />
|
|
190
|
+
</NodeResizeControl> */}
|
|
191
|
+
<div
|
|
192
|
+
className={
|
|
193
|
+
"innernode" +
|
|
194
|
+
(visualTrigger ? " intrigger" : "") +
|
|
195
|
+
(storedata.error ? " error" : "")
|
|
196
|
+
}
|
|
197
|
+
>
|
|
198
|
+
<NodeHeader node_data={storedata} />
|
|
199
|
+
<NodeName node_data={storedata} />
|
|
200
|
+
<NodeBody node_data={storedata} />
|
|
201
|
+
<NodeFooter node_data={storedata} />
|
|
202
|
+
</div>
|
|
203
|
+
</>
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export default DefaultNode;
|
|
208
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.context-menu {
|
|
2
|
+
background: white;
|
|
3
|
+
border-style: solid;
|
|
4
|
+
box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%);
|
|
5
|
+
position: absolute;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.context-menu button {
|
|
9
|
+
border: none;
|
|
10
|
+
display: block;
|
|
11
|
+
padding: 0.5em;
|
|
12
|
+
text-align: left;
|
|
13
|
+
width: 100%;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.context-menu button:hover {
|
|
17
|
+
background: white;
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
.colorspace {
|
|
2
|
+
margin: 0.2rem;
|
|
3
|
+
&_title {
|
|
4
|
+
font-size: 0.7em;
|
|
5
|
+
font-weight: bold;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
10
|
+
|
|
11
|
+
& label {
|
|
12
|
+
font-size: 0.7rem;
|
|
13
|
+
}
|
|
14
|
+
& input {
|
|
15
|
+
font-size: 0.7rem;
|
|
16
|
+
max-height: 0.7rem;
|
|
17
|
+
}
|
|
18
|
+
& input[type="range"] {
|
|
19
|
+
width: 100%;
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
-webkit-appearance: none;
|
|
23
|
+
background-color: #666;
|
|
24
|
+
height: 0.7rem;
|
|
25
|
+
border-radius: 5px;
|
|
26
|
+
|
|
27
|
+
&::-webkit-slider-thumb,
|
|
28
|
+
&::-webkit-range-thumb,
|
|
29
|
+
&::-moz-range-thumb {
|
|
30
|
+
width: 0.7rem;
|
|
31
|
+
height: 0.7rem;
|
|
32
|
+
background-color: #cc1c1c;
|
|
33
|
+
border-radius: 50%;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|