@mhamz.01/easyflow-whiteboard 2.58.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,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,
|
|
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,8 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
import { ActiveSelection } from "fabric";
|
|
3
3
|
export const useCopyPaste = ({ fabricCanvas }) => {
|
|
4
|
-
|
|
5
|
-
const clipboardRef = useRef([]);
|
|
4
|
+
const clipboardRef = useRef(null);
|
|
6
5
|
useEffect(() => {
|
|
7
6
|
const handleKeyDown = async (e) => {
|
|
8
7
|
if (e.target instanceof HTMLInputElement ||
|
|
@@ -16,67 +15,45 @@ export const useCopyPaste = ({ fabricCanvas }) => {
|
|
|
16
15
|
const active = canvas.getActiveObject();
|
|
17
16
|
if (!active)
|
|
18
17
|
return;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const clones = await Promise.all(active.getObjects().map((obj) => obj.clone()));
|
|
23
|
-
// Snapshot each child's ABSOLUTE canvas position at copy time
|
|
24
|
-
// getObjects() inside activeSelection returns group-relative coords,
|
|
25
|
-
// so we must add the group's position to get true canvas coords
|
|
26
|
-
const groupLeft = active.left ?? 0;
|
|
27
|
-
const groupTop = active.top ?? 0;
|
|
28
|
-
const groupAngle = active.angle ?? 0;
|
|
29
|
-
clones.forEach((clone, i) => {
|
|
30
|
-
const src = active.getObjects()[i];
|
|
31
|
-
clone.set({
|
|
32
|
-
left: (src.left ?? 0) + groupLeft,
|
|
33
|
-
top: (src.top ?? 0) + groupTop,
|
|
34
|
-
angle: (src.angle ?? 0) + groupAngle,
|
|
35
|
-
});
|
|
36
|
-
clone.setCoords();
|
|
37
|
-
});
|
|
38
|
-
clipboardRef.current = clones;
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
// Single object — clone detached
|
|
42
|
-
const clone = await active.clone();
|
|
43
|
-
clipboardRef.current = [clone];
|
|
44
|
-
}
|
|
18
|
+
// Clone once at copy time — store as-is, exactly like official docs
|
|
19
|
+
const cloned = await active.clone();
|
|
20
|
+
clipboardRef.current = cloned;
|
|
45
21
|
return;
|
|
46
22
|
}
|
|
47
23
|
// ── Paste ────────────────────────────────────────────────────────────
|
|
48
24
|
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
49
|
-
|
|
25
|
+
const copied = clipboardRef.current;
|
|
26
|
+
if (!copied)
|
|
50
27
|
return;
|
|
28
|
+
// Clone again so multiple pastes work independently
|
|
29
|
+
const clonedObj = await copied.clone();
|
|
51
30
|
canvas.discardActiveObject();
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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);
|
|
63
44
|
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
addedObjects.push(clone);
|
|
67
|
-
}
|
|
68
|
-
if (addedObjects.length === 1) {
|
|
69
|
-
canvas.setActiveObject(addedObjects[0]);
|
|
45
|
+
// setCoords on the selection itself — not on individual children
|
|
46
|
+
clonedObj.setCoords();
|
|
70
47
|
}
|
|
71
48
|
else {
|
|
72
|
-
|
|
73
|
-
canvas.setActiveObject(newSelection);
|
|
49
|
+
canvas.add(clonedObj);
|
|
74
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);
|
|
75
56
|
canvas.requestRenderAll();
|
|
76
|
-
// Update clipboard with the newly pasted clones (detached copies)
|
|
77
|
-
// so next Ctrl+V offsets from current paste position
|
|
78
|
-
// CRITICAL: clone them immediately so they're detached from canvas
|
|
79
|
-
clipboardRef.current = await Promise.all(addedObjects.map((obj) => obj.clone()));
|
|
80
57
|
}
|
|
81
58
|
};
|
|
82
59
|
window.addEventListener("keydown", handleKeyDown);
|