@tscircuit/schematic-viewer 2.0.44 → 2.0.46
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.
|
@@ -34,6 +34,8 @@ export interface MouseTrackerContextValue {
|
|
|
34
34
|
export const MouseTrackerContext =
|
|
35
35
|
createContext<MouseTrackerContextValue | null>(null)
|
|
36
36
|
|
|
37
|
+
const DRAG_THRESHOLD_PX = 5
|
|
38
|
+
|
|
37
39
|
const boundsAreEqual = (
|
|
38
40
|
a: BoundingBoxBounds | null | undefined,
|
|
39
41
|
b: BoundingBoxBounds | null | undefined,
|
|
@@ -60,6 +62,7 @@ export const MouseTracker = ({ children }: { children: ReactNode }) => {
|
|
|
60
62
|
boundingBoxes: new Map<string, BoundingBoxRegistration>(),
|
|
61
63
|
hoveringIds: new Set<string>(),
|
|
62
64
|
subscribers: new Set<() => void>(),
|
|
65
|
+
mouseDownPosition: null as { x: number; y: number } | null,
|
|
63
66
|
})
|
|
64
67
|
|
|
65
68
|
const notifySubscribers = useCallback(() => {
|
|
@@ -158,11 +161,36 @@ export const MouseTracker = ({ children }: { children: ReactNode }) => {
|
|
|
158
161
|
const handlePointerLeave = () => {
|
|
159
162
|
if (storeRef.current.pointer === null) return
|
|
160
163
|
storeRef.current.pointer = null
|
|
164
|
+
storeRef.current.mouseDownPosition = null
|
|
161
165
|
updateHovering()
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
const handleMouseDown = (event: MouseEvent) => {
|
|
169
|
+
storeRef.current.mouseDownPosition = {
|
|
170
|
+
x: event.clientX,
|
|
171
|
+
y: event.clientY,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
164
175
|
const handleClick = (event: MouseEvent) => {
|
|
165
176
|
const { clientX, clientY } = event
|
|
177
|
+
const mouseDownPos = storeRef.current.mouseDownPosition
|
|
178
|
+
|
|
179
|
+
// Check if this was a drag (movement > threshold)
|
|
180
|
+
if (mouseDownPos) {
|
|
181
|
+
const distance = Math.sqrt(
|
|
182
|
+
Math.pow(clientX - mouseDownPos.x, 2) +
|
|
183
|
+
Math.pow(clientY - mouseDownPos.y, 2),
|
|
184
|
+
)
|
|
185
|
+
if (distance > DRAG_THRESHOLD_PX) {
|
|
186
|
+
// This was a drag, not a click - don't trigger onClick
|
|
187
|
+
storeRef.current.mouseDownPosition = null
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
storeRef.current.mouseDownPosition = null
|
|
193
|
+
|
|
166
194
|
for (const registration of storeRef.current.boundingBoxes.values()) {
|
|
167
195
|
const bounds = registration.bounds
|
|
168
196
|
if (!bounds) continue
|
|
@@ -189,6 +217,7 @@ export const MouseTracker = ({ children }: { children: ReactNode }) => {
|
|
|
189
217
|
window.addEventListener("pointerleave", handlePointerLeave)
|
|
190
218
|
window.addEventListener("pointercancel", handlePointerLeave)
|
|
191
219
|
window.addEventListener("blur", handlePointerLeave)
|
|
220
|
+
window.addEventListener("mousedown", handleMouseDown, { passive: true })
|
|
192
221
|
window.addEventListener("click", handleClick, { passive: true })
|
|
193
222
|
|
|
194
223
|
return () => {
|
|
@@ -198,6 +227,7 @@ export const MouseTracker = ({ children }: { children: ReactNode }) => {
|
|
|
198
227
|
window.removeEventListener("pointerleave", handlePointerLeave)
|
|
199
228
|
window.removeEventListener("pointercancel", handlePointerLeave)
|
|
200
229
|
window.removeEventListener("blur", handlePointerLeave)
|
|
230
|
+
window.removeEventListener("mousedown", handleMouseDown)
|
|
201
231
|
window.removeEventListener("click", handleClick)
|
|
202
232
|
}
|
|
203
233
|
}, [updateHovering])
|
|
@@ -35,6 +35,7 @@ interface Props {
|
|
|
35
35
|
svgDivRef: React.RefObject<HTMLDivElement | null>
|
|
36
36
|
containerRef: React.RefObject<HTMLDivElement | null>
|
|
37
37
|
onComponentClick?: (componentId: string, event: MouseEvent) => void
|
|
38
|
+
onHoverChange?: (componentId: string, isHovering: boolean) => void
|
|
38
39
|
showOutline: boolean
|
|
39
40
|
circuitJsonKey: string
|
|
40
41
|
}
|
|
@@ -44,6 +45,7 @@ export const SchematicComponentMouseTarget = ({
|
|
|
44
45
|
svgDivRef,
|
|
45
46
|
containerRef,
|
|
46
47
|
onComponentClick,
|
|
48
|
+
onHoverChange,
|
|
47
49
|
showOutline,
|
|
48
50
|
circuitJsonKey,
|
|
49
51
|
}: Props) => {
|
|
@@ -157,6 +159,13 @@ export const SchematicComponentMouseTarget = ({
|
|
|
157
159
|
onClick: onComponentClick ? handleClick : undefined,
|
|
158
160
|
})
|
|
159
161
|
|
|
162
|
+
// Notify parent of hover state changes
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (onHoverChange) {
|
|
165
|
+
onHoverChange(componentId, hovering)
|
|
166
|
+
}
|
|
167
|
+
}, [hovering, componentId, onHoverChange])
|
|
168
|
+
|
|
160
169
|
if (!measurement || !hovering || !showOutline) {
|
|
161
170
|
return null
|
|
162
171
|
}
|
|
@@ -172,10 +181,8 @@ export const SchematicComponentMouseTarget = ({
|
|
|
172
181
|
width: rect.width,
|
|
173
182
|
height: rect.height,
|
|
174
183
|
border: "1.5px solid rgba(51, 153, 255, 0.9)",
|
|
175
|
-
borderRadius: "6px",
|
|
176
184
|
pointerEvents: "none",
|
|
177
185
|
zIndex: zIndexMap.schematicComponentHoverOutline,
|
|
178
|
-
boxShadow: "0 0 6px rgba(51, 153, 255, 0.35)",
|
|
179
186
|
}}
|
|
180
187
|
/>
|
|
181
188
|
)
|
|
@@ -7,7 +7,7 @@ import { useChangeSchematicComponentLocationsInSvg } from "lib/hooks/useChangeSc
|
|
|
7
7
|
import { useChangeSchematicTracesForMovedComponents } from "lib/hooks/useChangeSchematicTracesForMovedComponents"
|
|
8
8
|
import { useSchematicGroupsOverlay } from "lib/hooks/useSchematicGroupsOverlay"
|
|
9
9
|
import { enableDebug } from "lib/utils/debug"
|
|
10
|
-
import { useEffect, useMemo, useRef, useState } from "react"
|
|
10
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|
11
11
|
import {
|
|
12
12
|
fromString,
|
|
13
13
|
identity,
|
|
@@ -123,6 +123,21 @@ export const SchematicViewer = ({
|
|
|
123
123
|
if (disableGroups) return false
|
|
124
124
|
return getStoredBoolean("schematic_viewer_show_groups", false)
|
|
125
125
|
})
|
|
126
|
+
const [isHoveringClickableComponent, setIsHoveringClickableComponent] =
|
|
127
|
+
useState(false)
|
|
128
|
+
const hoveringComponentsRef = useRef<Set<string>>(new Set())
|
|
129
|
+
|
|
130
|
+
const handleComponentHoverChange = useCallback(
|
|
131
|
+
(componentId: string, isHovering: boolean) => {
|
|
132
|
+
if (isHovering) {
|
|
133
|
+
hoveringComponentsRef.current.add(componentId)
|
|
134
|
+
} else {
|
|
135
|
+
hoveringComponentsRef.current.delete(componentId)
|
|
136
|
+
}
|
|
137
|
+
setIsHoveringClickableComponent(hoveringComponentsRef.current.size > 0)
|
|
138
|
+
},
|
|
139
|
+
[],
|
|
140
|
+
)
|
|
126
141
|
const svgDivRef = useRef<HTMLDivElement>(null)
|
|
127
142
|
const touchStartRef = useRef<{ x: number; y: number } | null>(null)
|
|
128
143
|
|
|
@@ -297,6 +312,11 @@ export const SchematicViewer = ({
|
|
|
297
312
|
: "auto",
|
|
298
313
|
transformOrigin: "0 0",
|
|
299
314
|
}}
|
|
315
|
+
className={
|
|
316
|
+
onSchematicComponentClicked
|
|
317
|
+
? "schematic-component-clickable"
|
|
318
|
+
: undefined
|
|
319
|
+
}
|
|
300
320
|
onTouchStart={(e) => {
|
|
301
321
|
if (editModeEnabled && isInteractionEnabled && !showSpiceOverlay) {
|
|
302
322
|
handleComponentTouchStartRef.current(e)
|
|
@@ -317,6 +337,11 @@ export const SchematicViewer = ({
|
|
|
317
337
|
|
|
318
338
|
return (
|
|
319
339
|
<MouseTracker>
|
|
340
|
+
{onSchematicComponentClicked && (
|
|
341
|
+
<style>
|
|
342
|
+
{`.schematic-component-clickable [data-schematic-component-id]:hover { cursor: pointer !important; }`}
|
|
343
|
+
</style>
|
|
344
|
+
)}
|
|
320
345
|
<div
|
|
321
346
|
ref={containerRef}
|
|
322
347
|
style={{
|
|
@@ -329,7 +354,9 @@ export const SchematicViewer = ({
|
|
|
329
354
|
? "grabbing"
|
|
330
355
|
: clickToInteractEnabled && !isInteractionEnabled
|
|
331
356
|
? "pointer"
|
|
332
|
-
:
|
|
357
|
+
: isHoveringClickableComponent && onSchematicComponentClicked
|
|
358
|
+
? "pointer"
|
|
359
|
+
: "grab",
|
|
333
360
|
minHeight: "300px",
|
|
334
361
|
...containerStyle,
|
|
335
362
|
}}
|
|
@@ -456,6 +483,7 @@ export const SchematicViewer = ({
|
|
|
456
483
|
containerRef={containerRef}
|
|
457
484
|
showOutline={true}
|
|
458
485
|
circuitJsonKey={circuitJsonKey}
|
|
486
|
+
onHoverChange={handleComponentHoverChange}
|
|
459
487
|
onComponentClick={(id, event) => {
|
|
460
488
|
onSchematicComponentClicked?.({
|
|
461
489
|
schematicComponentId: id,
|