153 lines
3.7 KiB
Vue
153 lines
3.7 KiB
Vue
<script lang="ts" setup>
|
|
/* eslint-disable vue/no-multiple-template-root */
|
|
import type { CanvasConnectionData } from '@/types';
|
|
import { isValidNodeConnectionType } from '@/utils/typeGuards';
|
|
import type { Connection, EdgeProps } from '@vue-flow/core';
|
|
import { useVueFlow, BaseEdge, EdgeLabelRenderer } from '@vue-flow/core';
|
|
import { NodeConnectionType } from 'n8n-workflow';
|
|
import { computed, useCssModule, ref, toRef } from 'vue';
|
|
import CanvasEdgeToolbar from './CanvasEdgeToolbar.vue';
|
|
import { getCustomPath } from './utils/edgePath';
|
|
|
|
const emit = defineEmits<{
|
|
add: [connection: Connection];
|
|
delete: [connection: Connection];
|
|
}>();
|
|
|
|
export type CanvasEdgeProps = EdgeProps<CanvasConnectionData> & {
|
|
readOnly?: boolean;
|
|
hovered?: boolean;
|
|
};
|
|
|
|
const props = defineProps<CanvasEdgeProps>();
|
|
|
|
const data = toRef(props, 'data');
|
|
|
|
const { onEdgeMouseEnter, onEdgeMouseLeave } = useVueFlow();
|
|
|
|
const isHovered = ref(false);
|
|
|
|
onEdgeMouseEnter(({ edge }) => {
|
|
if (edge.id !== props.id) return;
|
|
isHovered.value = true;
|
|
});
|
|
onEdgeMouseLeave(({ edge }) => {
|
|
if (edge.id !== props.id) return;
|
|
isHovered.value = false;
|
|
});
|
|
|
|
const $style = useCssModule();
|
|
|
|
const connectionType = computed(() =>
|
|
isValidNodeConnectionType(props.data.source.type)
|
|
? props.data.source.type
|
|
: NodeConnectionType.Main,
|
|
);
|
|
|
|
const renderToolbar = computed(() => (props.selected || isHovered.value) && !props.readOnly);
|
|
|
|
const isMainConnection = computed(() => data.value.source.type === NodeConnectionType.Main);
|
|
|
|
const status = computed(() => props.data.status);
|
|
const statusColor = computed(() => {
|
|
if (props.selected) {
|
|
return 'var(--color-background-dark)';
|
|
} else if (status.value === 'success') {
|
|
return 'var(--color-success)';
|
|
} else if (status.value === 'pinned') {
|
|
return 'var(--color-secondary)';
|
|
} else if (status.value === 'running') {
|
|
return 'var(--color-primary)';
|
|
} else if (!isMainConnection.value) {
|
|
return 'var(--node-type-supplemental-color)';
|
|
} else {
|
|
return 'var(--color-foreground-xdark)';
|
|
}
|
|
});
|
|
|
|
const edgeStyle = computed(() => ({
|
|
...props.style,
|
|
...(isMainConnection.value ? {} : { strokeDasharray: '8,8' }),
|
|
strokeWidth: 2,
|
|
stroke: isHovered.value ? 'var(--color-primary)' : statusColor.value,
|
|
}));
|
|
|
|
const edgeLabelStyle = computed(() => ({ color: statusColor.value }));
|
|
|
|
const edgeToolbarStyle = computed(() => {
|
|
const [, labelX, labelY] = path.value;
|
|
return {
|
|
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
|
};
|
|
});
|
|
|
|
const path = computed(() => getCustomPath(props));
|
|
|
|
const connection = computed<Connection>(() => ({
|
|
source: props.source,
|
|
target: props.target,
|
|
sourceHandle: props.sourceHandleId,
|
|
targetHandle: props.targetHandleId,
|
|
}));
|
|
|
|
function onAdd() {
|
|
emit('add', connection.value);
|
|
}
|
|
|
|
function onDelete() {
|
|
emit('delete', connection.value);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<BaseEdge
|
|
:id="id"
|
|
:class="$style.edge"
|
|
:style="edgeStyle"
|
|
:path="path[0]"
|
|
:marker-end="markerEnd"
|
|
:interaction-width="40"
|
|
/>
|
|
|
|
<EdgeLabelRenderer>
|
|
<div
|
|
data-test-id="edge-label-wrapper"
|
|
:style="edgeToolbarStyle"
|
|
:class="$style.edgeLabelWrapper"
|
|
@mouseenter="isHovered = true"
|
|
@mouseleave="isHovered = false"
|
|
>
|
|
<CanvasEdgeToolbar
|
|
v-if="renderToolbar"
|
|
:type="connectionType"
|
|
@add="onAdd"
|
|
@delete="onDelete"
|
|
/>
|
|
<div v-else :style="edgeLabelStyle" :class="$style.edgeLabel">{{ label }}</div>
|
|
</div>
|
|
</EdgeLabelRenderer>
|
|
</template>
|
|
|
|
<style lang="scss" module>
|
|
.edge {
|
|
transition:
|
|
stroke 0.3s ease,
|
|
fill 0.3s ease;
|
|
}
|
|
|
|
.edgeLabelWrapper {
|
|
transform: translateY(calc(var(--spacing-xs) * -1));
|
|
position: absolute;
|
|
}
|
|
|
|
.edgeLabel {
|
|
font-size: var(--font-size-xs);
|
|
background-color: hsla(
|
|
var(--color-canvas-background-h),
|
|
var(--color-canvas-background-s),
|
|
var(--color-canvas-background-l),
|
|
0.85
|
|
);
|
|
}
|
|
</style>
|