@cs-open/react-fabric 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -0
- package/dist/cjs/components/BackgroundImage/index.cjs +1 -1
- package/dist/cjs/components/BackgroundImage/index.cjs.map +1 -1
- package/dist/cjs/components/Canvas/index.cjs +1 -1
- package/dist/cjs/components/Canvas/index.cjs.map +1 -1
- package/dist/cjs/components/Control/index.cjs +1 -1
- package/dist/cjs/components/Control/index.cjs.map +1 -1
- package/dist/cjs/components/Control2/index.cjs +2 -0
- package/dist/cjs/components/Control2/index.cjs.map +1 -0
- package/dist/cjs/components/Ellipse/index.cjs +1 -1
- package/dist/cjs/components/Ellipse/index.cjs.map +1 -1
- package/dist/cjs/components/Group/index.cjs +1 -1
- package/dist/cjs/components/Group/index.cjs.map +1 -1
- package/dist/cjs/components/IText/index.cjs +2 -0
- package/dist/cjs/components/IText/index.cjs.map +1 -0
- package/dist/cjs/components/Image/index.cjs +1 -1
- package/dist/cjs/components/Image/index.cjs.map +1 -1
- package/dist/cjs/components/Line/index.cjs +1 -1
- package/dist/cjs/components/Line/index.cjs.map +1 -1
- package/dist/cjs/components/Loading/index.cjs +11 -0
- package/dist/cjs/components/Loading/index.cjs.map +1 -0
- package/dist/cjs/components/NodeToolbarPortal/index.cjs +1 -1
- package/dist/cjs/components/NodeToolbarPortal/index.cjs.map +1 -1
- package/dist/cjs/components/Objects/index.cjs +1 -1
- package/dist/cjs/components/Objects/index.cjs.map +1 -1
- package/dist/cjs/components/Path/index.cjs +1 -1
- package/dist/cjs/components/Path/index.cjs.map +1 -1
- package/dist/cjs/components/Polyline/index.cjs +2 -0
- package/dist/cjs/components/Polyline/index.cjs.map +1 -0
- package/dist/cjs/components/ReactFabricProvider.cjs +1 -1
- package/dist/cjs/components/Rect/index.cjs +1 -1
- package/dist/cjs/components/Rect/index.cjs.map +1 -1
- package/dist/cjs/components/StoreUpdater/index.cjs +1 -1
- package/dist/cjs/components/StoreUpdater/index.cjs.map +1 -1
- package/dist/cjs/components/Text/index.cjs +1 -1
- package/dist/cjs/components/Text/index.cjs.map +1 -1
- package/dist/cjs/components/Textbox/index.cjs +2 -0
- package/dist/cjs/components/Textbox/index.cjs.map +1 -0
- package/dist/cjs/components/WavyLine/index.cjs +2 -0
- package/dist/cjs/components/WavyLine/index.cjs.map +1 -0
- package/dist/cjs/container/ReactFabric/index.cjs +1 -1
- package/dist/cjs/container/ReactFabric/index.cjs.map +1 -1
- package/dist/cjs/hooks/useCreateObject.cjs +1 -1
- package/dist/cjs/hooks/useCreateObject.cjs.map +1 -1
- package/dist/cjs/hooks/useDidUpdate.cjs +1 -1
- package/dist/cjs/hooks/useDraggable.cjs +1 -1
- package/dist/cjs/hooks/useInstancePosition.cjs +2 -0
- package/dist/cjs/hooks/useInstancePosition.cjs.map +1 -0
- package/dist/cjs/hooks/useReactFabric.cjs +1 -1
- package/dist/cjs/hooks/useReactFabric.cjs.map +1 -1
- package/dist/cjs/hooks/useResizeHandler.cjs +1 -1
- package/dist/cjs/hooks/useResizeHandler.cjs.map +1 -1
- package/dist/cjs/hooks/useSplitProps.cjs +1 -1
- package/dist/cjs/hooks/useStore.cjs +1 -1
- package/dist/cjs/hooks/useStore.cjs.map +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/plugins/FreeDraw.cjs +2 -0
- package/dist/cjs/plugins/FreeDraw.cjs.map +1 -0
- package/dist/cjs/plugins/FreeRect.cjs +1 -1
- package/dist/cjs/plugins/FreeText.cjs +2 -0
- package/dist/cjs/plugins/FreeText.cjs.map +1 -0
- package/dist/cjs/plugins/Mask.cjs +2 -0
- package/dist/cjs/plugins/Mask.cjs.map +1 -0
- package/dist/cjs/plugins/Pinch.cjs +1 -1
- package/dist/cjs/plugins/Pinch.cjs.map +1 -1
- package/dist/cjs/plugins/index.cjs +2 -0
- package/dist/cjs/plugins/index.cjs.map +1 -0
- package/dist/cjs/store/index.cjs +1 -1
- package/dist/cjs/store/index.cjs.map +1 -1
- package/dist/cjs/store/initialState.cjs +1 -1
- package/dist/cjs/store/initialState.cjs.map +1 -1
- package/dist/cjs/utils/business.cjs +2 -0
- package/dist/cjs/utils/business.cjs.map +1 -0
- package/dist/cjs/utils/childrenWithPosition.cjs +2 -0
- package/dist/cjs/utils/childrenWithPosition.cjs.map +1 -0
- package/dist/esm/components/BackgroundImage/index.mjs +1 -1
- package/dist/esm/components/BackgroundImage/index.mjs.map +1 -1
- package/dist/esm/components/Canvas/index.mjs +1 -1
- package/dist/esm/components/Canvas/index.mjs.map +1 -1
- package/dist/esm/components/Control/index.mjs +1 -1
- package/dist/esm/components/Control/index.mjs.map +1 -1
- package/dist/esm/components/Control2/index.mjs +2 -0
- package/dist/esm/components/Control2/index.mjs.map +1 -0
- package/dist/esm/components/Ellipse/index.mjs +1 -1
- package/dist/esm/components/Ellipse/index.mjs.map +1 -1
- package/dist/esm/components/Group/index.mjs.map +1 -1
- package/dist/esm/components/IText/index.mjs +2 -0
- package/dist/esm/components/IText/index.mjs.map +1 -0
- package/dist/esm/components/Image/index.mjs +2 -0
- package/dist/esm/components/Image/index.mjs.map +1 -0
- package/dist/esm/components/Line/index.mjs +1 -1
- package/dist/esm/components/Line/index.mjs.map +1 -1
- package/dist/esm/components/Loading/index.mjs +11 -0
- package/dist/esm/components/Loading/index.mjs.map +1 -0
- package/dist/esm/components/NodeToolbarPortal/index.mjs +1 -1
- package/dist/esm/components/NodeToolbarPortal/index.mjs.map +1 -1
- package/dist/esm/components/Objects/index.mjs +1 -1
- package/dist/esm/components/Objects/index.mjs.map +1 -1
- package/dist/esm/components/Path/index.mjs +1 -1
- package/dist/esm/components/Path/index.mjs.map +1 -1
- package/dist/esm/components/Rect/index.mjs +1 -1
- package/dist/esm/components/Rect/index.mjs.map +1 -1
- package/dist/esm/components/StoreUpdater/index.mjs +1 -1
- package/dist/esm/components/StoreUpdater/index.mjs.map +1 -1
- package/dist/esm/components/Text/index.mjs +1 -1
- package/dist/esm/components/Text/index.mjs.map +1 -1
- package/dist/esm/components/Textbox/index.mjs +2 -0
- package/dist/esm/components/Textbox/index.mjs.map +1 -0
- package/dist/esm/components/WavyLine/index.mjs +2 -0
- package/dist/esm/components/WavyLine/index.mjs.map +1 -0
- package/dist/esm/container/ReactFabric/index.mjs +1 -1
- package/dist/esm/container/ReactFabric/index.mjs.map +1 -1
- package/dist/esm/hooks/useCreateObject.mjs.map +1 -1
- package/dist/esm/hooks/useInstancePosition.mjs +2 -0
- package/dist/esm/hooks/useInstancePosition.mjs.map +1 -0
- package/dist/esm/hooks/useReactFabric.mjs +1 -1
- package/dist/esm/hooks/useReactFabric.mjs.map +1 -1
- package/dist/esm/hooks/useResizeHandler.mjs +1 -1
- package/dist/esm/hooks/useResizeHandler.mjs.map +1 -1
- package/dist/esm/hooks/useStore.mjs +1 -1
- package/dist/esm/hooks/useStore.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/plugins/FreeDraw.mjs +2 -0
- package/dist/esm/plugins/FreeDraw.mjs.map +1 -0
- package/dist/esm/plugins/FreeText.mjs +2 -0
- package/dist/esm/plugins/FreeText.mjs.map +1 -0
- package/dist/esm/plugins/Pinch.mjs +1 -1
- package/dist/esm/plugins/Pinch.mjs.map +1 -1
- package/dist/esm/store/index.mjs +1 -1
- package/dist/esm/store/index.mjs.map +1 -1
- package/dist/esm/store/initialState.mjs +1 -1
- package/dist/esm/store/initialState.mjs.map +1 -1
- package/dist/esm/utils/business.mjs +2 -0
- package/dist/esm/utils/business.mjs.map +1 -0
- package/dist/esm/utils/childrenWithPosition.mjs +2 -0
- package/dist/esm/utils/childrenWithPosition.mjs.map +1 -0
- package/dist/esm/utils/constants.mjs +1 -1
- package/dist/esm/utils/constants.mjs.map +1 -1
- package/dist/esm/utils/dom.mjs +2 -0
- package/dist/esm/utils/dom.mjs.map +1 -0
- package/dist/esm/utils/position.mjs +2 -0
- package/dist/esm/utils/position.mjs.map +1 -0
- package/dist/esm/utils/props.mjs +1 -1
- package/dist/esm/utils/props.mjs.map +1 -1
- package/dist/types/components/BackgroundImage/index.d.ts.map +1 -1
- package/dist/types/components/Canvas/index.d.ts.map +1 -1
- package/dist/types/components/Control/index.d.ts.map +1 -1
- package/dist/types/components/Control2/index.d.ts +28 -0
- package/dist/types/components/Control2/index.d.ts.map +1 -0
- package/dist/types/components/Ellipse/index.d.ts +3 -0
- package/dist/types/components/Ellipse/index.d.ts.map +1 -1
- package/dist/types/components/Group/index.d.ts.map +1 -1
- package/dist/types/components/IText/index.d.ts +12 -0
- package/dist/types/components/IText/index.d.ts.map +1 -0
- package/dist/types/components/Image/index.d.ts +4 -4
- package/dist/types/components/Image/index.d.ts.map +1 -1
- package/dist/types/components/Line/index.d.ts +2 -0
- package/dist/types/components/Line/index.d.ts.map +1 -1
- package/dist/types/components/Loading/index.d.ts +3 -0
- package/dist/types/components/Loading/index.d.ts.map +1 -0
- package/dist/types/components/NodeToolbarPortal/index.d.ts.map +1 -1
- package/dist/types/components/Objects/index.d.ts.map +1 -1
- package/dist/types/components/Path/index.d.ts +2 -0
- package/dist/types/components/Path/index.d.ts.map +1 -1
- package/dist/types/components/Polyline/index.d.ts +12 -0
- package/dist/types/components/Polyline/index.d.ts.map +1 -0
- package/dist/types/components/Rect/index.d.ts +4 -2
- package/dist/types/components/Rect/index.d.ts.map +1 -1
- package/dist/types/components/StoreUpdater/index.d.ts +1 -1
- package/dist/types/components/StoreUpdater/index.d.ts.map +1 -1
- package/dist/types/components/Text/index.d.ts +2 -0
- package/dist/types/components/Text/index.d.ts.map +1 -1
- package/dist/types/components/Textbox/index.d.ts +12 -0
- package/dist/types/components/Textbox/index.d.ts.map +1 -0
- package/dist/types/components/WavyLine/index.d.ts +12 -0
- package/dist/types/components/WavyLine/index.d.ts.map +1 -0
- package/dist/types/container/ReactFabric/index.d.ts +4 -0
- package/dist/types/container/ReactFabric/index.d.ts.map +1 -1
- package/dist/types/hooks/useCreateObject.d.ts.map +1 -1
- package/dist/types/hooks/useInstancePosition.d.ts +12 -0
- package/dist/types/hooks/useInstancePosition.d.ts.map +1 -0
- package/dist/types/hooks/useReactFabric.d.ts +1 -1
- package/dist/types/hooks/useReactFabric.d.ts.map +1 -1
- package/dist/types/hooks/useResizeHandler.d.ts.map +1 -1
- package/dist/types/index.d.ts +13 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/plugins/FreeDraw.d.ts +9 -0
- package/dist/types/plugins/FreeDraw.d.ts.map +1 -0
- package/dist/types/plugins/FreeText.d.ts +11 -0
- package/dist/types/plugins/FreeText.d.ts.map +1 -0
- package/dist/types/plugins/Mask.d.ts +8 -0
- package/dist/types/plugins/Mask.d.ts.map +1 -0
- package/dist/types/plugins/Pinch.d.ts +15 -3
- package/dist/types/plugins/Pinch.d.ts.map +1 -1
- package/dist/types/plugins/index.d.ts +7 -0
- package/dist/types/plugins/index.d.ts.map +1 -0
- package/dist/types/store/index.d.ts.map +1 -1
- package/dist/types/store/initialState.d.ts.map +1 -1
- package/dist/types/types/component-props.d.ts +0 -35
- package/dist/types/types/component-props.d.ts.map +1 -1
- package/dist/types/types/store.d.ts +13 -0
- package/dist/types/types/store.d.ts.map +1 -1
- package/dist/types/utils/business.d.ts +83 -0
- package/dist/types/utils/business.d.ts.map +1 -0
- package/dist/types/utils/childrenWithPosition.d.ts +6 -0
- package/dist/types/utils/childrenWithPosition.d.ts.map +1 -0
- package/package.json +22 -18
- package/dist/cjs/toolbar/Vertical/index.cjs +0 -2
- package/dist/cjs/toolbar/Vertical/index.cjs.map +0 -1
- package/dist/esm/toolbar/Vertical/index.mjs +0 -2
- package/dist/esm/toolbar/Vertical/index.mjs.map +0 -1
- package/dist/types/toolbar/Vertical/index.d.ts +0 -10
- package/dist/types/toolbar/Vertical/index.d.ts.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# @cs-open/react-fabric
|
|
2
|
+
|
|
3
|
+
React 组件库,基于 Fabric.js 构建,提供强大的 Canvas 绘图功能。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @cs-open/react-fabric
|
|
9
|
+
# 或者
|
|
10
|
+
yarn add @cs-open/react-fabric
|
|
11
|
+
# 或者
|
|
12
|
+
pnpm add @cs-open/react-fabric
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 依赖要求
|
|
16
|
+
|
|
17
|
+
### 必需依赖
|
|
18
|
+
|
|
19
|
+
以下依赖会自动安装:
|
|
20
|
+
|
|
21
|
+
- `fabric` - Fabric.js 核心库
|
|
22
|
+
- `react` - React 框架
|
|
23
|
+
- `react-dom` - React DOM 渲染器
|
|
24
|
+
- `zustand` - 状态管理
|
|
25
|
+
- `@floating-ui/core` - 浮动 UI 组件
|
|
26
|
+
- `use-sync-external-store` - 同步外部存储
|
|
27
|
+
|
|
28
|
+
### 可选依赖
|
|
29
|
+
|
|
30
|
+
某些插件需要额外的依赖才能正常工作:
|
|
31
|
+
|
|
32
|
+
#### Pinch 插件(触摸手势支持)
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install hammerjs
|
|
36
|
+
# 如果使用 TypeScript,还需要安装类型定义
|
|
37
|
+
npm install @types/hammerjs
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**注意**:如果没有安装 `hammerjs`,Pinch 插件会自动禁用,不会影响其他功能。
|
|
41
|
+
|
|
42
|
+
## 使用示例
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import React from 'react'
|
|
46
|
+
import { Canvas, Rect, Text } from '@cs-open/react-fabric'
|
|
47
|
+
|
|
48
|
+
function App() {
|
|
49
|
+
return (
|
|
50
|
+
<Canvas width={800} height={600}>
|
|
51
|
+
<Rect left={100} top={100} width={200} height={100} fill="red" />
|
|
52
|
+
<Text left={150} top={150} text="Hello Fabric!" fontSize={20} fill="white" />
|
|
53
|
+
</Canvas>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default App
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 插件系统
|
|
61
|
+
|
|
62
|
+
### 内置插件
|
|
63
|
+
|
|
64
|
+
- **Pinch**: 触摸设备双指缩放支持
|
|
65
|
+
- **FreeDraw**: 自由绘制工具
|
|
66
|
+
- **FreeRect**: 自由矩形绘制工具
|
|
67
|
+
- **FreeText**: 自由文本工具
|
|
68
|
+
- **GridLine**: 网格线辅助工具
|
|
69
|
+
- **Mask**: 遮罩效果
|
|
70
|
+
- **Pinch**: 触摸手势支持
|
|
71
|
+
|
|
72
|
+
### 使用插件
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { Canvas } from '@cs-open/react-fabric'
|
|
76
|
+
import { Pinch, FreeDraw } from '@cs-open/react-fabric/plugins'
|
|
77
|
+
|
|
78
|
+
function App() {
|
|
79
|
+
return (
|
|
80
|
+
<Canvas width={800} height={600}>
|
|
81
|
+
{/* 你的画布内容 */}
|
|
82
|
+
<Pinch />
|
|
83
|
+
<FreeDraw />
|
|
84
|
+
</Canvas>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 故障排除
|
|
90
|
+
|
|
91
|
+
### Pinch 插件不工作
|
|
92
|
+
|
|
93
|
+
如果触摸手势功能不工作,请确保:
|
|
94
|
+
|
|
95
|
+
1. 已安装 `hammerjs`:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install hammerjs
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
2. 检查控制台是否有相关警告信息
|
|
102
|
+
|
|
103
|
+
3. 确保在触摸设备上测试
|
|
104
|
+
|
|
105
|
+
### 其他问题
|
|
106
|
+
|
|
107
|
+
如果遇到其他问题,请检查:
|
|
108
|
+
|
|
109
|
+
1. 所有必需依赖是否正确安装
|
|
110
|
+
2. 浏览器控制台是否有错误信息
|
|
111
|
+
3. 确保使用兼容的浏览器版本
|
|
112
|
+
|
|
113
|
+
DOM control
|
|
114
|
+
|
|
115
|
+
```jsx
|
|
116
|
+
<Rect>
|
|
117
|
+
<div className="w-full h-full bg-red-500">
|
|
118
|
+
<EvaluatesHtml
|
|
119
|
+
type="right"
|
|
120
|
+
pageSize={pageSize}
|
|
121
|
+
allSentences={allSentences}
|
|
122
|
+
isPaperFront={isPaperFront}
|
|
123
|
+
fontSizeMemo={fontSizeMemo / 1.2}
|
|
124
|
+
singleColumnLeft={singleColumnLeft}
|
|
125
|
+
/>
|
|
126
|
+
</div>
|
|
127
|
+
</Rect>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 许可证
|
|
131
|
+
|
|
132
|
+
MIT
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var h=require("fabric"),s=require("react"),F=require("../../hooks/useDidUpdate.cjs"),w=require("../../hooks/useStore.cjs");const q=o=>({width:o.width,height:o.height}),R=s.forwardRef(({src:o,onLoad:b,onScaling:V,scaleToFit:l,scaleToCover:g,...d},S)=>{const a=s.useRef(null),n=w.useStoreApi(),{width:T,height:p}=w.useStore(q),m=s.useCallback(c=>{const{canvas:e,manualZoom:r=1,defaultCentered:f}=n.getState();if(!a.current)return;if(!e){console.warn("updateViewport: !canvas");return}if(!e.backgroundImage){console.warn("updateViewport: !canvas.backgroundImage");return}const i=c.scaleToFit?h.util.findScaleToFit(a.current,e):c.scaleToCover?h.util.findScaleToCover(a.current,e):1,t=i*r;e.setViewportTransform([t,0,0,t,0,0]);let u=0,v=0;if(f&&e.backgroundImage){const k=e.backgroundImage.width||0,C=e.backgroundImage.height||0;u=(e.width-k*t)/2,v=(e.height-C*t)/2}const I=[t,0,0,t,u,v];e.setViewportTransform(I),e.requestRenderAll(),n.setState({fitZoom:i,manualZoom:r,zoom:t})},[n]);return s.useEffect(()=>{m({scaleToFit:l,scaleToCover:g})},[T,p,l,g,m,n]),s.useEffect(()=>{if(!o){console.warn("ReactFabricBackgroundImage: !src");return}const{domNode:c,setLoading:e}=n.getState();return e(!0),h.FabricImage.fromURL(o,{crossOrigin:"anonymous"}).then(r=>{const f=r.getSrc(),i=a.current?.getSrc();if(i&&f!==i)return;const{canvas:t}=n.getState();if(t){const u={...d,angle:0};if(r.set({...u,objectCaching:!1}),t.getContext().imageSmoothingEnabled=!0,t.getContext().imageSmoothingQuality="high",t.backgroundImage=r,a.current=r,requestAnimationFrame(()=>{m({scaleToFit:l,scaleToCover:g})}),!t.viewportTransform){console.warn("!viewport");return}b?.(r)}else console.warn("ReactFabric:BackgroundImage: !canvas",t)}).catch(console.error).finally(()=>{c&&(c.dataset.src=o),e(!1)}),()=>{const{canvas:r}=n.getState();r?.backgroundImage&&(r.backgroundImage=void 0,r.remove(a.current),a.current=null,r.renderAll())}},[o]),F.useDidUpdate(()=>{const{canvas:c}=n.getState();a.current&&(Object.entries(d).forEach(([e,r])=>{e==="angle"?a.current?.rotate(r):a.current?.set(e,r)}),c?.requestRenderAll())},[d,n]),s.useImperativeHandle(S,()=>({instance:a.current})),null});var A=s.memo(R);exports.default=A;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +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
|
|
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 currentSrc = imageSource.getSrc()\n const latestSrc = backgroundImageRef.current?.getSrc()\n\n if (latestSrc && currentSrc !== latestSrc) {\n return\n }\n\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 }, [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","currentSrc","latestSrc","removeAngleOptions","useDidUpdate","key","value","useImperativeHandle","memo"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var c=require("react/jsx-runtime"),i=require("fabric"),a=require("react"),j=require("../../hooks/useDraggable.cjs"),x=require("../../hooks/useResizeHandler.cjs"),y=require("../../hooks/useSplitProps.cjs"),b=require("../../hooks/useStore.cjs"),z=require("../../utils/events.cjs");const E={position:"absolute",width:"100%",height:"100%",top:0,left:0},M=({children:w,onMouseWheel:l,...S})=>{const e=a.useRef(),o=b.useStoreApi(),f=a.useRef(null);j.default();const d=a.useRef(null),q=b.useStore(r=>r.controls),[P,n]=y.useSplitProps(S);return a.useLayoutEffect(()=>{const r=f.current;e.current=new i.Canvas(r||void 0,{...n});const t=z.bindEvents(e.current,P);return o.setState({canvas:e.current}),window.canvas=e.current,()=>{t(),e.current?.dispose(),r?.remove(),e.current=void 0,o.setState({canvas:null})}},[]),x.default(),a.useEffect(()=>{const r=t=>{const{zoomable:R,panAble:_,maxManualZoom:v,minManualZoom:m,fitZoom:p=1,zoom:g}=o.getState();if(t.e.preventDefault(),t.e.stopPropagation(),t.e.wheelDeltaY!==0){if(t.e.ctrlKey||t.e.wheelDeltaY===void 0){if(!R)return;const u=t.e.deltaY>0?.95:1.05;let s=g/p*u;s>v&&(s=v),s<m&&(s=m);const h=s*p;e.current?.zoomToPoint(new i.Point(t.e.offsetX,t.e.offsetY),h),o.setState({manualZoom:s,zoom:h})}else{if(!_)return;const u=1.5,s=new i.Point(-t.e.deltaX*u,-t.e.deltaY*u);e.current?.relativePan(s)}l?.(t)}};return e.current?.on("mouse:wheel",r),()=>{e.current?.off("mouse:wheel",r)}},[l,o]),a.useEffect(()=>{o.setState({domNode:d.current?.closest(".react-fabric")})},[o]),a.useEffect(()=>{e.current&&(e.current.set(n),e.current.requestRenderAll())},[n]),c.jsxs("div",{className:"react-fabric__canvas",ref:d,style:E,children:[c.jsx("canvas",{ref:f}),w,q.map(r=>c.jsx("div",{id:r.id,style:{position:"absolute"},className:`react-fabric__control ${r.className}`,ref:r.ref,children:r.children},r.id))]})};exports.default=M;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +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
|
|
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 { useStore, 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 const controls = useStore(state => state.controls)\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 }, [])\n\n useResizeHandler()\n\n useEffect(() => {\n const onMouseWheelHandler = (opt: TPointerEventInfo<WheelEvent>) => {\n const { zoomable, panAble, 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 const 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 if (!panAble) return\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 {controls.map(control => (\n <div\n key={control.id}\n id={control.id}\n style={{\n position: 'absolute',\n }}\n className={`react-fabric__control ${control.className}`}\n ref={control.ref}\n >\n {control.children}\n </div>\n ))}\n </div>\n )\n}\n\nexport default Canvas\n"],"names":["style","Canvas","children","onMouseWheel","props","canvasRef","useRef","store","useStoreApi","canvasDomRef","useDraggable","domRef","controls","useStore","state","listeners","attributes","useSplitProps","useLayoutEffect","canvas","BaseCanvas","unbindEvents","bindEvents","useResizeHandler","useEffect","onMouseWheelHandler","opt","zoomable","panAble","maxManualZoom","minManualZoom","fitZoom","zoom","zoomFactor","newManualZoom","combinedZoom","Point","sensitivityFactor","delta","jsxs","jsx","control"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var p=require("react/jsx-runtime"),m=require("@floating-ui/core"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var p=require("react/jsx-runtime"),m=require("@floating-ui/core"),q=require("fabric"),t=require("react"),R=require("../../hooks/useStore.cjs"),b=require("../NodeToolbarPortal/index.cjs");const P=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=>q.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:E})=>{i.current&&Object.assign(i.current.style,{left:`${a}px`,top:`${E}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(b.default,{className:`absolute ${w}`,ref:i,onClick:e=>{e.stopPropagation(),e.preventDefault()},children:x})]})});exports.default=P;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +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
|
|
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 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={`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"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var x=require("react/jsx-runtime"),p=require("@floating-ui/core"),b=require("fabric"),n=require("react"),E=require("../../hooks/useStore.cjs");const O=()=>Math.random().toString(36).substring(2)+Date.now().toString(36),P=n.forwardRef(({children:u,className:g,placement:v="bottom",open:a=!0,onOpenChange:w,closeOnOutsideClick:S=!0,Content:h},l)=>{const m=n.useRef(void 0),r=E.useStore(e=>e.canvas),o=n.useRef(null),[c,j]=n.useState(O()),i=E.useStoreApi(),y=n.useCallback(e=>{e!==m.current&&(m.current=e,typeof l=="function"?l(e):l&&(l.current=e))},[l]),C=n.useMemo(()=>n.Children.only(u)&&n.isValidElement(u)?n.cloneElement(u,{ref:y,...u.props}):u,[u,y]),d=n.useCallback(()=>{if(!m.current||!o.current||!r)return;const e=o.current.getBoundingClientRect();if(e.width===0||e.height===0){requestAnimationFrame(d);return}const t=m.current.getCoords().map(s=>b.util.sendPointToPlane(s,r.viewportTransform,void 0)),f={getElementRects:s=>s,getDimensions:s=>s,getClippingRect:()=>({x:0,y:0,width:r.width,height:r.height})},R={x:t[0].x,y:t[0].y,width:t[2].x-t[0].x,height:t[2].y-t[0].y};p.computePosition(R,o.current.getBoundingClientRect(),{platform:f,placement:v,middleware:[p.offset(5),p.flip(),p.shift({padding:5})]}).then(({x:s,y:q})=>{o.current&&Object.assign(o.current.style,{left:`${s}px`,top:`${q}px`})})},[r,v]);return n.useEffect(()=>{a&&requestAnimationFrame(d)},[a,d]),n.useEffect(()=>(r?.on("after:render",d),()=>{r?.off("after:render",d)}),[r,d]),n.useEffect(()=>{const e=f=>{f.e.stopPropagation(),f.e.preventDefault()},t=f=>{o.current?.contains(f.target)||S&&a&&w?.(!1)};return r?.on("mouse:down",e),document.addEventListener("mousedown",t,!1),()=>{r?.off("mouse:down",e),document.removeEventListener("mousedown",t,!1)}},[r,S,w,a]),n.useEffect(()=>{const{controls:e}=i.getState();if(!e.find(t=>t.id===c))return i.setState({controls:[...e,{id:c,className:g,children:h,ref:o}]}),()=>{i.setState({controls:e.filter(t=>t.id!==c)})}},[i]),n.useEffect(()=>{const{controls:e}=i.getState();e.find(t=>t.id===c)&&i.setState({controls:e.map(t=>t.id===c?{...t,children:a?h:null,className:g}:t)})},[h,g,c,i,a]),x.jsx(x.Fragment,{children:C})});exports.default=P;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/components/Control2/index.tsx"],"sourcesContent":["import { computePosition, flip, offset, shift } from '@floating-ui/core'\nimport type { ComputePositionConfig } from '@floating-ui/react'\nimport type { FabricObject, FabricObjectProps, TPointerEvent, TPointerEventInfo } from 'fabric'\nimport { util } from 'fabric'\nimport type { ReactNode } from 'react'\nimport { useState } from 'react'\nimport { useMemo } from 'react'\nimport { useCallback } from 'react'\nimport { useRef } from 'react'\nimport { useEffect } from 'react'\nimport React from 'react'\nimport { useStore, useStoreApi } from '../../hooks/useStore'\nimport type { AllObjectEvents } from '../../types/object'\n\n// 生成随机ID的函数\nconst generateRandomId = (): string => {\n return Math.random().toString(36).substring(2) + Date.now().toString(36)\n}\n\n/**\n * @desc 不能内置支持 selected , 因为需要 rect 开启 lockMovementX lockMovementY, 这样支持的场景就受限了\n */\nexport type Control2Props = 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 }: Control2Props,\n forwardRef,\n ) => {\n const instanceRef = useRef<FabricObject | undefined>(undefined)\n const canvas = useStore(state => state.canvas)\n const floatingElRef = useRef<HTMLDivElement>(null)\n const [id, _setId] = useState(generateRandomId())\n\n const store = useStoreApi()\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 // 这里测试下来是多余的,可删除;after:render 后都有值\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 // 这里测试下来是多余的,可删除;\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 useEffect(() => {\n const { controls } = store.getState()\n\n const control = controls.find(control => control.id === id)\n\n if (control) {\n return\n }\n\n store.setState({\n controls: [\n ...controls,\n {\n id,\n className,\n children: Content,\n ref: floatingElRef,\n },\n ],\n })\n return () => {\n store.setState({\n controls: controls.filter(control => control.id !== id),\n })\n }\n }, [store])\n\n useEffect(() => {\n const { controls } = store.getState()\n const control = controls.find(control => control.id === id)\n\n if (control) {\n store.setState({\n controls: controls.map(control =>\n control.id === id ? { ...control, children: open ? Content : null, className } : control,\n ),\n })\n }\n }, [Content, className, id, store, open])\n\n return <>{newChildren}</>\n },\n)\n\nexport default Control\n"],"names":["generateRandomId","Control","React","children","className","placement","open","onOpenChange","closeOnOutsideClick","Content","forwardRef","instanceRef","useRef","canvas","useStore","state","floatingElRef","id","_setId","useState","store","useStoreApi","refCallback","useCallback","node","newChildren","useMemo","updatePosition","floatingRect","viewportCoords","point","util","platform","data","element","virtualEl","computePosition","offset","flip","shift","x","y","useEffect","handleCanvasClick","e","handleDocumentClick","controls","control","jsx","Fragment"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var l=require("fabric"),r=require("react"),c=require("../../hooks/useCreateObject.cjs"),p=require("../../hooks/useSplitProps.cjs"),d=require("../../hooks/useInstancePosition.cjs");const v=r.forwardRef(({group:t,children:u,...s},i)=>{const[a,o,n]=p.useSplitProps(s),e=c.useCreateObject({Constructor:l.Ellipse,defaultValues:n,attributes:o,group:t,listeners:a});return r.useImperativeHandle(i,()=>e,[e]),d.useInstancePosition(e,u)});var b=r.memo(v);exports.default=b;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +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
|
|
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, type ReactNode } from 'react'\nimport { useCreateObject } from '../../hooks/useCreateObject'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport type { AllObjectEvents } from '../../types/object'\nimport { useInstancePosition } from '../../hooks/useInstancePosition'\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 children?: ReactNode\n} & T\n\nconst Ellipse = forwardRef<BaseEllipse | undefined, EllipseProps>(({ group, children, ...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 useImperativeHandle(ref, () => instance, [instance])\n\n return useInstancePosition(instance, children)\n})\n\nexport default memo(Ellipse)\n"],"names":["Ellipse","forwardRef","group","children","props","ref","listeners","attributes","defaultValues","useSplitProps","instance","useCreateObject","BaseEllipse","useImperativeHandle","useInstancePosition","memo"],"mappings":"wPAiBA,MAAMA,EAAUC,EAAAA,WAAkD,CAAC,CAAE,MAAAC,EAAO,SAAAC,EAAU,GAAGC,CAAM,EAAGC,IAAQ,CACxG,KAAM,CAACC,EAAWC,EAAYC,CAAa,EAAIC,EAAAA,cAAcL,CAAK,EAE5DM,EAAWC,EAAAA,gBAAgB,CAC/B,YAAaC,EAAAA,QACb,cAAAJ,EACA,WAAAD,EACA,MAAAL,EACA,UAAAI,CACF,CAAC,EACD,OAAAO,EAAAA,oBAAoBR,EAAK,IAAMK,EAAU,CAACA,CAAQ,CAAC,EAE5CI,sBAAoBJ,EAAUP,CAAQ,CAC/C,CAAC,EAED,MAAeY,OAAKf,CAAO"}
|
|
@@ -1,2 +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"),
|
|
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"),S=require("../../hooks/useStore.cjs"),m=require("../../utils/events.cjs");const q=r.memo(({children:c,controlsVisibility:u,...d})=>{const s=S.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?m.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
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +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
|
|
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 }, [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,CACF,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 e=require("fabric"),t=require("react"),p=require("../../hooks/useCreateObject.cjs"),d=require("../../hooks/useSplitProps.cjs"),l=require("../../hooks/useInstancePosition.cjs");e.IText.prototype.set({_getNonTransformedDimensions(){return new e.Point(this.width,this.height).scalarAdd(this.padding)},_calculateCurrentDimensions(){return e.util.transformPoint(this._getTransformedDimensions(),this.getViewportTransform(),!0)}});const m=t.forwardRef(({group:s,text:i,children:n,...a},o)=>{const[u,c]=d.useSplitProps(a),r=p.useCreateObject({Constructor:e.IText,param:i,attributes:c,group:s,listeners:u});return t.useImperativeHandle(o,()=>r,[r]),l.useInstancePosition(r,n)});var h=t.memo(m);exports.default=h;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/components/IText/index.tsx"],"sourcesContent":["import type { Group as BaseGroup } from 'fabric'\nimport { IText, util, Point } from 'fabric'\nimport { forwardRef, memo, useImperativeHandle, type ReactNode } from 'react'\nimport { useCreateObject } from '../../hooks/useCreateObject'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport type { AllObjectEvents } from '../../types/object'\nimport { useInstancePosition } from '../../hooks/useInstancePosition'\n\nexport type ITextProps<T = unknown> = Partial<ConstructorParameters<typeof IText>[1] & AllObjectEvents> & {\n group?: BaseGroup\n text: string\n children?: ReactNode\n} & T\n\nIText.prototype.set({\n _getNonTransformedDimensions() {\n // Object dimensions\n return new Point(this.width, this.height).scalarAdd(this.padding)\n },\n _calculateCurrentDimensions() {\n // Controls dimensions\n return util.transformPoint(this._getTransformedDimensions(), this.getViewportTransform(), true)\n },\n})\n\nconst ITextBox = forwardRef<IText | undefined, ITextProps>(({ group, text, children, ...props }, ref) => {\n const [listeners, attributes] = useSplitProps(props)\n\n const instance = useCreateObject({\n Constructor: IText,\n param: text,\n attributes,\n group,\n listeners,\n })\n\n useImperativeHandle(ref, () => instance, [instance])\n\n return useInstancePosition(instance, children)\n})\n\nexport default memo(ITextBox)\n"],"names":["IText","Point","util","ITextBox","forwardRef","group","text","children","props","ref","listeners","attributes","useSplitProps","instance","useCreateObject","useImperativeHandle","useInstancePosition","x","memo"],"mappings":"wPAcAA,EAAAA,MAAM,UAAU,IAAI,CAClB,8BAA+B,CAE7B,OAAO,IAAIC,EAAM,MAAA,KAAK,MAAO,KAAK,MAAM,EAAE,UAAU,KAAK,OAAO,CAClE,EACA,6BAA8B,CAE5B,OAAOC,EAAAA,KAAK,eAAe,KAAK,4BAA6B,KAAK,uBAAwB,EAAI,CAChG,CACF,CAAC,EAED,MAAMC,EAAWC,EAA0C,WAAA,CAAC,CAAE,MAAAC,EAAO,KAAAC,EAAM,SAAAC,EAAU,GAAGC,CAAM,EAAGC,IAAQ,CACvG,KAAM,CAACC,EAAWC,CAAU,EAAIC,gBAAcJ,CAAK,EAE7CK,EAAWC,EAAgB,gBAAA,CAC/B,YAAad,EACb,MAAA,MAAOM,EACP,WAAAK,EACA,MAAAN,EACA,UAAAK,CACF,CAAC,EAED,OAAAK,sBAAoBN,EAAK,IAAMI,EAAU,CAACA,CAAQ,CAAC,EAE5CG,EAAoBH,oBAAAA,EAAUN,CAAQ,CAC/C,CAAC,EAED,IAAAU,EAAeC,EAAAA,KAAKf,CAAQ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var l=require("fabric"),e=require("react"),g=require("../../hooks/useStore.cjs"),p=require("../../hooks/useInstancePosition.cjs");const R=e.forwardRef(({group:u,src:n,onLoad:c,children:m,...s},v)=>{const r=e.useRef(),o=e.useRef(c),a=g.useStoreApi(),i=e.useRef(s);return e.useEffect(()=>{o.current=c,i.current=s}),e.useEffect(()=>{const{canvas:f}=a.getState();if(!f?.getElement())return;const d=u??f;return l.FabricImage.fromURL(n,{crossOrigin:"anonymous"}).then(t=>{r.current=t,t.set(i.current),o.current?.(t),d.add(t)}),()=>{r.current&&d?.remove(r.current)}},[n,a,u]),e.useImperativeHandle(v,()=>r.current),p.useInstancePosition(r.current,m)});var q=e.memo(R);exports.default=q;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../../src/components/Image/index.tsx"],"sourcesContent":["import type { ImageProps as FabricImageProps, ObjectEvents, SerializedImageProps
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/components/Image/index.tsx"],"sourcesContent":["import type { ImageProps as FabricImageProps, ObjectEvents, SerializedImageProps } from 'fabric'\nimport { FabricImage } from 'fabric'\nimport type { Group as BaseGroup } from 'fabric'\nimport { forwardRef, memo, useEffect, useImperativeHandle, useRef, type ReactNode } from 'react'\nimport { useStoreApi } from '../../hooks/useStore'\nimport { useInstancePosition } from '../../hooks/useInstancePosition'\n\nexport type ImageProps = Partial<FabricImageProps> & {\n src: string\n group?: BaseGroup\n onLoad?: (imageSource: FabricImage<Partial<ImageProps>, SerializedImageProps, ObjectEvents>) => void\n children?: ReactNode\n}\n\nconst Image = forwardRef<FabricImage | undefined, ImageProps>(({ group, src, onLoad, children, ...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 useImperativeHandle(ref, () => instanceRef.current)\n\n return useInstancePosition(instanceRef.current, children)\n})\n\nexport default memo(Image)\n"],"names":["Image","forwardRef","group","src","onLoad","children","options","ref","instanceRef","useRef","onLoadRef","store","useStoreApi","optionsRef","useEffect","canvas","parent","FabricImage","imageSource","useImperativeHandle","useInstancePosition","memo"],"mappings":"sMAcA,MAAMA,EAAQC,EAAAA,WAAgD,CAAC,CAAE,MAAAC,EAAO,IAAAC,EAAK,OAAAC,EAAQ,SAAAC,EAAU,GAAGC,CAAQ,EAAGC,IAAQ,CACnH,MAAMC,EAAcC,EAAAA,OAAoB,EAClCC,EAAYD,SAAOL,CAAM,EACzBO,EAAQC,EAAY,YAAA,EACpBC,EAAaJ,EAAAA,OAAOH,CAAO,EAGjC,OAAAQ,EAAAA,UAAU,IAAM,CACdJ,EAAU,QAAUN,EACpBS,EAAW,QAAUP,CACvB,CAAC,EAGDQ,EAAAA,UAAU,IAAM,CACd,KAAM,CAAE,OAAAC,CAAO,EAAIJ,EAAM,SAAS,EAClC,GAAI,CAACI,GAAQ,WAAW,EAAG,OAE3B,MAAMC,EAASd,GAASa,EAExB,OAAAE,cAAY,QAAQd,EAAK,CAAE,YAAa,WAAY,CAAC,EAAE,KAAKe,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,CAACL,EAAKQ,EAAOT,CAAK,CAAC,EAEtBiB,sBAAoBZ,EAAK,IAAMC,EAAY,OAAO,EAE3CY,EAAAA,oBAAoBZ,EAAY,QAASH,CAAQ,CAC1D,CAAC,EAED,IAAegB,EAAAA,EAAAA,KAAKrB,CAAK"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var v=require("fabric"),r=require("react"),d=require("../../hooks/useCreateObject.cjs"),P=require("../../hooks/useSplitProps.cjs"),b=require("../../hooks/useInstancePosition.cjs");const f=r.forwardRef(({group:t,x1:u,y1:s,x2:a,y2:i,children:o,...n},c)=>{const[p,l]=P.useSplitProps(n),e=d.useCreateObject({Constructor:v.Line,param:[u,s,a,i],attributes:l,group:t,listeners:p});return r.useImperativeHandle(c,()=>e,[e]),b.useInstancePosition(e,o)});var m=r.memo(f);exports.default=m;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +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
|
|
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, type ReactNode } from 'react'\nimport { useCreateObject } from '../../hooks/useCreateObject'\nimport { useSplitProps } from '../../hooks/useSplitProps'\nimport type { AllObjectEvents } from '../../types/object'\nimport { useInstancePosition } from '../../hooks/useInstancePosition'\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 children?: ReactNode\n} & T\n\nconst Line = forwardRef<Handle, LineProps>(({ group, x1, y1, x2, y2, children, ...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 useInstancePosition(instance, children)\n})\n\nexport default memo(Line)\n"],"names":["Line","forwardRef","group","x1","y1","x2","y2","children","props","ref","listeners","attributes","useSplitProps","instance","useCreateObject","BaseLine","useImperativeHandle","useInstancePosition","memo"],"mappings":"wPAgBA,MAAMA,EAAOC,EAAAA,WAA8B,CAAC,CAAE,MAAAC,EAAO,GAAAC,EAAI,GAAAC,EAAI,GAAAC,EAAI,GAAAC,EAAI,SAAAC,EAAU,GAAGC,CAAM,EAAGC,IAAQ,CACjG,KAAM,CAACC,EAAWC,CAAU,EAAIC,EAAAA,cAAcJ,CAAK,EAE7CK,EAAWC,EAAgB,gBAAA,CAC/B,YAAaC,EACb,KAAA,MAAO,CAACZ,EAAIC,EAAIC,EAAIC,CAAE,EACtB,WAAAK,EACA,MAAAT,EACA,UAAAQ,CACF,CAAC,EAED,OAAAM,EAAAA,oBAAoBP,EAAK,IAAMI,EAAU,CAACA,CAAQ,CAAC,EAE5CI,sBAAoBJ,EAAUN,CAAQ,CAC/C,CAAC,EAED,IAAeW,EAAAA,EAAAA,KAAKlB,CAAI"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),r=require("../../hooks/useStore.cjs");const o=()=>r.useStore(t=>t.loading)?e.jsxs("div",{style:{position:"absolute",bottom:0,left:0,right:0,top:0,zIndex:40,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",overflow:"hidden"},children:[e.jsx("span",{style:{width:"48px",height:"48px",border:"5px solid #04aa65",borderBottomColor:"transparent",borderRadius:"50%",display:"inline-block",boxSizing:"border-box",animation:"rotation 1s linear infinite"}}),e.jsx("style",{children:`
|
|
2
|
+
@keyframes rotation {
|
|
3
|
+
0% {
|
|
4
|
+
transform: rotate(0deg);
|
|
5
|
+
}
|
|
6
|
+
100% {
|
|
7
|
+
transform: rotate(360deg);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`})]}):null;exports.default=o;
|
|
11
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/components/Loading/index.tsx"],"sourcesContent":["import { 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\n style={{\n position: 'absolute',\n bottom: 0,\n left: 0,\n right: 0,\n top: 0,\n zIndex: 40,\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'hidden',\n }}\n >\n <span\n style={{\n width: '48px',\n height: '48px',\n border: '5px solid #04aa65',\n borderBottomColor: 'transparent',\n borderRadius: '50%',\n display: 'inline-block',\n boxSizing: 'border-box',\n animation: 'rotation 1s linear infinite',\n }}\n ></span>\n <style>\n {`\n @keyframes rotation {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }\n `}\n </style>\n </div>\n ) : null\n}\n\nexport default Loading\n"],"names":["Loading","useStore","state","jsxs","jsx"],"mappings":"6IAGMA,MAAAA,EAAU,IACEC,EAAAA,SAASC,GAASA,EAAM,OAAO,EAG7CC,EAAAA,KAAC,MACC,CAAA,MAAO,CACL,SAAU,WACV,OAAQ,EACR,KAAM,EACN,MAAO,EACP,IAAK,EACL,OAAQ,GACR,QAAS,OACT,cAAe,SACf,WAAY,SACZ,eAAgB,SAChB,SAAU,QACZ,EAEA,SAAA,CAAAC,EAAAA,IAAC,OAAA,CACC,MAAO,CACL,MAAO,OACP,OAAQ,OACR,OAAQ,oBACR,kBAAmB,cACnB,aAAc,MACd,QAAS,eACT,UAAW,aACX,UAAW,6BACb,CACD,CAAA,EACDA,MAAC,QACE,CAAA,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAUH,CAAA,CAAA,CAAA,CACF,EACE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var i=require("react-dom"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var i=require("react-dom"),a=require("react"),d=require("../../hooks/useStore.cjs");const f=u=>u.domNode?.querySelector(".react-fabric__canvas"),v=a.forwardRef(({children:u,className:l,onClick:n},t)=>{const o=d.useStore(f),e=a.useRef(),s=a.useCallback(r=>{r!==e.current&&(e.current=r||void 0,typeof t=="function"?t(r):t&&(t.current=r))},[t]);if(a.useEffect(()=>()=>{e.current&&(e.current.remove(),e.current=void 0)},[]),!o||e.current)return e.current?i.createPortal(u,e.current):null;const c=document.createElement("div");return c.className=`react-fabric__portal ${l}`,n&&c.addEventListener("click",r=>{r.stopPropagation(),n(r)}),o.appendChild(c),s(c),i.createPortal(u,c)});exports.default=v;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../../src/components/NodeToolbarPortal/index.tsx"],"sourcesContent":["import { createPortal } from 'react-dom'\nimport
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/components/NodeToolbarPortal/index.tsx"],"sourcesContent":["import { createPortal } from 'react-dom'\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 = `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","e"],"mappings":"wJAMA,MAAMA,EAAYC,GAA4BA,EAAM,SAAS,cAAc,uBAAuB,EAQ5FC,EAAoBC,EAAAA,WAAkC,CAAC,CAAE,SAAAC,EAAU,UAAAC,EAAW,QAAAC,CAAQ,EAAGH,IAAe,CAC5G,MAAMI,EAAaC,WAASR,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,UAAY,wBAAwBX,CAAS,GAC7CC,GACFU,EAAI,iBAAiB,QAASC,GAAK,CACjCA,EAAE,gBACFX,EAAAA,EAAQW,CAAQ,CAClB,CAAC,EAEHV,EAAW,YAAYS,CAAG,EAC1BL,EAAYK,CAAG,EAERD,EAAAA,aAAaX,EAAUY,CAAG,CACnC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),d=require("react"),l=require("../Path/index.cjs"),u=require("../Text/index.cjs"),x=require("../Rect/index.cjs"),s=require("../Line/index.cjs"),o=require("../IText/index.cjs");const v=({objects:r})=>{const a={rect:x.default,path:l.default,text:u.default,line:s.default,"i-text":u.default,itext:o.default};return r?e.jsx(e.Fragment,{children:r?.map(({type:i,...n})=>{const t=a[i.toLowerCase()];return t?e.jsx(t,{...n}):null})}):null};var c=d.memo(v);exports.default=c;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +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;
|
|
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'\nimport IText from '../IText'\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 itext: IText,\n }\n\n if (!objects) return null\n return (\n <>\n {objects?.map(({ type, ...options }) => {\n const Component = components[type.toLowerCase() as keyof typeof components]\n if (!Component) {\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","IText","jsx","Fragment","type","options","Component","memo"],"mappings":"sRAWA,MAAMA,EAAU,CAAC,CAAE,QAAAC,CAAQ,IAAoB,CAC7C,MAAMC,EAAa,CACjB,KAAMC,EAAAA,QACN,KAAMC,UACN,KAAMC,UACN,KAAMC,EACN,QAAA,SAAUD,EAAAA,QACV,MAAOE,EAAAA,OACT,EAEA,OAAKN,EAEHO,EAAAA,IAAAC,EAAAA,SAAA,CACG,SAAAR,GAAS,IAAI,CAAC,CAAE,KAAAS,EAAM,GAAGC,CAAQ,IAAM,CACtC,MAAMC,EAAYV,EAAWQ,EAAK,YAAwC,CAAA,EAC1E,OAAKE,EAGEJ,EAAAA,IAACI,EAAA,CAAW,GAAGD,EAAS,EAFtB,IAGX,CAAC,CACH,CAAA,EAVmB,IAYvB,EAEA,IAAeE,EAAAA,EAAAA,KAAKb,CAAO"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var c=require("fabric"),r=require("react"),p=require("../../hooks/useCreateObject.cjs"),l=require("../../hooks/useSplitProps.cjs"),v=require("../../hooks/useInstancePosition.cjs");const d=r.forwardRef(({group:t,path:s="M 0 0",children:u,...a},i)=>{const[o,n]=l.useSplitProps(a),e=p.useCreateObject({Constructor:c.Path,param:s,attributes:n,group:t,listeners:o});return r.useImperativeHandle(i,()=>e,[e]),v.useInstancePosition(e,u)});var P=r.memo(d);exports.default=P;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|