@logicflow/core 2.2.0-alpha.1 → 2.2.0-alpha.3

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.
Files changed (119) hide show
  1. package/.turbo/turbo-build$colon$dev.log +2 -57
  2. package/.turbo/turbo-build.log +6 -6
  3. package/CHANGELOG.md +12 -0
  4. package/dist/index.css +3 -2
  5. package/dist/index.min.js +1 -1
  6. package/dist/index.min.js.map +1 -1
  7. package/es/LogicFlow.d.ts +9 -0
  8. package/es/constant/index.d.ts +1 -1
  9. package/es/constant/index.js +1 -1
  10. package/es/constant/theme.d.ts +136 -0
  11. package/es/constant/theme.js +680 -0
  12. package/es/index.css +3 -2
  13. package/es/model/GraphModel.d.ts +9 -2
  14. package/es/model/GraphModel.js +17 -6
  15. package/es/model/TransformModel.js +9 -9
  16. package/es/model/edge/BaseEdgeModel.js +7 -2
  17. package/es/model/edge/PolylineEdgeModel.d.ts +6 -0
  18. package/es/model/edge/PolylineEdgeModel.js +23 -1
  19. package/es/model/node/BaseNodeModel.d.ts +12 -1
  20. package/es/model/node/BaseNodeModel.js +6 -1
  21. package/es/model/node/HtmlNodeModel.d.ts +12 -0
  22. package/es/model/node/HtmlNodeModel.js +19 -0
  23. package/es/model/node/PolygonNodeModel.js +3 -3
  24. package/es/options.d.ts +1 -1
  25. package/es/style/index.css +3 -2
  26. package/es/style/index.less +3 -2
  27. package/es/style/raw.d.ts +1 -1
  28. package/es/style/raw.js +1 -1
  29. package/es/tool/MultipleSelectTool.js +6 -5
  30. package/es/util/edge.d.ts +1 -1
  31. package/es/util/edge.js +2 -2
  32. package/es/util/geometry.d.ts +8 -0
  33. package/es/util/geometry.js +79 -0
  34. package/es/util/theme.d.ts +2 -65
  35. package/es/util/theme.js +4 -281
  36. package/es/view/Control.d.ts +5 -0
  37. package/es/view/Control.js +44 -57
  38. package/es/view/edge/PolylineEdge.js +13 -2
  39. package/es/view/node/BaseNode.d.ts +1 -0
  40. package/es/view/node/BaseNode.js +14 -10
  41. package/es/view/node/HtmlNode.js +2 -4
  42. package/es/view/overlay/Grid.d.ts +12 -1
  43. package/es/view/overlay/Grid.js +85 -23
  44. package/es/view/overlay/OutlineOverlay.d.ts +1 -0
  45. package/es/view/overlay/OutlineOverlay.js +17 -16
  46. package/es/view/overlay/gridConfig.d.ts +46 -0
  47. package/es/view/overlay/gridConfig.js +99 -0
  48. package/es/view/shape/Polygon.d.ts +0 -7
  49. package/es/view/shape/Polygon.js +12 -43
  50. package/lib/LogicFlow.d.ts +9 -0
  51. package/lib/constant/index.d.ts +1 -1
  52. package/lib/constant/index.js +16 -2
  53. package/lib/constant/theme.d.ts +136 -0
  54. package/lib/constant/theme.js +683 -0
  55. package/lib/index.css +3 -2
  56. package/lib/model/GraphModel.d.ts +9 -2
  57. package/lib/model/GraphModel.js +18 -7
  58. package/lib/model/TransformModel.js +9 -9
  59. package/lib/model/edge/BaseEdgeModel.js +7 -2
  60. package/lib/model/edge/PolylineEdgeModel.d.ts +6 -0
  61. package/lib/model/edge/PolylineEdgeModel.js +23 -1
  62. package/lib/model/node/BaseNodeModel.d.ts +12 -1
  63. package/lib/model/node/BaseNodeModel.js +6 -1
  64. package/lib/model/node/HtmlNodeModel.d.ts +12 -0
  65. package/lib/model/node/HtmlNodeModel.js +19 -0
  66. package/lib/model/node/PolygonNodeModel.js +3 -3
  67. package/lib/options.d.ts +1 -1
  68. package/lib/style/index.css +3 -2
  69. package/lib/style/index.less +3 -2
  70. package/lib/style/raw.d.ts +1 -1
  71. package/lib/style/raw.js +1 -1
  72. package/lib/tool/MultipleSelectTool.js +6 -5
  73. package/lib/util/edge.d.ts +1 -1
  74. package/lib/util/edge.js +2 -2
  75. package/lib/util/geometry.d.ts +8 -0
  76. package/lib/util/geometry.js +81 -1
  77. package/lib/util/theme.d.ts +2 -65
  78. package/lib/util/theme.js +15 -292
  79. package/lib/view/Control.d.ts +5 -0
  80. package/lib/view/Control.js +44 -57
  81. package/lib/view/edge/PolylineEdge.js +13 -2
  82. package/lib/view/node/BaseNode.d.ts +1 -0
  83. package/lib/view/node/BaseNode.js +14 -10
  84. package/lib/view/node/HtmlNode.js +1 -3
  85. package/lib/view/overlay/Grid.d.ts +12 -1
  86. package/lib/view/overlay/Grid.js +83 -21
  87. package/lib/view/overlay/OutlineOverlay.d.ts +1 -0
  88. package/lib/view/overlay/OutlineOverlay.js +17 -16
  89. package/lib/view/overlay/gridConfig.d.ts +46 -0
  90. package/lib/view/overlay/gridConfig.js +104 -0
  91. package/lib/view/shape/Polygon.d.ts +0 -7
  92. package/lib/view/shape/Polygon.js +13 -45
  93. package/package.json +1 -1
  94. package/src/LogicFlow.tsx +10 -0
  95. package/src/constant/index.ts +2 -2
  96. package/src/constant/theme.ts +708 -0
  97. package/src/model/GraphModel.ts +19 -7
  98. package/src/model/TransformModel.ts +9 -9
  99. package/src/model/edge/BaseEdgeModel.ts +10 -2
  100. package/src/model/edge/PolylineEdgeModel.ts +26 -1
  101. package/src/model/node/BaseNodeModel.ts +9 -1
  102. package/src/model/node/HtmlNodeModel.ts +14 -0
  103. package/src/model/node/PolygonNodeModel.ts +2 -0
  104. package/src/options.ts +1 -1
  105. package/src/style/index.less +3 -2
  106. package/src/style/raw.ts +3 -2
  107. package/src/tool/MultipleSelectTool.tsx +6 -5
  108. package/src/util/edge.ts +2 -1
  109. package/src/util/geometry.ts +99 -0
  110. package/src/util/theme.ts +12 -303
  111. package/src/view/Control.tsx +61 -63
  112. package/src/view/edge/PolylineEdge.tsx +14 -2
  113. package/src/view/node/BaseNode.tsx +8 -3
  114. package/src/view/node/HtmlNode.tsx +27 -10
  115. package/src/view/overlay/Grid.tsx +187 -30
  116. package/src/view/overlay/OutlineOverlay.tsx +35 -47
  117. package/src/view/overlay/gridConfig.ts +103 -0
  118. package/src/view/shape/Polygon.tsx +12 -49
  119. package/stats.html +1 -1
@@ -1,9 +1,10 @@
1
1
  import { Component } from 'preact/compat'
2
- import { cloneDeep, assign } from 'lodash-es'
2
+ import { cloneDeep, assign, isNil } from 'lodash-es'
3
3
  import { observer } from '../..'
4
+ import { mergeMajorBoldConfig, MajorBoldConfig } from './gridConfig'
4
5
  import { createUuid } from '../../util'
5
6
  import { GraphModel } from '../../model'
6
- import { DEFAULT_GRID_SIZE } from '../../constant'
7
+ import { defaultGrid } from '../../constant'
7
8
 
8
9
  type IProps = {
9
10
  graphModel: GraphModel
@@ -39,25 +40,93 @@ export class Grid extends Component<IProps> {
39
40
  )
40
41
  }
41
42
 
43
+ // 计算与 size 整除的虚线周期,使边长能被 (dash + gap) 完整分割
44
+ private getDashArrayForSize(dashCfg?: { pattern: number[] }): string {
45
+ // 若提供了直接可用的 pattern,则优先使用
46
+ const direct = dashCfg?.pattern?.filter(
47
+ (n) => typeof n === 'number' && n > 0,
48
+ )
49
+ return direct?.join(',') || '2,1'
50
+ }
51
+
52
+ // 计算一个周期内的最大加粗索引(作为周期大小)
53
+ private getPeriod(advanced: any): number {
54
+ const list = Array.isArray(advanced?.boldIndices)
55
+ ? advanced.boldIndices.filter((n: any) => typeof n === 'number' && n > 0)
56
+ : []
57
+ return list.length ? Math.max(...list) : 0
58
+ }
59
+
60
+ // 计算加粗线宽,优先使用自定义;否则根据周期与厚度估算
61
+ private getBoldStrokeWidth(
62
+ advanced: any,
63
+ size: number,
64
+ thickness?: number,
65
+ period?: number,
66
+ ): number {
67
+ if (typeof advanced?.customBoldWidth === 'number')
68
+ return advanced.customBoldWidth
69
+ const baseThickness = Math.max(1, thickness ?? 1)
70
+ const p = Math.max(1, period ?? this.getPeriod(advanced))
71
+ return Math.min(baseThickness, (size * p) / 2) / 2
72
+ }
73
+
74
+ // 渲染 mesh 类型四条边的虚线,减少重复代码
75
+ private renderMeshEdgeLines(
76
+ size: number,
77
+ color: string,
78
+ strokeWidth: number,
79
+ opacity: number,
80
+ dash?: string,
81
+ ) {
82
+ const segments = [
83
+ { d: `M 0 0 H ${size}` },
84
+ { d: `M 0 ${size} H ${size}` },
85
+ { d: `M 0 0 V ${size}` },
86
+ { d: `M ${size} 0 V ${size}` },
87
+ ]
88
+ return (
89
+ <g opacity={opacity} fill="transparent">
90
+ {segments.map((seg) => (
91
+ <path
92
+ d={seg.d}
93
+ stroke={color}
94
+ strokeWidth={strokeWidth / 2}
95
+ strokeDasharray={dash}
96
+ strokeLinecap="butt"
97
+ fill="transparent"
98
+ />
99
+ ))}
100
+ </g>
101
+ )
102
+ }
103
+
42
104
  // 网格类型为交叉线
43
105
  // todo: 采用背景缩放的方式,实现更好的体验
44
106
  renderMesh() {
45
- const { config, size = 1, visible } = this.gridOptions
46
- const { color, thickness = 1 } = config ?? {}
107
+ const {
108
+ config,
109
+ size = 1,
110
+ visible,
111
+ majorBold,
112
+ } = this.gridOptions as Grid.GridOptions & {
113
+ majorBold?: boolean | MajorBoldConfig
114
+ }
115
+ const { config: advanced } = mergeMajorBoldConfig(majorBold)
116
+ const { opacity: baseOpacity } = advanced
117
+ const color: string = (config?.color ?? '#D7DEEB') as string
118
+ const thickness: number = (config?.thickness ?? 1) as number
47
119
 
48
120
  // 对于交叉线网格,线的宽度不能大于网格大小的一半
49
121
  const strokeWidth = Math.min(Math.max(1, thickness), size / 2)
50
- const d = `M 0 0 H ${size} V ${size} H 0 Z`
51
- const opacity = visible ? 1 : 0
52
- return (
53
- <path
54
- d={d}
55
- stroke={color}
56
- strokeWidth={strokeWidth / 2}
57
- opacity={opacity}
58
- fill="transparent"
59
- />
60
- )
122
+ const opacity = visible ? baseOpacity : 0
123
+
124
+ // 根据 size 自动计算合适的 dash/gap 周期,使 size 能被 (dash + gap) 整除
125
+ const dash =
126
+ majorBold === false
127
+ ? undefined
128
+ : this.getDashArrayForSize(advanced.dashArrayConfig)
129
+ return this.renderMeshEdgeLines(size, color, strokeWidth, opacity, dash)
61
130
  }
62
131
 
63
132
  render() {
@@ -65,7 +134,16 @@ export class Grid extends Component<IProps> {
65
134
  graphModel: { transformModel, grid },
66
135
  } = this.props
67
136
  this.gridOptions = grid
68
- const { type, size = 1 } = this.gridOptions
137
+ const {
138
+ type,
139
+ config = {},
140
+ size = 1,
141
+ majorBold,
142
+ } = this.gridOptions as Grid.GridOptions & {
143
+ majorBold?: boolean | MajorBoldConfig
144
+ }
145
+ const showMajorBold = majorBold !== false && !isNil(majorBold)
146
+ const { config: advanced } = mergeMajorBoldConfig(majorBold)
69
147
  const { SCALE_X, SKEW_Y, SKEW_X, SCALE_Y, TRANSLATE_X, TRANSLATE_Y } =
70
148
  transformModel
71
149
  const matrixString = [
@@ -77,9 +155,8 @@ export class Grid extends Component<IProps> {
77
155
  TRANSLATE_Y,
78
156
  ].join(',')
79
157
  const transform = `matrix(${matrixString})`
80
- // const transitionStyle = {
81
- // transition: 'all 0.1s ease',
82
- // };
158
+ const radius = Math.min(Math.max(2, config.thickness ?? 1), size / 4)
159
+ const opacity = showMajorBold ? advanced.opacity : 1
83
160
  return (
84
161
  <div className="lf-grid">
85
162
  <svg
@@ -101,8 +178,91 @@ export class Grid extends Component<IProps> {
101
178
  {type === 'dot' && this.renderDot()}
102
179
  {type === 'mesh' && this.renderMesh()}
103
180
  </pattern>
181
+ {type === 'dot' && advanced.boldIndices.length ? (
182
+ <pattern
183
+ id={`${this.id}-dot-major`}
184
+ patternUnits="userSpaceOnUse"
185
+ patternTransform={transform}
186
+ x="0"
187
+ y="0"
188
+ width={size * this.getPeriod(advanced)}
189
+ height={size * this.getPeriod(advanced)}
190
+ >
191
+ <g
192
+ fill={this.gridOptions.config?.color ?? '#D7DEEB'}
193
+ opacity={this.gridOptions.visible ? opacity : 0}
194
+ >
195
+ <circle cx={0} cy={0} r={(radius * 1.5) / 2} />
196
+ <circle
197
+ cx={size * this.getPeriod(advanced)}
198
+ cy={0}
199
+ r={(radius * 1.5) / 2}
200
+ />
201
+ <circle
202
+ cx={0}
203
+ cy={size * this.getPeriod(advanced)}
204
+ r={(radius * 1.5) / 2}
205
+ />
206
+ <circle
207
+ cx={size * this.getPeriod(advanced)}
208
+ cy={size * this.getPeriod(advanced)}
209
+ r={(radius * 1.5) / 2}
210
+ />
211
+ </g>
212
+ </pattern>
213
+ ) : null}
214
+ {type === 'mesh' && advanced.boldIndices.length ? (
215
+ <pattern
216
+ id={`${this.id}-major`}
217
+ patternUnits="userSpaceOnUse"
218
+ patternTransform={transform}
219
+ x="0"
220
+ y="0"
221
+ width={size * this.getPeriod(advanced)}
222
+ height={size * this.getPeriod(advanced)}
223
+ >
224
+ {advanced.boldIndices.map((i: number) => (
225
+ <g>
226
+ <path
227
+ d={`M ${size * i} 0 V ${size * this.getPeriod(advanced)}`}
228
+ stroke={this.gridOptions.config?.color ?? '#D7DEEB'}
229
+ strokeWidth={this.getBoldStrokeWidth(
230
+ advanced,
231
+ size,
232
+ (this.gridOptions.config ?? {}).thickness,
233
+ this.getPeriod(advanced),
234
+ )}
235
+ opacity={this.gridOptions.visible ? opacity : 0}
236
+ fill="transparent"
237
+ />
238
+ <path
239
+ d={`M 0 ${size * i} H ${size * this.getPeriod(advanced)}`}
240
+ stroke={this.gridOptions.config?.color ?? '#D7DEEB'}
241
+ strokeWidth={this.getBoldStrokeWidth(
242
+ advanced,
243
+ size,
244
+ (this.gridOptions.config ?? {}).thickness,
245
+ this.getPeriod(advanced),
246
+ )}
247
+ opacity={this.gridOptions.visible ? opacity : 0}
248
+ fill="transparent"
249
+ />
250
+ </g>
251
+ ))}
252
+ </pattern>
253
+ ) : null}
104
254
  </defs>
105
255
  <rect width="100%" height="100%" fill={`url(#${this.id})`} />
256
+ {type === 'dot' && showMajorBold && advanced.boldIndices.length ? (
257
+ <rect
258
+ width="100%"
259
+ height="100%"
260
+ fill={`url(#${this.id}-dot-major)`}
261
+ />
262
+ ) : null}
263
+ {type === 'mesh' && showMajorBold && advanced.boldIndices.length ? (
264
+ <rect width="100%" height="100%" fill={`url(#${this.id}-major)`} />
265
+ ) : null}
106
266
  </svg>
107
267
  </div>
108
268
  )
@@ -137,20 +297,17 @@ export namespace Grid {
137
297
  */
138
298
  thickness?: number
139
299
  }
140
- }
141
-
142
- export const defaultProps: GridOptions = {
143
- size: DEFAULT_GRID_SIZE,
144
- visible: true,
145
- type: 'dot',
146
- config: {
147
- color: '#ababab',
148
- thickness: 1,
149
- },
300
+ /**
301
+ * 行为配置:支持 boolean 或高级对象
302
+ * - false: 禁用特殊样式,opacity=1,无加粗,无虚线
303
+ * - true: 启用默认样式,opacity=0.75,每第5个加粗/实线,虚线动态计算
304
+ * - object: 高级配置,详见 MajorBoldConfig
305
+ */
306
+ majorBold?: boolean | MajorBoldConfig
150
307
  }
151
308
 
152
309
  export function getGridOptions(options: number | boolean | GridOptions) {
153
- const defaultOptions = cloneDeep(Grid.defaultProps)
310
+ const defaultOptions = cloneDeep(defaultGrid) as GridOptions
154
311
  if (typeof options === 'number') {
155
312
  return assign(defaultOptions, { size: options })
156
313
  } else if (typeof options === 'boolean') {
@@ -16,6 +16,26 @@ type IProps = {
16
16
 
17
17
  @observer
18
18
  export class OutlineOverlay extends Component<IProps> {
19
+ // 通用渲染函数:根据点集合与样式计算包围盒并返回矩形轮廓
20
+ private renderRectOutline(
21
+ pointsList: any[],
22
+ style: Record<string, unknown>,
23
+ className: string,
24
+ defaultOffsets: { widthOffset: number; heightOffset: number },
25
+ ) {
26
+ const {
27
+ widthOffset = defaultOffsets.widthOffset,
28
+ heightOffset = defaultOffsets.heightOffset,
29
+ } = (style || {}) as any
30
+ const { x, y, width, height } = getBBoxOfPoints(
31
+ pointsList,
32
+ widthOffset,
33
+ heightOffset,
34
+ )
35
+ return (
36
+ <Rect className={className} {...{ x, y, width, height }} {...style} />
37
+ )
38
+ }
19
39
  // 节点outline
20
40
  getNodesOutline() {
21
41
  const { graphModel } = this.props
@@ -52,10 +72,8 @@ export class OutlineOverlay extends Component<IProps> {
52
72
  {...{
53
73
  x,
54
74
  y,
55
- // width: width + 10,
56
- // height: height + 10,
57
- width: width + 10,
58
- height: height + 10,
75
+ width: width + 4,
76
+ height: height + 4,
59
77
  }}
60
78
  {...attributes}
61
79
  />,
@@ -96,22 +114,12 @@ export class OutlineOverlay extends Component<IProps> {
96
114
  // 直线outline
97
115
  getLineOutline(line: LineEdgeModel) {
98
116
  const { startPoint, endPoint } = line
99
- const x = (startPoint.x + endPoint.x) / 2
100
- const y = (startPoint.y + endPoint.y) / 2
101
- const width = Math.abs(startPoint.x - endPoint.x) + 10
102
- const height = Math.abs(startPoint.y - endPoint.y) + 10
103
117
  const style = line.getOutlineStyle()
104
- return (
105
- <Rect
106
- className="lf-outline-edge"
107
- {...{
108
- x,
109
- y,
110
- width,
111
- height,
112
- }}
113
- {...style}
114
- />
118
+ return this.renderRectOutline(
119
+ [startPoint, endPoint],
120
+ style,
121
+ 'lf-outline-edge',
122
+ { widthOffset: 10, heightOffset: 10 },
115
123
  )
116
124
  }
117
125
 
@@ -119,42 +127,22 @@ export class OutlineOverlay extends Component<IProps> {
119
127
  getPolylineOutline(polyline: PolylineEdgeModel) {
120
128
  const { points } = polyline
121
129
  const pointsList = points2PointsList(points)
122
- const bbox = getBBoxOfPoints(pointsList, 8)
123
- const { x, y, width, height } = bbox
124
130
  const style = polyline.getOutlineStyle()
125
- return (
126
- <Rect
127
- className="lf-outline"
128
- {...{
129
- x,
130
- y,
131
- width,
132
- height,
133
- }}
134
- {...style}
135
- />
136
- )
131
+ return this.renderRectOutline(pointsList, style, 'lf-outline', {
132
+ widthOffset: 8,
133
+ heightOffset: 16,
134
+ })
137
135
  }
138
136
 
139
137
  // 曲线outline
140
138
  getBezierOutline(bezier: BezierEdgeModel) {
141
139
  const { path } = bezier
142
140
  const pointsList = getBezierPoints(path)
143
- const bbox = getBBoxOfPoints(pointsList, 8)
144
- const { x, y, width, height } = bbox
145
141
  const style = bezier.getOutlineStyle()
146
- return (
147
- <Rect
148
- className="lf-outline"
149
- {...{
150
- x,
151
- y,
152
- width,
153
- height,
154
- }}
155
- {...style}
156
- />
157
- )
142
+ return this.renderRectOutline(pointsList, style, 'lf-outline', {
143
+ widthOffset: 8,
144
+ heightOffset: 16,
145
+ })
158
146
  }
159
147
 
160
148
  render() {
@@ -0,0 +1,103 @@
1
+ /**
2
+ * 网格高级 majorBold 配置。
3
+ *
4
+ * 示例(布尔值):
5
+ * - false:禁用特殊样式,opacity=1,无加粗,无虚线
6
+ * - true:启用默认值,opacity=0.75,每第 5 条线/点加粗/实线,
7
+ * 虚线图案通过 getDashArrayForSize(size) 计算
8
+ *
9
+ * 示例(对象):
10
+ * {
11
+ * opacity: 0.6,
12
+ * boldIndices: [4, 8],
13
+ * dashArrayConfig: { pattern: [4,2,4] },
14
+ * customBoldWidth: 2,
15
+ * }
16
+ */
17
+ export type MajorBoldConfig = {
18
+ /** 默认网格透明度 (0-1) */
19
+ opacity: number
20
+ /** 一个周期内需要加粗/实线的索引列表 */
21
+ boldIndices: number[]
22
+ /** 虚线样式的计算配置 */
23
+ dashArrayConfig: {
24
+ /** 固定的虚线模式(可选) */
25
+ pattern: number[]
26
+ }
27
+ /** mesh 网格的自定义加粗线宽(可选) */
28
+ customBoldWidth?: number
29
+ }
30
+
31
+ export const defaultGridConfig: MajorBoldConfig = {
32
+ opacity: 1,
33
+ boldIndices: [5],
34
+ dashArrayConfig: {
35
+ pattern: [2, 1],
36
+ },
37
+ customBoldWidth: 2,
38
+ }
39
+
40
+ /**
41
+ * 校验并规范化 GridConfig。
42
+ * - 将 opacity 限制在 [0,1] 范围
43
+ * - boldIndices 过滤为正整数并去重
44
+ */
45
+ export function validateGridConfig(cfg: MajorBoldConfig): MajorBoldConfig {
46
+ const opacity = Math.max(
47
+ 0,
48
+ Math.min(1, cfg.opacity ?? defaultGridConfig.opacity),
49
+ )
50
+ const boldIndicesSet = new Set<number>()
51
+ const boldRaw = Array.isArray(cfg.boldIndices)
52
+ ? cfg.boldIndices
53
+ : defaultGridConfig.boldIndices
54
+ for (const n of boldRaw) {
55
+ if (typeof n === 'number' && Number.isFinite(n) && n > 0) {
56
+ boldIndicesSet.add(Math.floor(n))
57
+ }
58
+ }
59
+ const boldIndices = Array.from(boldIndicesSet)
60
+ const pattern = Array.isArray(cfg.dashArrayConfig?.pattern)
61
+ ? cfg.dashArrayConfig.pattern
62
+ : defaultGridConfig.dashArrayConfig.pattern
63
+ const dashArrayConfig = {
64
+ pattern,
65
+ }
66
+ const customBoldWidth = cfg.customBoldWidth
67
+ return {
68
+ opacity,
69
+ boldIndices: boldIndices.length
70
+ ? boldIndices
71
+ : defaultGridConfig.boldIndices,
72
+ dashArrayConfig,
73
+ customBoldWidth,
74
+ }
75
+ }
76
+
77
+ /**
78
+ * 合并用户行为配置与默认值。
79
+ * - false → 关闭模式:opacity=1,bold=[],关闭虚线
80
+ * - true → 默认模式:采用 defaultGridConfig
81
+ * - object → 自定义模式:校验后使用用户配置
82
+ */
83
+ export function mergeMajorBoldConfig(input?: boolean | MajorBoldConfig): {
84
+ mode: 'disabled' | 'default' | 'custom'
85
+ config: MajorBoldConfig
86
+ } {
87
+ if (input === false) {
88
+ return {
89
+ mode: 'disabled',
90
+ config: {
91
+ opacity: 1,
92
+ boldIndices: [],
93
+ dashArrayConfig: {
94
+ pattern: [],
95
+ },
96
+ },
97
+ }
98
+ }
99
+ if (input === true || input == null) {
100
+ return { mode: 'default', config: { ...defaultGridConfig } }
101
+ }
102
+ return { mode: 'custom', config: validateGridConfig(input) }
103
+ }
@@ -1,6 +1,7 @@
1
1
  import { createElement as h } from 'preact/compat'
2
2
  import { forEach, toPairs } from 'lodash-es'
3
3
  import { LogicFlow } from '../..'
4
+ import { generateRoundedCorners } from '../../util/geometry'
4
5
 
5
6
  export type IPolygonProps = {
6
7
  points: LogicFlow.PointTuple[]
@@ -9,52 +10,6 @@ export type IPolygonProps = {
9
10
  className?: string
10
11
  radius?: number
11
12
  }
12
- /**
13
- * 生成带圆角的多边形路径
14
- * @param points 多边形顶点坐标数组
15
- * @param radius 圆角半径
16
- * @returns SVG 路径字符串
17
- */
18
- export function createRoundedPolygonPath(points, radius): string {
19
- const pointList = points.map((point) => ({ x: point[0], y: point[1] }))
20
- const len = pointList.length
21
- if (len < 3) return ''
22
-
23
- const r = Math.abs(radius)
24
- let path = ''
25
-
26
- for (let i = 0; i < len; i++) {
27
- const prev = pointList[(i - 1 + len) % len]
28
- const curr = pointList[i]
29
- const next = pointList[(i + 1) % len]
30
-
31
- // 向量
32
- const v1 = { x: curr.x - prev.x, y: curr.y - prev.y }
33
- const v2 = { x: next.x - curr.x, y: next.y - curr.y }
34
-
35
- // 单位向量
36
- const len1 = Math.hypot(v1.x, v1.y)
37
- const len2 = Math.hypot(v2.x, v2.y)
38
- const u1 = { x: v1.x / len1, y: v1.y / len1 }
39
- const u2 = { x: v2.x / len2, y: v2.y / len2 }
40
-
41
- // 起点 = curr - u1 * r,终点 = curr + u2 * r
42
- const start = { x: curr.x - u1.x * r, y: curr.y - u1.y * r }
43
- const end = { x: curr.x + u2.x * r, y: curr.y + u2.y * r }
44
-
45
- if (i === 0) {
46
- path += `M ${start.x} ${start.y} `
47
- } else {
48
- path += `L ${start.x} ${start.y} `
49
- }
50
-
51
- // Q 控制点是当前拐角点
52
- path += `Q ${curr.x} ${curr.y} ${end.x} ${end.y} `
53
- }
54
-
55
- path += 'Z'
56
- return path
57
- }
58
13
 
59
14
  export function Polygon(props: IPolygonProps): h.JSX.Element {
60
15
  const { points = [], className, radius } = props
@@ -74,13 +29,21 @@ export function Polygon(props: IPolygonProps): h.JSX.Element {
74
29
  })
75
30
 
76
31
  if (className) {
77
- attrs.classNmae = `lf-basic-shape ${className}`
32
+ attrs.className = `lf-basic-shape ${className}`
78
33
  } else {
79
34
  attrs.className = 'lf-basic-shape'
80
35
  }
81
36
  if (radius) {
82
- const path = createRoundedPolygonPath(points, radius)
83
- attrs.d = path
37
+ const pointList = points.map((point) => ({ x: point[0], y: point[1] }))
38
+ const rounded = generateRoundedCorners(pointList, radius, true)
39
+ const d = rounded.length
40
+ ? `M ${rounded[0].x} ${rounded[0].y} ${rounded
41
+ .slice(1)
42
+ .map((p) => `L ${p.x} ${p.y}`)
43
+ .join(' ')} Z`
44
+ : ''
45
+ attrs.d = d
46
+ delete attrs.points
84
47
  return <path {...attrs} />
85
48
  } else {
86
49
  attrs.points = points.map((point) => point.join(',')).join(' ')