@tscircuit/schematic-viewer 2.0.55 → 2.0.57

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.
Files changed (68) hide show
  1. package/dist/index.js +7 -3
  2. package/dist/index.js.map +1 -1
  3. package/package.json +4 -1
  4. package/.github/workflows/bun-formatcheck.yml +0 -26
  5. package/.github/workflows/bun-pver-release.yml +0 -59
  6. package/.github/workflows/bun-typecheck.yml +0 -26
  7. package/.github/workflows/on-merge-inform-release-tracker.yml +0 -24
  8. package/CLAUDE.md +0 -1
  9. package/biome.json +0 -56
  10. package/bun.lockb +0 -0
  11. package/cosmos.config.json +0 -3
  12. package/cosmos.decorator.tsx +0 -3
  13. package/docs/circuit-to-svg-metadata.md +0 -151
  14. package/docs/dragndrop-spec.md +0 -39
  15. package/examples/example1-resistor-and-capacitor.fixture.tsx +0 -16
  16. package/examples/example10-groups-view-schematic-groups.fixture.tsx +0 -76
  17. package/examples/example11-automatic-grouping-view-schematic-groups.fixture.tsx +0 -109
  18. package/examples/example12-spice-boost-converter.fixture.tsx +0 -78
  19. package/examples/example13-disablegroups.fixture.tsx +0 -30
  20. package/examples/example14-schematic-component-click.fixture.tsx +0 -46
  21. package/examples/example15-analog-simulation-viewer.fixture.tsx +0 -145
  22. package/examples/example16-no-analog-simulation.fixture.tsx +0 -13
  23. package/examples/example17-schematic-ports.fixture.tsx +0 -49
  24. package/examples/example2-small-circuit.fixture.tsx +0 -48
  25. package/examples/example3-small-circuit-without-debug-grid.fixture.tsx +0 -44
  26. package/examples/example4-reset-edit-events.fixture.tsx +0 -57
  27. package/examples/example5-circuit-json-rerender.fixture.tsx +0 -110
  28. package/examples/example6-click-to-interact.fixture.tsx +0 -36
  29. package/examples/example7-schematic-viewer-fix-snapping.fixture.tsx +0 -123
  30. package/examples/example8-color-overrides.fixture.tsx +0 -52
  31. package/examples/example9-spice-rc-charging-voltage-divider.fixture.tsx +0 -77
  32. package/index.html +0 -12
  33. package/lib/components/AnalogSimulationViewer.tsx +0 -300
  34. package/lib/components/ControlledSchematicViewer.tsx +0 -40
  35. package/lib/components/EditIcon.tsx +0 -46
  36. package/lib/components/GridIcon.tsx +0 -45
  37. package/lib/components/MouseTracker.tsx +0 -257
  38. package/lib/components/SchematicComponentMouseTarget.tsx +0 -189
  39. package/lib/components/SchematicPortMouseTarget.tsx +0 -224
  40. package/lib/components/SchematicViewer.tsx +0 -582
  41. package/lib/components/SpiceIcon.tsx +0 -14
  42. package/lib/components/SpicePlot.tsx +0 -221
  43. package/lib/components/SpiceSimulationIcon.tsx +0 -32
  44. package/lib/components/SpiceSimulationOverlay.tsx +0 -250
  45. package/lib/components/ViewMenu.tsx +0 -218
  46. package/lib/components/ViewMenuIcon.tsx +0 -47
  47. package/lib/dev/render-to-circuit-json.ts +0 -8
  48. package/lib/hooks/use-resize-handling.ts +0 -35
  49. package/lib/hooks/useChangeSchematicComponentLocationsInSvg.ts +0 -117
  50. package/lib/hooks/useChangeSchematicTracesForMovedComponents.ts +0 -121
  51. package/lib/hooks/useComponentDragging.ts +0 -251
  52. package/lib/hooks/useLocalStorage.ts +0 -63
  53. package/lib/hooks/useMouseEventsOverBoundingBox.ts +0 -74
  54. package/lib/hooks/useSchematicGroupsOverlay.ts +0 -364
  55. package/lib/hooks/useSpiceSimulation.ts +0 -149
  56. package/lib/index.ts +0 -4
  57. package/lib/types/edit-events.ts +0 -16
  58. package/lib/types/eecircuit-engine.d.ts +0 -147
  59. package/lib/utils/debug.ts +0 -9
  60. package/lib/utils/get-component-offset-due-to-events.ts +0 -43
  61. package/lib/utils/spice-utils.ts +0 -128
  62. package/lib/utils/z-index-map.ts +0 -11
  63. package/lib/workers/spice-simulation.worker.ts +0 -51
  64. package/scripts/build-worker-blob-url.ts +0 -55
  65. package/src/main.tsx +0 -21
  66. package/tsconfig.json +0 -33
  67. package/tsup-webworker.config.ts +0 -13
  68. package/vite.config.js +0 -15
@@ -1,47 +0,0 @@
1
- import { zIndexMap } from "../utils/z-index-map"
2
-
3
- export const ViewMenuIcon = ({
4
- onClick,
5
- active,
6
- }: { onClick: () => void; active: boolean }) => {
7
- const handleInteraction = (e: React.MouseEvent | React.TouchEvent) => {
8
- e.preventDefault()
9
- onClick()
10
- }
11
-
12
- return (
13
- <div
14
- onClick={handleInteraction}
15
- onTouchEnd={handleInteraction}
16
- title={active ? "Hide view menu" : "Show view menu"}
17
- style={{
18
- position: "absolute",
19
- top: "16px",
20
- right: "16px",
21
- backgroundColor: active ? "#4CAF50" : "#fff",
22
- color: active ? "#fff" : "#000",
23
- padding: "8px",
24
- borderRadius: "4px",
25
- cursor: "pointer",
26
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
27
- display: "flex",
28
- alignItems: "center",
29
- gap: "4px",
30
- zIndex: zIndexMap.viewMenuIcon,
31
- }}
32
- >
33
- <svg
34
- width="16"
35
- height="16"
36
- viewBox="0 0 24 24"
37
- fill="none"
38
- stroke="currentColor"
39
- strokeWidth="2"
40
- >
41
- <circle cx="12" cy="12" r="1" />
42
- <circle cx="12" cy="5" r="1" />
43
- <circle cx="12" cy="19" r="1" />
44
- </svg>
45
- </div>
46
- )
47
- }
@@ -1,8 +0,0 @@
1
- import * as Core from "@tscircuit/core"
2
- import type { CircuitJson } from "circuit-json"
3
-
4
- export const renderToCircuitJson = (board: React.ReactElement) => {
5
- const circuit = new Core.Circuit()
6
- circuit.add(board)
7
- return circuit.getCircuitJson() as CircuitJson
8
- }
@@ -1,35 +0,0 @@
1
- import { useEffect, useState } from "react"
2
-
3
- export const useResizeHandling = (
4
- containerRef: React.RefObject<HTMLElement>,
5
- ) => {
6
- const [containerWidth, setContainerWidth] = useState(0)
7
- const [containerHeight, setContainerHeight] = useState(0)
8
-
9
- useEffect(() => {
10
- if (!containerRef.current) return
11
-
12
- const updateDimensions = () => {
13
- const rect = containerRef.current?.getBoundingClientRect()
14
- setContainerWidth(rect?.width || 0)
15
- setContainerHeight(rect?.height || 0)
16
- }
17
-
18
- // Set initial dimensions
19
- updateDimensions()
20
-
21
- // Add resize listener
22
- const resizeObserver = new ResizeObserver(updateDimensions)
23
- resizeObserver.observe(containerRef.current)
24
-
25
- // Fallback to window resize
26
- window.addEventListener("resize", updateDimensions)
27
-
28
- return () => {
29
- resizeObserver.disconnect()
30
- window.removeEventListener("resize", updateDimensions)
31
- }
32
- }, [])
33
-
34
- return { containerWidth, containerHeight }
35
- }
@@ -1,117 +0,0 @@
1
- import { su } from "@tscircuit/soup-util"
2
- import type {
3
- ManualEditEvent,
4
- EditSchematicComponentLocationEventWithElement,
5
- } from "lib/types/edit-events"
6
- import { type Matrix, compose, applyToPoint } from "transformation-matrix"
7
- import { useEffect, useRef } from "react"
8
- import { getComponentOffsetDueToEvents } from "lib/utils/get-component-offset-due-to-events"
9
- import type { CircuitJson } from "circuit-json"
10
-
11
- /**
12
- * This hook automatically applies the edit events to the schematic components
13
- * inside the svg div.
14
- *
15
- * Schematic components are "<g>" elements with a "data-circuit-json-type"
16
- * attribute equal to "schematic_component", these elements also have a
17
- * data-schematic-component-id attribute equal to the schematic_component_id
18
- */
19
- export const useChangeSchematicComponentLocationsInSvg = ({
20
- svgDivRef,
21
- realToSvgProjection,
22
- svgToScreenProjection,
23
- activeEditEvent,
24
- editEvents,
25
- }: {
26
- svgDivRef: React.RefObject<HTMLDivElement | null>
27
- realToSvgProjection: Matrix
28
- svgToScreenProjection: Matrix
29
- activeEditEvent: EditSchematicComponentLocationEventWithElement | null
30
- editEvents: ManualEditEvent[]
31
- }) => {
32
- // Keep track of the last known SVG content
33
- const lastSvgContentRef = useRef<string | null>(null)
34
-
35
- useEffect(() => {
36
- const svg = svgDivRef.current
37
- if (!svg) return
38
-
39
- // Create a MutationObserver to watch for changes in the div's content
40
- const observer = new MutationObserver((mutations) => {
41
- // Check if the SVG content has changed
42
- const currentSvgContent = svg.innerHTML
43
- if (currentSvgContent !== lastSvgContentRef.current) {
44
- lastSvgContentRef.current = currentSvgContent
45
-
46
- // Apply the transforms
47
- applyTransforms()
48
- }
49
- })
50
-
51
- // Function to apply transforms to components
52
- const applyTransforms = () => {
53
- const componentsThatHaveBeenMoved = new Set<string>()
54
- for (const event of editEvents) {
55
- if (
56
- "edit_event_type" in event &&
57
- event.edit_event_type === "edit_schematic_component_location"
58
- ) {
59
- componentsThatHaveBeenMoved.add(event.schematic_component_id)
60
- }
61
- }
62
- if (activeEditEvent) {
63
- componentsThatHaveBeenMoved.add(activeEditEvent.schematic_component_id)
64
- }
65
-
66
- // Reset all transforms
67
- const allComponents = svg.querySelectorAll(
68
- '[data-circuit-json-type="schematic_component"]',
69
- )
70
-
71
- for (const component of Array.from(allComponents)) {
72
- const schematic_component_id = component.getAttribute(
73
- "data-schematic-component-id",
74
- )!
75
-
76
- const offsetMm = getComponentOffsetDueToEvents({
77
- editEvents: [
78
- ...editEvents,
79
- ...(activeEditEvent ? [activeEditEvent] : []),
80
- ],
81
- schematic_component_id,
82
- })
83
-
84
- const offsetPx = {
85
- x: offsetMm.x * realToSvgProjection.a,
86
- y: offsetMm.y * realToSvgProjection.d,
87
- }
88
-
89
- const style: any = (component as any).style
90
- style.transform = `translate(${offsetPx.x}px, ${offsetPx.y}px)`
91
- if (
92
- activeEditEvent?.schematic_component_id === schematic_component_id
93
- ) {
94
- style.outline = "solid 2px rgba(255,0,0,0.5)"
95
- style.outlineOffset = "5px"
96
- } else if (style.outline) {
97
- style.outline = ""
98
- }
99
- }
100
- }
101
-
102
- // Start observing the div for changes
103
- observer.observe(svg, {
104
- childList: true, // Watch for changes to the child elements
105
- subtree: false, // Watch for changes in the entire subtree
106
- characterData: false, // Watch for changes to text content
107
- })
108
-
109
- // Apply transforms immediately on mount or when editEvents change
110
- applyTransforms()
111
-
112
- // Cleanup function
113
- return () => {
114
- observer.disconnect()
115
- }
116
- }, [svgDivRef, editEvents, activeEditEvent]) // Dependencies remain the same
117
- }
@@ -1,121 +0,0 @@
1
- import { useEffect, useRef } from "react"
2
- import { su } from "@tscircuit/soup-util"
3
- import type { ManualEditEvent } from "../types/edit-events"
4
- import type { CircuitJson } from "circuit-json"
5
-
6
- /**
7
- * This hook makes traces dashed when their connected components are being moved
8
- */
9
- export const useChangeSchematicTracesForMovedComponents = ({
10
- svgDivRef,
11
- circuitJson,
12
- activeEditEvent,
13
- editEvents,
14
- }: {
15
- svgDivRef: React.RefObject<HTMLDivElement | null>
16
- circuitJson: CircuitJson
17
- activeEditEvent: ManualEditEvent | null
18
- editEvents: ManualEditEvent[]
19
- }) => {
20
- // Keep track of the last known SVG content
21
- const lastSvgContentRef = useRef<string | null>(null)
22
-
23
- useEffect(() => {
24
- const svg = svgDivRef.current
25
- if (!svg) return
26
-
27
- const updateTraceStyles = () => {
28
- // Reset all traces to solid
29
- const allTraces = svg.querySelectorAll(
30
- '[data-circuit-json-type="schematic_trace"] path',
31
- )
32
-
33
- // Reset all traces to solid
34
- for (const trace of Array.from(allTraces)) {
35
- trace.setAttribute("stroke-dasharray", "0")
36
- ;(trace as any).style.animation = ""
37
- }
38
-
39
- // If there's an active edit event, make connected traces dashed
40
- for (const editEvent of [
41
- ...editEvents,
42
- ...(activeEditEvent ? [activeEditEvent] : []),
43
- ]) {
44
- if (
45
- "schematic_component_id" in editEvent &&
46
- editEvent.edit_event_type === "edit_schematic_component_location"
47
- ) {
48
- const sch_component = su(circuitJson).schematic_component.get(
49
- editEvent.schematic_component_id,
50
- )
51
- if (!sch_component) return
52
-
53
- const src_ports = su(circuitJson).source_port.list({
54
- source_component_id: sch_component.source_component_id,
55
- })
56
- const src_port_ids = new Set(src_ports.map((sp) => sp.source_port_id))
57
- const src_traces = su(circuitJson)
58
- .source_trace.list()
59
- .filter((st) =>
60
- st.connected_source_port_ids?.some((spi: string) =>
61
- src_port_ids.has(spi),
62
- ),
63
- )
64
- const src_trace_ids = new Set(
65
- src_traces.map((st) => st.source_trace_id),
66
- )
67
- const schematic_traces = su(circuitJson)
68
- .schematic_trace.list()
69
- .filter((st) => src_trace_ids.has(st.source_trace_id!))
70
-
71
- // Make the connected traces dashed
72
- schematic_traces.forEach((trace) => {
73
- const traceElements = svg.querySelectorAll(
74
- `[data-schematic-trace-id="${trace.schematic_trace_id}"] path`,
75
- )
76
- for (const traceElement of Array.from(traceElements)) {
77
- if (traceElement.getAttribute("class")?.includes("invisible"))
78
- continue
79
- traceElement.setAttribute("stroke-dasharray", "20,20")
80
- ;(traceElement as any).style.animation =
81
- "dash-animation 350ms linear infinite, pulse-animation 900ms linear infinite"
82
-
83
- if (!svg.querySelector("style#dash-animation")) {
84
- const style = document.createElement("style")
85
- style.id = "dash-animation"
86
- style.textContent = `
87
- @keyframes dash-animation {
88
- to {
89
- stroke-dashoffset: -40;
90
- }
91
- }
92
- @keyframes pulse-animation {
93
- 0% { opacity: 0.6; }
94
- 50% { opacity: 0.2; }
95
- 100% { opacity: 0.6; }
96
- }
97
- `
98
- svg.appendChild(style)
99
- }
100
- }
101
- })
102
- }
103
- }
104
- }
105
-
106
- // Apply styles immediately
107
- updateTraceStyles()
108
-
109
- // Cleanup function
110
- const observer = new MutationObserver(updateTraceStyles)
111
- observer.observe(svg, {
112
- childList: true, // Watch for changes to the child elements
113
- subtree: false, // Watch for changes in the entire subtree
114
- characterData: false, // Watch for changes to text content
115
- })
116
-
117
- return () => {
118
- observer.disconnect()
119
- }
120
- }, [svgDivRef, activeEditEvent, circuitJson, editEvents])
121
- }
@@ -1,251 +0,0 @@
1
- import { su } from "@tscircuit/soup-util"
2
- import Debug from "lib/utils/debug"
3
- import { getComponentOffsetDueToEvents } from "lib/utils/get-component-offset-due-to-events"
4
- import { useCallback, useEffect, useRef, useState } from "react"
5
- import { type Matrix, compose } from "transformation-matrix"
6
- import type {
7
- EditSchematicComponentLocationEventWithElement,
8
- ManualEditEvent,
9
- } from "../types/edit-events"
10
-
11
- const debug = Debug.extend("useComponentDragging")
12
-
13
- export const useComponentDragging = ({
14
- onEditEvent,
15
- editEvents = [],
16
- circuitJson,
17
- cancelDrag,
18
- svgToScreenProjection,
19
- realToSvgProjection,
20
- enabled = false,
21
- snapToGrid = false,
22
- }: {
23
- circuitJson: any[]
24
- editEvents: ManualEditEvent[]
25
- /** The projection returned from use-mouse-matrix-transform, indicating zoom on svg */
26
- svgToScreenProjection: Matrix
27
- /** The projection returned from circuit-to-svg, mm to svg */
28
- realToSvgProjection: Matrix
29
- onEditEvent?: (event: ManualEditEvent) => void
30
- cancelDrag?: () => void
31
- enabled?: boolean
32
- snapToGrid?: boolean
33
- }): {
34
- handleMouseDown: (e: React.MouseEvent) => void
35
- handleTouchStart: (e: React.TouchEvent) => void
36
- isDragging: boolean
37
- activeEditEvent: EditSchematicComponentLocationEventWithElement | null
38
- } => {
39
- const [activeEditEvent, setActiveEditEvent] =
40
- useState<EditSchematicComponentLocationEventWithElement | null>(null)
41
- const realToScreenProjection = compose(
42
- realToSvgProjection,
43
- svgToScreenProjection,
44
- )
45
-
46
- /**
47
- * Drag start position in screen space
48
- */
49
- const dragStartPosRef = useRef<{
50
- x: number
51
- y: number
52
- } | null>(null)
53
-
54
- const activeEditEventRef =
55
- useRef<EditSchematicComponentLocationEventWithElement | null>(null)
56
-
57
- // Store the latest positions of components being tracked
58
- const componentPositionsRef = useRef<Map<string, { x: number; y: number }>>(
59
- new Map(),
60
- )
61
-
62
- // Update position map with the latest positions from edit events
63
- useEffect(() => {
64
- // Process completed edit events to track latest positions
65
- editEvents.forEach((event) => {
66
- if (
67
- "edit_event_type" in event &&
68
- event.edit_event_type === "edit_schematic_component_location" &&
69
- !event.in_progress
70
- ) {
71
- componentPositionsRef.current.set(event.schematic_component_id, {
72
- ...event.new_center,
73
- })
74
- }
75
- })
76
- }, [editEvents])
77
-
78
- const startDrag = useCallback(
79
- (clientX: number, clientY: number, target: Element) => {
80
- if (!enabled) return false
81
-
82
- const componentGroup = target.closest(
83
- '[data-circuit-json-type="schematic_component"]',
84
- )
85
- if (!componentGroup) return false
86
-
87
- const schematic_component_id = componentGroup.getAttribute(
88
- "data-schematic-component-id",
89
- )
90
- if (!schematic_component_id) return false
91
-
92
- if (cancelDrag) cancelDrag()
93
-
94
- const schematic_component = su(circuitJson).schematic_component.get(
95
- schematic_component_id,
96
- )
97
- if (!schematic_component) return false
98
-
99
- dragStartPosRef.current = { x: clientX, y: clientY }
100
-
101
- let current_position: { x: number; y: number }
102
- const trackedPosition = componentPositionsRef.current.get(
103
- schematic_component_id,
104
- )
105
-
106
- if (trackedPosition) {
107
- current_position = { ...trackedPosition }
108
- } else {
109
- const editEventOffset = getComponentOffsetDueToEvents({
110
- editEvents,
111
- schematic_component_id: schematic_component_id,
112
- })
113
-
114
- current_position = {
115
- x: schematic_component.center.x + editEventOffset.x,
116
- y: schematic_component.center.y + editEventOffset.y,
117
- }
118
-
119
- componentPositionsRef.current.set(schematic_component_id, {
120
- ...current_position,
121
- })
122
- }
123
-
124
- const newEditEvent: EditSchematicComponentLocationEventWithElement = {
125
- edit_event_id: Math.random().toString(36).substr(2, 9),
126
- edit_event_type: "edit_schematic_component_location",
127
- schematic_component_id: schematic_component_id,
128
- original_center: current_position,
129
- new_center: { ...current_position },
130
- in_progress: true,
131
- created_at: Date.now(),
132
- _element: componentGroup as any,
133
- }
134
-
135
- activeEditEventRef.current = newEditEvent
136
- setActiveEditEvent(newEditEvent)
137
- return true
138
- },
139
- [cancelDrag, enabled, circuitJson, editEvents],
140
- )
141
-
142
- const handleMouseDown = useCallback(
143
- (e: React.MouseEvent) => {
144
- startDrag(e.clientX, e.clientY, e.target as Element)
145
- },
146
- [startDrag],
147
- )
148
-
149
- const handleTouchStart = useCallback(
150
- (e: React.TouchEvent) => {
151
- if (e.touches.length !== 1) return
152
- const touch = e.touches[0]
153
- if (startDrag(touch.clientX, touch.clientY, e.target as Element)) {
154
- e.preventDefault()
155
- }
156
- },
157
- [startDrag],
158
- )
159
-
160
- const updateDragPosition = useCallback(
161
- (clientX: number, clientY: number) => {
162
- if (!activeEditEventRef.current || !dragStartPosRef.current) return
163
-
164
- const screenDelta = {
165
- x: clientX - dragStartPosRef.current.x,
166
- y: clientY - dragStartPosRef.current.y,
167
- }
168
-
169
- const mmDelta = {
170
- x: screenDelta.x / realToScreenProjection.a,
171
- y: screenDelta.y / realToScreenProjection.d,
172
- }
173
-
174
- let newCenter = {
175
- x: activeEditEventRef.current.original_center.x + mmDelta.x,
176
- y: activeEditEventRef.current.original_center.y + mmDelta.y,
177
- }
178
- if (snapToGrid) {
179
- const snap = (v: number) => Math.round(v * 10) / 10
180
- newCenter = { x: snap(newCenter.x), y: snap(newCenter.y) }
181
- }
182
-
183
- const newEditEvent = {
184
- ...activeEditEventRef.current,
185
- new_center: newCenter,
186
- }
187
-
188
- activeEditEventRef.current = newEditEvent
189
- setActiveEditEvent(newEditEvent)
190
- },
191
- [realToScreenProjection, snapToGrid],
192
- )
193
-
194
- const handleMouseMove = useCallback(
195
- (e: MouseEvent) => updateDragPosition(e.clientX, e.clientY),
196
- [updateDragPosition],
197
- )
198
-
199
- const handleTouchMove = useCallback(
200
- (e: TouchEvent) => {
201
- if (e.touches.length !== 1 || !activeEditEventRef.current) return
202
- e.preventDefault()
203
- const touch = e.touches[0]
204
- updateDragPosition(touch.clientX, touch.clientY)
205
- },
206
- [updateDragPosition],
207
- )
208
-
209
- const endDrag = useCallback(() => {
210
- if (!activeEditEventRef.current) return
211
- const finalEvent = {
212
- ...activeEditEventRef.current,
213
- in_progress: false,
214
- }
215
-
216
- componentPositionsRef.current.set(finalEvent.schematic_component_id, {
217
- ...finalEvent.new_center,
218
- })
219
-
220
- debug("endDrag calling onEditEvent with new edit event", {
221
- newEditEvent: finalEvent,
222
- })
223
- if (onEditEvent) onEditEvent(finalEvent)
224
- activeEditEventRef.current = null
225
- dragStartPosRef.current = null
226
- setActiveEditEvent(null)
227
- }, [onEditEvent])
228
-
229
- const handleMouseUp = useCallback(() => endDrag(), [endDrag])
230
- const handleTouchEnd = useCallback(() => endDrag(), [endDrag])
231
-
232
- useEffect(() => {
233
- window.addEventListener("mousemove", handleMouseMove)
234
- window.addEventListener("mouseup", handleMouseUp)
235
- window.addEventListener("touchmove", handleTouchMove, { passive: false })
236
- window.addEventListener("touchend", handleTouchEnd)
237
- return () => {
238
- window.removeEventListener("mousemove", handleMouseMove)
239
- window.removeEventListener("mouseup", handleMouseUp)
240
- window.removeEventListener("touchmove", handleTouchMove)
241
- window.removeEventListener("touchend", handleTouchEnd)
242
- }
243
- }, [handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd])
244
-
245
- return {
246
- handleMouseDown,
247
- handleTouchStart,
248
- isDragging: !!activeEditEventRef.current,
249
- activeEditEvent: activeEditEvent,
250
- }
251
- }
@@ -1,63 +0,0 @@
1
- import { useCallback } from "react"
2
-
3
- export const STORAGE_KEYS = {
4
- IS_SHOWING_SCHEMATIC_GROUPS: "schematic_viewer_show_groups",
5
- } as const
6
-
7
- export const getStoredBoolean = (
8
- key: string,
9
- defaultValue: boolean,
10
- ): boolean => {
11
- if (typeof window === "undefined") return defaultValue
12
- try {
13
- const stored = localStorage.getItem(key)
14
- return stored !== null ? JSON.parse(stored) : defaultValue
15
- } catch {
16
- return defaultValue
17
- }
18
- }
19
-
20
- export const setStoredBoolean = (key: string, value: boolean): void => {
21
- if (typeof window === "undefined") return
22
- try {
23
- localStorage.setItem(key, JSON.stringify(value))
24
- } catch {}
25
- }
26
-
27
- export const useLocalStorage = () => {
28
- const getBoolean = useCallback(
29
- (key: string, defaultValue: boolean): boolean => {
30
- return getStoredBoolean(key, defaultValue)
31
- },
32
- [],
33
- )
34
-
35
- const setBoolean = useCallback((key: string, value: boolean): void => {
36
- setStoredBoolean(key, value)
37
- }, [])
38
-
39
- return {
40
- getBoolean,
41
- setBoolean,
42
- }
43
- }
44
-
45
- export const useLocalStorageValue = (key: string, defaultValue: boolean) => {
46
- const { getBoolean, setBoolean } = useLocalStorage()
47
-
48
- const getValue = useCallback(() => {
49
- return getBoolean(key, defaultValue)
50
- }, [getBoolean, key, defaultValue])
51
-
52
- const setValue = useCallback(
53
- (value: boolean) => {
54
- setBoolean(key, value)
55
- },
56
- [setBoolean, key],
57
- )
58
-
59
- return {
60
- getValue,
61
- setValue,
62
- }
63
- }