@mhamz.01/easyflow-whiteboard 1.16.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.
@@ -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;CAC/C;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,2CAkezB"}
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 = (itemX, itemY, itemWidth, itemHeight, box) => {
132
- const canvas = fabricCanvas?.current;
133
- if (!canvas)
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
- // Get the offset of the actual canvas element within the consumer's page
136
- const rect = canvas.getElement().getBoundingClientRect();
137
- // Adjust screen coordinates to be relative to the canvas element
138
- const boxSceneX1 = (box.x1 - rect.left - canvasViewport.x) / canvasZoom;
139
- const boxSceneY1 = (box.y1 - rect.top - canvasViewport.y) / canvasZoom;
140
- const boxSceneX2 = (box.x2 - rect.left - canvasViewport.x) / canvasZoom;
141
- const boxSceneY2 = (box.y2 - rect.top - canvasViewport.y) / canvasZoom;
142
- const bX1 = Math.min(boxSceneX1, boxSceneX2);
143
- const bY1 = Math.min(boxSceneY1, boxSceneY2);
144
- const bX2 = Math.max(boxSceneX1, boxSceneX2);
145
- const bY2 = Math.max(boxSceneY1, boxSceneY2);
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 overflow-hidden", style: { zIndex: 50 }, onWheel: handleOverlayWheel, onClick: (e) => {
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: `translate3d(${canvasViewport.x}px, ${canvasViewport.y}px, 0)`,
364
- willChange: "transform",
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,4CAoQvC"}
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,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,SAsKnB,CAAC"}
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"}
@@ -54,11 +54,14 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
54
54
  });
55
55
  selRect.setCoords();
56
56
  canvas.renderAll();
57
+ // ← FIX: Add canvas element offset
58
+ const canvasElement = canvas.getElement();
59
+ const rect = canvasElement.getBoundingClientRect();
57
60
  setSelectionBox({
58
- x1: Math.min(selStart.x, p.x) * canvasZoom + canvasViewport.x,
59
- y1: Math.min(selStart.y, p.y) * canvasZoom + canvasViewport.y,
60
- x2: Math.max(selStart.x, p.x) * canvasZoom + canvasViewport.x,
61
- y2: Math.max(selStart.y, p.y) * canvasZoom + canvasViewport.y,
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,
62
65
  });
63
66
  });
64
67
  };
@@ -110,14 +113,6 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
110
113
  setActiveTool("select");
111
114
  }
112
115
  };
113
- const handleCanvasClick = (e) => {
114
- // If user clicks the canvas background (not an object), clear overlay selection
115
- if (!e.target) {
116
- // You should pass a setter to clear selected IDs in your overlay
117
- // setOverlaySelectedIds(new Set());
118
- }
119
- };
120
- canvas.on("mouse:down", handleCanvasClick);
121
116
  canvas.on("selection:created", onSelected);
122
117
  canvas.on("selection:updated", onSelected);
123
118
  canvas.on("selection:cleared", onDeselected);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhamz.01/easyflow-whiteboard",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "description": "A feature-rich whiteboard component built with Fabric.js and React",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",