@kestra-io/ui-libs 0.0.6 → 0.0.7
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/package.json +3 -2
- package/src/components/misc/Duration.vue +1 -1
- package/src/components/misc/ExecutionInformations.vue +3 -2
- package/src/components/misc/TaskIcon.vue +5 -3
- package/src/components/nodes/BasicNode.vue +6 -2
- package/src/components/nodes/CollapsedClusterNode.vue +27 -9
- package/src/components/nodes/TriggerNode.vue +1 -16
- package/src/components/topology/Topology.vue +6 -0
- package/src/scss/app.scss +11 -1
- package/src/utils/VueFlowUtils.js +31 -9
- package/src/utils/YamlUtils.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kestra-io/ui-libs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -27,9 +27,10 @@
|
|
|
27
27
|
"moment": "^2.29.4",
|
|
28
28
|
"vue": "^3.3.4",
|
|
29
29
|
"vue-material-design-icons": "^5.2.0",
|
|
30
|
+
"vue3-popper": "^1.5.0",
|
|
30
31
|
"vuex": "^4.1.0",
|
|
31
32
|
"yaml": "^2.3.1",
|
|
32
|
-
"
|
|
33
|
+
"dagre": "^0.8.5"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@vitejs/plugin-vue": "^4.2.3",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<template #content>
|
|
5
5
|
<span v-for="(history, index) in histories" :key="'tt-' + index">
|
|
6
6
|
<span class="square" :class="squareClass(history.state)" />
|
|
7
|
-
<strong>{{ history.state }}:</strong>
|
|
7
|
+
<strong>{{ history.state }}:</strong>{{ $filters.date(history.date, 'iso') }} <br>
|
|
8
8
|
</span>
|
|
9
9
|
</template>
|
|
10
10
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="btn-group content rounded-1 content-children">
|
|
3
|
-
<span v-if="taskRuns.length > 0">{{ taskRuns.length }} task runs</span>
|
|
3
|
+
<span v-if="taskRuns.length > 0" class="taskRunCount">{{ taskRuns.length }} task runs</span>
|
|
4
|
+
<span v-if="taskRuns.length > 0">|</span>
|
|
4
5
|
<span><duration :histories="histories" /></span>
|
|
5
6
|
</div>
|
|
6
7
|
</template>
|
|
@@ -115,6 +116,6 @@
|
|
|
115
116
|
align-self: stretch;
|
|
116
117
|
pointer-events: none;
|
|
117
118
|
cursor: default;
|
|
118
|
-
font-size: 0.
|
|
119
|
+
font-size: 0.70rem;
|
|
119
120
|
}
|
|
120
121
|
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
:class="[color ? `bg-${color}`: '']"
|
|
3
|
+
:class="[color ? `bg-${color}`: 'bg-white']"
|
|
4
4
|
class="div-icon rounded d-flex justify-content-center"
|
|
5
5
|
data-bs-toggle="tooltip"
|
|
6
6
|
data-bs-placement="top"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
name: "TaskIcon",
|
|
19
19
|
props: {
|
|
20
20
|
customIcon: {
|
|
21
|
-
type:
|
|
21
|
+
type: Object,
|
|
22
22
|
default: undefined
|
|
23
23
|
},
|
|
24
24
|
cls: {
|
|
@@ -73,7 +73,9 @@
|
|
|
73
73
|
margin: 0.2rem;
|
|
74
74
|
width: 25px;
|
|
75
75
|
height: 25px;
|
|
76
|
-
background-color: var(--bs-white);
|
|
77
76
|
border: 0.4px solid var(--bs-border-color);
|
|
78
77
|
}
|
|
78
|
+
.bg-white {
|
|
79
|
+
background-color: var(--bs-white);
|
|
80
|
+
}
|
|
79
81
|
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
:class="[`border-${
|
|
3
|
+
:class="[`border-${borderColor}`]"
|
|
4
4
|
class="node-wrapper rounded-3 border"
|
|
5
5
|
@mouseover="mouseover"
|
|
6
6
|
@mouseleave="mouseleave"
|
|
@@ -151,6 +151,10 @@
|
|
|
151
151
|
},
|
|
152
152
|
computed: {
|
|
153
153
|
...mapState("execution", ["execution"]),
|
|
154
|
+
borderColor() {
|
|
155
|
+
const color = this.data.color ? this.data.color === "default" ? null : this.data.color : null
|
|
156
|
+
return color ? color : this.expandable ? "blue" : null
|
|
157
|
+
},
|
|
154
158
|
EVENTS() {
|
|
155
159
|
return EVENTS
|
|
156
160
|
},
|
|
@@ -223,7 +227,7 @@
|
|
|
223
227
|
}
|
|
224
228
|
|
|
225
229
|
.description-button {
|
|
226
|
-
color: var(--bs-gray-
|
|
230
|
+
color: var(--bs-gray-700);
|
|
227
231
|
cursor: pointer;
|
|
228
232
|
width: 25px;
|
|
229
233
|
}
|
|
@@ -1,32 +1,40 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Handle type="source" :position="sourcePosition"
|
|
2
|
+
<Handle type="source" :position="sourcePosition"/>
|
|
3
3
|
<div class="collapsed-cluster-node">
|
|
4
|
-
<span
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
<span class="node-text">
|
|
5
|
+
<component v-if="data.iconComponent" :is="data.iconComponent" :class="`text-${data.color} me-2`" />
|
|
6
|
+
{{ id }}
|
|
7
|
+
</span>
|
|
8
|
+
<div class="position-absolute top-0 text-white d-flex top-button-div">
|
|
9
|
+
<slot name="badge-button-before"/>
|
|
7
10
|
<span
|
|
8
11
|
v-if="expandable"
|
|
9
12
|
class="rounded-button"
|
|
10
13
|
:class="[`bg-${data.color}`]"
|
|
11
14
|
@click="$emit(EVENTS.EXPAND, id)"
|
|
12
15
|
>
|
|
13
|
-
<ArrowExpand class="button-icon" alt="Expand task"
|
|
16
|
+
<ArrowExpand class="button-icon" alt="Expand task"/>
|
|
14
17
|
</span>
|
|
15
|
-
<slot name="badge-button-after"
|
|
18
|
+
<slot name="badge-button-after"/>
|
|
16
19
|
</div>
|
|
17
20
|
</div>
|
|
18
|
-
<Handle type="target" :position="targetPosition"
|
|
21
|
+
<Handle type="target" :position="targetPosition"/>
|
|
19
22
|
</template>
|
|
20
23
|
|
|
21
24
|
<script>
|
|
22
25
|
import {EVENTS} from "../../utils/constants.js";
|
|
23
26
|
import ArrowExpand from "vue-material-design-icons/ArrowExpand.vue";
|
|
27
|
+
import Webhook from "vue-material-design-icons/Webhook.vue";
|
|
24
28
|
import {Handle} from "@vue-flow/core";
|
|
25
29
|
|
|
26
30
|
export default {
|
|
27
31
|
components: {
|
|
28
32
|
Handle,
|
|
29
|
-
ArrowExpand
|
|
33
|
+
ArrowExpand,
|
|
34
|
+
Webhook
|
|
35
|
+
},
|
|
36
|
+
created(){
|
|
37
|
+
console.log(this.data)
|
|
30
38
|
},
|
|
31
39
|
inheritAttrs: false,
|
|
32
40
|
props: {
|
|
@@ -45,7 +53,7 @@
|
|
|
45
53
|
data: {
|
|
46
54
|
type: Object,
|
|
47
55
|
required: true
|
|
48
|
-
}
|
|
56
|
+
},
|
|
49
57
|
},
|
|
50
58
|
data() {
|
|
51
59
|
return {
|
|
@@ -75,6 +83,16 @@
|
|
|
75
83
|
.collapsed-cluster-node {
|
|
76
84
|
width: 150px;
|
|
77
85
|
height: 44px;
|
|
86
|
+
padding: 8px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.node-text {
|
|
90
|
+
color: black;
|
|
91
|
+
font-size: 0.90rem;
|
|
92
|
+
display: flex;
|
|
93
|
+
html.dark & {
|
|
94
|
+
color: white;
|
|
95
|
+
}
|
|
78
96
|
}
|
|
79
97
|
|
|
80
98
|
.rounded-button {
|
|
@@ -108,19 +108,4 @@
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
-
</script>
|
|
112
|
-
<style scoped lang="scss">
|
|
113
|
-
.rounded-button {
|
|
114
|
-
border-radius: 1rem;
|
|
115
|
-
width: 1rem;
|
|
116
|
-
height: 1rem;
|
|
117
|
-
display: flex;
|
|
118
|
-
justify-content: center;
|
|
119
|
-
align-items: center;
|
|
120
|
-
margin-left: 0.25rem;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.button-icon {
|
|
124
|
-
font-size: 0.75rem;
|
|
125
|
-
}
|
|
126
|
-
</style>
|
|
111
|
+
</script>
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import Utils from "../../utils/Utils.js";
|
|
23
23
|
import VueflowUtils from "../../utils/VueFlowUtils.js";
|
|
24
24
|
import {CLUSTER_UID_SEPARATOR} from "../../utils/constants.js";
|
|
25
|
+
import {Background} from "@vue-flow/background";
|
|
25
26
|
|
|
26
27
|
const props = defineProps({
|
|
27
28
|
id: {
|
|
@@ -101,6 +102,10 @@
|
|
|
101
102
|
generateGraph();
|
|
102
103
|
})
|
|
103
104
|
|
|
105
|
+
watch(() => props.isHorizontal, () => {
|
|
106
|
+
generateGraph();
|
|
107
|
+
})
|
|
108
|
+
|
|
104
109
|
const generateGraph = () => {
|
|
105
110
|
VueFlowUtils.cleanGraph(props.id);
|
|
106
111
|
|
|
@@ -285,6 +290,7 @@
|
|
|
285
290
|
:elevate-nodes-on-select="false"
|
|
286
291
|
:elevate-edges-on-select="false"
|
|
287
292
|
>
|
|
293
|
+
<Background />
|
|
288
294
|
<template #node-cluster="clusterProps">
|
|
289
295
|
<ClusterNode
|
|
290
296
|
v-bind="clusterProps"
|
package/src/scss/app.scss
CHANGED
|
@@ -13,10 +13,19 @@ marker[id*='id=marker-custom&type=arrowclosed'] polyline {
|
|
|
13
13
|
fill: $gray-700;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
marker[id*='id=marker-danger&type=arrowclosed'] polyline {
|
|
17
|
+
stroke: $danger;
|
|
18
|
+
fill: $danger;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
.vue-flow__handle {
|
|
17
22
|
opacity: 0 !important;
|
|
18
23
|
}
|
|
19
24
|
|
|
25
|
+
.vue-flow__edge-path {
|
|
26
|
+
stroke: $gray-700;
|
|
27
|
+
}
|
|
28
|
+
|
|
20
29
|
|
|
21
30
|
.top-button-div {
|
|
22
31
|
width: 100%;
|
|
@@ -32,10 +41,11 @@ marker[id*='id=marker-custom&type=arrowclosed'] polyline {
|
|
|
32
41
|
justify-content: center;
|
|
33
42
|
align-items: center;
|
|
34
43
|
margin-left: 0.25rem;
|
|
44
|
+
z-index: 2000;
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
.button-icon {
|
|
38
|
-
font-size: 0.
|
|
48
|
+
font-size: 0.66rem;
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
|
|
@@ -212,12 +212,12 @@ export default class VueFlowUtils {
|
|
|
212
212
|
|
|
213
213
|
static nodeColor(node, collapsed, flowSource) {
|
|
214
214
|
if (this.isTaskNode(node)) {
|
|
215
|
-
if (collapsed.includes(node.uid)) {
|
|
216
|
-
return "blue";
|
|
217
|
-
}
|
|
218
215
|
if (YamlUtils.isTaskError(flowSource, node.task.id)) {
|
|
219
216
|
return "danger"
|
|
220
217
|
}
|
|
218
|
+
if (collapsed.includes(node.uid)) {
|
|
219
|
+
return "blue";
|
|
220
|
+
}
|
|
221
221
|
if (node.task.type === "io.kestra.core.tasks.flows.Flow") {
|
|
222
222
|
return "primary"
|
|
223
223
|
}
|
|
@@ -230,8 +230,10 @@ export default class VueFlowUtils {
|
|
|
230
230
|
static getClusterTaskIdWithEndNodeUid (nodeUid, flowGraph) {
|
|
231
231
|
const cluster = flowGraph.clusters.find(cluster => cluster.end === nodeUid);
|
|
232
232
|
if (cluster) {
|
|
233
|
+
|
|
233
234
|
return Utils.splitFirst(cluster.cluster.uid, CLUSTER_UID_SEPARATOR);
|
|
234
235
|
}
|
|
236
|
+
|
|
235
237
|
return undefined;
|
|
236
238
|
}
|
|
237
239
|
|
|
@@ -274,6 +276,9 @@ export default class VueFlowUtils {
|
|
|
274
276
|
if (YamlUtils.isTaskError(flowSource, edge.source) || YamlUtils.isTaskError(flowSource, edge.target)) {
|
|
275
277
|
return "danger"
|
|
276
278
|
}
|
|
279
|
+
if (this.isClusterError(flowSource, edge.source) || this.isClusterError(flowSource, edge.target)) {
|
|
280
|
+
return "danger"
|
|
281
|
+
}
|
|
277
282
|
return null;
|
|
278
283
|
}
|
|
279
284
|
|
|
@@ -339,6 +344,7 @@ export default class VueFlowUtils {
|
|
|
339
344
|
}
|
|
340
345
|
|
|
341
346
|
const clusterUid = cluster.cluster.uid;
|
|
347
|
+
const isClusterError = YamlUtils.isTaskError(flowSource, Utils.splitFirst(clusterUid, CLUSTER_UID_SEPARATOR))
|
|
342
348
|
const dagreNode = dagreGraph.node(clusterUid)
|
|
343
349
|
const parentNode = cluster.parents ? cluster.parents[cluster.parents.length - 1] : undefined;
|
|
344
350
|
|
|
@@ -353,9 +359,9 @@ export default class VueFlowUtils {
|
|
|
353
359
|
},
|
|
354
360
|
data: {
|
|
355
361
|
collapsable: true,
|
|
356
|
-
color: clusterUid === "Triggers" ? "success" : "blue"
|
|
362
|
+
color: clusterUid === "Triggers" ? "success" : isClusterError ? "danger" : "blue"
|
|
357
363
|
},
|
|
358
|
-
class: `bg-light-${clusterUid === "Triggers" ? "success" : "blue"}-border rounded p-2`,
|
|
364
|
+
class: `bg-light-${clusterUid === "Triggers" ? "success" : isClusterError ? "danger" : "blue"}-border rounded p-2`,
|
|
359
365
|
})
|
|
360
366
|
}
|
|
361
367
|
}
|
|
@@ -405,11 +411,12 @@ export default class VueFlowUtils {
|
|
|
405
411
|
flowId: flowId,
|
|
406
412
|
isFlowable: this.isTaskNode(node) ? flowables.includes(taskId) : false,
|
|
407
413
|
color: nodeType != "dot" ? this.nodeColor(node, collapsed, flowSource) : null,
|
|
408
|
-
expandable: taskId ?
|
|
414
|
+
expandable: taskId ? edgeReplacer[CLUSTER_UID_SEPARATOR + taskId] !== undefined : this.isCollapsedCluster(node),
|
|
409
415
|
isReadOnly: isReadOnly,
|
|
410
|
-
link: node.task?.type === "io.kestra.core.tasks.flows.Flow" ? this.linkDatas(node.task) : false
|
|
416
|
+
link: node.task?.type === "io.kestra.core.tasks.flows.Flow" ? this.linkDatas(node.task) : false,
|
|
417
|
+
iconComponent: this.isCollapsedCluster(node) ? "webhook" : null
|
|
411
418
|
},
|
|
412
|
-
class: node.type === "collapsedcluster" ? `bg-light-${node.uid === "Triggers" ? "success" : "blue"}-border rounded
|
|
419
|
+
class: node.type === "collapsedcluster" ? `bg-light-${node.uid === "Triggers" ? "success" : YamlUtils.isTaskError(flowSource, taskId) ? "danger" : "blue"}-border rounded` : "",
|
|
413
420
|
})
|
|
414
421
|
}
|
|
415
422
|
}
|
|
@@ -422,7 +429,7 @@ export default class VueFlowUtils {
|
|
|
422
429
|
target: newEdge.target,
|
|
423
430
|
type: "edge",
|
|
424
431
|
markerEnd: YamlUtils.extractTask(flowSource, newEdge.target) ? {
|
|
425
|
-
id: "marker-custom",
|
|
432
|
+
id: YamlUtils.isTaskError(flowSource, newEdge.target) ? "marker-danger" : "marker-custom",
|
|
426
433
|
type: MarkerType.ArrowClosed,
|
|
427
434
|
} : "",
|
|
428
435
|
data: {
|
|
@@ -442,4 +449,19 @@ export default class VueFlowUtils {
|
|
|
442
449
|
return elements;
|
|
443
450
|
}
|
|
444
451
|
|
|
452
|
+
static isClusterError(flowSource, clusterUid) {
|
|
453
|
+
if(clusterUid.startsWith(CLUSTER_UID_SEPARATOR)) {
|
|
454
|
+
if(clusterUid.endsWith("_end")) {
|
|
455
|
+
|
|
456
|
+
return YamlUtils.isTaskError(flowSource, Utils.splitFirst(clusterUid.substring(0, clusterUid.length - 4), CLUSTER_UID_SEPARATOR));
|
|
457
|
+
}
|
|
458
|
+
if(clusterUid.endsWith("_start")) {
|
|
459
|
+
|
|
460
|
+
return YamlUtils.isTaskError(flowSource, Utils.splitFirst(clusterUid.substring(0, clusterUid.length - 6), CLUSTER_UID_SEPARATOR));
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return false
|
|
465
|
+
}
|
|
466
|
+
|
|
445
467
|
}
|
package/src/utils/YamlUtils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import JsYaml from "js-yaml";
|
|
2
2
|
import yaml, {Document, YAMLMap, isSeq, isMap, Pair, Scalar, YAMLSeq, LineCounter} from "yaml";
|
|
3
|
-
import _cloneDeep from "lodash/cloneDeep"
|
|
3
|
+
import _cloneDeep from "lodash/cloneDeep.js"
|
|
4
4
|
import {SECTIONS} from "./constants.js";
|
|
5
5
|
|
|
6
6
|
const TOSTRING_OPTIONS = {lineWidth: 0};
|