@mhamz.01/easyflow-whiteboard 1.17.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/node/custom-node-overlay-layer.d.ts +2 -0
- package/dist/components/node/custom-node-overlay-layer.d.ts.map +1 -1
- package/dist/components/node/custom-node-overlay-layer.js +42 -27
- package/dist/components/whiteboard/whiteboard-test.d.ts.map +1 -1
- package/dist/components/whiteboard/whiteboard-test.js +2 -1
- package/dist/hooks/useSelection.d.ts.map +1 -1
- package/dist/hooks/useSelection.js +31 -58
- package/package.json +1 -1
|
@@ -38,6 +38,8 @@ interface CanvasOverlayLayerProps {
|
|
|
38
38
|
} | null;
|
|
39
39
|
selectedCanvasObjects?: FabricObject[];
|
|
40
40
|
fabricCanvas?: React.RefObject<Canvas | null>;
|
|
41
|
+
selectedIds: Set<string>;
|
|
42
|
+
setSelectedIds: React.Dispatch<React.SetStateAction<Set<string>>>;
|
|
41
43
|
}
|
|
42
44
|
export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, onDocumentsUpdate, canvasZoom, canvasViewport, selectionBox, selectedCanvasObjects, fabricCanvas, }: CanvasOverlayLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
43
45
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-node-overlay-layer.d.ts","sourceRoot":"","sources":["../../../src/components/node/custom-node-overlay-layer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAM9C,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,UAAU,uBAAuB;IAC/B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACxC,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,YAAY,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzE,qBAAqB,CAAC,EAAE,YAAY,EAAE,CAAC;IACvC,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"custom-node-overlay-layer.d.ts","sourceRoot":"","sources":["../../../src/components/node/custom-node-overlay-layer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAM9C,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,UAAU,uBAAuB;IAC/B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACxC,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,YAAY,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzE,qBAAqB,CAAC,EAAE,YAAY,EAAE,CAAC;IACvC,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9C,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;CACnE;AAaD,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EACzC,KAAK,EACL,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,UAAc,EACd,cAA+B,EAC/B,YAAmB,EACnB,qBAA0B,EAC1B,YAAY,GACb,EAAE,uBAAuB,2CAgfzB"}
|
|
@@ -128,42 +128,35 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
128
128
|
return { x: doc.x, y: doc.y };
|
|
129
129
|
return undefined;
|
|
130
130
|
};
|
|
131
|
-
const isItemInSelectionBox = (
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
const isItemInSelectionBox = (x, y, width, height, box) => {
|
|
132
|
+
// Get overlay element offset (recalculate every time for sidebar changes)
|
|
133
|
+
const overlayRect = overlayRef.current?.getBoundingClientRect();
|
|
134
|
+
if (!overlayRect)
|
|
134
135
|
return false;
|
|
135
|
-
//
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
return !(itemX + itemWidth < bX1 ||
|
|
147
|
-
itemX > bX2 ||
|
|
148
|
-
itemY + itemHeight < bY1 ||
|
|
149
|
-
itemY > bY2);
|
|
136
|
+
// Item position in screen space (relative to whiteboard)
|
|
137
|
+
const itemX1 = x * canvasZoom + canvasViewport.x;
|
|
138
|
+
const itemY1 = y * canvasZoom + canvasViewport.y;
|
|
139
|
+
const itemX2 = itemX1 + width * canvasZoom;
|
|
140
|
+
const itemY2 = itemY1 + height * canvasZoom;
|
|
141
|
+
// Selection box adjusted for overlay offset from viewport
|
|
142
|
+
const boxX1 = Math.min(box.x1, box.x2) - overlayRect.left;
|
|
143
|
+
const boxY1 = Math.min(box.y1, box.y2) - overlayRect.top;
|
|
144
|
+
const boxX2 = Math.max(box.x1, box.x2) - overlayRect.left;
|
|
145
|
+
const boxY2 = Math.max(box.y1, box.y2) - overlayRect.top;
|
|
146
|
+
return !(boxX2 < itemX1 || boxX1 > itemX2 || boxY2 < itemY1 || boxY1 > itemY2);
|
|
150
147
|
};
|
|
151
148
|
// ── Selection box detection ──────────────────────────────────────────────────
|
|
152
149
|
useEffect(() => {
|
|
153
150
|
if (!selectionBox)
|
|
154
151
|
return;
|
|
155
152
|
const newSelected = new Set();
|
|
156
|
-
// Use standard widths for your nodes if they are fixed,
|
|
157
|
-
// or pass their actual dimensions if dynamic.
|
|
158
153
|
localTasks.forEach((task) => {
|
|
159
|
-
if (isItemInSelectionBox(task.x, task.y, 300, 140, selectionBox))
|
|
154
|
+
if (isItemInSelectionBox(task.x, task.y, 300, 140, selectionBox))
|
|
160
155
|
newSelected.add(task.id);
|
|
161
|
-
}
|
|
162
156
|
});
|
|
163
157
|
localDocuments.forEach((doc) => {
|
|
164
|
-
if (isItemInSelectionBox(doc.x, doc.y, 300, 160, selectionBox))
|
|
158
|
+
if (isItemInSelectionBox(doc.x, doc.y, 300, 160, selectionBox))
|
|
165
159
|
newSelected.add(doc.id);
|
|
166
|
-
}
|
|
167
160
|
});
|
|
168
161
|
setSelectedIds(newSelected);
|
|
169
162
|
}, [selectionBox, localTasks, localDocuments, canvasZoom, canvasViewport]);
|
|
@@ -216,6 +209,28 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
216
209
|
// Prevents mobile pull-to-refresh
|
|
217
210
|
document.body.style.touchAction = "none";
|
|
218
211
|
};
|
|
212
|
+
// Add this useEffect in CanvasOverlayLayer
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
const canvas = fabricCanvas?.current;
|
|
215
|
+
if (!canvas)
|
|
216
|
+
return;
|
|
217
|
+
const handleCanvasClick = (e) => {
|
|
218
|
+
// Clear custom node selection when clicking empty canvas
|
|
219
|
+
if (!e.target) {
|
|
220
|
+
setSelectedIds(new Set());
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const handleSelectionCleared = () => {
|
|
224
|
+
// Clear custom nodes when Fabric selection is cleared
|
|
225
|
+
setSelectedIds(new Set());
|
|
226
|
+
};
|
|
227
|
+
canvas.on("mouse:down", handleCanvasClick);
|
|
228
|
+
canvas.on("selection:cleared", handleSelectionCleared);
|
|
229
|
+
return () => {
|
|
230
|
+
canvas.off("mouse:down", handleCanvasClick);
|
|
231
|
+
canvas.off("selection:cleared", handleSelectionCleared);
|
|
232
|
+
};
|
|
233
|
+
}, [fabricCanvas, setSelectedIds]);
|
|
219
234
|
// ── Drag move (HTML Node side) ───────────────────────────────────────────────
|
|
220
235
|
useEffect(() => {
|
|
221
236
|
if (!dragging)
|
|
@@ -356,11 +371,11 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
356
371
|
zIndex: isDragging ? 1000 : 1,
|
|
357
372
|
}, children: children }, id));
|
|
358
373
|
};
|
|
359
|
-
return (_jsx("div", { ref: overlayRef, className: "absolute inset-0 pointer-events-none
|
|
374
|
+
return (_jsx("div", { ref: overlayRef, className: "absolute inset-0 pointer-events-none", style: { zIndex: 50 }, onWheel: handleOverlayWheel, onClick: (e) => {
|
|
360
375
|
if (e.target === e.currentTarget)
|
|
361
376
|
setSelectedIds(new Set());
|
|
362
377
|
}, children: _jsxs("div", { className: "absolute top-0 left-0 pointer-events-none", style: {
|
|
363
|
-
transform: `
|
|
364
|
-
|
|
378
|
+
transform: `translate(${canvasViewport.x}px, ${canvasViewport.y}px)`,
|
|
379
|
+
transformOrigin: "top left",
|
|
365
380
|
}, children: [localTasks.map((task) => renderItem(task.id, task.x, task.y, _jsx(TaskNode, { ...task, isSelected: selectedIds.has(task.id), onSelect: handleSelect, onDragStart: handleDragStart, onStatusChange: handleStatusChange, zoom: 1 }))), localDocuments.map((doc) => renderItem(doc.id, doc.x, doc.y, _jsx(DocumentNode, { ...doc, isSelected: selectedIds.has(doc.id), onSelect: handleSelect, onDragStart: handleDragStart })))] }) }));
|
|
366
381
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whiteboard-test.d.ts","sourceRoot":"","sources":["../../../src/components/whiteboard/whiteboard-test.tsx"],"names":[],"mappings":"AA4CA,MAAM,CAAC,OAAO,UAAU,gBAAgB,
|
|
1
|
+
{"version":3,"file":"whiteboard-test.d.ts","sourceRoot":"","sources":["../../../src/components/whiteboard/whiteboard-test.tsx"],"names":[],"mappings":"AA4CA,MAAM,CAAC,OAAO,UAAU,gBAAgB,4CAuQvC"}
|
|
@@ -37,6 +37,7 @@ export default function FabricWhiteboard() {
|
|
|
37
37
|
const eraserActiveRef = useRef(false);
|
|
38
38
|
const eraserPathRef = useRef(null);
|
|
39
39
|
const eraserPathPointsRef = useRef([]);
|
|
40
|
+
const [selectedIds, setSelectedIds] = useState(new Set());
|
|
40
41
|
// Store
|
|
41
42
|
const activeTool = useWhiteboardStore((state) => state.activeTool);
|
|
42
43
|
const toolOptions = useWhiteboardStore((state) => state.toolOptions);
|
|
@@ -203,5 +204,5 @@ export default function FabricWhiteboard() {
|
|
|
203
204
|
backgroundImage: `radial-gradient(circle, rgba(255,255,255,0.2) 1.2px, transparent 1.2px)`,
|
|
204
205
|
backgroundSize: "40px 40px",
|
|
205
206
|
zIndex: 0,
|
|
206
|
-
} }), _jsx("canvas", { ref: canvasRef, className: "absolute inset-0", style: { zIndex: 1 } }), _jsx(CanvasOverlayLayer, { tasks: tasks, documents: documents, onTasksUpdate: setTasks, onDocumentsUpdate: setDocuments, canvasZoom: canvasZoom, canvasViewport: canvasViewport, selectionBox: selectionBox, selectedCanvasObjects: selectedCanvasObjects, fabricCanvas: fabricCanvasRef }), _jsxs("div", { className: "absolute inset-0 pointer-events-none", style: { zIndex: 100 }, children: [_jsx("div", { className: "pointer-events-auto", children: _jsx(WhiteboardToolbar, { fabricCanvas: fabricCanvasRef, isRestoringRef: isRestoringRef, onAddTask: handleAddTaskFromDropdown, onAddDocument: handleAddDocumentFromDropdown }) }), _jsx("div", { className: "pointer-events-auto", children: _jsx(ToolOptionsPanel, { fabricCanvas: fabricCanvasRef }) }), _jsx("div", { className: "pointer-events-auto", children: _jsx(ZoomControls, { zoom: canvasZoom, onZoomIn: handleZoomIn, onZoomOut: handleZoomOut, onResetZoom: handleResetZoom }) })] })] }) }));
|
|
207
|
+
} }), _jsx("canvas", { ref: canvasRef, className: "absolute inset-0", style: { zIndex: 1 } }), _jsx(CanvasOverlayLayer, { tasks: tasks, documents: documents, onTasksUpdate: setTasks, onDocumentsUpdate: setDocuments, canvasZoom: canvasZoom, canvasViewport: canvasViewport, selectionBox: selectionBox, selectedCanvasObjects: selectedCanvasObjects, fabricCanvas: fabricCanvasRef, selectedIds: selectedIds, setSelectedIds: setSelectedIds }), _jsxs("div", { className: "absolute inset-0 pointer-events-none", style: { zIndex: 100 }, children: [_jsx("div", { className: "pointer-events-auto", children: _jsx(WhiteboardToolbar, { fabricCanvas: fabricCanvasRef, isRestoringRef: isRestoringRef, onAddTask: handleAddTaskFromDropdown, onAddDocument: handleAddDocumentFromDropdown }) }), _jsx("div", { className: "pointer-events-auto", children: _jsx(ToolOptionsPanel, { fabricCanvas: fabricCanvasRef }) }), _jsx("div", { className: "pointer-events-auto", children: _jsx(ZoomControls, { zoom: canvasZoom, onZoomIn: handleZoomIn, onZoomOut: handleZoomOut, onResetZoom: handleResetZoom }) })] })] }) }));
|
|
207
208
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSelection.d.ts","sourceRoot":"","sources":["../../src/hooks/useSelection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAQ,YAAY,
|
|
1
|
+
{"version":3,"file":"useSelection.d.ts","sourceRoot":"","sources":["../../src/hooks/useSelection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAQ,YAAY,EAAe,MAAM,QAAQ,CAAC;AAMjE,UAAU,iBAAiB;IACzB,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,eAAe,EAAE,CAAC,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1F,wBAAwB,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAC5D,YAAY,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAC/C;AAED,eAAO,MAAM,YAAY,GAAI,oHAQ1B,iBAAiB,SA6JnB,CAAC"}
|
|
@@ -17,13 +17,7 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
|
|
|
17
17
|
let selRect = null;
|
|
18
18
|
let rafId = null;
|
|
19
19
|
const onDown = (e) => {
|
|
20
|
-
|
|
21
|
-
if (!e.target) {
|
|
22
|
-
setSelectedCanvasObjects([]);
|
|
23
|
-
setSelectedObjectType(null);
|
|
24
|
-
}
|
|
25
|
-
// 2. Marquee Selection Logic
|
|
26
|
-
if (activeTool !== "select" || e.target || isDrawingRef.current)
|
|
20
|
+
if (activeTool !== "select" || e.target)
|
|
27
21
|
return;
|
|
28
22
|
isSelecting = true;
|
|
29
23
|
const p = canvas.getScenePoint(e.e);
|
|
@@ -33,12 +27,12 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
|
|
|
33
27
|
top: p.y,
|
|
34
28
|
width: 0,
|
|
35
29
|
height: 0,
|
|
36
|
-
fill: "
|
|
37
|
-
stroke: "
|
|
38
|
-
strokeWidth:
|
|
30
|
+
fill: "transparent",
|
|
31
|
+
stroke: "transparent",
|
|
32
|
+
strokeWidth: 0,
|
|
39
33
|
selectable: false,
|
|
40
34
|
evented: false,
|
|
41
|
-
visible: false,
|
|
35
|
+
visible: false,
|
|
42
36
|
});
|
|
43
37
|
canvas.add(selRect);
|
|
44
38
|
canvas.renderAll();
|
|
@@ -52,21 +46,22 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
|
|
|
52
46
|
const p = canvas.getScenePoint(e.e);
|
|
53
47
|
const w = p.x - selStart.x;
|
|
54
48
|
const h = p.y - selStart.y;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
selRect.set({
|
|
50
|
+
left: w < 0 ? p.x : selStart.x,
|
|
51
|
+
top: h < 0 ? p.y : selStart.y,
|
|
52
|
+
width: Math.abs(w),
|
|
53
|
+
height: Math.abs(h),
|
|
54
|
+
});
|
|
60
55
|
selRect.setCoords();
|
|
61
56
|
canvas.renderAll();
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
const rect =
|
|
57
|
+
// ← FIX: Add canvas element offset
|
|
58
|
+
const canvasElement = canvas.getElement();
|
|
59
|
+
const rect = canvasElement.getBoundingClientRect();
|
|
65
60
|
setSelectionBox({
|
|
66
|
-
x1:
|
|
67
|
-
y1:
|
|
68
|
-
x2: (
|
|
69
|
-
y2: (
|
|
61
|
+
x1: Math.min(selStart.x, p.x) * canvasZoom + canvasViewport.x + rect.left,
|
|
62
|
+
y1: Math.min(selStart.y, p.y) * canvasZoom + canvasViewport.y + rect.top,
|
|
63
|
+
x2: Math.max(selStart.x, p.x) * canvasZoom + canvasViewport.x + rect.left,
|
|
64
|
+
y2: Math.max(selStart.y, p.y) * canvasZoom + canvasViewport.y + rect.top,
|
|
70
65
|
});
|
|
71
66
|
});
|
|
72
67
|
};
|
|
@@ -81,54 +76,35 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
|
|
|
81
76
|
canvas.renderAll();
|
|
82
77
|
isSelecting = false;
|
|
83
78
|
selRect = null;
|
|
84
|
-
|
|
85
|
-
setTimeout(() => setSelectionBox(null), 50);
|
|
79
|
+
setTimeout(() => setSelectionBox(null), 100);
|
|
86
80
|
};
|
|
87
81
|
const onSelected = () => {
|
|
88
82
|
const sel = canvas.getActiveObject();
|
|
89
83
|
if (!sel || isDrawingRef.current)
|
|
90
84
|
return;
|
|
91
|
-
|
|
92
|
-
? sel.getObjects()
|
|
93
|
-
: [sel];
|
|
94
|
-
// Track previous positions to prevent jumping during drag
|
|
95
|
-
objects.forEach((obj) => {
|
|
96
|
-
obj._prevLeft = obj.left;
|
|
97
|
-
obj._prevTop = obj.top;
|
|
98
|
-
});
|
|
99
|
-
setSelectedCanvasObjects(objects);
|
|
85
|
+
setSelectedCanvasObjects(sel.type === "activeSelection" ? sel.getObjects() : [sel]);
|
|
100
86
|
const typeMap = {
|
|
101
87
|
rect: "rectangle",
|
|
102
88
|
circle: "circle",
|
|
103
89
|
line: "line",
|
|
90
|
+
arrow: "arrow",
|
|
91
|
+
"bidirectional-arrow": "arrow",
|
|
104
92
|
"i-text": "text",
|
|
105
93
|
text: "text",
|
|
106
94
|
path: "pen",
|
|
107
95
|
image: "image",
|
|
108
96
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
97
|
+
const t = sel instanceof Frame
|
|
98
|
+
? "frame"
|
|
99
|
+
: sel instanceof FabricImage
|
|
100
|
+
? "image"
|
|
101
|
+
: sel instanceof Arrow
|
|
102
|
+
? "arrow"
|
|
103
|
+
: sel instanceof BidirectionalArrow
|
|
104
|
+
? "arrow"
|
|
105
|
+
: typeMap[sel.type] ?? null;
|
|
118
106
|
setSelectedObjectType(t);
|
|
119
107
|
};
|
|
120
|
-
// ── Sync Fabric movement to HTML Nodes ────────────────────────
|
|
121
|
-
const onObjectMoving = (e) => {
|
|
122
|
-
const target = e.transform?.target || e.target;
|
|
123
|
-
if (!target)
|
|
124
|
-
return;
|
|
125
|
-
// Ensure prev positions exist to avoid NaN or jumps
|
|
126
|
-
if (target._prevLeft === undefined)
|
|
127
|
-
target._prevLeft = target.left;
|
|
128
|
-
if (target._prevTop === undefined)
|
|
129
|
-
target._prevTop = target.top;
|
|
130
|
-
target.setCoords();
|
|
131
|
-
};
|
|
132
108
|
const onDeselected = () => {
|
|
133
109
|
setSelectedObjectType(null);
|
|
134
110
|
setSelectionBox(null);
|
|
@@ -137,11 +113,9 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
|
|
|
137
113
|
setActiveTool("select");
|
|
138
114
|
}
|
|
139
115
|
};
|
|
140
|
-
// Event Binding
|
|
141
116
|
canvas.on("selection:created", onSelected);
|
|
142
117
|
canvas.on("selection:updated", onSelected);
|
|
143
118
|
canvas.on("selection:cleared", onDeselected);
|
|
144
|
-
canvas.on("object:moving", onObjectMoving);
|
|
145
119
|
canvas.on("mouse:down", onDown);
|
|
146
120
|
canvas.on("mouse:move", onMove);
|
|
147
121
|
canvas.on("mouse:up", onUp);
|
|
@@ -149,7 +123,6 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
|
|
|
149
123
|
canvas.off("selection:created", onSelected);
|
|
150
124
|
canvas.off("selection:updated", onSelected);
|
|
151
125
|
canvas.off("selection:cleared", onDeselected);
|
|
152
|
-
canvas.off("object:moving", onObjectMoving);
|
|
153
126
|
canvas.off("mouse:down", onDown);
|
|
154
127
|
canvas.off("mouse:move", onMove);
|
|
155
128
|
canvas.off("mouse:up", onUp);
|