@yh-ui/flow 1.0.53 → 1.0.55

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/dist/Flow.vue CHANGED
@@ -120,7 +120,7 @@
120
120
  </template>
121
121
 
122
122
  <script setup>
123
- import { ref, computed, watch, onMounted, onBeforeUnmount, shallowRef, useId } from "vue";
123
+ import { ref, computed, watch, onMounted, onBeforeUnmount, shallowRef, useId, toRaw } from "vue";
124
124
  if (typeof window !== "undefined") window.__YH_FLOW_VERSION__ = "1.0.1";
125
125
  import EdgeRenderer from "./renderer/EdgeRenderer.vue";
126
126
  import EdgeHandlesRenderer from "./renderer/EdgeHandlesRenderer.vue";
@@ -860,16 +860,19 @@ pluginManager.init(flowInstance);
860
860
  defineExpose(flowInstance);
861
861
  watch(
862
862
  () => props.nodes,
863
- (newNodes) => {
864
- if (draggingNodeId.value) return;
865
- nodesRef.value = newNodes || [];
863
+ (nodes) => {
864
+ if (toRaw(nodesRef.value) !== toRaw(nodes)) {
865
+ nodesRef.value = nodes;
866
+ }
866
867
  },
867
868
  { deep: true }
868
869
  );
869
870
  watch(
870
871
  () => props.edges,
871
- (newEdges) => {
872
- edgesRef.value = newEdges || [];
872
+ (edges) => {
873
+ if (toRaw(edgesRef.value) !== toRaw(edges)) {
874
+ edgesRef.value = edges;
875
+ }
873
876
  },
874
877
  { deep: true }
875
878
  );
@@ -20,7 +20,15 @@ const defaultOptions = {
20
20
  workerUrl: ""
21
21
  };
22
22
  async function applyDagreLayout(nodes, edges, options) {
23
- const dagreLib = await Promise.resolve().then(() => require("dagre"));
23
+ let dagreLib;
24
+ try {
25
+ dagreLib = await Promise.resolve().then(() => require("dagre"));
26
+ } catch {
27
+ throw new Error('[YH-UI Flow] Layout engine "dagre" is not installed. Please install "dagre" to use the dagre layout algorithm.');
28
+ }
29
+ if (!dagreLib) {
30
+ throw new Error('[YH-UI Flow] Layout engine "dagre" is not installed. Please install "dagre" to use the dagre layout algorithm.');
31
+ }
24
32
  const dagre = dagreLib.default || dagreLib;
25
33
  const graphlib = dagreLib.graphlib || dagre.graphlib;
26
34
  const g = new graphlib.Graph();
@@ -63,11 +71,18 @@ async function applyElkLayout(nodes, edges, options) {
63
71
  const elkPath = "elkjs";
64
72
  let elkLib;
65
73
  try {
66
- elkLib = await Promise.resolve(`${/* @vite-ignore */
67
- bundledPath}`).then(s => require(s));
74
+ try {
75
+ elkLib = await Promise.resolve(`${/* @vite-ignore */
76
+ bundledPath}`).then(s => require(s));
77
+ } catch {
78
+ elkLib = await Promise.resolve(`${/* @vite-ignore */
79
+ elkPath}`).then(s => require(s));
80
+ }
68
81
  } catch {
69
- elkLib = await Promise.resolve(`${/* @vite-ignore */
70
- elkPath}`).then(s => require(s));
82
+ throw new Error('[YH-UI Flow] Layout engine "elkjs" is not installed. Please install "elkjs" to use the elk layout algorithm.');
83
+ }
84
+ if (!elkLib) {
85
+ throw new Error('[YH-UI Flow] Layout engine "elkjs" is not installed. Please install "elkjs" to use the elk layout algorithm.');
71
86
  }
72
87
  const ELK = elkLib.default || elkLib;
73
88
  const elk = new ELK();
@@ -116,8 +131,16 @@ async function applyElkLayout(nodes, edges, options) {
116
131
  }
117
132
  async function applyForceLayout(nodes, edges, options, flowInstance) {
118
133
  const d3ForcePath = "d3-force";
119
- const d3ForceLib = await Promise.resolve(`${/* @vite-ignore */
120
- d3ForcePath}`).then(s => require(s));
134
+ let d3ForceLib;
135
+ try {
136
+ d3ForceLib = await Promise.resolve(`${/* @vite-ignore */
137
+ d3ForcePath}`).then(s => require(s));
138
+ } catch {
139
+ throw new Error('[YH-UI Flow] Layout engine "d3-force" is not installed. Please install "d3-force" to use the force layout algorithm.');
140
+ }
141
+ if (!d3ForceLib) {
142
+ throw new Error('[YH-UI Flow] Layout engine "d3-force" is not installed. Please install "d3-force" to use the force layout algorithm.');
143
+ }
121
144
  const d3Force = d3ForceLib.default || d3ForceLib;
122
145
  const forceNodes = nodes.map(node => ({
123
146
  id: node.id,
@@ -12,7 +12,22 @@ const defaultOptions = {
12
12
  workerUrl: ""
13
13
  };
14
14
  async function applyDagreLayout(nodes, edges, options) {
15
- const dagreLib = await import("dagre");
15
+ let dagreLib;
16
+ try {
17
+ dagreLib = await import(
18
+ /* @vite-ignore */
19
+ "dagre"
20
+ );
21
+ } catch {
22
+ throw new Error(
23
+ '[YH-UI Flow] Layout engine "dagre" is not installed. Please install "dagre" to use the dagre layout algorithm.'
24
+ );
25
+ }
26
+ if (!dagreLib) {
27
+ throw new Error(
28
+ '[YH-UI Flow] Layout engine "dagre" is not installed. Please install "dagre" to use the dagre layout algorithm.'
29
+ );
30
+ }
16
31
  const dagre = dagreLib.default || dagreLib;
17
32
  const graphlib = dagreLib.graphlib || dagre.graphlib;
18
33
  const g = new graphlib.Graph();
@@ -52,14 +67,25 @@ async function applyElkLayout(nodes, edges, options) {
52
67
  const elkPath = "elkjs";
53
68
  let elkLib;
54
69
  try {
55
- elkLib = await import(
56
- /* @vite-ignore */
57
- bundledPath
58
- );
70
+ try {
71
+ elkLib = await import(
72
+ /* @vite-ignore */
73
+ bundledPath
74
+ );
75
+ } catch {
76
+ elkLib = await import(
77
+ /* @vite-ignore */
78
+ elkPath
79
+ );
80
+ }
59
81
  } catch {
60
- elkLib = await import(
61
- /* @vite-ignore */
62
- elkPath
82
+ throw new Error(
83
+ '[YH-UI Flow] Layout engine "elkjs" is not installed. Please install "elkjs" to use the elk layout algorithm.'
84
+ );
85
+ }
86
+ if (!elkLib) {
87
+ throw new Error(
88
+ '[YH-UI Flow] Layout engine "elkjs" is not installed. Please install "elkjs" to use the elk layout algorithm.'
63
89
  );
64
90
  }
65
91
  const ELK = elkLib.default || elkLib;
@@ -106,10 +132,22 @@ async function applyElkLayout(nodes, edges, options) {
106
132
  }
107
133
  async function applyForceLayout(nodes, edges, options, flowInstance) {
108
134
  const d3ForcePath = "d3-force";
109
- const d3ForceLib = await import(
110
- /* @vite-ignore */
111
- d3ForcePath
112
- );
135
+ let d3ForceLib;
136
+ try {
137
+ d3ForceLib = await import(
138
+ /* @vite-ignore */
139
+ d3ForcePath
140
+ );
141
+ } catch {
142
+ throw new Error(
143
+ '[YH-UI Flow] Layout engine "d3-force" is not installed. Please install "d3-force" to use the force layout algorithm.'
144
+ );
145
+ }
146
+ if (!d3ForceLib) {
147
+ throw new Error(
148
+ '[YH-UI Flow] Layout engine "d3-force" is not installed. Please install "d3-force" to use the force layout algorithm.'
149
+ );
150
+ }
113
151
  const d3Force = d3ForceLib.default || d3ForceLib;
114
152
  const forceNodes = nodes.map((node) => ({
115
153
  id: node.id,
@@ -14,7 +14,11 @@
14
14
  >
15
15
  <defs>
16
16
  <!-- Dynamic masks to create a true gap in the line behind the label -->
17
- <mask v-for="ed in edgeData" :key="`mask-${ed.edge.id}`" :id="getMaskId(ed.edge.id)">
17
+ <mask
18
+ v-for="ed in edgeData.filter(e => e.edge.label)"
19
+ :key="`mask-${ed.edge.id}`"
20
+ :id="getMaskId(ed.edge.id)"
21
+ >
18
22
  <rect x="-5000" y="-5000" width="10000" height="10000" fill="white" />
19
23
  <rect
20
24
  :x="ed.labelX - ed.labelWidth / 2 - 4"
@@ -61,8 +65,28 @@
61
65
  style="cursor: pointer; pointer-events: all"
62
66
  />
63
67
 
64
- <!-- Visible Path -->
68
+ <!-- Visible Path with Mask (when label exists) -->
65
69
  <path
70
+ v-if="ed.edge.label"
71
+ :d="ed.path"
72
+ :stroke="ed.stroke"
73
+ :stroke-width="ed.strokeWidth"
74
+ fill="none"
75
+ :class="{
76
+ 'yh-flow-edge-path': true,
77
+ 'is-animated': ed.edge.animated
78
+ }"
79
+ :mask="`url(#${getMaskId(ed.edge.id)})`"
80
+ :style="{
81
+ pointerEvents: 'none',
82
+ transition: 'stroke 0.2s, stroke-width 0.2s',
83
+ stroke: ed.stroke
84
+ }"
85
+ />
86
+
87
+ <!-- Visible Path without Mask (when no label exists) -->
88
+ <path
89
+ v-else
66
90
  :d="ed.path"
67
91
  :stroke="ed.stroke"
68
92
  :stroke-width="ed.strokeWidth"
@@ -71,7 +95,6 @@
71
95
  'yh-flow-edge-path': true,
72
96
  'is-animated': ed.edge.animated
73
97
  }"
74
- :mask="ed.edge.label ? `url(#${getMaskId(ed.edge.id)})` : void 0"
75
98
  :style="{
76
99
  pointerEvents: 'none',
77
100
  transition: 'stroke 0.2s, stroke-width 0.2s',
@@ -151,6 +174,12 @@ const getLabelStyle = (edge) => {
151
174
  return styles;
152
175
  };
153
176
  const edgeData = computed(() => {
177
+ console.log(
178
+ "EdgeRenderer computed edgeData running. Nodes count:",
179
+ props.nodes.length,
180
+ "Node 1 pos:",
181
+ props.nodes.find((n) => n.id === "1")?.position
182
+ );
154
183
  const result = [];
155
184
  for (const edge of props.edges) {
156
185
  if (!edge || edge.hidden) continue;
@@ -64,7 +64,7 @@
64
64
  </template>
65
65
 
66
66
  <script setup>
67
- import { computed, ref, onMounted, onBeforeUnmount } from "vue";
67
+ import { computed, ref, onMounted, onBeforeUnmount, watch } from "vue";
68
68
  import {
69
69
  getCustomNodeTemplate,
70
70
  getCustomNode,
@@ -373,6 +373,30 @@ onBeforeUnmount(() => {
373
373
  }
374
374
  nodeElements.clear();
375
375
  });
376
+ watch(
377
+ () => props.nodes,
378
+ (newNodes) => {
379
+ let hasChanges = false;
380
+ newNodes.forEach((node) => {
381
+ if (!node.measured) {
382
+ const el = nodeElements.get(node.id);
383
+ if (el) {
384
+ const rect = el.getBoundingClientRect();
385
+ const width = Math.round(rect.width);
386
+ const height = Math.round(rect.height);
387
+ if (width > 0 && height > 0) {
388
+ node.measured = { width, height };
389
+ hasChanges = true;
390
+ }
391
+ }
392
+ }
393
+ });
394
+ if (hasChanges) {
395
+ emit("nodes-measured");
396
+ }
397
+ },
398
+ { flush: "post" }
399
+ );
376
400
  const setNodeRef = (el, nodeId) => {
377
401
  if (el) {
378
402
  nodeElements.set(nodeId, el);
@@ -8,6 +8,7 @@ exports.getEdgeCenter = getEdgeCenter;
8
8
  exports.getEdgePath = getEdgePath;
9
9
  exports.getEdgePosition = getEdgePosition;
10
10
  exports.getHandlePosition = getHandlePosition;
11
+ exports.getSelfLoopPath = getSelfLoopPath;
11
12
  exports.getSmoothStepPath = getSmoothStepPath;
12
13
  exports.getStepPath = getStepPath;
13
14
  exports.getStraightPath = getStraightPath;
@@ -208,7 +209,37 @@ function getSmoothStepPath(params) {
208
209
  return [`M${sourceX},${sourceY}`, `L${sourceX},${midY - sign1Y * r}`, `Q${sourceX},${midY} ${sourceX + sign2X * r},${midY}`, `L${targetX - sign2X * r},${midY}`, `Q${targetX},${midY} ${targetX},${midY + sign1Y * r}`, `L${targetX},${targetY}`].join(" ");
209
210
  }
210
211
  }
212
+ function getSelfLoopPath(sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, nodeWidth, nodeHeight) {
213
+ const w = nodeWidth ?? 150;
214
+ const h = nodeHeight ?? 50;
215
+ const dx = targetX - sourceX;
216
+ const dy = targetY - sourceY;
217
+ const dist = Math.sqrt(dx * dx + dy * dy);
218
+ if (dist < 5) {
219
+ const loopSize = 40;
220
+ if (sourcePosition === "right") {
221
+ return `M${sourceX},${sourceY} C${sourceX + loopSize},${sourceY - loopSize} ${sourceX + loopSize},${sourceY + loopSize} ${sourceX},${sourceY}`;
222
+ } else if (sourcePosition === "left") {
223
+ return `M${sourceX},${sourceY} C${sourceX - loopSize},${sourceY - loopSize} ${sourceX - loopSize},${sourceY + loopSize} ${sourceX},${sourceY}`;
224
+ } else if (sourcePosition === "top") {
225
+ return `M${sourceX},${sourceY} C${sourceX - loopSize},${sourceY - loopSize} ${sourceX + loopSize},${sourceY - loopSize} ${sourceX},${sourceY}`;
226
+ } else {
227
+ return `M${sourceX},${sourceY} C${sourceX - loopSize},${sourceY + loopSize} ${sourceX + loopSize},${sourceY + loopSize} ${sourceX},${sourceY}`;
228
+ }
229
+ }
230
+ const offset = Math.max(w, h, 60) * 0.5;
231
+ let c1x = sourceX;
232
+ let c1y = sourceY;
233
+ if (sourcePosition === "right") c1x += offset;else if (sourcePosition === "left") c1x -= offset;else if (sourcePosition === "top") c1y -= offset;else if (sourcePosition === "bottom") c1y += offset;
234
+ let c2x = targetX;
235
+ let c2y = targetY;
236
+ if (targetPosition === "right") c2x += offset;else if (targetPosition === "left") c2x -= offset;else if (targetPosition === "top") c2y -= offset;else if (targetPosition === "bottom") c2y += offset;
237
+ return `M${sourceX},${sourceY} C${c1x},${c1y} ${c2x},${c2y} ${targetX},${targetY}`;
238
+ }
211
239
  function getEdgePath(type, params) {
240
+ if (params.isSelfLoop) {
241
+ return getSelfLoopPath(params.sourceX, params.sourceY, params.targetX, params.targetY, params.sourcePosition, params.targetPosition, params.nodeWidth, params.nodeHeight);
242
+ }
212
243
  switch (type) {
213
244
  case "bezier":
214
245
  case "default":
@@ -229,8 +260,58 @@ function getEdgeCenter(params) {
229
260
  sourceY,
230
261
  targetX,
231
262
  targetY,
232
- type = "bezier"
263
+ type = "bezier",
264
+ isSelfLoop,
265
+ nodeWidth,
266
+ nodeHeight,
267
+ sourcePosition,
268
+ targetPosition
233
269
  } = params;
270
+ if (isSelfLoop) {
271
+ const w = nodeWidth ?? 150;
272
+ const h = nodeHeight ?? 50;
273
+ const dx = targetX - sourceX;
274
+ const dy = targetY - sourceY;
275
+ const dist = Math.sqrt(dx * dx + dy * dy);
276
+ let c1x = sourceX,
277
+ c1y = sourceY,
278
+ c2x = targetX,
279
+ c2y = targetY;
280
+ if (dist < 5) {
281
+ const loopSize = 40;
282
+ if (sourcePosition === "right") {
283
+ c1x = sourceX + loopSize;
284
+ c1y = sourceY - loopSize;
285
+ c2x = sourceX + loopSize;
286
+ c2y = sourceY + loopSize;
287
+ } else if (sourcePosition === "left") {
288
+ c1x = sourceX - loopSize;
289
+ c1y = sourceY - loopSize;
290
+ c2x = sourceX - loopSize;
291
+ c2y = sourceY + loopSize;
292
+ } else if (sourcePosition === "top") {
293
+ c1x = sourceX - loopSize;
294
+ c1y = sourceY - loopSize;
295
+ c2x = sourceX + loopSize;
296
+ c2y = sourceY - loopSize;
297
+ } else {
298
+ c1x = sourceX - loopSize;
299
+ c1y = sourceY + loopSize;
300
+ c2x = sourceX + loopSize;
301
+ c2y = sourceY + loopSize;
302
+ }
303
+ } else {
304
+ const offset = Math.max(w, h, 60) * 0.5;
305
+ if (sourcePosition === "right") c1x += offset;else if (sourcePosition === "left") c1x -= offset;else if (sourcePosition === "top") c1y -= offset;else if (sourcePosition === "bottom") c1y += offset;
306
+ if (targetPosition === "right") c2x += offset;else if (targetPosition === "left") c2x -= offset;else if (targetPosition === "top") c2y -= offset;else if (targetPosition === "bottom") c2y += offset;
307
+ }
308
+ return {
309
+ x: 0.125 * sourceX + 0.375 * c1x + 0.375 * c2x + 0.125 * targetX,
310
+ y: 0.125 * sourceY + 0.375 * c1y + 0.375 * c2y + 0.125 * targetY,
311
+ ox: 0,
312
+ oy: 0
313
+ };
314
+ }
234
315
  if (type === "bezier" || type === "default") {
235
316
  const curvature = params.curvature ?? 0.25;
236
317
  const srcDir = getDir(params.sourcePosition);
@@ -8,6 +8,9 @@ export interface EdgePathParams {
8
8
  targetPosition: Position;
9
9
  /** 控制曲率,0~1,默认 0.25 */
10
10
  curvature?: number;
11
+ isSelfLoop?: boolean;
12
+ nodeWidth?: number;
13
+ nodeHeight?: number;
11
14
  }
12
15
  /**
13
16
  * 获取连接点的位置坐标
@@ -60,6 +63,10 @@ export declare function getStepPath(params: EdgePathParams): string;
60
63
  * 生成平滑阶梯线路径(带圆角转折的 step)
61
64
  */
62
65
  export declare function getSmoothStepPath(params: EdgePathParams): string;
66
+ /**
67
+ * 生成自环曲线路径
68
+ */
69
+ export declare function getSelfLoopPath(sourceX: number, sourceY: number, targetX: number, targetY: number, sourcePosition: Position, targetPosition: Position, nodeWidth?: number, nodeHeight?: number): string;
63
70
  /**
64
71
  * 根据类型获取连线路径
65
72
  */
@@ -148,7 +148,52 @@ export function getSmoothStepPath(params) {
148
148
  ].join(" ");
149
149
  }
150
150
  }
151
+ export function getSelfLoopPath(sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, nodeWidth, nodeHeight) {
152
+ const w = nodeWidth ?? 150;
153
+ const h = nodeHeight ?? 50;
154
+ const dx = targetX - sourceX;
155
+ const dy = targetY - sourceY;
156
+ const dist = Math.sqrt(dx * dx + dy * dy);
157
+ if (dist < 5) {
158
+ const loopSize = 40;
159
+ if (sourcePosition === "right") {
160
+ return `M${sourceX},${sourceY} C${sourceX + loopSize},${sourceY - loopSize} ${sourceX + loopSize},${sourceY + loopSize} ${sourceX},${sourceY}`;
161
+ } else if (sourcePosition === "left") {
162
+ return `M${sourceX},${sourceY} C${sourceX - loopSize},${sourceY - loopSize} ${sourceX - loopSize},${sourceY + loopSize} ${sourceX},${sourceY}`;
163
+ } else if (sourcePosition === "top") {
164
+ return `M${sourceX},${sourceY} C${sourceX - loopSize},${sourceY - loopSize} ${sourceX + loopSize},${sourceY - loopSize} ${sourceX},${sourceY}`;
165
+ } else {
166
+ return `M${sourceX},${sourceY} C${sourceX - loopSize},${sourceY + loopSize} ${sourceX + loopSize},${sourceY + loopSize} ${sourceX},${sourceY}`;
167
+ }
168
+ }
169
+ const offset = Math.max(w, h, 60) * 0.5;
170
+ let c1x = sourceX;
171
+ let c1y = sourceY;
172
+ if (sourcePosition === "right") c1x += offset;
173
+ else if (sourcePosition === "left") c1x -= offset;
174
+ else if (sourcePosition === "top") c1y -= offset;
175
+ else if (sourcePosition === "bottom") c1y += offset;
176
+ let c2x = targetX;
177
+ let c2y = targetY;
178
+ if (targetPosition === "right") c2x += offset;
179
+ else if (targetPosition === "left") c2x -= offset;
180
+ else if (targetPosition === "top") c2y -= offset;
181
+ else if (targetPosition === "bottom") c2y += offset;
182
+ return `M${sourceX},${sourceY} C${c1x},${c1y} ${c2x},${c2y} ${targetX},${targetY}`;
183
+ }
151
184
  export function getEdgePath(type, params) {
185
+ if (params.isSelfLoop) {
186
+ return getSelfLoopPath(
187
+ params.sourceX,
188
+ params.sourceY,
189
+ params.targetX,
190
+ params.targetY,
191
+ params.sourcePosition,
192
+ params.targetPosition,
193
+ params.nodeWidth,
194
+ params.nodeHeight
195
+ );
196
+ }
152
197
  switch (type) {
153
198
  case "bezier":
154
199
  case "default":
@@ -164,7 +209,66 @@ export function getEdgePath(type, params) {
164
209
  }
165
210
  }
166
211
  export function getEdgeCenter(params) {
167
- const { sourceX, sourceY, targetX, targetY, type = "bezier" } = params;
212
+ const {
213
+ sourceX,
214
+ sourceY,
215
+ targetX,
216
+ targetY,
217
+ type = "bezier",
218
+ isSelfLoop,
219
+ nodeWidth,
220
+ nodeHeight,
221
+ sourcePosition,
222
+ targetPosition
223
+ } = params;
224
+ if (isSelfLoop) {
225
+ const w = nodeWidth ?? 150;
226
+ const h = nodeHeight ?? 50;
227
+ const dx = targetX - sourceX;
228
+ const dy = targetY - sourceY;
229
+ const dist = Math.sqrt(dx * dx + dy * dy);
230
+ let c1x = sourceX, c1y = sourceY, c2x = targetX, c2y = targetY;
231
+ if (dist < 5) {
232
+ const loopSize = 40;
233
+ if (sourcePosition === "right") {
234
+ c1x = sourceX + loopSize;
235
+ c1y = sourceY - loopSize;
236
+ c2x = sourceX + loopSize;
237
+ c2y = sourceY + loopSize;
238
+ } else if (sourcePosition === "left") {
239
+ c1x = sourceX - loopSize;
240
+ c1y = sourceY - loopSize;
241
+ c2x = sourceX - loopSize;
242
+ c2y = sourceY + loopSize;
243
+ } else if (sourcePosition === "top") {
244
+ c1x = sourceX - loopSize;
245
+ c1y = sourceY - loopSize;
246
+ c2x = sourceX + loopSize;
247
+ c2y = sourceY - loopSize;
248
+ } else {
249
+ c1x = sourceX - loopSize;
250
+ c1y = sourceY + loopSize;
251
+ c2x = sourceX + loopSize;
252
+ c2y = sourceY + loopSize;
253
+ }
254
+ } else {
255
+ const offset = Math.max(w, h, 60) * 0.5;
256
+ if (sourcePosition === "right") c1x += offset;
257
+ else if (sourcePosition === "left") c1x -= offset;
258
+ else if (sourcePosition === "top") c1y -= offset;
259
+ else if (sourcePosition === "bottom") c1y += offset;
260
+ if (targetPosition === "right") c2x += offset;
261
+ else if (targetPosition === "left") c2x -= offset;
262
+ else if (targetPosition === "top") c2y -= offset;
263
+ else if (targetPosition === "bottom") c2y += offset;
264
+ }
265
+ return {
266
+ x: 0.125 * sourceX + 0.375 * c1x + 0.375 * c2x + 0.125 * targetX,
267
+ y: 0.125 * sourceY + 0.375 * c1y + 0.375 * c2y + 0.125 * targetY,
268
+ ox: 0,
269
+ oy: 0
270
+ };
271
+ }
168
272
  if (type === "bezier" || type === "default") {
169
273
  const curvature = params.curvature ?? 0.25;
170
274
  const srcDir = getDir(params.sourcePosition);
@@ -56,14 +56,7 @@ function isValidConnection(sourceNode, targetNode, connection) {
56
56
  }
57
57
  if (connection.source === connection.target) {
58
58
  return {
59
- isValid: false,
60
- message: "Cannot connect to the same node"
61
- };
62
- }
63
- if (connection.target === connection.source) {
64
- return {
65
- isValid: false,
66
- message: "Cannot create self-loop"
59
+ isValid: true
67
60
  };
68
61
  }
69
62
  return {
@@ -41,10 +41,7 @@ export function isValidConnection(sourceNode, targetNode, connection) {
41
41
  return { isValid: false, message: "Target node not found" };
42
42
  }
43
43
  if (connection.source === connection.target) {
44
- return { isValid: false, message: "Cannot connect to the same node" };
45
- }
46
- if (connection.target === connection.source) {
47
- return { isValid: false, message: "Cannot create self-loop" };
44
+ return { isValid: true };
48
45
  }
49
46
  return { isValid: true };
50
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yh-ui/flow",
3
- "version": "1.0.53",
3
+ "version": "1.0.55",
4
4
  "description": "YH-UI High-performance Flow Chart Component",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -32,8 +32,8 @@
32
32
  "lint": "eslint ."
33
33
  },
34
34
  "dependencies": {
35
- "@yh-ui/utils": "^1.0.53",
36
- "@yh-ui/hooks": "^1.0.53"
35
+ "@yh-ui/utils": "^1.0.55",
36
+ "@yh-ui/hooks": "^1.0.55"
37
37
  },
38
38
  "devDependencies": {
39
39
  "vue": "^3.5.35",
@@ -44,7 +44,8 @@
44
44
  "vue": "^3.5.35",
45
45
  "dagre": ">=0.8.5",
46
46
  "elkjs": ">=0.9.0",
47
- "d3-force": ">=3.0.0"
47
+ "d3-force": ">=3.0.0",
48
+ "html-to-image": ">=1.11.0"
48
49
  },
49
50
  "peerDependenciesMeta": {
50
51
  "dagre": {
@@ -55,6 +56,9 @@
55
56
  },
56
57
  "d3-force": {
57
58
  "optional": true
59
+ },
60
+ "html-to-image": {
61
+ "optional": true
58
62
  }
59
63
  },
60
64
  "publishConfig": {