@mhamz.01/easyflow-whiteboard 2.57.0 → 2.59.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":"useCopyPaste.d.ts","sourceRoot":"","sources":["../../src/hooks/useCopyPaste.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"useCopyPaste.d.ts","sourceRoot":"","sources":["../../src/hooks/useCopyPaste.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,EAAiC,MAAM,QAAQ,CAAC;AAE/D,UAAU,iBAAiB;IACzB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACxC;AAED,eAAO,MAAM,YAAY,GAAI,kBAAkB,iBAAiB,SAmE/D,CAAC"}
|
|
@@ -1,78 +1,62 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
|
+
import { ActiveSelection } from "fabric";
|
|
2
3
|
export const useCopyPaste = ({ fabricCanvas }) => {
|
|
3
|
-
// Clipboard lives in a ref — no React state needed, never causes re-renders
|
|
4
4
|
const clipboardRef = useRef(null);
|
|
5
5
|
useEffect(() => {
|
|
6
6
|
const handleKeyDown = async (e) => {
|
|
7
|
-
// Don't intercept when typing in inputs
|
|
8
7
|
if (e.target instanceof HTMLInputElement ||
|
|
9
8
|
e.target instanceof HTMLTextAreaElement)
|
|
10
9
|
return;
|
|
11
10
|
const canvas = fabricCanvas.current;
|
|
12
11
|
if (!canvas)
|
|
13
12
|
return;
|
|
14
|
-
// ── Copy
|
|
13
|
+
// ── Copy ────────────────────────────────────────────────────────────
|
|
15
14
|
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
16
15
|
const active = canvas.getActiveObject();
|
|
17
16
|
if (!active)
|
|
18
17
|
return;
|
|
19
|
-
//
|
|
20
|
-
// safe to store and paste multiple times
|
|
18
|
+
// Clone once at copy time — store as-is, exactly like official docs
|
|
21
19
|
const cloned = await active.clone();
|
|
22
20
|
clipboardRef.current = cloned;
|
|
23
21
|
return;
|
|
24
22
|
}
|
|
25
|
-
// ── Paste
|
|
23
|
+
// ── Paste ────────────────────────────────────────────────────────────
|
|
26
24
|
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
27
25
|
const copied = clipboardRef.current;
|
|
28
26
|
if (!copied)
|
|
29
27
|
return;
|
|
28
|
+
// Clone again so multiple pastes work independently
|
|
29
|
+
const clonedObj = await copied.clone();
|
|
30
30
|
canvas.discardActiveObject();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
angle: (obj.angle ?? 0) + groupAngle,
|
|
48
|
-
});
|
|
49
|
-
childClone.setCoords();
|
|
50
|
-
canvas.add(childClone);
|
|
51
|
-
addedObjects.push(childClone);
|
|
52
|
-
}
|
|
53
|
-
// Select all pasted objects together
|
|
54
|
-
if (addedObjects.length > 0) {
|
|
55
|
-
const { ActiveSelection } = await import("fabric");
|
|
56
|
-
const newSelection = new ActiveSelection(addedObjects, { canvas });
|
|
57
|
-
canvas.setActiveObject(newSelection);
|
|
58
|
-
// Update clipboard to new clones for cascading offset on next paste
|
|
59
|
-
clipboardRef.current = newSelection;
|
|
60
|
-
}
|
|
31
|
+
clonedObj.set({
|
|
32
|
+
left: clonedObj.left + 10,
|
|
33
|
+
top: clonedObj.top + 10,
|
|
34
|
+
evented: true,
|
|
35
|
+
});
|
|
36
|
+
if (clonedObj instanceof ActiveSelection) {
|
|
37
|
+
// Official docs pattern exactly:
|
|
38
|
+
// set canvas reference on the ActiveSelection FIRST,
|
|
39
|
+
// then use forEachObject to add children — not getObjects()
|
|
40
|
+
// forEachObject is the correct API for iterating ActiveSelection
|
|
41
|
+
clonedObj.canvas = canvas;
|
|
42
|
+
clonedObj.forEachObject((obj) => {
|
|
43
|
+
canvas.add(obj);
|
|
44
|
+
});
|
|
45
|
+
// setCoords on the selection itself — not on individual children
|
|
46
|
+
clonedObj.setCoords();
|
|
61
47
|
}
|
|
62
48
|
else {
|
|
63
|
-
|
|
64
|
-
left: (cloned.left ?? 0) + offset,
|
|
65
|
-
top: (cloned.top ?? 0) + offset,
|
|
66
|
-
});
|
|
67
|
-
cloned.setCoords();
|
|
68
|
-
canvas.add(cloned);
|
|
69
|
-
canvas.setActiveObject(cloned);
|
|
70
|
-
clipboardRef.current = cloned;
|
|
49
|
+
canvas.add(clonedObj);
|
|
71
50
|
}
|
|
51
|
+
// Official docs pattern: offset clipboard itself for cascading paste
|
|
52
|
+
// This is simpler and safer than storing new clones as clipboard
|
|
53
|
+
copied.top += 10;
|
|
54
|
+
copied.left += 10;
|
|
55
|
+
canvas.setActiveObject(clonedObj);
|
|
72
56
|
canvas.requestRenderAll();
|
|
73
57
|
}
|
|
74
58
|
};
|
|
75
59
|
window.addEventListener("keydown", handleKeyDown);
|
|
76
60
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
77
|
-
}, [fabricCanvas]);
|
|
61
|
+
}, [fabricCanvas]);
|
|
78
62
|
};
|