diff --git a/README.md b/README.md index 5cdc041..b628e82 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,7 @@ export default () => ( /> ) ``` + +## Port Hover Tooltip + +If the SVG generated by `circuit-to-svg` includes `data-schematic-port-id` attributes, the viewer will display the port's name when you hover over a schematic port. diff --git a/lib/components/SchematicViewer.tsx b/lib/components/SchematicViewer.tsx index 1e9a2c8..c67e985 100644 --- a/lib/components/SchematicViewer.tsx +++ b/lib/components/SchematicViewer.tsx @@ -19,6 +19,7 @@ import { EditIcon } from "./EditIcon" import { GridIcon } from "./GridIcon" import type { CircuitJson } from "circuit-json" import { zIndexMap } from "../utils/z-index-map" +import { useSchematicPortHover } from "../hooks/useSchematicPortHover" interface Props { circuitJson: CircuitJson @@ -189,6 +190,11 @@ export const SchematicViewer = ({ editEvents: editEventsWithUnappliedEditEvents, }) + const hoverData = useSchematicPortHover({ + svgDivRef, + circuitJson, + }) + const svgDiv = useMemo( () => (
)} {svgDiv} + {hoverData && ( +
+ {hoverData.name} +
+ )}
) } diff --git a/lib/hooks/useSchematicPortHover.ts b/lib/hooks/useSchematicPortHover.ts new file mode 100644 index 0000000..c23b3f6 --- /dev/null +++ b/lib/hooks/useSchematicPortHover.ts @@ -0,0 +1,65 @@ +import { useEffect, useState } from "react" +import { su } from "@tscircuit/soup-util" +import type { CircuitJson } from "circuit-json" + +export interface HoverData { + name: string + x: number + y: number +} + +export const useSchematicPortHover = ({ + svgDivRef, + circuitJson, +}: { + svgDivRef: React.RefObject + circuitJson: CircuitJson +}) => { + const [hover, setHover] = useState(null) + + useEffect(() => { + const svgDiv = svgDivRef.current + if (!svgDiv) return + + const handleMouseOver = (e: MouseEvent) => { + const target = e.target as Element | null + if (!target) return + const portGroup = target.closest( + "[data-schematic-port-id]", + ) as SVGGraphicsElement | null + if (!portGroup) return + const schPortId = portGroup.getAttribute("data-schematic-port-id") + if (!schPortId) return + const schPort = su(circuitJson).schematic_port.get(schPortId) + if (!schPort) return + const srcPort = su(circuitJson).source_port.get(schPort.source_port_id) + if (!srcPort) return + const rect = portGroup.getBoundingClientRect() + setHover({ + name: srcPort.name || "", + x: rect.x + rect.width / 2, + y: rect.y, + }) + } + + const handleMouseOut = (e: MouseEvent) => { + const target = e.target as Element | null + if (!target) return + if ( + target.closest("[data-schematic-port-id]") && + !svgDiv.contains(e.relatedTarget as Node) + ) { + setHover(null) + } + } + + svgDiv.addEventListener("mouseover", handleMouseOver) + svgDiv.addEventListener("mouseout", handleMouseOut) + return () => { + svgDiv.removeEventListener("mouseover", handleMouseOver) + svgDiv.removeEventListener("mouseout", handleMouseOut) + } + }, [svgDivRef, circuitJson]) + + return hover +} diff --git a/lib/utils/z-index-map.ts b/lib/utils/z-index-map.ts index 891151e..c4472ed 100644 --- a/lib/utils/z-index-map.ts +++ b/lib/utils/z-index-map.ts @@ -2,4 +2,5 @@ export const zIndexMap = { schematicEditIcon: 50, schematicGridIcon: 49, clickToInteractOverlay: 100, + tooltip: 101, }