@archireport/react-native-drawing 0.2.0

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 (323) hide show
  1. package/README.md +181 -0
  2. package/lib/commonjs/DrawingEditor.js +815 -0
  3. package/lib/commonjs/DrawingEditor.js.map +1 -0
  4. package/lib/commonjs/assets/toolbar-icons/arrow-disabled.png +0 -0
  5. package/lib/commonjs/assets/toolbar-icons/arrow-enabled.png +0 -0
  6. package/lib/commonjs/assets/toolbar-icons/arrow.png +0 -0
  7. package/lib/commonjs/assets/toolbar-icons/circle-disabled.png +0 -0
  8. package/lib/commonjs/assets/toolbar-icons/circle-enabled.png +0 -0
  9. package/lib/commonjs/assets/toolbar-icons/circle.png +0 -0
  10. package/lib/commonjs/assets/toolbar-icons/freehand-disabled.png +0 -0
  11. package/lib/commonjs/assets/toolbar-icons/freehand-enabled.png +0 -0
  12. package/lib/commonjs/assets/toolbar-icons/freehand.png +0 -0
  13. package/lib/commonjs/assets/toolbar-icons/line-disabled.png +0 -0
  14. package/lib/commonjs/assets/toolbar-icons/line-enabled.png +0 -0
  15. package/lib/commonjs/assets/toolbar-icons/line.png +0 -0
  16. package/lib/commonjs/assets/toolbar-icons/measure-disabled.png +0 -0
  17. package/lib/commonjs/assets/toolbar-icons/measure-enabled.png +0 -0
  18. package/lib/commonjs/assets/toolbar-icons/measure.png +0 -0
  19. package/lib/commonjs/assets/toolbar-icons/move-disabled.png +0 -0
  20. package/lib/commonjs/assets/toolbar-icons/move-enabled.png +0 -0
  21. package/lib/commonjs/assets/toolbar-icons/move.png +0 -0
  22. package/lib/commonjs/assets/toolbar-icons/polygon-disabled.png +0 -0
  23. package/lib/commonjs/assets/toolbar-icons/polygon-enabled.png +0 -0
  24. package/lib/commonjs/assets/toolbar-icons/polygon.png +0 -0
  25. package/lib/commonjs/assets/toolbar-icons/rectangle-disabled.png +0 -0
  26. package/lib/commonjs/assets/toolbar-icons/rectangle-enabled.png +0 -0
  27. package/lib/commonjs/assets/toolbar-icons/rectangle.png +0 -0
  28. package/lib/commonjs/assets/toolbar-icons/text-disabled.png +0 -0
  29. package/lib/commonjs/assets/toolbar-icons/text-enabled.png +0 -0
  30. package/lib/commonjs/assets/toolbar-icons/text.png +0 -0
  31. package/lib/commonjs/components/ColorPalette.js +379 -0
  32. package/lib/commonjs/components/ColorPalette.js.map +1 -0
  33. package/lib/commonjs/components/LineWidthSlider.js +70 -0
  34. package/lib/commonjs/components/LineWidthSlider.js.map +1 -0
  35. package/lib/commonjs/components/MeasurementEditModal.js +153 -0
  36. package/lib/commonjs/components/MeasurementEditModal.js.map +1 -0
  37. package/lib/commonjs/components/MiniMap.js +244 -0
  38. package/lib/commonjs/components/MiniMap.js.map +1 -0
  39. package/lib/commonjs/components/TextAnnotation.js +162 -0
  40. package/lib/commonjs/components/TextAnnotation.js.map +1 -0
  41. package/lib/commonjs/components/TextEditModal.js +133 -0
  42. package/lib/commonjs/components/TextEditModal.js.map +1 -0
  43. package/lib/commonjs/components/Toolbar.js +198 -0
  44. package/lib/commonjs/components/Toolbar.js.map +1 -0
  45. package/lib/commonjs/components/ZoomBadge.js +161 -0
  46. package/lib/commonjs/components/ZoomBadge.js.map +1 -0
  47. package/lib/commonjs/hooks/useFreehandGesture.js +173 -0
  48. package/lib/commonjs/hooks/useFreehandGesture.js.map +1 -0
  49. package/lib/commonjs/hooks/usePolygonGesture.js +109 -0
  50. package/lib/commonjs/hooks/usePolygonGesture.js.map +1 -0
  51. package/lib/commonjs/hooks/useSelectionGesture.js +236 -0
  52. package/lib/commonjs/hooks/useSelectionGesture.js.map +1 -0
  53. package/lib/commonjs/hooks/useShapeGesture.js +181 -0
  54. package/lib/commonjs/hooks/useShapeGesture.js.map +1 -0
  55. package/lib/commonjs/hooks/useViewportGesture.js +238 -0
  56. package/lib/commonjs/hooks/useViewportGesture.js.map +1 -0
  57. package/lib/commonjs/index.js +104 -0
  58. package/lib/commonjs/index.js.map +1 -0
  59. package/lib/commonjs/package.json +1 -0
  60. package/lib/commonjs/renderers/ArrowRenderer.js +118 -0
  61. package/lib/commonjs/renderers/ArrowRenderer.js.map +1 -0
  62. package/lib/commonjs/renderers/CircleRenderer.js +51 -0
  63. package/lib/commonjs/renderers/CircleRenderer.js.map +1 -0
  64. package/lib/commonjs/renderers/FreehandRenderer.js +31 -0
  65. package/lib/commonjs/renderers/FreehandRenderer.js.map +1 -0
  66. package/lib/commonjs/renderers/InProgressRenderer.js +174 -0
  67. package/lib/commonjs/renderers/InProgressRenderer.js.map +1 -0
  68. package/lib/commonjs/renderers/LineRenderer.js +27 -0
  69. package/lib/commonjs/renderers/LineRenderer.js.map +1 -0
  70. package/lib/commonjs/renderers/MeasurementRenderer.js +134 -0
  71. package/lib/commonjs/renderers/MeasurementRenderer.js.map +1 -0
  72. package/lib/commonjs/renderers/ObjectRenderer.js +65 -0
  73. package/lib/commonjs/renderers/ObjectRenderer.js.map +1 -0
  74. package/lib/commonjs/renderers/PolygonRenderer.js +46 -0
  75. package/lib/commonjs/renderers/PolygonRenderer.js.map +1 -0
  76. package/lib/commonjs/renderers/RectRenderer.js +51 -0
  77. package/lib/commonjs/renderers/RectRenderer.js.map +1 -0
  78. package/lib/commonjs/renderers/SelectedObjectRenderer.js +592 -0
  79. package/lib/commonjs/renderers/SelectedObjectRenderer.js.map +1 -0
  80. package/lib/commonjs/renderers/SelectionOverlay.js +120 -0
  81. package/lib/commonjs/renderers/SelectionOverlay.js.map +1 -0
  82. package/lib/commonjs/store/useDrawingStore.js +354 -0
  83. package/lib/commonjs/store/useDrawingStore.js.map +1 -0
  84. package/lib/commonjs/types.js +6 -0
  85. package/lib/commonjs/types.js.map +1 -0
  86. package/lib/commonjs/utils/colors.js +44 -0
  87. package/lib/commonjs/utils/colors.js.map +1 -0
  88. package/lib/commonjs/utils/coordinates.js +81 -0
  89. package/lib/commonjs/utils/coordinates.js.map +1 -0
  90. package/lib/commonjs/utils/hitTesting.js +181 -0
  91. package/lib/commonjs/utils/hitTesting.js.map +1 -0
  92. package/lib/commonjs/utils/serialization.js +42 -0
  93. package/lib/commonjs/utils/serialization.js.map +1 -0
  94. package/lib/commonjs/utils/shapeDetection.js +151 -0
  95. package/lib/commonjs/utils/shapeDetection.js.map +1 -0
  96. package/lib/commonjs/utils/smoothing.js +85 -0
  97. package/lib/commonjs/utils/smoothing.js.map +1 -0
  98. package/lib/module/DrawingEditor.js +811 -0
  99. package/lib/module/DrawingEditor.js.map +1 -0
  100. package/lib/module/assets/toolbar-icons/arrow-disabled.png +0 -0
  101. package/lib/module/assets/toolbar-icons/arrow-enabled.png +0 -0
  102. package/lib/module/assets/toolbar-icons/arrow.png +0 -0
  103. package/lib/module/assets/toolbar-icons/circle-disabled.png +0 -0
  104. package/lib/module/assets/toolbar-icons/circle-enabled.png +0 -0
  105. package/lib/module/assets/toolbar-icons/circle.png +0 -0
  106. package/lib/module/assets/toolbar-icons/freehand-disabled.png +0 -0
  107. package/lib/module/assets/toolbar-icons/freehand-enabled.png +0 -0
  108. package/lib/module/assets/toolbar-icons/freehand.png +0 -0
  109. package/lib/module/assets/toolbar-icons/line-disabled.png +0 -0
  110. package/lib/module/assets/toolbar-icons/line-enabled.png +0 -0
  111. package/lib/module/assets/toolbar-icons/line.png +0 -0
  112. package/lib/module/assets/toolbar-icons/measure-disabled.png +0 -0
  113. package/lib/module/assets/toolbar-icons/measure-enabled.png +0 -0
  114. package/lib/module/assets/toolbar-icons/measure.png +0 -0
  115. package/lib/module/assets/toolbar-icons/move-disabled.png +0 -0
  116. package/lib/module/assets/toolbar-icons/move-enabled.png +0 -0
  117. package/lib/module/assets/toolbar-icons/move.png +0 -0
  118. package/lib/module/assets/toolbar-icons/polygon-disabled.png +0 -0
  119. package/lib/module/assets/toolbar-icons/polygon-enabled.png +0 -0
  120. package/lib/module/assets/toolbar-icons/polygon.png +0 -0
  121. package/lib/module/assets/toolbar-icons/rectangle-disabled.png +0 -0
  122. package/lib/module/assets/toolbar-icons/rectangle-enabled.png +0 -0
  123. package/lib/module/assets/toolbar-icons/rectangle.png +0 -0
  124. package/lib/module/assets/toolbar-icons/text-disabled.png +0 -0
  125. package/lib/module/assets/toolbar-icons/text-enabled.png +0 -0
  126. package/lib/module/assets/toolbar-icons/text.png +0 -0
  127. package/lib/module/components/ColorPalette.js +374 -0
  128. package/lib/module/components/ColorPalette.js.map +1 -0
  129. package/lib/module/components/LineWidthSlider.js +64 -0
  130. package/lib/module/components/LineWidthSlider.js.map +1 -0
  131. package/lib/module/components/MeasurementEditModal.js +148 -0
  132. package/lib/module/components/MeasurementEditModal.js.map +1 -0
  133. package/lib/module/components/MiniMap.js +239 -0
  134. package/lib/module/components/MiniMap.js.map +1 -0
  135. package/lib/module/components/TextAnnotation.js +157 -0
  136. package/lib/module/components/TextAnnotation.js.map +1 -0
  137. package/lib/module/components/TextEditModal.js +128 -0
  138. package/lib/module/components/TextEditModal.js.map +1 -0
  139. package/lib/module/components/Toolbar.js +193 -0
  140. package/lib/module/components/Toolbar.js.map +1 -0
  141. package/lib/module/components/ZoomBadge.js +155 -0
  142. package/lib/module/components/ZoomBadge.js.map +1 -0
  143. package/lib/module/hooks/useFreehandGesture.js +169 -0
  144. package/lib/module/hooks/useFreehandGesture.js.map +1 -0
  145. package/lib/module/hooks/usePolygonGesture.js +106 -0
  146. package/lib/module/hooks/usePolygonGesture.js.map +1 -0
  147. package/lib/module/hooks/useSelectionGesture.js +232 -0
  148. package/lib/module/hooks/useSelectionGesture.js.map +1 -0
  149. package/lib/module/hooks/useShapeGesture.js +177 -0
  150. package/lib/module/hooks/useShapeGesture.js.map +1 -0
  151. package/lib/module/hooks/useViewportGesture.js +234 -0
  152. package/lib/module/hooks/useViewportGesture.js.map +1 -0
  153. package/lib/module/index.js +20 -0
  154. package/lib/module/index.js.map +1 -0
  155. package/lib/module/package.json +1 -0
  156. package/lib/module/renderers/ArrowRenderer.js +113 -0
  157. package/lib/module/renderers/ArrowRenderer.js.map +1 -0
  158. package/lib/module/renderers/CircleRenderer.js +46 -0
  159. package/lib/module/renderers/CircleRenderer.js.map +1 -0
  160. package/lib/module/renderers/FreehandRenderer.js +26 -0
  161. package/lib/module/renderers/FreehandRenderer.js.map +1 -0
  162. package/lib/module/renderers/InProgressRenderer.js +169 -0
  163. package/lib/module/renderers/InProgressRenderer.js.map +1 -0
  164. package/lib/module/renderers/LineRenderer.js +22 -0
  165. package/lib/module/renderers/LineRenderer.js.map +1 -0
  166. package/lib/module/renderers/MeasurementRenderer.js +129 -0
  167. package/lib/module/renderers/MeasurementRenderer.js.map +1 -0
  168. package/lib/module/renderers/ObjectRenderer.js +60 -0
  169. package/lib/module/renderers/ObjectRenderer.js.map +1 -0
  170. package/lib/module/renderers/PolygonRenderer.js +41 -0
  171. package/lib/module/renderers/PolygonRenderer.js.map +1 -0
  172. package/lib/module/renderers/RectRenderer.js +46 -0
  173. package/lib/module/renderers/RectRenderer.js.map +1 -0
  174. package/lib/module/renderers/SelectedObjectRenderer.js +587 -0
  175. package/lib/module/renderers/SelectedObjectRenderer.js.map +1 -0
  176. package/lib/module/renderers/SelectionOverlay.js +116 -0
  177. package/lib/module/renderers/SelectionOverlay.js.map +1 -0
  178. package/lib/module/store/useDrawingStore.js +350 -0
  179. package/lib/module/store/useDrawingStore.js.map +1 -0
  180. package/lib/module/types.js +4 -0
  181. package/lib/module/types.js.map +1 -0
  182. package/lib/module/utils/colors.js +40 -0
  183. package/lib/module/utils/colors.js.map +1 -0
  184. package/lib/module/utils/coordinates.js +71 -0
  185. package/lib/module/utils/coordinates.js.map +1 -0
  186. package/lib/module/utils/hitTesting.js +171 -0
  187. package/lib/module/utils/hitTesting.js.map +1 -0
  188. package/lib/module/utils/serialization.js +36 -0
  189. package/lib/module/utils/serialization.js.map +1 -0
  190. package/lib/module/utils/shapeDetection.js +147 -0
  191. package/lib/module/utils/shapeDetection.js.map +1 -0
  192. package/lib/module/utils/smoothing.js +80 -0
  193. package/lib/module/utils/smoothing.js.map +1 -0
  194. package/lib/typescript/DrawingEditor.d.ts +3 -0
  195. package/lib/typescript/DrawingEditor.d.ts.map +1 -0
  196. package/lib/typescript/components/ColorPalette.d.ts +9 -0
  197. package/lib/typescript/components/ColorPalette.d.ts.map +1 -0
  198. package/lib/typescript/components/LineWidthSlider.d.ts +11 -0
  199. package/lib/typescript/components/LineWidthSlider.d.ts.map +1 -0
  200. package/lib/typescript/components/MeasurementEditModal.d.ts +11 -0
  201. package/lib/typescript/components/MeasurementEditModal.d.ts.map +1 -0
  202. package/lib/typescript/components/MiniMap.d.ts +23 -0
  203. package/lib/typescript/components/MiniMap.d.ts.map +1 -0
  204. package/lib/typescript/components/TextAnnotation.d.ts +22 -0
  205. package/lib/typescript/components/TextAnnotation.d.ts.map +1 -0
  206. package/lib/typescript/components/TextEditModal.d.ts +10 -0
  207. package/lib/typescript/components/TextEditModal.d.ts.map +1 -0
  208. package/lib/typescript/components/Toolbar.d.ts +13 -0
  209. package/lib/typescript/components/Toolbar.d.ts.map +1 -0
  210. package/lib/typescript/components/ZoomBadge.d.ts +9 -0
  211. package/lib/typescript/components/ZoomBadge.d.ts.map +1 -0
  212. package/lib/typescript/hooks/useFreehandGesture.d.ts +47 -0
  213. package/lib/typescript/hooks/useFreehandGesture.d.ts.map +1 -0
  214. package/lib/typescript/hooks/usePolygonGesture.d.ts +47 -0
  215. package/lib/typescript/hooks/usePolygonGesture.d.ts.map +1 -0
  216. package/lib/typescript/hooks/useSelectionGesture.d.ts +32 -0
  217. package/lib/typescript/hooks/useSelectionGesture.d.ts.map +1 -0
  218. package/lib/typescript/hooks/useShapeGesture.d.ts +54 -0
  219. package/lib/typescript/hooks/useShapeGesture.d.ts.map +1 -0
  220. package/lib/typescript/hooks/useViewportGesture.d.ts +37 -0
  221. package/lib/typescript/hooks/useViewportGesture.d.ts.map +1 -0
  222. package/lib/typescript/index.d.ts +11 -0
  223. package/lib/typescript/index.d.ts.map +1 -0
  224. package/lib/typescript/renderers/ArrowRenderer.d.ts +9 -0
  225. package/lib/typescript/renderers/ArrowRenderer.d.ts.map +1 -0
  226. package/lib/typescript/renderers/CircleRenderer.d.ts +9 -0
  227. package/lib/typescript/renderers/CircleRenderer.d.ts.map +1 -0
  228. package/lib/typescript/renderers/FreehandRenderer.d.ts +9 -0
  229. package/lib/typescript/renderers/FreehandRenderer.d.ts.map +1 -0
  230. package/lib/typescript/renderers/InProgressRenderer.d.ts +32 -0
  231. package/lib/typescript/renderers/InProgressRenderer.d.ts.map +1 -0
  232. package/lib/typescript/renderers/LineRenderer.d.ts +9 -0
  233. package/lib/typescript/renderers/LineRenderer.d.ts.map +1 -0
  234. package/lib/typescript/renderers/MeasurementRenderer.d.ts +9 -0
  235. package/lib/typescript/renderers/MeasurementRenderer.d.ts.map +1 -0
  236. package/lib/typescript/renderers/ObjectRenderer.d.ts +12 -0
  237. package/lib/typescript/renderers/ObjectRenderer.d.ts.map +1 -0
  238. package/lib/typescript/renderers/PolygonRenderer.d.ts +13 -0
  239. package/lib/typescript/renderers/PolygonRenderer.d.ts.map +1 -0
  240. package/lib/typescript/renderers/RectRenderer.d.ts +9 -0
  241. package/lib/typescript/renderers/RectRenderer.d.ts.map +1 -0
  242. package/lib/typescript/renderers/SelectedObjectRenderer.d.ts +18 -0
  243. package/lib/typescript/renderers/SelectedObjectRenderer.d.ts.map +1 -0
  244. package/lib/typescript/renderers/SelectionOverlay.d.ts +21 -0
  245. package/lib/typescript/renderers/SelectionOverlay.d.ts.map +1 -0
  246. package/lib/typescript/store/useDrawingStore.d.ts +30 -0
  247. package/lib/typescript/store/useDrawingStore.d.ts.map +1 -0
  248. package/lib/typescript/types.d.ts +130 -0
  249. package/lib/typescript/types.d.ts.map +1 -0
  250. package/lib/typescript/utils/colors.d.ts +11 -0
  251. package/lib/typescript/utils/colors.d.ts.map +1 -0
  252. package/lib/typescript/utils/coordinates.d.ts +34 -0
  253. package/lib/typescript/utils/coordinates.d.ts.map +1 -0
  254. package/lib/typescript/utils/hitTesting.d.ts +18 -0
  255. package/lib/typescript/utils/hitTesting.d.ts.map +1 -0
  256. package/lib/typescript/utils/serialization.d.ts +17 -0
  257. package/lib/typescript/utils/serialization.d.ts.map +1 -0
  258. package/lib/typescript/utils/shapeDetection.d.ts +22 -0
  259. package/lib/typescript/utils/shapeDetection.d.ts.map +1 -0
  260. package/lib/typescript/utils/smoothing.d.ts +16 -0
  261. package/lib/typescript/utils/smoothing.d.ts.map +1 -0
  262. package/package.json +108 -0
  263. package/src/DrawingEditor.tsx +1071 -0
  264. package/src/assets/toolbar-icons/arrow-disabled.png +0 -0
  265. package/src/assets/toolbar-icons/arrow-enabled.png +0 -0
  266. package/src/assets/toolbar-icons/arrow.png +0 -0
  267. package/src/assets/toolbar-icons/circle-disabled.png +0 -0
  268. package/src/assets/toolbar-icons/circle-enabled.png +0 -0
  269. package/src/assets/toolbar-icons/circle.png +0 -0
  270. package/src/assets/toolbar-icons/freehand-disabled.png +0 -0
  271. package/src/assets/toolbar-icons/freehand-enabled.png +0 -0
  272. package/src/assets/toolbar-icons/freehand.png +0 -0
  273. package/src/assets/toolbar-icons/line-disabled.png +0 -0
  274. package/src/assets/toolbar-icons/line-enabled.png +0 -0
  275. package/src/assets/toolbar-icons/line.png +0 -0
  276. package/src/assets/toolbar-icons/measure-disabled.png +0 -0
  277. package/src/assets/toolbar-icons/measure-enabled.png +0 -0
  278. package/src/assets/toolbar-icons/measure.png +0 -0
  279. package/src/assets/toolbar-icons/move-disabled.png +0 -0
  280. package/src/assets/toolbar-icons/move-enabled.png +0 -0
  281. package/src/assets/toolbar-icons/move.png +0 -0
  282. package/src/assets/toolbar-icons/polygon-disabled.png +0 -0
  283. package/src/assets/toolbar-icons/polygon-enabled.png +0 -0
  284. package/src/assets/toolbar-icons/polygon.png +0 -0
  285. package/src/assets/toolbar-icons/rectangle-disabled.png +0 -0
  286. package/src/assets/toolbar-icons/rectangle-enabled.png +0 -0
  287. package/src/assets/toolbar-icons/rectangle.png +0 -0
  288. package/src/assets/toolbar-icons/text-disabled.png +0 -0
  289. package/src/assets/toolbar-icons/text-enabled.png +0 -0
  290. package/src/assets/toolbar-icons/text.png +0 -0
  291. package/src/components/ColorPalette.tsx +497 -0
  292. package/src/components/LineWidthSlider.tsx +87 -0
  293. package/src/components/MeasurementEditModal.tsx +163 -0
  294. package/src/components/MiniMap.tsx +275 -0
  295. package/src/components/TextAnnotation.tsx +198 -0
  296. package/src/components/TextEditModal.tsx +139 -0
  297. package/src/components/Toolbar.tsx +254 -0
  298. package/src/components/ZoomBadge.tsx +166 -0
  299. package/src/hooks/useFreehandGesture.ts +249 -0
  300. package/src/hooks/usePolygonGesture.ts +162 -0
  301. package/src/hooks/useSelectionGesture.ts +293 -0
  302. package/src/hooks/useShapeGesture.ts +256 -0
  303. package/src/hooks/useViewportGesture.ts +337 -0
  304. package/src/index.tsx +51 -0
  305. package/src/renderers/ArrowRenderer.tsx +123 -0
  306. package/src/renderers/CircleRenderer.tsx +60 -0
  307. package/src/renderers/FreehandRenderer.tsx +33 -0
  308. package/src/renderers/InProgressRenderer.tsx +217 -0
  309. package/src/renderers/LineRenderer.tsx +34 -0
  310. package/src/renderers/MeasurementRenderer.tsx +179 -0
  311. package/src/renderers/ObjectRenderer.tsx +42 -0
  312. package/src/renderers/PolygonRenderer.tsx +66 -0
  313. package/src/renderers/RectRenderer.tsx +60 -0
  314. package/src/renderers/SelectedObjectRenderer.tsx +738 -0
  315. package/src/renderers/SelectionOverlay.tsx +170 -0
  316. package/src/store/useDrawingStore.ts +357 -0
  317. package/src/types.ts +186 -0
  318. package/src/utils/colors.ts +98 -0
  319. package/src/utils/coordinates.ts +75 -0
  320. package/src/utils/hitTesting.ts +242 -0
  321. package/src/utils/serialization.ts +45 -0
  322. package/src/utils/shapeDetection.ts +160 -0
  323. package/src/utils/smoothing.ts +84 -0
@@ -0,0 +1,811 @@
1
+ "use strict";
2
+
3
+ import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, forwardRef } from "react";
4
+ import { View, StyleSheet, Platform, useWindowDimensions } from "react-native";
5
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
6
+ import { Canvas, Image, useImage, useCanvasRef, Fill, Group, rect } from "@shopify/react-native-skia";
7
+ import { GestureHandlerRootView, GestureDetector, Gesture } from "react-native-gesture-handler";
8
+ import { useSharedValue, useDerivedValue } from "react-native-reanimated";
9
+ import { useDrawingStore } from "./store/useDrawingStore";
10
+ import { useFreehandGesture } from "./hooks/useFreehandGesture";
11
+ import { useShapeGesture } from "./hooks/useShapeGesture";
12
+ import { usePolygonGesture } from "./hooks/usePolygonGesture";
13
+ import { useSelectionGesture } from "./hooks/useSelectionGesture";
14
+ import { useViewportGesture } from "./hooks/useViewportGesture";
15
+ import { ObjectRenderer } from "./renderers/ObjectRenderer";
16
+ import { SelectionOverlay } from "./renderers/SelectionOverlay";
17
+ import { SelectedObjectRenderer } from "./renderers/SelectedObjectRenderer";
18
+ import { FreehandInProgress, ShapeInProgress, PolygonInProgress } from "./renderers/InProgressRenderer";
19
+ import { Toolbar } from "./components/Toolbar";
20
+ import { ColorPalette } from "./components/ColorPalette";
21
+ import { LineWidthSlider } from "./components/LineWidthSlider";
22
+ import { TextAnnotation } from "./components/TextAnnotation";
23
+ import { TextEditModal } from "./components/TextEditModal";
24
+ import { MeasurementEditModal } from "./components/MeasurementEditModal";
25
+ import { ZoomBadge } from "./components/ZoomBadge";
26
+ import { MiniMap } from "./components/MiniMap";
27
+ import { normalize } from "./utils/coordinates";
28
+ import { generateId } from "./utils/serialization";
29
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
30
+ const TWO_POINT_TOOLS = ["line", "arrow", "rectangle", "circle", "measure"];
31
+ const WEB_CURSOR_STYLES = {
32
+ default: {
33
+ cursor: "default"
34
+ },
35
+ crosshair: {
36
+ cursor: "crosshair"
37
+ },
38
+ grab: {
39
+ cursor: "grab"
40
+ },
41
+ grabbing: {
42
+ cursor: "grabbing"
43
+ },
44
+ text: {
45
+ cursor: "text"
46
+ }
47
+ };
48
+ export const DrawingEditor = /*#__PURE__*/forwardRef(function DrawingEditor({
49
+ imageSource,
50
+ initialObjects,
51
+ maxZoom,
52
+ onSave: _onSave,
53
+ onDismiss: _onDismiss,
54
+ style,
55
+ renderToolbar,
56
+ renderColorPalette
57
+ }, ref) {
58
+ // ─── Phone vs tablet detection ───────────────────────────────────────────
59
+ const {
60
+ width: winWidth,
61
+ height: winHeight
62
+ } = useWindowDimensions();
63
+ const isPhone = Math.min(winWidth, winHeight) < 768;
64
+ const insets = useSafeAreaInsets();
65
+
66
+ // ─── Store ───────────────────────────────────────────────────────────────
67
+ const currentTool = useDrawingStore(s => s.currentTool);
68
+ const strokeColor = useDrawingStore(s => s.strokeColor);
69
+ const fillColor = useDrawingStore(s => s.fillColor);
70
+ const fillAlpha = useDrawingStore(s => s.fillAlpha);
71
+ const lineWidth = useDrawingStore(s => s.lineWidth);
72
+ const objects = useDrawingStore(s => s.objects);
73
+ const selectedObjectId = useDrawingStore(s => s.selectedObjectId);
74
+ const canvasSize = useDrawingStore(s => s.canvasSize);
75
+ const undoStack = useDrawingStore(s => s.undoStack);
76
+ const setTool = useDrawingStore(s => s.setTool);
77
+ const setStrokeColor = useDrawingStore(s => s.setStrokeColor);
78
+ const setLineWidth = useDrawingStore(s => s.setLineWidth);
79
+ const setCanvasSize = useDrawingStore(s => s.setCanvasSize);
80
+ const addObject = useDrawingStore(s => s.addObject);
81
+ const updateObject = useDrawingStore(s => s.updateObject);
82
+ const deleteObject = useDrawingStore(s => s.deleteObject);
83
+ const selectObject = useDrawingStore(s => s.selectObject);
84
+ const moveObject = useDrawingStore(s => s.moveObject);
85
+ const undo = useDrawingStore(s => s.undo);
86
+ const clearAll = useDrawingStore(s => s.clearAll);
87
+ const loadObjects = useDrawingStore(s => s.loadObjects);
88
+
89
+ // ─── Image ───────────────────────────────────────────────────────────────
90
+ const skiaImage = useImage(typeof imageSource === "string" ? imageSource : imageSource, err => console.warn("[DrawingEditor] Failed to load image:", err.message));
91
+ const canvasRef = useCanvasRef();
92
+
93
+ // ─── Layout ──────────────────────────────────────────────────────────────
94
+ const [layoutSize, setLayoutSize] = useState({
95
+ width: 0,
96
+ height: 0
97
+ });
98
+ const [isPointerDown, setIsPointerDown] = useState(false);
99
+ const wheelTargetRef = useRef(null);
100
+ const setWheelTargetRef = useCallback(node => {
101
+ wheelTargetRef.current = node;
102
+ }, []);
103
+ const handleLayout = useCallback(event => {
104
+ const {
105
+ width,
106
+ height
107
+ } = event.nativeEvent.layout;
108
+ setLayoutSize({
109
+ width,
110
+ height
111
+ });
112
+ }, []);
113
+
114
+ // Reset store when component mounts (new image selected)
115
+ useEffect(() => {
116
+ clearAll();
117
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
118
+
119
+ // Load initial objects once canvas is sized
120
+ useEffect(() => {
121
+ if (initialObjects && canvasSize.width > 0) {
122
+ loadObjects(initialObjects);
123
+ }
124
+ }, [canvasSize.width > 0]); // eslint-disable-line react-hooks/exhaustive-deps
125
+
126
+ // ─── Shared values synced with store ─────────────────────────────────────
127
+ const colorSV = useSharedValue(strokeColor);
128
+ const lineWidthSV = useSharedValue(lineWidth);
129
+ const fillColorSV = useSharedValue(fillColor);
130
+ const fillAlphaSV = useSharedValue(fillAlpha);
131
+ const toolSV = useSharedValue(currentTool);
132
+ useEffect(() => {
133
+ colorSV.value = strokeColor;
134
+ }, [strokeColor]);
135
+ useEffect(() => {
136
+ lineWidthSV.value = lineWidth;
137
+ }, [lineWidth]);
138
+ useEffect(() => {
139
+ fillColorSV.value = fillColor;
140
+ }, [fillColor]);
141
+ useEffect(() => {
142
+ fillAlphaSV.value = fillAlpha;
143
+ }, [fillAlpha]);
144
+ useEffect(() => {
145
+ toolSV.value = currentTool;
146
+ }, [currentTool]);
147
+
148
+ // ─── Determine active tool shape type ───────────────────────────────────
149
+ const isFreehand = currentTool === "freehand";
150
+ const isPolygon = currentTool === "polygon";
151
+ const isTwoPointShape = TWO_POINT_TOOLS.includes(currentTool);
152
+ const isTextMode = currentTool === "text";
153
+
154
+ // ─── Image fit calculation ───────────────────────────────────────────────
155
+ const imageRect = useMemo(() => {
156
+ if (!skiaImage || layoutSize.width === 0 || layoutSize.height === 0) {
157
+ return {
158
+ x: 0,
159
+ y: 0,
160
+ width: layoutSize.width,
161
+ height: layoutSize.height
162
+ };
163
+ }
164
+ const imgW = skiaImage.width();
165
+ const imgH = skiaImage.height();
166
+ const scaleX = layoutSize.width / imgW;
167
+ const scaleY = layoutSize.height / imgH;
168
+ const scale = Math.min(scaleX, scaleY);
169
+ const w = imgW * scale;
170
+ const h = imgH * scale;
171
+ return {
172
+ x: (layoutSize.width - w) / 2,
173
+ y: (layoutSize.height - h) / 2,
174
+ width: w,
175
+ height: h
176
+ };
177
+ }, [skiaImage, layoutSize]);
178
+
179
+ // ─── Effective canvas size = image rect ─────────────────────────────────
180
+ const effectiveCanvasSize = useMemo(() => ({
181
+ width: imageRect.width,
182
+ height: imageRect.height
183
+ }), [imageRect.width, imageRect.height]);
184
+
185
+ // Sync to store for external consumers
186
+ useEffect(() => {
187
+ if (imageRect.width > 0 && imageRect.height > 0) {
188
+ setCanvasSize({
189
+ width: imageRect.width,
190
+ height: imageRect.height
191
+ });
192
+ }
193
+ }, [imageRect.width, imageRect.height, setCanvasSize]);
194
+
195
+ // ─── Image bounds shared values (for worklet access) ─────────────────────
196
+ const imageOffsetXSV = useSharedValue(0);
197
+ const imageOffsetYSV = useSharedValue(0);
198
+ const imageWidthSV = useSharedValue(0);
199
+ const imageHeightSV = useSharedValue(0);
200
+ useEffect(() => {
201
+ imageOffsetXSV.value = imageRect.x;
202
+ imageOffsetYSV.value = imageRect.y;
203
+ imageWidthSV.value = imageRect.width;
204
+ imageHeightSV.value = imageRect.height;
205
+ }, [imageRect]);
206
+
207
+ // ─── Viewport (zoom/pan) ─────────────────────────────────────────────────
208
+ const [isZoomed, setIsZoomed] = useState(false);
209
+ const layoutWidthSV = useSharedValue(0);
210
+ const layoutHeightSV = useSharedValue(0);
211
+ useEffect(() => {
212
+ layoutWidthSV.value = layoutSize.width;
213
+ layoutHeightSV.value = layoutSize.height;
214
+ }, [layoutSize]);
215
+ const handlePinchEnd = useCallback(() => {
216
+ setTool(null);
217
+ }, [setTool]);
218
+ const viewport = useViewportGesture({
219
+ maxZoom,
220
+ layoutWidth: layoutWidthSV,
221
+ layoutHeight: layoutHeightSV,
222
+ imageOffsetX: imageOffsetXSV,
223
+ imageOffsetY: imageOffsetYSV,
224
+ imageWidth: imageWidthSV,
225
+ imageHeight: imageHeightSV,
226
+ onPinchEnd: handlePinchEnd
227
+ });
228
+
229
+ // Track isZoomed state for UI.
230
+ // Only tracks zoom level — tool deactivation is handled by onPinchEnd.
231
+ useEffect(() => {
232
+ const id = setInterval(() => {
233
+ const zoomed = viewport.scale.value > 1.01;
234
+ setIsZoomed(prev => {
235
+ if (prev !== zoomed) return zoomed;
236
+ return prev;
237
+ });
238
+ }, 200);
239
+ return () => clearInterval(id);
240
+ }, [viewport.scale, viewport.isPinching]);
241
+
242
+ // ─── Measurement editing modal ───────────────────────────────────────────
243
+ const [measureEditVisible, setMeasureEditVisible] = useState(false);
244
+ const [measureEditTarget, setMeasureEditTarget] = useState(null);
245
+ const lastUsedUnit = useRef("cm");
246
+ const handleMeasureCreated = useCallback(id => {
247
+ setMeasureEditTarget(id);
248
+ setMeasureEditVisible(true);
249
+ }, []);
250
+ const handleMeasureSubmit = useCallback((value, unit) => {
251
+ if (measureEditTarget) {
252
+ updateObject(measureEditTarget, {
253
+ text: value,
254
+ unit
255
+ });
256
+ }
257
+ lastUsedUnit.current = unit;
258
+ setMeasureEditVisible(false);
259
+ setMeasureEditTarget(null);
260
+ }, [measureEditTarget, updateObject]);
261
+ const handleMeasureCancel = useCallback(() => {
262
+ if (measureEditTarget) {
263
+ deleteObject(measureEditTarget);
264
+ }
265
+ setMeasureEditVisible(false);
266
+ setMeasureEditTarget(null);
267
+ }, [measureEditTarget, deleteObject]);
268
+
269
+ // ─── Gesture: Freehand ───────────────────────────────────────────────────
270
+ const freehand = useFreehandGesture({
271
+ colorSV,
272
+ lineWidthSV,
273
+ fillColorSV,
274
+ fillAlphaSV,
275
+ canvasSize: effectiveCanvasSize,
276
+ enabled: isFreehand && !selectedObjectId,
277
+ imageOffsetX: imageOffsetXSV,
278
+ imageOffsetY: imageOffsetYSV,
279
+ imageWidth: imageWidthSV,
280
+ imageHeight: imageHeightSV,
281
+ viewScale: viewport.scale,
282
+ viewTranslateX: viewport.translateX,
283
+ viewTranslateY: viewport.translateY
284
+ });
285
+
286
+ // ─── Gesture: Shape ──────────────────────────────────────────────────────
287
+ const shape = useShapeGesture({
288
+ shapeType: isTwoPointShape ? currentTool : "line",
289
+ colorSV,
290
+ lineWidthSV,
291
+ fillColorSV,
292
+ fillAlphaSV,
293
+ canvasSize: effectiveCanvasSize,
294
+ enabled: isTwoPointShape && !selectedObjectId,
295
+ imageOffsetX: imageOffsetXSV,
296
+ imageOffsetY: imageOffsetYSV,
297
+ imageWidth: imageWidthSV,
298
+ imageHeight: imageHeightSV,
299
+ viewScale: viewport.scale,
300
+ viewTranslateX: viewport.translateX,
301
+ viewTranslateY: viewport.translateY,
302
+ onMeasureCreated: handleMeasureCreated
303
+ });
304
+
305
+ // ─── Gesture: Polygon ────────────────────────────────────────────────────
306
+ const polygon = usePolygonGesture({
307
+ colorSV,
308
+ lineWidthSV,
309
+ fillColorSV,
310
+ fillAlphaSV,
311
+ canvasSize: effectiveCanvasSize,
312
+ enabled: isPolygon && !selectedObjectId,
313
+ imageOffsetX: imageOffsetXSV,
314
+ imageOffsetY: imageOffsetYSV,
315
+ imageWidth: imageWidthSV,
316
+ imageHeight: imageHeightSV,
317
+ viewScale: viewport.scale,
318
+ viewTranslateX: viewport.translateX,
319
+ viewTranslateY: viewport.translateY
320
+ });
321
+
322
+ // Cancel polygon construction when switching away from polygon tool
323
+ const prevToolRef = useRef(currentTool);
324
+ useEffect(() => {
325
+ if (prevToolRef.current === "polygon" && currentTool !== "polygon") {
326
+ polygon.cancel();
327
+ }
328
+ prevToolRef.current = currentTool;
329
+ }, [currentTool]); // eslint-disable-line react-hooks/exhaustive-deps
330
+
331
+ // ─── Gesture: Text placement (on empty space tap) ────────────────────────
332
+ const handleTextTap = useCallback((x, y) => {
333
+ const pos = normalize({
334
+ x,
335
+ y
336
+ }, effectiveCanvasSize);
337
+ const newText = {
338
+ id: generateId(),
339
+ type: "text",
340
+ position: pos,
341
+ width: 0.2,
342
+ // 20% of canvas
343
+ height: 0.08,
344
+ value: "Text",
345
+ color: strokeColor,
346
+ lineWidth
347
+ };
348
+ addObject(newText);
349
+ setTextEditTarget(newText.id);
350
+ setTextEditValue("Text");
351
+ setTextEditVisible(true);
352
+ }, [effectiveCanvasSize, strokeColor, lineWidth, addObject]);
353
+
354
+ // ─── Handle tap on empty space (deselect or place text) ──────────────────
355
+ const handleTapEmpty = useCallback((x, y) => {
356
+ if (isTextMode) {
357
+ handleTextTap(x, y);
358
+ } else {
359
+ selectObject(null);
360
+ }
361
+ }, [isTextMode, handleTextTap, selectObject]);
362
+
363
+ // ─── Gesture: Selection (always active) ──────────────────────────────────
364
+ const selection = useSelectionGesture({
365
+ canvasSize: effectiveCanvasSize,
366
+ imageOffsetX: imageOffsetXSV,
367
+ imageOffsetY: imageOffsetYSV,
368
+ viewScale: viewport.scale,
369
+ viewTranslateX: viewport.translateX,
370
+ viewTranslateY: viewport.translateY,
371
+ onTapEmpty: handleTapEmpty
372
+ });
373
+
374
+ // ─── Compose all gestures ────────────────────────────────────────────────
375
+ const composedGesture = useMemo(() => {
376
+ // Navigation gestures (pinch + two-finger pan) run simultaneously
377
+ const navigationGestures = Gesture.Simultaneous(viewport.pinchGesture, viewport.twoFingerPanGesture);
378
+
379
+ // Drawing/selection pan gestures — these must NOT be delayed by tap recognition
380
+ const noToolActive = currentTool === null;
381
+ const drawingPanGestures = selectedObjectId ? selection.panGesture : noToolActive && isZoomed ? viewport.singleFingerPanGesture : isFreehand ? freehand.gesture : isTwoPointShape ? shape.gesture : selection.panGesture;
382
+
383
+ // Tap gestures (double-tap and single-tap) — run in Exclusive so
384
+ // double-tap has priority over single-tap, but they don't block pan
385
+ const tapGestures = Gesture.Exclusive(viewport.doubleTapResetGesture,
386
+ // Polygon tap has priority over selection tap when polygon tool is active
387
+ ...(isPolygon ? [polygon.gesture] : [selection.doubleTapGesture, selection.tapGesture]));
388
+
389
+ // Pan and tap gestures run simultaneously — pan starts immediately
390
+ // without waiting for tap recognition timeout
391
+ const interactionGestures = Gesture.Simultaneous(drawingPanGestures, tapGestures);
392
+
393
+ // Navigation and interaction run simultaneously
394
+ return Gesture.Simultaneous(navigationGestures, interactionGestures);
395
+ }, [viewport.pinchGesture, viewport.twoFingerPanGesture, viewport.doubleTapResetGesture, viewport.singleFingerPanGesture, selection.doubleTapGesture, selection.panGesture, selection.tapGesture, polygon.gesture, freehand.gesture, shape.gesture, currentTool, selectedObjectId, isZoomed, isFreehand, isTwoPointShape, isPolygon]);
396
+
397
+ // ─── Text editing modal ──────────────────────────────────────────────────
398
+ const [textEditVisible, setTextEditVisible] = useState(false);
399
+ const [textEditTarget, setTextEditTarget] = useState(null);
400
+ const [textEditValue, setTextEditValue] = useState("");
401
+ const handleTextSubmit = useCallback(value => {
402
+ if (textEditTarget) {
403
+ updateObject(textEditTarget, {
404
+ value
405
+ });
406
+ }
407
+ setTextEditVisible(false);
408
+ setTextEditTarget(null);
409
+ }, [textEditTarget, updateObject]);
410
+ const handleTextDoubleTap = useCallback(id => {
411
+ const obj = objects.find(o => o.id === id);
412
+ if (obj?.type === "text") {
413
+ setTextEditTarget(id);
414
+ setTextEditValue(obj.value);
415
+ setTextEditVisible(true);
416
+ }
417
+ }, [objects]);
418
+
419
+ // ─── Computed values ─────────────────────────────────────────────────────
420
+ const selectedObject = useMemo(() => selectedObjectId ? objects.find(o => o.id === selectedObjectId) ?? null : null, [objects, selectedObjectId]);
421
+ const textObjects = useMemo(() => objects.filter(o => o.type === "text"), [objects]);
422
+ const nonTextObjects = useMemo(() => objects.filter(o => o.type !== "text"), [objects]);
423
+ const unselectedNonTextObjects = useMemo(() => nonTextObjects.filter(o => o.id !== selectedObjectId), [nonTextObjects, selectedObjectId]);
424
+
425
+ // ─── Imperative ref ──────────────────────────────────────────────────────
426
+ useImperativeHandle(ref, () => ({
427
+ undo,
428
+ setTool,
429
+ exportImage: async () => {
430
+ // Reset viewport to identity for a clean capture
431
+ const prevScale = viewport.scale.value;
432
+ const prevTx = viewport.translateX.value;
433
+ const prevTy = viewport.translateY.value;
434
+ viewport.scale.value = 1;
435
+ viewport.translateX.value = 0;
436
+ viewport.translateY.value = 0;
437
+
438
+ // Wait a frame for Skia to re-render
439
+ await new Promise(r => requestAnimationFrame(r));
440
+ const bounds = {
441
+ x: Math.round(imageRect.x),
442
+ y: Math.round(imageRect.y),
443
+ width: Math.round(imageRect.width),
444
+ height: Math.round(imageRect.height)
445
+ };
446
+ const image = canvasRef.current?.makeImageSnapshot(bounds);
447
+
448
+ // Restore viewport
449
+ viewport.scale.value = prevScale;
450
+ viewport.translateX.value = prevTx;
451
+ viewport.translateY.value = prevTy;
452
+ if (!image) return null;
453
+ return image.encodeToBytes();
454
+ },
455
+ exportImageBase64: async () => {
456
+ // Reset viewport to identity for a clean capture
457
+ const prevScale = viewport.scale.value;
458
+ const prevTx = viewport.translateX.value;
459
+ const prevTy = viewport.translateY.value;
460
+ viewport.scale.value = 1;
461
+ viewport.translateX.value = 0;
462
+ viewport.translateY.value = 0;
463
+ await new Promise(r => requestAnimationFrame(r));
464
+ const bounds = {
465
+ x: Math.round(imageRect.x),
466
+ y: Math.round(imageRect.y),
467
+ width: Math.round(imageRect.width),
468
+ height: Math.round(imageRect.height)
469
+ };
470
+ const image = canvasRef.current?.makeImageSnapshot(bounds);
471
+ viewport.scale.value = prevScale;
472
+ viewport.translateX.value = prevTx;
473
+ viewport.translateY.value = prevTy;
474
+ if (!image) return null;
475
+ return image.encodeToBase64();
476
+ },
477
+ getObjects: () => objects,
478
+ clearAll,
479
+ resetZoom: viewport.resetViewport
480
+ }), [undo, setTool, objects, clearAll, imageRect, viewport]);
481
+
482
+ // ─── Toolbar callbacks ───────────────────────────────────────────────────
483
+ const handleSelectTool = useCallback(tool => {
484
+ setTool(tool);
485
+ }, [setTool]);
486
+ const handleUndo = useCallback(() => {
487
+ undo();
488
+ }, [undo]);
489
+ const handleResetZoom = useCallback(() => {
490
+ viewport.resetViewport();
491
+ }, [viewport]);
492
+
493
+ // ─── Viewport transform for Skia (animated) ─────────────────────────────
494
+ const viewportTransform = useDerivedValue(() => [{
495
+ translateX: viewport.translateX.value
496
+ }, {
497
+ translateY: viewport.translateY.value
498
+ }, {
499
+ scale: viewport.scale.value
500
+ }]);
501
+ const usesMoveCursor = selectedObjectId !== null || currentTool === null;
502
+ const handlePointerDown = useCallback(() => {
503
+ if (Platform.OS === "web" && usesMoveCursor) {
504
+ setIsPointerDown(true);
505
+ }
506
+ }, [usesMoveCursor]);
507
+ const handlePointerRelease = useCallback(() => {
508
+ if (Platform.OS === "web") {
509
+ setIsPointerDown(false);
510
+ }
511
+ }, []);
512
+ const handleWheel = useCallback(event => {
513
+ const wheelEvent = event.nativeEvent ?? event;
514
+ if (!wheelEvent.ctrlKey || wheelEvent.deltaY === undefined) return;
515
+ event.preventDefault?.();
516
+ wheelEvent.preventDefault?.();
517
+ const bounds = event.currentTarget?.getBoundingClientRect?.();
518
+ if (!bounds) return;
519
+ const clientX = wheelEvent.clientX ?? event.clientX;
520
+ const clientY = wheelEvent.clientY ?? event.clientY;
521
+ if (clientX === undefined || clientY === undefined) return;
522
+ const localX = clientX - bounds.left;
523
+ const localY = clientY - bounds.top;
524
+ const scaleFactor = Math.min(1.25, Math.max(0.8, Math.exp(-wheelEvent.deltaY * 0.01)));
525
+ viewport.zoomAt(localX, localY, scaleFactor);
526
+ }, [viewport]);
527
+ useEffect(() => {
528
+ if (Platform.OS !== "web") return;
529
+ const target = wheelTargetRef.current;
530
+ if (!target?.addEventListener || !target.removeEventListener) return;
531
+ const options = {
532
+ passive: false
533
+ };
534
+ target.addEventListener("wheel", handleWheel, options);
535
+ return () => {
536
+ target.removeEventListener?.("wheel", handleWheel, options);
537
+ };
538
+ }, [handleWheel]);
539
+ const webPointerHandlers = Platform.OS === "web" ? {
540
+ onPointerDown: handlePointerDown,
541
+ onPointerUp: handlePointerRelease,
542
+ onPointerCancel: handlePointerRelease,
543
+ onPointerLeave: handlePointerRelease
544
+ } : undefined;
545
+ const canvasCursorStyle = useMemo(() => {
546
+ if (Platform.OS !== "web") return undefined;
547
+ if (usesMoveCursor && isPointerDown) {
548
+ return WEB_CURSOR_STYLES.grabbing;
549
+ }
550
+ if (usesMoveCursor) {
551
+ return WEB_CURSOR_STYLES.grab;
552
+ }
553
+ if (isTextMode) return WEB_CURSOR_STYLES.text;
554
+ if (currentTool) return WEB_CURSOR_STYLES.crosshair;
555
+ return WEB_CURSOR_STYLES.default;
556
+ }, [currentTool, isPointerDown, isTextMode, usesMoveCursor]);
557
+
558
+ // ─── Render ──────────────────────────────────────────────────────────────
559
+ return /*#__PURE__*/_jsxs(GestureHandlerRootView, {
560
+ style: [styles.root, style],
561
+ children: [/*#__PURE__*/_jsxs(View, {
562
+ style: [styles.editorContainer, isPhone ? styles.editorContainerPhone : styles.editorContainerTablet],
563
+ children: [!isPhone && /*#__PURE__*/_jsx(View, {
564
+ style: styles.sidebarLeft,
565
+ children: renderColorPalette ? renderColorPalette({
566
+ strokeColor,
567
+ fillColor,
568
+ fillAlpha,
569
+ onSelectStrokeColor: setStrokeColor,
570
+ onSelectFillColor: useDrawingStore.getState().setFillColor,
571
+ onSelectFillAlpha: useDrawingStore.getState().setFillAlpha
572
+ }) : /*#__PURE__*/_jsx(ColorPalette, {
573
+ strokeColor: strokeColor,
574
+ onSelectColor: setStrokeColor,
575
+ direction: "vertical"
576
+ })
577
+ }), /*#__PURE__*/_jsxs(View, {
578
+ style: styles.canvasContainer,
579
+ onLayout: handleLayout,
580
+ children: [/*#__PURE__*/_jsx(GestureDetector, {
581
+ gesture: composedGesture,
582
+ children: /*#__PURE__*/_jsxs(View, {
583
+ ...webPointerHandlers,
584
+ ref: setWheelTargetRef,
585
+ style: [StyleSheet.absoluteFill, canvasCursorStyle],
586
+ children: [/*#__PURE__*/_jsxs(Canvas
587
+ // On web, the Skia <canvas> element must not capture pointer events.
588
+ // RNGH attaches its listeners to the parent View and calls
589
+ // setPointerCapture on event.target; if the <canvas> is the topmost
590
+ // hit target it grabs the pointer and starves the gesture of the move
591
+ // stream (only stray points get through).
592
+ // The Skia web view drops the `pointerEvents` prop, so we must deliver
593
+ // `pointer-events: none` through `style` instead — it is inherited by
594
+ // the inner <canvas>, making it click-through.
595
+ , {
596
+ style: Platform.OS === "web" ? {
597
+ ...StyleSheet.absoluteFillObject,
598
+ pointerEvents: "none"
599
+ } : StyleSheet.absoluteFill,
600
+ ref: canvasRef,
601
+ children: [/*#__PURE__*/_jsx(Fill, {
602
+ color: "black"
603
+ }), /*#__PURE__*/_jsxs(Group, {
604
+ transform: viewportTransform,
605
+ children: [skiaImage && /*#__PURE__*/_jsx(Image, {
606
+ image: skiaImage,
607
+ x: imageRect.x,
608
+ y: imageRect.y,
609
+ width: imageRect.width,
610
+ height: imageRect.height,
611
+ fit: "contain"
612
+ }), /*#__PURE__*/_jsxs(Group, {
613
+ transform: [{
614
+ translateX: imageRect.x
615
+ }, {
616
+ translateY: imageRect.y
617
+ }],
618
+ clip: rect(0, 0, imageRect.width, imageRect.height),
619
+ children: [unselectedNonTextObjects.map(obj => /*#__PURE__*/_jsx(ObjectRenderer, {
620
+ object: obj,
621
+ canvasSize: effectiveCanvasSize
622
+ }, obj.id)), selectedObject && selectedObject.type !== "text" && /*#__PURE__*/_jsxs(_Fragment, {
623
+ children: [/*#__PURE__*/_jsx(SelectedObjectRenderer, {
624
+ object: selectedObject,
625
+ canvasSize: effectiveCanvasSize,
626
+ isDragging: selection.isDragging,
627
+ draggingAnchorIndex: selection.draggingAnchorIndex,
628
+ dragOffsetX: selection.dragOffsetX,
629
+ dragOffsetY: selection.dragOffsetY
630
+ }), /*#__PURE__*/_jsx(SelectionOverlay, {
631
+ object: selectedObject,
632
+ canvasSize: effectiveCanvasSize,
633
+ draggingAnchorIndex: selection.draggingAnchorIndex,
634
+ dragOffsetX: selection.dragOffsetX,
635
+ dragOffsetY: selection.dragOffsetY,
636
+ isDragging: selection.isDragging
637
+ })]
638
+ }), isFreehand && /*#__PURE__*/_jsx(FreehandInProgress, {
639
+ path: freehand.inProgressPath,
640
+ color: freehand.inProgressColor,
641
+ lineWidth: freehand.inProgressWidth,
642
+ isDrawing: freehand.isDrawing
643
+ }), isTwoPointShape && /*#__PURE__*/_jsx(ShapeInProgress, {
644
+ shapeType: currentTool,
645
+ startX: shape.startX,
646
+ startY: shape.startY,
647
+ currentX: shape.currentX,
648
+ currentY: shape.currentY,
649
+ color: shape.inProgressColor,
650
+ lineWidth: shape.inProgressWidth,
651
+ fillColor: shape.inProgressFillColor,
652
+ fillAlpha: shape.inProgressFillAlpha,
653
+ isDrawing: shape.isDrawing
654
+ }), isPolygon && /*#__PURE__*/_jsx(PolygonInProgress, {
655
+ pointsFlat: polygon.pointsFlat,
656
+ pointCount: polygon.pointCount,
657
+ isActive: polygon.isActive
658
+ })]
659
+ })]
660
+ })]
661
+ }), textObjects.map(textObj => /*#__PURE__*/_jsx(TextAnnotation, {
662
+ object: textObj,
663
+ canvasSize: effectiveCanvasSize,
664
+ imageOffset: {
665
+ x: imageRect.x,
666
+ y: imageRect.y
667
+ },
668
+ isSelected: selectedObjectId === textObj.id,
669
+ onSelect: selectObject,
670
+ onMove: moveObject,
671
+ onDoubleTap: handleTextDoubleTap,
672
+ onDelete: deleteObject,
673
+ viewScale: viewport.scale,
674
+ viewTranslateX: viewport.translateX,
675
+ viewTranslateY: viewport.translateY
676
+ }, textObj.id))]
677
+ })
678
+ }), /*#__PURE__*/_jsx(ZoomBadge, {
679
+ scale: viewport.scale,
680
+ onPress: handleResetZoom
681
+ }), /*#__PURE__*/_jsx(MiniMap, {
682
+ image: skiaImage,
683
+ imageRect: imageRect,
684
+ scale: viewport.scale,
685
+ translateX: viewport.translateX,
686
+ translateY: viewport.translateY,
687
+ visible: isZoomed,
688
+ onPanViewport: viewport.panViewport
689
+ })]
690
+ }), !isPhone && /*#__PURE__*/_jsx(View, {
691
+ style: styles.sidebarRight,
692
+ children: /*#__PURE__*/_jsx(LineWidthSlider, {
693
+ value: lineWidth,
694
+ onValueChange: setLineWidth,
695
+ orientation: "vertical"
696
+ })
697
+ })]
698
+ }), isPhone ? /*#__PURE__*/_jsxs(View, {
699
+ style: [styles.phoneBottomArea, {
700
+ paddingBottom: insets.bottom
701
+ }],
702
+ children: [/*#__PURE__*/_jsxs(View, {
703
+ style: styles.phoneControlsBar,
704
+ children: [renderColorPalette ? renderColorPalette({
705
+ strokeColor,
706
+ fillColor,
707
+ fillAlpha,
708
+ onSelectStrokeColor: setStrokeColor,
709
+ onSelectFillColor: useDrawingStore.getState().setFillColor,
710
+ onSelectFillAlpha: useDrawingStore.getState().setFillAlpha
711
+ }) : /*#__PURE__*/_jsx(ColorPalette, {
712
+ strokeColor: strokeColor,
713
+ onSelectColor: setStrokeColor,
714
+ direction: "horizontal"
715
+ }), /*#__PURE__*/_jsx(LineWidthSlider, {
716
+ value: lineWidth,
717
+ onValueChange: setLineWidth,
718
+ orientation: "horizontal"
719
+ })]
720
+ }), renderToolbar ? renderToolbar({
721
+ currentTool,
722
+ onSelectTool: handleSelectTool,
723
+ onUndo: handleUndo,
724
+ canUndo: undoStack.length > 0,
725
+ isZoomed,
726
+ onResetZoom: handleResetZoom
727
+ }) : /*#__PURE__*/_jsx(Toolbar, {
728
+ currentTool: currentTool,
729
+ onSelectTool: handleSelectTool,
730
+ onUndo: handleUndo,
731
+ canUndo: undoStack.length > 0,
732
+ isZoomed: isZoomed,
733
+ onResetZoom: handleResetZoom
734
+ })]
735
+ }) : /*#__PURE__*/_jsx(View, {
736
+ pointerEvents: "box-none",
737
+ style: styles.toolbarOverlay,
738
+ children: renderToolbar ? renderToolbar({
739
+ currentTool,
740
+ onSelectTool: handleSelectTool,
741
+ onUndo: handleUndo,
742
+ canUndo: undoStack.length > 0,
743
+ isZoomed,
744
+ onResetZoom: handleResetZoom
745
+ }) : /*#__PURE__*/_jsx(Toolbar, {
746
+ currentTool: currentTool,
747
+ onSelectTool: handleSelectTool,
748
+ onUndo: handleUndo,
749
+ canUndo: undoStack.length > 0,
750
+ isZoomed: isZoomed,
751
+ onResetZoom: handleResetZoom
752
+ })
753
+ }), /*#__PURE__*/_jsx(TextEditModal, {
754
+ visible: textEditVisible,
755
+ initialValue: textEditValue,
756
+ onSubmit: handleTextSubmit,
757
+ onCancel: () => setTextEditVisible(false)
758
+ }), /*#__PURE__*/_jsx(MeasurementEditModal, {
759
+ visible: measureEditVisible,
760
+ initialValue: "",
761
+ initialUnit: lastUsedUnit.current,
762
+ onSubmit: handleMeasureSubmit,
763
+ onCancel: handleMeasureCancel
764
+ })]
765
+ });
766
+ });
767
+ const styles = StyleSheet.create({
768
+ root: {
769
+ flex: 1,
770
+ backgroundColor: "#000000",
771
+ position: "relative"
772
+ },
773
+ editorContainer: {
774
+ flex: 1,
775
+ flexDirection: "row"
776
+ },
777
+ editorContainerTablet: {
778
+ paddingBottom: 92
779
+ },
780
+ editorContainerPhone: {
781
+ flexDirection: "column"
782
+ },
783
+ sidebarLeft: {
784
+ justifyContent: "center",
785
+ paddingHorizontal: 4
786
+ },
787
+ canvasContainer: {
788
+ flex: 1
789
+ },
790
+ sidebarRight: {
791
+ justifyContent: "center",
792
+ paddingHorizontal: 4
793
+ },
794
+ phoneBottomArea: {
795
+ backgroundColor: "#000000"
796
+ },
797
+ phoneControlsBar: {
798
+ flexDirection: "row",
799
+ alignItems: "center",
800
+ justifyContent: "center",
801
+ paddingHorizontal: 4
802
+ },
803
+ toolbarOverlay: {
804
+ position: "absolute",
805
+ left: 0,
806
+ right: 0,
807
+ bottom: 12,
808
+ alignItems: "center"
809
+ }
810
+ });
811
+ //# sourceMappingURL=DrawingEditor.js.map