@mhamz.01/easyflow-whiteboard 1.0.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.
Files changed (127) hide show
  1. package/dist/components/node/custom-node-overlay-layer.d.ts +44 -0
  2. package/dist/components/node/custom-node-overlay-layer.d.ts.map +1 -0
  3. package/dist/components/node/custom-node-overlay-layer.js +353 -0
  4. package/dist/components/node/custom-node.d.ts +17 -0
  5. package/dist/components/node/custom-node.d.ts.map +1 -0
  6. package/dist/components/node/custom-node.js +63 -0
  7. package/dist/components/node/document-node.d.ts +14 -0
  8. package/dist/components/node/document-node.d.ts.map +1 -0
  9. package/dist/components/node/document-node.js +58 -0
  10. package/dist/components/toolbar/document-dropdown.d.ts +14 -0
  11. package/dist/components/toolbar/document-dropdown.d.ts.map +1 -0
  12. package/dist/components/toolbar/document-dropdown.js +66 -0
  13. package/dist/components/toolbar/options/arrow-options.d.ts +8 -0
  14. package/dist/components/toolbar/options/arrow-options.d.ts.map +1 -0
  15. package/dist/components/toolbar/options/arrow-options.js +109 -0
  16. package/dist/components/toolbar/options/erase-option.d.ts +2 -0
  17. package/dist/components/toolbar/options/erase-option.d.ts.map +1 -0
  18. package/dist/components/toolbar/options/erase-option.js +20 -0
  19. package/dist/components/toolbar/options/image-options.d.ts +2 -0
  20. package/dist/components/toolbar/options/image-options.d.ts.map +1 -0
  21. package/dist/components/toolbar/options/image-options.js +10 -0
  22. package/dist/components/toolbar/options/line-options.d.ts +2 -0
  23. package/dist/components/toolbar/options/line-options.d.ts.map +1 -0
  24. package/dist/components/toolbar/options/line-options.js +46 -0
  25. package/dist/components/toolbar/options/pen-option.d.ts +2 -0
  26. package/dist/components/toolbar/options/pen-option.d.ts.map +1 -0
  27. package/dist/components/toolbar/options/pen-option.js +53 -0
  28. package/dist/components/toolbar/options/shape-option.d.ts +6 -0
  29. package/dist/components/toolbar/options/shape-option.d.ts.map +1 -0
  30. package/dist/components/toolbar/options/shape-option.js +58 -0
  31. package/dist/components/toolbar/options/text-option.d.ts +2 -0
  32. package/dist/components/toolbar/options/text-option.d.ts.map +1 -0
  33. package/dist/components/toolbar/options/text-option.js +73 -0
  34. package/dist/components/toolbar/task-dropdown.d.ts +15 -0
  35. package/dist/components/toolbar/task-dropdown.d.ts.map +1 -0
  36. package/dist/components/toolbar/task-dropdown.js +85 -0
  37. package/dist/components/toolbar/toolbar-button.d.ts +12 -0
  38. package/dist/components/toolbar/toolbar-button.d.ts.map +1 -0
  39. package/dist/components/toolbar/toolbar-button.js +8 -0
  40. package/dist/components/toolbar/toolbar-seperator.d.ts +6 -0
  41. package/dist/components/toolbar/toolbar-seperator.d.ts.map +1 -0
  42. package/dist/components/toolbar/toolbar-seperator.js +5 -0
  43. package/dist/components/toolbar/tooloptions-panel.d.ts +8 -0
  44. package/dist/components/toolbar/tooloptions-panel.d.ts.map +1 -0
  45. package/dist/components/toolbar/tooloptions-panel.js +88 -0
  46. package/dist/components/toolbar/whiteboard-toolbar.d.ts +28 -0
  47. package/dist/components/toolbar/whiteboard-toolbar.d.ts.map +1 -0
  48. package/dist/components/toolbar/whiteboard-toolbar.js +160 -0
  49. package/dist/components/ui/dropdown-menu.d.ts +26 -0
  50. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  51. package/dist/components/ui/dropdown-menu.js +51 -0
  52. package/dist/components/ui/label.d.ts +5 -0
  53. package/dist/components/ui/label.d.ts.map +1 -0
  54. package/dist/components/ui/label.js +8 -0
  55. package/dist/components/ui/slider.d.ts +5 -0
  56. package/dist/components/ui/slider.d.ts.map +1 -0
  57. package/dist/components/ui/slider.js +14 -0
  58. package/dist/components/whiteboard/whiteboard-test.d.ts +2 -0
  59. package/dist/components/whiteboard/whiteboard-test.d.ts.map +1 -0
  60. package/dist/components/whiteboard/whiteboard-test.js +207 -0
  61. package/dist/components/whiteboard/whiteboard.d.ts +1 -0
  62. package/dist/components/whiteboard/whiteboard.d.ts.map +1 -0
  63. package/dist/components/whiteboard/whiteboard.js +911 -0
  64. package/dist/components/zoomcontrol/zoom-control.d.ts +9 -0
  65. package/dist/components/zoomcontrol/zoom-control.d.ts.map +1 -0
  66. package/dist/components/zoomcontrol/zoom-control.js +7 -0
  67. package/dist/hooks/useCanvasInit.d.ts +15 -0
  68. package/dist/hooks/useCanvasInit.d.ts.map +1 -0
  69. package/dist/hooks/useCanvasInit.js +89 -0
  70. package/dist/hooks/useDrawing.d.ts +23 -0
  71. package/dist/hooks/useDrawing.d.ts.map +1 -0
  72. package/dist/hooks/useDrawing.js +142 -0
  73. package/dist/hooks/useEraser.d.ts +27 -0
  74. package/dist/hooks/useEraser.d.ts.map +1 -0
  75. package/dist/hooks/useEraser.js +143 -0
  76. package/dist/hooks/useLiveUpdate.d.ts +9 -0
  77. package/dist/hooks/useLiveUpdate.d.ts.map +1 -0
  78. package/dist/hooks/useLiveUpdate.js +63 -0
  79. package/dist/hooks/useMouseHandlers.d.ts +25 -0
  80. package/dist/hooks/useMouseHandlers.d.ts.map +1 -0
  81. package/dist/hooks/useMouseHandlers.js +44 -0
  82. package/dist/hooks/usePan.d.ts +17 -0
  83. package/dist/hooks/usePan.d.ts.map +1 -0
  84. package/dist/hooks/usePan.js +80 -0
  85. package/dist/hooks/usePersistance.d.ts +13 -0
  86. package/dist/hooks/usePersistance.d.ts.map +1 -0
  87. package/dist/hooks/usePersistance.js +79 -0
  88. package/dist/hooks/useSelection.d.ts +21 -0
  89. package/dist/hooks/useSelection.d.ts.map +1 -0
  90. package/dist/hooks/useSelection.js +142 -0
  91. package/dist/hooks/useTextStyle.d.ts +9 -0
  92. package/dist/hooks/useTextStyle.d.ts.map +1 -0
  93. package/dist/hooks/useTextStyle.js +32 -0
  94. package/dist/hooks/useToolManager.d.ts +15 -0
  95. package/dist/hooks/useToolManager.d.ts.map +1 -0
  96. package/dist/hooks/useToolManager.js +115 -0
  97. package/dist/hooks/useZoom.d.ts +25 -0
  98. package/dist/hooks/useZoom.d.ts.map +1 -0
  99. package/dist/hooks/useZoom.js +133 -0
  100. package/dist/index.d.ts +4 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +30 -0
  103. package/dist/lib/eraser-brush.d.ts +1 -0
  104. package/dist/lib/eraser-brush.d.ts.map +1 -0
  105. package/dist/lib/eraser-brush.js +21 -0
  106. package/dist/lib/fabric-arrow.d.ts +16 -0
  107. package/dist/lib/fabric-arrow.d.ts.map +1 -0
  108. package/dist/lib/fabric-arrow.js +50 -0
  109. package/dist/lib/fabric-bidirectional-arrow.d.ts +20 -0
  110. package/dist/lib/fabric-bidirectional-arrow.d.ts.map +1 -0
  111. package/dist/lib/fabric-bidirectional-arrow.js +65 -0
  112. package/dist/lib/fabric-frame.d.ts +7 -0
  113. package/dist/lib/fabric-frame.d.ts.map +1 -0
  114. package/dist/lib/fabric-frame.js +25 -0
  115. package/dist/lib/fabric-utils.d.ts +30 -0
  116. package/dist/lib/fabric-utils.d.ts.map +1 -0
  117. package/dist/lib/fabric-utils.js +273 -0
  118. package/dist/lib/utils.d.ts +3 -0
  119. package/dist/lib/utils.d.ts.map +1 -0
  120. package/dist/lib/utils.js +5 -0
  121. package/dist/store/whiteboard-store.d.ts +99 -0
  122. package/dist/store/whiteboard-store.d.ts.map +1 -0
  123. package/dist/store/whiteboard-store.js +137 -0
  124. package/dist/types/canvas-node.d.ts +24 -0
  125. package/dist/types/canvas-node.d.ts.map +1 -0
  126. package/dist/types/canvas-node.js +1 -0
  127. package/package.json +34 -0
@@ -0,0 +1,80 @@
1
+ import { useEffect } from "react";
2
+ export const usePan = ({ fabricCanvas, activeTool, handleZoom, setCanvasViewport, }) => {
3
+ useEffect(() => {
4
+ const canvas = fabricCanvas.current;
5
+ if (!canvas)
6
+ return;
7
+ const canvasEl = canvas.getElement();
8
+ canvasEl.style.touchAction = "none";
9
+ let isPanning = false;
10
+ let lastX = 0;
11
+ let lastY = 0;
12
+ let lastTouchDistance = 0;
13
+ const onDown = (opt) => {
14
+ if (activeTool !== "pan")
15
+ return;
16
+ const e = opt.e;
17
+ // Pinch initialization
18
+ if (e.touches && e.touches.length === 2) {
19
+ isPanning = false;
20
+ lastTouchDistance = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
21
+ return;
22
+ }
23
+ // Pan initialization
24
+ const pointer = e.touches ? e.touches[0] : e;
25
+ isPanning = true;
26
+ lastX = pointer.clientX;
27
+ lastY = pointer.clientY;
28
+ canvas.setCursor("grabbing");
29
+ };
30
+ const onMove = (opt) => {
31
+ if (activeTool !== "pan")
32
+ return;
33
+ const e = opt.e;
34
+ // Handle pinch zoom (two fingers)
35
+ if (e.touches && e.touches.length === 2) {
36
+ const currentDistance = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
37
+ if (lastTouchDistance > 0) {
38
+ const zoom = canvas.getZoom();
39
+ const delta = (currentDistance - lastTouchDistance) * 0.01;
40
+ const newZoom = zoom + delta;
41
+ const midX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
42
+ const midY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
43
+ const rect = canvasEl.getBoundingClientRect();
44
+ handleZoom(newZoom, { x: midX - rect.left, y: midY - rect.top });
45
+ }
46
+ lastTouchDistance = currentDistance;
47
+ return;
48
+ }
49
+ // Handle panning (one finger or mouse)
50
+ if (isPanning) {
51
+ const pointer = e.touches ? e.touches[0] : e;
52
+ const vpt = canvas.viewportTransform;
53
+ if (vpt) {
54
+ vpt[4] += pointer.clientX - lastX;
55
+ vpt[5] += pointer.clientY - lastY;
56
+ canvas.requestRenderAll();
57
+ }
58
+ lastX = pointer.clientX;
59
+ lastY = pointer.clientY;
60
+ }
61
+ };
62
+ const onUp = () => {
63
+ const vpt = canvas.viewportTransform;
64
+ if (vpt) {
65
+ setCanvasViewport({ x: vpt[4], y: vpt[5] });
66
+ }
67
+ isPanning = false;
68
+ lastTouchDistance = 0;
69
+ canvas.setCursor(activeTool === "pan" ? "grab" : "default");
70
+ };
71
+ canvas.on("mouse:down", onDown);
72
+ canvas.on("mouse:move", onMove);
73
+ canvas.on("mouse:up", onUp);
74
+ return () => {
75
+ canvas.off("mouse:down", onDown);
76
+ canvas.off("mouse:move", onMove);
77
+ canvas.off("mouse:up", onUp);
78
+ };
79
+ }, [activeTool, handleZoom, setCanvasViewport, fabricCanvas]);
80
+ };
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import { Canvas } from "fabric";
3
+ interface UsePersistenceProps {
4
+ fabricCanvas: React.RefObject<Canvas | null>;
5
+ tasks: any[];
6
+ documents: any[];
7
+ pushHistory: (state: string) => void;
8
+ isRestoringRef: React.RefObject<boolean>;
9
+ suppressHistoryRef: React.RefObject<boolean>;
10
+ }
11
+ export declare const usePersistence: ({ fabricCanvas, tasks, documents, pushHistory, isRestoringRef, suppressHistoryRef, }: UsePersistenceProps) => void;
12
+ export {};
13
+ //# sourceMappingURL=usePersistance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePersistance.d.ts","sourceRoot":"","sources":["../../src/hooks/usePersistance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,UAAU,mBAAmB;IAC3B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzC,kBAAkB,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;CAC9C;AAKD,eAAO,MAAM,cAAc,GAAI,sFAO5B,mBAAmB,SAgFrB,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { useEffect, useCallback } from "react";
2
+ const CANVAS_KEY = "easyflow_whiteboard_canvas";
3
+ const NODES_KEY = "easyflow_whiteboard_nodes";
4
+ export const usePersistence = ({ fabricCanvas, tasks, documents, pushHistory, isRestoringRef, suppressHistoryRef, }) => {
5
+ // ── SAVE CANVAS LOGIC ────────────────────────────────────────────────
6
+ const saveCanvas = useCallback(() => {
7
+ const canvas = fabricCanvas.current;
8
+ if (!canvas || isRestoringRef.current || suppressHistoryRef.current)
9
+ return;
10
+ const performSave = () => {
11
+ try {
12
+ const json = JSON.stringify(canvas.toJSON());
13
+ localStorage.setItem(CANVAS_KEY, json);
14
+ pushHistory(json);
15
+ console.log("💾 Canvas saved via requestIdleCallback");
16
+ }
17
+ catch (err) {
18
+ console.error("Canvas save failed:", err);
19
+ }
20
+ };
21
+ // Use requestIdleCallback for zero-lag background processing
22
+ if (typeof window.requestIdleCallback === "function") {
23
+ window.requestIdleCallback(performSave);
24
+ }
25
+ else {
26
+ performSave();
27
+ }
28
+ }, [pushHistory, fabricCanvas, isRestoringRef, suppressHistoryRef]);
29
+ // ── SAVE NODES LOGIC ─────────────────────────────────────────────────
30
+ const saveNodes = useCallback(() => {
31
+ if (isRestoringRef.current)
32
+ return;
33
+ const performSave = () => {
34
+ try {
35
+ const nodesData = { tasks, documents };
36
+ localStorage.setItem(NODES_KEY, JSON.stringify(nodesData));
37
+ console.log("💾 Custom nodes saved");
38
+ }
39
+ catch (err) {
40
+ console.error("Nodes save failed:", err);
41
+ }
42
+ };
43
+ if (typeof window.requestIdleCallback === "function") {
44
+ window.requestIdleCallback(performSave);
45
+ }
46
+ else {
47
+ performSave();
48
+ }
49
+ }, [tasks, documents, isRestoringRef]);
50
+ // ── EFFECT: CANVAS EVENT LISTENERS ──────────────────────────────────
51
+ useEffect(() => {
52
+ const canvas = fabricCanvas.current;
53
+ if (!canvas)
54
+ return;
55
+ let saveTimeout; // ← Changed
56
+ const debouncedSave = (opt) => {
57
+ if (opt?.target?.excludeFromExport)
58
+ return;
59
+ clearTimeout(saveTimeout);
60
+ saveTimeout = window.setTimeout(saveCanvas, 2000); // ← Use window.setTimeout
61
+ };
62
+ canvas.on("object:modified", debouncedSave);
63
+ canvas.on("object:added", debouncedSave);
64
+ canvas.on("object:removed", debouncedSave);
65
+ canvas.on("path:created", debouncedSave);
66
+ return () => {
67
+ clearTimeout(saveTimeout);
68
+ canvas.off("object:modified", debouncedSave);
69
+ canvas.off("object:added", debouncedSave);
70
+ canvas.off("object:removed", debouncedSave);
71
+ canvas.off("path:created", debouncedSave);
72
+ };
73
+ }, [saveCanvas, fabricCanvas]);
74
+ // ── EFFECT: REACT STATE (NODES) WATCHER ─────────────────────────────
75
+ useEffect(() => {
76
+ const timeout = setTimeout(saveNodes, 2000);
77
+ return () => clearTimeout(timeout);
78
+ }, [saveNodes]);
79
+ };
@@ -0,0 +1,21 @@
1
+ import { Canvas, FabricObject } from "fabric";
2
+ interface UseSelectionProps {
3
+ fabricCanvas: React.RefObject<Canvas | null>;
4
+ activeTool: string;
5
+ canvasZoom: number;
6
+ canvasViewport: {
7
+ x: number;
8
+ y: number;
9
+ };
10
+ setSelectionBox: (box: {
11
+ x1: number;
12
+ y1: number;
13
+ x2: number;
14
+ y2: number;
15
+ } | null) => void;
16
+ setSelectedCanvasObjects: (objects: FabricObject[]) => void;
17
+ isDrawingRef: React.MutableRefObject<boolean>;
18
+ }
19
+ export declare const useSelection: ({ fabricCanvas, activeTool, canvasZoom, canvasViewport, setSelectionBox, setSelectedCanvasObjects, isDrawingRef, }: UseSelectionProps) => void;
20
+ export {};
21
+ //# sourceMappingURL=useSelection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSelection.d.ts","sourceRoot":"","sources":["../../src/hooks/useSelection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAQ,YAAY,EAAe,MAAM,QAAQ,CAAC;AAMjE,UAAU,iBAAiB;IACzB,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,eAAe,EAAE,CAAC,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1F,wBAAwB,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAC5D,YAAY,EAAE,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAC/C;AAED,eAAO,MAAM,YAAY,GAAI,oHAQ1B,iBAAiB,SA0JnB,CAAC"}
@@ -0,0 +1,142 @@
1
+ // hooks/useSelection.ts
2
+ import { useEffect } from "react";
3
+ import { Rect, FabricImage } from "fabric";
4
+ import { Frame } from "../lib/fabric-frame";
5
+ import { Arrow } from "../lib/fabric-arrow";
6
+ import { BidirectionalArrow } from "../lib/fabric-bidirectional-arrow";
7
+ import { useWhiteboardStore } from "../store/whiteboard-store";
8
+ export const useSelection = ({ fabricCanvas, activeTool, canvasZoom, canvasViewport, setSelectionBox, setSelectedCanvasObjects, isDrawingRef, }) => {
9
+ const setSelectedObjectType = useWhiteboardStore((state) => state.setSelectedObjectType);
10
+ const setActiveTool = useWhiteboardStore((state) => state.setActiveTool);
11
+ useEffect(() => {
12
+ const canvas = fabricCanvas.current;
13
+ if (!canvas)
14
+ return;
15
+ let isSelecting = false;
16
+ let selStart = { x: 0, y: 0 };
17
+ let selRect = null;
18
+ let rafId = null;
19
+ const onDown = (e) => {
20
+ if (activeTool !== "select" || e.target)
21
+ return;
22
+ isSelecting = true;
23
+ const p = canvas.getScenePoint(e.e);
24
+ selStart = { x: p.x, y: p.y };
25
+ selRect = new Rect({
26
+ left: p.x,
27
+ top: p.y,
28
+ width: 0,
29
+ height: 0,
30
+ fill: "transparent",
31
+ stroke: "transparent",
32
+ strokeWidth: 0,
33
+ selectable: false,
34
+ evented: false,
35
+ visible: false,
36
+ });
37
+ canvas.add(selRect);
38
+ canvas.renderAll();
39
+ };
40
+ const onMove = (e) => {
41
+ if (!isSelecting || !selRect)
42
+ return;
43
+ if (rafId !== null)
44
+ cancelAnimationFrame(rafId);
45
+ rafId = requestAnimationFrame(() => {
46
+ const p = canvas.getScenePoint(e.e);
47
+ const w = p.x - selStart.x;
48
+ const h = p.y - selStart.y;
49
+ selRect.set({
50
+ left: w < 0 ? p.x : selStart.x,
51
+ top: h < 0 ? p.y : selStart.y,
52
+ width: Math.abs(w),
53
+ height: Math.abs(h),
54
+ });
55
+ selRect.setCoords();
56
+ canvas.renderAll();
57
+ setSelectionBox({
58
+ x1: Math.min(selStart.x, p.x) * canvasZoom + canvasViewport.x,
59
+ y1: Math.min(selStart.y, p.y) * canvasZoom + canvasViewport.y,
60
+ x2: Math.max(selStart.x, p.x) * canvasZoom + canvasViewport.x,
61
+ y2: Math.max(selStart.y, p.y) * canvasZoom + canvasViewport.y,
62
+ });
63
+ });
64
+ };
65
+ const onUp = () => {
66
+ if (!isSelecting || !selRect)
67
+ return;
68
+ if (rafId !== null) {
69
+ cancelAnimationFrame(rafId);
70
+ rafId = null;
71
+ }
72
+ canvas.remove(selRect);
73
+ canvas.renderAll();
74
+ isSelecting = false;
75
+ selRect = null;
76
+ setTimeout(() => setSelectionBox(null), 100);
77
+ };
78
+ const onSelected = () => {
79
+ const sel = canvas.getActiveObject();
80
+ if (!sel || isDrawingRef.current)
81
+ return;
82
+ setSelectedCanvasObjects(sel.type === "activeSelection" ? sel.getObjects() : [sel]);
83
+ const typeMap = {
84
+ rect: "rectangle",
85
+ circle: "circle",
86
+ line: "line",
87
+ arrow: "arrow",
88
+ "bidirectional-arrow": "arrow",
89
+ "i-text": "text",
90
+ text: "text",
91
+ path: "pen",
92
+ image: "image",
93
+ };
94
+ const t = sel instanceof Frame
95
+ ? "frame"
96
+ : sel instanceof FabricImage
97
+ ? "image"
98
+ : sel instanceof Arrow
99
+ ? "arrow"
100
+ : sel instanceof BidirectionalArrow
101
+ ? "arrow"
102
+ : typeMap[sel.type] ?? null;
103
+ setSelectedObjectType(t);
104
+ };
105
+ const onDeselected = () => {
106
+ setSelectedObjectType(null);
107
+ setSelectionBox(null);
108
+ setSelectedCanvasObjects([]);
109
+ if (!isDrawingRef.current && activeTool !== "select" && activeTool !== "pan") {
110
+ setActiveTool("select");
111
+ }
112
+ };
113
+ canvas.on("selection:created", onSelected);
114
+ canvas.on("selection:updated", onSelected);
115
+ canvas.on("selection:cleared", onDeselected);
116
+ canvas.on("mouse:down", onDown);
117
+ canvas.on("mouse:move", onMove);
118
+ canvas.on("mouse:up", onUp);
119
+ return () => {
120
+ canvas.off("selection:created", onSelected);
121
+ canvas.off("selection:updated", onSelected);
122
+ canvas.off("selection:cleared", onDeselected);
123
+ canvas.off("mouse:down", onDown);
124
+ canvas.off("mouse:move", onMove);
125
+ canvas.off("mouse:up", onUp);
126
+ if (rafId !== null)
127
+ cancelAnimationFrame(rafId);
128
+ if (selRect)
129
+ canvas.remove(selRect);
130
+ };
131
+ }, [
132
+ activeTool,
133
+ canvasZoom,
134
+ canvasViewport,
135
+ fabricCanvas,
136
+ setSelectionBox,
137
+ setSelectedCanvasObjects,
138
+ setSelectedObjectType,
139
+ setActiveTool,
140
+ isDrawingRef
141
+ ]);
142
+ };
@@ -0,0 +1,9 @@
1
+ import { RefObject } from "react";
2
+ import { Canvas } from "fabric";
3
+ interface UseTextStyleProps {
4
+ fabricCanvas: RefObject<Canvas | null>;
5
+ toolOptions: any;
6
+ }
7
+ export declare const useTextStyle: ({ fabricCanvas, toolOptions }: UseTextStyleProps) => void;
8
+ export {};
9
+ //# sourceMappingURL=useTextStyle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTextStyle.d.ts","sourceRoot":"","sources":["../../src/hooks/useTextStyle.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAE9C,UAAU,iBAAiB;IACzB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,WAAW,EAAE,GAAG,CAAC;CAClB;AAED,eAAO,MAAM,YAAY,GAAI,+BAA+B,iBAAiB,SAiC5E,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { useEffect } from "react";
2
+ export const useTextStyle = ({ fabricCanvas, toolOptions }) => {
3
+ useEffect(() => {
4
+ const canvas = fabricCanvas.current;
5
+ if (!canvas)
6
+ return;
7
+ const activeObject = canvas.getActiveObject();
8
+ if (!activeObject)
9
+ return;
10
+ const updateTextStyle = (obj) => {
11
+ if (obj.type !== "i-text" && obj.type !== "text")
12
+ return;
13
+ const options = toolOptions.text;
14
+ obj.set({
15
+ fontSize: options.fontSize,
16
+ fontFamily: options.fontFamily,
17
+ fontWeight: options.fontWeight,
18
+ fill: options.color,
19
+ textAlign: options.textAlign,
20
+ });
21
+ obj.dirty = true;
22
+ };
23
+ if (activeObject.type === "activeSelection") {
24
+ const objects = activeObject.getObjects();
25
+ objects.forEach((obj) => updateTextStyle(obj));
26
+ }
27
+ else {
28
+ updateTextStyle(activeObject);
29
+ }
30
+ canvas.requestRenderAll();
31
+ }, [toolOptions.text, fabricCanvas]);
32
+ };
@@ -0,0 +1,15 @@
1
+ import { RefObject } from "react";
2
+ import { Canvas, Circle } from "fabric";
3
+ import * as fabric from "fabric";
4
+ interface UseToolManagerProps {
5
+ fabricCanvas: RefObject<Canvas | null>;
6
+ activeTool: string;
7
+ toolOptions: any;
8
+ eraserTraceRef: RefObject<Circle | null>;
9
+ eraserPathRef: RefObject<fabric.Path | null>;
10
+ eraserPathPointsRef: RefObject<string[]>;
11
+ eraserTargetsRef: RefObject<Set<any>>;
12
+ }
13
+ export declare const useToolManager: ({ fabricCanvas, activeTool, toolOptions, eraserTraceRef, eraserPathRef, eraserPathPointsRef, eraserTargetsRef, }: UseToolManagerProps) => void;
14
+ export {};
15
+ //# sourceMappingURL=useToolManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToolManager.d.ts","sourceRoot":"","sources":["../../src/hooks/useToolManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,UAAU,mBAAmB;IAC3B,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,cAAc,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC7C,mBAAmB,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,gBAAgB,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;CACvC;AAED,eAAO,MAAM,cAAc,GAAI,kHAQ5B,mBAAmB,SA8HrB,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { useEffect } from "react";
2
+ import { calculateDashArray } from "../lib/fabric-utils";
3
+ export const useToolManager = ({ fabricCanvas, activeTool, toolOptions, eraserTraceRef, eraserPathRef, eraserPathPointsRef, eraserTargetsRef, }) => {
4
+ useEffect(() => {
5
+ const canvas = fabricCanvas.current;
6
+ if (!canvas)
7
+ return;
8
+ // Clean up eraser trace when switching tools
9
+ if (activeTool !== "eraser" && eraserTraceRef.current) {
10
+ canvas.remove(eraserTraceRef.current);
11
+ eraserTraceRef.current = null;
12
+ eraserTargetsRef.current.clear();
13
+ if (eraserPathRef.current) {
14
+ canvas.remove(eraserPathRef.current);
15
+ eraserPathRef.current = null;
16
+ eraserPathPointsRef.current = [];
17
+ }
18
+ }
19
+ switch (activeTool) {
20
+ case "select":
21
+ canvas.isDrawingMode = false;
22
+ canvas.selection = true;
23
+ canvas.defaultCursor = "default";
24
+ canvas.hoverCursor = "move";
25
+ canvas.forEachObject((obj) => {
26
+ obj.selectable = true;
27
+ obj.evented = true;
28
+ obj.hoverCursor = "move";
29
+ });
30
+ break;
31
+ case "pan":
32
+ canvas.isDrawingMode = false;
33
+ canvas.selection = false;
34
+ canvas.defaultCursor = "grab";
35
+ canvas.hoverCursor = "grab";
36
+ canvas.forEachObject((obj) => {
37
+ obj.selectable = false;
38
+ obj.evented = false;
39
+ });
40
+ break;
41
+ case "pen":
42
+ canvas.isDrawingMode = true;
43
+ canvas.selection = false;
44
+ canvas.defaultCursor = "crosshair";
45
+ canvas.hoverCursor = "crosshair";
46
+ if (canvas.freeDrawingBrush) {
47
+ canvas.freeDrawingBrush.color = toolOptions.pen.color;
48
+ canvas.freeDrawingBrush.width = toolOptions.pen.strokeWidth;
49
+ if (toolOptions.pen.strokeDashArray) {
50
+ const dynamicDashArray = calculateDashArray(toolOptions.pen.strokeDashArray, toolOptions.pen.strokeWidth);
51
+ canvas.freeDrawingBrush.strokeDashArray = dynamicDashArray || null;
52
+ }
53
+ else {
54
+ canvas.freeDrawingBrush.strokeDashArray = null;
55
+ }
56
+ }
57
+ canvas.forEachObject((obj) => {
58
+ obj.selectable = false;
59
+ obj.evented = false;
60
+ });
61
+ break;
62
+ case "eraser":
63
+ canvas.isDrawingMode = false;
64
+ canvas.selection = false;
65
+ canvas.defaultCursor = "none";
66
+ canvas.hoverCursor = "none";
67
+ if (eraserTraceRef.current) {
68
+ eraserTraceRef.current.set({ visible: false });
69
+ }
70
+ canvas.forEachObject((obj) => {
71
+ obj.selectable = false;
72
+ obj.evented = true;
73
+ });
74
+ break;
75
+ case "text":
76
+ canvas.isDrawingMode = false;
77
+ canvas.selection = false;
78
+ canvas.defaultCursor = "text";
79
+ canvas.hoverCursor = "text";
80
+ canvas.forEachObject((obj) => {
81
+ obj.selectable = false;
82
+ obj.evented = false;
83
+ });
84
+ break;
85
+ case "rectangle":
86
+ case "circle":
87
+ case "line":
88
+ case "frame":
89
+ case "arrow":
90
+ canvas.isDrawingMode = false;
91
+ canvas.selection = false;
92
+ canvas.defaultCursor = "crosshair";
93
+ canvas.hoverCursor = "crosshair";
94
+ canvas.forEachObject((obj) => {
95
+ obj.selectable = false;
96
+ obj.evented = false;
97
+ });
98
+ break;
99
+ default:
100
+ canvas.isDrawingMode = false;
101
+ canvas.selection = true;
102
+ canvas.defaultCursor = "default";
103
+ canvas.hoverCursor = "move";
104
+ }
105
+ canvas.renderAll();
106
+ }, [
107
+ activeTool,
108
+ toolOptions,
109
+ fabricCanvas,
110
+ eraserTraceRef,
111
+ eraserPathRef,
112
+ eraserPathPointsRef,
113
+ eraserTargetsRef,
114
+ ]);
115
+ };
@@ -0,0 +1,25 @@
1
+ import { RefObject } from "react";
2
+ import { Canvas } from "fabric";
3
+ interface UseZoomProps {
4
+ fabricCanvas: RefObject<Canvas | null>;
5
+ MIN_ZOOM: number;
6
+ MAX_ZOOM: number;
7
+ canvasZoom: number;
8
+ canvasViewport: {
9
+ x: number;
10
+ y: number;
11
+ };
12
+ setCanvasZoom: (zoom: number) => void;
13
+ setCanvasViewport: (viewport: {
14
+ x: number;
15
+ y: number;
16
+ }) => void;
17
+ }
18
+ export declare const useZoom: ({ fabricCanvas, MIN_ZOOM, MAX_ZOOM, canvasZoom, canvasViewport, setCanvasZoom, setCanvasViewport, }: UseZoomProps) => {
19
+ handleZoom: (newZoom: number, point?: {
20
+ x: number;
21
+ y: number;
22
+ }) => void;
23
+ };
24
+ export {};
25
+ //# sourceMappingURL=useZoom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useZoom.d.ts","sourceRoot":"","sources":["../../src/hooks/useZoom.ts"],"names":[],"mappings":"AACA,OAAa,EAA0B,SAAS,EAAE,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,MAAM,EAAuB,MAAM,QAAQ,CAAC;AAErD,UAAU,YAAY;IACpB,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,iBAAiB,EAAE,CAAC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,eAAO,MAAM,OAAO,GAAI,qGAQrB,YAAY;0BAED,MAAM,UAAU;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;CAoJrD,CAAC"}