@mhamz.01/easyflow-whiteboard 1.16.0 → 1.17.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.
@@ -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,EAA8B,MAAM,QAAQ,CAAC;AAMhF,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,SAsLnB,CAAC"}
@@ -17,7 +17,13 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
17
17
  let selRect = null;
18
18
  let rafId = null;
19
19
  const onDown = (e) => {
20
- if (activeTool !== "select" || e.target)
20
+ // 1. Handle Selection Clearing
21
+ if (!e.target) {
22
+ setSelectedCanvasObjects([]);
23
+ setSelectedObjectType(null);
24
+ }
25
+ // 2. Marquee Selection Logic
26
+ if (activeTool !== "select" || e.target || isDrawingRef.current)
21
27
  return;
22
28
  isSelecting = true;
23
29
  const p = canvas.getScenePoint(e.e);
@@ -27,12 +33,12 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
27
33
  top: p.y,
28
34
  width: 0,
29
35
  height: 0,
30
- fill: "transparent",
31
- stroke: "transparent",
32
- strokeWidth: 0,
36
+ fill: "rgba(0, 120, 255, 0.1)", // Slight tint helps debugging
37
+ stroke: "rgba(0, 120, 255, 0.5)",
38
+ strokeWidth: 1,
33
39
  selectable: false,
34
40
  evented: false,
35
- visible: false,
41
+ visible: false, // Keep invisible, we use the React Overlay for UI
36
42
  });
37
43
  canvas.add(selRect);
38
44
  canvas.renderAll();
@@ -46,19 +52,21 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
46
52
  const p = canvas.getScenePoint(e.e);
47
53
  const w = p.x - selStart.x;
48
54
  const h = p.y - selStart.y;
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
- });
55
+ const x1 = w < 0 ? p.x : selStart.x;
56
+ const y1 = h < 0 ? p.y : selStart.y;
57
+ const width = Math.abs(w);
58
+ const height = Math.abs(h);
59
+ selRect.set({ left: x1, top: y1, width, height });
55
60
  selRect.setCoords();
56
61
  canvas.renderAll();
62
+ // CRITICAL: Get the physical position of the canvas on the screen
63
+ // This prevents the "abrupt jump" when the library is inside a layout with margins/sidebars.
64
+ const rect = canvas.getElement().getBoundingClientRect();
57
65
  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,
66
+ x1: x1 * canvasZoom + canvasViewport.x + rect.left,
67
+ y1: y1 * canvasZoom + canvasViewport.y + rect.top,
68
+ x2: (x1 + width) * canvasZoom + canvasViewport.x + rect.left,
69
+ y2: (y1 + height) * canvasZoom + canvasViewport.y + rect.top,
62
70
  });
63
71
  });
64
72
  };
@@ -73,35 +81,54 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
73
81
  canvas.renderAll();
74
82
  isSelecting = false;
75
83
  selRect = null;
76
- setTimeout(() => setSelectionBox(null), 100);
84
+ // Delay clearing the box slightly to allow intersection logic to finish
85
+ setTimeout(() => setSelectionBox(null), 50);
77
86
  };
78
87
  const onSelected = () => {
79
88
  const sel = canvas.getActiveObject();
80
89
  if (!sel || isDrawingRef.current)
81
90
  return;
82
- setSelectedCanvasObjects(sel.type === "activeSelection" ? sel.getObjects() : [sel]);
91
+ const objects = sel.type === "activeSelection"
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);
83
100
  const typeMap = {
84
101
  rect: "rectangle",
85
102
  circle: "circle",
86
103
  line: "line",
87
- arrow: "arrow",
88
- "bidirectional-arrow": "arrow",
89
104
  "i-text": "text",
90
105
  text: "text",
91
106
  path: "pen",
92
107
  image: "image",
93
108
  };
94
- const t = sel instanceof Frame
95
- ? "frame"
96
- : sel instanceof FabricImage
97
- ? "image"
98
- : sel instanceof Arrow
99
- ? "arrow"
100
- : sel instanceof BidirectionalArrow
101
- ? "arrow"
102
- : typeMap[sel.type] ?? null;
109
+ let t = null;
110
+ if (sel instanceof Frame)
111
+ t = "frame";
112
+ else if (sel instanceof Arrow || sel instanceof BidirectionalArrow)
113
+ t = "arrow";
114
+ else if (sel instanceof FabricImage)
115
+ t = "image";
116
+ else
117
+ t = typeMap[sel.type] ?? "object";
103
118
  setSelectedObjectType(t);
104
119
  };
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
+ };
105
132
  const onDeselected = () => {
106
133
  setSelectedObjectType(null);
107
134
  setSelectionBox(null);
@@ -110,17 +137,11 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
110
137
  setActiveTool("select");
111
138
  }
112
139
  };
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);
140
+ // Event Binding
121
141
  canvas.on("selection:created", onSelected);
122
142
  canvas.on("selection:updated", onSelected);
123
143
  canvas.on("selection:cleared", onDeselected);
144
+ canvas.on("object:moving", onObjectMoving);
124
145
  canvas.on("mouse:down", onDown);
125
146
  canvas.on("mouse:move", onMove);
126
147
  canvas.on("mouse:up", onUp);
@@ -128,6 +149,7 @@ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewp
128
149
  canvas.off("selection:created", onSelected);
129
150
  canvas.off("selection:updated", onSelected);
130
151
  canvas.off("selection:cleared", onDeselected);
152
+ canvas.off("object:moving", onObjectMoving);
131
153
  canvas.off("mouse:down", onDown);
132
154
  canvas.off("mouse:move", onMove);
133
155
  canvas.off("mouse:up", onUp);
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.17.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",