@canmingir/link 1.2.18 → 1.2.19

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canmingir/link",
3
- "version": "1.2.18",
3
+ "version": "1.2.19",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -1,3 +1,5 @@
1
+ import { Box } from "@mui/material";
2
+
1
3
  import React, { useId, useLayoutEffect, useMemo, useState } from "react";
2
4
 
3
5
  const DynamicConnector = ({
@@ -19,6 +21,11 @@ const DynamicConnector = ({
19
21
  gradient = null,
20
22
  curvature = 0.5,
21
23
  connectorType = "curved",
24
+ label = null,
25
+ labelStyle = {},
26
+ labelPosition = 0.5,
27
+ labelOffsetX = 0,
28
+ labelOffsetY = -10,
22
29
  }) => {
23
30
  const uniqueId = useId();
24
31
  const [dims, setDims] = useState(null);
@@ -143,118 +150,169 @@ const DynamicConnector = ({
143
150
  const dashArray = getDashArray();
144
151
 
145
152
  return (
146
- <svg
147
- style={{
148
- position: "absolute",
149
- inset: 0,
150
- pointerEvents: "none",
151
- overflow: "visible",
152
- zIndex: 0,
153
- }}
154
- width="100%"
155
- height="100%"
156
- viewBox={`0 0 ${dims.w} ${dims.h}`}
157
- >
158
- <defs>
159
- {gradient && (
160
- <linearGradient
161
- id={ids.gradient}
162
- gradientUnits="userSpaceOnUse"
163
- x1={points.parent.x}
164
- y1={points.parent.y}
165
- x2={points.children[0]?.x || points.parent.x}
166
- y2={points.children[0]?.y || points.parent.y}
167
- >
168
- <stop offset="0%" stopColor={gradient.from} />
169
- <stop offset="100%" stopColor={gradient.to} />
170
- </linearGradient>
171
- )}
172
-
173
- {showArrow && (
174
- <marker
175
- id={ids.arrow}
176
- viewBox="0 0 10 10"
177
- refX="9"
178
- refY="5"
179
- markerWidth={arrowSize}
180
- markerHeight={arrowSize}
181
- orient="auto-start-reverse"
182
- >
183
- <path
184
- d="M 0 0 L 10 5 L 0 10 z"
185
- fill={gradient ? `url(#${ids.gradient})` : stroke}
186
- />
187
- </marker>
188
- )}
189
-
190
- {animated && <style>{animationStyle}</style>}
191
- </defs>
192
-
193
- {points.children.map((child, i) => {
194
- const pathD = getPath(points.parent, child);
195
- const pathStroke = gradient ? `url(#${ids.gradient})` : stroke;
196
-
197
- return (
198
- <g key={i}>
199
- <path
200
- d={pathD}
201
- fill="none"
202
- stroke={pathStroke}
203
- strokeWidth={strokeWidth}
204
- strokeDasharray={animated ? "8,4" : dashArray}
205
- strokeLinecap="round"
206
- strokeLinejoin="round"
207
- markerEnd={showArrow ? `url(#${ids.arrow})` : undefined}
208
- style={{
209
- transition: "stroke 0.2s ease, stroke-width 0.2s ease",
210
- ...(animated
211
- ? {
212
- animation: `flowAnimation ${
213
- 0.5 / animationSpeed
214
- }s linear infinite`,
215
- }
216
- : {}),
217
- }}
218
- />
219
- </g>
220
- );
221
- })}
222
-
223
- {showDots && (
224
- <>
225
- <circle
226
- cx={points.parent.x}
227
- cy={points.parent.y}
228
- r={dotRadius}
229
- fill={gradient ? gradient.from : effectiveDotColor}
230
- stroke="#fff"
231
- strokeWidth={1.5}
232
- style={{
233
- filter: "drop-shadow(0 1px 2px rgba(0,0,0,0.15))",
234
- }}
235
- />
236
-
237
- {points.children.map((child, i) => {
238
- const dotFill = gradient ? gradient.to : effectiveDotColor;
239
-
240
- return (
241
- <circle
242
- key={i}
243
- cx={child.x}
244
- cy={child.y}
245
- r={dotRadius}
246
- fill={dotFill}
247
- stroke="#fff"
248
- strokeWidth={1.5}
153
+ <>
154
+ <svg
155
+ style={{
156
+ position: "absolute",
157
+ inset: 0,
158
+ pointerEvents: "none",
159
+ overflow: "visible",
160
+ zIndex: 0,
161
+ }}
162
+ width="100%"
163
+ height="100%"
164
+ viewBox={`0 0 ${dims.w} ${dims.h}`}
165
+ >
166
+ <defs>
167
+ {gradient && (
168
+ <linearGradient
169
+ id={ids.gradient}
170
+ gradientUnits="userSpaceOnUse"
171
+ x1={points.parent.x}
172
+ y1={points.parent.y}
173
+ x2={points.children[0]?.x || points.parent.x}
174
+ y2={points.children[0]?.y || points.parent.y}
175
+ >
176
+ <stop offset="0%" stopColor={gradient.from} />
177
+ <stop offset="100%" stopColor={gradient.to} />
178
+ </linearGradient>
179
+ )}
180
+
181
+ {showArrow && (
182
+ <marker
183
+ id={ids.arrow}
184
+ viewBox="0 0 10 10"
185
+ refX="9"
186
+ refY="5"
187
+ markerWidth={arrowSize}
188
+ markerHeight={arrowSize}
189
+ orient="auto-start-reverse"
190
+ >
191
+ <path
192
+ d="M 0 0 L 10 5 L 0 10 z"
193
+ fill={gradient ? `url(#${ids.gradient})` : stroke}
194
+ />
195
+ </marker>
196
+ )}
197
+
198
+ {animated && <style>{animationStyle}</style>}
199
+ </defs>
200
+
201
+ {points.children.map((child, i) => {
202
+ const pathD = getPath(points.parent, child);
203
+ const pathStroke = gradient ? `url(#${ids.gradient})` : stroke;
204
+
205
+ const labelX =
206
+ points.parent.x +
207
+ (child.x - points.parent.x) * labelPosition +
208
+ labelOffsetX;
209
+ const labelY =
210
+ points.parent.y +
211
+ (child.y - points.parent.y) * labelPosition +
212
+ labelOffsetY;
213
+
214
+ return (
215
+ <g key={i}>
216
+ <path
217
+ d={pathD}
218
+ fill="none"
219
+ stroke={pathStroke}
220
+ strokeWidth={strokeWidth}
221
+ strokeDasharray={animated ? "8,4" : dashArray}
222
+ strokeLinecap="round"
223
+ strokeLinejoin="round"
224
+ markerEnd={showArrow ? `url(#${ids.arrow})` : undefined}
249
225
  style={{
250
- filter: "drop-shadow(0 1px 2px rgba(0,0,0,0.15))",
226
+ transition: "stroke 0.2s ease, stroke-width 0.2s ease",
227
+ ...(animated
228
+ ? {
229
+ animation: `flowAnimation ${
230
+ 0.5 / animationSpeed
231
+ }s linear infinite`,
232
+ }
233
+ : {}),
251
234
  }}
252
235
  />
253
- );
254
- })}
255
- </>
256
- )}
257
- </svg>
236
+ </g>
237
+ );
238
+ })}
239
+
240
+ {showDots && (
241
+ <>
242
+ <circle
243
+ cx={points.parent.x}
244
+ cy={points.parent.y}
245
+ r={dotRadius}
246
+ fill={gradient ? gradient.from : effectiveDotColor}
247
+ stroke="#fff"
248
+ strokeWidth={1.5}
249
+ style={{
250
+ filter: "drop-shadow(0 1px 2px rgba(0,0,0,0.15))",
251
+ }}
252
+ />
253
+
254
+ {points.children.map((child, i) => {
255
+ const dotFill = gradient ? gradient.to : effectiveDotColor;
256
+
257
+ return (
258
+ <circle
259
+ key={i}
260
+ cx={child.x}
261
+ cy={child.y}
262
+ r={dotRadius}
263
+ fill={dotFill}
264
+ stroke="#fff"
265
+ strokeWidth={1.5}
266
+ style={{
267
+ filter: "drop-shadow(0 1px 2px rgba(0,0,0,0.15))",
268
+ }}
269
+ />
270
+ );
271
+ })}
272
+ </>
273
+ )}
274
+ </svg>
275
+
276
+ {label &&
277
+ points.children.map((child, i) => {
278
+ const labelX =
279
+ points.parent.x +
280
+ (child.x - points.parent.x) * labelPosition +
281
+ labelOffsetX;
282
+ const labelY =
283
+ points.parent.y +
284
+ (child.y - points.parent.y) * labelPosition +
285
+ labelOffsetY;
286
+
287
+ return (
288
+ <Box
289
+ key={`label-${i}`}
290
+ sx={{
291
+ position: "absolute",
292
+ left: labelX,
293
+ top: labelY,
294
+ transform: "translate(-50%, -50%)",
295
+ px: 1,
296
+ py: 0.5,
297
+ borderRadius: 1,
298
+ backgroundColor: "background.paper",
299
+ border: 1,
300
+ borderColor: labelStyle.color || stroke,
301
+ fontSize: labelStyle.fontSize || "12px",
302
+ fontWeight: labelStyle.fontWeight || 600,
303
+ color: labelStyle.textColor || stroke,
304
+ pointerEvents: "none",
305
+ userSelect: "none",
306
+ zIndex: 10,
307
+ boxShadow: 1,
308
+ whiteSpace: "nowrap",
309
+ }}
310
+ >
311
+ {label}
312
+ </Box>
313
+ );
314
+ })}
315
+ </>
258
316
  );
259
317
  };
260
318
 
@@ -38,6 +38,15 @@ export const useNodeStyle = ({ node, type, variant, style, plugin }) => {
38
38
  }) || {};
39
39
  }
40
40
 
41
+ let pluginEdgeTokens = {};
42
+ if (resolvedPlugin && typeof resolvedPlugin.edge === "function") {
43
+ pluginEdgeTokens =
44
+ resolvedPlugin.edge({
45
+ node,
46
+ style: styleTokens,
47
+ }) || {};
48
+ }
49
+
41
50
  const rawNodeStyle = {
42
51
  ...baseStyle,
43
52
  ...variantTokens,
@@ -47,9 +56,14 @@ export const useNodeStyle = ({ node, type, variant, style, plugin }) => {
47
56
 
48
57
  const nodeStyle = applySemanticTokens(rawNodeStyle, baseStyle);
49
58
 
59
+ const edgeStyle = {
60
+ ...pluginEdgeTokens,
61
+ };
62
+
50
63
  return {
51
64
  baseStyle,
52
65
  nodeStyle,
66
+ edgeStyle,
53
67
  plugin: resolvedPlugin,
54
68
  };
55
69
  }, [node, type, variant, style, plugin]);
@@ -24,6 +24,7 @@ const FlowNodeView = ({
24
24
  const {
25
25
  baseStyle,
26
26
  nodeStyle,
27
+ edgeStyle,
27
28
  plugin: _plugin,
28
29
  } = useNodeStyle({
29
30
  node,
@@ -60,11 +61,29 @@ const FlowNodeView = ({
60
61
  selectionColor = baseStyle.selectionColor ?? "#64748b",
61
62
  } = nodeStyle;
62
63
 
64
+ const edgeProps = {
65
+ lineColor: edgeStyle.lineColor ?? lineColor,
66
+ lineWidth: edgeStyle.lineWidth ?? lineWidth,
67
+ lineStyle: edgeStyle.lineStyle ?? lineStyle,
68
+ showDots: edgeStyle.showDots ?? showDots,
69
+ dotRadius: edgeStyle.dotRadius ?? dotRadius,
70
+ dotColor: edgeStyle.dotColor ?? dotColor,
71
+ showArrow: edgeStyle.showArrow ?? showArrow,
72
+ arrowSize: edgeStyle.arrowSize ?? arrowSize,
73
+ animated: edgeStyle.animated ?? animated,
74
+ animationSpeed: edgeStyle.animationSpeed ?? animationSpeed,
75
+ gradient: edgeStyle.gradient ?? gradient,
76
+ curvature: edgeStyle.curvature ?? curvature,
77
+ connectorType: edgeStyle.connectorType ?? connectorType,
78
+ };
79
+
63
80
  const isHorizontal = direction === "horizontal";
64
81
 
65
- const strokeWidth = toPxNumber(lineWidth, 1.5);
82
+ const strokeWidth = toPxNumber(edgeProps.lineWidth, 1.5);
66
83
  const dashStyle =
67
- lineStyle === "dashed" || lineStyle === "dotted" ? lineStyle : "solid";
84
+ edgeProps.lineStyle === "dashed" || edgeProps.lineStyle === "dotted"
85
+ ? edgeProps.lineStyle
86
+ : "solid";
68
87
 
69
88
  const containerRef = useRef(null);
70
89
  const parentRef = useRef(null);
@@ -153,26 +172,89 @@ const FlowNodeView = ({
153
172
 
154
173
  {hasChildren && (
155
174
  <>
156
- <DynamicConnector
157
- containerEl={containerRef.current}
158
- parentEl={parentRef.current}
159
- childEls={childElList}
160
- stroke={lineColor}
161
- strokeWidth={strokeWidth}
162
- lineStyle={dashStyle}
163
- tick={connectorTick}
164
- orientation={direction}
165
- showDots={showDots}
166
- dotRadius={dotRadius}
167
- dotColor={dotColor}
168
- showArrow={showArrow}
169
- arrowSize={arrowSize}
170
- animated={animated}
171
- animationSpeed={animationSpeed}
172
- gradient={gradient}
173
- curvature={curvature}
174
- connectorType={connectorType}
175
- />
175
+ {node.children.map((child, index) => {
176
+ let childEdgeProps = { ...edgeProps };
177
+ if (_plugin && typeof _plugin.edge === "function") {
178
+ const childSpecificStyle = _plugin.edge({
179
+ node,
180
+ child,
181
+ style: nodeStyle,
182
+ });
183
+ if (childSpecificStyle) {
184
+ childEdgeProps = {
185
+ lineColor:
186
+ childSpecificStyle.lineColor ?? childEdgeProps.lineColor,
187
+ lineWidth:
188
+ childSpecificStyle.lineWidth ?? childEdgeProps.lineWidth,
189
+ lineStyle:
190
+ childSpecificStyle.lineStyle ?? childEdgeProps.lineStyle,
191
+ showDots:
192
+ childSpecificStyle.showDots ?? childEdgeProps.showDots,
193
+ dotRadius:
194
+ childSpecificStyle.dotRadius ?? childEdgeProps.dotRadius,
195
+ dotColor:
196
+ childSpecificStyle.dotColor ?? childEdgeProps.dotColor,
197
+ showArrow:
198
+ childSpecificStyle.showArrow ?? childEdgeProps.showArrow,
199
+ arrowSize:
200
+ childSpecificStyle.arrowSize ?? childEdgeProps.arrowSize,
201
+ animated:
202
+ childSpecificStyle.animated ?? childEdgeProps.animated,
203
+ animationSpeed:
204
+ childSpecificStyle.animationSpeed ??
205
+ childEdgeProps.animationSpeed,
206
+ gradient:
207
+ childSpecificStyle.gradient ?? childEdgeProps.gradient,
208
+ curvature:
209
+ childSpecificStyle.curvature ?? childEdgeProps.curvature,
210
+ connectorType:
211
+ childSpecificStyle.connectorType ??
212
+ childEdgeProps.connectorType,
213
+ label: childSpecificStyle.label,
214
+ labelStyle: childSpecificStyle.labelStyle,
215
+ labelPosition: childSpecificStyle.labelPosition,
216
+ labelOffsetX: childSpecificStyle.labelOffsetX,
217
+ labelOffsetY: childSpecificStyle.labelOffsetY,
218
+ };
219
+ }
220
+ }
221
+
222
+ const childStrokeWidth = toPxNumber(childEdgeProps.lineWidth, 1.5);
223
+ const childDashStyle =
224
+ childEdgeProps.lineStyle === "dashed" ||
225
+ childEdgeProps.lineStyle === "dotted"
226
+ ? childEdgeProps.lineStyle
227
+ : "solid";
228
+
229
+ return (
230
+ <DynamicConnector
231
+ key={child.id}
232
+ containerEl={containerRef.current}
233
+ parentEl={parentRef.current}
234
+ childEls={[childElList[index]]}
235
+ stroke={childEdgeProps.lineColor}
236
+ strokeWidth={childStrokeWidth}
237
+ lineStyle={childDashStyle}
238
+ tick={connectorTick}
239
+ orientation={direction}
240
+ showDots={childEdgeProps.showDots}
241
+ dotRadius={childEdgeProps.dotRadius}
242
+ dotColor={childEdgeProps.dotColor}
243
+ showArrow={childEdgeProps.showArrow}
244
+ arrowSize={childEdgeProps.arrowSize}
245
+ animated={childEdgeProps.animated}
246
+ animationSpeed={childEdgeProps.animationSpeed}
247
+ gradient={childEdgeProps.gradient}
248
+ curvature={childEdgeProps.curvature}
249
+ connectorType={childEdgeProps.connectorType}
250
+ label={childEdgeProps.label}
251
+ labelStyle={childEdgeProps.labelStyle}
252
+ labelPosition={childEdgeProps.labelPosition}
253
+ labelOffsetX={childEdgeProps.labelOffsetX}
254
+ labelOffsetY={childEdgeProps.labelOffsetY}
255
+ />
256
+ );
257
+ })}
176
258
 
177
259
  <Box
178
260
  sx={{