@kestra-io/ui-libs 0.0.2 → 0.0.4

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.
@@ -1,390 +0,0 @@
1
- import YamlUtils from "./YamlUtils.js";
2
- import {MarkerType, Position, useVueFlow} from "@vue-flow/core";
3
- import dagre from "dagre";
4
- import Utils from "./Utils.js";
5
-
6
- // Vue flow methods to interact with Graph
7
- const {
8
- id,
9
- getNodes,
10
- removeNodes,
11
- getEdges,
12
- removeEdges,
13
- fitView,
14
- getElements,
15
- removeSelectedElements,
16
- } = useVueFlow();
17
-
18
- export default class GraphUtils {
19
- // Graph generation methods
20
- static cleanGraph = () => {
21
- removeEdges(getEdges.value)
22
- removeNodes(getNodes.value)
23
- removeSelectedElements(getElements.value)
24
- }
25
-
26
- static generateGraph = (flowGraph, source, isHorizontal, isReadOnly, flowData) => {
27
- this.cleanGraph();
28
- // next tick
29
- emit("loading", true);
30
- try {
31
- const elements = [];
32
- if (!flowGraph || !flowHaveTasks(source)) {
33
- elements.value.push({
34
- id: "start",
35
- label: "",
36
- type: "dot",
37
- position: {x: 0, y: 0},
38
- style: {
39
- width: "5px",
40
- height: "5px"
41
- },
42
- sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
43
- targetPosition: isHorizontal ? Position.Left : Position.Top,
44
- parentNode: undefined,
45
- draggable: false,
46
- })
47
- elements.value.push({
48
- id: "end",
49
- label: "",
50
- type: "dot",
51
- position: isHorizontal ? {x: 50, y: 0} : {x: 0, y: 50},
52
- style: {
53
- width: "5px",
54
- height: "5px"
55
- },
56
- sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
57
- targetPosition: isHorizontal ? Position.Left : Position.Top,
58
- parentNode: undefined,
59
- draggable: false,
60
- })
61
- elements.value.push({
62
- id: "start|end",
63
- source: "start",
64
- target: "end",
65
- type: "edge",
66
- markerEnd: MarkerType.ArrowClosed,
67
- data: {
68
- edge: {
69
- relation: {
70
- relationType: "SEQUENTIAL"
71
- }
72
- },
73
- isFlowable: false,
74
- initTask: true,
75
- }
76
- })
77
-
78
- emit("loading", false);
79
- return;
80
- }
81
- const dagreGraph = this.generateDagreGraph(flowGraph);
82
- const clusters = {};
83
- for (let cluster of (flowGraph.clusters || [])) {
84
- for (let nodeUid of cluster.nodes) {
85
- clusters[nodeUid] = cluster.cluster;
86
- }
87
-
88
- const dagreNode = dagreGraph.node(cluster.cluster.uid)
89
- const parentNode = cluster.parents ? cluster.parents[cluster.parents.length - 1] : undefined;
90
-
91
- const clusterUid = cluster.cluster.uid;
92
- elements.value.push({
93
- id: clusterUid,
94
- label: clusterUid,
95
- type: "cluster",
96
- parentNode: parentNode,
97
- position: this.getNodePosition(dagreNode, parentNode ? dagreGraph.node(parentNode) : undefined),
98
- style: {
99
- width: clusterUid === "Triggers" && isHorizontal ? "400px" : dagreNode.width + "px",
100
- height: clusterUid === "Triggers" && !isHorizontal ? "250px" : dagreNode.height + "px",
101
- },
102
- })
103
- }
104
-
105
- let disabledLowCode = [];
106
-
107
- for (const node of flowGraph.nodes) {
108
- const dagreNode = dagreGraph.node(node.uid);
109
- let nodeType = "task";
110
- if (node.type.includes("GraphClusterEnd")) {
111
- nodeType = "dot";
112
- } else if (clusters[node.uid] === undefined && node.type.includes("GraphClusterRoot")) {
113
- nodeType = "dot";
114
- } else if (node.type.includes("GraphClusterRoot")) {
115
- nodeType = "dot";
116
- } else if (node.type.includes("GraphTrigger")) {
117
- nodeType = "trigger";
118
- }
119
- // Disable interaction for Dag task
120
- // because our low code editor can not handle it for now
121
- if (this.isTaskNode(node) && node.task.type === "io.kestra.core.tasks.flows.Dag") {
122
- disabledLowCode.push(node.task.id);
123
- YamlUtils.getChildrenTasks(source, node.task.id).forEach(child => {
124
- disabledLowCode.push(child);
125
- })
126
- }
127
-
128
- elements.value.push({
129
- id: node.uid,
130
- label: this.isTaskNode(node) ? node.task.id : "",
131
- type: nodeType,
132
- position: this.getNodePosition(dagreNode, clusters[node.uid] ? dagreGraph.node(clusters[node.uid].uid) : undefined),
133
- style: {
134
- width: this.getNodeWidth(node) + "px",
135
- height: this.getNodeHeight(node) + "px"
136
- },
137
- sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
138
- targetPosition: isHorizontal ? Position.Left : Position.Top,
139
- parentNode: clusters[node.uid] ? clusters[node.uid].uid : undefined,
140
- draggable: nodeType === "task" && !isReadOnly && this.isTaskNode(node) ? !disabledLowCode.includes(node.task.id) : false,
141
- data: {
142
- node: node,
143
- namespace: flowData.namespace,
144
- flowId: flowData.flowId,
145
- revision: flowData.execution ? flowData.execution.flowRevision : undefined,
146
- isFlowable: this.isTaskNode(node) ? (flowGraph && flowGraph.flowables ? flowGraph.flowables : []).includes(node.task.id) : false
147
- },
148
- })
149
- }
150
-
151
- for (const edge of flowGraph.edges) {
152
- elements.value.push({
153
- id: edge.source + "|" + edge.target,
154
- source: edge.source,
155
- target: edge.target,
156
- type: "edge",
157
- markerEnd: MarkerType.ArrowClosed,
158
- data: {
159
- edge: edge,
160
- haveAdd: complexEdgeHaveAdd(edge),
161
- isFlowable: flowables().includes(edge.source) || flowables().includes(edge.target),
162
- nextTaskId: getNextTaskId(edge.target),
163
- disabled: disabledLowCode.includes(edge.source)
164
- }
165
- })
166
- }
167
- return elements;
168
- } catch (e) {
169
- console.error("Error while creating topology graph: " + e);
170
- } finally {
171
- emit("loading", false);
172
- }
173
- }
174
-
175
- static getFirstTaskId = (source) => {
176
- return YamlUtils.getFirstTask(source);
177
- }
178
-
179
- static getNextTaskId = (target, source, edges) => {
180
- while (YamlUtils.extractTask(source, target) === undefined) {
181
- const edge = edges.find(e => e.source === target)
182
- if (!edge) {
183
- return null
184
- }
185
- target = edge.target
186
- }
187
- return target
188
- }
189
-
190
- static generateDagreGraph = (flowGraph) => {
191
- const dagreGraph = new dagre.graphlib.Graph({compound: true})
192
- dagreGraph.setDefaultEdgeLabel(() => ({}))
193
- dagreGraph.setGraph({rankdir: isHorizontal.value ? "LR" : "TB"})
194
-
195
- for (const node of flowGraph.nodes) {
196
- dagreGraph.setNode(node.uid, {
197
- width: this.getNodeWidth(node),
198
- height: this.getNodeHeight(node)
199
- })
200
- }
201
-
202
- for (const edge of flowGraph.edges) {
203
- dagreGraph.setEdge(edge.source, edge.target)
204
- }
205
-
206
- for (let cluster of (flowGraph.clusters || [])) {
207
- dagreGraph.setNode(cluster.cluster.uid, {clusterLabelPos: "top"});
208
-
209
- if (cluster.parents) {
210
- dagreGraph.setParent(cluster.cluster.uid, cluster.parents[cluster.parents.length - 1]);
211
- }
212
-
213
- for (let node of (cluster.nodes || [])) {
214
- dagreGraph.setParent(node, cluster.cluster.uid)
215
- }
216
- }
217
- dagre.layout(dagreGraph)
218
- return dagreGraph;
219
- }
220
-
221
- static getNodePosition = (n, parent) => {
222
- const position = {x: n.x - n.width / 2, y: n.y - n.height / 2};
223
-
224
- // bug with parent node,
225
- if (parent) {
226
- const parentPosition = this.getNodePosition(parent);
227
- position.x = position.x - parentPosition.x;
228
- position.y = position.y - parentPosition.y;
229
- }
230
- return position;
231
- };
232
-
233
- static getNodeWidth = (node) => {
234
- return this.isTaskNode(node) || this.isTriggerNode(node) ? 202 : 5;
235
- };
236
-
237
- static getNodeHeight = (node, isHorizontal) => {
238
- return this.isTaskNode(node) || this.isTriggerNode(node) ? 55 : (isHorizontal ? 55 : 5);
239
- };
240
-
241
- static isTaskNode = (node) => {
242
- return node.task !== undefined && (node.type === "io.kestra.core.models.hierarchies.GraphTask" || node.type === "io.kestra.core.models.hierarchies.GraphClusterRoot")
243
- };
244
-
245
- static isTriggerNode = (node) => {
246
- return node.trigger !== undefined && (node.type === "io.kestra.core.models.hierarchies.GraphTrigger");
247
- }
248
-
249
-
250
- static complexEdgeHaveAdd = (edge) => {
251
- // Check if edge is an ending flowable
252
- // If true, enable add button to add a task
253
- // under the flowable task
254
- const isEndtoEndEdge = edge.source.includes("_end") && edge.target.includes("_end")
255
- if (isEndtoEndEdge) {
256
- // Cluster uid contains the flowable task id
257
- // So we look for the cluster having this end edge
258
- // to return his flowable id
259
- return [getClusterTaskIdWithEndNodeUid(edge.source), "after"];
260
- }
261
- if (this.isLinkToFirstFlowableTask(edge)) {
262
- return [this.getFirstTaskId(), "before"];
263
- }
264
-
265
- return undefined;
266
- }
267
-
268
- static getClusterTaskIdWithEndNodeUid = (flowGraph, nodeUid) => {
269
- const cluster = flowGraph.clusters.find(cluster => cluster.end === nodeUid);
270
- if (cluster) {
271
- return Utils.splitFirst(cluster.cluster.uid, "cluster_");
272
- }
273
-
274
- return undefined;
275
- }
276
-
277
- static isLinkToFirstFlowableTask = (edge) => {
278
- const firstTaskId = this.getFirstTaskId();
279
-
280
- return flowables().includes(firstTaskId) && edge.target === firstTaskId;
281
- }
282
-
283
- // Graph interactions functions
284
- static onMouseOver = (node) => {
285
- if (!dragging.value) {
286
- linkedElements(id, node.uid).forEach((n) => {
287
- if (n.type === "task") {
288
- n.style = {...n.style, outline: "0.5px solid " + cssVariable("--bs-yellow")}
289
- }
290
- });
291
- }
292
-
293
- }
294
-
295
- static onMouseLeave = () => {
296
- resetNodesStyle();
297
- }
298
-
299
- static resetNodesStyle = () => {
300
- getNodes.value.filter(n => n.type === "task" || n.type === " trigger")
301
- .forEach(n => {
302
- n.style = {...n.style, opacity: "1", outline: "none"}
303
- })
304
- }
305
-
306
- //onNodeDragStart((e) => {
307
- // dragging.value = true;
308
- // resetNodesStyle();
309
- // e.node.style = {...e.node.style, zIndex: 1976}
310
- // lastPosition.value = e.node.position;
311
- // })
312
- //
313
- // onNodeDragStop((e) => {
314
- // dragging.value = false;
315
- // if (checkIntersections(e.intersections, e.node) === null) {
316
- // const taskNode1 = e.node;
317
- // // check multiple intersection with task
318
- // const taskNode2 = e.intersections.find(n => n.type === "task");
319
- // if (taskNode2) {
320
- // try {
321
- // emit("on-edit", YamlUtils.swapTasks(props.source, taskNode1.id, taskNode2.id))
322
- // } catch (e) {
323
- // store.dispatch("core/showMessage", {
324
- // variant: "error",
325
- // title: t("cannot swap tasks"),
326
- // message: t(e.message, e.messageOptions)
327
- // });
328
- // taskNode1.position = lastPosition.value;
329
- // }
330
- // } else {
331
- // taskNode1.position = lastPosition.value;
332
- // }
333
- // } else {
334
- // e.node.position = lastPosition.value;
335
- // }
336
- // resetNodesStyle();
337
- // e.node.style = {...e.node.style, zIndex: 1}
338
- // lastPosition.value = null;
339
- // })
340
- //
341
- // onNodeDrag((e) => {
342
- // resetNodesStyle();
343
- // getNodes.value.filter(n => n.id !== e.node.id).forEach(n => {
344
- // if (n.type === "trigger" || (n.type === "task" && YamlUtils.isParentChildrenRelation(props.source, n.id, e.node.id))) {
345
- // n.style = {...n.style, opacity: "0.5"}
346
- // } else {
347
- // n.style = {...n.style, opacity: "1"}
348
- // }
349
- // })
350
- // if (!checkIntersections(e.intersections, e.node) && e.intersections.filter(n => n.type === "task").length === 1) {
351
- // e.intersections.forEach(n => {
352
- // if (n.type === "task") {
353
- // n.style = {...n.style, outline: "0.5px solid " + cssVariable("--bs-primary")}
354
- // }
355
- // })
356
- // e.node.style = {...e.node.style, outline: "0.5px solid " + cssVariable("--bs-primary")}
357
- // }
358
- // })
359
-
360
- static checkIntersections = (intersections, node) => {
361
- const tasksMeet = intersections.filter(n => n.type === "task").map(n => n.id);
362
- if (tasksMeet.length > 1) {
363
- return "toomuchtaskerror";
364
- }
365
- if (tasksMeet.length === 1 && YamlUtils.isParentChildrenRelation(props.source, tasksMeet[0], node.id)) {
366
- return "parentchildrenerror";
367
- }
368
- if (intersections.filter(n => n.type === "trigger").length > 0) {
369
- return "triggererror";
370
- }
371
- return null;
372
- }
373
-
374
- static toggleOrientation = (isHorizontal) => {
375
- localStorage.setItem(
376
- "topology-orientation",
377
- localStorage.getItem("topology-orientation") !== "0" ? "0" : "1"
378
- );
379
- isHorizontal = localStorage.getItem("topology-orientation") === "1";
380
- // emit an event
381
- this.generateGraph();
382
- fitView();
383
- }
384
-
385
- // Flow check functions
386
- static flowHaveTasks = (source) => {
387
- return source ? YamlUtils.flowHaveTasks(source) : false;
388
- }
389
-
390
- }