Skip to content
Open
89 changes: 89 additions & 0 deletions examples/example14-double-click-edit.fixture.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useCallback, useState } from "react"
import { ControlledSchematicViewer } from "lib/components/ControlledSchematicViewer"
import { renderToCircuitJson } from "lib/dev/render-to-circuit-json"

export default function Example14DoubleClickEdit() {
const [lastDoubleClickedComponent, setLastDoubleClickedComponent] = useState<
string | null
>(null)

const handleDoubleClick = useCallback(
({ schematicComponentId }: { schematicComponentId: string }) => {
setLastDoubleClickedComponent(schematicComponentId)

if (typeof window !== "undefined") {
window.alert(`Open edit dialog for ${schematicComponentId}`)
}
},
[],
)

return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "1rem",
padding: "1rem",
height: "100%",
boxSizing: "border-box",
}}
>
<div style={{ flex: 1, minHeight: 400 }}>
<ControlledSchematicViewer
circuitJson={renderToCircuitJson(
<board width="12mm" height="12mm">
<resistor
name="R1"
resistance={220}
schX={-4}
schY={1}
/>
<capacitor
name="C1"
capacitance="10uF"
schX={4}
schY={-1}
/>
<chip
name="U1"
footprint="soic8"
pinLabels={{
pin1: "OUT",
pin2: "GND",
pin3: "IN-",
pin4: "IN+",
pin5: "VREF",
pin6: "NC",
pin7: "NC",
pin8: "VCC",
}}
schX={0}
schY={0}
/>
<trace from=".R1 .pin2" to=".U1 .pin4" />
<trace from=".C1 .pin1" to=".U1 .pin3" />
<trace from=".R1 .pin1" to=".C1 .pin2" />
</board>,
)}
containerStyle={{ height: "100%" }}
onClickComponent={handleDoubleClick}
/>
</div>

<div>
<p>
Double-click any component to simulate opening its editing dialog. The
cursor becomes a pointer to indicate interactivity.
</p>
{lastDoubleClickedComponent ? (
<p>
Last double-clicked component: <strong>{lastDoubleClickedComponent}</strong>
</p>
) : (
<p>Double-click a component to see its identifier here.</p>
)}
</div>
</div>
)
}
6 changes: 6 additions & 0 deletions lib/components/ControlledSchematicViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ export const ControlledSchematicViewer = ({
editingEnabled = false,
debug = false,
clickToInteractEnabled = false,
onClickComponent,
}: {
circuitJson: any[]
containerStyle?: React.CSSProperties
debugGrid?: boolean
editingEnabled?: boolean
debug?: boolean
clickToInteractEnabled?: boolean
onClickComponent?: (args: {
schematicComponentId: string
event: MouseEvent
}) => void
}) => {
const [editEvents, setEditEvents] = useState<ManualEditEvent[]>([])

Expand All @@ -29,6 +34,7 @@ export const ControlledSchematicViewer = ({
editingEnabled={editingEnabled}
debug={debug}
clickToInteractEnabled={clickToInteractEnabled}
onClickComponent={onClickComponent}
/>
)
}
68 changes: 68 additions & 0 deletions lib/components/SchematicViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ interface Props {
colorOverrides?: ColorOverrides
spiceSimulationEnabled?: boolean
disableGroups?: boolean
onClickComponent?: (args: {
schematicComponentId: string
event: MouseEvent
}) => void
}

export const SchematicViewer = ({
Expand All @@ -56,6 +60,7 @@ export const SchematicViewer = ({
colorOverrides,
spiceSimulationEnabled = false,
disableGroups = false,
onClickComponent,
}: Props) => {
if (debug) {
enableDebug()
Expand Down Expand Up @@ -264,6 +269,69 @@ export const SchematicViewer = ({
handleComponentTouchStartRef.current = handleComponentTouchStart
}, [handleComponentTouchStart])

useEffect(() => {
if (!onClickComponent) return

const svgContainer = svgDivRef.current
if (!svgContainer) return

const handleDoubleClick = (event: MouseEvent) => {
if (
(clickToInteractEnabled && !isInteractionEnabled) ||
showSpiceOverlay
) {
return
}

const target = event.target as Element | null
const componentGroup = target?.closest(
'[data-circuit-json-type="schematic_component"]',
) as HTMLElement | null

if (!componentGroup) return

const schematicComponentId = componentGroup.getAttribute(
"data-schematic-component-id",
)

if (!schematicComponentId) return

onClickComponent({ schematicComponentId, event })
}

svgContainer.addEventListener("dblclick", handleDoubleClick)

const componentElements = Array.from(
svgContainer.querySelectorAll(
'[data-circuit-json-type="schematic_component"]',
),
) as HTMLElement[]

const previousCursorMap = new Map<HTMLElement, string | null>()
componentElements.forEach((element) => {
previousCursorMap.set(element, element.style.cursor || null)
element.style.cursor = "pointer"
})

return () => {
svgContainer.removeEventListener("dblclick", handleDoubleClick)
componentElements.forEach((element) => {
const previousCursor = previousCursorMap.get(element)
if (previousCursor) {
element.style.cursor = previousCursor
} else {
element.style.removeProperty("cursor")
}
})
}
}, [
svgString,
onClickComponent,
clickToInteractEnabled,
isInteractionEnabled,
showSpiceOverlay,
])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much code in this file (keep things clean, move functionality to separate files appropriately)


const svgDiv = useMemo(
() => (
<div
Expand Down