@tscircuit/schematic-viewer 2.0.37 → 2.0.38
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/index.js +203 -191
- package/dist/index.js.map +1 -1
- package/lib/hooks/useSchematicGroupsOverlay.ts +275 -259
- package/package.json +1 -1
|
@@ -28,288 +28,304 @@ export const useSchematicGroupsOverlay = (
|
|
|
28
28
|
const { svgDivRef, circuitJson, circuitJsonKey, showGroups } = options
|
|
29
29
|
|
|
30
30
|
useEffect(() => {
|
|
31
|
+
// Always clean up existing overlays first
|
|
32
|
+
if (svgDivRef.current) {
|
|
33
|
+
const existingOverlays = svgDivRef.current.querySelectorAll(
|
|
34
|
+
".schematic-group-overlay",
|
|
35
|
+
)
|
|
36
|
+
existingOverlays.forEach((overlay) => overlay.remove())
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
if (
|
|
32
40
|
!svgDivRef.current ||
|
|
33
41
|
!showGroups ||
|
|
34
42
|
!circuitJson ||
|
|
35
43
|
circuitJson.length === 0
|
|
36
44
|
) {
|
|
37
|
-
if (svgDivRef.current) {
|
|
38
|
-
const existingOverlays = svgDivRef.current.querySelectorAll(
|
|
39
|
-
".schematic-group-overlay",
|
|
40
|
-
)
|
|
41
|
-
existingOverlays.forEach((overlay) => overlay.remove())
|
|
42
|
-
}
|
|
43
45
|
return
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return
|
|
49
|
-
}
|
|
48
|
+
// Small delay to ensure SVG is rendered when groups are enabled from localStorage
|
|
49
|
+
const timeoutId = setTimeout(() => {
|
|
50
|
+
if (!svgDivRef.current) return
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const sourceGroups =
|
|
56
|
-
su(circuitJson)
|
|
57
|
-
.source_group?.list()
|
|
58
|
-
.filter((x) => !!!x.is_subcircuit) || []
|
|
59
|
-
const schematicComponents =
|
|
60
|
-
su(circuitJson).schematic_component?.list() || []
|
|
61
|
-
|
|
62
|
-
const sourceGroupHierarchy = new Map<string, string[]>()
|
|
63
|
-
sourceGroups.forEach((group) => {
|
|
64
|
-
const groupWithParent = group as any
|
|
65
|
-
if (groupWithParent.parent_source_group_id) {
|
|
66
|
-
const children =
|
|
67
|
-
sourceGroupHierarchy.get(groupWithParent.parent_source_group_id) ||
|
|
68
|
-
[]
|
|
69
|
-
children.push(group.source_group_id)
|
|
70
|
-
sourceGroupHierarchy.set(
|
|
71
|
-
groupWithParent.parent_source_group_id,
|
|
72
|
-
children,
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const getAllDescendantSourceGroups = (
|
|
78
|
-
sourceGroupId: string,
|
|
79
|
-
): string[] => {
|
|
80
|
-
const descendants: string[] = []
|
|
81
|
-
const children = sourceGroupHierarchy.get(sourceGroupId) || []
|
|
82
|
-
for (const child of children) {
|
|
83
|
-
descendants.push(child)
|
|
84
|
-
descendants.push(...getAllDescendantSourceGroups(child))
|
|
85
|
-
}
|
|
86
|
-
return descendants
|
|
52
|
+
const svg = svgDivRef.current.querySelector("svg")
|
|
53
|
+
if (!svg) {
|
|
54
|
+
return
|
|
87
55
|
}
|
|
88
56
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
57
|
+
const existingOverlays = svg.querySelectorAll(".schematic-group-overlay")
|
|
58
|
+
existingOverlays.forEach((overlay) => overlay.remove())
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const sourceGroups =
|
|
62
|
+
su(circuitJson)
|
|
63
|
+
.source_group?.list()
|
|
64
|
+
.filter((x) => !!!x.is_subcircuit) || []
|
|
65
|
+
const schematicComponents =
|
|
66
|
+
su(circuitJson).schematic_component?.list() || []
|
|
67
|
+
|
|
68
|
+
const sourceGroupHierarchy = new Map<string, string[]>()
|
|
69
|
+
sourceGroups.forEach((group) => {
|
|
70
|
+
const groupWithParent = group as any
|
|
71
|
+
if (groupWithParent.parent_source_group_id) {
|
|
72
|
+
const children =
|
|
73
|
+
sourceGroupHierarchy.get(
|
|
74
|
+
groupWithParent.parent_source_group_id,
|
|
75
|
+
) || []
|
|
76
|
+
children.push(group.source_group_id)
|
|
77
|
+
sourceGroupHierarchy.set(
|
|
78
|
+
groupWithParent.parent_source_group_id,
|
|
79
|
+
children,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const getAllDescendantSourceGroups = (
|
|
85
|
+
sourceGroupId: string,
|
|
86
|
+
): string[] => {
|
|
87
|
+
const descendants: string[] = []
|
|
88
|
+
const children = sourceGroupHierarchy.get(sourceGroupId) || []
|
|
89
|
+
for (const child of children) {
|
|
90
|
+
descendants.push(child)
|
|
91
|
+
descendants.push(...getAllDescendantSourceGroups(child))
|
|
92
|
+
}
|
|
93
|
+
return descendants
|
|
95
94
|
}
|
|
96
|
-
return 1 + getGroupDepthLevel(groupWithParent.parent_source_group_id)
|
|
97
|
-
}
|
|
98
95
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
96
|
+
const getGroupDepthLevel = (sourceGroupId: string): number => {
|
|
97
|
+
const groupWithParent = sourceGroups.find(
|
|
98
|
+
(g) => g.source_group_id === sourceGroupId,
|
|
99
|
+
) as any
|
|
100
|
+
if (!groupWithParent?.parent_source_group_id) {
|
|
101
|
+
return 0
|
|
102
|
+
}
|
|
103
|
+
return 1 + getGroupDepthLevel(groupWithParent.parent_source_group_id)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const hasMeaningfulGroups =
|
|
107
|
+
sourceGroups.length > 0 &&
|
|
108
|
+
sourceGroups.some((group) => group.name && group.name.trim() !== "")
|
|
109
|
+
|
|
110
|
+
let groupsToRender: Array<{
|
|
111
|
+
id: string
|
|
112
|
+
name: string
|
|
113
|
+
components: any[]
|
|
114
|
+
color: string
|
|
115
|
+
depthLevel: number
|
|
116
|
+
hasChildren: boolean
|
|
117
|
+
sourceGroupId?: string
|
|
118
|
+
}> = []
|
|
119
|
+
|
|
120
|
+
if (hasMeaningfulGroups) {
|
|
121
|
+
const groupMap = new Map<string, any[]>()
|
|
122
|
+
|
|
123
|
+
for (const comp of schematicComponents) {
|
|
124
|
+
const sourceComp = su(circuitJson).source_component.get(
|
|
125
|
+
comp.source_component_id,
|
|
126
|
+
)
|
|
127
|
+
if (sourceComp?.source_group_id) {
|
|
128
|
+
if (!groupMap.has(sourceComp.source_group_id)) {
|
|
129
|
+
groupMap.set(sourceComp.source_group_id, [])
|
|
130
|
+
}
|
|
131
|
+
groupMap.get(sourceComp.source_group_id)!.push(comp)
|
|
123
132
|
}
|
|
124
|
-
groupMap.get(sourceComp.source_group_id)!.push(comp)
|
|
125
133
|
}
|
|
134
|
+
|
|
135
|
+
sourceGroups.forEach((group, index) => {
|
|
136
|
+
let groupComponents = groupMap.get(group.source_group_id) || []
|
|
137
|
+
|
|
138
|
+
const descendantGroups = getAllDescendantSourceGroups(
|
|
139
|
+
group.source_group_id,
|
|
140
|
+
)
|
|
141
|
+
for (const descendantGroupId of descendantGroups) {
|
|
142
|
+
const descendantComponents = groupMap.get(descendantGroupId) || []
|
|
143
|
+
groupComponents = [...groupComponents, ...descendantComponents]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (groupComponents.length > 0) {
|
|
147
|
+
const depthLevel = getGroupDepthLevel(group.source_group_id)
|
|
148
|
+
const hasChildren =
|
|
149
|
+
getAllDescendantSourceGroups(group.source_group_id).length > 0
|
|
150
|
+
|
|
151
|
+
if (group.name?.startsWith("unnamed_board")) return
|
|
152
|
+
groupsToRender.push({
|
|
153
|
+
id: group.source_group_id,
|
|
154
|
+
name: group.name || `Group ${index + 1}`,
|
|
155
|
+
components: groupComponents,
|
|
156
|
+
color: GROUP_COLORS[index % GROUP_COLORS.length],
|
|
157
|
+
depthLevel,
|
|
158
|
+
hasChildren,
|
|
159
|
+
sourceGroupId: group.source_group_id,
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
})
|
|
126
163
|
}
|
|
164
|
+
// else {
|
|
165
|
+
// const componentTypeGroups = new Map<string, any[]>()
|
|
166
|
+
|
|
167
|
+
// for (const comp of schematicComponents) {
|
|
168
|
+
// const sourceComp = su(circuitJson).source_component.get(comp.source_component_id)
|
|
169
|
+
// if (sourceComp) {
|
|
170
|
+
// const componentType = sourceComp.ftype || "other"
|
|
171
|
+
// if (!componentTypeGroups.has(componentType)) {
|
|
172
|
+
// componentTypeGroups.set(componentType, [])
|
|
173
|
+
// }
|
|
174
|
+
// componentTypeGroups.get(componentType)!.push(comp)
|
|
175
|
+
// }
|
|
176
|
+
// }
|
|
177
|
+
// // groupsToRender = Array.from(componentTypeGroups.entries()).map(
|
|
178
|
+
// // ([type, components], index) => ({
|
|
179
|
+
// // id: `type_${type}`,
|
|
180
|
+
// // name: `${type.charAt(0).toUpperCase() + type.slice(1)}s`,
|
|
181
|
+
// // components,
|
|
182
|
+
// // color: GROUP_COLORS[index % GROUP_COLORS.length],
|
|
183
|
+
// // depthLevel: 0,
|
|
184
|
+
// // hasChildren: false,
|
|
185
|
+
// // }),
|
|
186
|
+
// // )
|
|
187
|
+
// }
|
|
188
|
+
|
|
189
|
+
const viewBox = svg.viewBox.baseVal
|
|
190
|
+
const svgRect = svg.getBoundingClientRect()
|
|
191
|
+
const scale =
|
|
192
|
+
Math.min(
|
|
193
|
+
svgRect.width / viewBox.width,
|
|
194
|
+
svgRect.height / viewBox.height,
|
|
195
|
+
) || 1
|
|
196
|
+
|
|
197
|
+
groupsToRender.sort((a, b) => a.depthLevel - b.depthLevel)
|
|
198
|
+
|
|
199
|
+
groupsToRender.forEach((group) => {
|
|
200
|
+
if (group.components.length === 0) return
|
|
201
|
+
|
|
202
|
+
const groupBounds = calculateGroupBounds(group.components, svg)
|
|
203
|
+
if (!groupBounds) return
|
|
204
|
+
|
|
205
|
+
const basePadding = Math.max(
|
|
206
|
+
8,
|
|
207
|
+
Math.min(25, 15 / Math.max(scale, 0.3)),
|
|
208
|
+
)
|
|
209
|
+
const hierarchyPadding = group.hasChildren ? basePadding * 0.6 : 0
|
|
210
|
+
const totalPadding = basePadding + hierarchyPadding
|
|
127
211
|
|
|
128
|
-
|
|
129
|
-
|
|
212
|
+
const baseStrokeWidth = Math.max(1, 2 / Math.max(scale, 0.5))
|
|
213
|
+
const strokeWidth =
|
|
214
|
+
group.depthLevel === 0 ? baseStrokeWidth : baseStrokeWidth * 0.7
|
|
130
215
|
|
|
131
|
-
const
|
|
132
|
-
|
|
216
|
+
const baseDashSize = Math.max(4, 8 / Math.max(scale, 0.5))
|
|
217
|
+
const dashMultiplier = group.hasChildren ? 1.3 : 1
|
|
218
|
+
const dashSize = baseDashSize * dashMultiplier
|
|
219
|
+
const gapSize = dashSize * 0.5
|
|
220
|
+
|
|
221
|
+
const groupOverlay = document.createElementNS(
|
|
222
|
+
"http://www.w3.org/2000/svg",
|
|
223
|
+
"rect",
|
|
133
224
|
)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
225
|
+
groupOverlay.setAttribute("class", "schematic-group-overlay")
|
|
226
|
+
groupOverlay.setAttribute(
|
|
227
|
+
"x",
|
|
228
|
+
(groupBounds.minX - totalPadding).toString(),
|
|
229
|
+
)
|
|
230
|
+
groupOverlay.setAttribute(
|
|
231
|
+
"y",
|
|
232
|
+
(groupBounds.minY - totalPadding).toString(),
|
|
233
|
+
)
|
|
234
|
+
groupOverlay.setAttribute(
|
|
235
|
+
"width",
|
|
236
|
+
(groupBounds.maxX - groupBounds.minX + totalPadding * 2).toString(),
|
|
237
|
+
)
|
|
238
|
+
groupOverlay.setAttribute(
|
|
239
|
+
"height",
|
|
240
|
+
(groupBounds.maxY - groupBounds.minY + totalPadding * 2).toString(),
|
|
241
|
+
)
|
|
242
|
+
groupOverlay.setAttribute("fill", "none")
|
|
243
|
+
groupOverlay.setAttribute("stroke", group.color)
|
|
244
|
+
groupOverlay.setAttribute("stroke-width", strokeWidth.toString())
|
|
245
|
+
groupOverlay.setAttribute(
|
|
246
|
+
"stroke-dasharray",
|
|
247
|
+
`${dashSize},${gapSize}`,
|
|
248
|
+
)
|
|
249
|
+
groupOverlay.setAttribute("opacity", "0.8")
|
|
250
|
+
groupOverlay.setAttribute("rx", "4")
|
|
251
|
+
groupOverlay.setAttribute("ry", "4")
|
|
138
252
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
253
|
+
const baseFontSize = Math.max(
|
|
254
|
+
6,
|
|
255
|
+
Math.min(20, 14 / Math.max(scale, 0.2)),
|
|
256
|
+
)
|
|
257
|
+
const fontSizeReduction =
|
|
258
|
+
group.depthLevel === 0 || group.depthLevel === 1
|
|
259
|
+
? 0
|
|
260
|
+
: group.depthLevel * 0.2
|
|
261
|
+
const fontSize = baseFontSize * (1 - fontSizeReduction)
|
|
262
|
+
|
|
263
|
+
const labelPadding = Math.max(1, fontSize * 0.2)
|
|
264
|
+
const labelText = group.name
|
|
265
|
+
|
|
266
|
+
const tempText = document.createElementNS(
|
|
267
|
+
"http://www.w3.org/2000/svg",
|
|
268
|
+
"text",
|
|
269
|
+
)
|
|
270
|
+
tempText.setAttribute("font-size", fontSize.toString())
|
|
271
|
+
tempText.setAttribute("font-family", "Arial, sans-serif")
|
|
272
|
+
tempText.textContent = labelText
|
|
273
|
+
svg.appendChild(tempText)
|
|
274
|
+
const textBBox = tempText.getBBox()
|
|
275
|
+
svg.removeChild(tempText)
|
|
276
|
+
|
|
277
|
+
const labelWidth = textBBox.width + labelPadding * 2
|
|
278
|
+
const labelHeight = fontSize + labelPadding * 2
|
|
279
|
+
const labelX = groupBounds.minX - totalPadding
|
|
280
|
+
const labelY = groupBounds.minY - totalPadding - labelHeight
|
|
281
|
+
|
|
282
|
+
const labelBg = document.createElementNS(
|
|
283
|
+
"http://www.w3.org/2000/svg",
|
|
284
|
+
"rect",
|
|
285
|
+
)
|
|
286
|
+
labelBg.setAttribute("class", "schematic-group-overlay")
|
|
287
|
+
labelBg.setAttribute("x", labelX.toString())
|
|
288
|
+
labelBg.setAttribute("y", (labelY - labelHeight).toString())
|
|
289
|
+
labelBg.setAttribute("width", labelWidth.toString())
|
|
290
|
+
labelBg.setAttribute("height", labelHeight.toString())
|
|
291
|
+
labelBg.setAttribute("fill", "transparent")
|
|
292
|
+
labelBg.setAttribute("rx", "3")
|
|
293
|
+
labelBg.setAttribute("ry", "3")
|
|
294
|
+
|
|
295
|
+
const groupLabel = document.createElementNS(
|
|
296
|
+
"http://www.w3.org/2000/svg",
|
|
297
|
+
"text",
|
|
298
|
+
)
|
|
299
|
+
groupLabel.setAttribute("class", "schematic-group-overlay")
|
|
300
|
+
groupLabel.setAttribute("x", (labelX + labelPadding).toString())
|
|
301
|
+
groupLabel.setAttribute(
|
|
302
|
+
"y",
|
|
303
|
+
(labelY + labelHeight - labelPadding).toString(),
|
|
304
|
+
)
|
|
305
|
+
groupLabel.setAttribute("fill", group.color)
|
|
306
|
+
groupLabel.setAttribute("font-size", fontSize.toString())
|
|
307
|
+
groupLabel.setAttribute("font-family", "Arial, sans-serif")
|
|
308
|
+
groupLabel.setAttribute(
|
|
309
|
+
"font-weight",
|
|
310
|
+
group.depthLevel === 0 ? "600" : "500",
|
|
311
|
+
)
|
|
312
|
+
groupLabel.setAttribute("stroke", group.color)
|
|
313
|
+
groupLabel.setAttribute(
|
|
314
|
+
"stroke-width",
|
|
315
|
+
Math.max(0.2, fontSize * 0.02).toString(),
|
|
316
|
+
)
|
|
317
|
+
groupLabel.textContent = labelText
|
|
318
|
+
|
|
319
|
+
svg.appendChild(groupOverlay)
|
|
320
|
+
svg.appendChild(labelBg)
|
|
321
|
+
svg.appendChild(groupLabel)
|
|
155
322
|
})
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.error("Error creating group overlays:", error)
|
|
156
325
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// for (const comp of schematicComponents) {
|
|
161
|
-
// const sourceComp = su(circuitJson).source_component.get(comp.source_component_id)
|
|
162
|
-
// if (sourceComp) {
|
|
163
|
-
// const componentType = sourceComp.ftype || "other"
|
|
164
|
-
// if (!componentTypeGroups.has(componentType)) {
|
|
165
|
-
// componentTypeGroups.set(componentType, [])
|
|
166
|
-
// }
|
|
167
|
-
// componentTypeGroups.get(componentType)!.push(comp)
|
|
168
|
-
// }
|
|
169
|
-
// }
|
|
170
|
-
// // groupsToRender = Array.from(componentTypeGroups.entries()).map(
|
|
171
|
-
// // ([type, components], index) => ({
|
|
172
|
-
// // id: `type_${type}`,
|
|
173
|
-
// // name: `${type.charAt(0).toUpperCase() + type.slice(1)}s`,
|
|
174
|
-
// // components,
|
|
175
|
-
// // color: GROUP_COLORS[index % GROUP_COLORS.length],
|
|
176
|
-
// // depthLevel: 0,
|
|
177
|
-
// // hasChildren: false,
|
|
178
|
-
// // }),
|
|
179
|
-
// // )
|
|
180
|
-
// }
|
|
181
|
-
|
|
182
|
-
const viewBox = svg.viewBox.baseVal
|
|
183
|
-
const svgRect = svg.getBoundingClientRect()
|
|
184
|
-
const scale =
|
|
185
|
-
Math.min(
|
|
186
|
-
svgRect.width / viewBox.width,
|
|
187
|
-
svgRect.height / viewBox.height,
|
|
188
|
-
) || 1
|
|
189
|
-
|
|
190
|
-
groupsToRender.sort((a, b) => a.depthLevel - b.depthLevel)
|
|
191
|
-
|
|
192
|
-
groupsToRender.forEach((group) => {
|
|
193
|
-
if (group.components.length === 0) return
|
|
194
|
-
|
|
195
|
-
const groupBounds = calculateGroupBounds(group.components, svg)
|
|
196
|
-
if (!groupBounds) return
|
|
197
|
-
|
|
198
|
-
const basePadding = Math.max(8, Math.min(25, 15 / Math.max(scale, 0.3)))
|
|
199
|
-
const hierarchyPadding = group.hasChildren ? basePadding * 0.6 : 0
|
|
200
|
-
const totalPadding = basePadding + hierarchyPadding
|
|
201
|
-
|
|
202
|
-
const baseStrokeWidth = Math.max(1, 2 / Math.max(scale, 0.5))
|
|
203
|
-
const strokeWidth =
|
|
204
|
-
group.depthLevel === 0 ? baseStrokeWidth : baseStrokeWidth * 0.7
|
|
205
|
-
|
|
206
|
-
const baseDashSize = Math.max(4, 8 / Math.max(scale, 0.5))
|
|
207
|
-
const dashMultiplier = group.hasChildren ? 1.3 : 1
|
|
208
|
-
const dashSize = baseDashSize * dashMultiplier
|
|
209
|
-
const gapSize = dashSize * 0.5
|
|
210
|
-
|
|
211
|
-
const groupOverlay = document.createElementNS(
|
|
212
|
-
"http://www.w3.org/2000/svg",
|
|
213
|
-
"rect",
|
|
214
|
-
)
|
|
215
|
-
groupOverlay.setAttribute("class", "schematic-group-overlay")
|
|
216
|
-
groupOverlay.setAttribute(
|
|
217
|
-
"x",
|
|
218
|
-
(groupBounds.minX - totalPadding).toString(),
|
|
219
|
-
)
|
|
220
|
-
groupOverlay.setAttribute(
|
|
221
|
-
"y",
|
|
222
|
-
(groupBounds.minY - totalPadding).toString(),
|
|
223
|
-
)
|
|
224
|
-
groupOverlay.setAttribute(
|
|
225
|
-
"width",
|
|
226
|
-
(groupBounds.maxX - groupBounds.minX + totalPadding * 2).toString(),
|
|
227
|
-
)
|
|
228
|
-
groupOverlay.setAttribute(
|
|
229
|
-
"height",
|
|
230
|
-
(groupBounds.maxY - groupBounds.minY + totalPadding * 2).toString(),
|
|
231
|
-
)
|
|
232
|
-
groupOverlay.setAttribute("fill", "none")
|
|
233
|
-
groupOverlay.setAttribute("stroke", group.color)
|
|
234
|
-
groupOverlay.setAttribute("stroke-width", strokeWidth.toString())
|
|
235
|
-
groupOverlay.setAttribute("stroke-dasharray", `${dashSize},${gapSize}`)
|
|
236
|
-
groupOverlay.setAttribute("opacity", "0.8")
|
|
237
|
-
groupOverlay.setAttribute("rx", "4")
|
|
238
|
-
groupOverlay.setAttribute("ry", "4")
|
|
239
|
-
|
|
240
|
-
const baseFontSize = Math.max(
|
|
241
|
-
6,
|
|
242
|
-
Math.min(20, 14 / Math.max(scale, 0.2)),
|
|
243
|
-
)
|
|
244
|
-
const fontSizeReduction =
|
|
245
|
-
group.depthLevel === 0 || group.depthLevel === 1
|
|
246
|
-
? 0
|
|
247
|
-
: group.depthLevel * 0.2
|
|
248
|
-
const fontSize = baseFontSize * (1 - fontSizeReduction)
|
|
249
|
-
|
|
250
|
-
const labelPadding = Math.max(1, fontSize * 0.2)
|
|
251
|
-
const labelText = group.name
|
|
252
|
-
|
|
253
|
-
const tempText = document.createElementNS(
|
|
254
|
-
"http://www.w3.org/2000/svg",
|
|
255
|
-
"text",
|
|
256
|
-
)
|
|
257
|
-
tempText.setAttribute("font-size", fontSize.toString())
|
|
258
|
-
tempText.setAttribute("font-family", "Arial, sans-serif")
|
|
259
|
-
tempText.textContent = labelText
|
|
260
|
-
svg.appendChild(tempText)
|
|
261
|
-
const textBBox = tempText.getBBox()
|
|
262
|
-
svg.removeChild(tempText)
|
|
263
|
-
|
|
264
|
-
const labelWidth = textBBox.width + labelPadding * 2
|
|
265
|
-
const labelHeight = fontSize + labelPadding * 2
|
|
266
|
-
const labelX = groupBounds.minX - totalPadding
|
|
267
|
-
const labelY = groupBounds.minY - totalPadding - labelHeight
|
|
268
|
-
|
|
269
|
-
const labelBg = document.createElementNS(
|
|
270
|
-
"http://www.w3.org/2000/svg",
|
|
271
|
-
"rect",
|
|
272
|
-
)
|
|
273
|
-
labelBg.setAttribute("class", "schematic-group-overlay")
|
|
274
|
-
labelBg.setAttribute("x", labelX.toString())
|
|
275
|
-
labelBg.setAttribute("y", (labelY - labelHeight).toString())
|
|
276
|
-
labelBg.setAttribute("width", labelWidth.toString())
|
|
277
|
-
labelBg.setAttribute("height", labelHeight.toString())
|
|
278
|
-
labelBg.setAttribute("fill", "transparent")
|
|
279
|
-
labelBg.setAttribute("rx", "3")
|
|
280
|
-
labelBg.setAttribute("ry", "3")
|
|
281
|
-
|
|
282
|
-
const groupLabel = document.createElementNS(
|
|
283
|
-
"http://www.w3.org/2000/svg",
|
|
284
|
-
"text",
|
|
285
|
-
)
|
|
286
|
-
groupLabel.setAttribute("class", "schematic-group-overlay")
|
|
287
|
-
groupLabel.setAttribute("x", (labelX + labelPadding).toString())
|
|
288
|
-
groupLabel.setAttribute(
|
|
289
|
-
"y",
|
|
290
|
-
(labelY + labelHeight - labelPadding).toString(),
|
|
291
|
-
)
|
|
292
|
-
groupLabel.setAttribute("fill", group.color)
|
|
293
|
-
groupLabel.setAttribute("font-size", fontSize.toString())
|
|
294
|
-
groupLabel.setAttribute("font-family", "Arial, sans-serif")
|
|
295
|
-
groupLabel.setAttribute(
|
|
296
|
-
"font-weight",
|
|
297
|
-
group.depthLevel === 0 ? "600" : "500",
|
|
298
|
-
)
|
|
299
|
-
groupLabel.setAttribute("stroke", group.color)
|
|
300
|
-
groupLabel.setAttribute(
|
|
301
|
-
"stroke-width",
|
|
302
|
-
Math.max(0.2, fontSize * 0.02).toString(),
|
|
303
|
-
)
|
|
304
|
-
groupLabel.textContent = labelText
|
|
305
|
-
|
|
306
|
-
svg.appendChild(groupOverlay)
|
|
307
|
-
svg.appendChild(labelBg)
|
|
308
|
-
svg.appendChild(groupLabel)
|
|
309
|
-
})
|
|
310
|
-
} catch (error) {
|
|
311
|
-
console.error("Error creating group overlays:", error)
|
|
312
|
-
}
|
|
326
|
+
}, 10) // Small delay to ensure SVG is ready
|
|
327
|
+
|
|
328
|
+
return () => clearTimeout(timeoutId)
|
|
313
329
|
}, [svgDivRef, circuitJsonKey, showGroups])
|
|
314
330
|
}
|
|
315
331
|
|