fix: action node between to state node on connect
also added connection line
This commit is contained in:
parent
42247bc31f
commit
00b80aa0f5
6 changed files with 161 additions and 34 deletions
|
|
@ -3,10 +3,12 @@ import { VueFlow, useVueFlow, Panel, PanelPosition } from "@vue-flow/core";
|
|||
import { Background } from "@vue-flow/background";
|
||||
import TransitionEdge from "./components/TransitionEdge.vue";
|
||||
import StateNode from "./components/StateNode.vue";
|
||||
import ActionNode from "./components/ActionNode.vue";
|
||||
import ConnectionLine from "./components/ConnectionLine.vue";
|
||||
import { useStore } from "./store";
|
||||
|
||||
let store = useStore();
|
||||
let { nodes, onConnect, addNodes, addEdges } = useVueFlow();
|
||||
let { nodes, findNode, onConnect, addNodes, addEdges } = useVueFlow();
|
||||
|
||||
function add_state() {
|
||||
let state_id = (nodes.value.length + 1).toString();
|
||||
|
|
@ -20,10 +22,50 @@ function add_state() {
|
|||
]);
|
||||
}
|
||||
|
||||
onConnect(params => {
|
||||
params.animated = true;
|
||||
params.type = "transition";
|
||||
addEdges([params]);
|
||||
onConnect(edge => {
|
||||
let source_node = findNode(edge.source);
|
||||
let target_node = findNode(edge.target);
|
||||
|
||||
let source_center = {
|
||||
x: source_node.position.x + source_node.dimensions.width / 2,
|
||||
y: source_node.position.y + source_node.dimensions.height / 2
|
||||
};
|
||||
|
||||
let target_center = {
|
||||
x: target_node.position.x + target_node.dimensions.width / 2,
|
||||
y: target_node.position.y + target_node.dimensions.height / 2
|
||||
};
|
||||
|
||||
let center_x = (source_center.x + target_center.x) / 2;
|
||||
let center_y = source_center.y - 16;
|
||||
|
||||
const action_node = {
|
||||
id: "action-" + frappe.utils.get_random(5),
|
||||
type: "action",
|
||||
label: "Approve",
|
||||
position: { x: center_x, y: center_y }
|
||||
};
|
||||
addNodes([action_node]);
|
||||
|
||||
let action_edge = {
|
||||
source: edge.source,
|
||||
sourceHandle: edge.sourceHandle,
|
||||
target: action_node.id,
|
||||
targetHandle: "left",
|
||||
type: "transition",
|
||||
updatable: true,
|
||||
animated: true
|
||||
};
|
||||
let state_edge = {
|
||||
source: action_node.id,
|
||||
sourceHandle: "right",
|
||||
target: edge.target,
|
||||
targetHandle: edge.targetHandle,
|
||||
type: "transition",
|
||||
updatable: true,
|
||||
animated: true
|
||||
};
|
||||
addEdges([action_edge, state_edge]);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -37,9 +79,15 @@ onConnect(params => {
|
|||
<template #node-state="node">
|
||||
<StateNode :node="node" />
|
||||
</template>
|
||||
<template #node-action="node">
|
||||
<ActionNode :node="node" />
|
||||
</template>
|
||||
<template #edge-transition="props">
|
||||
<TransitionEdge v-bind="props" />
|
||||
</template>
|
||||
<template #connection-line="props">
|
||||
<ConnectionLine v-bind="props" />
|
||||
</template>
|
||||
</VueFlow>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
44
frappe/public/js/workflow_builder/components/ActionNode.vue
Normal file
44
frappe/public/js/workflow_builder/components/ActionNode.vue
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<script setup>
|
||||
import { Handle } from "@vue-flow/core";
|
||||
|
||||
const props = defineProps({
|
||||
node: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="node" tabindex="0">
|
||||
<div v-if="node.label" class="node-label">{{ node.label }}</div>
|
||||
<div v-else class="node-placeholder text-muted">{{ __("No Label") }}</div>
|
||||
<Handle
|
||||
v-for="handle in ['top', 'right', 'bottom', 'left']"
|
||||
class="handle"
|
||||
:style="{ [handle]: '-3px', opacity: 0 }"
|
||||
type="source"
|
||||
:position="handle"
|
||||
:id="handle"
|
||||
@click.stop
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.node {
|
||||
position: relative;
|
||||
background-color: var(--gray-500);
|
||||
font-weight: 500;
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
color: var(--fg-color);
|
||||
border: 1px solid var(--fg-color);
|
||||
box-shadow: var(--shadow-base);
|
||||
}
|
||||
|
||||
.vue-flow__node.selected .node {
|
||||
outline: 1.5px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<script setup>
|
||||
import { getSmoothStepPath, SmoothStepEdge } from "@vue-flow/core";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
sourceX: { type: Number, required: true },
|
||||
sourceY: { type: Number, required: true },
|
||||
targetX: { type: Number, required: true },
|
||||
targetY: { type: Number, required: true },
|
||||
sourcePosition: { type: String, required: false },
|
||||
targetPosition: { type: String, required: false },
|
||||
sourceHandle: { type: Object, required: false },
|
||||
targetHandle: { type: Object, required: false },
|
||||
markerEnd: { type: String, required: false }
|
||||
});
|
||||
|
||||
let opposite = {
|
||||
left: "left",
|
||||
right: "right",
|
||||
top: "bottom",
|
||||
bottom: "top"
|
||||
};
|
||||
|
||||
const d = computed(() =>
|
||||
getSmoothStepPath({
|
||||
...props,
|
||||
borderRadius: 30,
|
||||
targetPosition: opposite[props.targetPosition]
|
||||
})
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<SmoothStepEdge class="animated" :path="d[0]" />
|
||||
</template>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { Handle, Position } from "@vue-flow/core";
|
||||
import { Handle } from "@vue-flow/core";
|
||||
|
||||
const props = defineProps({
|
||||
node: {
|
||||
|
|
@ -13,10 +13,14 @@ const props = defineProps({
|
|||
<div class="node" tabindex="0">
|
||||
<div v-if="node.label" class="node-label">{{ node.label }}</div>
|
||||
<div v-else class="node-placeholder text-muted">{{ __("No Label") }}</div>
|
||||
<Handle class="handle handle-a" type="source" :position="Position.Top" id="a" />
|
||||
<Handle class="handle handle-b" type="source" :position="Position.Right" id="b" />
|
||||
<Handle class="handle handle-c" type="source" :position="Position.Bottom" id="c" />
|
||||
<Handle class="handle handle-d" type="source" :position="Position.Left" id="d" />
|
||||
<Handle
|
||||
v-for="handle in ['top', 'right', 'bottom', 'left']"
|
||||
class="handle"
|
||||
:style="{ [handle]: '-12px'}"
|
||||
type="source"
|
||||
:position="handle"
|
||||
:id="handle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -44,21 +48,5 @@ const props = defineProps({
|
|||
background-color: var(--gray-600);
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&.handle-a {
|
||||
top: -12px;
|
||||
}
|
||||
|
||||
&.handle-b {
|
||||
right: -12px;
|
||||
}
|
||||
|
||||
&.handle-c {
|
||||
bottom: -12px;
|
||||
}
|
||||
|
||||
&.handle-d {
|
||||
left: -12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ const props = defineProps({
|
|||
targetPosition: { type: String, required: false },
|
||||
sourceHandle: { type: String, required: false },
|
||||
targetHandle: { type: String, required: false },
|
||||
source: { type: String, required: false },
|
||||
target: { type: String, required: false },
|
||||
sourceNode: { type: Object, required: true },
|
||||
targetNode: { type: Object, required: true },
|
||||
markerEnd: { type: String, required: false },
|
||||
|
|
@ -23,23 +25,24 @@ const props = defineProps({
|
|||
|
||||
let marker_end = {
|
||||
type: "arrow",
|
||||
width: 20,
|
||||
height: 20,
|
||||
width: 15,
|
||||
height: 15,
|
||||
strokeWidth: 1.5,
|
||||
color: "#687178"
|
||||
};
|
||||
|
||||
let marker_end_primary = {
|
||||
type: "arrow",
|
||||
width: 15,
|
||||
height: 15,
|
||||
strokeWidth: 1.5,
|
||||
width: 11,
|
||||
height: 11,
|
||||
strokeWidth: 1.7,
|
||||
color: "#2490ef"
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selected,
|
||||
() => {
|
||||
if (props.target?.startsWith("action-")) return;
|
||||
findEdge(props.id).markerEnd = props.selected ? marker_end_primary : marker_end;
|
||||
},
|
||||
{ immediate: true }
|
||||
|
|
|
|||
|
|
@ -6,13 +6,23 @@ export const useStore = defineStore("workflow-builder-store", () => {
|
|||
elements: [
|
||||
{ id: "1", label: "Open", type: "state", position: { x: 300, y: 150 } },
|
||||
{ id: "2", label: "Approved", type: "state", position: { x: 700, y: 150 } },
|
||||
{ id: "action-1", label: "Approve", type: "action", position: { x: 500, y: 170 } },
|
||||
{
|
||||
id: "edge-1-2",
|
||||
id: "edge-1-action-1",
|
||||
source: "1",
|
||||
target: "action-1",
|
||||
type: "transition",
|
||||
sourceHandle: "right",
|
||||
targetHandle: "left",
|
||||
animated: true,
|
||||
},
|
||||
{
|
||||
id: "edge-action-1-2",
|
||||
source: "action-1",
|
||||
target: "2",
|
||||
type: "transition",
|
||||
sourceHandle: "b",
|
||||
targetHandle: "d",
|
||||
sourceHandle: "right",
|
||||
targetHandle: "left",
|
||||
animated: true,
|
||||
},
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue