@mhamz.01/easyflow-whiteboard 2.4.0 → 2.5.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":"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,2CAofzB"}
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,2CAiezB"}
@@ -183,60 +183,33 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
183
183
  itemsToDrag = [itemId];
184
184
  setSelectedIds(new Set([itemId]));
185
185
  }
186
- // ── Read VPT first — single source of truth for coordinate space ──
187
- const vpt = fabricCanvas?.current?.viewportTransform;
188
- const liveZoom = vpt ? vpt[0] : 1;
189
- const liveVpX = vpt ? vpt[4] : 0;
190
- const liveVpY = vpt ? vpt[5] : 0;
191
- // ── Snapshot world positions from DOM directly ──
192
- // Avoids stale localTasks/localDocuments state by back-calculating
193
- // world position from the actual rendered screen position of the element
194
186
  const startPositions = new Map();
195
187
  itemsToDrag.forEach((id) => {
196
- const el = document.querySelector(`[data-node-id="${id}"]`);
197
- if (el) {
198
- const rect = el.getBoundingClientRect();
199
- // Back-calculate world coords from actual DOM position — always accurate
200
- const worldX = (rect.left - liveVpX) / liveZoom;
201
- const worldY = (rect.top - liveVpY) / liveZoom;
202
- startPositions.set(id, { x: worldX, y: worldY });
203
- }
204
- else {
205
- // Fallback to state if DOM element not found
206
- const pos = getItemPosition(id);
207
- if (pos)
208
- startPositions.set(id, pos);
209
- }
188
+ const pos = getItemPosition(id);
189
+ if (pos)
190
+ startPositions.set(id, pos);
210
191
  });
211
192
  const canvasObjectsStartPos = new Map();
212
193
  selectedCanvasObjects.forEach((obj) => {
213
194
  canvasObjectsStartPos.set(obj, { left: obj.left || 0, top: obj.top || 0 });
214
195
  });
215
- // ── Offset from pointer to node's top-left in screen space ──
216
- const clickedEl = document.querySelector(`[data-node-id="${itemId}"]`);
217
- let offsetX = 0;
218
- let offsetY = 0;
219
- if (clickedEl) {
220
- const rect = clickedEl.getBoundingClientRect();
221
- offsetX = pointer.clientX - rect.left;
222
- offsetY = pointer.clientY - rect.top;
223
- }
224
- else {
225
- const clickedPos = getItemPosition(itemId);
226
- if (!clickedPos)
227
- return;
228
- const screenX = clickedPos.x * liveZoom + liveVpX;
229
- const screenY = clickedPos.y * liveZoom + liveVpY;
230
- offsetX = pointer.clientX - screenX;
231
- offsetY = pointer.clientY - screenY;
232
- }
196
+ const clickedPos = getItemPosition(itemId);
197
+ if (!clickedPos)
198
+ return;
199
+ // ── FIX: Read VPT directly — never stale, always frame-perfect ──
200
+ const vpt = fabricCanvas?.current?.viewportTransform;
201
+ const liveZoom = vpt ? vpt[0] : 1;
202
+ const liveVpX = vpt ? vpt[4] : 0;
203
+ const liveVpY = vpt ? vpt[5] : 0;
204
+ const screenX = clickedPos.x * liveZoom + liveVpX;
205
+ const screenY = clickedPos.y * liveZoom + liveVpY;
233
206
  dragStateRef.current = {
234
207
  isDragging: true,
235
208
  itemIds: itemsToDrag,
236
209
  startPositions,
237
210
  canvasObjectsStartPos,
238
- offsetX,
239
- offsetY,
211
+ offsetX: pointer.clientX - screenX,
212
+ offsetY: pointer.clientY - screenY,
240
213
  };
241
214
  setDragging({ itemIds: itemsToDrag });
242
215
  document.body.style.cursor = "grabbing";
@@ -261,13 +234,12 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
261
234
  const firstStart = startPositions.get(firstId);
262
235
  if (!firstStart)
263
236
  return;
264
- // ── Live VPT — no React state, no staleness ──
237
+ // ── FIX: Read VPT live inside rAF perfectly in sync with Fabric ──
265
238
  const vpt = fabricCanvas?.current?.viewportTransform;
266
239
  const liveZoom = vpt ? vpt[0] : 1;
267
240
  const liveVpX = vpt ? vpt[4] : 0;
268
241
  const liveVpY = vpt ? vpt[5] : 0;
269
- // pointer - offset = top-left of node in screen space
270
- // subtract vpX, divide by zoom = world position
242
+ // Convert screen world using live VPT values
271
243
  const newX = (pointer.clientX - offsetX - liveVpX) / liveZoom;
272
244
  const newY = (pointer.clientY - offsetY - liveVpY) / liveZoom;
273
245
  const deltaX = newX - firstStart.x;
@@ -317,7 +289,7 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
317
289
  window.removeEventListener("touchend", handleEnd);
318
290
  window.removeEventListener("touchcancel", handleEnd);
319
291
  };
320
- }, [dragging, canvasZoom, canvasViewport, localTasks, localDocuments, fabricCanvas]);
292
+ }, [dragging, localTasks, localDocuments, fabricCanvas]);
321
293
  // ── Selection, Status, Keyboard Logic ────────────────────────────────────────
322
294
  const handleSelect = (id, e) => {
323
295
  if (e?.shiftKey || e?.ctrlKey || e?.metaKey) {
@@ -369,15 +341,22 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
369
341
  const renderItem = (id, x, y, children) => {
370
342
  const screenX = x * canvasZoom;
371
343
  const screenY = y * canvasZoom;
372
- const isDraggingItem = dragging?.itemIds.includes(id);
373
- return (_jsx("div", { "data-node-id": id, className: "pointer-events-auto absolute", style: {
344
+ // 1. Detect if the user is interacting with the canvas at all
345
+ // 'dragging' is your existing state.
346
+ // You might want to pass 'isZooming' or 'isPanning' from your main canvas component here.
347
+ const isDragging = dragging?.itemIds.includes(id);
348
+ return (_jsx("div", { className: "pointer-events-auto absolute", style: {
374
349
  left: 0,
375
350
  top: 0,
351
+ // 2. Use translate3d for GPU performance
376
352
  transform: `translate3d(${screenX}px, ${screenY}px, 0) scale(${canvasZoom})`,
377
353
  transformOrigin: "top left",
354
+ // 3. THE FIX: Remove transition entirely during any viewport change
355
+ // Any 'ease' during zoom causes the "shaking" behavior.
378
356
  transition: "none",
357
+ // 4. Optimization
379
358
  willChange: "transform",
380
- zIndex: isDraggingItem ? 1000 : 1,
359
+ zIndex: isDragging ? 1000 : 1,
381
360
  }, children: children }, id));
382
361
  };
383
362
  return (_jsx("div", { ref: overlayRef, className: "absolute inset-0 pointer-events-none", style: { zIndex: 50 }, onWheel: handleOverlayWheel, onClick: (e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhamz.01/easyflow-whiteboard",
3
- "version": "2.4.0",
3
+ "version": "2.5.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",