@kestra-io/ui-libs 0.0.1 → 0.0.2
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 +1 -1
- package/package.json +4 -20
- package/src/components/buttons/AddTaskButton.vue +33 -0
- package/src/components/index.js +21 -0
- package/src/components/misc/Duration.vue +110 -0
- package/src/components/misc/ExecutionInformations.vue +119 -0
- package/src/components/misc/State.vue +57 -0
- package/src/components/misc/TaskIcon.vue +50 -0
- package/src/components/nodes/BasicNode.vue +226 -0
- package/src/components/nodes/ClusterNode.vue +53 -0
- package/src/components/nodes/CollapsedClusterNode.vue +94 -0
- package/src/components/nodes/DependenciesNode.vue +128 -0
- package/src/components/nodes/DotNode.vue +46 -0
- package/src/components/nodes/EdgeNode.vue +67 -0
- package/src/components/nodes/TaskNode.vue +168 -0
- package/src/components/nodes/TriggerNode.vue +101 -0
- package/src/index.js +8 -0
- package/src/scss/bootstrap-dark.scss +49 -0
- package/src/scss/bootstrap.scss +72 -0
- package/src/scss/custom.scss +42 -0
- package/src/scss/index.scss +8 -0
- package/src/utils/GraphUtils.js +390 -0
- package/src/utils/Utils.js +47 -0
- package/src/utils/YamlUtils.js +478 -0
- package/src/utils/constants.js +19 -0
- package/src/utils/global.js +23 -0
- package/src/utils/state.js +165 -0
- package/dist/kestra-ui.js +0 -2594
- package/dist/kestra-ui.umd.cjs +0 -1
- package/dist/style.css +0 -1
- /package/{dist → src/scss}/_theme-dark.scss +0 -0
- /package/{dist → src/scss}/_variables.scss +0 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
Kestra UI library implemented in both the website and the application UI.
|
|
12
12
|
|
|
13
|
-
Install with npm: `npm install @kestra
|
|
13
|
+
Install with npm: `npm install @kestra/ui-library`
|
|
14
14
|
|
|
15
15
|
<p align="center">
|
|
16
16
|
<a href="https://kestra.io/"><b>Website</b></a> •
|
package/package.json
CHANGED
|
@@ -1,31 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kestra-io/ui-libs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
|
-
"
|
|
6
|
+
"src",
|
|
7
7
|
"package.json"
|
|
8
8
|
],
|
|
9
|
-
"main": "./
|
|
10
|
-
"module": "./
|
|
11
|
-
"exports": {
|
|
12
|
-
".": {
|
|
13
|
-
"import": "./dist/kestra-ui.js",
|
|
14
|
-
"require": "./dist/kestra-ui.umd.cjs"
|
|
15
|
-
},
|
|
16
|
-
"./dist/*.css": {
|
|
17
|
-
"import": "./dist/*.css",
|
|
18
|
-
"require": "./dist/*.css"
|
|
19
|
-
},
|
|
20
|
-
"./dist/*.scss": {
|
|
21
|
-
"import": "./dist/*.css",
|
|
22
|
-
"require": "./dist/*.css"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
9
|
+
"main": "./src/index.js",
|
|
10
|
+
"module": "./src/index.js",
|
|
25
11
|
"scripts": {
|
|
26
|
-
"prepublishOnly": "vite build",
|
|
27
12
|
"dev": "vite",
|
|
28
|
-
"build": "vite build",
|
|
29
13
|
"debug": "vite --debug",
|
|
30
14
|
"preview": "vite preview",
|
|
31
15
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path ./.gitignore"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Plus from "vue-material-design-icons/Plus.vue";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
name: "AddTaskButton",
|
|
6
|
+
components: {Plus},
|
|
7
|
+
props: {
|
|
8
|
+
addTask: {
|
|
9
|
+
type: Boolean,
|
|
10
|
+
default: false
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<div class="add-task-div rounded d-flex justify-content-center align-items-center">
|
|
18
|
+
<Plus v-if="addTask" alt="add task icon" />
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<style scoped lang="scss">
|
|
23
|
+
.add-task-div {
|
|
24
|
+
margin: 0.2rem;
|
|
25
|
+
width: 25px;
|
|
26
|
+
height: 25px;
|
|
27
|
+
border: 0.4px solid var(--bs-border-color);
|
|
28
|
+
background-color: var(--bs-white);
|
|
29
|
+
html.dark & {
|
|
30
|
+
background-color: var(--card-bg);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// nodes
|
|
2
|
+
import ClusterNode from "./nodes/ClusterNode.vue";
|
|
3
|
+
import DotNode from "./nodes/DotNode.vue";
|
|
4
|
+
import EdgeNode from "./nodes/EdgeNode.vue";
|
|
5
|
+
import TaskNode from "./nodes/TaskNode.vue";
|
|
6
|
+
import TriggerNode from "./nodes/TriggerNode.vue"
|
|
7
|
+
import BasicNode from "./nodes/BasicNode.vue";
|
|
8
|
+
import CollapsedClusterNode from "./nodes/CollapsedClusterNode.vue";
|
|
9
|
+
import DependenciesNode from "./nodes/DependenciesNode.vue"
|
|
10
|
+
|
|
11
|
+
// misc
|
|
12
|
+
import ExecutionInformations from "./misc/ExecutionInformations.vue";
|
|
13
|
+
import State from "./misc/State.vue";
|
|
14
|
+
import TaskIcon from "./misc/TaskIcon.vue";
|
|
15
|
+
|
|
16
|
+
// buttons
|
|
17
|
+
import AddTaskButton from "./buttons/AddTaskButton.vue";
|
|
18
|
+
|
|
19
|
+
export {ClusterNode, DotNode, EdgeNode, TaskNode, TriggerNode, BasicNode, CollapsedClusterNode, DependenciesNode};
|
|
20
|
+
export {ExecutionInformations, State, TaskIcon};
|
|
21
|
+
export {AddTaskButton};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span>
|
|
3
|
+
<el-tooltip v-if="histories" popper-class="duration-tt" :persistent="false" transition="" :hide-after="0">
|
|
4
|
+
<template #content>
|
|
5
|
+
<span v-for="(history, index) in histories" :key="'tt-' + index">
|
|
6
|
+
<span class="square" :class="squareClass(history.state)" />
|
|
7
|
+
<strong>{{ history.state }}:</strong> {{ $filters.date(history.date, 'iso') }} <br>
|
|
8
|
+
</span>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<span>{{ duration }}</span>
|
|
12
|
+
</el-tooltip>
|
|
13
|
+
</span>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script>
|
|
17
|
+
import State from "../../utils/state";
|
|
18
|
+
import Utils from "../../utils/Utils";
|
|
19
|
+
|
|
20
|
+
const ts = date => new Date(date).getTime();
|
|
21
|
+
|
|
22
|
+
export default {
|
|
23
|
+
props: {
|
|
24
|
+
histories: {
|
|
25
|
+
type: Array,
|
|
26
|
+
default: undefined
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
watch: {
|
|
30
|
+
histories(newValue, oldValue) {
|
|
31
|
+
if (oldValue[0].date !== newValue[0].date) {
|
|
32
|
+
this.paint()
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
data () {
|
|
37
|
+
return {
|
|
38
|
+
duration: "",
|
|
39
|
+
refreshHandler: undefined
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
mounted() {
|
|
43
|
+
this.paint()
|
|
44
|
+
},
|
|
45
|
+
computed: {
|
|
46
|
+
start() {
|
|
47
|
+
return this.histories && this.histories.length && ts(this.histories[0].date);
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
lastStep() {
|
|
51
|
+
return this.histories[this.histories.length - 1]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
methods: {
|
|
55
|
+
paint() {
|
|
56
|
+
if (!this.refreshHandler) {
|
|
57
|
+
this.refreshHandler = setInterval(() => {
|
|
58
|
+
this.computeDuration()
|
|
59
|
+
if (this.histories && !State.isRunning(this.lastStep.state)) {
|
|
60
|
+
this.cancel();
|
|
61
|
+
}
|
|
62
|
+
}, 100);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
cancel() {
|
|
66
|
+
if (this.refreshHandler) {
|
|
67
|
+
clearInterval(this.refreshHandler);
|
|
68
|
+
this.refreshHandler = undefined
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
delta() {
|
|
72
|
+
return this.stop() - this.start;
|
|
73
|
+
},
|
|
74
|
+
stop() {
|
|
75
|
+
if (!this.histories || State.isRunning(this.lastStep.state)) {
|
|
76
|
+
return +new Date();
|
|
77
|
+
}
|
|
78
|
+
return ts(this.lastStep.date)
|
|
79
|
+
},
|
|
80
|
+
computeDuration() {
|
|
81
|
+
this.duration = Utils.humanDuration(this.delta() / 1000)
|
|
82
|
+
},
|
|
83
|
+
squareClass(state) {
|
|
84
|
+
return [
|
|
85
|
+
"bg-" + State.colorClass()[state]
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
beforeUnmount() {
|
|
90
|
+
this.cancel();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<style lang="scss">
|
|
96
|
+
.duration-tt {
|
|
97
|
+
.tooltip-inner {
|
|
98
|
+
text-align: left;
|
|
99
|
+
white-space: nowrap;
|
|
100
|
+
max-width: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.square {
|
|
104
|
+
display: inline-block;
|
|
105
|
+
width: 10px;
|
|
106
|
+
height: 10px;
|
|
107
|
+
margin-right: 5px;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="btn-group content rounded-1 content-children">
|
|
3
|
+
<span v-if="taskRuns.length > 0">{{ taskRuns.length }} task runs</span>
|
|
4
|
+
<span><duration :histories="histories" /></span>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
<script>
|
|
8
|
+
import Delete from "vue-material-design-icons/Delete.vue";
|
|
9
|
+
import Pencil from "vue-material-design-icons/Pencil.vue";
|
|
10
|
+
import {EVENTS} from "../../utils/constants"
|
|
11
|
+
import moment from "moment";
|
|
12
|
+
import Duration from "./Duration.vue";
|
|
13
|
+
import State from "../../utils/state.js";
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
name: "ExecutionInformations",
|
|
17
|
+
computed: {
|
|
18
|
+
EVENTS() {
|
|
19
|
+
return EVENTS
|
|
20
|
+
},
|
|
21
|
+
taskRunList() {
|
|
22
|
+
return this.execution && this.execution.taskRunList ? this.execution.taskRunList : []
|
|
23
|
+
},
|
|
24
|
+
taskRuns() {
|
|
25
|
+
return this.taskRunList.filter(t => t.taskId === this.task.id)
|
|
26
|
+
},
|
|
27
|
+
state() {
|
|
28
|
+
if (!this.taskRuns) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (this.taskRuns.length === 1) {
|
|
33
|
+
return this.taskRuns[0].state.current
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const allStates = this.taskRuns.map(t => t.state.current);
|
|
37
|
+
|
|
38
|
+
const SORT_STATUS = [
|
|
39
|
+
State.FAILED,
|
|
40
|
+
State.KILLED,
|
|
41
|
+
State.WARNING,
|
|
42
|
+
State.KILLING,
|
|
43
|
+
State.RUNNING,
|
|
44
|
+
State.SUCCESS,
|
|
45
|
+
State.RESTARTED,
|
|
46
|
+
State.CREATED,
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// sorting based on SORT_STATUS array
|
|
50
|
+
const result = allStates
|
|
51
|
+
.map((item) => {
|
|
52
|
+
const n = SORT_STATUS.indexOf(item[1]);
|
|
53
|
+
SORT_STATUS[n] = undefined;
|
|
54
|
+
return [n, item]
|
|
55
|
+
})
|
|
56
|
+
.sort()
|
|
57
|
+
.map((j) => j[1])
|
|
58
|
+
|
|
59
|
+
return result[0];
|
|
60
|
+
},
|
|
61
|
+
histories() {
|
|
62
|
+
if (!this.taskRuns) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const max = Math.max(...this.taskRuns
|
|
67
|
+
.filter(value => value.state.histories && value.state.histories.length > 0)
|
|
68
|
+
.map(value => new Date(value.state.histories[value.state.histories.length - 1].date).getTime()));
|
|
69
|
+
|
|
70
|
+
const duration = Math.max(...this.taskRuns
|
|
71
|
+
.map((taskRun) => moment.duration(taskRun.state.duration).asMilliseconds() / 1000, 0));
|
|
72
|
+
|
|
73
|
+
return [
|
|
74
|
+
{date: moment(max).subtract(duration, "second"), state: "CREATED"},
|
|
75
|
+
{date: moment(max), state: this.state}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
components: {Duration},
|
|
80
|
+
props: {
|
|
81
|
+
color: {
|
|
82
|
+
type: String,
|
|
83
|
+
default: "primary"
|
|
84
|
+
},
|
|
85
|
+
execution: {
|
|
86
|
+
type: Object,
|
|
87
|
+
default: null
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
</script>
|
|
93
|
+
<style scoped>
|
|
94
|
+
.content {
|
|
95
|
+
display: flex;
|
|
96
|
+
|
|
97
|
+
*:not(:first-child)::before {
|
|
98
|
+
content: "";
|
|
99
|
+
position: absolute;
|
|
100
|
+
left: 0;
|
|
101
|
+
top: 50%;
|
|
102
|
+
height: 50%;
|
|
103
|
+
border-left: 1px solid var(--bs-gray-100);
|
|
104
|
+
transform: translateY(-50%);
|
|
105
|
+
z-index: 500;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.content-children {
|
|
110
|
+
flex-grow: 1;
|
|
111
|
+
display: flex;
|
|
112
|
+
height: 1.25rem;
|
|
113
|
+
gap: 0.5rem;
|
|
114
|
+
align-self: stretch;
|
|
115
|
+
pointer-events: none;
|
|
116
|
+
cursor: default;
|
|
117
|
+
font-size: 0.75rem;
|
|
118
|
+
}
|
|
119
|
+
</style>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import AlertOctagonOutline from "vue-material-design-icons/AlertOctagonOutline.vue";
|
|
3
|
+
import Check from "vue-material-design-icons/Check.vue";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: "State",
|
|
7
|
+
props: {
|
|
8
|
+
color: {
|
|
9
|
+
type: String,
|
|
10
|
+
default: "primary"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
computed: {
|
|
14
|
+
stateIcon() {
|
|
15
|
+
switch (this.color) {
|
|
16
|
+
case "primary":
|
|
17
|
+
return AlertOctagonOutline
|
|
18
|
+
case "danger":
|
|
19
|
+
return AlertOctagonOutline
|
|
20
|
+
case "success":
|
|
21
|
+
return Check
|
|
22
|
+
case "warning":
|
|
23
|
+
return AlertOctagonOutline
|
|
24
|
+
default:
|
|
25
|
+
return AlertOctagonOutline
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
stateText() {
|
|
29
|
+
switch (this.color) {
|
|
30
|
+
case "primary":
|
|
31
|
+
return "Running"
|
|
32
|
+
case "danger":
|
|
33
|
+
return "Failed"
|
|
34
|
+
case "success":
|
|
35
|
+
return "Success"
|
|
36
|
+
case "warning":
|
|
37
|
+
return "Warning"
|
|
38
|
+
default:
|
|
39
|
+
return "Error"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<div :class="[`text-${color}`]">
|
|
48
|
+
<component class="icon" :is="stateIcon" fill-color="#ff0000" />
|
|
49
|
+
<span>{{ stateText }}</span>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<style scoped>
|
|
54
|
+
.icon {
|
|
55
|
+
margin-right: 0.3rem;
|
|
56
|
+
}
|
|
57
|
+
</style>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="[color ? `bg-${color}`: '']"
|
|
4
|
+
class="div-icon rounded d-flex justify-content-center"
|
|
5
|
+
>
|
|
6
|
+
<img :src="backgroundImage" alt="task icon">
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
<script>
|
|
10
|
+
import {Buffer} from "buffer";
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
name: "TaskIcon",
|
|
14
|
+
props: {
|
|
15
|
+
icon: {
|
|
16
|
+
type: Object,
|
|
17
|
+
default: undefined
|
|
18
|
+
},
|
|
19
|
+
color: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: undefined
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
computed: {
|
|
25
|
+
backgroundImage() {
|
|
26
|
+
return `data:image/svg+xml;base64,${this.imageBase64}`
|
|
27
|
+
},
|
|
28
|
+
imageBase64() {
|
|
29
|
+
const icon = this.icon ? this.icon.icon : undefined;
|
|
30
|
+
return icon ? icon : Buffer.from("<svg xmlns=\"http://www.w3.org/2000/svg\" " +
|
|
31
|
+
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" aria-hidden=\"true\" " +
|
|
32
|
+
"focusable=\"false\" width=\"0.75em\" height=\"1em\" style=\"-ms-transform: " +
|
|
33
|
+
"rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);\" " +
|
|
34
|
+
"preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 384 512\">" +
|
|
35
|
+
"<path d=\"M288 32H0v448h384V128l-96-96zm64 416H32V64h224l96 96v288z\" fill=\"#0D1523FF\"/>" +
|
|
36
|
+
"</svg>", "utf8").toString("base64");
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
<style scoped>
|
|
42
|
+
.div-icon {
|
|
43
|
+
padding: 0.3rem;
|
|
44
|
+
margin: 0.2rem;
|
|
45
|
+
width: 25px;
|
|
46
|
+
height: 25px;
|
|
47
|
+
background-color: var(--bs-white);
|
|
48
|
+
border: 0.4px solid var(--bs-border-color);
|
|
49
|
+
}
|
|
50
|
+
</style>
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="[`border-${!expandable ? data.color : 'blue'}`]"
|
|
4
|
+
class="node-wrapper rounded-3 border"
|
|
5
|
+
@mouseover="mouseover"
|
|
6
|
+
@mouseleave="mouseleave"
|
|
7
|
+
>
|
|
8
|
+
<div v-if="state" class="status-div" :class="[`bg-${stateColor}`]" />
|
|
9
|
+
<div>
|
|
10
|
+
<TaskIcon :icon="data.icon" :class="taskIconBg" />
|
|
11
|
+
</div>
|
|
12
|
+
<div class="node-content">
|
|
13
|
+
<div class="d-flex justify-content-around">
|
|
14
|
+
<div class="text-truncate task-title">
|
|
15
|
+
<span> {{ id }} </span>
|
|
16
|
+
</div>
|
|
17
|
+
<InformationOutline
|
|
18
|
+
v-if="description"
|
|
19
|
+
@click="$emit(EVENTS.SHOW_DESCRIPTION, {id: id, description:description})"
|
|
20
|
+
class="description-button mx-2"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
<slot name="content" />
|
|
24
|
+
</div>
|
|
25
|
+
<div class="position-absolute top-0 text-white d-flex top-button-div">
|
|
26
|
+
<slot name="badge-button-before" />
|
|
27
|
+
<span
|
|
28
|
+
v-if="link"
|
|
29
|
+
class="rounded-button"
|
|
30
|
+
:class="[`bg-${data.color}`]"
|
|
31
|
+
@click="$emit(EVENTS.OPEN_LINK, data)"
|
|
32
|
+
>
|
|
33
|
+
<OpenInNew class="button-icon" alt="Open in new tab" />
|
|
34
|
+
</span>
|
|
35
|
+
<span
|
|
36
|
+
v-if="expandable"
|
|
37
|
+
class="rounded-button"
|
|
38
|
+
:class="[`bg-${data.color}`]"
|
|
39
|
+
@click="$emit(EVENTS.EXPAND)"
|
|
40
|
+
>
|
|
41
|
+
<ArrowExpand class="button-icon" alt="Expand task" />
|
|
42
|
+
</span>
|
|
43
|
+
<slot name="badge-button-after" />
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script>
|
|
49
|
+
import TaskIcon from "../misc/TaskIcon.vue";
|
|
50
|
+
import InformationOutline from "vue-material-design-icons/InformationOutline.vue";
|
|
51
|
+
import {EVENTS} from "../../utils/constants.js";
|
|
52
|
+
import ArrowExpand from "vue-material-design-icons/ArrowExpand.vue";
|
|
53
|
+
import OpenInNew from "vue-material-design-icons/OpenInNew.vue";
|
|
54
|
+
|
|
55
|
+
export default {
|
|
56
|
+
components: {
|
|
57
|
+
ArrowExpand,
|
|
58
|
+
TaskIcon,
|
|
59
|
+
InformationOutline,
|
|
60
|
+
OpenInNew
|
|
61
|
+
},
|
|
62
|
+
emits: [
|
|
63
|
+
EVENTS.EXPAND,
|
|
64
|
+
EVENTS.OPEN_LINK,
|
|
65
|
+
EVENTS.SHOW_LOGS,
|
|
66
|
+
EVENTS.MOUSE_OVER,
|
|
67
|
+
EVENTS.MOUSE_LEAVE,
|
|
68
|
+
EVENTS.ADD_ERROR,
|
|
69
|
+
EVENTS.EDIT,
|
|
70
|
+
EVENTS.DELETE,
|
|
71
|
+
EVENTS.ADD_TASK
|
|
72
|
+
],
|
|
73
|
+
inheritAttrs: false,
|
|
74
|
+
props: {
|
|
75
|
+
id: {
|
|
76
|
+
type: String,
|
|
77
|
+
default: undefined
|
|
78
|
+
},
|
|
79
|
+
type: {
|
|
80
|
+
type: String,
|
|
81
|
+
default: undefined
|
|
82
|
+
},
|
|
83
|
+
disabled: {
|
|
84
|
+
type: Boolean,
|
|
85
|
+
default: undefined
|
|
86
|
+
},
|
|
87
|
+
state: {
|
|
88
|
+
type: String,
|
|
89
|
+
default: undefined
|
|
90
|
+
},
|
|
91
|
+
data: {
|
|
92
|
+
type: Object,
|
|
93
|
+
required: true
|
|
94
|
+
},
|
|
95
|
+
style: {
|
|
96
|
+
type: Object,
|
|
97
|
+
required: false,
|
|
98
|
+
default: undefined
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
methods: {
|
|
102
|
+
mouseover() {
|
|
103
|
+
this.$emit(EVENTS.MOUSE_OVER, this.data.node);
|
|
104
|
+
},
|
|
105
|
+
mouseleave() {
|
|
106
|
+
this.$emit(EVENTS.MOUSE_LEAVE);
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
data() {
|
|
110
|
+
return {
|
|
111
|
+
filter: undefined,
|
|
112
|
+
isOpen: false,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
computed: {
|
|
116
|
+
EVENTS() {
|
|
117
|
+
return EVENTS
|
|
118
|
+
},
|
|
119
|
+
expandable() {
|
|
120
|
+
return this.data?.expandable || false
|
|
121
|
+
},
|
|
122
|
+
link() {
|
|
123
|
+
return this.data?.link || false
|
|
124
|
+
},
|
|
125
|
+
description() {
|
|
126
|
+
const node = this.data.node.task ?? this.data.node.trigger ?? null
|
|
127
|
+
if (node) {
|
|
128
|
+
return node.description ?? null
|
|
129
|
+
}
|
|
130
|
+
return null
|
|
131
|
+
},
|
|
132
|
+
taskIconBg() {
|
|
133
|
+
return !["default", "danger"].includes(this.data.color) ? this.data.color : "";
|
|
134
|
+
},
|
|
135
|
+
stateColor() {
|
|
136
|
+
switch (this.state) {
|
|
137
|
+
case "RUNNING":
|
|
138
|
+
return "primary"
|
|
139
|
+
case "SUCCESS":
|
|
140
|
+
return "success"
|
|
141
|
+
case "WARNING":
|
|
142
|
+
return "warning"
|
|
143
|
+
case "FAILED":
|
|
144
|
+
return "danger"
|
|
145
|
+
default:
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<style lang="scss" scoped>
|
|
154
|
+
@import "../../scss/custom";
|
|
155
|
+
|
|
156
|
+
.node-wrapper {
|
|
157
|
+
background-color: var(--bs-white);
|
|
158
|
+
|
|
159
|
+
html.dark & {
|
|
160
|
+
background-color: var(--card-bg);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
width: 184px;
|
|
164
|
+
height: 44px;
|
|
165
|
+
margin: 0;
|
|
166
|
+
padding: 8px;
|
|
167
|
+
display: flex;
|
|
168
|
+
z-index: 150000;
|
|
169
|
+
align-items: center;
|
|
170
|
+
box-shadow: 0 12px 12px 0 rgba(130, 103, 158, 0.10);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.node-content {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
justify-content: center;
|
|
177
|
+
margin-left: 0.7rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.description-button {
|
|
181
|
+
color: $gray-600;
|
|
182
|
+
cursor: pointer;
|
|
183
|
+
width: 25px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.material-design-icon.icon-rounded {
|
|
187
|
+
border-radius: 1rem;
|
|
188
|
+
padding: 1px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.rounded-button {
|
|
192
|
+
border-radius: 1rem;
|
|
193
|
+
width: 1rem;
|
|
194
|
+
height: 1rem;
|
|
195
|
+
display: flex;
|
|
196
|
+
justify-content: center;
|
|
197
|
+
align-items: center;
|
|
198
|
+
margin-left: 0.25rem;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.button-icon {
|
|
202
|
+
font-size: 0.75rem;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.task-title {
|
|
206
|
+
font-size: 0.75rem;
|
|
207
|
+
font-weight: 700;
|
|
208
|
+
line-height: 1.5rem;
|
|
209
|
+
color: var(--bs-black);
|
|
210
|
+
|
|
211
|
+
html.dark & {
|
|
212
|
+
color: var(--bs-white);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
width: 6rem;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
.status-div {
|
|
220
|
+
width: 8px;
|
|
221
|
+
height: 100%;
|
|
222
|
+
position: absolute;
|
|
223
|
+
left: -0.04438rem;
|
|
224
|
+
border-radius: 0.5rem 0 0 0.5rem;
|
|
225
|
+
}
|
|
226
|
+
</style>
|