@mhamz.01/easyflow-whiteboard 2.54.0 → 2.56.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.
- package/dist/components/whiteboard/whiteboard-test.d.ts.map +1 -1
- package/dist/components/whiteboard/whiteboard-test.js +3 -0
- package/dist/hooks/useCanvasInit.d.ts.map +1 -1
- package/dist/hooks/useCanvasInit.js +3 -1
- package/dist/hooks/useCopyPaste.d.ts +8 -0
- package/dist/hooks/useCopyPaste.d.ts.map +1 -0
- package/dist/hooks/useCopyPaste.js +61 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whiteboard-test.d.ts","sourceRoot":"","sources":["../../../src/components/whiteboard/whiteboard-test.tsx"],"names":[],"mappings":"AAQE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"whiteboard-test.d.ts","sourceRoot":"","sources":["../../../src/components/whiteboard/whiteboard-test.tsx"],"names":[],"mappings":"AAQE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAoBlE,YAAY,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAIxE,UAAU,IAAI;IACZ,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,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,UAAU,qBAAqB;IAC7B,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;QACf,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;KACxB,CAAC;IACF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,GAAG,EAAE,CAAC;QACb,SAAS,EAAE,GAAG,EAAE,CAAC;KAClB,KAAK,IAAI,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,WAAW,EACX,MAAM,EACN,cAAc,GACf,EAAE,qBAAqB,2CA2PvB"}
|
|
@@ -21,6 +21,7 @@ import { useLiveUpdate } from "../../hooks/useLiveUpdate";
|
|
|
21
21
|
import { useMouseHandlers } from "../../hooks/useMouseHandlers";
|
|
22
22
|
import { usePersistence } from "../../hooks/usePersistance";
|
|
23
23
|
import { ToolbarSkeleton } from "../toolbar/toolbar-skeleton/toolbar-skeleton";
|
|
24
|
+
import { useCopyPaste } from "../../hooks/useCopyPaste";
|
|
24
25
|
classRegistry.setClass(Frame, "frame");
|
|
25
26
|
export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs, }) {
|
|
26
27
|
// Refs
|
|
@@ -100,6 +101,8 @@ export default function FabricWhiteboard({ initialData, onSave, saveDebounceMs,
|
|
|
100
101
|
pushHistory,
|
|
101
102
|
addCanvasObject,
|
|
102
103
|
});
|
|
104
|
+
// Copy-paste handlers
|
|
105
|
+
useCopyPaste({ fabricCanvas: fabricCanvasRef });
|
|
103
106
|
// Eraser handlers
|
|
104
107
|
const eraserHandlers = useEraser({
|
|
105
108
|
fabricCanvas: fabricCanvasRef,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCanvasInit.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasInit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAY,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKhC,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAK,MAAM,CAAC;IACnB,KAAK,CAAC,EAAM,GAAG,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;CACnB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAW,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACxD,eAAe,EAAK,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAU,MAAM,CAAC;IAC3B,kBAAkB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,cAAc,EAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,EAAS,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAY,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC3C,YAAY,EAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAQ,qBAAqB,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAID,eAAO,MAAM,aAAa,GAAI,4IAW3B,kBAAkB,
|
|
1
|
+
{"version":3,"file":"useCanvasInit.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasInit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAY,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKhC,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAK,MAAM,CAAC;IACnB,KAAK,CAAC,EAAM,GAAG,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;CACnB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAW,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACxD,eAAe,EAAK,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAU,MAAM,CAAC;IAC3B,kBAAkB,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,cAAc,EAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,EAAS,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAY,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC3C,YAAY,EAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAQ,qBAAqB,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAID,eAAO,MAAM,aAAa,GAAI,4IAW3B,kBAAkB,SA2HpB,CAAC"}
|
|
@@ -68,12 +68,14 @@ export const useCanvasInit = ({ canvasRef, fabricCanvasRef, activeTool, suppress
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
isRestoringRef.current = false;
|
|
71
|
-
|
|
71
|
+
if (!disposed)
|
|
72
|
+
onReady?.();
|
|
72
73
|
}
|
|
73
74
|
else {
|
|
74
75
|
if (!disposed) {
|
|
75
76
|
addWelcomeContent(canvas, "https://res.cloudinary.com/dqyjffc9q/image/upload/v1773420161/easyflow-logo_nbpfd7.png");
|
|
76
77
|
pushHistory(JSON.stringify(canvas.toJSON()));
|
|
78
|
+
onReady?.();
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RefObject } from "react";
|
|
2
|
+
import { Canvas } from "fabric";
|
|
3
|
+
interface UseCopyPasteProps {
|
|
4
|
+
fabricCanvas: RefObject<Canvas | null>;
|
|
5
|
+
}
|
|
6
|
+
export declare const useCopyPaste: ({ fabricCanvas }: UseCopyPasteProps) => void;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=useCopyPaste.d.ts.map
|
|
@@ -0,0 +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,EAAgB,MAAM,QAAQ,CAAC;AAE9C,UAAU,iBAAiB;IACzB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACxC;AAED,eAAO,MAAM,YAAY,GAAI,kBAAkB,iBAAiB,SAmE/D,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
export const useCopyPaste = ({ fabricCanvas }) => {
|
|
3
|
+
// Clipboard lives in a ref — no React state needed, never causes re-renders
|
|
4
|
+
const clipboardRef = useRef(null);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const handleKeyDown = async (e) => {
|
|
7
|
+
// Don't intercept when typing in inputs
|
|
8
|
+
if (e.target instanceof HTMLInputElement ||
|
|
9
|
+
e.target instanceof HTMLTextAreaElement)
|
|
10
|
+
return;
|
|
11
|
+
const canvas = fabricCanvas.current;
|
|
12
|
+
if (!canvas)
|
|
13
|
+
return;
|
|
14
|
+
// ── Copy ──────────────────────────────────────────────────────────────
|
|
15
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
16
|
+
const active = canvas.getActiveObject();
|
|
17
|
+
if (!active)
|
|
18
|
+
return;
|
|
19
|
+
// clone() returns a deep copy with no canvas reference —
|
|
20
|
+
// safe to store and paste multiple times
|
|
21
|
+
const cloned = await active.clone();
|
|
22
|
+
clipboardRef.current = cloned;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// ── Paste ─────────────────────────────────────────────────────────────
|
|
26
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
27
|
+
const copied = clipboardRef.current;
|
|
28
|
+
if (!copied)
|
|
29
|
+
return;
|
|
30
|
+
// Clone again from clipboard so paste can be repeated (Ctrl+V × N)
|
|
31
|
+
const cloned = await copied.clone();
|
|
32
|
+
// Offset so paste doesn't land exactly on top of the original
|
|
33
|
+
cloned.set({
|
|
34
|
+
left: (cloned.left ?? 0) + 20,
|
|
35
|
+
top: (cloned.top ?? 0) + 20,
|
|
36
|
+
// Clear any active selection state from the cloned object
|
|
37
|
+
evented: true,
|
|
38
|
+
});
|
|
39
|
+
// If it's a group/active selection, ungroup into individual objects
|
|
40
|
+
if (cloned.type === "activeSelection" && "getObjects" in cloned) {
|
|
41
|
+
cloned.canvas = canvas;
|
|
42
|
+
cloned.getObjects().forEach((obj) => {
|
|
43
|
+
canvas.add(obj);
|
|
44
|
+
});
|
|
45
|
+
cloned.setCoords();
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
canvas.add(cloned);
|
|
49
|
+
}
|
|
50
|
+
// Select the newly pasted object(s)
|
|
51
|
+
canvas.setActiveObject(cloned);
|
|
52
|
+
canvas.requestRenderAll();
|
|
53
|
+
// Update clipboard to the new clone so next paste offsets again
|
|
54
|
+
clipboardRef.current = cloned;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
59
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
60
|
+
}, [fabricCanvas]); // registered once — canvas read via ref
|
|
61
|
+
};
|