Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions examples/example21-connected-trace-hover.fixture.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { SchematicViewer } from "lib/components/SchematicViewer"
import { renderToCircuitJson } from "lib/dev/render-to-circuit-json"

const circuitJson = renderToCircuitJson(
<board width="12mm" height="10mm" routingDisabled>
<resistor name="R1" resistance="1k" schX={-3} schY={1.5} />
<resistor name="R2" resistance="2k" schX={3} schY={1.5} />
<capacitor name="C1" capacitance="1uF" schX={0} schY={-2} />

<trace from=".R1 .pin2" to="net.SIGNAL" />
<trace from=".R2 .pin1" to="net.SIGNAL" />
<trace from=".C1 .pin1" to="net.SIGNAL" />
</board>,
)

export default () => (
<SchematicViewer
circuitJson={circuitJson}
containerStyle={{ height: "100%" }}
debugGrid
/>
)
19 changes: 19 additions & 0 deletions lib/components/SchematicViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { useMouseMatrixTransform } from "use-mouse-matrix-transform"
import { useResizeHandling } from "../hooks/use-resize-handling"
import { useComponentDragging } from "../hooks/useComponentDragging"
import { useHighlightConnectedSchematicTraces } from "../hooks/useHighlightConnectedSchematicTraces"
import type { ManualEditEvent } from "../types/edit-events"
import { EditIcon } from "./EditIcon"
import { GridIcon } from "./GridIcon"
Expand Down Expand Up @@ -345,6 +346,12 @@ export const SchematicViewer = ({
editEvents: editEventsWithUnappliedEditEvents,
})

useHighlightConnectedSchematicTraces({
svgDivRef,
circuitJson,
circuitJsonKey,
})

// Add group overlays when enabled
useSchematicGroupsOverlay({
svgDivRef,
Expand Down Expand Up @@ -396,6 +403,18 @@ export const SchematicViewer = ({

return (
<MouseTracker>
<style>
{`
.schematic-trace-net-hover path:not(.trace-invisible-hover-outline) {
stroke: #f97316 !important;
transition: stroke 120ms ease;
}

.schematic-trace-net-hover .trace-crossing-outline {
opacity: 0 !important;
}
`}
</style>
{onSchematicComponentClicked && (
<style>
{`.schematic-component-clickable [data-schematic-component-id]:hover { cursor: pointer !important; }`}
Expand Down
96 changes: 96 additions & 0 deletions lib/hooks/useHighlightConnectedSchematicTraces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { useEffect, useMemo } from "react"
import { su } from "@tscircuit/soup-util"
import type { CircuitJson } from "circuit-json"

export const useHighlightConnectedSchematicTraces = ({
svgDivRef,
circuitJson,
circuitJsonKey,
}: {
svgDivRef: React.RefObject<HTMLDivElement | null>
circuitJson: CircuitJson
circuitJsonKey: string
}) => {
const traceIdsByGroupKey = useMemo(() => {
const groups = new Map<string, string[]>()

try {
for (const trace of su(circuitJson).schematic_trace.list()) {
const traceId = trace.schematic_trace_id
if (!traceId) continue

const groupKey =
(trace as any).source_net_id ?? trace.source_trace_id ?? traceId
const traceIds = groups.get(groupKey) ?? []
traceIds.push(traceId)
groups.set(groupKey, traceIds)
}
} catch (err) {
console.error("Failed to derive connected schematic trace groups", err)
}

return groups
}, [circuitJson, circuitJsonKey])

useEffect(() => {
const svg = svgDivRef.current
if (!svg || traceIdsByGroupKey.size === 0) return

const groupKeyByTraceId = new Map<string, string>()
for (const [groupKey, traceIds] of traceIdsByGroupKey) {
for (const traceId of traceIds) {
groupKeyByTraceId.set(traceId, groupKey)
}
}

const highlightedElements = new Set<Element>()

const clearHighlight = () => {
for (const element of highlightedElements) {
element.classList.remove("schematic-trace-net-hover")
}
highlightedElements.clear()
}

const highlightTraceGroup = (traceId: string) => {
const groupKey = groupKeyByTraceId.get(traceId)
if (!groupKey) return

clearHighlight()

for (const connectedTraceId of traceIdsByGroupKey.get(groupKey) ?? []) {
const traceElement = svg.querySelector(
`[data-schematic-trace-id="${connectedTraceId}"]`,
)
if (!traceElement) continue
traceElement.classList.add("schematic-trace-net-hover")
highlightedElements.add(traceElement)
}
}

const cleanupFns: Array<() => void> = []

for (const traceId of groupKeyByTraceId.keys()) {
const traceElement = svg.querySelector(
`[data-schematic-trace-id="${traceId}"]`,
)
if (!traceElement) continue

const handlePointerEnter = () => highlightTraceGroup(traceId)
const handlePointerLeave = () => clearHighlight()

traceElement.addEventListener("pointerenter", handlePointerEnter)
traceElement.addEventListener("pointerleave", handlePointerLeave)

cleanupFns.push(() => {
traceElement.removeEventListener("pointerenter", handlePointerEnter)
traceElement.removeEventListener("pointerleave", handlePointerLeave)
})
}

return () => {
for (const cleanup of cleanupFns) cleanup()
clearHighlight()
}
}, [svgDivRef, traceIdsByGroupKey])
}
Loading