@cs-open/react-fabric 0.0.3

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 (256) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/components/BackgroundImage/index.cjs +2 -0
  3. package/dist/cjs/components/BackgroundImage/index.cjs.map +1 -0
  4. package/dist/cjs/components/Canvas/index.cjs +2 -0
  5. package/dist/cjs/components/Canvas/index.cjs.map +1 -0
  6. package/dist/cjs/components/Control/index.cjs +2 -0
  7. package/dist/cjs/components/Control/index.cjs.map +1 -0
  8. package/dist/cjs/components/Ellipse/index.cjs +2 -0
  9. package/dist/cjs/components/Ellipse/index.cjs.map +1 -0
  10. package/dist/cjs/components/Group/index.cjs +2 -0
  11. package/dist/cjs/components/Group/index.cjs.map +1 -0
  12. package/dist/cjs/components/Image/index.cjs +2 -0
  13. package/dist/cjs/components/Image/index.cjs.map +1 -0
  14. package/dist/cjs/components/Line/index.cjs +2 -0
  15. package/dist/cjs/components/Line/index.cjs.map +1 -0
  16. package/dist/cjs/components/Loading/index.cjs +2 -0
  17. package/dist/cjs/components/Loading/index.cjs.map +1 -0
  18. package/dist/cjs/components/NodeToolbarPortal/index.cjs +2 -0
  19. package/dist/cjs/components/NodeToolbarPortal/index.cjs.map +1 -0
  20. package/dist/cjs/components/Objects/index.cjs +2 -0
  21. package/dist/cjs/components/Objects/index.cjs.map +1 -0
  22. package/dist/cjs/components/Path/index.cjs +2 -0
  23. package/dist/cjs/components/Path/index.cjs.map +1 -0
  24. package/dist/cjs/components/ReactFabricProvider.cjs +2 -0
  25. package/dist/cjs/components/ReactFabricProvider.cjs.map +1 -0
  26. package/dist/cjs/components/Rect/index.cjs +2 -0
  27. package/dist/cjs/components/Rect/index.cjs.map +1 -0
  28. package/dist/cjs/components/StoreUpdater/index.cjs +2 -0
  29. package/dist/cjs/components/StoreUpdater/index.cjs.map +1 -0
  30. package/dist/cjs/components/Text/index.cjs +2 -0
  31. package/dist/cjs/components/Text/index.cjs.map +1 -0
  32. package/dist/cjs/constants.cjs +2 -0
  33. package/dist/cjs/constants.cjs.map +1 -0
  34. package/dist/cjs/container/ReactFabric/Wrapper.cjs +2 -0
  35. package/dist/cjs/container/ReactFabric/Wrapper.cjs.map +1 -0
  36. package/dist/cjs/container/ReactFabric/index.cjs +2 -0
  37. package/dist/cjs/container/ReactFabric/index.cjs.map +1 -0
  38. package/dist/cjs/contexts/StoreContext.cjs +2 -0
  39. package/dist/cjs/contexts/StoreContext.cjs.map +1 -0
  40. package/dist/cjs/hooks/useCreateObject.cjs +2 -0
  41. package/dist/cjs/hooks/useCreateObject.cjs.map +1 -0
  42. package/dist/cjs/hooks/useDidUpdate.cjs +2 -0
  43. package/dist/cjs/hooks/useDidUpdate.cjs.map +1 -0
  44. package/dist/cjs/hooks/useDraggable.cjs +2 -0
  45. package/dist/cjs/hooks/useDraggable.cjs.map +1 -0
  46. package/dist/cjs/hooks/useReactFabric.cjs +2 -0
  47. package/dist/cjs/hooks/useReactFabric.cjs.map +1 -0
  48. package/dist/cjs/hooks/useResizeHandler.cjs +2 -0
  49. package/dist/cjs/hooks/useResizeHandler.cjs.map +1 -0
  50. package/dist/cjs/hooks/useSplitProps.cjs +2 -0
  51. package/dist/cjs/hooks/useSplitProps.cjs.map +1 -0
  52. package/dist/cjs/hooks/useStore.cjs +2 -0
  53. package/dist/cjs/hooks/useStore.cjs.map +1 -0
  54. package/dist/cjs/hooks/useZoom.cjs +2 -0
  55. package/dist/cjs/hooks/useZoom.cjs.map +1 -0
  56. package/dist/cjs/index.cjs +2 -0
  57. package/dist/cjs/index.cjs.map +1 -0
  58. package/dist/cjs/plugins/FreeRect.cjs +2 -0
  59. package/dist/cjs/plugins/FreeRect.cjs.map +1 -0
  60. package/dist/cjs/plugins/GridLine.cjs +5 -0
  61. package/dist/cjs/plugins/GridLine.cjs.map +1 -0
  62. package/dist/cjs/plugins/Pinch.cjs +2 -0
  63. package/dist/cjs/plugins/Pinch.cjs.map +1 -0
  64. package/dist/cjs/store/index.cjs +2 -0
  65. package/dist/cjs/store/index.cjs.map +1 -0
  66. package/dist/cjs/store/initialState.cjs +2 -0
  67. package/dist/cjs/store/initialState.cjs.map +1 -0
  68. package/dist/cjs/toolbar/Vertical/index.cjs +2 -0
  69. package/dist/cjs/toolbar/Vertical/index.cjs.map +1 -0
  70. package/dist/cjs/types/canvas.cjs +2 -0
  71. package/dist/cjs/types/canvas.cjs.map +1 -0
  72. package/dist/cjs/types/component-props.cjs +2 -0
  73. package/dist/cjs/types/component-props.cjs.map +1 -0
  74. package/dist/cjs/types/general.cjs +2 -0
  75. package/dist/cjs/types/general.cjs.map +1 -0
  76. package/dist/cjs/types/nodes.cjs +2 -0
  77. package/dist/cjs/types/nodes.cjs.map +1 -0
  78. package/dist/cjs/types/object.cjs +2 -0
  79. package/dist/cjs/types/object.cjs.map +1 -0
  80. package/dist/cjs/types/store.cjs +2 -0
  81. package/dist/cjs/types/store.cjs.map +1 -0
  82. package/dist/cjs/types/utils.cjs +2 -0
  83. package/dist/cjs/types/utils.cjs.map +1 -0
  84. package/dist/cjs/utils/constants.cjs +2 -0
  85. package/dist/cjs/utils/constants.cjs.map +1 -0
  86. package/dist/cjs/utils/dom.cjs +2 -0
  87. package/dist/cjs/utils/dom.cjs.map +1 -0
  88. package/dist/cjs/utils/events.cjs +2 -0
  89. package/dist/cjs/utils/events.cjs.map +1 -0
  90. package/dist/cjs/utils/position.cjs +2 -0
  91. package/dist/cjs/utils/position.cjs.map +1 -0
  92. package/dist/cjs/utils/props.cjs +2 -0
  93. package/dist/cjs/utils/props.cjs.map +1 -0
  94. package/dist/esm/components/BackgroundImage/index.mjs +2 -0
  95. package/dist/esm/components/BackgroundImage/index.mjs.map +1 -0
  96. package/dist/esm/components/Canvas/index.mjs +2 -0
  97. package/dist/esm/components/Canvas/index.mjs.map +1 -0
  98. package/dist/esm/components/Control/index.mjs +2 -0
  99. package/dist/esm/components/Control/index.mjs.map +1 -0
  100. package/dist/esm/components/Ellipse/index.mjs +2 -0
  101. package/dist/esm/components/Ellipse/index.mjs.map +1 -0
  102. package/dist/esm/components/Group/index.mjs +2 -0
  103. package/dist/esm/components/Group/index.mjs.map +1 -0
  104. package/dist/esm/components/Line/index.mjs +2 -0
  105. package/dist/esm/components/Line/index.mjs.map +1 -0
  106. package/dist/esm/components/Loading/index.mjs +2 -0
  107. package/dist/esm/components/Loading/index.mjs.map +1 -0
  108. package/dist/esm/components/NodeToolbarPortal/index.mjs +2 -0
  109. package/dist/esm/components/NodeToolbarPortal/index.mjs.map +1 -0
  110. package/dist/esm/components/Objects/index.mjs +2 -0
  111. package/dist/esm/components/Objects/index.mjs.map +1 -0
  112. package/dist/esm/components/Path/index.mjs +2 -0
  113. package/dist/esm/components/Path/index.mjs.map +1 -0
  114. package/dist/esm/components/ReactFabricProvider.mjs +2 -0
  115. package/dist/esm/components/ReactFabricProvider.mjs.map +1 -0
  116. package/dist/esm/components/Rect/index.mjs +2 -0
  117. package/dist/esm/components/Rect/index.mjs.map +1 -0
  118. package/dist/esm/components/StoreUpdater/index.mjs +2 -0
  119. package/dist/esm/components/StoreUpdater/index.mjs.map +1 -0
  120. package/dist/esm/components/Text/index.mjs +2 -0
  121. package/dist/esm/components/Text/index.mjs.map +1 -0
  122. package/dist/esm/container/ReactFabric/Wrapper.mjs +2 -0
  123. package/dist/esm/container/ReactFabric/Wrapper.mjs.map +1 -0
  124. package/dist/esm/container/ReactFabric/index.mjs +2 -0
  125. package/dist/esm/container/ReactFabric/index.mjs.map +1 -0
  126. package/dist/esm/contexts/StoreContext.mjs +2 -0
  127. package/dist/esm/contexts/StoreContext.mjs.map +1 -0
  128. package/dist/esm/hooks/useCreateObject.mjs +2 -0
  129. package/dist/esm/hooks/useCreateObject.mjs.map +1 -0
  130. package/dist/esm/hooks/useDidUpdate.mjs +2 -0
  131. package/dist/esm/hooks/useDidUpdate.mjs.map +1 -0
  132. package/dist/esm/hooks/useDraggable.mjs +2 -0
  133. package/dist/esm/hooks/useDraggable.mjs.map +1 -0
  134. package/dist/esm/hooks/useReactFabric.mjs +2 -0
  135. package/dist/esm/hooks/useReactFabric.mjs.map +1 -0
  136. package/dist/esm/hooks/useResizeHandler.mjs +2 -0
  137. package/dist/esm/hooks/useResizeHandler.mjs.map +1 -0
  138. package/dist/esm/hooks/useSplitProps.mjs +2 -0
  139. package/dist/esm/hooks/useSplitProps.mjs.map +1 -0
  140. package/dist/esm/hooks/useStore.mjs +2 -0
  141. package/dist/esm/hooks/useStore.mjs.map +1 -0
  142. package/dist/esm/hooks/useZoom.mjs +2 -0
  143. package/dist/esm/hooks/useZoom.mjs.map +1 -0
  144. package/dist/esm/index.mjs +2 -0
  145. package/dist/esm/index.mjs.map +1 -0
  146. package/dist/esm/plugins/FreeRect.mjs +2 -0
  147. package/dist/esm/plugins/FreeRect.mjs.map +1 -0
  148. package/dist/esm/plugins/GridLine.mjs +5 -0
  149. package/dist/esm/plugins/GridLine.mjs.map +1 -0
  150. package/dist/esm/plugins/Pinch.mjs +2 -0
  151. package/dist/esm/plugins/Pinch.mjs.map +1 -0
  152. package/dist/esm/store/index.mjs +2 -0
  153. package/dist/esm/store/index.mjs.map +1 -0
  154. package/dist/esm/store/initialState.mjs +2 -0
  155. package/dist/esm/store/initialState.mjs.map +1 -0
  156. package/dist/esm/toolbar/Vertical/index.mjs +2 -0
  157. package/dist/esm/toolbar/Vertical/index.mjs.map +1 -0
  158. package/dist/esm/utils/constants.mjs +2 -0
  159. package/dist/esm/utils/constants.mjs.map +1 -0
  160. package/dist/esm/utils/events.mjs +2 -0
  161. package/dist/esm/utils/events.mjs.map +1 -0
  162. package/dist/esm/utils/props.mjs +2 -0
  163. package/dist/esm/utils/props.mjs.map +1 -0
  164. package/dist/types/components/BackgroundImage/index.d.ts +19 -0
  165. package/dist/types/components/BackgroundImage/index.d.ts.map +1 -0
  166. package/dist/types/components/Canvas/index.d.ts +11 -0
  167. package/dist/types/components/Canvas/index.d.ts.map +1 -0
  168. package/dist/types/components/Control/index.d.ts +28 -0
  169. package/dist/types/components/Control/index.d.ts.map +1 -0
  170. package/dist/types/components/Ellipse/index.d.ts +19 -0
  171. package/dist/types/components/Ellipse/index.d.ts.map +1 -0
  172. package/dist/types/components/Group/index.d.ts +24 -0
  173. package/dist/types/components/Group/index.d.ts.map +1 -0
  174. package/dist/types/components/Image/index.d.ts +18 -0
  175. package/dist/types/components/Image/index.d.ts.map +1 -0
  176. package/dist/types/components/Line/index.d.ts +11 -0
  177. package/dist/types/components/Line/index.d.ts.map +1 -0
  178. package/dist/types/components/Loading/index.d.ts +3 -0
  179. package/dist/types/components/Loading/index.d.ts.map +1 -0
  180. package/dist/types/components/NodeToolbarPortal/index.d.ts +9 -0
  181. package/dist/types/components/NodeToolbarPortal/index.d.ts.map +1 -0
  182. package/dist/types/components/Objects/index.d.ts +9 -0
  183. package/dist/types/components/Objects/index.d.ts.map +1 -0
  184. package/dist/types/components/Path/index.d.ts +11 -0
  185. package/dist/types/components/Path/index.d.ts.map +1 -0
  186. package/dist/types/components/ReactFabricProvider.d.ts +11 -0
  187. package/dist/types/components/ReactFabricProvider.d.ts.map +1 -0
  188. package/dist/types/components/Rect/index.d.ts +20 -0
  189. package/dist/types/components/Rect/index.d.ts.map +1 -0
  190. package/dist/types/components/StoreUpdater/index.d.ts +8 -0
  191. package/dist/types/components/StoreUpdater/index.d.ts.map +1 -0
  192. package/dist/types/components/Text/index.d.ts +11 -0
  193. package/dist/types/components/Text/index.d.ts.map +1 -0
  194. package/dist/types/constants.d.ts +5 -0
  195. package/dist/types/constants.d.ts.map +1 -0
  196. package/dist/types/container/ReactFabric/Wrapper.d.ts +10 -0
  197. package/dist/types/container/ReactFabric/Wrapper.d.ts.map +1 -0
  198. package/dist/types/container/ReactFabric/index.d.ts +69 -0
  199. package/dist/types/container/ReactFabric/index.d.ts.map +1 -0
  200. package/dist/types/contexts/StoreContext.d.ts +4 -0
  201. package/dist/types/contexts/StoreContext.d.ts.map +1 -0
  202. package/dist/types/hooks/useCreateObject.d.ts +15 -0
  203. package/dist/types/hooks/useCreateObject.d.ts.map +1 -0
  204. package/dist/types/hooks/useDidUpdate.d.ts +9 -0
  205. package/dist/types/hooks/useDidUpdate.d.ts.map +1 -0
  206. package/dist/types/hooks/useDraggable.d.ts +3 -0
  207. package/dist/types/hooks/useDraggable.d.ts.map +1 -0
  208. package/dist/types/hooks/useReactFabric.d.ts +55 -0
  209. package/dist/types/hooks/useReactFabric.d.ts.map +1 -0
  210. package/dist/types/hooks/useResizeHandler.d.ts +3 -0
  211. package/dist/types/hooks/useResizeHandler.d.ts.map +1 -0
  212. package/dist/types/hooks/useSplitProps.d.ts +15 -0
  213. package/dist/types/hooks/useSplitProps.d.ts.map +1 -0
  214. package/dist/types/hooks/useStore.d.ts +13 -0
  215. package/dist/types/hooks/useStore.d.ts.map +1 -0
  216. package/dist/types/hooks/useZoom.d.ts +2 -0
  217. package/dist/types/hooks/useZoom.d.ts.map +1 -0
  218. package/dist/types/index.d.ts +29 -0
  219. package/dist/types/index.d.ts.map +1 -0
  220. package/dist/types/plugins/FreeRect.d.ts +15 -0
  221. package/dist/types/plugins/FreeRect.d.ts.map +1 -0
  222. package/dist/types/plugins/GridLine.d.ts +7 -0
  223. package/dist/types/plugins/GridLine.d.ts.map +1 -0
  224. package/dist/types/plugins/Pinch.d.ts +8 -0
  225. package/dist/types/plugins/Pinch.d.ts.map +1 -0
  226. package/dist/types/store/index.d.ts +10 -0
  227. package/dist/types/store/index.d.ts.map +1 -0
  228. package/dist/types/store/initialState.d.ts +12 -0
  229. package/dist/types/store/initialState.d.ts.map +1 -0
  230. package/dist/types/toolbar/Vertical/index.d.ts +10 -0
  231. package/dist/types/toolbar/Vertical/index.d.ts.map +1 -0
  232. package/dist/types/types/canvas.d.ts +15 -0
  233. package/dist/types/types/canvas.d.ts.map +1 -0
  234. package/dist/types/types/component-props.d.ts +41 -0
  235. package/dist/types/types/component-props.d.ts.map +1 -0
  236. package/dist/types/types/general.d.ts +2 -0
  237. package/dist/types/types/general.d.ts.map +1 -0
  238. package/dist/types/types/nodes.d.ts +91 -0
  239. package/dist/types/types/nodes.d.ts.map +1 -0
  240. package/dist/types/types/object.d.ts +22 -0
  241. package/dist/types/types/object.d.ts.map +1 -0
  242. package/dist/types/types/store.d.ts +59 -0
  243. package/dist/types/types/store.d.ts.map +1 -0
  244. package/dist/types/types/utils.d.ts +6 -0
  245. package/dist/types/types/utils.d.ts.map +1 -0
  246. package/dist/types/utils/constants.d.ts +5 -0
  247. package/dist/types/utils/constants.d.ts.map +1 -0
  248. package/dist/types/utils/dom.d.ts +3 -0
  249. package/dist/types/utils/dom.d.ts.map +1 -0
  250. package/dist/types/utils/events.d.ts +5 -0
  251. package/dist/types/utils/events.d.ts.map +1 -0
  252. package/dist/types/utils/position.d.ts +9 -0
  253. package/dist/types/utils/position.d.ts.map +1 -0
  254. package/dist/types/utils/props.d.ts +4 -0
  255. package/dist/types/utils/props.d.ts.map +1 -0
  256. package/package.json +100 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 cs-open
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var f=require("fabric"),i=require("react"),F=require("../../hooks/useDidUpdate.cjs"),w=require("../../hooks/useStore.cjs");const q=n=>({width:n.width,height:n.height}),R=i.forwardRef(({src:n,onLoad:b,onScaling:V,scaleToFit:l,scaleToCover:d,...g},S)=>{const r=i.useRef(null),a=w.useStoreApi(),{width:T,height:p}=w.useStore(q),m=i.useCallback(c=>{const{canvas:e,manualZoom:t=1,defaultCentered:o}=a.getState();if(!r.current)return;if(!e){console.warn("updateViewport: !canvas");return}if(!e.backgroundImage){console.warn("updateViewport: !canvas.backgroundImage");return}const u=c.scaleToFit?f.util.findScaleToFit(r.current,e):c.scaleToCover?f.util.findScaleToCover(r.current,e):1,s=u*t;e.setViewportTransform([s,0,0,s,0,0]);let h=0,v=0;if(o&&e.backgroundImage){const k=e.backgroundImage.width||0,C=e.backgroundImage.height||0;h=(e.width-k*s)/2,v=(e.height-C*s)/2}const I=[s,0,0,s,h,v];e.setViewportTransform(I),e.requestRenderAll(),a.setState({fitZoom:u,manualZoom:t,zoom:s})},[a]);return i.useEffect(()=>{m({scaleToFit:l,scaleToCover:d})},[T,p,l,d,m,a]),i.useEffect(()=>{if(!n){console.warn("ReactFabricBackgroundImage: !src");return}const{domNode:c,setLoading:e}=a.getState();return e(!0),f.FabricImage.fromURL(n,{crossOrigin:"anonymous"}).then(t=>{const{canvas:o}=a.getState();if(o){const u={...g,angle:0};if(t.set({...u,objectCaching:!1}),o.getContext().imageSmoothingEnabled=!0,o.getContext().imageSmoothingQuality="high",o.backgroundImage=t,r.current=t,requestAnimationFrame(()=>{m({scaleToFit:l,scaleToCover:d})}),!o.viewportTransform){console.warn("!viewport");return}b?.(t)}else console.warn("ReactFabric:BackgroundImage: !canvas",o)}).catch(console.error).finally(()=>{c&&(c.dataset.src=n),e(!1)}),()=>{const{canvas:t}=a.getState();t?.backgroundImage&&(t.backgroundImage=void 0,t.remove(r.current),r.current=null,t.renderAll())}},[n]),F.useDidUpdate(()=>{const{canvas:c}=a.getState();r.current&&(Object.entries(g).forEach(([e,t])=>{e==="angle"?r.current?.rotate(t):r.current?.set(e,t)}),c?.requestRenderAll())},[g,a]),i.useImperativeHandle(S,()=>({instance:r.current})),null});var A=i.memo(R);exports.default=A;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/BackgroundImage/index.tsx"],"sourcesContent":["import type {\n BasicTransformEvent,\n ImageProps,\n ObjectEvents,\n SerializedImageProps,\n TDegree,\n TPointerEvent,\n} from 'fabric'\nimport { FabricImage, util } from 'fabric'\nimport { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef } from 'react'\nimport { useDidUpdate } from '../../hooks/useDidUpdate'\nimport { useStore, useStoreApi } from '../../hooks/useStore'\nimport type { ReactFabricState } from '../../types/store'\n\nexport type Handle = {}\n\ntype ScaleMode = {\n scaleToFit?: boolean\n scaleToCover?: boolean\n}\nexport type BackgroundImageProps = Partial<ImageProps> & {\n src: string\n onLoad?: (imageSource: FabricImage<Partial<ImageProps>, SerializedImageProps, ObjectEvents>) => void\n onScaling?: (scale: BasicTransformEvent<TPointerEvent>) => void\n\n /** 自动缩放至容器宽高 */\n} & ScaleMode\n\nconst selector = (s: ReactFabricState) => ({\n width: s.width,\n height: s.height,\n})\n\nconst BackgroundImage = forwardRef<Handle, BackgroundImageProps>(\n ({ src, onLoad, onScaling, scaleToFit, scaleToCover, ...options }, ref) => {\n const backgroundImageRef = useRef<FabricImage | null>(null)\n\n const store = useStoreApi()\n\n const { width, height } = useStore(selector)\n\n const updateViewport = useCallback(\n (params: { scaleToFit?: boolean; scaleToCover?: boolean }) => {\n const { canvas, manualZoom = 1, defaultCentered } = store.getState()\n if (!backgroundImageRef.current) {\n return\n }\n if (!canvas) {\n console.warn('updateViewport: !canvas')\n return\n }\n if (!canvas.backgroundImage) {\n console.warn('updateViewport: !canvas.backgroundImage')\n return\n }\n\n // 1. 计算缩放\n const fitZoom = params.scaleToFit\n ? util.findScaleToFit(backgroundImageRef.current, canvas)\n : params.scaleToCover\n ? util.findScaleToCover(backgroundImageRef.current, canvas)\n : 1\n\n const combinedZoom = fitZoom * manualZoom\n\n // 2. 先应用基础缩放\n canvas.setViewportTransform([combinedZoom, 0, 0, combinedZoom, 0, 0])\n\n // 3. 如果需要居中,计算偏移量\n let deltaX = 0\n let deltaY = 0\n\n if (defaultCentered && canvas.backgroundImage) {\n const bgWidth = canvas.backgroundImage.width || 0\n const bgHeight = canvas.backgroundImage.height || 0\n deltaX = (canvas.width! - bgWidth * combinedZoom) / 2\n deltaY = (canvas.height! - bgHeight * combinedZoom) / 2\n // const canvasCenter = {\n // x: canvas.width! / 2,\n // y: canvas.height! / 2,\n // }\n // const bgCenter = {\n // x:\n // (canvas.backgroundImage.left! + (canvas.backgroundImage.width! * canvas.backgroundImage.scaleX!) / 2) *\n // combinedZoom,\n // y:\n // (canvas.backgroundImage.top! + (canvas.backgroundImage.height! * canvas.backgroundImage.scaleY!) / 2) *\n // combinedZoom,\n // }\n // deltaX = canvasCenter.x - bgCenter.x\n // deltaY = canvasCenter.y - bgCenter.y\n }\n\n // 4. 应用最终变换\n const finalTransform: [number, number, number, number, number, number] = [\n combinedZoom,\n 0,\n 0,\n combinedZoom,\n deltaX,\n deltaY,\n ]\n\n canvas.setViewportTransform(finalTransform)\n canvas.requestRenderAll()\n\n // 5. 更新 store\n store.setState({\n fitZoom,\n manualZoom,\n zoom: combinedZoom,\n })\n },\n [store],\n )\n\n // 监听 width, height 的变化\n useEffect(() => {\n updateViewport({\n scaleToFit,\n scaleToCover,\n })\n }, [width, height, scaleToFit, scaleToCover, updateViewport, store])\n\n useEffect(() => {\n if (!src) {\n console.warn('ReactFabricBackgroundImage: !src')\n return\n }\n const { domNode, setLoading } = store.getState()\n setLoading(true)\n FabricImage.fromURL(src, { crossOrigin: 'anonymous' })\n .then(imageSource => {\n const { canvas } = store.getState()\n if (canvas) {\n // 初始化时角度的旋转,一定要放到 updateViewport 之后;先画正图片,再进行旋转\n const removeAngleOptions = { ...options, angle: 0 }\n imageSource.set({\n ...removeAngleOptions,\n objectCaching: false,\n })\n canvas.getContext().imageSmoothingEnabled = true\n canvas.getContext().imageSmoothingQuality = 'high'\n canvas.backgroundImage = imageSource\n backgroundImageRef.current = imageSource\n\n requestAnimationFrame(() => {\n updateViewport({\n scaleToFit,\n scaleToCover,\n })\n })\n\n const viewport = canvas.viewportTransform\n if (!viewport) {\n console.warn('!viewport')\n return\n }\n\n // imageSource.angle = options.angle || 0\n onLoad?.(imageSource)\n } else {\n console.warn('ReactFabric:BackgroundImage: !canvas', canvas)\n }\n })\n .catch(console.error)\n .finally(() => {\n if (domNode) domNode.dataset.src = src\n setLoading(false)\n })\n\n return () => {\n const { canvas } = store.getState()\n if (canvas?.backgroundImage) {\n canvas.backgroundImage = undefined\n canvas.remove(backgroundImageRef.current!)\n backgroundImageRef.current = null\n canvas.renderAll()\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [src])\n\n useDidUpdate(() => {\n const { canvas } = store.getState()\n\n if (backgroundImageRef.current) {\n Object.entries(options).forEach(([key, value]) => {\n if (key === 'angle') {\n backgroundImageRef.current?.rotate(value as TDegree)\n } else {\n backgroundImageRef.current?.set(key, value)\n }\n })\n canvas?.requestRenderAll()\n }\n }, [options, store])\n\n useImperativeHandle(ref, () => ({\n // 是最新的?\n instance: backgroundImageRef.current,\n }))\n\n return null\n },\n)\n\nexport default memo(BackgroundImage)\n"],"names":["selector","s","BackgroundImage","forwardRef","src","onLoad","onScaling","scaleToFit","scaleToCover","options","ref","backgroundImageRef","useRef","store","useStoreApi","width","height","useStore","updateViewport","useCallback","params","canvas","manualZoom","defaultCentered","fitZoom","util","combinedZoom","deltaX","deltaY","bgWidth","bgHeight","finalTransform","useEffect","domNode","setLoading","FabricImage","imageSource","removeAngleOptions","useDidUpdate","key","value","useImperativeHandle","memo"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var h=require("react/jsx-runtime"),c=require("fabric"),n=require("react"),E=require("../../hooks/useDraggable.cjs"),R=require("../../hooks/useResizeHandler.cjs"),g=require("../../hooks/useSplitProps.cjs"),z=require("../../hooks/useStore.cjs"),Y=require("../../utils/events.cjs");const j={position:"absolute",width:"100%",height:"100%",top:0,left:0},x=({children:w,onMouseWheel:l,...q})=>{const e=n.useRef(),t=z.useStoreApi(),i=n.useRef(null);E.default();const f=n.useRef(null),[S,u]=g.useSplitProps(q);return n.useLayoutEffect(()=>{const o=i.current;e.current=new c.Canvas(o||void 0,{...u});const r=Y.bindEvents(e.current,S);return t.setState({canvas:e.current}),window.canvas=e.current,()=>{r(),e.current?.dispose(),o?.remove(),e.current=void 0,t.setState({canvas:null})}},[]),R.default(),n.useEffect(()=>{const o=r=>{const{zoomable:b,maxManualZoom:v,minManualZoom:d,fitZoom:m=1,zoom:P}=t.getState();if(r.e.preventDefault(),r.e.stopPropagation(),r.e.wheelDeltaY!==0){if(r.e.ctrlKey||r.e.wheelDeltaY===void 0){if(!b)return;const a=r.e.deltaY>0?.95:1.05;let s=P/m*a;s>v&&(s=v),s<d&&(s=d);const p=s*m;e.current?.zoomToPoint(new c.Point(r.e.offsetX,r.e.offsetY),p),t.setState({manualZoom:s,zoom:p})}else{const a=new c.Point(-r.e.deltaX*1.5,-r.e.deltaY*1.5);e.current?.relativePan(a)}l?.(r)}};return e.current?.on("mouse:wheel",o),()=>{e.current?.off("mouse:wheel",o)}},[l,t]),n.useEffect(()=>{t.setState({domNode:f.current?.closest(".react-fabric")})},[t]),n.useEffect(()=>{e.current&&(e.current.set(u),e.current.requestRenderAll())},[u]),h.jsxs("div",{className:"react-fabric__canvas",ref:f,style:j,children:[h.jsx("canvas",{ref:i}),w]})};exports.default=x;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Canvas/index.tsx"],"sourcesContent":["import type { CanvasEvents, CanvasOptions, TPointerEventInfo } from 'fabric'\nimport { Canvas as BaseCanvas, Point } from 'fabric'\nimport type { CSSProperties, PropsWithChildren } from 'react'\nimport { useEffect, useLayoutEffect, useRef } from 'react'\nimport useDraggable from '../../hooks/useDraggable'\nimport useResizeHandler from '../../hooks/useResizeHandler'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport { useStoreApi } from '../../hooks/useStore'\nimport type { AllCanvasEvents } from '../../types/canvas'\nimport { bindEvents } from '../../utils/events'\n\nconst style: CSSProperties = {\n position: 'absolute',\n width: '100%',\n height: '100%',\n top: 0,\n left: 0,\n}\n\n// 首先定义事件类型\ntype CanvasEventProps = {\n [K in keyof AllCanvasEvents]: AllCanvasEvents[K]\n}\n\n// 分离配置属性类型\ntype CanvasConfigProps = Omit<CanvasOptions, keyof CanvasEventProps>\n\n// 重新定义 CanvasProps\nexport type CanvasProps = PropsWithChildren<Partial<CanvasConfigProps> & Partial<CanvasEventProps>>\n\nconst Canvas = ({ children, onMouseWheel, ...props }: CanvasProps) => {\n const canvasRef = useRef<BaseCanvas>()\n const store = useStoreApi()\n const canvasDomRef = useRef<HTMLCanvasElement | null>(null)\n useDraggable()\n const domRef = useRef<HTMLDivElement>(null)\n\n const [listeners, attributes] = useSplitProps(props)\n\n useLayoutEffect(() => {\n const canvas = canvasDomRef.current\n\n canvasRef.current = new BaseCanvas(canvas || undefined, {\n ...attributes,\n })\n\n // 绑定事件并获取清理函数\n const unbindEvents = bindEvents<CanvasEvents>(canvasRef.current, listeners)\n\n store.setState({\n canvas: canvasRef.current,\n })\n //@ts-expect-error\n window.canvas = canvasRef.current\n\n return () => {\n unbindEvents() // 调用清理函数\n canvasRef.current?.dispose()\n canvas?.remove()\n canvasRef.current = undefined // 清除引用\n store.setState({\n canvas: null,\n })\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n useResizeHandler()\n\n useEffect(() => {\n const onMouseWheelHandler = (opt: TPointerEventInfo<WheelEvent>) => {\n const { zoomable, maxManualZoom, minManualZoom, fitZoom = 1, zoom } = store.getState()\n\n // 阻止默认行为\n opt.e.preventDefault()\n opt.e.stopPropagation()\n\n // 如果是惯性滚动,直接返回\n if ((opt.e as any).wheelDeltaY === 0) return\n\n // 检查是否为缩放手势(Mac 上的双指捏合/张开)\n if (opt.e.ctrlKey || (opt.e as any).wheelDeltaY === undefined) {\n // 缩放逻辑\n if (!zoomable) return\n\n const delta = opt.e.deltaY\n const zoomFactor = delta > 0 ? 0.95 : 1.05\n let currentManualZoom = zoom / fitZoom\n let newManualZoom = currentManualZoom * zoomFactor\n\n if (newManualZoom > maxManualZoom) newManualZoom = maxManualZoom\n if (newManualZoom < minManualZoom) newManualZoom = minManualZoom\n\n const combinedZoom = newManualZoom * fitZoom\n\n canvasRef.current?.zoomToPoint(new Point(opt.e.offsetX, opt.e.offsetY), combinedZoom)\n\n store.setState({\n manualZoom: newManualZoom,\n zoom: combinedZoom,\n })\n } else {\n // 平移逻辑\n // 如果觉得太灵敏了,可以调小这个值,比如改为 1.2 或更小\n const sensitivityFactor = 1.5\n const delta = new Point(-opt.e.deltaX * sensitivityFactor, -opt.e.deltaY * sensitivityFactor)\n canvasRef.current?.relativePan(delta)\n }\n\n onMouseWheel?.(opt)\n }\n\n canvasRef.current?.on('mouse:wheel', onMouseWheelHandler)\n\n return () => {\n canvasRef.current?.off('mouse:wheel', onMouseWheelHandler)\n }\n }, [onMouseWheel, store])\n\n useEffect(() => {\n store.setState({\n domNode: domRef.current?.closest('.react-fabric') as HTMLDivElement,\n })\n }, [store])\n\n useEffect(() => {\n if (canvasRef.current) {\n canvasRef.current.set(attributes)\n canvasRef.current.requestRenderAll()\n }\n }, [attributes])\n\n return (\n <div className=\"react-fabric__canvas\" ref={domRef} style={style}>\n <canvas ref={canvasDomRef}></canvas>\n {children}\n </div>\n )\n}\n\nexport default Canvas\n"],"names":["style","Canvas","children","onMouseWheel","props","canvasRef","useRef","store","useStoreApi","canvasDomRef","useDraggable","domRef","listeners","attributes","useSplitProps","useLayoutEffect","canvas","BaseCanvas","unbindEvents","bindEvents","useResizeHandler","useEffect","onMouseWheelHandler","opt","zoomable","maxManualZoom","minManualZoom","fitZoom","zoom","zoomFactor","newManualZoom","combinedZoom","Point","delta","jsxs","jsx"],"mappings":"iWAWMA,EAAuB,CAC3B,SAAU,WACV,MAAO,OACP,OAAQ,OACR,IAAK,EACL,KAAM,CACR,EAaMC,EAAS,CAAC,CAAE,SAAAC,EAAU,aAAAC,EAAc,GAAGC,CAAM,IAAmB,CACpE,MAAMC,EAAYC,EAAAA,SACZC,EAAQC,EAAAA,YACRC,EAAAA,EAAeH,SAAiC,IAAI,EAC1DI,EAAAA,QACA,EAAA,MAAMC,EAASL,EAAuB,OAAA,IAAI,EAEpC,CAACM,EAAWC,CAAU,EAAIC,EAAAA,cAAcV,CAAK,EAEnD,OAAAW,EAAAA,gBAAgB,IAAM,CACpB,MAAMC,EAASP,EAAa,QAE5BJ,EAAU,QAAU,IAAIY,EAAAA,OAAWD,GAAU,OAAW,CACtD,GAAGH,CACL,CAAC,EAGD,MAAMK,EAAeC,EAAAA,WAAyBd,EAAU,QAASO,CAAS,EAE1E,OAAAL,EAAM,SAAS,CACb,OAAQF,EAAU,OACpB,CAAC,EAED,OAAO,OAASA,EAAU,QAEnB,IAAM,CACXa,EAAa,EACbb,EAAU,SAAS,QAAA,EACnBW,GAAQ,OACRX,EAAAA,EAAU,QAAU,OACpBE,EAAM,SAAS,CACb,OAAQ,IACV,CAAC,CACH,CAEF,EAAG,CAAE,CAAA,EAELa,EAAAA,QAEAC,EAAAA,EAAAA,UAAU,IAAM,CACd,MAAMC,EAAuBC,GAAuC,CAClE,KAAM,CAAE,SAAAC,EAAU,cAAAC,EAAe,cAAAC,EAAe,QAAAC,EAAU,EAAG,KAAAC,CAAK,EAAIrB,EAAM,SAAS,EAOrF,GAJAgB,EAAI,EAAE,eAAe,EACrBA,EAAI,EAAE,gBAAA,EAGDA,EAAI,EAAU,cAAgB,EAGnC,IAAIA,EAAI,EAAE,SAAYA,EAAI,EAAU,cAAgB,OAAW,CAE7D,GAAI,CAACC,EAAU,OAGf,MAAMK,EADQN,EAAI,EAAE,OACO,EAAI,IAAO,KAEtC,IAAIO,EADoBF,EAAOD,EACSE,EAEpCC,EAAgBL,IAAeK,EAAgBL,GAC/CK,EAAgBJ,IAAeI,EAAgBJ,GAEnD,MAAMK,EAAeD,EAAgBH,EAErCtB,EAAU,SAAS,YAAY,IAAI2B,QAAMT,EAAI,EAAE,QAASA,EAAI,EAAE,OAAO,EAAGQ,CAAY,EAEpFxB,EAAM,SAAS,CACb,WAAYuB,EACZ,KAAMC,CACR,CAAC,CACH,KAAO,CAIL,MAAME,EAAQ,IAAID,EAAAA,MAAM,CAACT,EAAI,EAAE,OAAS,IAAmB,CAACA,EAAI,EAAE,OAAS,GAAiB,EAC5FlB,EAAU,SAAS,YAAY4B,CAAK,CACtC,CAEA9B,IAAeoB,CAAG,CACpB,CAAA,EAEA,OAAAlB,EAAU,SAAS,GAAG,cAAeiB,CAAmB,EAEjD,IAAM,CACXjB,EAAU,SAAS,IAAI,cAAeiB,CAAmB,CAC3D,CACF,EAAG,CAACnB,EAAcI,CAAK,CAAC,EAExBc,EAAAA,UAAU,IAAM,CACdd,EAAM,SAAS,CACb,QAASI,EAAO,SAAS,QAAQ,eAAe,CAClD,CAAC,CACH,EAAG,CAACJ,CAAK,CAAC,EAEVc,EAAAA,UAAU,IAAM,CACVhB,EAAU,UACZA,EAAU,QAAQ,IAAIQ,CAAU,EAChCR,EAAU,QAAQ,iBAEtB,EAAA,EAAG,CAACQ,CAAU,CAAC,EAGbqB,EAAAA,KAAC,OAAI,UAAU,uBAAuB,IAAKvB,EAAQ,MAAOX,EACxD,SAAAmC,CAAAA,EAAAA,IAAC,UAAO,IAAK1B,CAAAA,CAAc,EAC1BP,CACH,CAAA,CAAA,CAEJ"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var p=require("react/jsx-runtime"),m=require("@floating-ui/core"),E=require("fabric"),b=require("tailwind-merge"),t=require("react"),R=require("../../hooks/useStore.cjs"),P=require("../NodeToolbarPortal/index.cjs");const j=t.forwardRef(({children:s,className:w,placement:g="bottom",open:o=!0,onOpenChange:f,closeOnOutsideClick:h=!0,Content:x},l)=>{const d=t.useRef(void 0),r=R.useStore(e=>e.canvas),i=t.useRef(null),v=t.useCallback(e=>{e!==d.current&&(d.current=e,typeof l=="function"?l(e):l&&(l.current=e))},[l]),C=t.useMemo(()=>t.Children.only(s)&&t.isValidElement(s)?t.cloneElement(s,{ref:v,...s.props}):s,[s,v]),c=t.useCallback(()=>{if(!d.current||!i.current||!r)return;const e=i.current.getBoundingClientRect();if(e.width===0||e.height===0){requestAnimationFrame(c);return}const n=d.current.getCoords().map(a=>E.util.sendPointToPlane(a,r.viewportTransform,void 0)),u={getElementRects:a=>a,getDimensions:a=>a,getClippingRect:()=>({x:0,y:0,width:r.width,height:r.height})},y={x:n[0].x,y:n[0].y,width:n[2].x-n[0].x,height:n[2].y-n[0].y};m.computePosition(y,i.current.getBoundingClientRect(),{platform:u,placement:g,middleware:[m.offset(5),m.flip(),m.shift({padding:5})]}).then(({x:a,y:q})=>{i.current&&Object.assign(i.current.style,{left:`${a}px`,top:`${q}px`})})},[r,g]);return t.useEffect(()=>{o&&requestAnimationFrame(c)},[o,c]),t.useEffect(()=>(r?.on("after:render",c),()=>{r?.off("after:render",c)}),[r,c]),t.useEffect(()=>{const e=u=>{u.e.stopPropagation(),u.e.preventDefault(),u.target===d.current?f?.(!o):o&&f?.(!1)},n=u=>{i.current?.contains(u.target)||h&&o&&f?.(!1)};return r?.on("mouse:down",e),document.addEventListener("mousedown",n,!1),()=>{r?.off("mouse:down",e),document.removeEventListener("mousedown",n,!1)}},[r,h,f,o]),p.jsxs(p.Fragment,{children:[C,o&&p.jsx(P.default,{className:b.twMerge("absolute",w),ref:i,onClick:e=>{e.stopPropagation(),e.preventDefault()},children:x})]})});exports.default=j;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Control/index.tsx"],"sourcesContent":["import { computePosition, flip, offset, shift,ComputePositionConfig } from '@floating-ui/core'\nimport type { FabricObject, FabricObjectProps, TPointerEvent, TPointerEventInfo } from 'fabric'\nimport { util } from 'fabric'\nimport { twMerge } from 'tailwind-merge'\nimport type { ReactNode } from 'react'\nimport { useMemo } from 'react'\nimport { useCallback } from 'react'\nimport { useRef } from 'react'\nimport { useEffect } from 'react'\nimport React from 'react'\nimport { useStore } from '../../hooks/useStore'\nimport type { AllObjectEvents } from '../../types/object'\nimport NodeToolbarPortal from '../NodeToolbarPortal'\n\n/**\n * @desc 不能内置支持 selected , 因为需要 rect 开启 lockMovementX lockMovementY, 这样支持的场景就受限了\n */\nexport type ControlProps = Partial<AllObjectEvents & FabricObjectProps> & {\n Content: ReactNode\n children: ReactNode\n placement?: ComputePositionConfig['placement']\n className?: string\n open?: boolean\n onOpenChange?: (open: boolean) => void\n closeOnOutsideClick?: boolean\n}\n\nconst Control = React.forwardRef(\n (\n {\n children,\n className,\n placement = 'bottom',\n open = true,\n onOpenChange,\n closeOnOutsideClick = true,\n Content,\n }: ControlProps,\n forwardRef,\n ) => {\n const instanceRef = useRef<FabricObject | undefined>(undefined)\n const canvas = useStore(state => state.canvas)\n const floatingElRef = useRef<HTMLDivElement>(null)\n\n // 使用 useCallback 创建稳定的 ref 回调\n const refCallback = useCallback(\n (node: any) => {\n // 只在节点真正改变时更新 ref\n if (node !== instanceRef.current) {\n instanceRef.current = node\n\n // 处理 forwardRef\n if (typeof forwardRef === 'function') {\n forwardRef(node)\n } else if (forwardRef) {\n forwardRef.current = node\n }\n }\n },\n [forwardRef],\n )\n\n // 使用 useMemo 缓存克隆的子元素\n const newChildren = useMemo(() => {\n if (React.Children.only(children) && React.isValidElement(children)) {\n return React.cloneElement(children, {\n ref: refCallback,\n ...children.props,\n })\n }\n return children\n }, [children, refCallback]) // 只在 children 或 refCallback 改变时重新克隆\n\n const updatePosition = useCallback(() => {\n if (!instanceRef.current || !floatingElRef.current || !canvas) {\n return\n }\n\n // 确保元素已经渲染并且有尺寸\n const floatingRect = floatingElRef.current.getBoundingClientRect()\n if (floatingRect.width === 0 || floatingRect.height === 0) {\n // 如果元素还没有尺寸,等待下一帧再试\n requestAnimationFrame(updatePosition)\n return\n }\n\n const sceneCoords = instanceRef.current.getCoords()\n const viewportCoords = sceneCoords.map(point => util.sendPointToPlane(point, canvas.viewportTransform, undefined))\n\n const platform = {\n getElementRects: (data:any) => data,\n getDimensions: (element:any) => element,\n getClippingRect: () => ({\n x: 0,\n y: 0,\n width: canvas.width,\n height: canvas.height,\n }),\n }\n\n const virtualEl = {\n x: viewportCoords[0].x,\n y: viewportCoords[0].y,\n width: viewportCoords[2].x - viewportCoords[0].x,\n height: viewportCoords[2].y - viewportCoords[0].y,\n }\n\n computePosition(virtualEl, floatingElRef.current.getBoundingClientRect(), {\n platform,\n placement,\n middleware: [offset(5), flip(), shift({ padding: 5 })],\n }).then(({ x, y }) => {\n if (!floatingElRef.current) return\n\n Object.assign(floatingElRef.current.style, {\n left: `${x}px`,\n top: `${y}px`,\n })\n })\n }, [canvas, placement])\n\n // 确保在元素挂载后更新位置\n useEffect(() => {\n if (open) {\n requestAnimationFrame(updatePosition)\n }\n }, [open, updatePosition])\n\n useEffect(() => {\n canvas?.on('after:render', updatePosition)\n\n return () => {\n canvas?.off('after:render', updatePosition)\n }\n }, [canvas, updatePosition])\n\n useEffect(() => {\n const handleCanvasClick = (e: TPointerEventInfo<TPointerEvent>) => {\n // 阻止事件冒泡到 document\n e.e.stopPropagation()\n e.e.preventDefault() // 也阻止默认行为\n\n // 使用 id 比较来确保正确匹配\n if (e.target === instanceRef.current) {\n onOpenChange?.(!open)\n }\n // 点击其他区域时关闭\n else if (open) {\n onOpenChange?.(false)\n }\n }\n\n const handleDocumentClick = (e: MouseEvent) => {\n // 如果点击在浮动内容内,不处理\n if (floatingElRef.current?.contains(e.target as Node)) {\n return\n }\n\n if (closeOnOutsideClick && open) {\n onOpenChange?.(false)\n }\n }\n\n canvas?.on('mouse:down', handleCanvasClick)\n // 改为冒泡阶段处理 document 事件\n document.addEventListener('mousedown', handleDocumentClick, false)\n\n return () => {\n canvas?.off('mouse:down', handleCanvasClick)\n document.removeEventListener('mousedown', handleDocumentClick, false)\n }\n }, [canvas, closeOnOutsideClick, onOpenChange, open])\n\n return (\n <>\n {newChildren}\n {open && (\n <NodeToolbarPortal\n className={twMerge('absolute', className)}\n ref={floatingElRef}\n onClick={e => {\n e.stopPropagation()\n e.preventDefault()\n }}\n >\n {Content}\n </NodeToolbarPortal>\n )}\n </>\n )\n },\n)\n\nexport default Control\n"],"names":["Control","React","children","className","placement","open","onOpenChange","closeOnOutsideClick","Content","forwardRef","instanceRef","useRef","canvas","useStore","state","floatingElRef","refCallback","useCallback","node","newChildren","useMemo","updatePosition","floatingRect","viewportCoords","point","util","platform","data","element","virtualEl","computePosition","offset","flip","shift","x","y","useEffect","handleCanvasClick","e","handleDocumentClick","jsxs","Fragment","jsx","NodeToolbarPortal","twMerge"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var o=require("fabric"),e=require("react"),p=require("../../hooks/useCreateObject.cjs"),n=require("../../hooks/useSplitProps.cjs");const c=e.forwardRef(({group:t,...u},s)=>{const[a,i,l]=n.useSplitProps(u),r=p.useCreateObject({Constructor:o.Ellipse,defaultValues:l,attributes:i,group:t,listeners:a});return e.useImperativeHandle(s,()=>r,[r]),null});var d=e.memo(c);exports.default=d;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Ellipse/index.tsx"],"sourcesContent":["import type { Group as BaseGroup } from 'fabric'\nimport { Ellipse as BaseEllipse } from 'fabric'\nimport { forwardRef, memo, useImperativeHandle } from 'react'\nimport { useCreateObject } from '../../hooks/useCreateObject'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport type { AllObjectEvents } from '../../types/object'\n\nexport type EllipseProps<T = unknown> = Partial<BaseEllipse & AllObjectEvents> & {\n group?: BaseGroup\n defaultLeft?: number\n defaultTop?: number\n defaultWidth?: number\n defaultHeight?: number\n} & T\n\nconst Ellipse = forwardRef<BaseEllipse | undefined, EllipseProps>(({ group, ...props }, ref) => {\n const [listeners, attributes, defaultValues] = useSplitProps(props)\n\n const instance = useCreateObject({\n Constructor: BaseEllipse,\n defaultValues,\n attributes,\n group,\n listeners,\n })\n\n useImperativeHandle(ref, () => instance, [instance])\n\n return null\n})\n\nexport default memo(Ellipse)\n"],"names":["Ellipse","forwardRef","group","props","ref","listeners","attributes","defaultValues","useSplitProps","instance","useCreateObject","BaseEllipse","useImperativeHandle","memo"],"mappings":"uMAeA,MAAMA,EAAUC,EAAAA,WAAkD,CAAC,CAAE,MAAAC,EAAO,GAAGC,CAAM,EAAGC,IAAQ,CAC9F,KAAM,CAACC,EAAWC,EAAYC,CAAa,EAAIC,EAAAA,cAAcL,CAAK,EAE5DM,EAAWC,kBAAgB,CAC/B,YAAaC,EAAAA,QACb,cAAAJ,EACA,WAAAD,EACA,MAAAJ,EACA,UAAAG,CACF,CAAC,EAED,OAAAO,EAAAA,oBAAoBR,EAAK,IAAMK,EAAU,CAACA,CAAQ,CAAC,EAE5C,IACT,CAAC,EAED,MAAeI,OAAKb,CAAO"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var l=require("react/jsx-runtime"),v=require("fabric"),r=require("react"),p=require("../../hooks/useDidUpdate.cjs"),f=require("../../hooks/useSplitProps.cjs"),m=require("../../hooks/useStore.cjs"),S=require("../../utils/events.cjs");const q=r.memo(({children:c,controlsVisibility:u,...d})=>{const s=m.useStoreApi(),[e,n]=r.useState(null),[a,i]=f.useSplitProps(d);return r.useLayoutEffect(()=>{const t=new v.Group([],{...i});return n(t),()=>{const{canvas:o}=s.getState();t&&o&&o.remove(t),n(null)}},[s]),r.useEffect(()=>{const{canvas:t}=s.getState();!e||!t||t.add(e)},[s,e]),r.useEffect(()=>e?S.bindEvents(e,a):void 0,[e,a]),r.useEffect(()=>{!e||!u||e.setControlsVisibility(u)},[e,u]),p.useDidUpdate(()=>{const{canvas:t}=s.getState();!e||!t||(e.set(i),e.setCoords(),t.requestRenderAll())},[i]),l.jsx(l.Fragment,{children:e&&r.Children.map(c,t=>r.isValidElement(t)?r.cloneElement(t,{group:e}):null)})});exports.default=q;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Group/index.tsx"],"sourcesContent":["import type { GroupEvents, GroupProps } from 'fabric'\nimport { Group as BaseGroup } from 'fabric'\nimport { Children, cloneElement, isValidElement, memo, useEffect, useLayoutEffect, useState } from 'react'\nimport { useDidUpdate } from '../../hooks/useDidUpdate'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport { useStoreApi } from '../../hooks/useStore'\nimport type { AllEvents } from '../../types/canvas'\nimport { bindEvents } from '../../utils/events'\n\nexport type MyGroupProps = Partial<GroupProps & AllEvents<GroupEvents>> & {\n children: React.ReactElement<{ group?: BaseGroup }>[] | React.ReactElement<{ group?: BaseGroup }>\n controlsVisibility?: {\n ml?: boolean\n mt?: boolean\n mr?: boolean\n mb?: boolean\n mtr?: boolean\n tl?: boolean\n tr?: boolean\n bl?: boolean\n br?: boolean\n }\n}\n\nconst Group = memo(({ children, controlsVisibility, ...props }: MyGroupProps) => {\n const store = useStoreApi()\n const [instance, setInstance] = useState<BaseGroup | null>(null)\n\n const [listeners, attributes] = useSplitProps(props)\n\n // 创建 Group 实例,但不立即添加到 canvas\n useLayoutEffect(() => {\n const newInstance = new BaseGroup([], {\n ...attributes,\n })\n setInstance(newInstance)\n\n return () => {\n const { canvas } = store.getState()\n if (newInstance && canvas) {\n canvas.remove(newInstance)\n }\n setInstance(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [store])\n\n // 等待子元素添加完成后,再将 group 添加到 canvas\n useEffect(() => {\n const { canvas } = store.getState()\n if (!instance || !canvas) return\n\n canvas.add(instance)\n }, [store, instance])\n\n // 单独处理事件绑定\n useEffect(() => {\n if (!instance) return\n\n const cleanup = bindEvents(instance, listeners)\n return cleanup\n }, [instance, listeners])\n\n // 专门处理 setControlsVisibility\n useEffect(() => {\n if (!instance || !controlsVisibility) return\n instance.setControlsVisibility(controlsVisibility)\n }, [instance, controlsVisibility])\n\n // 处理属性更新\n useDidUpdate(() => {\n const { canvas } = store.getState()\n if (!instance || !canvas) return\n\n instance.set(attributes)\n instance.setCoords()\n canvas.requestRenderAll()\n }, [attributes])\n\n return (\n <>\n {instance &&\n Children.map(children, child => {\n if (isValidElement(child)) {\n return cloneElement(child, {\n group: instance,\n })\n }\n return null\n })}\n </>\n )\n})\n\nexport default Group\n"],"names":["Group","memo","children","controlsVisibility","props","store","useStoreApi","instance","setInstance","useState","listeners","attributes","useSplitProps","useLayoutEffect","newInstance","BaseGroup","canvas","useEffect","bindEvents","useDidUpdate","jsx","Fragment","Children","child","isValidElement","cloneElement"],"mappings":"mTAwBMA,EAAQC,EAAAA,KAAK,CAAC,CAAE,SAAAC,EAAU,mBAAAC,EAAoB,GAAGC,CAAM,IAAoB,CAC/E,MAAMC,EAAQC,gBACR,CAACC,EAAUC,CAAW,EAAIC,EAAAA,SAA2B,IAAI,EAEzD,CAACC,EAAWC,CAAU,EAAIC,EAAAA,cAAcR,CAAK,EAGnD,OAAAS,EAAgB,gBAAA,IAAM,CACpB,MAAMC,EAAc,IAAIC,QAAU,CAAA,EAAI,CACpC,GAAGJ,CACL,CAAC,EACD,OAAAH,EAAYM,CAAW,EAEhB,IAAM,CACX,KAAM,CAAE,OAAAE,CAAO,EAAIX,EAAM,SAAS,EAC9BS,GAAeE,GACjBA,EAAO,OAAOF,CAAW,EAE3BN,EAAY,IAAI,CAClB,CAEF,EAAG,CAACH,CAAK,CAAC,EAGVY,EAAU,UAAA,IAAM,CACd,KAAM,CAAE,OAAAD,CAAO,EAAIX,EAAM,SAAS,EAC9B,CAACE,GAAY,CAACS,GAElBA,EAAO,IAAIT,CAAQ,CACrB,EAAG,CAACF,EAAOE,CAAQ,CAAC,EAGpBU,YAAU,IACHV,EAEWW,EAAAA,WAAWX,EAAUG,CAAS,EAF/B,OAId,CAACH,EAAUG,CAAS,CAAC,EAGxBO,EAAAA,UAAU,IAAM,CACV,CAACV,GAAY,CAACJ,GAClBI,EAAS,sBAAsBJ,CAAkB,CACnD,EAAG,CAACI,EAAUJ,CAAkB,CAAC,EAGjCgB,EAAAA,aAAa,IAAM,CACjB,KAAM,CAAE,OAAAH,CAAO,EAAIX,EAAM,SAAS,EAC9B,CAACE,GAAY,CAACS,IAElBT,EAAS,IAAII,CAAU,EACvBJ,EAAS,UACTS,EAAAA,EAAO,iBAAiB,EAC1B,EAAG,CAACL,CAAU,CAAC,EAGbS,EAAAA,IAAAC,EAAAA,SAAA,CACG,SAAAd,GACCe,EAAAA,SAAS,IAAIpB,EAAUqB,GACjBC,EAAAA,eAAeD,CAAK,EACfE,EAAAA,aAAaF,EAAO,CACzB,MAAOhB,CACT,CAAC,EAEI,IACR,CACL,CAAA,CAEJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var v=require("fabric"),e=require("react"),l=require("../../hooks/useDidUpdate.cjs"),p=require("../../hooks/useStore.cjs");const R=e.forwardRef(({group:a,src:c,onLoad:o,...t},m)=>{const r=e.useRef(),i=e.useRef(o),u=p.useStoreApi(),d=e.useRef(t);return e.useEffect(()=>{i.current=o,d.current=t}),e.useEffect(()=>{const{canvas:n}=u.getState();if(!n?.getElement())return;const f=a??n;return v.FabricImage.fromURL(c,{crossOrigin:"anonymous"}).then(s=>{r.current=s,s.set(d.current),i.current?.(s),f.add(s)}),()=>{r.current&&f?.remove(r.current)}},[c,u,a]),l.useDidUpdate(()=>{const{canvas:n}=u.getState();r.current&&(r.current.set(t),r.current.setCoords(),n?.requestRenderAll())},[t,u]),e.useImperativeHandle(m,()=>({instance:r.current})),null});var g=e.memo(R);exports.default=g;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Image/index.tsx"],"sourcesContent":["import type { ImageProps as FabricImageProps, ObjectEvents, SerializedImageProps, Canvas } from 'fabric'\nimport { FabricImage } from 'fabric'\nimport type { Group as BaseGroup } from 'fabric'\nimport { forwardRef, memo, useEffect, useImperativeHandle, useRef } from 'react'\nimport { useDidUpdate } from '../../hooks/useDidUpdate'\nimport { useStoreApi } from '../../hooks/useStore'\n\nexport type Handle = {\n instance: FabricImage | undefined\n}\n\nexport type ImageProps = Partial<FabricImageProps> & {\n src: string\n group?: BaseGroup\n onLoad?: (imageSource: FabricImage<Partial<ImageProps>, SerializedImageProps, ObjectEvents>) => void\n}\n\nconst Image = forwardRef<Handle, ImageProps>(({ group, src, onLoad, ...options }, ref) => {\n const instanceRef = useRef<FabricImage>()\n const onLoadRef = useRef(onLoad)\n const store = useStoreApi()\n const optionsRef = useRef(options)\n\n // 更新 onLoadRef\n useEffect(() => {\n onLoadRef.current = onLoad\n optionsRef.current = options\n })\n\n // 只在 src 改变时初始化 image\n useEffect(() => {\n const { canvas } = store.getState()\n if (!canvas?.getElement()) return\n\n const parent = group ?? canvas\n\n FabricImage.fromURL(src, { crossOrigin: 'anonymous' }).then(imageSource => {\n instanceRef.current = imageSource\n imageSource.set(optionsRef.current)\n onLoadRef.current?.(imageSource)\n parent.add(imageSource)\n })\n\n return () => {\n if (instanceRef.current) {\n parent?.remove(instanceRef.current)\n }\n }\n }, [src, store, group]) // 只包含初始化需要的依赖\n\n // 只在 options 更新时执行\n useDidUpdate(() => {\n const { canvas } = store.getState()\n\n if (instanceRef.current) {\n instanceRef.current.set(options)\n instanceRef.current.setCoords()\n canvas?.requestRenderAll()\n }\n }, [options, store])\n\n useImperativeHandle(ref, () => ({\n instance: instanceRef.current,\n }))\n\n return null\n})\n\nexport default memo(Image)\n"],"names":["Image","forwardRef","group","src","onLoad","options","ref","instanceRef","useRef","onLoadRef","store","useStoreApi","optionsRef","useEffect","canvas","parent","FabricImage","imageSource","useDidUpdate","useImperativeHandle","memo"],"mappings":"+LAiBA,MAAMA,EAAQC,EAAAA,WAA+B,CAAC,CAAE,MAAAC,EAAO,IAAAC,EAAK,OAAAC,EAAQ,GAAGC,CAAQ,EAAGC,IAAQ,CACxF,MAAMC,EAAcC,EAAAA,OAAoB,EAClCC,EAAYD,EAAAA,OAAOJ,CAAM,EACzBM,EAAQC,EAAAA,YACRC,EAAAA,EAAaJ,SAAOH,CAAO,EAGjC,OAAAQ,EAAU,UAAA,IAAM,CACdJ,EAAU,QAAUL,EACpBQ,EAAW,QAAUP,CACvB,CAAC,EAGDQ,EAAAA,UAAU,IAAM,CACd,KAAM,CAAE,OAAAC,CAAO,EAAIJ,EAAM,SAAS,EAClC,GAAI,CAACI,GAAQ,WAAc,EAAA,OAE3B,MAAMC,EAASb,GAASY,EAExB,OAAAE,EAAAA,YAAY,QAAQb,EAAK,CAAE,YAAa,WAAY,CAAC,EAAE,KAAKc,GAAe,CACzEV,EAAY,QAAUU,EACtBA,EAAY,IAAIL,EAAW,OAAO,EAClCH,EAAU,UAAUQ,CAAW,EAC/BF,EAAO,IAAIE,CAAW,CACxB,CAAC,EAEM,IAAM,CACPV,EAAY,SACdQ,GAAQ,OAAOR,EAAY,OAAO,CAEtC,CACF,EAAG,CAACJ,EAAKO,EAAOR,CAAK,CAAC,EAGtBgB,EAAAA,aAAa,IAAM,CACjB,KAAM,CAAE,OAAAJ,CAAO,EAAIJ,EAAM,WAErBH,EAAY,UACdA,EAAY,QAAQ,IAAIF,CAAO,EAC/BE,EAAY,QAAQ,UAAA,EACpBO,GAAQ,iBAEZ,EAAA,EAAG,CAACT,EAASK,CAAK,CAAC,EAEnBS,EAAAA,oBAAoBb,EAAK,KAAO,CAC9B,SAAUC,EAAY,OACxB,EAAE,EAEK,IACT,CAAC,EAED,IAAea,EAAAA,EAAAA,KAAKpB,CAAK"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var c=require("fabric"),e=require("react"),v=require("../../hooks/useCreateObject.cjs"),b=require("../../hooks/useSplitProps.cjs");const f=e.forwardRef(({group:t,x1:u,y1:a,x2:s,y2:i,...o},l)=>{const[n,p]=b.useSplitProps(o),r=v.useCreateObject({Constructor:c.Line,param:[u,a,s,i],attributes:p,group:t,listeners:n});return e.useImperativeHandle(l,()=>r,[r]),null});var m=e.memo(f);exports.default=m;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Line/index.tsx"],"sourcesContent":["import type { Group as BaseGroup } from 'fabric'\nimport { Line as BaseLine } from 'fabric'\nimport { forwardRef, memo, useImperativeHandle } from 'react'\nimport { useCreateObject } from '../../hooks/useCreateObject'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport type { AllObjectEvents } from '../../types/object'\n\nexport type Handle = BaseLine | undefined\n\nexport type LineProps<T = unknown> = Partial<ConstructorParameters<typeof BaseLine>[1] & AllObjectEvents> & {\n group?: BaseGroup\n path?: string\n} & T\n\nconst Line = forwardRef<Handle, LineProps>(({ group, x1, y1, x2, y2, ...props }, ref) => {\n const [listeners, attributes] = useSplitProps(props)\n\n const instance = useCreateObject({\n Constructor: BaseLine,\n param: [x1, y1, x2, y2],\n attributes,\n group,\n listeners,\n })\n\n useImperativeHandle(ref, () => instance, [instance])\n\n return null\n})\n\nexport default memo(Line)\n"],"names":["Line","forwardRef","group","x1","y1","x2","y2","props","ref","listeners","attributes","useSplitProps","instance","useCreateObject","BaseLine","useImperativeHandle","memo"],"mappings":"uMAcA,MAAMA,EAAOC,aAA8B,CAAC,CAAE,MAAAC,EAAO,GAAAC,EAAI,GAAAC,EAAI,GAAAC,EAAI,GAAAC,EAAI,GAAGC,CAAM,EAAGC,IAAQ,CACvF,KAAM,CAACC,EAAWC,CAAU,EAAIC,gBAAcJ,CAAK,EAE7CK,EAAWC,EAAAA,gBAAgB,CAC/B,YAAaC,EAAAA,KACb,MAAO,CAACX,EAAIC,EAAIC,EAAIC,CAAE,EACtB,WAAAI,EACA,MAAAR,EACA,UAAAO,CACF,CAAC,EAED,OAAAM,EAAAA,oBAAoBP,EAAK,IAAMI,EAAU,CAACA,CAAQ,CAAC,EAE5C,IACT,CAAC,EAED,IAAeI,EAAAA,EAAAA,KAAKhB,CAAI"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),r=require("@ant-design/icons"),s=require("../../hooks/useStore.cjs");const i=()=>s.useStore(t=>t.loading)?e.jsxs("div",{className:"absolute inset-0 z-[40] flex flex-col items-center justify-center overflow-hidden",children:[e.jsx("div",{className:"absolute bottom-0 left-0 right-0 top-0 ]"}),e.jsx(r.LoadingOutlined,{spin:!0,className:"text-lg text-[var(--color-primary)]"})]}):null;exports.default=i;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Loading/index.tsx"],"sourcesContent":["import { LoadingOutlined } from '@ant-design/icons'\nimport { useStore } from '../../hooks/useStore'\n\n// bg-[hsla(221,14%,4%,0.6)\nconst Loading = () => {\n const loading = useStore(state => state.loading)\n\n return loading ? (\n <div className=\"absolute inset-0 z-[40] flex flex-col items-center justify-center overflow-hidden\">\n <div className=\"absolute bottom-0 left-0 right-0 top-0 ]\"></div>\n <LoadingOutlined spin className=\"text-lg text-[var(--color-primary)]\" />\n </div>\n ) : null\n}\n\nexport default Loading\n"],"names":["Loading","useStore","state","jsxs","jsx","LoadingOutlined"],"mappings":"4KAIA,MAAMA,EAAU,IACEC,EAAAA,SAASC,GAASA,EAAM,OAAO,EAG7CC,OAAC,OAAI,UAAU,oFACb,UAAAC,EAAC,IAAA,MAAA,CAAI,UAAU,0CAA2C,CAAA,EAC1DA,EAAAA,IAACC,EAAAA,gBAAA,CAAgB,KAAI,GAAC,UAAU,sCAAsC,CACxE,CAAA,CAAA,EACE"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var i=require("react-dom"),d=require("tailwind-merge"),u=require("react"),f=require("../../hooks/useStore.cjs");const v=a=>a.domNode?.querySelector(".react-fabric__canvas"),m=u.forwardRef(({children:a,className:l,onClick:n},t)=>{const o=f.useStore(v),e=u.useRef(),s=u.useCallback(r=>{r!==e.current&&(e.current=r||void 0,typeof t=="function"?t(r):t&&(t.current=r))},[t]);if(u.useEffect(()=>()=>{e.current&&(e.current.remove(),e.current=void 0)},[]),!o||e.current)return e.current?i.createPortal(a,e.current):null;const c=document.createElement("div");return c.className=d.twMerge("react-fabric__portal",l),n&&c.addEventListener("click",r=>{r.stopPropagation(),n(r)}),o.appendChild(c),s(c),i.createPortal(a,c)});exports.default=m;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/NodeToolbarPortal/index.tsx"],"sourcesContent":["import { createPortal } from 'react-dom'\nimport { twMerge } from 'tailwind-merge'\nimport type { ReactNode } from 'react'\nimport { forwardRef, useCallback, useEffect, useRef } from 'react'\nimport { useStore } from '../../hooks/useStore'\nimport type { ReactFabricState } from '../../types/store'\n\nconst selector = (state: ReactFabricState) => state.domNode?.querySelector('.react-fabric__canvas')\n\ninterface Props {\n children: ReactNode\n className?: string\n onClick?: (e: React.MouseEvent) => void\n}\n\nconst NodeToolbarPortal = forwardRef<HTMLDivElement, Props>(({ children, className, onClick }, forwardRef) => {\n const wrapperRef = useStore(selector)\n const containerRef = useRef<HTMLDivElement>()\n\n // 使用 useCallback 创建稳定的 ref 回调\n const refCallback = useCallback(\n (node: HTMLDivElement | null) => {\n if (node !== containerRef.current) {\n containerRef.current = node || undefined\n\n if (typeof forwardRef === 'function') {\n forwardRef(node)\n } else if (forwardRef) {\n forwardRef.current = node\n }\n }\n },\n [forwardRef],\n )\n\n // 组件卸载时清理\n useEffect(() => {\n return () => {\n if (containerRef.current) {\n containerRef.current.remove()\n containerRef.current = undefined\n }\n }\n }, [])\n\n // 如果已经有 container 或没有 wrapper,直接返回\n if (!wrapperRef || containerRef.current) {\n return containerRef.current ? createPortal(children, containerRef.current) : null\n }\n\n // 创建 DOM 元素\n const div = document.createElement('div')\n div.className = twMerge('react-fabric__portal', className)\n if (onClick) {\n div.addEventListener('click', e => {\n e.stopPropagation()\n onClick(e as any)\n })\n }\n wrapperRef.appendChild(div)\n refCallback(div)\n\n return createPortal(children, div)\n})\n\nexport default NodeToolbarPortal\n"],"names":["selector","state","NodeToolbarPortal","forwardRef","children","className","onClick","wrapperRef","useStore","containerRef","useRef","refCallback","useCallback","node","useEffect","createPortal","div","twMerge","e"],"mappings":"oLAOA,MAAMA,EAAYC,GAA4BA,EAAM,SAAS,cAAc,uBAAuB,EAQ5FC,EAAoBC,EAAkC,WAAA,CAAC,CAAE,SAAAC,EAAU,UAAAC,EAAW,QAAAC,CAAQ,EAAGH,IAAe,CAC5G,MAAMI,EAAaC,EAASR,SAAAA,CAAQ,EAC9BS,EAAeC,EAAAA,OAAAA,EAGfC,EAAcC,EAAAA,YACjBC,GAAgC,CAC3BA,IAASJ,EAAa,UACxBA,EAAa,QAAUI,GAAQ,OAE3B,OAAOV,GAAe,WACxBA,EAAWU,CAAI,EACNV,IACTA,EAAW,QAAUU,GAG3B,EACA,CAACV,CAAU,CACb,EAaA,GAVAW,EAAAA,UAAU,IACD,IAAM,CACPL,EAAa,UACfA,EAAa,QAAQ,SACrBA,EAAa,QAAU,OAE3B,EACC,CAAA,CAAE,EAGD,CAACF,GAAcE,EAAa,QAC9B,OAAOA,EAAa,QAAUM,EAAAA,aAAaX,EAAUK,EAAa,OAAO,EAAI,KAI/E,MAAMO,EAAM,SAAS,cAAc,KAAK,EACxC,OAAAA,EAAI,UAAYC,EAAAA,QAAQ,uBAAwBZ,CAAS,EACrDC,GACFU,EAAI,iBAAiB,QAASE,GAAK,CACjCA,EAAE,gBACFZ,EAAAA,EAAQY,CAAQ,CAClB,CAAC,EAEHX,EAAW,YAAYS,CAAG,EAC1BL,EAAYK,CAAG,EAERD,EAAAA,aAAaX,EAAUY,CAAG,CACnC,CAAC"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),l=require("react"),d=require("../Path/index.cjs"),u=require("../Text/index.cjs"),s=require("../Rect/index.cjs"),o=require("../Line/index.cjs");const x=({objects:r})=>{const a={rect:s.default,path:d.default,text:u.default,line:o.default,"i-text":u.default};return r?e.jsx(e.Fragment,{children:r?.map(({type:n,...i})=>{const t=a[n.toLowerCase()];return t?e.jsx(t,{...i}):null})}):null};var c=l.memo(x);exports.default=c;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Objects/index.tsx"],"sourcesContent":["import { memo } from 'react'\nimport Path from '../Path'\nimport Text from '../Text'\nimport Rect from '../Rect'\nimport Line from '../Line'\n\nexport type ObjectsProps = {\n objects: { type: string; [index: string]: any }[]\n}\n\nconst Objects = ({ objects }: ObjectsProps) => {\n const components = {\n rect: Rect,\n path: Path,\n text: Text,\n line: Line,\n 'i-text': Text,\n }\n\n if (!objects) return null\n return (\n <>\n {objects?.map(({ type, ...options }) => {\n // @ts-expect-error\n const Component = components[type.toLowerCase()]\n if (!Component) {\n \n return null\n }\n return <Component {...options} />\n })}\n </>\n )\n}\n\nexport default memo(Objects)\n"],"names":["Objects","objects","components","Rect","Path","Text","Line","jsx","Fragment","type","options","Component","memo"],"mappings":"sPAUA,MAAMA,EAAU,CAAC,CAAE,QAAAC,CAAQ,IAAoB,CAC7C,MAAMC,EAAa,CACjB,KAAMC,UACN,KAAMC,EAAAA,QACN,KAAMC,EAAAA,QACN,KAAMC,EAAAA,QACN,SAAUD,SACZ,EAEA,OAAKJ,EAEHM,MAAAC,EAAAA,SAAA,CACG,SAAAP,GAAS,IAAI,CAAC,CAAE,KAAAQ,EAAM,GAAGC,CAAQ,IAAM,CAEtC,MAAMC,EAAYT,EAAWO,EAAK,YAAA,CAAa,EAC/C,OAAKE,EAIEJ,EAACI,IAAAA,EAAA,CAAW,GAAGD,CAAAA,CAAS,EAFtB,IAGX,CAAC,CACH,CAAA,EAZmB,IAcvB,EAEA,IAAeE,EAAAA,EAAAA,KAAKZ,CAAO"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var p=require("fabric"),e=require("react"),l=require("../../hooks/useCreateObject.cjs"),c=require("../../hooks/useSplitProps.cjs");const n=e.forwardRef(({group:t,path:u="M 0 0",...a},s)=>{const[i,o]=c.useSplitProps(a),r=l.useCreateObject({Constructor:p.Path,param:u,attributes:o,group:t,listeners:i});return e.useImperativeHandle(s,()=>r,[r]),null});var v=e.memo(n);exports.default=v;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Path/index.tsx"],"sourcesContent":["import type { Group as BaseGroup } from 'fabric'\nimport { Path as BasePath } from 'fabric'\nimport { forwardRef, memo, useImperativeHandle } from 'react'\nimport { useCreateObject } from '../../hooks/useCreateObject'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport type { AllObjectEvents } from '../../types/object'\n\nexport type Handle = BasePath | undefined\n\nexport type PathProps<T = unknown> = Partial<ConstructorParameters<typeof BasePath>[1] & AllObjectEvents> & {\n group?: BaseGroup\n path?: string\n} & T\n\nconst Path = forwardRef<Handle, PathProps>(({ group, path = 'M 0 0', ...props }, ref) => {\n const [listeners, attributes] = useSplitProps(props)\n\n const instance = useCreateObject({\n Constructor: BasePath,\n param: path,\n attributes,\n group,\n listeners,\n })\n\n useImperativeHandle(ref, () => instance, [instance])\n\n return null\n})\n\nexport default memo(Path)\n"],"names":["Path","forwardRef","group","path","props","ref","listeners","attributes","useSplitProps","instance","useCreateObject","BasePath","useImperativeHandle","memo"],"mappings":"uMAcA,MAAMA,EAAOC,EAAAA,WAA8B,CAAC,CAAE,MAAAC,EAAO,KAAAC,EAAO,QAAS,GAAGC,CAAM,EAAGC,IAAQ,CACvF,KAAM,CAACC,EAAWC,CAAU,EAAIC,EAAcJ,cAAAA,CAAK,EAE7CK,EAAWC,EAAAA,gBAAgB,CAC/B,YAAaC,EAAAA,KACb,MAAOR,EACP,WAAAI,EACA,MAAAL,EACA,UAAAI,CACF,CAAC,EAED,OAAAM,EAAoBP,oBAAAA,EAAK,IAAMI,EAAU,CAACA,CAAQ,CAAC,EAE5C,IACT,CAAC,EAED,MAAeI,OAAKb,CAAI"}
@@ -0,0 +1,2 @@
1
+ "use strict";var n=require("react/jsx-runtime"),o=require("react"),u=require("../contexts/StoreContext.cjs"),c=require("../store/index.cjs");function s({initialNodes:e,defaultNodes:i,initialWidth:r,initialHeight:t,children:a}){const[d]=o.useState(()=>c.createStore({nodes:e,defaultNodes:i,width:r,height:t}));return n.jsx(u.Provider,{value:d,children:a})}exports.ReactFabricProvider=s;
2
+ //# sourceMappingURL=ReactFabricProvider.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactFabricProvider.cjs","sources":["../../../src/components/ReactFabricProvider.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { useState } from 'react'\nimport { Provider } from '../contexts/StoreContext'\nimport { createStore } from '../store'\nimport type { Node } from '../types/nodes'\n\nexport type ReactFabricProviderProps = {\n initialNodes?: Node[]\n defaultNodes?: Node[]\n initialWidth?: number\n initialHeight?: number\n children: ReactNode\n}\nexport function ReactFabricProvider({\n initialNodes: nodes,\n defaultNodes,\n initialWidth: width,\n initialHeight: height,\n children,\n}: ReactFabricProviderProps) {\n const [store] = useState(() =>\n createStore({\n nodes,\n defaultNodes,\n width,\n height,\n }),\n )\n\n return <Provider value={store}>{children}</Provider>\n}\n"],"names":["ReactFabricProvider","nodes","defaultNodes","width","height","children","store","useState","createStore","jsx","Provider"],"mappings":"6IAagB,SAAAA,EAAoB,CAClC,aAAcC,EACd,aAAAC,EACA,aAAcC,EACd,cAAeC,EACf,SAAAC,CACF,EAA6B,CAC3B,KAAM,CAACC,CAAK,EAAIC,EAAAA,SAAS,IACvBC,EAAAA,YAAY,CACV,MAAAP,EACA,aAAAC,EACA,MAAAC,EACA,OAAAC,CACF,CAAC,CACH,EAEA,OAAOK,EAAAA,IAACC,WAAA,CAAS,MAAOJ,EAAQ,SAAAD,CAAAA,CAAS,CAC3C"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var l=require("fabric"),e=require("react"),c=require("../../hooks/useCreateObject.cjs"),p=require("../../hooks/useSplitProps.cjs");const n=e.forwardRef(({group:t,...u},s)=>{const[a,i,o]=p.useSplitProps(u),r=c.useCreateObject({Constructor:l.Rect,defaultValues:o,attributes:i,group:t,listeners:a});return e.useImperativeHandle(s,()=>r,[r]),null});var v=e.memo(n);exports.default=v;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Rect/index.tsx"],"sourcesContent":["import type { Group as BaseGroup } from 'fabric'\nimport { Rect as BaseRect } from 'fabric'\nimport { forwardRef, memo, useImperativeHandle } from 'react'\nimport { useCreateObject } from '../../hooks/useCreateObject'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport type { AllObjectEvents } from '../../types/object'\n\nexport type Handle = BaseRect | undefined\n\nexport type RectProps<T = unknown> = Partial<BaseRect & AllObjectEvents> & {\n group?: BaseGroup\n defaultLeft?: number\n defaultTop?: number\n defaultWidth?: number\n defaultHeight?: number\n} & T\n\nconst Rect = forwardRef<Handle, RectProps>(({ group, ...props }, ref) => {\n const [listeners, attributes, defaultValues] = useSplitProps(props)\n\n const instance = useCreateObject({\n Constructor: BaseRect,\n defaultValues,\n attributes,\n group,\n listeners,\n })\n\n useImperativeHandle(ref, () => instance, [instance])\n\n return null\n})\n\nexport default memo(Rect)\n"],"names":["Rect","forwardRef","group","props","ref","listeners","attributes","defaultValues","useSplitProps","instance","useCreateObject","BaseRect","useImperativeHandle","memo"],"mappings":"uMAiBA,MAAMA,EAAOC,EAAAA,WAA8B,CAAC,CAAE,MAAAC,EAAO,GAAGC,CAAM,EAAGC,IAAQ,CACvE,KAAM,CAACC,EAAWC,EAAYC,CAAa,EAAIC,EAAAA,cAAcL,CAAK,EAE5DM,EAAWC,kBAAgB,CAC/B,YAAaC,EAAAA,KACb,cAAAJ,EACA,WAAAD,EACA,MAAAJ,EACA,UAAAG,CACF,CAAC,EAED,OAAAO,EAAAA,oBAAoBR,EAAK,IAAMK,EAAU,CAACA,CAAQ,CAAC,EAE5C,IACT,CAAC,EAED,MAAeI,OAAKb,CAAI"}
@@ -0,0 +1,2 @@
1
+ "use strict";var l=require("react"),g=require("zustand/shallow"),u=require("../../hooks/useStore.cjs");const D=["minManualZoom","maxManualZoom","width","height","zoomable","defaultCentered","defaultSelection","defaultDraggable","selection"],n=[...D],d=e=>({setMinManualZoom:e.setMinManualZoom,setMaxManualZoom:e.setMaxManualZoom,reset:e.reset,setDefaultSelection:e.setDefaultSelection,setDefaultDraggable:e.setDefaultDraggable}),r={};function Z(e){const{reset:s,setMinManualZoom:f,setMaxManualZoom:i,setDefaultSelection:c,setDefaultDraggable:m}=u.useStore(d,g.shallow),M=u.useStoreApi();l.useEffect(()=>(c(e.defaultSelection),m(e.defaultDraggable),()=>{o.current=r,s()}),[]);const o=l.useRef(r);return l.useEffect(()=>{for(const t of n){const a=e[t],S=o.current[t];a!==S&&(typeof e[t]>"u"||(t==="minManualZoom"?f(a):t==="maxManualZoom"?i(a):M.setState({[t]:a})))}o.current=e},n.map(t=>e[t])),null}exports.StoreUpdater=Z;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/StoreUpdater/index.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport { shallow } from 'zustand/shallow'\nimport type { ReactFabricProps } from '../../container/ReactFabric'\nimport { useStore, useStoreApi } from '../../hooks/useStore'\nimport type { Node } from '../../types/nodes'\nimport type { ReactFabricState } from '../../types/store'\n\nconst reactFabricFieldsToTrack = [\n 'minManualZoom',\n 'maxManualZoom',\n 'width',\n 'height',\n 'zoomable',\n 'defaultCentered',\n 'defaultSelection',\n 'defaultDraggable',\n 'selection',\n] as const\n\ntype ReactFabricFieldsToTrack = (typeof reactFabricFieldsToTrack)[number]\n\ntype StoreUpdaterProps = Pick<ReactFabricProps, ReactFabricFieldsToTrack>\n\nconst fieldsToTrack = [...reactFabricFieldsToTrack, ] as const\n\nconst selector = (s: ReactFabricState) => ({\n setMinManualZoom: s.setMinManualZoom,\n setMaxManualZoom: s.setMaxManualZoom,\n reset: s.reset,\n setDefaultSelection: s.setDefaultSelection,\n setDefaultDraggable: s.setDefaultDraggable,\n})\n\nconst initPrevValues = {}\n\nexport function StoreUpdater<NodeType extends Node = Node>(props: StoreUpdaterProps) {\n const { reset, setMinManualZoom, setMaxManualZoom, setDefaultSelection,setDefaultDraggable } = useStore(selector, shallow)\n const store = useStoreApi<NodeType>()\n\n useEffect(() => {\n setDefaultSelection(props.defaultSelection)\n setDefaultDraggable(props.defaultDraggable)\n\n return () => {\n previousFields.current = initPrevValues\n reset()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const previousFields = useRef<Partial<StoreUpdaterProps>>(initPrevValues)\n\n useEffect(\n () => {\n for (const fieldName of fieldsToTrack) {\n const fieldValue = props[fieldName]\n const previousFieldValue = previousFields.current[fieldName]\n\n if (fieldValue === previousFieldValue) continue\n if (typeof props[fieldName] === 'undefined') continue\n else if (fieldName === 'minManualZoom') setMinManualZoom(fieldValue as number)\n else if (fieldName === 'maxManualZoom') setMaxManualZoom(fieldValue as number)\n else store.setState({ [fieldName]: fieldValue })\n }\n previousFields.current = props\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n fieldsToTrack.map(fieldName => props[fieldName]),\n )\n\n return null\n}\n"],"names":["reactFabricFieldsToTrack","fieldsToTrack","selector","s","initPrevValues","StoreUpdater","props","reset","setMinManualZoom","setMaxManualZoom","setDefaultSelection","setDefaultDraggable","useStore","shallow","store","useStoreApi","useEffect","previousFields","useRef","fieldName","fieldValue","previousFieldValue"],"mappings":"uGAOA,MAAMA,EAA2B,CAC/B,gBACA,gBACA,QACA,SACA,WACA,kBACA,mBACA,mBACA,WACF,EAMMC,EAAgB,CAAC,GAAGD,CAA0B,EAE9CE,EAAYC,IAAyB,CACzC,iBAAkBA,EAAE,iBACpB,iBAAkBA,EAAE,iBACpB,MAAOA,EAAE,MACT,oBAAqBA,EAAE,oBACvB,oBAAqBA,EAAE,mBACzB,GAEMC,EAAiB,CAAA,EAEP,SAAAC,EAA2CC,EAA0B,CACnF,KAAM,CAAE,MAAAC,EAAO,iBAAAC,EAAkB,iBAAAC,EAAkB,oBAAAC,EAAoB,oBAAAC,CAAoB,EAAIC,EAAAA,SAASV,EAAUW,EAAAA,OAAO,EACnHC,EAAQC,EAAAA,YAAAA,EAEdC,EAAAA,UAAU,KACRN,EAAoBJ,EAAM,gBAAgB,EAC1CK,EAAoBL,EAAM,gBAAgB,EAEnC,IAAM,CACXW,EAAe,QAAUb,EACzBG,EACF,CAAA,GAEC,CAAE,CAAA,EAEL,MAAMU,EAAiBC,EAAAA,OAAmCd,CAAc,EAExE,OAAAY,YACE,IAAM,CACJ,UAAWG,KAAalB,EAAe,CACrC,MAAMmB,EAAad,EAAMa,CAAS,EAC5BE,EAAqBJ,EAAe,QAAQE,CAAS,EAEvDC,IAAeC,IACf,OAAOf,EAAMa,CAAS,EAAM,MACvBA,IAAc,gBAAiBX,EAAiBY,CAAoB,EACpED,IAAc,gBAAiBV,EAAiBW,CAAoB,EACxEN,EAAM,SAAS,CAAE,CAACK,CAAS,EAAGC,CAAW,CAAC,GACjD,CACAH,EAAe,QAAUX,CAC3B,EAEAL,EAAc,IAAIkB,GAAab,EAAMa,CAAS,CAAC,CACjD,EAEO,IACT"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var c=require("fabric"),t=require("react"),f=require("../../hooks/useCreateObject.cjs"),m=require("../../hooks/useSplitProps.cjs"),p=require("../../hooks/useStore.cjs"),v=require("fontfaceobserver");const F=t.forwardRef(({group:u,text:a,...i},o)=>{const s=p.useStoreApi(),[n,e]=m.useSplitProps(i),r=f.useCreateObject({Constructor:c.FabricText,param:a,attributes:e,group:u,listeners:n});return t.useEffect(()=>{const{canvas:l}=s.getState();!e.fontFamily||!r||new v(e.fontFamily).load().then(()=>{r.set({fontFamily:e.fontFamily}),l?.requestRenderAll()}).catch(()=>{console.error(`ReactFabric: \u5B57\u4F53\u52A0\u8F7D\u5931\u8D25: ${e.fontFamily}`)})},[e.fontFamily,r]),t.useImperativeHandle(o,()=>r,[r]),null});var d=t.memo(F);exports.default=d;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/components/Text/index.tsx"],"sourcesContent":["import type { Group as BaseGroup } from 'fabric'\nimport { FabricText } from 'fabric'\nimport { forwardRef, memo, useEffect, useImperativeHandle } from 'react'\nimport { useCreateObject } from '../../hooks/useCreateObject'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport { useStoreApi } from '../../hooks/useStore'\nimport type { AllObjectEvents } from '../../types/object'\nimport FontFaceObserver from 'fontfaceobserver'\n\nexport type Handle = FabricText | undefined\n\nexport type TextProps<T = unknown> = Partial<ConstructorParameters<typeof FabricText>[1] & AllObjectEvents> & {\n group?: BaseGroup\n text: string\n} & T\n\nconst Text = forwardRef<Handle, TextProps>(({ group, text, ...props }, ref) => {\n const store = useStoreApi()\n\n const [listeners, attributes] = useSplitProps(props)\n\n const instance = useCreateObject({\n Constructor: FabricText,\n param: text,\n attributes,\n group,\n listeners,\n })\n\n /**\n * 字体只有被使用了才会加载;先由 Text 使用一次, 监听加载完成,之后再 set 一次\n */\n useEffect(() => {\n const { canvas } = store.getState()\n if (!attributes.fontFamily || !instance) return\n const font = new FontFaceObserver(attributes.fontFamily)\n font\n .load()\n .then(() => {\n instance.set({\n fontFamily: attributes.fontFamily,\n })\n canvas?.requestRenderAll()\n })\n .catch(() => {\n // 加载失败\n console.error(`ReactFabric: 字体加载失败: ${attributes.fontFamily}`)\n })\n }, [attributes.fontFamily, instance])\n\n useImperativeHandle(ref, () => instance, [instance])\n\n return null\n})\n\nexport default memo(Text)\n"],"names":["Text","forwardRef","group","text","props","ref","store","useStoreApi","listeners","attributes","useSplitProps","instance","useCreateObject","FabricText","useEffect","canvas","FontFaceObserver","useImperativeHandle","memo"],"mappings":"2QAgBA,MAAMA,EAAOC,EAAAA,WAA8B,CAAC,CAAE,MAAAC,EAAO,KAAAC,EAAM,GAAGC,CAAM,EAAGC,IAAQ,CAC7E,MAAMC,EAAQC,EAAAA,cAER,CAACC,EAAWC,CAAU,EAAIC,EAAAA,cAAcN,CAAK,EAE7CO,EAAWC,kBAAgB,CAC/B,YAAaC,aACb,MAAOV,EACP,WAAAM,EACA,MAAAP,EACA,UAAAM,CACF,CAAC,EAKD,OAAAM,EAAAA,UAAU,IAAM,CACd,KAAM,CAAE,OAAAC,CAAO,EAAIT,EAAM,SAAS,EAC9B,CAACG,EAAW,YAAc,CAACE,GAClB,IAAIK,EAAiBP,EAAW,UAAU,EAEpD,OACA,KAAK,IAAM,CACVE,EAAS,IAAI,CACX,WAAYF,EAAW,UACzB,CAAC,EACDM,GAAQ,iBAAA,CACV,CAAC,EACA,MAAM,IAAM,CAEX,QAAQ,MAAM,sDAAwBN,EAAW,UAAU,EAAE,CAC/D,CAAC,CACL,EAAG,CAACA,EAAW,WAAYE,CAAQ,CAAC,EAEpCM,EAAAA,oBAAoBZ,EAAK,IAAMM,EAAU,CAACA,CAAQ,CAAC,EAE5C,IACT,CAAC,EAED,MAAeO,OAAKlB,CAAI"}
@@ -0,0 +1,2 @@
1
+ "use strict";const r={error001:()=>"[React Fabric]: \u7F3A\u5931 zustand Provider \u7956\u5148\u5143\u7D20",error004:()=>"dom\u5143\u7D20\u6CA1\u6709\u5BBD\u9AD8"};exports.errorMessages=r;
2
+ //# sourceMappingURL=constants.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.cjs","sources":["../../src/constants.ts"],"sourcesContent":["export const errorMessages = {\n error001: () => '[React Fabric]: 缺失 zustand Provider 祖先元素',\n error004: () => 'dom元素没有宽高',\n}\n"],"names":["errorMessages"],"mappings":"aAAO,MAAMA,EAAgB,CAC3B,SAAU,IAAM,yEAChB,SAAU,IAAM,yCAClB"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("react/jsx-runtime"),n=require("react"),u=require("../../components/ReactFabricProvider.cjs"),o=require("../../contexts/StoreContext.cjs");function s({children:r,nodes:i,defaultNodes:t,width:a,height:d}){return n.useContext(o.default)?e.jsx(e.Fragment,{children:r}):e.jsx(u.ReactFabricProvider,{initialNodes:i,defaultNodes:t,initialWidth:a,initialHeight:d,children:r})}exports.Wrapper=s;
2
+ //# sourceMappingURL=Wrapper.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Wrapper.cjs","sources":["../../../../src/container/ReactFabric/Wrapper.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { useContext } from 'react'\nimport { ReactFabricProvider } from '../../components/ReactFabricProvider'\nimport StoreContext from '../../contexts/StoreContext'\nimport type { Node } from '../../types/nodes'\n\nexport function Wrapper({\n children,\n nodes,\n defaultNodes,\n width,\n height,\n}: {\n children: ReactNode\n nodes?: Node[]\n defaultNodes?: Node[]\n width?: number\n height?: number\n}) {\n const isWrapped = useContext(StoreContext)\n\n if (isWrapped) {\n return <>{children}</>\n }\n\n return (\n <ReactFabricProvider initialNodes={nodes} defaultNodes={defaultNodes} initialWidth={width} initialHeight={height}>\n {children}\n </ReactFabricProvider>\n )\n}\n"],"names":["Wrapper","children","nodes","defaultNodes","width","height","useContext","StoreContext","jsx","Fragment","ReactFabricProvider"],"mappings":"sKAMgB,SAAAA,EAAQ,CACtB,SAAAC,EACA,MAAAC,EACA,aAAAC,EACA,MAAAC,EACA,OAAAC,CACF,EAMG,CAGD,OAFkBC,EAAWC,WAAAA,EAAY,OAAA,EAGhCC,EAAAC,IAAAA,EAAAA,SAAA,CAAG,SAAAR,CAAS,CAAA,EAInBO,MAACE,EAAA,oBAAA,CAAoB,aAAcR,EAAO,aAAcC,EAAc,aAAcC,EAAO,cAAeC,EACvG,SAAAJ,CAAAA,CACH,CAEJ"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),a=require("react"),j=require("./Wrapper.cjs"),w=require("../../components/Canvas/index.cjs"),q=require("../../components/Loading/index.cjs"),y=require("../../components/StoreUpdater/index.cjs");const D={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0},W=a.forwardRef(({minManualZoom:r,maxManualZoom:i,className:l,children:t,width:n,height:u,selection:d,style:s,onMouseWheel:c,onMouseDown:m,onMouseMove:h,onMouseUp:f,zoomable:M,defaultSelection:v,defaultCentered:x,defaultDraggable:p,...b},g)=>e.jsx("div",{style:{...s,...D},ref:g,className:`react-fabric ${l||""}`,children:e.jsxs(j.Wrapper,{width:n,height:u,children:[e.jsx(y.StoreUpdater,{minManualZoom:r,maxManualZoom:i,zoomable:M,defaultCentered:x,selection:d,defaultSelection:v,defaultDraggable:p}),e.jsx(w.default,{onMouseDown:m,onMouseMove:h,onMouseUp:f,onMouseWheel:c,...b,children:t}),e.jsx(q.default,{})]})})),o=a.memo(W);o.displayName="ReactFabric",exports.default=o;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/container/ReactFabric/index.tsx"],"sourcesContent":["import type { CSSProperties } from 'react'\nimport { forwardRef, memo } from 'react'\n\n// 自定义基础事件类型\ntype FabricEvent = {\n e: Event\n pointer: { x: number; y: number }\n target?: unknown\n subTargets?: unknown[]\n button?: number\n isClick?: boolean\n transform?: unknown\n}\n\n// 重新定义 CanvasProps,不再从 components/Canvas 导入\ntype BaseCanvasProps = {\n width?: number\n height?: number\n selection?: boolean\n children?: React.ReactNode\n // 添加其他必要的 Canvas 属性\n}\n\nimport { Wrapper } from './Wrapper'\n\nimport Canvas from '../../components/Canvas'\nimport Loading from '../../components/Loading'\nimport { StoreUpdater } from '../../components/StoreUpdater'\n\nconst wrapperStyle: CSSProperties = {\n width: '100%',\n height: '100%',\n overflow: 'hidden',\n position: 'relative',\n zIndex: 0,\n}\n\nexport type ReactFabricProps = BaseCanvasProps & {\n style?: CSSProperties\n className?: string\n zoomable?: boolean\n minManualZoom?: number\n maxManualZoom?: number\n /** 背景图是否默认居中显示\n * @default false\n */\n defaultCentered?: boolean\n /** 是否启用选择功能\n * @default true\n * */\n defaultSelection?: boolean\n /**\n * 是否默认可拖拽\n */\n defaultDraggable?: boolean\n onMouseDown?: (e: FabricEvent) => void\n onMouseMove?: (e: FabricEvent) => void\n onMouseUp?: (e: FabricEvent) => void\n onMouseWheel?: (e: FabricEvent) => void\n}\n\nconst ForwardReactFabric = forwardRef<HTMLDivElement, ReactFabricProps>(\n (\n {\n minManualZoom,\n maxManualZoom,\n className,\n children,\n width,\n height,\n selection,\n style,\n onMouseWheel,\n onMouseDown,\n onMouseMove,\n onMouseUp,\n zoomable,\n defaultSelection,\n defaultCentered,\n defaultDraggable,\n ...rest\n },\n ref,\n ) => {\n return (\n <div style={{ ...style, ...wrapperStyle }} ref={ref} className={`react-fabric ${className || ''}`}>\n <Wrapper width={width} height={height}>\n <StoreUpdater\n minManualZoom={minManualZoom}\n maxManualZoom={maxManualZoom}\n zoomable={zoomable}\n defaultCentered={defaultCentered}\n selection={selection}\n defaultSelection={defaultSelection}\n defaultDraggable={defaultDraggable}\n />\n\n <Canvas\n onMouseDown={onMouseDown}\n onMouseMove={onMouseMove}\n onMouseUp={onMouseUp}\n onMouseWheel={onMouseWheel}\n {...rest}\n >\n {children}\n </Canvas>\n <Loading />\n </Wrapper>\n </div>\n )\n },\n)\n\ntype ReactFabricComponent = React.MemoExoticComponent<typeof ForwardReactFabric>\n\nconst ReactFabric: ReactFabricComponent = memo(ForwardReactFabric)\nReactFabric.displayName = 'ReactFabric'\n\nexport default ReactFabric\n"],"names":["wrapperStyle","ForwardReactFabric","forwardRef","minManualZoom","maxManualZoom","className","children","width","height","selection","style","onMouseWheel","onMouseDown","onMouseMove","onMouseUp","zoomable","defaultSelection","defaultCentered","defaultDraggable","rest","ref","jsx","jsxs","Wrapper","StoreUpdater","Canvas","Loading","ReactFabric","memo"],"mappings":"ySA6BMA,MAAAA,EAA8B,CAClC,MAAO,OACP,OAAQ,OACR,SAAU,SACV,SAAU,WACV,OAAQ,CACV,EA0BMC,EAAqBC,EAAAA,WACzB,CACE,CACE,cAAAC,EACA,cAAAC,EACA,UAAAC,EACA,SAAAC,EACA,MAAAC,EACA,OAAAC,EACA,UAAAC,EACA,MAAAC,EACA,aAAAC,EACA,YAAAC,EACA,YAAAC,EACA,UAAAC,EACA,SAAAC,EACA,iBAAAC,EACA,gBAAAC,EACA,iBAAAC,EACA,GAAGC,CACL,EACAC,IAGEC,EAAAA,IAAC,MAAI,CAAA,MAAO,CAAE,GAAGX,EAAO,GAAGV,CAAa,EAAG,IAAKoB,EAAK,UAAW,gBAAgBf,GAAa,EAAE,GAC7F,SAAAiB,EAAAA,KAACC,UAAA,CAAQ,MAAOhB,EAAO,OAAQC,EAC7B,SAAAa,CAAAA,MAACG,EAAAA,aAAA,CACC,cAAerB,EACf,cAAeC,EACf,SAAUW,EACV,gBAAiBE,EACjB,UAAWR,EACX,iBAAkBO,EAClB,iBAAkBE,CAAAA,CACpB,EAEAG,EAACI,IAAAA,EAAAA,QAAA,CACC,YAAab,EACb,YAAaC,EACb,UAAWC,EACX,aAAcH,EACb,GAAGQ,EAEH,SAAAb,EACH,EACAe,EAAAA,IAACK,EAAA,QAAA,CAAQ,CAAA,GACX,CACF,CAAA,CAGN,EAIMC,EAAoCC,OAAK3B,CAAkB,EACjE0B,EAAY,YAAc"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var r=require("react");const e=r.createContext(null),t=e.Provider;exports.Provider=t,exports.default=e;
2
+ //# sourceMappingURL=StoreContext.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StoreContext.cjs","sources":["../../../src/contexts/StoreContext.ts"],"sourcesContent":["import { createContext } from 'react'\n\nimport type { createStore } from '../store'\n\nconst StoreContext = createContext<ReturnType<typeof createStore> | null>(null)\n\nexport const Provider = StoreContext.Provider\nexport default StoreContext\n"],"names":["StoreContext","createContext","Provider"],"mappings":"2FAIA,MAAMA,EAAeC,EAAAA,cAAqD,IAAI,EAEjEC,EAAWF,EAAa"}
@@ -0,0 +1,2 @@
1
+ "use strict";var n=require("react"),q=require("../utils/events.cjs"),m=require("../utils/props.cjs"),j=require("./useDidUpdate.cjs"),h=require("./useStore.cjs");function C({Constructor:o,attributes:u,defaultValues:i={},param:c,group:a,listeners:l={}}){const d=h.useStoreApi(),f=h.useStore(t=>t.canvas),p=n.useRef(null),e=n.useMemo(()=>{const t=Object.keys(i).length>0;p.current=t?"uncontrolled":"controlled";const s={...u,...m.transformDefaultProps(i)};return c!==void 0?new o(c,s):new o(s)},[o,c]),r=n.useMemo(()=>a??f,[a,f]);return n.useEffect(()=>{if(!(!e||!r))return r.add(e),()=>{try{e&&(e.off(),r.contains(e)&&r.remove(e),typeof e.dispose=="function"&&e.dispose())}catch(t){console.warn("\u6E05\u7406\u5BF9\u8C61\u65F6\u53D1\u751F\u9519\u8BEF:",t)}}},[e,r]),n.useEffect(()=>{if(e)return q.bindEvents(e,l)},[e,l]),j.useDidUpdate(()=>{if(!e)return;const{canvas:t}=d.getState();if(p.current==="uncontrolled"){const s=Object.entries(u).reduce((v,[g,b])=>(g.match(/^(left|top|width|height|scaleX|scaleY|angle|points|path|originX|originY)$/)||(v[g]=b),v),{});Object.keys(s).length>0&&(e.set(s),t?.requestRenderAll())}else e.set(u),e.setCoords(),t?.requestRenderAll()},[e,u,d]),e}exports.useCreateObject=C;
2
+ //# sourceMappingURL=useCreateObject.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCreateObject.cjs","sources":["../../../src/hooks/useCreateObject.ts"],"sourcesContent":["import type { FabricObject, Group } from 'fabric'\nimport { useEffect, useMemo, useRef } from 'react'\nimport type { AllObjectEvents } from '../types/object'\nimport { bindEvents } from '../utils/events'\nimport { transformDefaultProps } from '../utils/props'\nimport { useDidUpdate } from './useDidUpdate'\nimport { useStore, useStoreApi } from './useStore'\n\ntype SingleParamConstructor<T> = new (attributes: any) => T\ntype DualParamConstructor<T, P> = new (param: P, attributes: any) => T\n\ntype CreateObjectProps<T extends FabricObject, P = any> = {\n Constructor: SingleParamConstructor<T> | DualParamConstructor<T, P>\n attributes: any\n defaultValues?: any\n param?: P\n group?: Group\n listeners?: Partial<AllObjectEvents>\n}\n\nexport function useCreateObject<T extends FabricObject, P = any>({\n Constructor,\n attributes,\n defaultValues = {},\n param,\n group,\n listeners = {},\n}: CreateObjectProps<T, P>) {\n const store = useStoreApi()\n const canvas = useStore( state => state.canvas)\n\n\n // 使用 ref 记录控制模式\n const modeRef = useRef<'controlled' | 'uncontrolled' | null>(null)\n\n // 在创建实例时确定控制模式\n const instance = useMemo(() => {\n const hasUncontrolledProps = Object.keys(defaultValues).length > 0\n modeRef.current = hasUncontrolledProps ? 'uncontrolled' : 'controlled'\n\n const mergedAttributes = {\n ...attributes,\n ...transformDefaultProps(defaultValues),\n }\n\n const newInstance =\n param !== undefined\n ? new (Constructor as DualParamConstructor<T, P>)(param, mergedAttributes)\n : new (Constructor as SingleParamConstructor<T>)(mergedAttributes)\n return newInstance\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [Constructor, param])\n\n // 计算父容器(group 或 canvas)\n const parent = useMemo(() => group ?? canvas, [group, canvas])\n\n // 处理实例的添加和移除\n useEffect(() => {\n if (!instance || !parent) return\n // 添加到父容器\n parent.add(instance)\n\n return () => {\n try {\n if (instance) {\n // 移除所有事件监听\n instance.off()\n\n // 从父容器中移除\n if (parent.contains(instance)) {\n parent.remove(instance)\n }\n\n // 如果存在 dispose 方法则调用\n if (typeof instance.dispose === 'function') {\n instance.dispose()\n }\n }\n } catch (error) {\n console.warn('清理对象时发生错误:', error)\n }\n }\n }, [instance, parent])\n\n // 处理事件绑定\n useEffect(() => {\n if (!instance) return\n return bindEvents(instance, listeners)\n }, [instance, listeners])\n\n // 处理属性更新\n useDidUpdate(() => {\n if (!instance) return\n const { canvas } = store.getState()\n\n // 在非受控模式下,跳过位置相关属性的更新\n if (modeRef.current === 'uncontrolled') {\n const styleUpdates = Object.entries(attributes).reduce(\n (acc, [key, value]) => {\n if (!key.match(/^(left|top|width|height|scaleX|scaleY|angle|points|path|originX|originY)$/)) {\n acc[key] = value\n }\n return acc\n },\n {} as Record<string, any>,\n )\n\n if (Object.keys(styleUpdates).length > 0) {\n instance.set(styleUpdates)\n canvas?.requestRenderAll()\n }\n } else {\n // 受控模式:正常更新所有属性\n instance.set(attributes)\n instance.setCoords()\n canvas?.requestRenderAll()\n }\n }, [instance, attributes, store])\n\n return instance\n}\n"],"names":["useCreateObject","Constructor","attributes","defaultValues","param","group","listeners","store","useStoreApi","canvas","useStore","state","modeRef","useRef","instance","useMemo","hasUncontrolledProps","mergedAttributes","transformDefaultProps","parent","useEffect","error","bindEvents","useDidUpdate","styleUpdates","acc","key","value"],"mappings":"iKAoBO,SAASA,EAAiD,CAC/D,YAAAC,EACA,WAAAC,EACA,cAAAC,EAAgB,GAChB,MAAAC,EACA,MAAAC,EACA,UAAAC,EAAY,CAAA,CACd,EAA4B,CAC1B,MAAMC,EAAQC,cACRC,EAAAA,EAASC,EAAAA,SAAUC,GAASA,EAAM,MAAM,EAIxCC,EAAUC,EAAAA,OAA6C,IAAI,EAG3DC,EAAWC,UAAQ,IAAM,CAC7B,MAAMC,EAAuB,OAAO,KAAKb,CAAa,EAAE,OAAS,EACjES,EAAQ,QAAUI,EAAuB,eAAiB,aAE1D,MAAMC,EAAmB,CACvB,GAAGf,EACH,GAAGgB,EAAAA,sBAAsBf,CAAa,CACxC,EAMA,OAHEC,IAAU,OACN,IAAKH,EAA2CG,EAAOa,CAAgB,EACvE,IAAKhB,EAA0CgB,CAAgB,CAGvE,EAAG,CAAChB,EAAaG,CAAK,CAAC,EAGjBe,EAASJ,EAAAA,QAAQ,IAAMV,GAASI,EAAQ,CAACJ,EAAOI,CAAM,CAAC,EAG7D,OAAAW,EAAAA,UAAU,IAAM,CACd,GAAI,EAAA,CAACN,GAAY,CAACK,GAElB,OAAAA,EAAO,IAAIL,CAAQ,EAEZ,IAAM,CACX,GAAI,CACEA,IAEFA,EAAS,IAGLK,EAAAA,EAAO,SAASL,CAAQ,GAC1BK,EAAO,OAAOL,CAAQ,EAIpB,OAAOA,EAAS,SAAY,YAC9BA,EAAS,QAAQ,EAGvB,OAASO,EAAO,CACd,QAAQ,KAAK,0DAAcA,CAAK,CAClC,CACF,CACF,EAAG,CAACP,EAAUK,CAAM,CAAC,EAGrBC,YAAU,IAAM,CACd,GAAKN,EACL,OAAOQ,aAAWR,EAAUR,CAAS,CACvC,EAAG,CAACQ,EAAUR,CAAS,CAAC,EAGxBiB,eAAa,IAAM,CACjB,GAAI,CAACT,EAAU,OACf,KAAM,CAAE,OAAAL,CAAO,EAAIF,EAAM,SAGzB,EAAA,GAAIK,EAAQ,UAAY,eAAgB,CACtC,MAAMY,EAAe,OAAO,QAAQtB,CAAU,EAAE,OAC9C,CAACuB,EAAK,CAACC,EAAKC,CAAK,KACVD,EAAI,MAAM,2EAA2E,IACxFD,EAAIC,CAAG,EAAIC,GAENF,GAET,EACF,EAEI,OAAO,KAAKD,CAAY,EAAE,OAAS,IACrCV,EAAS,IAAIU,CAAY,EACzBf,GAAQ,mBAEZ,MAEEK,EAAS,IAAIZ,CAAU,EACvBY,EAAS,UAAA,EACTL,GAAQ,iBAEZ,CAAA,EAAG,CAACK,EAAUZ,EAAYK,CAAK,CAAC,EAEzBO,CACT"}
@@ -0,0 +1,2 @@
1
+ "use strict";var r=require("react");function s(t,u){const e=r.useRef(!1);r.useEffect(()=>{if(e.current)return t();e.current=!0},u)}exports.useDidUpdate=s;
2
+ //# sourceMappingURL=useDidUpdate.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDidUpdate.cjs","sources":["../../../src/hooks/useDidUpdate.ts"],"sourcesContent":["import type { DependencyList, EffectCallback } from 'react'\nimport { useEffect, useRef } from 'react'\n\n/**\n * 只在依赖更新时执行的 effect hook\n * @param effect 要执行的副作用函数\n * @param deps 依赖数组\n * https://mantine.dev/hooks/use-did-update/\n */\nexport function useDidUpdate(effect: EffectCallback, deps?: DependencyList): void {\n const didMount = useRef(false)\n\n useEffect(() => {\n if (didMount.current) {\n return effect()\n }\n\n didMount.current = true\n return undefined\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n}\n"],"names":["useDidUpdate","effect","deps","didMount","useRef","useEffect"],"mappings":"oCASO,SAASA,EAAaC,EAAwBC,EAA6B,CAChF,MAAMC,EAAWC,SAAO,EAAK,EAE7BC,YAAU,IAAM,CACd,GAAIF,EAAS,QACX,OAAOF,EAAO,EAGhBE,EAAS,QAAU,EAIrB,EAAGD,CAAI,CACT"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var g=require("zustand/shallow"),u=require("react"),v=require("./useStore.cjs");const b=r=>({canvas:r.canvas,draggable:r.draggable}),h=r=>{const e=r.get("selectable");return r.set("selectable",!1),()=>{r.set("selectable",e)}},p=r=>{if(!r)return()=>{};const e=r.getObjects().map(n=>h(n));return()=>{e.forEach(n=>n())}},w=()=>{const r=v.useStoreApi(),{canvas:e,draggable:n}=v.useStore(b,g.shallow),o=u.useRef(0),l=u.useRef(0),a=u.useRef(!1);return u.useEffect(()=>{if(!n)return;const d=p(e),i=({e:t})=>{const c=t.changedTouches?.[0];a.current=!0,o.current=t.clientX??c?.clientX,l.current=t.clientY??c?.clientY},f=({e:t})=>{if(a.current&&e){var c=e.viewportTransform;const s=t.changedTouches?.[0];c[4]+=(t.clientX??s?.clientX)-o.current,c[5]+=(t.clientY??s?.clientY)-l.current,e.requestRenderAll(),o.current=t.clientX??s?.clientX,l.current=t.clientY??s?.clientY}},m=t=>{e&&(e.setViewportTransform(e.viewportTransform),a.current=!1)};return e?.on("mouse:down",i),e?.on("mouse:move",f),e?.on("mouse:up",m),()=>{d(),e?.off("mouse:down",i),e?.off("mouse:move",f),e?.off("mouse:up",m)}},[e,n,r]),null};exports.default=w;
2
+ //# sourceMappingURL=useDraggable.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDraggable.cjs","sources":["../../../src/hooks/useDraggable.ts"],"sourcesContent":["import type { Canvas, FabricObject, TPointerEvent, TPointerEventInfo } from 'fabric'\nimport { shallow } from 'zustand/shallow'\nimport { useEffect, useRef } from 'react'\nimport type { ReactFabricState } from '../types/store'\nimport { useStore, useStoreApi } from './useStore'\n\nconst selector = (s: ReactFabricState) => ({\n canvas: s.canvas,\n draggable: s.draggable,\n})\n\nconst stageObject = (object: FabricObject) => {\n const raw = object.get('selectable')\n object.set('selectable', false)\n\n return () => {\n object.set('selectable', raw)\n }\n}\n\nconst stage = (canvas: Canvas | null) => {\n if (!canvas) return () => {}\n const objects = canvas.getObjects()\n\n const unStagedList = objects.map(object => {\n return stageObject(object)\n })\n\n return () => {\n unStagedList.forEach(fn => fn())\n }\n}\n\nconst useDraggable = () => {\n const store = useStoreApi()\n const { canvas, draggable } = useStore(selector, shallow)\n const lastPosXRef = useRef(0)\n const lastPosYRef = useRef(0)\n const isDraggingRef = useRef(false)\n\n useEffect(() => {\n if (!draggable) return\n const unStaged = stage(canvas)\n\n const onMouseDown = ({ e }: TPointerEventInfo<MouseEvent>) => {\n // @ts-expect-error 触摸屏拿不到clintX\n const touch = e.changedTouches?.[0]\n isDraggingRef.current = true\n lastPosXRef.current = e.clientX ?? touch?.clientX\n lastPosYRef.current = e.clientY ?? touch?.clientY\n }\n\n const onMouseMove = ({ e }: TPointerEventInfo<MouseEvent>) => {\n if (isDraggingRef.current && canvas) {\n var vpt = canvas.viewportTransform\n // @ts-expect-error 触摸屏拿不到clintX\n const touch = e.changedTouches?.[0]\n vpt[4] += (e.clientX ?? touch?.clientX) - lastPosXRef.current!\n vpt[5] += (e.clientY ?? touch?.clientY) - lastPosYRef.current!\n canvas.requestRenderAll()\n lastPosXRef.current = e.clientX ?? touch?.clientX\n lastPosYRef.current = e.clientY ?? touch?.clientY\n }\n }\n\n const onMouseUp = (_: TPointerEventInfo<TPointerEvent>) => {\n if (canvas) {\n canvas.setViewportTransform(canvas.viewportTransform)\n isDraggingRef.current = false\n }\n }\n\n canvas?.on('mouse:down', onMouseDown)\n canvas?.on('mouse:move', onMouseMove)\n canvas?.on('mouse:up', onMouseUp)\n\n return () => {\n unStaged()\n canvas?.off('mouse:down', onMouseDown)\n canvas?.off('mouse:move', onMouseMove)\n canvas?.off('mouse:up', onMouseUp)\n }\n }, [canvas, draggable, store])\n\n return null\n}\n\nexport default useDraggable\n"],"names":["selector","s","stageObject","object","raw","stage","canvas","unStagedList","fn","useDraggable","store","useStoreApi","draggable","useStore","shallow","lastPosXRef","useRef","lastPosYRef","isDraggingRef","useEffect","unStaged","onMouseDown","e","touch","onMouseMove","vpt","onMouseUp","_"],"mappings":"oJAMMA,MAAAA,EAAYC,IAAyB,CACzC,OAAQA,EAAE,OACV,UAAWA,EAAE,SACf,GAEMC,EAAeC,GAAyB,CAC5C,MAAMC,EAAMD,EAAO,IAAI,YAAY,EACnC,OAAAA,EAAO,IAAI,aAAc,EAAK,EAEvB,IAAM,CACXA,EAAO,IAAI,aAAcC,CAAG,CAC9B,CACF,EAEMC,EAASC,GAA0B,CACvC,GAAI,CAACA,EAAQ,MAAO,IAAM,CAAA,EAG1B,MAAMC,EAFUD,EAAO,aAEM,IAAIH,GACxBD,EAAYC,CAAM,CAC1B,EAED,MAAO,IAAM,CACXI,EAAa,QAAQC,GAAMA,EAAAA,CAAI,CACjC,CACF,EAEMC,EAAe,IAAM,CACzB,MAAMC,EAAQC,EAAY,YAAA,EACpB,CAAE,OAAAL,EAAQ,UAAAM,CAAU,EAAIC,EAASb,SAAAA,EAAUc,EAAO,OAAA,EAClDC,EAAcC,SAAO,CAAC,EACtBC,EAAcD,EAAAA,OAAO,CAAC,EACtBE,EAAgBF,EAAAA,OAAO,EAAK,EAElC,OAAAG,EAAAA,UAAU,IAAM,CACd,GAAI,CAACP,EAAW,OAChB,MAAMQ,EAAWf,EAAMC,CAAM,EAEvBe,EAAc,CAAC,CAAE,EAAAC,CAAE,IAAqC,CAE5D,MAAMC,EAAQD,EAAE,iBAAiB,CAAC,EAClCJ,EAAc,QAAU,GACxBH,EAAY,QAAUO,EAAE,SAAWC,GAAO,QAC1CN,EAAY,QAAUK,EAAE,SAAWC,GAAO,OAC5C,EAEMC,EAAc,CAAC,CAAE,EAAAF,CAAE,IAAqC,CAC5D,GAAIJ,EAAc,SAAWZ,EAAQ,CACnC,IAAImB,EAAMnB,EAAO,kBAEjB,MAAMiB,EAAQD,EAAE,iBAAiB,CAAC,EAClCG,EAAI,CAAC,IAAMH,EAAE,SAAWC,GAAO,SAAWR,EAAY,QACtDU,EAAI,CAAC,IAAMH,EAAE,SAAWC,GAAO,SAAWN,EAAY,QACtDX,EAAO,iBAAA,EACPS,EAAY,QAAUO,EAAE,SAAWC,GAAO,QAC1CN,EAAY,QAAUK,EAAE,SAAWC,GAAO,OAC5C,CACF,EAEMG,EAAaC,GAAwC,CACrDrB,IACFA,EAAO,qBAAqBA,EAAO,iBAAiB,EACpDY,EAAc,QAAU,GAE5B,EAEA,OAAAZ,GAAQ,GAAG,aAAce,CAAW,EACpCf,GAAQ,GAAG,aAAckB,CAAW,EACpClB,GAAQ,GAAG,WAAYoB,CAAS,EAEzB,IAAM,CACXN,EAAAA,EACAd,GAAQ,IAAI,aAAce,CAAW,EACrCf,GAAQ,IAAI,aAAckB,CAAW,EACrClB,GAAQ,IAAI,WAAYoB,CAAS,CACnC,CACF,EAAG,CAACpB,EAAQM,EAAWF,CAAK,CAAC,EAEtB,IACT"}
@@ -0,0 +1,2 @@
1
+ "use strict";var Z=require("zustand/vanilla/shallow"),r=require("react"),u=require("./useStore.cjs");const S=e=>({draggable:e.draggable,selection:e.selection,isDragging:e.isDragging,width:e.width,height:e.height,canvas:e.canvas,setDraggable:e.setDraggable,setIsDragging:e.setIsDragging,lastPosX:e.lastPosX,lastPosY:e.lastPosY,setSelection:e.setSelection,zoomable:e.zoomable,setZoomable:e.setZoomable,scale:e.scale,zoom:e.zoom,defaultCentered:e.defaultCentered,manualZoom:e.manualZoom,maxManualZoom:e.maxManualZoom,minManualZoom:e.minManualZoom,loading:e.loading});function b(){const e=u.useStoreApi(),i=u.useStore(S,Z.shallow),l=r.useMemo(()=>({getCanvas:()=>e.getState().canvas,getNodes:()=>e.getState().nodes.map(t=>({...t})),getState:()=>e.getState,zoomIn:()=>{let{manualZoom:t,canvas:o,fitZoom:m}=e.getState();t+=.2;const n=e.getState().maxManualZoom,a=e.getState().minManualZoom;t>n&&(t=n),t<a&&(t=a);const s=t*m;if(o?.backgroundImage&&o){const g=o.getCenterPoint();o.zoomToPoint(g,s)}e.setState({manualZoom:t,zoom:s})},zoomOut:()=>{let{manualZoom:t,canvas:o,fitZoom:m}=e.getState();t-=.2;const n=e.getState().maxManualZoom,a=e.getState().minManualZoom;t>n&&(t=n),t<a&&(t=a);const s=t*m;if(o?.backgroundImage&&o){const g=o.getCenterPoint();o.zoomToPoint(g,s)}e.setState({manualZoom:t,zoom:s})},getZoom:()=>e.getState().zoom,resetViewport:()=>{let{canvas:t,fitZoom:o=1,defaultCentered:m}=e.getState();const n=1,a=n*o;if(t){if(m&&t.backgroundImage){const s=t.backgroundImage.width||0,g=t.backgroundImage.height||0,c=(t.width-s*a)/2,d=(t.height-g*a)/2;t.setViewportTransform([a,0,0,a,c,d])}else t.setViewportTransform([a,0,0,a,0,0]);t.requestRenderAll()}e.setState({manualZoom:n,zoom:a})}}),[e]);return r.useMemo(()=>({...l,...i}),[l,i])}exports.useReactFabric=b;
2
+ //# sourceMappingURL=useReactFabric.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useReactFabric.cjs","sources":["../../../src/hooks/useReactFabric.ts"],"sourcesContent":["import { shallow } from 'zustand/vanilla/shallow'\nimport { useMemo } from 'react'\nimport type { ReactFabricState } from '../types/store'\nimport { useStore, useStoreApi } from './useStore'\n\nconst selector = (s: ReactFabricState) => ({\n draggable: s.draggable,\n selection: s.selection,\n isDragging: s.isDragging,\n width: s.width,\n height: s.height,\n canvas: s.canvas,\n setDraggable: s.setDraggable,\n setIsDragging: s.setIsDragging,\n lastPosX: s.lastPosX,\n lastPosY: s.lastPosY,\n setSelection: s.setSelection,\n zoomable: s.zoomable,\n setZoomable: s.setZoomable,\n scale: s.scale,\n zoom: s.zoom,\n defaultCentered: s.defaultCentered,\n manualZoom: s.manualZoom,\n maxManualZoom: s.maxManualZoom,\n minManualZoom: s.minManualZoom,\n loading: s.loading,\n})\n\nexport function useReactFabric() {\n const store = useStoreApi()\n const state = useStore(selector, shallow)\n\n const generalHelper = useMemo(() => {\n return {\n getCanvas: () => store.getState().canvas,\n getNodes: () => store.getState().nodes.map(n => ({ ...n })),\n getState: () => store.getState,\n\n /**\n * 放大\n */\n zoomIn: () => {\n let { manualZoom, canvas, fitZoom } = store.getState()\n manualZoom += 0.2\n const maxManualZoom = store.getState().maxManualZoom\n const minManualZoom = store.getState().minManualZoom\n if (manualZoom > maxManualZoom) manualZoom = maxManualZoom\n if (manualZoom < minManualZoom) manualZoom = minManualZoom\n\n const combinedZoom = manualZoom * fitZoom\n const bgImage = canvas?.backgroundImage\n if (bgImage && canvas) {\n // 返回画布的中心点,这个点是固定的 不受背景图位置和变换的影响\n const center = canvas.getCenterPoint()\n canvas.zoomToPoint(center, combinedZoom)\n }\n\n store.setState({\n manualZoom: manualZoom,\n zoom: combinedZoom,\n })\n },\n\n /**\n * 缩小\n */\n zoomOut: () => {\n let { manualZoom, canvas, fitZoom } = store.getState()\n manualZoom -= 0.2\n const maxManualZoom = store.getState().maxManualZoom\n const minManualZoom = store.getState().minManualZoom\n if (manualZoom > maxManualZoom) manualZoom = maxManualZoom\n if (manualZoom < minManualZoom) manualZoom = minManualZoom\n\n const combinedZoom = manualZoom * fitZoom\n const bgImage = canvas?.backgroundImage\n if (bgImage && canvas) {\n // 返回画布的中心点,这个点是固定的 不受背景图位置和变换的影响\n const center = canvas.getCenterPoint()\n canvas.zoomToPoint(center, combinedZoom)\n }\n\n store.setState({\n manualZoom: manualZoom,\n zoom: combinedZoom,\n })\n },\n getZoom: () => store.getState().zoom,\n\n /**\n * 重置视口/缩放\n */\n resetViewport: () => {\n let { canvas, fitZoom = 1, defaultCentered } = store.getState()\n\n const manualZoom = 1\n const combinedZoom = manualZoom * fitZoom\n if (canvas) {\n // 先重置 viewport transform\n if (defaultCentered && canvas.backgroundImage) {\n const bgWidth = canvas.backgroundImage.width || 0\n const bgHeight = canvas.backgroundImage.height || 0\n const deltaX = (canvas.width! - bgWidth * combinedZoom) / 2\n const deltaY = (canvas.height! - bgHeight * combinedZoom) / 2\n canvas.setViewportTransform([combinedZoom, 0, 0, combinedZoom, deltaX, deltaY])\n } else {\n canvas.setViewportTransform([combinedZoom, 0, 0, combinedZoom, 0, 0])\n }\n canvas.requestRenderAll()\n }\n\n store.setState({\n manualZoom: manualZoom,\n zoom: combinedZoom,\n })\n },\n }\n }, [store])\n\n return useMemo(\n () => ({\n ...generalHelper,\n ...state,\n }),\n [generalHelper, state],\n )\n}\n"],"names":["selector","s","useReactFabric","store","useStoreApi","state","useStore","shallow","generalHelper","useMemo","n","manualZoom","canvas","fitZoom","maxManualZoom","minManualZoom","combinedZoom","center","defaultCentered","bgWidth","bgHeight","deltaX","deltaY"],"mappings":"qGAKA,MAAMA,EAAYC,IAAyB,CACzC,UAAWA,EAAE,UACb,UAAWA,EAAE,UACb,WAAYA,EAAE,WACd,MAAOA,EAAE,MACT,OAAQA,EAAE,OACV,OAAQA,EAAE,OACV,aAAcA,EAAE,aAChB,cAAeA,EAAE,cACjB,SAAUA,EAAE,SACZ,SAAUA,EAAE,SACZ,aAAcA,EAAE,aAChB,SAAUA,EAAE,SACZ,YAAaA,EAAE,YACf,MAAOA,EAAE,MACT,KAAMA,EAAE,KACR,gBAAiBA,EAAE,gBACnB,WAAYA,EAAE,WACd,cAAeA,EAAE,cACjB,cAAeA,EAAE,cACjB,QAASA,EAAE,OACb,GAEgB,SAAAC,GAAiB,CAC/B,MAAMC,EAAQC,EAAAA,YACRC,EAAAA,EAAQC,EAAAA,SAASN,EAAUO,EAAO,OAAA,EAElCC,EAAgBC,UAAQ,KACrB,CACL,UAAW,IAAMN,EAAM,WAAW,OAClC,SAAU,IAAMA,EAAM,WAAW,MAAM,IAAIO,IAAM,CAAE,GAAGA,CAAE,EAAE,EAC1D,SAAU,IAAMP,EAAM,SAKtB,OAAQ,IAAM,CACZ,GAAI,CAAE,WAAAQ,EAAY,OAAAC,EAAQ,QAAAC,CAAQ,EAAIV,EAAM,SAAS,EACrDQ,GAAc,GACd,MAAMG,EAAgBX,EAAM,SAAW,EAAA,cACjCY,EAAgBZ,EAAM,WAAW,cACnCQ,EAAaG,IAAeH,EAAaG,GACzCH,EAAaI,IAAeJ,EAAaI,GAE7C,MAAMC,EAAeL,EAAaE,EAElC,GADgBD,GAAQ,iBACTA,EAAQ,CAErB,MAAMK,EAASL,EAAO,iBACtBA,EAAO,YAAYK,EAAQD,CAAY,CACzC,CAEAb,EAAM,SAAS,CACb,WAAYQ,EACZ,KAAMK,CACR,CAAC,CACH,EAKA,QAAS,IAAM,CACb,GAAI,CAAE,WAAAL,EAAY,OAAAC,EAAQ,QAAAC,CAAQ,EAAIV,EAAM,SAAS,EACrDQ,GAAc,GACd,MAAMG,EAAgBX,EAAM,SAAA,EAAW,cACjCY,EAAgBZ,EAAM,SAAA,EAAW,cACnCQ,EAAaG,IAAeH,EAAaG,GACzCH,EAAaI,IAAeJ,EAAaI,GAE7C,MAAMC,EAAeL,EAAaE,EAElC,GADgBD,GAAQ,iBACTA,EAAQ,CAErB,MAAMK,EAASL,EAAO,eAAA,EACtBA,EAAO,YAAYK,EAAQD,CAAY,CACzC,CAEAb,EAAM,SAAS,CACb,WAAYQ,EACZ,KAAMK,CACR,CAAC,CACH,EACA,QAAS,IAAMb,EAAM,WAAW,KAKhC,cAAe,IAAM,CACnB,GAAI,CAAE,OAAAS,EAAQ,QAAAC,EAAU,EAAG,gBAAAK,CAAgB,EAAIf,EAAM,SAAA,EAErD,MAAMQ,EAAa,EACbK,EAAeL,EAAaE,EAClC,GAAID,EAAQ,CAEV,GAAIM,GAAmBN,EAAO,gBAAiB,CAC7C,MAAMO,EAAUP,EAAO,gBAAgB,OAAS,EAC1CQ,EAAWR,EAAO,gBAAgB,QAAU,EAC5CS,GAAUT,EAAO,MAASO,EAAUH,GAAgB,EACpDM,GAAUV,EAAO,OAAUQ,EAAWJ,GAAgB,EAC5DJ,EAAO,qBAAqB,CAACI,EAAc,EAAG,EAAGA,EAAcK,EAAQC,CAAM,CAAC,CAChF,MACEV,EAAO,qBAAqB,CAACI,EAAc,EAAG,EAAGA,EAAc,EAAG,CAAC,CAAC,EAEtEJ,EAAO,iBAAA,CACT,CAEAT,EAAM,SAAS,CACb,WAAYQ,EACZ,KAAMK,CACR,CAAC,CACH,CACF,GACC,CAACb,CAAK,CAAC,EAEV,OAAOM,EAAAA,QACL,KAAO,CACL,GAAGD,EACH,GAAGH,CACL,GACA,CAACG,EAAeH,CAAK,CACvB,CACF"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var d=require("lodash-es"),o=require("react"),a=require("./useStore.cjs");const c=()=>{const{domNode:t,canvas:s,setDimensions:i}=a.useStore(e=>({domNode:e.domNode,canvas:e.canvas,setDimensions:e.setDimensions})),r=o.useCallback(()=>{if(!t||!s)return;const{width:e,height:n}=t.getBoundingClientRect();i({width:e,height:n})},[t,s,i]);o.useEffect(()=>{if(!t||!s)return;r();const e=d.throttle(r,200,{leading:!0,trailing:!0});window.addEventListener("resize",e);const n=new ResizeObserver(e);return n.observe(t),()=>{window.removeEventListener("resize",e),n.disconnect(),e.cancel()}},[r,t,s])};exports.default=c;
2
+ //# sourceMappingURL=useResizeHandler.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResizeHandler.cjs","sources":["../../../src/hooks/useResizeHandler.ts"],"sourcesContent":["import { throttle } from 'lodash-es'\nimport { useCallback, useEffect } from 'react'\nimport { useStore } from './useStore'\n\nconst useResizeHandler = () => {\n const { domNode, canvas, setDimensions } = useStore(state => ({\n domNode: state.domNode,\n canvas: state.canvas,\n setDimensions: state.setDimensions,\n }))\n\n const updateDimensions = useCallback(() => {\n if (!domNode || !canvas) return\n\n // 获取父容器的尺寸\n const { width, height } = domNode.getBoundingClientRect()\n\n // 使用 store 中的 setDimensions 方法\n setDimensions({ width, height })\n\n // canvas.requestRenderAll()\n }, [domNode, canvas, setDimensions])\n\n useEffect(() => {\n if (!domNode || !canvas) return\n\n // 初始化时立即计算一次\n updateDimensions()\n const throttledUpdate = throttle(updateDimensions, 200, {\n leading: true,\n trailing: true,\n })\n\n window.addEventListener('resize', throttledUpdate)\n const resizeObserver = new ResizeObserver(throttledUpdate)\n resizeObserver.observe(domNode)\n\n return () => {\n window.removeEventListener('resize', throttledUpdate)\n resizeObserver.disconnect()\n throttledUpdate.cancel()\n }\n }, [updateDimensions, domNode, canvas])\n}\n\nexport default useResizeHandler\n"],"names":["useResizeHandler","domNode","canvas","setDimensions","useStore","state","updateDimensions","useCallback","width","height","useEffect","throttledUpdate","throttle","resizeObserver"],"mappings":"8IAIMA,MAAAA,EAAmB,IAAM,CAC7B,KAAM,CAAE,QAAAC,EAAS,OAAAC,EAAQ,cAAAC,CAAc,EAAIC,WAASC,IAAU,CAC5D,QAASA,EAAM,QACf,OAAQA,EAAM,OACd,cAAeA,EAAM,aACvB,EAAE,EAEIC,EAAmBC,EAAY,YAAA,IAAM,CACzC,GAAI,CAACN,GAAW,CAACC,EAAQ,OAGzB,KAAM,CAAE,MAAAM,EAAO,OAAAC,CAAO,EAAIR,EAAQ,wBAGlCE,EAAc,CAAE,MAAAK,EAAO,OAAAC,CAAO,CAAC,CAGjC,EAAG,CAACR,EAASC,EAAQC,CAAa,CAAC,EAEnCO,YAAU,IAAM,CACd,GAAI,CAACT,GAAW,CAACC,EAAQ,OAGzBI,EACA,EAAA,MAAMK,EAAkBC,WAASN,EAAkB,IAAK,CACtD,QAAS,GACT,SAAU,EACZ,CAAC,EAED,OAAO,iBAAiB,SAAUK,CAAe,EACjD,MAAME,EAAiB,IAAI,eAAeF,CAAe,EACzD,OAAAE,EAAe,QAAQZ,CAAO,EAEvB,IAAM,CACX,OAAO,oBAAoB,SAAUU,CAAe,EACpDE,EAAe,WAAW,EAC1BF,EAAgB,QAClB,CACF,EAAG,CAACL,EAAkBL,EAASC,CAAM,CAAC,CACxC"}
@@ -0,0 +1,2 @@
1
+ "use strict";var s=require("react"),u=require("../utils/constants.cjs");const h=r=>{u.UNCONTROLLED_PROPS.forEach(t=>{const e=`default${t.charAt(0).toUpperCase()}${t.slice(1)}`;if(r[t]!==void 0&&r[e]!==void 0)throw new Error(`Cannot use both controlled '${t}' and uncontrolled '${e}' for the same property. Either use controlled mode with '${t}', or uncontrolled mode with '${e}', but not both.`)});const i=s.useMemo(()=>JSON.stringify(Object.fromEntries(Object.entries(r).filter(([t])=>!t.startsWith("on")&&!t.startsWith("default")))),[r]),n=s.useMemo(()=>{const t={};return Object.entries(r).forEach(([e,o])=>{e.startsWith("on")&&(t[e]=o)}),t},[r]),c=s.useMemo(()=>{const t={};return Object.entries(r).forEach(([e,o])=>{e.startsWith("default")&&(t[e]=o)}),t},[r]),a=s.useMemo(()=>{const t={};return Object.entries(r).forEach(([e,o])=>{!e.startsWith("on")&&!e.startsWith("default")&&(t[e]=o)}),t},[i]);return[n,a,c]};exports.useSplitProps=h;
2
+ //# sourceMappingURL=useSplitProps.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSplitProps.cjs","sources":["../../../src/hooks/useSplitProps.ts"],"sourcesContent":["import { useMemo } from 'react'\nimport type { AllCanvasEvents } from '../types/canvas'\nimport { UNCONTROLLED_PROPS } from '../utils/constants'\n\n// 提取 default 属性的键\ntype DefaultKeys<T> = keyof T & `default${string}`\n\n// 从 default 属性名转换为普通属性名\ntype DefaultToRegular<T> = {\n [K in DefaultKeys<T> as Uncapitalize<K extends `default${infer R}` ? R : never>]: T[K]\n}\n\n// 返回类型定义\ntype SplitResult<T> = [\n { [K in keyof AllCanvasEvents]?: AllCanvasEvents[K] }, // listeners\n Omit<T, keyof AllCanvasEvents | DefaultKeys<T>>, // attributes\n DefaultToRegular<T>, // defaults\n]\n\n// TODO 同时存在defaultValue和attributes时,defaultValue优先级更高,进行报错,受控非受控模式只能二选一\nexport const useSplitProps = <T extends Record<string, any>>(props: T): SplitResult<T> => {\n // 检查属性冲突\n UNCONTROLLED_PROPS.forEach(prop => {\n const defaultProp = `default${prop.charAt(0).toUpperCase()}${prop.slice(1)}`\n if (props[prop] !== undefined && props[defaultProp] !== undefined) {\n throw new Error(\n `Cannot use both controlled '${prop}' and uncontrolled '${defaultProp}' for the same property. ` +\n `Either use controlled mode with '${prop}', or uncontrolled mode with '${defaultProp}', but not both.`,\n )\n }\n })\n\n // 计算非事件和非default属性的值的字符串表示\n const nonEventAndDefaultPropsString = useMemo(\n () =>\n JSON.stringify(\n Object.fromEntries(\n Object.entries(props).filter(([key]) => !key.startsWith('on') && !key.startsWith('default')),\n ),\n ),\n [props],\n )\n\n // 事件处理函数\n const listeners = useMemo(() => {\n const result: { [key: string]: any } = {}\n Object.entries(props).forEach(([key, value]) => {\n if (key.startsWith('on')) {\n result[key] = value\n }\n })\n return result as { [K in keyof AllCanvasEvents]?: AllCanvasEvents[K] }\n }, [props])\n\n // default 属性\n const defaults = useMemo(() => {\n const result: Record<string, unknown> = {}\n Object.entries(props).forEach(([key, value]) => {\n if (key.startsWith('default')) {\n result[key] = value\n }\n })\n return result as DefaultToRegular<T>\n }, [props])\n\n // 其他属性\n const attributes = useMemo(\n () => {\n const result: { [key: string]: any } = {}\n Object.entries(props).forEach(([key, value]) => {\n if (!key.startsWith('on') && !key.startsWith('default')) {\n result[key] = value\n }\n })\n return result as Omit<T, keyof AllCanvasEvents | DefaultKeys<T>>\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [nonEventAndDefaultPropsString],\n )\n\n return [listeners, attributes, defaults]\n}\n"],"names":["useSplitProps","props","UNCONTROLLED_PROPS","prop","defaultProp","nonEventAndDefaultPropsString","useMemo","key","listeners","result","value","defaults","attributes"],"mappings":"wEAoBa,MAAAA,EAAgDC,GAA6B,CAExFC,qBAAmB,QAAQC,GAAQ,CACjC,MAAMC,EAAc,UAAUD,EAAK,OAAO,CAAC,EAAE,YAAY,CAAC,GAAGA,EAAK,MAAM,CAAC,CAAC,GAC1E,GAAIF,EAAME,CAAI,IAAM,QAAaF,EAAMG,CAAW,IAAM,OACtD,MAAM,IAAI,MACR,+BAA+BD,CAAI,uBAAuBC,CAAW,6DAC/BD,CAAI,iCAAiCC,CAAW,kBACxF,CAEJ,CAAC,EAGD,MAAMC,EAAgCC,EAAAA,QACpC,IACE,KAAK,UACH,OAAO,YACL,OAAO,QAAQL,CAAK,EAAE,OAAO,CAAC,CAACM,CAAG,IAAM,CAACA,EAAI,WAAW,IAAI,GAAK,CAACA,EAAI,WAAW,SAAS,CAAC,CAC7F,CACF,EACF,CAACN,CAAK,CACR,EAGMO,EAAYF,UAAQ,IAAM,CAC9B,MAAMG,EAAiC,GACvC,OAAO,OAAA,QAAQR,CAAK,EAAE,QAAQ,CAAC,CAACM,EAAKG,CAAK,IAAM,CAC1CH,EAAI,WAAW,IAAI,IACrBE,EAAOF,CAAG,EAAIG,EAElB,CAAC,EACMD,CACT,EAAG,CAACR,CAAK,CAAC,EAGJU,EAAWL,EAAQ,QAAA,IAAM,CAC7B,MAAMG,EAAkC,GACxC,cAAO,QAAQR,CAAK,EAAE,QAAQ,CAAC,CAACM,EAAKG,CAAK,IAAM,CAC1CH,EAAI,WAAW,SAAS,IAC1BE,EAAOF,CAAG,EAAIG,EAElB,CAAC,EACMD,CACT,EAAG,CAACR,CAAK,CAAC,EAGJW,EAAaN,EAAAA,QACjB,IAAM,CACJ,MAAMG,EAAiC,GACvC,OAAA,OAAO,QAAQR,CAAK,EAAE,QAAQ,CAAC,CAACM,EAAKG,CAAK,IAAM,CAC1C,CAACH,EAAI,WAAW,IAAI,GAAK,CAACA,EAAI,WAAW,SAAS,IACpDE,EAAOF,CAAG,EAAIG,EAElB,CAAC,EACMD,CACT,EAEA,CAACJ,CAA6B,CAChC,EAEA,MAAO,CAACG,EAAWI,EAAYD,CAAQ,CACzC"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("react"),o=require("zustand/traditional"),u=require("../contexts/StoreContext.cjs");function s(t,n){const r=e.useContext(u.default);if(r===null)throw new Error("\u7F3A\u5931zustandProvider");return o.useStoreWithEqualityFn(r,t,n)}function i(){const t=e.useContext(u.default);if(t===null)throw new Error("\u7F3A\u5931zustandProvider");return e.useMemo(()=>({getState:t.getState,setState:t.setState,subscribe:t.subscribe}),[t])}exports.useStore=s,exports.useStoreApi=i;
2
+ //# sourceMappingURL=useStore.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStore.cjs","sources":["../../../src/hooks/useStore.ts"],"sourcesContent":["import { useContext, useMemo } from 'react'\nimport type { StoreApi } from 'zustand'\nimport type { UseBoundStoreWithEqualityFn } from 'zustand/traditional'\nimport { useStoreWithEqualityFn as useZustandStore } from 'zustand/traditional'\nimport StoreContext from '../contexts/StoreContext'\nimport type { Node } from '../types/nodes'\nimport type { ReactFabricState } from '../types/store'\n\nfunction useStore<StateSlice = unknown>(\n selector: (state: ReactFabricState) => StateSlice,\n equalityFn?: (a: StateSlice, b: StateSlice) => boolean,\n) {\n const store = useContext(StoreContext)\n\n if (store === null) {\n throw new Error('缺失zustandProvider')\n }\n\n return useZustandStore(store, selector, equalityFn)\n}\n\nfunction useStoreApi<NodeType extends Node = Node>() {\n const store = useContext(StoreContext) as UseBoundStoreWithEqualityFn<StoreApi<ReactFabricState<NodeType>>> | null\n\n if (store === null) {\n throw new Error('缺失zustandProvider')\n }\n\n return useMemo(\n () => ({\n getState: store.getState,\n setState: store.setState,\n subscribe: store.subscribe,\n }),\n [store],\n )\n}\n\nexport { useStore, useStoreApi }\n"],"names":["useStore","selector","equalityFn","store","useContext","StoreContext","useZustandStore","useStoreApi","useMemo"],"mappings":"+GAQA,SAASA,EACPC,EACAC,EACA,CACA,MAAMC,EAAQC,aAAWC,SAAY,EAErC,GAAIF,IAAU,KACZ,MAAM,IAAI,MAAM,6BAAmB,EAGrC,OAAOG,EAAAA,uBAAgBH,EAAOF,EAAUC,CAAU,CACpD,CAEA,SAASK,GAA4C,CACnD,MAAMJ,EAAQC,EAAWC,WAAAA,SAAY,EAErC,GAAIF,IAAU,KACZ,MAAM,IAAI,MAAM,6BAAmB,EAGrC,OAAOK,UACL,KAAO,CACL,SAAUL,EAAM,SAChB,SAAUA,EAAM,SAChB,UAAWA,EAAM,SACnB,GACA,CAACA,CAAK,CACR,CACF"}
@@ -0,0 +1,2 @@
1
+ "use strict";var o=require("zustand/shallow"),r=require("./useStore.cjs");const s=e=>e.zoom;function u(){return r.useStore(s,o.shallow)}exports.useZoom=u;
2
+ //# sourceMappingURL=useZoom.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useZoom.cjs","sources":["../../../src/hooks/useZoom.ts"],"sourcesContent":["import { shallow } from 'zustand/shallow'\nimport { useStore } from '../hooks/useStore'\nimport type { ReactFabricState } from '../types/store'\n\nconst nodesSelector = (state: ReactFabricState) => state.zoom\n\nexport function useZoom(): number {\n const zoom = useStore(nodesSelector, shallow)\n\n return zoom\n}\n"],"names":["nodesSelector","state","useZoom","useStore","shallow"],"mappings":"0EAIA,MAAMA,EAAiBC,GAA4BA,EAAM,cAEzCC,GAAkB,CAGhC,OAFaC,EAAAA,SAASH,EAAeI,SAAO,CAG9C"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("./container/ReactFabric/index.cjs"),r=require("./hooks/useReactFabric.cjs"),a=require("./hooks/useZoom.cjs"),i=require("./toolbar/Vertical/index.cjs"),u=require("./components/BackgroundImage/index.cjs"),d=require("./components/Canvas/index.cjs"),t=require("./components/Group/index.cjs"),l=require("./components/NodeToolbarPortal/index.cjs"),n=require("./components/ReactFabricProvider.cjs"),c=require("./components/Rect/index.cjs"),v=require("./components/Control/index.cjs"),o=require("./components/Text/index.cjs"),q=require("./components/Path/index.cjs"),f=require("./components/Line/index.cjs"),x=require("./components/Ellipse/index.cjs"),$=require("./components/Objects/index.cjs"),b=require("./plugins/FreeRect.cjs"),s=require("./plugins/GridLine.cjs"),P=require("./plugins/Pinch.cjs");exports.ReactFabric=e.default,exports.useReactFabric=r.useReactFabric,exports.useZoom=a.useZoom,exports.PluginToolbarVertical=i.default,exports.BackgroundImage=u.default,exports.Canvas=d.default,exports.Group=t.default,exports.NodeToolbarPortal=l.default,exports.ReactFabricProvider=n.ReactFabricProvider,exports.Rect=c.default,exports.Control=v.default,exports.Text=o.default,exports.Path=q.default,exports.Line=f.default,exports.Ellipse=x.default,exports.Objects=$.default,exports.PluginFreeRect=b.default,exports.PluginGridLine=s.default,exports.PluginPinch=P.default;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var A=require("fabric"),o=require("react"),C=require("../hooks/useStore.cjs");const h={stroke:"#04aa65",strokeWidth:5,fill:"transparent"},R=({onComplete:f,disableWhenActive:d=!0,...s})=>{const m=C.useStoreApi(),u=o.useRef(null),t=o.useRef(null),c=o.useRef(!1),l=o.useRef(!1),a=o.useMemo(()=>s?.strokeWidth??h.strokeWidth,[s?.strokeWidth]);return o.useEffect(()=>{const{canvas:e}=m.getState();if(!e)return;const y=e.hoverCursor;e.hoverCursor="crosshair";const v=r=>{if(!u.current||!t.current)return;const{x:n,y:i}=e.getScenePoint(r.e);t.current.set({left:Math.min(u.current.x,n),top:Math.min(u.current.y,i),width:Math.abs(n-u.current.x)-a,height:Math.abs(i-u.current.y)-a}),t.current.setCoords(),e.requestRenderAll()},p=()=>{if(e.off("mouse:move",v),e.off("mouse:up",p),!t.current)return;const r=t.current,n=()=>{e.remove(r),u.current=null,t.current=null,c.current=!1,l.current=!1,e.requestRenderAll()};if(r.width<10||r.height<10)n();else{const i=r.getCoords();l.current=!0,Promise.resolve(f({...h,...s,left:r.left,top:r.top,width:r.width,height:r.height,pointsArray:i.reduce((M,w)=>[...M,Math.round(w.x),Math.round(w.y)],[])},{canvas:e})).finally(n)}},g=r=>{if(c.current||l.current)return;const n=e.getScenePoint(r.e);u.current=n,!(d&&e.getActiveObject())&&(c.current=!0,t.current=new A.Rect({...h,left:n.x,top:n.y,width:0,height:0,...s}),e.add(t.current),e.on("mouse:move",v),e.on("mouse:up",p))};return e.on("mouse:down",g),()=>{e.hoverCursor=y,e.off("mouse:down",g),t.current&&e.remove(t.current),c.current=!1,e.requestRenderAll()}},[m,f,s,a,d]),null};exports.default=R;
2
+ //# sourceMappingURL=FreeRect.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FreeRect.cjs","sources":["../../../src/plugins/FreeRect.tsx"],"sourcesContent":["import type { Canvas, RectProps, TPointerEvent, TPointerEventInfo } from 'fabric'\nimport { Rect } from 'fabric'\nimport { useEffect, useMemo, useRef } from 'react'\nimport { useStoreApi } from '../hooks/useStore'\n\nexport type FreeRectProps = Partial<RectProps> & {\n onComplete: (\n rectProps: Required<\n Pick<RectProps, 'left' | 'top' | 'width' | 'height'> & {\n pointsArray: number[]\n }\n > &\n RectProps,\n { canvas }: { canvas: Canvas },\n ) => Promise<void>\n /**\n * 当画布有选中对象时,是否禁用\n */\n disableWhenActive?: boolean\n}\n\nconst DEFAULT_OPTIONS: Pick<RectProps, 'stroke' | 'strokeWidth' | 'fill'> = {\n stroke: '#04aa65',\n strokeWidth: 5,\n fill: 'transparent',\n}\n\nconst FreeRect = ({ onComplete, disableWhenActive = true, ...rectProps }: FreeRectProps) => {\n const store = useStoreApi()\n const mouseDownPoint = useRef<{ x: number; y: number } | null>(null)\n const rectRef = useRef<Rect | null>(null)\n const isDrawing = useRef(false)\n const isCompleting = useRef(false)\n const strokeWidth = useMemo(() => rectProps?.strokeWidth ?? DEFAULT_OPTIONS.strokeWidth, [rectProps?.strokeWidth])\n\n useEffect(() => {\n const { canvas } = store.getState()\n if (!canvas) return\n const originHoverCursor = canvas.hoverCursor\n canvas.hoverCursor = 'crosshair'\n\n const handleMouseMove = (opt: TPointerEventInfo<TPointerEvent>) => {\n if (!mouseDownPoint.current || !rectRef.current) return\n\n const { x: currentX, y: currentY } = canvas.getScenePoint(opt.e)\n\n rectRef.current.set({\n left: Math.min(mouseDownPoint.current.x, currentX),\n top: Math.min(mouseDownPoint.current.y, currentY),\n width: Math.abs(currentX - mouseDownPoint.current.x) - strokeWidth,\n height: Math.abs(currentY - mouseDownPoint.current.y) - strokeWidth,\n })\n\n rectRef.current.setCoords()\n canvas.requestRenderAll()\n }\n\n const handleMouseUp = () => {\n canvas.off('mouse:move', handleMouseMove)\n canvas.off('mouse:up', handleMouseUp)\n if (!rectRef.current) return\n\n const currentRect = rectRef.current\n\n const cleanup = () => {\n canvas.remove(currentRect)\n mouseDownPoint.current = null\n rectRef.current = null\n isDrawing.current = false\n isCompleting.current = false\n canvas.requestRenderAll()\n }\n\n if (currentRect.width < 10 || currentRect.height < 10) {\n cleanup()\n } else {\n const coords = currentRect.getCoords()\n isCompleting.current = true\n Promise.resolve(\n onComplete(\n {\n ...DEFAULT_OPTIONS,\n ...(rectProps as RectProps),\n left: currentRect.left,\n top: currentRect.top,\n width: currentRect.width,\n height: currentRect.height,\n pointsArray: coords.reduce<number[]>(\n (acc, coord) => [...acc, Math.round(coord.x), Math.round(coord.y)],\n [],\n ),\n },\n {\n canvas: canvas,\n },\n ),\n ).finally(cleanup)\n }\n }\n\n const handleMouseDown = (opt: TPointerEventInfo<TPointerEvent>) => {\n if (isDrawing.current || isCompleting.current) return\n\n const point = canvas.getScenePoint(opt.e)\n mouseDownPoint.current = point\n\n if (disableWhenActive) {\n const activeObject = canvas.getActiveObject()\n if (activeObject) {\n return\n }\n }\n\n isDrawing.current = true\n rectRef.current = new Rect({\n ...DEFAULT_OPTIONS,\n left: point.x,\n top: point.y,\n width: 0,\n height: 0,\n ...rectProps,\n })\n canvas.add(rectRef.current)\n\n canvas.on('mouse:move', handleMouseMove)\n canvas.on('mouse:up', handleMouseUp)\n }\n\n canvas.on('mouse:down', handleMouseDown)\n\n return () => {\n canvas.hoverCursor = originHoverCursor\n canvas.off('mouse:down', handleMouseDown)\n if (rectRef.current) {\n canvas.remove(rectRef.current)\n }\n isDrawing.current = false\n canvas.requestRenderAll()\n }\n }, [store, onComplete, rectProps, strokeWidth, disableWhenActive])\n\n return null\n}\n\nexport default FreeRect\n"],"names":["DEFAULT_OPTIONS","FreeRect","onComplete","disableWhenActive","rectProps","store","useStoreApi","mouseDownPoint","useRef","rectRef","isDrawing","isCompleting","strokeWidth","useMemo","useEffect","canvas","originHoverCursor","handleMouseMove","opt","currentX","currentY","handleMouseUp","currentRect","cleanup","coords","acc","coord","handleMouseDown","point","Rect"],"mappings":"kJAqBA,MAAMA,EAAsE,CAC1E,OAAQ,UACR,YAAa,EACb,KAAM,aACR,EAEMC,EAAW,CAAC,CAAE,WAAAC,EAAY,kBAAAC,EAAoB,GAAM,GAAGC,CAAU,IAAqB,CAC1F,MAAMC,EAAQC,gBACRC,EAAiBC,SAAwC,IAAI,EAC7DC,EAAUD,EAAAA,OAAoB,IAAI,EAClCE,EAAYF,EAAAA,OAAO,EAAK,EACxBG,EAAeH,EAAAA,OAAO,EAAK,EAC3BI,EAAcC,UAAQ,IAAMT,GAAW,aAAeJ,EAAgB,YAAa,CAACI,GAAW,WAAW,CAAC,EAEjH,OAAAU,EAAAA,UAAU,IAAM,CACd,KAAM,CAAE,OAAAC,CAAO,EAAIV,EAAM,SACzB,EAAA,GAAI,CAACU,EAAQ,OACb,MAAMC,EAAoBD,EAAO,YACjCA,EAAO,YAAc,YAErB,MAAME,EAAmBC,GAA0C,CACjE,GAAI,CAACX,EAAe,SAAW,CAACE,EAAQ,QAAS,OAEjD,KAAM,CAAE,EAAGU,EAAU,EAAGC,CAAS,EAAIL,EAAO,cAAcG,EAAI,CAAC,EAE/DT,EAAQ,QAAQ,IAAI,CAClB,KAAM,KAAK,IAAIF,EAAe,QAAQ,EAAGY,CAAQ,EACjD,IAAK,KAAK,IAAIZ,EAAe,QAAQ,EAAGa,CAAQ,EAChD,MAAO,KAAK,IAAID,EAAWZ,EAAe,QAAQ,CAAC,EAAIK,EACvD,OAAQ,KAAK,IAAIQ,EAAWb,EAAe,QAAQ,CAAC,EAAIK,CAC1D,CAAC,EAEDH,EAAQ,QAAQ,UAAA,EAChBM,EAAO,iBAAA,CACT,EAEMM,EAAgB,IAAM,CAG1B,GAFAN,EAAO,IAAI,aAAcE,CAAe,EACxCF,EAAO,IAAI,WAAYM,CAAa,EAChC,CAACZ,EAAQ,QAAS,OAEtB,MAAMa,EAAcb,EAAQ,QAEtBc,EAAU,IAAM,CACpBR,EAAO,OAAOO,CAAW,EACzBf,EAAe,QAAU,KACzBE,EAAQ,QAAU,KAClBC,EAAU,QAAU,GACpBC,EAAa,QAAU,GACvBI,EAAO,iBAAiB,CAC1B,EAEA,GAAIO,EAAY,MAAQ,IAAMA,EAAY,OAAS,GACjDC,QACK,CACL,MAAMC,EAASF,EAAY,YAC3BX,EAAa,QAAU,GACvB,QAAQ,QACNT,EACE,CACE,GAAGF,EACH,GAAII,EACJ,KAAMkB,EAAY,KAClB,IAAKA,EAAY,IACjB,MAAOA,EAAY,MACnB,OAAQA,EAAY,OACpB,YAAaE,EAAO,OAClB,CAACC,EAAKC,IAAU,CAAC,GAAGD,EAAK,KAAK,MAAMC,EAAM,CAAC,EAAG,KAAK,MAAMA,EAAM,CAAC,CAAC,EACjE,CACF,CAAA,CACF,EACA,CACE,OAAQX,CACV,CACF,CACF,EAAE,QAAQQ,CAAO,CACnB,CACF,EAEMI,EAAmBT,GAA0C,CACjE,GAAIR,EAAU,SAAWC,EAAa,QAAS,OAE/C,MAAMiB,EAAQb,EAAO,cAAcG,EAAI,CAAC,EACxCX,EAAe,QAAUqB,EAErB,EAAAzB,GACmBY,EAAO,gBAAgB,KAM9CL,EAAU,QAAU,GACpBD,EAAQ,QAAU,IAAIoB,EAAAA,KAAK,CACzB,GAAG7B,EACH,KAAM4B,EAAM,EACZ,IAAKA,EAAM,EACX,MAAO,EACP,OAAQ,EACR,GAAGxB,CACL,CAAC,EACDW,EAAO,IAAIN,EAAQ,OAAO,EAE1BM,EAAO,GAAG,aAAcE,CAAe,EACvCF,EAAO,GAAG,WAAYM,CAAa,EACrC,EAEA,OAAAN,EAAO,GAAG,aAAcY,CAAe,EAEhC,IAAM,CACXZ,EAAO,YAAcC,EACrBD,EAAO,IAAI,aAAcY,CAAe,EACpClB,EAAQ,SACVM,EAAO,OAAON,EAAQ,OAAO,EAE/BC,EAAU,QAAU,GACpBK,EAAO,kBACT,CACF,EAAG,CAACV,EAAOH,EAAYE,EAAWQ,EAAaT,CAAiB,CAAC,EAE1D,IACT"}
@@ -0,0 +1,5 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var a=require("react/jsx-runtime"),r=require("react");const e={pointerEvents:"none",position:"absolute",inset:0,"--s":"20px","--n":3,"--t":"1px","--g":"3px","--c":"#1890ff70 25%, #0000 0",background:`conic-gradient(at var(--g) var(--t), var(--c)) calc((var(--s) / var(--n) - var(--g) + var(--t)) / 2) 0 /
2
+ calc(var(--s) / var(--n)) var(--s),
3
+ conic-gradient(from 180deg at var(--t) var(--g), var(--c)) 0 calc((var(--s) / var(--n) - var(--g) + var(--t)) / 2) /
4
+ var(--s) calc(var(--s) / var(--n))`},v=r.memo(()=>a.jsx("div",{style:e}));exports.default=v;
5
+ //# sourceMappingURL=GridLine.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GridLine.cjs","sources":["../../../src/plugins/GridLine.tsx"],"sourcesContent":["import type { CSSProperties } from 'react'\nimport { memo } from 'react'\n\nexport interface MyCustomCSS extends CSSProperties {\n [key: `--${string}`]: number | string\n}\n\nconst gridLinesStyle: MyCustomCSS = {\n pointerEvents: 'none',\n position: 'absolute',\n inset: 0,\n '--s': '20px' /* control the size of the grid */,\n '--n': 3 /* control the granularity */,\n '--t': '1px' /* the thickness */,\n '--g': '3px' /* the gap between dashes */,\n '--c': '#1890ff70 25%, #0000 0',\n background: `conic-gradient(at var(--g) var(--t), var(--c)) calc((var(--s) / var(--n) - var(--g) + var(--t)) / 2) 0 /\n calc(var(--s) / var(--n)) var(--s),\n conic-gradient(from 180deg at var(--t) var(--g), var(--c)) 0 calc((var(--s) / var(--n) - var(--g) + var(--t)) / 2) /\n var(--s) calc(var(--s) / var(--n))`,\n /* you probably don't need to set any size in your case */\n /* width: calc(round(down, 100%, var(--s)) + var(--t));\n height: calc(4 * var(--s) + var(--t)); */\n}\n\nconst GridLine = memo(() => {\n return <div style={gridLinesStyle}></div>\n})\n\nexport default GridLine\n"],"names":["gridLinesStyle","GridLine","memo","jsx"],"mappings":"0HAOMA,MAAAA,EAA8B,CAClC,cAAe,OACf,SAAU,WACV,MAAO,EACP,MAAO,OACP,MAAO,EACP,MAAO,MACP,MAAO,MACP,MAAO,yBACP,WAAY;AAAA;AAAA;AAAA,yCAOd,EAEMC,EAAWC,OAAK,IACbC,EAAAA,IAAC,OAAI,MAAOH,CAAAA,CAAgB,CACpC"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var l=require("react"),S=require("../hooks/useStore.cjs"),d=require("hammerjs"),m=require("fabric");const g=()=>{const o=S.useStoreApi(),{canvas:r}=o.getState(),c=l.useRef(1);return l.useEffect(()=>{const a=e=>{const{canvas:s,manualZoom:v,fitZoom:h,minManualZoom:i,maxManualZoom:f}=o.getState();let n=e.scale*v;n<=i&&(n=i),n>=f&&(n=f);const p=n*h,E=e.srcEvent instanceof TouchEvent?new m.Point(e.srcEvent.touches[0].clientX,e.srcEvent.touches[0].clientY):new m.Point(e.srcEvent.offsetX,e.srcEvent.offsetY);s?.zoomToPoint(E,p),c.current=n},u=()=>{const{fitZoom:e}=o.getState(),s=c.current*e;o.setState({manualZoom:c.current,zoom:s})};if(!r)return;const t=new d(r.getSelectionElement());return t.get("pinch").set({enable:!0}),t.on("pinchmove",a),t.on("pinchend",u),()=>{t&&(t.off("pinchmove",a),t.off("pinchend",u),t.destroy())}},[r,o]),null};exports.default=g;
2
+ //# sourceMappingURL=Pinch.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pinch.cjs","sources":["../../../src/plugins/Pinch.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport { useStoreApi } from '../hooks/useStore'\nimport Hammer from 'hammerjs'\nimport { Point } from 'fabric'\n\n/**\n * 双指缩放(捏)\n * @param fabric\n * @returns\n */\nconst Pinch = () => {\n const store = useStoreApi()\n const { canvas } = store.getState()\n // 双指缩放记录\n const newManualZoomRef = useRef(1)\n /**\n * 注册时间\n */\n useEffect(() => {\n /**\n * 双指缩放\n * @param e\n */\n const onPinchMove = (e: HammerInput) => {\n const { canvas, manualZoom, fitZoom, minManualZoom, maxManualZoom } = store.getState()\n let newManualZoom = e.scale * manualZoom\n // 最小缩放限制\n if (newManualZoom <= minManualZoom) {\n newManualZoom = minManualZoom\n }\n // 最大缩放限制\n if (newManualZoom >= maxManualZoom) {\n newManualZoom = maxManualZoom\n }\n // 计算原图比例\n const combinedZoom = newManualZoom * fitZoom\n // 渲染更新\n const point =\n e.srcEvent instanceof TouchEvent\n ? new Point(e.srcEvent.touches[0].clientX, e.srcEvent.touches[0].clientY)\n : new Point(e.srcEvent.offsetX, e.srcEvent.offsetY)\n canvas?.zoomToPoint(point, combinedZoom)\n // 记录手动缩放比例,onPinchEnd使用\n newManualZoomRef.current = newManualZoom\n }\n /**\n * 缩放结束\n */\n const onPinchEnd = () => {\n const { fitZoom } = store.getState()\n const combinedZoom = newManualZoomRef.current * fitZoom\n store.setState({\n manualZoom: newManualZoomRef.current,\n zoom: combinedZoom,\n })\n }\n if (!canvas) return\n\n const hammer = new Hammer(canvas.getSelectionElement())\n // 启用 pinch 事件识别器\n hammer.get('pinch').set({ enable: true })\n hammer.on('pinchmove', onPinchMove)\n hammer.on('pinchend', onPinchEnd)\n\n return () => {\n if (hammer) {\n hammer.off('pinchmove', onPinchMove)\n hammer.off('pinchend', onPinchEnd)\n hammer.destroy()\n }\n }\n }, [canvas, store])\n\n return null\n}\n\nexport default Pinch\n"],"names":["Pinch","store","useStoreApi","canvas","newManualZoomRef","useRef","useEffect","onPinchMove","e","manualZoom","fitZoom","minManualZoom","maxManualZoom","newManualZoom","combinedZoom","point","Point","onPinchEnd","hammer","Hammer"],"mappings":"8KAUMA,EAAQ,IAAM,CAClB,MAAMC,EAAQC,EAAAA,YAAAA,EACR,CAAE,OAAAC,CAAO,EAAIF,EAAM,WAEnBG,EAAmBC,SAAO,CAAC,EAIjC,OAAAC,EAAAA,UAAU,IAAM,CAKd,MAAMC,EAAeC,GAAmB,CACtC,KAAM,CAAE,OAAAL,EAAQ,WAAAM,EAAY,QAAAC,EAAS,cAAAC,EAAe,cAAAC,CAAc,EAAIX,EAAM,WAC5E,IAAIY,EAAgBL,EAAE,MAAQC,EAE1BI,GAAiBF,IACnBE,EAAgBF,GAGdE,GAAiBD,IACnBC,EAAgBD,GAGlB,MAAME,EAAeD,EAAgBH,EAE/BK,EACJP,EAAE,oBAAoB,WAClB,IAAIQ,EAAAA,MAAMR,EAAE,SAAS,QAAQ,CAAC,EAAE,QAASA,EAAE,SAAS,QAAQ,CAAC,EAAE,OAAO,EACtE,IAAIQ,QAAMR,EAAE,SAAS,QAASA,EAAE,SAAS,OAAO,EACtDL,GAAQ,YAAYY,EAAOD,CAAY,EAEvCV,EAAiB,QAAUS,CAC7B,EAIMI,EAAa,IAAM,CACvB,KAAM,CAAE,QAAAP,CAAQ,EAAIT,EAAM,SAAA,EACpBa,EAAeV,EAAiB,QAAUM,EAChDT,EAAM,SAAS,CACb,WAAYG,EAAiB,QAC7B,KAAMU,CACR,CAAC,CACH,EACA,GAAI,CAACX,EAAQ,OAEb,MAAMe,EAAS,IAAIC,EAAOhB,EAAO,oBAAqB,CAAA,EAEtD,OAAAe,EAAO,IAAI,OAAO,EAAE,IAAI,CAAE,OAAQ,EAAK,CAAC,EACxCA,EAAO,GAAG,YAAaX,CAAW,EAClCW,EAAO,GAAG,WAAYD,CAAU,EAEzB,IAAM,CACPC,IACFA,EAAO,IAAI,YAAaX,CAAW,EACnCW,EAAO,IAAI,WAAYD,CAAU,EACjCC,EAAO,UAEX,CACF,EAAG,CAACf,EAAQF,CAAK,CAAC,EAEX,IACT"}