@tscircuit/schematic-viewer 2.0.41 → 2.0.43
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.d.ts +25 -2
- package/dist/index.js +441 -44
- package/dist/index.js.map +1 -1
- package/examples/example14-schematic-component-click.fixture.tsx +46 -0
- package/lib/components/ControlledSchematicViewer.tsx +6 -0
- package/lib/components/MouseTracker.tsx +227 -0
- package/lib/components/SchematicComponentMouseTarget.tsx +182 -0
- package/lib/components/SchematicViewer.tsx +170 -120
- package/lib/components/SpicePlot.tsx +18 -0
- package/lib/components/SpiceSimulationOverlay.tsx +4 -1
- package/lib/hooks/useMouseEventsOverBoundingBox.ts +74 -0
- package/lib/index.ts +2 -0
- package/lib/utils/z-index-map.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
convertCircuitJsonToSchematicSvg
|
|
4
4
|
} from "circuit-to-svg";
|
|
5
|
+
import { su as su6 } from "@tscircuit/soup-util";
|
|
5
6
|
|
|
6
7
|
// lib/hooks/useChangeSchematicComponentLocationsInSvg.ts
|
|
7
8
|
import "@tscircuit/soup-util";
|
|
@@ -469,7 +470,7 @@ var enableDebug = () => {
|
|
|
469
470
|
var debug_default = debug;
|
|
470
471
|
|
|
471
472
|
// lib/components/SchematicViewer.tsx
|
|
472
|
-
import { useEffect as
|
|
473
|
+
import { useEffect as useEffect11, useMemo as useMemo5, useRef as useRef7, useState as useState6 } from "react";
|
|
473
474
|
import {
|
|
474
475
|
fromString,
|
|
475
476
|
identity,
|
|
@@ -691,7 +692,8 @@ var zIndexMap = {
|
|
|
691
692
|
viewMenuIcon: 48,
|
|
692
693
|
viewMenu: 55,
|
|
693
694
|
viewMenuBackdrop: 54,
|
|
694
|
-
clickToInteractOverlay: 100
|
|
695
|
+
clickToInteractOverlay: 100,
|
|
696
|
+
schematicComponentHoverOutline: 47
|
|
695
697
|
};
|
|
696
698
|
|
|
697
699
|
// lib/components/EditIcon.tsx
|
|
@@ -846,7 +848,7 @@ import { su as su5 } from "@tscircuit/soup-util";
|
|
|
846
848
|
// package.json
|
|
847
849
|
var package_default = {
|
|
848
850
|
name: "@tscircuit/schematic-viewer",
|
|
849
|
-
version: "2.0.
|
|
851
|
+
version: "2.0.42",
|
|
850
852
|
main: "dist/index.js",
|
|
851
853
|
type: "module",
|
|
852
854
|
scripts: {
|
|
@@ -1148,7 +1150,8 @@ var SpicePlot = ({
|
|
|
1148
1150
|
plotData,
|
|
1149
1151
|
nodes,
|
|
1150
1152
|
isLoading,
|
|
1151
|
-
error
|
|
1153
|
+
error,
|
|
1154
|
+
hasRun
|
|
1152
1155
|
}) => {
|
|
1153
1156
|
const yAxisLabel = useMemo2(() => {
|
|
1154
1157
|
const hasVoltage = nodes.some((n) => n.toLowerCase().startsWith("v("));
|
|
@@ -1173,6 +1176,21 @@ var SpicePlot = ({
|
|
|
1173
1176
|
}
|
|
1174
1177
|
);
|
|
1175
1178
|
}
|
|
1179
|
+
if (!hasRun) {
|
|
1180
|
+
return /* @__PURE__ */ jsx7(
|
|
1181
|
+
"div",
|
|
1182
|
+
{
|
|
1183
|
+
style: {
|
|
1184
|
+
height: "300px",
|
|
1185
|
+
width: "100%",
|
|
1186
|
+
display: "flex",
|
|
1187
|
+
alignItems: "center",
|
|
1188
|
+
justifyContent: "center"
|
|
1189
|
+
},
|
|
1190
|
+
children: 'Click "Run" to start the simulation.'
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1176
1194
|
if (error) {
|
|
1177
1195
|
return /* @__PURE__ */ jsxs4(
|
|
1178
1196
|
"div",
|
|
@@ -1294,7 +1312,8 @@ var SpiceSimulationOverlay = ({
|
|
|
1294
1312
|
isLoading,
|
|
1295
1313
|
error,
|
|
1296
1314
|
simOptions,
|
|
1297
|
-
onSimOptionsChange
|
|
1315
|
+
onSimOptionsChange,
|
|
1316
|
+
hasRun
|
|
1298
1317
|
}) => {
|
|
1299
1318
|
const [startTimeDraft, setStartTimeDraft] = useState3(
|
|
1300
1319
|
String(simOptions.startTime)
|
|
@@ -1397,7 +1416,8 @@ var SpiceSimulationOverlay = ({
|
|
|
1397
1416
|
plotData,
|
|
1398
1417
|
nodes: filteredNodes,
|
|
1399
1418
|
isLoading,
|
|
1400
|
-
error
|
|
1419
|
+
error,
|
|
1420
|
+
hasRun
|
|
1401
1421
|
}
|
|
1402
1422
|
) }),
|
|
1403
1423
|
/* @__PURE__ */ jsxs5(
|
|
@@ -1501,7 +1521,7 @@ var SpiceSimulationOverlay = ({
|
|
|
1501
1521
|
backgroundColor: "#f0f0f0",
|
|
1502
1522
|
cursor: "pointer"
|
|
1503
1523
|
},
|
|
1504
|
-
children: "Rerun"
|
|
1524
|
+
children: hasRun ? "Rerun" : "Run"
|
|
1505
1525
|
}
|
|
1506
1526
|
)
|
|
1507
1527
|
] })
|
|
@@ -1758,8 +1778,349 @@ var setStoredBoolean = (key, value) => {
|
|
|
1758
1778
|
}
|
|
1759
1779
|
};
|
|
1760
1780
|
|
|
1781
|
+
// lib/components/MouseTracker.tsx
|
|
1782
|
+
import {
|
|
1783
|
+
createContext,
|
|
1784
|
+
useCallback as useCallback3,
|
|
1785
|
+
useContext,
|
|
1786
|
+
useEffect as useEffect8,
|
|
1787
|
+
useMemo as useMemo3,
|
|
1788
|
+
useRef as useRef4
|
|
1789
|
+
} from "react";
|
|
1790
|
+
import { Fragment as Fragment2, jsx as jsx9 } from "react/jsx-runtime";
|
|
1791
|
+
var MouseTrackerContext = createContext(null);
|
|
1792
|
+
var boundsAreEqual = (a, b) => {
|
|
1793
|
+
if (!a && !b) return true;
|
|
1794
|
+
if (!a || !b) return false;
|
|
1795
|
+
return a.minX === b.minX && a.maxX === b.maxX && a.minY === b.minY && a.maxY === b.maxY;
|
|
1796
|
+
};
|
|
1797
|
+
var MouseTracker = ({ children }) => {
|
|
1798
|
+
const existingContext = useContext(MouseTrackerContext);
|
|
1799
|
+
if (existingContext) {
|
|
1800
|
+
return /* @__PURE__ */ jsx9(Fragment2, { children });
|
|
1801
|
+
}
|
|
1802
|
+
const storeRef = useRef4({
|
|
1803
|
+
pointer: null,
|
|
1804
|
+
boundingBoxes: /* @__PURE__ */ new Map(),
|
|
1805
|
+
hoveringIds: /* @__PURE__ */ new Set(),
|
|
1806
|
+
subscribers: /* @__PURE__ */ new Set()
|
|
1807
|
+
});
|
|
1808
|
+
const notifySubscribers = useCallback3(() => {
|
|
1809
|
+
for (const callback of storeRef.current.subscribers) {
|
|
1810
|
+
callback();
|
|
1811
|
+
}
|
|
1812
|
+
}, []);
|
|
1813
|
+
const updateHovering = useCallback3(() => {
|
|
1814
|
+
const pointer = storeRef.current.pointer;
|
|
1815
|
+
const newHovering = /* @__PURE__ */ new Set();
|
|
1816
|
+
if (pointer) {
|
|
1817
|
+
for (const [id, registration] of storeRef.current.boundingBoxes) {
|
|
1818
|
+
const bounds = registration.bounds;
|
|
1819
|
+
if (!bounds) continue;
|
|
1820
|
+
if (pointer.x >= bounds.minX && pointer.x <= bounds.maxX && pointer.y >= bounds.minY && pointer.y <= bounds.maxY) {
|
|
1821
|
+
newHovering.add(id);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
const prevHovering = storeRef.current.hoveringIds;
|
|
1826
|
+
if (newHovering.size === prevHovering.size && [...newHovering].every((id) => prevHovering.has(id))) {
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
storeRef.current.hoveringIds = newHovering;
|
|
1830
|
+
notifySubscribers();
|
|
1831
|
+
}, [notifySubscribers]);
|
|
1832
|
+
const registerBoundingBox = useCallback3(
|
|
1833
|
+
(id, registration) => {
|
|
1834
|
+
storeRef.current.boundingBoxes.set(id, registration);
|
|
1835
|
+
updateHovering();
|
|
1836
|
+
},
|
|
1837
|
+
[updateHovering]
|
|
1838
|
+
);
|
|
1839
|
+
const updateBoundingBox = useCallback3(
|
|
1840
|
+
(id, registration) => {
|
|
1841
|
+
const existing = storeRef.current.boundingBoxes.get(id);
|
|
1842
|
+
if (existing && boundsAreEqual(existing.bounds, registration.bounds) && existing.onClick === registration.onClick) {
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
storeRef.current.boundingBoxes.set(id, registration);
|
|
1846
|
+
updateHovering();
|
|
1847
|
+
},
|
|
1848
|
+
[updateHovering]
|
|
1849
|
+
);
|
|
1850
|
+
const unregisterBoundingBox = useCallback3(
|
|
1851
|
+
(id) => {
|
|
1852
|
+
const removed = storeRef.current.boundingBoxes.delete(id);
|
|
1853
|
+
if (removed) {
|
|
1854
|
+
updateHovering();
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
[updateHovering]
|
|
1858
|
+
);
|
|
1859
|
+
const subscribe = useCallback3((listener) => {
|
|
1860
|
+
storeRef.current.subscribers.add(listener);
|
|
1861
|
+
return () => {
|
|
1862
|
+
storeRef.current.subscribers.delete(listener);
|
|
1863
|
+
};
|
|
1864
|
+
}, []);
|
|
1865
|
+
const isHovering = useCallback3((id) => {
|
|
1866
|
+
return storeRef.current.hoveringIds.has(id);
|
|
1867
|
+
}, []);
|
|
1868
|
+
useEffect8(() => {
|
|
1869
|
+
const handlePointerPosition = (event) => {
|
|
1870
|
+
const { clientX, clientY } = event;
|
|
1871
|
+
const pointer = storeRef.current.pointer;
|
|
1872
|
+
if (pointer && pointer.x === clientX && pointer.y === clientY) {
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
storeRef.current.pointer = { x: clientX, y: clientY };
|
|
1876
|
+
updateHovering();
|
|
1877
|
+
};
|
|
1878
|
+
const handlePointerLeave = () => {
|
|
1879
|
+
if (storeRef.current.pointer === null) return;
|
|
1880
|
+
storeRef.current.pointer = null;
|
|
1881
|
+
updateHovering();
|
|
1882
|
+
};
|
|
1883
|
+
const handleClick = (event) => {
|
|
1884
|
+
const { clientX, clientY } = event;
|
|
1885
|
+
for (const registration of storeRef.current.boundingBoxes.values()) {
|
|
1886
|
+
const bounds = registration.bounds;
|
|
1887
|
+
if (!bounds) continue;
|
|
1888
|
+
if (clientX >= bounds.minX && clientX <= bounds.maxX && clientY >= bounds.minY && clientY <= bounds.maxY) {
|
|
1889
|
+
registration.onClick?.(event);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
};
|
|
1893
|
+
window.addEventListener("pointermove", handlePointerPosition, {
|
|
1894
|
+
passive: true
|
|
1895
|
+
});
|
|
1896
|
+
window.addEventListener("pointerdown", handlePointerPosition, {
|
|
1897
|
+
passive: true
|
|
1898
|
+
});
|
|
1899
|
+
window.addEventListener("pointerup", handlePointerPosition, {
|
|
1900
|
+
passive: true
|
|
1901
|
+
});
|
|
1902
|
+
window.addEventListener("pointerleave", handlePointerLeave);
|
|
1903
|
+
window.addEventListener("pointercancel", handlePointerLeave);
|
|
1904
|
+
window.addEventListener("blur", handlePointerLeave);
|
|
1905
|
+
window.addEventListener("click", handleClick, { passive: true });
|
|
1906
|
+
return () => {
|
|
1907
|
+
window.removeEventListener("pointermove", handlePointerPosition);
|
|
1908
|
+
window.removeEventListener("pointerdown", handlePointerPosition);
|
|
1909
|
+
window.removeEventListener("pointerup", handlePointerPosition);
|
|
1910
|
+
window.removeEventListener("pointerleave", handlePointerLeave);
|
|
1911
|
+
window.removeEventListener("pointercancel", handlePointerLeave);
|
|
1912
|
+
window.removeEventListener("blur", handlePointerLeave);
|
|
1913
|
+
window.removeEventListener("click", handleClick);
|
|
1914
|
+
};
|
|
1915
|
+
}, [updateHovering]);
|
|
1916
|
+
const value = useMemo3(
|
|
1917
|
+
() => ({
|
|
1918
|
+
registerBoundingBox,
|
|
1919
|
+
updateBoundingBox,
|
|
1920
|
+
unregisterBoundingBox,
|
|
1921
|
+
subscribe,
|
|
1922
|
+
isHovering
|
|
1923
|
+
}),
|
|
1924
|
+
[
|
|
1925
|
+
registerBoundingBox,
|
|
1926
|
+
updateBoundingBox,
|
|
1927
|
+
unregisterBoundingBox,
|
|
1928
|
+
subscribe,
|
|
1929
|
+
isHovering
|
|
1930
|
+
]
|
|
1931
|
+
);
|
|
1932
|
+
return /* @__PURE__ */ jsx9(MouseTrackerContext.Provider, { value, children });
|
|
1933
|
+
};
|
|
1934
|
+
|
|
1935
|
+
// lib/components/SchematicComponentMouseTarget.tsx
|
|
1936
|
+
import { useCallback as useCallback4, useEffect as useEffect10, useRef as useRef6, useState as useState5 } from "react";
|
|
1937
|
+
|
|
1938
|
+
// lib/hooks/useMouseEventsOverBoundingBox.ts
|
|
1939
|
+
import {
|
|
1940
|
+
useContext as useContext2,
|
|
1941
|
+
useEffect as useEffect9,
|
|
1942
|
+
useId,
|
|
1943
|
+
useMemo as useMemo4,
|
|
1944
|
+
useRef as useRef5,
|
|
1945
|
+
useSyncExternalStore
|
|
1946
|
+
} from "react";
|
|
1947
|
+
var useMouseEventsOverBoundingBox = (options) => {
|
|
1948
|
+
const context = useContext2(MouseTrackerContext);
|
|
1949
|
+
if (!context) {
|
|
1950
|
+
throw new Error(
|
|
1951
|
+
"useMouseEventsOverBoundingBox must be used within a MouseTracker"
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
const id = useId();
|
|
1955
|
+
const latestOptionsRef = useRef5(options);
|
|
1956
|
+
latestOptionsRef.current = options;
|
|
1957
|
+
const handleClick = useMemo4(
|
|
1958
|
+
() => (event) => {
|
|
1959
|
+
latestOptionsRef.current.onClick?.(event);
|
|
1960
|
+
},
|
|
1961
|
+
[]
|
|
1962
|
+
);
|
|
1963
|
+
useEffect9(() => {
|
|
1964
|
+
context.registerBoundingBox(id, {
|
|
1965
|
+
bounds: latestOptionsRef.current.bounds,
|
|
1966
|
+
onClick: latestOptionsRef.current.onClick ? handleClick : void 0
|
|
1967
|
+
});
|
|
1968
|
+
return () => {
|
|
1969
|
+
context.unregisterBoundingBox(id);
|
|
1970
|
+
};
|
|
1971
|
+
}, [context, handleClick, id]);
|
|
1972
|
+
useEffect9(() => {
|
|
1973
|
+
context.updateBoundingBox(id, {
|
|
1974
|
+
bounds: latestOptionsRef.current.bounds,
|
|
1975
|
+
onClick: latestOptionsRef.current.onClick ? handleClick : void 0
|
|
1976
|
+
});
|
|
1977
|
+
}, [
|
|
1978
|
+
context,
|
|
1979
|
+
handleClick,
|
|
1980
|
+
id,
|
|
1981
|
+
options.bounds?.minX,
|
|
1982
|
+
options.bounds?.maxX,
|
|
1983
|
+
options.bounds?.minY,
|
|
1984
|
+
options.bounds?.maxY,
|
|
1985
|
+
options.onClick
|
|
1986
|
+
]);
|
|
1987
|
+
const hovering = useSyncExternalStore(
|
|
1988
|
+
context.subscribe,
|
|
1989
|
+
() => context.isHovering(id),
|
|
1990
|
+
() => false
|
|
1991
|
+
);
|
|
1992
|
+
return { hovering };
|
|
1993
|
+
};
|
|
1994
|
+
|
|
1995
|
+
// lib/components/SchematicComponentMouseTarget.tsx
|
|
1996
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1997
|
+
var areMeasurementsEqual = (a, b) => {
|
|
1998
|
+
if (!a && !b) return true;
|
|
1999
|
+
if (!a || !b) return false;
|
|
2000
|
+
return Math.abs(a.bounds.minX - b.bounds.minX) < 0.5 && Math.abs(a.bounds.maxX - b.bounds.maxX) < 0.5 && Math.abs(a.bounds.minY - b.bounds.minY) < 0.5 && Math.abs(a.bounds.maxY - b.bounds.maxY) < 0.5 && Math.abs(a.rect.left - b.rect.left) < 0.5 && Math.abs(a.rect.top - b.rect.top) < 0.5 && Math.abs(a.rect.width - b.rect.width) < 0.5 && Math.abs(a.rect.height - b.rect.height) < 0.5;
|
|
2001
|
+
};
|
|
2002
|
+
var SchematicComponentMouseTarget = ({
|
|
2003
|
+
componentId,
|
|
2004
|
+
svgDivRef,
|
|
2005
|
+
containerRef,
|
|
2006
|
+
onComponentClick,
|
|
2007
|
+
showOutline,
|
|
2008
|
+
circuitJsonKey
|
|
2009
|
+
}) => {
|
|
2010
|
+
const [measurement, setMeasurement] = useState5(null);
|
|
2011
|
+
const frameRef = useRef6(null);
|
|
2012
|
+
const measure = useCallback4(() => {
|
|
2013
|
+
frameRef.current = null;
|
|
2014
|
+
const svgDiv = svgDivRef.current;
|
|
2015
|
+
const container = containerRef.current;
|
|
2016
|
+
if (!svgDiv || !container) {
|
|
2017
|
+
setMeasurement((prev) => prev ? null : prev);
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
const element = svgDiv.querySelector(
|
|
2021
|
+
`[data-schematic-component-id="${componentId}"]`
|
|
2022
|
+
);
|
|
2023
|
+
if (!element) {
|
|
2024
|
+
setMeasurement((prev) => prev ? null : prev);
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
const elementRect = element.getBoundingClientRect();
|
|
2028
|
+
const containerRect = container.getBoundingClientRect();
|
|
2029
|
+
const nextMeasurement = {
|
|
2030
|
+
bounds: {
|
|
2031
|
+
minX: elementRect.left,
|
|
2032
|
+
maxX: elementRect.right,
|
|
2033
|
+
minY: elementRect.top,
|
|
2034
|
+
maxY: elementRect.bottom
|
|
2035
|
+
},
|
|
2036
|
+
rect: {
|
|
2037
|
+
left: elementRect.left - containerRect.left,
|
|
2038
|
+
top: elementRect.top - containerRect.top,
|
|
2039
|
+
width: elementRect.width,
|
|
2040
|
+
height: elementRect.height
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
setMeasurement(
|
|
2044
|
+
(prev) => areMeasurementsEqual(prev, nextMeasurement) ? prev : nextMeasurement
|
|
2045
|
+
);
|
|
2046
|
+
}, [componentId, containerRef, svgDivRef]);
|
|
2047
|
+
const scheduleMeasure = useCallback4(() => {
|
|
2048
|
+
if (frameRef.current !== null) return;
|
|
2049
|
+
frameRef.current = window.requestAnimationFrame(measure);
|
|
2050
|
+
}, [measure]);
|
|
2051
|
+
useEffect10(() => {
|
|
2052
|
+
scheduleMeasure();
|
|
2053
|
+
}, [scheduleMeasure, circuitJsonKey]);
|
|
2054
|
+
useEffect10(() => {
|
|
2055
|
+
scheduleMeasure();
|
|
2056
|
+
const svgDiv = svgDivRef.current;
|
|
2057
|
+
const container = containerRef.current;
|
|
2058
|
+
if (!svgDiv || !container) return;
|
|
2059
|
+
const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
|
|
2060
|
+
scheduleMeasure();
|
|
2061
|
+
}) : null;
|
|
2062
|
+
resizeObserver?.observe(container);
|
|
2063
|
+
resizeObserver?.observe(svgDiv);
|
|
2064
|
+
const mutationObserver = typeof MutationObserver !== "undefined" ? new MutationObserver(() => {
|
|
2065
|
+
scheduleMeasure();
|
|
2066
|
+
}) : null;
|
|
2067
|
+
mutationObserver?.observe(svgDiv, {
|
|
2068
|
+
attributes: true,
|
|
2069
|
+
attributeFilter: ["style", "transform"],
|
|
2070
|
+
subtree: true,
|
|
2071
|
+
childList: true
|
|
2072
|
+
});
|
|
2073
|
+
window.addEventListener("scroll", scheduleMeasure, true);
|
|
2074
|
+
window.addEventListener("resize", scheduleMeasure);
|
|
2075
|
+
return () => {
|
|
2076
|
+
resizeObserver?.disconnect();
|
|
2077
|
+
mutationObserver?.disconnect();
|
|
2078
|
+
window.removeEventListener("scroll", scheduleMeasure, true);
|
|
2079
|
+
window.removeEventListener("resize", scheduleMeasure);
|
|
2080
|
+
if (frameRef.current !== null) {
|
|
2081
|
+
cancelAnimationFrame(frameRef.current);
|
|
2082
|
+
frameRef.current = null;
|
|
2083
|
+
}
|
|
2084
|
+
};
|
|
2085
|
+
}, [scheduleMeasure, svgDivRef, containerRef]);
|
|
2086
|
+
const handleClick = useCallback4(
|
|
2087
|
+
(event) => {
|
|
2088
|
+
if (onComponentClick) {
|
|
2089
|
+
onComponentClick(componentId, event);
|
|
2090
|
+
}
|
|
2091
|
+
},
|
|
2092
|
+
[componentId, onComponentClick]
|
|
2093
|
+
);
|
|
2094
|
+
const bounds = measurement?.bounds ?? null;
|
|
2095
|
+
const { hovering } = useMouseEventsOverBoundingBox({
|
|
2096
|
+
bounds,
|
|
2097
|
+
onClick: onComponentClick ? handleClick : void 0
|
|
2098
|
+
});
|
|
2099
|
+
if (!measurement || !hovering || !showOutline) {
|
|
2100
|
+
return null;
|
|
2101
|
+
}
|
|
2102
|
+
const rect = measurement.rect;
|
|
2103
|
+
return /* @__PURE__ */ jsx10(
|
|
2104
|
+
"div",
|
|
2105
|
+
{
|
|
2106
|
+
style: {
|
|
2107
|
+
position: "absolute",
|
|
2108
|
+
left: rect.left,
|
|
2109
|
+
top: rect.top,
|
|
2110
|
+
width: rect.width,
|
|
2111
|
+
height: rect.height,
|
|
2112
|
+
border: "1.5px solid rgba(51, 153, 255, 0.9)",
|
|
2113
|
+
borderRadius: "6px",
|
|
2114
|
+
pointerEvents: "none",
|
|
2115
|
+
zIndex: zIndexMap.schematicComponentHoverOutline,
|
|
2116
|
+
boxShadow: "0 0 6px rgba(51, 153, 255, 0.35)"
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
);
|
|
2120
|
+
};
|
|
2121
|
+
|
|
1761
2122
|
// lib/components/SchematicViewer.tsx
|
|
1762
|
-
import { jsx as
|
|
2123
|
+
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1763
2124
|
var SchematicViewer = ({
|
|
1764
2125
|
circuitJson,
|
|
1765
2126
|
containerStyle,
|
|
@@ -1772,13 +2133,14 @@ var SchematicViewer = ({
|
|
|
1772
2133
|
clickToInteractEnabled = false,
|
|
1773
2134
|
colorOverrides,
|
|
1774
2135
|
spiceSimulationEnabled = false,
|
|
1775
|
-
disableGroups = false
|
|
2136
|
+
disableGroups = false,
|
|
2137
|
+
onSchematicComponentClicked
|
|
1776
2138
|
}) => {
|
|
1777
2139
|
if (debug3) {
|
|
1778
2140
|
enableDebug();
|
|
1779
2141
|
}
|
|
1780
|
-
const [showSpiceOverlay, setShowSpiceOverlay] =
|
|
1781
|
-
const [spiceSimOptions, setSpiceSimOptions] =
|
|
2142
|
+
const [showSpiceOverlay, setShowSpiceOverlay] = useState6(false);
|
|
2143
|
+
const [spiceSimOptions, setSpiceSimOptions] = useState6({
|
|
1782
2144
|
showVoltage: true,
|
|
1783
2145
|
showCurrent: false,
|
|
1784
2146
|
startTime: 0,
|
|
@@ -1789,11 +2151,11 @@ var SchematicViewer = ({
|
|
|
1789
2151
|
const getCircuitHash = (circuitJson2) => {
|
|
1790
2152
|
return `${circuitJson2?.length || 0}_${circuitJson2?.editCount || 0}`;
|
|
1791
2153
|
};
|
|
1792
|
-
const circuitJsonKey =
|
|
2154
|
+
const circuitJsonKey = useMemo5(
|
|
1793
2155
|
() => getCircuitHash(circuitJson),
|
|
1794
2156
|
[circuitJson]
|
|
1795
2157
|
);
|
|
1796
|
-
const spiceString =
|
|
2158
|
+
const spiceString = useMemo5(() => {
|
|
1797
2159
|
if (!spiceSimulationEnabled) return null;
|
|
1798
2160
|
try {
|
|
1799
2161
|
return getSpiceFromCircuitJson(circuitJson, spiceSimOptions);
|
|
@@ -1807,24 +2169,36 @@ var SchematicViewer = ({
|
|
|
1807
2169
|
spiceSimOptions.startTime,
|
|
1808
2170
|
spiceSimOptions.duration
|
|
1809
2171
|
]);
|
|
2172
|
+
const [hasSpiceSimRun, setHasSpiceSimRun] = useState6(false);
|
|
2173
|
+
useEffect11(() => {
|
|
2174
|
+
setHasSpiceSimRun(false);
|
|
2175
|
+
}, [circuitJsonKey]);
|
|
1810
2176
|
const {
|
|
1811
2177
|
plotData,
|
|
1812
2178
|
nodes,
|
|
1813
2179
|
isLoading: isSpiceSimLoading,
|
|
1814
2180
|
error: spiceSimError
|
|
1815
|
-
} = useSpiceSimulation(spiceString);
|
|
1816
|
-
const [editModeEnabled, setEditModeEnabled] =
|
|
1817
|
-
const [snapToGrid, setSnapToGrid] =
|
|
1818
|
-
const [isInteractionEnabled, setIsInteractionEnabled] =
|
|
2181
|
+
} = useSpiceSimulation(hasSpiceSimRun ? spiceString : null);
|
|
2182
|
+
const [editModeEnabled, setEditModeEnabled] = useState6(defaultEditMode);
|
|
2183
|
+
const [snapToGrid, setSnapToGrid] = useState6(true);
|
|
2184
|
+
const [isInteractionEnabled, setIsInteractionEnabled] = useState6(
|
|
1819
2185
|
!clickToInteractEnabled
|
|
1820
2186
|
);
|
|
1821
|
-
const [showViewMenu, setShowViewMenu] =
|
|
1822
|
-
const [showSchematicGroups, setShowSchematicGroups] =
|
|
2187
|
+
const [showViewMenu, setShowViewMenu] = useState6(false);
|
|
2188
|
+
const [showSchematicGroups, setShowSchematicGroups] = useState6(() => {
|
|
1823
2189
|
if (disableGroups) return false;
|
|
1824
2190
|
return getStoredBoolean("schematic_viewer_show_groups", false);
|
|
1825
2191
|
});
|
|
1826
|
-
const svgDivRef =
|
|
1827
|
-
const touchStartRef =
|
|
2192
|
+
const svgDivRef = useRef7(null);
|
|
2193
|
+
const touchStartRef = useRef7(null);
|
|
2194
|
+
const schematicComponentIds = useMemo5(() => {
|
|
2195
|
+
try {
|
|
2196
|
+
return su6(circuitJson).schematic_component?.list()?.map((component) => component.schematic_component_id) ?? [];
|
|
2197
|
+
} catch (err) {
|
|
2198
|
+
console.error("Failed to derive schematic component ids", err);
|
|
2199
|
+
return [];
|
|
2200
|
+
}
|
|
2201
|
+
}, [circuitJsonKey, circuitJson]);
|
|
1828
2202
|
const handleTouchStart = (e) => {
|
|
1829
2203
|
const touch = e.touches[0];
|
|
1830
2204
|
touchStartRef.current = {
|
|
@@ -1844,9 +2218,9 @@ var SchematicViewer = ({
|
|
|
1844
2218
|
}
|
|
1845
2219
|
touchStartRef.current = null;
|
|
1846
2220
|
};
|
|
1847
|
-
const [internalEditEvents, setInternalEditEvents] =
|
|
1848
|
-
const circuitJsonRef =
|
|
1849
|
-
|
|
2221
|
+
const [internalEditEvents, setInternalEditEvents] = useState6([]);
|
|
2222
|
+
const circuitJsonRef = useRef7(circuitJson);
|
|
2223
|
+
useEffect11(() => {
|
|
1850
2224
|
const circuitHash = getCircuitHash(circuitJson);
|
|
1851
2225
|
const circuitHashRef = getCircuitHash(circuitJsonRef.current);
|
|
1852
2226
|
if (circuitHash !== circuitHashRef) {
|
|
@@ -1867,7 +2241,7 @@ var SchematicViewer = ({
|
|
|
1867
2241
|
enabled: isInteractionEnabled && !showSpiceOverlay
|
|
1868
2242
|
});
|
|
1869
2243
|
const { containerWidth, containerHeight } = useResizeHandling(containerRef);
|
|
1870
|
-
const svgString =
|
|
2244
|
+
const svgString = useMemo5(() => {
|
|
1871
2245
|
if (!containerWidth || !containerHeight) return "";
|
|
1872
2246
|
return convertCircuitJsonToSchematicSvg(circuitJson, {
|
|
1873
2247
|
width: containerWidth,
|
|
@@ -1879,13 +2253,13 @@ var SchematicViewer = ({
|
|
|
1879
2253
|
colorOverrides
|
|
1880
2254
|
});
|
|
1881
2255
|
}, [circuitJsonKey, containerWidth, containerHeight]);
|
|
1882
|
-
const containerBackgroundColor =
|
|
2256
|
+
const containerBackgroundColor = useMemo5(() => {
|
|
1883
2257
|
const match = svgString.match(
|
|
1884
2258
|
/<svg[^>]*style="[^"]*background-color:\s*([^;\"]+)/i
|
|
1885
2259
|
);
|
|
1886
2260
|
return match?.[1] ?? "transparent";
|
|
1887
2261
|
}, [svgString]);
|
|
1888
|
-
const realToSvgProjection =
|
|
2262
|
+
const realToSvgProjection = useMemo5(() => {
|
|
1889
2263
|
if (!svgString) return identity();
|
|
1890
2264
|
const transformString = svgString.match(
|
|
1891
2265
|
/data-real-to-screen-transform="([^"]+)"/
|
|
@@ -1903,7 +2277,7 @@ var SchematicViewer = ({
|
|
|
1903
2277
|
onEditEvent(event);
|
|
1904
2278
|
}
|
|
1905
2279
|
};
|
|
1906
|
-
const editEventsWithUnappliedEditEvents =
|
|
2280
|
+
const editEventsWithUnappliedEditEvents = useMemo5(() => {
|
|
1907
2281
|
return [...unappliedEditEvents, ...internalEditEvents];
|
|
1908
2282
|
}, [unappliedEditEvents, internalEditEvents]);
|
|
1909
2283
|
const {
|
|
@@ -1940,12 +2314,12 @@ var SchematicViewer = ({
|
|
|
1940
2314
|
circuitJsonKey,
|
|
1941
2315
|
showGroups: showSchematicGroups && !disableGroups
|
|
1942
2316
|
});
|
|
1943
|
-
const handleComponentTouchStartRef =
|
|
1944
|
-
|
|
2317
|
+
const handleComponentTouchStartRef = useRef7(handleComponentTouchStart);
|
|
2318
|
+
useEffect11(() => {
|
|
1945
2319
|
handleComponentTouchStartRef.current = handleComponentTouchStart;
|
|
1946
2320
|
}, [handleComponentTouchStart]);
|
|
1947
|
-
const svgDiv =
|
|
1948
|
-
() => /* @__PURE__ */
|
|
2321
|
+
const svgDiv = useMemo5(
|
|
2322
|
+
() => /* @__PURE__ */ jsx11(
|
|
1949
2323
|
"div",
|
|
1950
2324
|
{
|
|
1951
2325
|
ref: svgDivRef,
|
|
@@ -1969,7 +2343,7 @@ var SchematicViewer = ({
|
|
|
1969
2343
|
showSpiceOverlay
|
|
1970
2344
|
]
|
|
1971
2345
|
);
|
|
1972
|
-
return /* @__PURE__ */ jsxs6(
|
|
2346
|
+
return /* @__PURE__ */ jsx11(MouseTracker, { children: /* @__PURE__ */ jsxs6(
|
|
1973
2347
|
"div",
|
|
1974
2348
|
{
|
|
1975
2349
|
ref: containerRef,
|
|
@@ -2010,7 +2384,7 @@ var SchematicViewer = ({
|
|
|
2010
2384
|
handleTouchEnd(e);
|
|
2011
2385
|
},
|
|
2012
2386
|
children: [
|
|
2013
|
-
!isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */
|
|
2387
|
+
!isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx11(
|
|
2014
2388
|
"div",
|
|
2015
2389
|
{
|
|
2016
2390
|
onClick: (e) => {
|
|
@@ -2029,7 +2403,7 @@ var SchematicViewer = ({
|
|
|
2029
2403
|
pointerEvents: "all",
|
|
2030
2404
|
touchAction: "pan-x pan-y pinch-zoom"
|
|
2031
2405
|
},
|
|
2032
|
-
children: /* @__PURE__ */
|
|
2406
|
+
children: /* @__PURE__ */ jsx11(
|
|
2033
2407
|
"div",
|
|
2034
2408
|
{
|
|
2035
2409
|
style: {
|
|
@@ -2046,28 +2420,28 @@ var SchematicViewer = ({
|
|
|
2046
2420
|
)
|
|
2047
2421
|
}
|
|
2048
2422
|
),
|
|
2049
|
-
/* @__PURE__ */
|
|
2423
|
+
/* @__PURE__ */ jsx11(
|
|
2050
2424
|
ViewMenuIcon,
|
|
2051
2425
|
{
|
|
2052
2426
|
active: showViewMenu,
|
|
2053
2427
|
onClick: () => setShowViewMenu(!showViewMenu)
|
|
2054
2428
|
}
|
|
2055
2429
|
),
|
|
2056
|
-
editingEnabled && /* @__PURE__ */
|
|
2430
|
+
editingEnabled && /* @__PURE__ */ jsx11(
|
|
2057
2431
|
EditIcon,
|
|
2058
2432
|
{
|
|
2059
2433
|
active: editModeEnabled,
|
|
2060
2434
|
onClick: () => setEditModeEnabled(!editModeEnabled)
|
|
2061
2435
|
}
|
|
2062
2436
|
),
|
|
2063
|
-
editingEnabled && editModeEnabled && /* @__PURE__ */
|
|
2437
|
+
editingEnabled && editModeEnabled && /* @__PURE__ */ jsx11(
|
|
2064
2438
|
GridIcon,
|
|
2065
2439
|
{
|
|
2066
2440
|
active: snapToGrid,
|
|
2067
2441
|
onClick: () => setSnapToGrid(!snapToGrid)
|
|
2068
2442
|
}
|
|
2069
2443
|
),
|
|
2070
|
-
/* @__PURE__ */
|
|
2444
|
+
/* @__PURE__ */ jsx11(
|
|
2071
2445
|
ViewMenu,
|
|
2072
2446
|
{
|
|
2073
2447
|
circuitJson,
|
|
@@ -2083,8 +2457,8 @@ var SchematicViewer = ({
|
|
|
2083
2457
|
}
|
|
2084
2458
|
}
|
|
2085
2459
|
),
|
|
2086
|
-
spiceSimulationEnabled && /* @__PURE__ */
|
|
2087
|
-
showSpiceOverlay && /* @__PURE__ */
|
|
2460
|
+
spiceSimulationEnabled && /* @__PURE__ */ jsx11(SpiceSimulationIcon, { onClick: () => setShowSpiceOverlay(true) }),
|
|
2461
|
+
showSpiceOverlay && /* @__PURE__ */ jsx11(
|
|
2088
2462
|
SpiceSimulationOverlay,
|
|
2089
2463
|
{
|
|
2090
2464
|
spiceString,
|
|
@@ -2094,15 +2468,38 @@ var SchematicViewer = ({
|
|
|
2094
2468
|
isLoading: isSpiceSimLoading,
|
|
2095
2469
|
error: spiceSimError,
|
|
2096
2470
|
simOptions: spiceSimOptions,
|
|
2097
|
-
onSimOptionsChange:
|
|
2471
|
+
onSimOptionsChange: (options) => {
|
|
2472
|
+
setHasSpiceSimRun(true);
|
|
2473
|
+
setSpiceSimOptions(options);
|
|
2474
|
+
},
|
|
2475
|
+
hasRun: hasSpiceSimRun
|
|
2098
2476
|
}
|
|
2099
2477
|
),
|
|
2478
|
+
onSchematicComponentClicked && schematicComponentIds.map((componentId) => /* @__PURE__ */ jsx11(
|
|
2479
|
+
SchematicComponentMouseTarget,
|
|
2480
|
+
{
|
|
2481
|
+
componentId,
|
|
2482
|
+
svgDivRef,
|
|
2483
|
+
containerRef,
|
|
2484
|
+
showOutline: true,
|
|
2485
|
+
circuitJsonKey,
|
|
2486
|
+
onComponentClick: (id, event) => {
|
|
2487
|
+
onSchematicComponentClicked?.({
|
|
2488
|
+
schematicComponentId: id,
|
|
2489
|
+
event
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
},
|
|
2493
|
+
componentId
|
|
2494
|
+
)),
|
|
2100
2495
|
svgDiv
|
|
2101
2496
|
]
|
|
2102
2497
|
}
|
|
2103
|
-
);
|
|
2498
|
+
) });
|
|
2104
2499
|
};
|
|
2105
2500
|
export {
|
|
2106
|
-
|
|
2501
|
+
MouseTracker,
|
|
2502
|
+
SchematicViewer,
|
|
2503
|
+
useMouseEventsOverBoundingBox
|
|
2107
2504
|
};
|
|
2108
2505
|
//# sourceMappingURL=index.js.map
|