@mhamz.01/easyflow-whiteboard 2.7.0 → 2.9.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,
|
|
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"}
|
|
@@ -19,8 +19,6 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
19
19
|
});
|
|
20
20
|
const rafIdRef = useRef(null);
|
|
21
21
|
const overlayRef = useRef(null);
|
|
22
|
-
// Add this ref at top of CanvasOverlayLayer:
|
|
23
|
-
const isHtmlDraggingRef = useRef(false);
|
|
24
22
|
// ── Sync props → local state ────────────────────────────────────────────────
|
|
25
23
|
useEffect(() => { setLocalTasks(tasks); }, [tasks]);
|
|
26
24
|
useEffect(() => { setLocalDocuments(documents); }, [documents]);
|
|
@@ -91,20 +89,20 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
91
89
|
if (!canvas)
|
|
92
90
|
return;
|
|
93
91
|
const handleObjectMoving = (e) => {
|
|
94
|
-
// ── Skip if user is dragging via HTML node — avoid position conflict ──
|
|
95
|
-
if (isHtmlDraggingRef.current)
|
|
96
|
-
return;
|
|
97
92
|
const target = e.transform?.target || e.target;
|
|
98
93
|
if (!target)
|
|
99
94
|
return;
|
|
95
|
+
// 1. Calculate delta in raw Scene Coordinates
|
|
96
|
+
// We do NOT divide by zoom here because target.left/top are world units.
|
|
100
97
|
const deltaX = target.left - (target._prevLeft ?? target.left);
|
|
101
98
|
const deltaY = target.top - (target._prevTop ?? target.top);
|
|
102
99
|
target._prevLeft = target.left;
|
|
103
100
|
target._prevTop = target.top;
|
|
104
101
|
if (deltaX === 0 && deltaY === 0)
|
|
105
102
|
return;
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
// 2. Apply the raw delta to HTML items
|
|
104
|
+
setLocalTasks((prev) => prev.map((t) => (selectedIds.has(t.id) ? { ...t, x: t.x + deltaX, y: t.y + deltaY } : t)));
|
|
105
|
+
setLocalDocuments((prev) => prev.map((d) => (selectedIds.has(d.id) ? { ...d, x: d.x + deltaX, y: d.y + deltaY } : d)));
|
|
108
106
|
};
|
|
109
107
|
const handleMouseDown = (e) => {
|
|
110
108
|
const target = e.target;
|
|
@@ -174,116 +172,108 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
174
172
|
return e;
|
|
175
173
|
};
|
|
176
174
|
const handleDragStart = (itemId, e) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
//
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
175
|
+
const canvas = fabricCanvas?.current;
|
|
176
|
+
if (!canvas || !e)
|
|
177
|
+
return;
|
|
178
|
+
// Use the raw native event for the most accurate pointer position
|
|
179
|
+
const nativeEvent = 'nativeEvent' in e ? e.nativeEvent : e;
|
|
180
|
+
const pointer = getPointerEvent(nativeEvent);
|
|
181
|
+
// 1. Get the ABSOLUTE current transform from the canvas engine
|
|
182
|
+
const vpt = canvas.viewportTransform || [1, 0, 0, 1, 0, 0];
|
|
183
|
+
const zoom = vpt[0];
|
|
184
|
+
const vpX = vpt[4];
|
|
185
|
+
const vpY = vpt[5];
|
|
186
|
+
// 2. Calculate World Position of the mouse click immediately
|
|
187
|
+
const clickWorldX = (pointer.clientX - vpX) / zoom;
|
|
188
|
+
const clickWorldY = (pointer.clientY - vpY) / zoom;
|
|
189
|
+
// 3. Get the item's current world position
|
|
190
|
+
const clickedPos = getItemPosition(itemId);
|
|
191
|
+
if (!clickedPos)
|
|
192
|
+
return;
|
|
193
|
+
// 4. Calculate the WORLD OFFSET
|
|
194
|
+
// This is the distance from the mouse to the node's top-left in World Units.
|
|
195
|
+
const worldOffsetX = clickWorldX - clickedPos.x;
|
|
196
|
+
const worldOffsetY = clickWorldY - clickedPos.y;
|
|
197
|
+
// 5. Setup Drag State
|
|
198
|
+
let itemsToDrag = selectedIds.has(itemId) ? Array.from(selectedIds) : [itemId];
|
|
199
|
+
if (!selectedIds.has(itemId))
|
|
197
200
|
setSelectedIds(new Set([itemId]));
|
|
198
|
-
}
|
|
199
201
|
const startPositions = new Map();
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
itemsToDrag.forEach(id => {
|
|
203
|
+
const pos = getItemPosition(id);
|
|
204
|
+
if (pos)
|
|
205
|
+
startPositions.set(id, pos);
|
|
203
206
|
});
|
|
204
207
|
const canvasObjectsStartPos = new Map();
|
|
205
|
-
selectedCanvasObjects.forEach(
|
|
208
|
+
selectedCanvasObjects.forEach(obj => {
|
|
206
209
|
canvasObjectsStartPos.set(obj, { left: obj.left || 0, top: obj.top || 0 });
|
|
207
210
|
});
|
|
208
|
-
const clickedPos = startPositions.get(itemId);
|
|
209
|
-
if (!clickedPos)
|
|
210
|
-
return;
|
|
211
|
-
// ── World-space offset ──
|
|
212
|
-
const pointerWorldX = (pointer.clientX - liveVpX) / liveZoom;
|
|
213
|
-
const pointerWorldY = (pointer.clientY - liveVpY) / liveZoom;
|
|
214
|
-
const offsetWorldX = pointerWorldX - clickedPos.x;
|
|
215
|
-
const offsetWorldY = pointerWorldY - clickedPos.y;
|
|
216
211
|
dragStateRef.current = {
|
|
217
212
|
isDragging: true,
|
|
218
213
|
itemIds: itemsToDrag,
|
|
219
214
|
startPositions,
|
|
220
215
|
canvasObjectsStartPos,
|
|
221
|
-
offsetX:
|
|
222
|
-
offsetY:
|
|
216
|
+
offsetX: worldOffsetX,
|
|
217
|
+
offsetY: worldOffsetY,
|
|
223
218
|
};
|
|
224
|
-
// ── Block object:moving sync for the duration of this HTML drag ──
|
|
225
|
-
isHtmlDraggingRef.current = true;
|
|
226
219
|
setDragging({ itemIds: itemsToDrag });
|
|
227
|
-
document.body.style.cursor = "grabbing";
|
|
228
|
-
document.body.style.userSelect = "none";
|
|
229
|
-
document.body.style.touchAction = "none";
|
|
230
220
|
};
|
|
231
221
|
// ── Drag move (HTML Node side) ───────────────────────────────────────────────
|
|
232
222
|
useEffect(() => {
|
|
233
223
|
if (!dragging)
|
|
234
224
|
return;
|
|
225
|
+
// Inside the useEffect that watches [dragging, localTasks, localDocuments, fabricCanvas]
|
|
235
226
|
const handleMove = (e) => {
|
|
236
227
|
if (!dragStateRef.current.isDragging)
|
|
237
228
|
return;
|
|
238
229
|
if (e.cancelable)
|
|
239
230
|
e.preventDefault();
|
|
240
231
|
const pointer = getPointerEvent(e);
|
|
232
|
+
// 1. Throttle updates using requestAnimationFrame for 120Hz/144Hz screen support
|
|
241
233
|
if (rafIdRef.current !== null)
|
|
242
234
|
cancelAnimationFrame(rafIdRef.current);
|
|
235
|
+
// Inside handleMove rAF
|
|
243
236
|
rafIdRef.current = requestAnimationFrame(() => {
|
|
244
237
|
const { itemIds, startPositions, canvasObjectsStartPos, offsetX, offsetY } = dragStateRef.current;
|
|
238
|
+
const canvas = fabricCanvas?.current;
|
|
239
|
+
if (!canvas)
|
|
240
|
+
return;
|
|
241
|
+
const vpt = canvas.viewportTransform;
|
|
242
|
+
const zoom = vpt[0];
|
|
243
|
+
const vpX = vpt[4];
|
|
244
|
+
const vpY = vpt[5];
|
|
245
|
+
// Current World position of the mouse
|
|
246
|
+
const currentWorldX = (pointer.clientX - vpX) / zoom;
|
|
247
|
+
const currentWorldY = (pointer.clientY - vpY) / zoom;
|
|
248
|
+
// The new world position of the node we are holding
|
|
249
|
+
const newX = currentWorldX - offsetX;
|
|
250
|
+
const newY = currentWorldY - offsetY;
|
|
245
251
|
const firstId = itemIds[0];
|
|
246
252
|
const firstStart = startPositions.get(firstId);
|
|
247
253
|
if (!firstStart)
|
|
248
254
|
return;
|
|
249
|
-
// ── FIX: Read VPT live inside rAF — perfectly in sync with Fabric ──
|
|
250
|
-
const vpt = fabricCanvas?.current?.viewportTransform;
|
|
251
|
-
const liveZoom = vpt ? vpt[0] : 1;
|
|
252
|
-
const liveVpX = vpt ? vpt[4] : 0;
|
|
253
|
-
const liveVpY = vpt ? vpt[5] : 0;
|
|
254
|
-
// Convert screen → world using live VPT values
|
|
255
|
-
const newX = (pointer.clientX - offsetX - liveVpX) / liveZoom;
|
|
256
|
-
const newY = (pointer.clientY - offsetY - liveVpY) / liveZoom;
|
|
257
255
|
const deltaX = newX - firstStart.x;
|
|
258
256
|
const deltaY = newY - firstStart.y;
|
|
259
|
-
|
|
257
|
+
// UPDATE: Only update if the delta has actually changed to prevent jitter
|
|
258
|
+
if (Math.abs(deltaX) < 0.01 && Math.abs(deltaY) < 0.01)
|
|
259
|
+
return;
|
|
260
|
+
setLocalTasks(prev => prev.map(t => itemIds.includes(t.id) ? {
|
|
260
261
|
...t,
|
|
261
262
|
x: (startPositions.get(t.id)?.x ?? t.x) + deltaX,
|
|
262
263
|
y: (startPositions.get(t.id)?.y ?? t.y) + deltaY,
|
|
263
264
|
} : t));
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
canvasObjectsStartPos.forEach((startPos, obj) => {
|
|
271
|
-
obj.set({
|
|
272
|
-
left: startPos.left + deltaX,
|
|
273
|
-
top: startPos.top + deltaY,
|
|
274
|
-
});
|
|
275
|
-
obj.setCoords();
|
|
276
|
-
});
|
|
277
|
-
fabricCanvas.current.requestRenderAll();
|
|
278
|
-
}
|
|
265
|
+
// Sync Fabric objects directly
|
|
266
|
+
canvasObjectsStartPos.forEach((startPos, obj) => {
|
|
267
|
+
obj.set({ left: startPos.left + deltaX, top: startPos.top + deltaY });
|
|
268
|
+
obj.setCoords();
|
|
269
|
+
});
|
|
270
|
+
canvas.requestRenderAll();
|
|
279
271
|
});
|
|
280
272
|
};
|
|
281
273
|
const handleEnd = () => {
|
|
282
274
|
if (rafIdRef.current !== null)
|
|
283
275
|
cancelAnimationFrame(rafIdRef.current);
|
|
284
276
|
dragStateRef.current.isDragging = false;
|
|
285
|
-
// ── Re-enable object:moving sync ──
|
|
286
|
-
isHtmlDraggingRef.current = false;
|
|
287
277
|
setDragging(null);
|
|
288
278
|
document.body.style.cursor = "";
|
|
289
279
|
document.body.style.userSelect = "";
|
|
@@ -367,7 +357,7 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
367
357
|
transformOrigin: "top left",
|
|
368
358
|
// 3. THE FIX: Remove transition entirely during any viewport change
|
|
369
359
|
// Any 'ease' during zoom causes the "shaking" behavior.
|
|
370
|
-
transition:
|
|
360
|
+
transition: isDragging ? 'none' : 'transform 0.1s ease-out',
|
|
371
361
|
// 4. Optimization
|
|
372
362
|
willChange: "transform",
|
|
373
363
|
zIndex: isDragging ? 1000 : 1,
|