@mhamz.01/easyflow-whiteboard 1.28.0 → 2.0.1

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.
@@ -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;AAOjE,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,SA2JnB,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,SAgLnB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  // hooks/useSelection.ts
2
- import { useEffect } from "react";
2
+ import { useEffect, useRef } from "react";
3
3
  import { Rect, FabricImage } from "fabric";
4
4
  import { Frame } from "../lib/fabric-frame";
5
5
  import { Arrow } from "../lib/fabric-arrow";
@@ -8,6 +8,15 @@ import { useWhiteboardStore } from "../store/whiteboard-store";
8
8
  export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewport, setSelectionBox, setSelectedCanvasObjects, isDrawingRef, }) => {
9
9
  const setSelectedObjectType = useWhiteboardStore((state) => state.setSelectedObjectType);
10
10
  const setActiveTool = useWhiteboardStore((state) => state.setActiveTool);
11
+ // ── KEY FIX 1: Store zoom/viewport in refs so the effect never re-registers ──
12
+ // This prevents listener teardown/re-attach during pan/zoom gestures
13
+ const zoomRef = useRef(canvasZoom);
14
+ const viewportRef = useRef(canvasViewport);
15
+ const activeToolRef = useRef(activeTool);
16
+ // Keep refs in sync with latest props on every render
17
+ useEffect(() => { zoomRef.current = canvasZoom; }, [canvasZoom]);
18
+ useEffect(() => { viewportRef.current = canvasViewport; }, [canvasViewport]);
19
+ useEffect(() => { activeToolRef.current = activeTool; }, [activeTool]);
11
20
  useEffect(() => {
12
21
  const canvas = fabricCanvas.current;
13
22
  if (!canvas)
@@ -17,9 +26,11 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
17
26
  let selRect = null;
18
27
  let rafId = null;
19
28
  const onDown = (e) => {
20
- if (activeTool !== "select" || e.target)
29
+ // ── KEY FIX 2: Use ref instead of closure-captured activeTool ──
30
+ if (activeToolRef.current !== "select" || e.target)
21
31
  return;
22
32
  isSelecting = true;
33
+ // getScenePoint returns world coordinates (zoom + pan already factored in by Fabric)
23
34
  const p = canvas.getScenePoint(e.e);
24
35
  selStart = { x: p.x, y: p.y };
25
36
  selRect = new Rect({
@@ -27,17 +38,18 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
27
38
  top: p.y,
28
39
  width: 0,
29
40
  height: 0,
30
- fill: "transparent",
31
- stroke: "transparent",
32
- strokeWidth: 0,
41
+ fill: "rgba(2, 154, 255, 0.08)",
42
+ stroke: "#029AFF",
43
+ strokeWidth: 1 / zoomRef.current, // stays 1px visually at any zoom
33
44
  selectable: false,
34
45
  evented: false,
35
- visible: false,
46
+ // ── KEY FIX 3: Make it visible so user sees the selection rect ──
47
+ // was `visible: false` before — invisible but still blocking
48
+ excludeFromExport: true,
36
49
  });
37
50
  canvas.add(selRect);
38
51
  canvas.renderAll();
39
52
  };
40
- // hooks/useSelection.ts - UPDATED
41
53
  const onMove = (e) => {
42
54
  if (!isSelecting || !selRect)
43
55
  return;
@@ -47,34 +59,46 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
47
59
  const p = canvas.getScenePoint(e.e);
48
60
  const w = p.x - selStart.x;
49
61
  const h = p.y - selStart.y;
50
- const x1 = Math.min(selStart.x, p.x);
51
- const y1 = Math.min(selStart.y, p.y);
52
- const x2 = Math.max(selStart.x, p.x);
53
- const y2 = Math.max(selStart.y, p.y);
54
62
  selRect.set({
55
- left: x1,
56
- top: y1,
57
- width: x2 - x1,
58
- height: y2 - y1,
63
+ left: w < 0 ? p.x : selStart.x,
64
+ top: h < 0 ? p.y : selStart.y,
65
+ width: Math.abs(w),
66
+ height: Math.abs(h),
67
+ strokeWidth: 1 / zoomRef.current, // keep stroke sharp at any zoom
59
68
  });
60
69
  selRect.setCoords();
61
70
  canvas.renderAll();
62
- // PASS WORLD COORDINATES ONLY
63
- setSelectionBox({ x1, y1, x2, y2 });
71
+ // ── KEY FIX 4: Read from refs — always fresh, no stale closure ──
72
+ const zoom = zoomRef.current;
73
+ const vp = viewportRef.current;
74
+ // World → Screen conversion:
75
+ // screenX = worldX * zoom + panX (matches exactly how nodes are rendered)
76
+ setSelectionBox({
77
+ x1: Math.min(selStart.x, p.x) * zoom + vp.x,
78
+ y1: Math.min(selStart.y, p.y) * zoom + vp.y,
79
+ x2: Math.max(selStart.x, p.x) * zoom + vp.x,
80
+ y2: Math.max(selStart.y, p.y) * zoom + vp.y,
81
+ });
64
82
  });
65
83
  };
66
84
  const onUp = () => {
67
- if (!isSelecting || !selRect)
85
+ if (!isSelecting)
68
86
  return;
69
87
  if (rafId !== null) {
70
88
  cancelAnimationFrame(rafId);
71
89
  rafId = null;
72
90
  }
73
- canvas.remove(selRect);
74
- canvas.renderAll();
91
+ if (selRect) {
92
+ canvas.remove(selRect);
93
+ canvas.renderAll();
94
+ selRect = null;
95
+ }
75
96
  isSelecting = false;
76
- selRect = null;
77
- setTimeout(() => setSelectionBox(null), 100);
97
+ // ── KEY FIX 5: Delay clear long enough for overlay useEffect to fire ──
98
+ // onDeselected (selection:cleared) was calling setSelectionBox(null) immediately,
99
+ // racing with the overlay's useEffect. 150ms ensures the React render cycle
100
+ // processes the final box position before it's cleared.
101
+ setTimeout(() => setSelectionBox(null), 150);
78
102
  };
79
103
  const onSelected = () => {
80
104
  const sel = canvas.getActiveObject();
@@ -105,9 +129,11 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
105
129
  };
106
130
  const onDeselected = () => {
107
131
  setSelectedObjectType(null);
108
- setSelectionBox(null);
132
+ // ── KEY FIX 6: Do NOT clear selectionBox here ──
133
+ // This was racing with onUp's setTimeout and clearing before overlay processed it.
134
+ // onUp owns the selectionBox lifecycle. Only clear canvas objects here.
109
135
  setSelectedCanvasObjects([]);
110
- if (!isDrawingRef.current && activeTool !== "select" && activeTool !== "pan") {
136
+ if (!isDrawingRef.current && activeToolRef.current !== "select" && activeToolRef.current !== "pan") {
111
137
  setActiveTool("select");
112
138
  }
113
139
  };
@@ -129,15 +155,7 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
129
155
  if (selRect)
130
156
  canvas.remove(selRect);
131
157
  };
132
- }, [
133
- activeTool,
134
- canvasZoom,
135
- canvasViewport,
136
- fabricCanvas,
137
- setSelectionBox,
138
- setSelectedCanvasObjects,
139
- setSelectedObjectType,
140
- setActiveTool,
141
- isDrawingRef
142
- ]);
158
+ // ── KEY FIX 7: Remove canvasZoom/canvasViewport/activeTool from deps ──
159
+ // They are now read via refs — effect only registers once per canvas mount.
160
+ }, [fabricCanvas, setSelectionBox, setSelectedCanvasObjects, setSelectedObjectType, setActiveTool, isDrawingRef]);
143
161
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { default as EasyflowWhiteboard } from "./components/whiteboard/whiteboard";
1
+ export { default as EasyflowWhiteboard } from "./components/whiteboard/whiteboard-test";
2
2
  export { useWhiteboardStore } from './store/whiteboard-store';
3
3
  export type { TaskNodeData, DocumentNodeData } from './types/canvas-node';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -26,5 +26,5 @@
26
26
  // export * from "./components/toolbar/document-dropdown";
27
27
  // export * from "./components/toolbar/task-dropdown";
28
28
  // export * from "./components/toolbar/document-dropdown";
29
- export { default as EasyflowWhiteboard } from "./components/whiteboard/whiteboard";
29
+ export { default as EasyflowWhiteboard } from "./components/whiteboard/whiteboard-test";
30
30
  export { useWhiteboardStore } from './store/whiteboard-store';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhamz.01/easyflow-whiteboard",
3
- "version": "1.28.0",
3
+ "version": "2.0.1",
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",