@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,342 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import * as Popover from "@radix-ui/react-popover";
|
|
3
|
+
import convert from "color-convert";
|
|
4
|
+
import "./colorpicker.scss";
|
|
5
|
+
|
|
6
|
+
const parse_colorstring = (colorstring: string): [string, any[]] => {
|
|
7
|
+
// This is a simplified parser and may need adjustments to handle all CSS color formats robustly
|
|
8
|
+
if (colorstring.startsWith("#")) {
|
|
9
|
+
return ["hex", [colorstring]];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let matches = colorstring.match(/^(\w+)\(([^)]+)\)$/);
|
|
13
|
+
if (!matches) return ["keyword", [colorstring]];
|
|
14
|
+
|
|
15
|
+
const colortype = matches[1];
|
|
16
|
+
const colordata = matches[2].split(",").map((n) => parseInt(n.trim()));
|
|
17
|
+
|
|
18
|
+
return [colortype, colordata];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const create_color_converter = (
|
|
22
|
+
type: string,
|
|
23
|
+
data: any[]
|
|
24
|
+
): { [key: string]: () => number[] | string } => {
|
|
25
|
+
if (!Array.isArray(data)) data = [data];
|
|
26
|
+
if (data[0] === undefined || data[0] === null)
|
|
27
|
+
return create_color_converter("rgb", [0, 0, 0]);
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
const source = convert[type];
|
|
30
|
+
if (!source) throw new Error("Unsupported color type: " + type);
|
|
31
|
+
|
|
32
|
+
source[type] = () => data;
|
|
33
|
+
|
|
34
|
+
const checkrgb = source.rgb(data);
|
|
35
|
+
if (!Array.isArray(checkrgb)) return create_color_converter("rgb", [0, 0, 0]);
|
|
36
|
+
if (checkrgb[0] === undefined || checkrgb[0] === null)
|
|
37
|
+
return create_color_converter("rgb", [0, 0, 0]);
|
|
38
|
+
|
|
39
|
+
const checkhsl = source.hsl(data);
|
|
40
|
+
if (!Array.isArray(checkhsl)) return create_color_converter("rgb", [0, 0, 0]);
|
|
41
|
+
if (checkhsl[0] === undefined || checkhsl[0] === null)
|
|
42
|
+
return create_color_converter("rgb", [0, 0, 0]);
|
|
43
|
+
|
|
44
|
+
const converter: { [key: string]: () => number[] | string } = {};
|
|
45
|
+
|
|
46
|
+
Object.keys(source).forEach((key) => {
|
|
47
|
+
const entry = source[key];
|
|
48
|
+
//check if entry is a function
|
|
49
|
+
if (typeof entry === "function") {
|
|
50
|
+
converter[key] = () => entry.apply(null, data);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return converter;
|
|
55
|
+
};
|
|
56
|
+
const create_color_converter_from_string = (
|
|
57
|
+
colorstring: string
|
|
58
|
+
): { [key: string]: () => number[] | string } => {
|
|
59
|
+
const [colortype, colordata] = parse_colorstring(colorstring);
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
if (!colortype || !convert[colortype]) {
|
|
62
|
+
console.error("Unsupported color type or invalid color string");
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return create_color_converter(colortype, colordata);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const HSLColorPicker = ({
|
|
70
|
+
onChange,
|
|
71
|
+
colorconverter,
|
|
72
|
+
}: {
|
|
73
|
+
onChange: (colorconverter: {
|
|
74
|
+
[key: string]: () => number[] | string;
|
|
75
|
+
}) => void;
|
|
76
|
+
colorconverter: { [key: string]: () => number[] | string };
|
|
77
|
+
}) => {
|
|
78
|
+
const [converter, setConverter] = useState(colorconverter);
|
|
79
|
+
const hsl = converter.hsl() as number[];
|
|
80
|
+
const rgb = converter.rgb() as number[];
|
|
81
|
+
const hsv = converter.hsv() as number[];
|
|
82
|
+
const hex = converter.hex() as string;
|
|
83
|
+
|
|
84
|
+
const colorStyle = {
|
|
85
|
+
backgroundColor: `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`,
|
|
86
|
+
padding: "10px",
|
|
87
|
+
margin: "10px 0",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
let g = "linear-gradient(to right";
|
|
91
|
+
for (let i = 0; i <= 360; i += 10) {
|
|
92
|
+
g += `, hsl(${i}, 100%, 50%)`;
|
|
93
|
+
}
|
|
94
|
+
g += ")";
|
|
95
|
+
const hueStyle = {
|
|
96
|
+
backgroundImage: "unset",
|
|
97
|
+
WebkitAppearance: "none" as any,
|
|
98
|
+
background: g,
|
|
99
|
+
height: 10,
|
|
100
|
+
borderRadius: 5,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div style={{ backgroundColor: "white" }}>
|
|
105
|
+
<div style={colorStyle}>Color Preview</div>
|
|
106
|
+
<div className="colorspace">
|
|
107
|
+
<div className="colorspace_title">RGB</div>
|
|
108
|
+
<div></div>
|
|
109
|
+
|
|
110
|
+
<label>Red</label>
|
|
111
|
+
<input
|
|
112
|
+
type="range"
|
|
113
|
+
min="0"
|
|
114
|
+
max="255"
|
|
115
|
+
value={rgb[0]}
|
|
116
|
+
onChange={(e) => {
|
|
117
|
+
const newrgb = [parseInt(e.target.value), rgb[1], rgb[2]];
|
|
118
|
+
const newconverter = create_color_converter("rgb", newrgb);
|
|
119
|
+
setConverter(newconverter);
|
|
120
|
+
onChange(newconverter);
|
|
121
|
+
}}
|
|
122
|
+
style={{ background: `linear-gradient(to right, #000, #f00)` }}
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
<label>Green</label>
|
|
126
|
+
<input
|
|
127
|
+
type="range"
|
|
128
|
+
min="0"
|
|
129
|
+
max="255"
|
|
130
|
+
value={rgb[1]}
|
|
131
|
+
onChange={(e) => {
|
|
132
|
+
const newrgb = [rgb[0], parseInt(e.target.value), rgb[2]];
|
|
133
|
+
const newconverter = create_color_converter("rgb", newrgb);
|
|
134
|
+
setConverter(newconverter);
|
|
135
|
+
onChange(newconverter);
|
|
136
|
+
}}
|
|
137
|
+
style={{ background: `linear-gradient(to right, #000, #0f0)` }}
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
<label>Blue</label>
|
|
141
|
+
<input
|
|
142
|
+
type="range"
|
|
143
|
+
min="0"
|
|
144
|
+
max="255"
|
|
145
|
+
value={rgb[2]}
|
|
146
|
+
onChange={(e) => {
|
|
147
|
+
const newrgb = [rgb[0], rgb[1], parseInt(e.target.value)];
|
|
148
|
+
const newconverter = create_color_converter("rgb", newrgb);
|
|
149
|
+
setConverter(newconverter);
|
|
150
|
+
onChange(newconverter);
|
|
151
|
+
}}
|
|
152
|
+
style={{ background: `linear-gradient(to right, #000, #00f)` }}
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
<div className="colorspace">
|
|
156
|
+
<div className="colorspace_title">HSL</div>
|
|
157
|
+
<div></div>
|
|
158
|
+
|
|
159
|
+
<label>Hue</label>
|
|
160
|
+
<input
|
|
161
|
+
type="range"
|
|
162
|
+
min="0"
|
|
163
|
+
max="360"
|
|
164
|
+
value={hsl[0]}
|
|
165
|
+
onChange={(e) => {
|
|
166
|
+
const newhsl = [parseInt(e.target.value), hsl[1], hsl[2]];
|
|
167
|
+
const newconverter = create_color_converter("hsl", newhsl);
|
|
168
|
+
setConverter(newconverter);
|
|
169
|
+
onChange(newconverter);
|
|
170
|
+
}}
|
|
171
|
+
style={{
|
|
172
|
+
background: `linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)`,
|
|
173
|
+
}}
|
|
174
|
+
/>
|
|
175
|
+
|
|
176
|
+
<label>Saturation</label>
|
|
177
|
+
<input
|
|
178
|
+
type="range"
|
|
179
|
+
min="0"
|
|
180
|
+
max="100"
|
|
181
|
+
value={hsl[1]}
|
|
182
|
+
onChange={(e) => {
|
|
183
|
+
const newhsl = [hsl[0], parseInt(e.target.value), hsl[2]];
|
|
184
|
+
const newconverter = create_color_converter("hsl", newhsl);
|
|
185
|
+
setConverter(newconverter);
|
|
186
|
+
onChange(newconverter);
|
|
187
|
+
}}
|
|
188
|
+
style={{
|
|
189
|
+
background: `linear-gradient(to right, #fff, hsl(${hsl[0]}, 100%, 50%))`,
|
|
190
|
+
}}
|
|
191
|
+
/>
|
|
192
|
+
|
|
193
|
+
<label>Lightness</label>
|
|
194
|
+
|
|
195
|
+
<input
|
|
196
|
+
type="range"
|
|
197
|
+
min="0"
|
|
198
|
+
max="100"
|
|
199
|
+
value={hsl[2]}
|
|
200
|
+
onChange={(e) => {
|
|
201
|
+
const newhsl = [hsl[0], hsl[1], parseInt(e.target.value)];
|
|
202
|
+
const newconverter = create_color_converter("hsl", newhsl);
|
|
203
|
+
setConverter(newconverter);
|
|
204
|
+
onChange(newconverter);
|
|
205
|
+
}}
|
|
206
|
+
style={{
|
|
207
|
+
background: `linear-gradient(to right, #000, hsl(${hsl[0]}, 100%, 50%), #fff)`,
|
|
208
|
+
}}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<div className="colorspace">
|
|
213
|
+
<div className="colorspace_title">HSV</div>
|
|
214
|
+
<div></div>
|
|
215
|
+
|
|
216
|
+
<label>Hue</label>
|
|
217
|
+
<input
|
|
218
|
+
type="range"
|
|
219
|
+
min="0"
|
|
220
|
+
max="360"
|
|
221
|
+
value={hsv[0]}
|
|
222
|
+
onChange={(e) => {
|
|
223
|
+
const newhsv = [parseInt(e.target.value), hsv[1], hsv[2]];
|
|
224
|
+
const newconverter = create_color_converter("hsv", newhsv);
|
|
225
|
+
setConverter(newconverter);
|
|
226
|
+
onChange(newconverter);
|
|
227
|
+
}}
|
|
228
|
+
style={{
|
|
229
|
+
background: `linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)`,
|
|
230
|
+
}}
|
|
231
|
+
/>
|
|
232
|
+
|
|
233
|
+
<label>Saturation</label>
|
|
234
|
+
<input
|
|
235
|
+
type="range"
|
|
236
|
+
min="0"
|
|
237
|
+
max="100"
|
|
238
|
+
value={hsv[1]}
|
|
239
|
+
onChange={(e) => {
|
|
240
|
+
const newhsv = [hsv[0], parseInt(e.target.value), hsv[2]];
|
|
241
|
+
const newconverter = create_color_converter("hsv", newhsv);
|
|
242
|
+
setConverter(newconverter);
|
|
243
|
+
onChange(newconverter);
|
|
244
|
+
}}
|
|
245
|
+
style={{
|
|
246
|
+
background: `linear-gradient(to right, #fff, hsl(${hsl[0]}, 100%, 50%))`,
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
|
|
250
|
+
<label>Value</label>
|
|
251
|
+
<input
|
|
252
|
+
type="range"
|
|
253
|
+
min="0"
|
|
254
|
+
max="100"
|
|
255
|
+
value={hsv[2]}
|
|
256
|
+
onChange={(e) => {
|
|
257
|
+
const newhsv = [hsv[0], hsv[1], parseInt(e.target.value)];
|
|
258
|
+
const newconverter = create_color_converter("hsv", newhsv);
|
|
259
|
+
setConverter(newconverter);
|
|
260
|
+
onChange(newconverter);
|
|
261
|
+
}}
|
|
262
|
+
style={{
|
|
263
|
+
background: `linear-gradient(to right, #000, hsl(${hsl[0]}, 100%, 50%))`,
|
|
264
|
+
}}
|
|
265
|
+
/>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<div className="colorspace">
|
|
269
|
+
<div className="colorspace_title">HEX</div>
|
|
270
|
+
<div></div>
|
|
271
|
+
|
|
272
|
+
<input
|
|
273
|
+
type="text"
|
|
274
|
+
value={hex}
|
|
275
|
+
onChange={(e) => {
|
|
276
|
+
const newconverter = create_color_converter("hex", [
|
|
277
|
+
e.target.value,
|
|
278
|
+
]);
|
|
279
|
+
setConverter(newconverter);
|
|
280
|
+
onChange(newconverter);
|
|
281
|
+
}}
|
|
282
|
+
/>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const CustomColorPicker = ({
|
|
289
|
+
inicolordata,
|
|
290
|
+
inicolorspace = "hex",
|
|
291
|
+
onChange,
|
|
292
|
+
}: {
|
|
293
|
+
inicolordata?: number[] | string | string[];
|
|
294
|
+
inicolorspace?: string;
|
|
295
|
+
onChange?: (colorconverter: {
|
|
296
|
+
[key: string]: () => number[] | string;
|
|
297
|
+
}) => void;
|
|
298
|
+
}) => {
|
|
299
|
+
if (inicolordata === undefined) {
|
|
300
|
+
inicolordata = [0, 0, 0];
|
|
301
|
+
inicolorspace = "rgb";
|
|
302
|
+
}
|
|
303
|
+
if (!Array.isArray(inicolordata)) inicolordata = [inicolordata];
|
|
304
|
+
|
|
305
|
+
let iniconverter = create_color_converter(inicolorspace, inicolordata);
|
|
306
|
+
|
|
307
|
+
if (iniconverter.rgb() === undefined)
|
|
308
|
+
iniconverter = create_color_converter("rgb", [0, 0, 0]);
|
|
309
|
+
const [color, setColor] = useState(iniconverter);
|
|
310
|
+
|
|
311
|
+
const innerSetColor = (colorconverter: {
|
|
312
|
+
[key: string]: () => number[] | string;
|
|
313
|
+
}) => {
|
|
314
|
+
setColor(colorconverter);
|
|
315
|
+
if (onChange) onChange(colorconverter);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const style = {
|
|
319
|
+
background: "#" + color.hex(),
|
|
320
|
+
borderRadius: "0.3rem",
|
|
321
|
+
width: "2rem",
|
|
322
|
+
height: "1rem",
|
|
323
|
+
};
|
|
324
|
+
return (
|
|
325
|
+
<Popover.Root>
|
|
326
|
+
<Popover.Trigger asChild>
|
|
327
|
+
<button style={style}></button>
|
|
328
|
+
</Popover.Trigger>
|
|
329
|
+
<Popover.Portal>
|
|
330
|
+
<Popover.Content side="left">
|
|
331
|
+
<HSLColorPicker
|
|
332
|
+
onChange={innerSetColor}
|
|
333
|
+
colorconverter={color}
|
|
334
|
+
></HSLColorPicker>
|
|
335
|
+
</Popover.Content>
|
|
336
|
+
</Popover.Portal>
|
|
337
|
+
</Popover.Root>
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
export default CustomColorPicker;
|
|
342
|
+
export { HSLColorPicker };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JsonView,
|
|
3
|
+
allExpanded,
|
|
4
|
+
darkStyles,
|
|
5
|
+
defaultStyles,
|
|
6
|
+
collapseAllNested,
|
|
7
|
+
} from "react-json-view-lite";
|
|
8
|
+
import "react-json-view-lite/dist/index.css";
|
|
9
|
+
const JSONDataDisplay = ({ data }: { data: any }) => {
|
|
10
|
+
return (
|
|
11
|
+
<JsonView
|
|
12
|
+
data={data}
|
|
13
|
+
style={darkStyles}
|
|
14
|
+
shouldExpandNode={collapseAllNested}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default JSONDataDisplay;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.tablecontainer {
|
|
2
|
+
overflow: auto;
|
|
3
|
+
background-color: white;
|
|
4
|
+
}
|
|
5
|
+
.tableHead {
|
|
6
|
+
color: var(--funheadercolor) !important;
|
|
7
|
+
background-color: var(--funcnodesbackground1);
|
|
8
|
+
font-weight: bold !important;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.tableheadercolor {
|
|
12
|
+
color: inherit !important;
|
|
13
|
+
font-family: inherit !important;
|
|
14
|
+
font-weight: inherit !important;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.indexcol {
|
|
18
|
+
background-color: var(--funcnodesbackground2);
|
|
19
|
+
color: var(--funheadercolor) !important;
|
|
20
|
+
font-family: inherit !important;
|
|
21
|
+
font-weight: inherit !important;
|
|
22
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import Table from "@mui/material/Table";
|
|
2
|
+
import TableBody from "@mui/material/TableBody";
|
|
3
|
+
import TableCell, { SortDirection } from "@mui/material/TableCell";
|
|
4
|
+
import TableContainer from "@mui/material/TableContainer";
|
|
5
|
+
import TableHead from "@mui/material/TableHead";
|
|
6
|
+
import TableRow from "@mui/material/TableRow";
|
|
7
|
+
import TableSortLabel from "@mui/material/TableSortLabel";
|
|
8
|
+
|
|
9
|
+
import { useMemo, useState } from "react";
|
|
10
|
+
|
|
11
|
+
import "./table.scss";
|
|
12
|
+
|
|
13
|
+
interface TableData {
|
|
14
|
+
columns: string[];
|
|
15
|
+
index: string[];
|
|
16
|
+
data: any[][];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type comparertype = (a: any, b: any) => 1 | -1 | 0;
|
|
20
|
+
type _SD = "asc" | "desc";
|
|
21
|
+
|
|
22
|
+
interface TransFormedTableData {
|
|
23
|
+
header: string[];
|
|
24
|
+
rows: any[][];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const transform_table_data = (data: TableData): TransFormedTableData => {
|
|
28
|
+
const rows = [];
|
|
29
|
+
if (data === undefined) {
|
|
30
|
+
// return empty table if data is undefined
|
|
31
|
+
return {
|
|
32
|
+
header: [],
|
|
33
|
+
rows: [],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (data.data === undefined) {
|
|
37
|
+
// if data.data is undefined, make it empty
|
|
38
|
+
data.data = [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (data.columns === undefined) {
|
|
42
|
+
// if np columns are defined, create columns based on the first row
|
|
43
|
+
|
|
44
|
+
// if data is empty, there are no columns
|
|
45
|
+
if (data.data.length === 0) {
|
|
46
|
+
data.columns = [];
|
|
47
|
+
} else {
|
|
48
|
+
// create columns based on the first row
|
|
49
|
+
data.columns = data.data[0].map((_, i) => `col${i}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (data.index === undefined) {
|
|
53
|
+
// if no index is defined, create index based on the number of rows
|
|
54
|
+
data.index = data.data.map((_, i) => `row${i}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < data.index.length; i++) {
|
|
58
|
+
const row = [data.index[i]];
|
|
59
|
+
for (let j = 0; j < data.columns.length; j++) {
|
|
60
|
+
row.push(data.data[i][j]);
|
|
61
|
+
}
|
|
62
|
+
rows.push(row);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
header: ["index", ...data.columns],
|
|
66
|
+
rows: rows,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function SortableTable({ tabledata }: { tabledata: TableData }) {
|
|
71
|
+
// State to manage the sorted column and direction
|
|
72
|
+
const transformed_table_data: TransFormedTableData = useMemo(
|
|
73
|
+
() => transform_table_data(tabledata),
|
|
74
|
+
[tabledata]
|
|
75
|
+
);
|
|
76
|
+
const [orderDirection, setOrderDirection] = useState<_SD>("asc");
|
|
77
|
+
const [orderBy, setOrderBy] = useState("index");
|
|
78
|
+
|
|
79
|
+
let order_by_index = transformed_table_data.header.indexOf(orderBy);
|
|
80
|
+
if (order_by_index === -1) {
|
|
81
|
+
order_by_index = 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Function to handle sorting
|
|
85
|
+
const handleSort = (column: string) => {
|
|
86
|
+
const isAsc = orderBy === column && orderDirection === "asc";
|
|
87
|
+
setOrderDirection(isAsc ? "desc" : "asc");
|
|
88
|
+
setOrderBy(column);
|
|
89
|
+
};
|
|
90
|
+
// Function to sort data
|
|
91
|
+
const sortData = (data: any[][], comparator: comparertype) => {
|
|
92
|
+
const stabilizedThis: [any[], number][] = data.map((el, index) => [
|
|
93
|
+
el,
|
|
94
|
+
index,
|
|
95
|
+
]);
|
|
96
|
+
stabilizedThis.sort((a, b) => {
|
|
97
|
+
const order = comparator(a[0], b[0]);
|
|
98
|
+
return order;
|
|
99
|
+
});
|
|
100
|
+
return stabilizedThis.map((el) => el[0]);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Comparator function for sorting
|
|
104
|
+
const getComparator = (order: _SD, orderBy: string): comparertype => {
|
|
105
|
+
return order === "desc"
|
|
106
|
+
? (a, b) => (b[order_by_index] < a[order_by_index] ? -1 : 1)
|
|
107
|
+
: (a, b) => (a[order_by_index] < b[order_by_index] ? -1 : 1);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Sort the rows
|
|
111
|
+
const sortedRows = sortData(
|
|
112
|
+
transformed_table_data.rows,
|
|
113
|
+
getComparator(orderDirection, orderBy)
|
|
114
|
+
);
|
|
115
|
+
return (
|
|
116
|
+
<TableContainer className="tablecontainer">
|
|
117
|
+
<Table size="small">
|
|
118
|
+
<TableHead className="tableHead">
|
|
119
|
+
<TableRow className="tableheadercolor">
|
|
120
|
+
{transformed_table_data.header.map((column) => (
|
|
121
|
+
<TableCell
|
|
122
|
+
key={column}
|
|
123
|
+
sortDirection={orderBy === column ? orderDirection : false}
|
|
124
|
+
className="tableheadercolor"
|
|
125
|
+
>
|
|
126
|
+
<TableSortLabel
|
|
127
|
+
active={orderBy === column}
|
|
128
|
+
direction={orderBy === column ? orderDirection : "asc"}
|
|
129
|
+
onClick={() => handleSort(column)}
|
|
130
|
+
className="tableheadercolor"
|
|
131
|
+
sx={{
|
|
132
|
+
"& .MuiTableSortLabel-icon": {
|
|
133
|
+
color: "inherit !important",
|
|
134
|
+
},
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
{column}
|
|
138
|
+
</TableSortLabel>
|
|
139
|
+
</TableCell>
|
|
140
|
+
))}
|
|
141
|
+
</TableRow>
|
|
142
|
+
</TableHead>
|
|
143
|
+
<TableBody>
|
|
144
|
+
{sortedRows.map((row, index) => (
|
|
145
|
+
<TableRow key={tabledata.index[index]}>
|
|
146
|
+
{row.map((cell, i) => (
|
|
147
|
+
<TableCell key={i} className={i == 0 ? "indexcol" : "datacol"}>
|
|
148
|
+
{cell}
|
|
149
|
+
</TableCell>
|
|
150
|
+
))}
|
|
151
|
+
</TableRow>
|
|
152
|
+
))}
|
|
153
|
+
</TableBody>
|
|
154
|
+
</Table>
|
|
155
|
+
</TableContainer>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export { SortableTable };
|