@mhamz.01/easyflow-whiteboard 2.47.0 → 2.50.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;AAS9C,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;AAS9C,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,2CAuiBzB"}
|
|
@@ -115,20 +115,29 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
115
115
|
setSelectedIds(new Set());
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
// At zoom=1 with identity VPT, getActiveObject() can return null before
|
|
119
|
+
// Fabric updates _activeObject. Use e.transform as the primary check —
|
|
120
|
+
// it is populated by Fabric's hit-test regardless of zoom level.
|
|
121
|
+
const transformTarget = e.transform?.target;
|
|
122
|
+
const activeObject = canvas.getActiveObject();
|
|
123
|
+
const activeObjects = canvas.getActiveObjects();
|
|
124
|
+
const isPartOfActiveSelection = transformTarget === target || // most reliable — direct from event
|
|
125
|
+
activeObject === target || // selection box group
|
|
126
|
+
activeObjects.includes(target); // individual object in multi-select
|
|
127
|
+
if (!isPartOfActiveSelection) {
|
|
124
128
|
setSelectedIds(new Set());
|
|
125
129
|
}
|
|
126
130
|
};
|
|
131
|
+
const handleSelectionCleared = () => {
|
|
132
|
+
setSelectedIds(new Set());
|
|
133
|
+
};
|
|
127
134
|
canvas.on("object:moving", handleObjectMoving);
|
|
128
135
|
canvas.on("mouse:down", handleMouseDown);
|
|
136
|
+
canvas.on("selection:cleared", handleSelectionCleared);
|
|
129
137
|
return () => {
|
|
130
138
|
canvas.off("object:moving", handleObjectMoving);
|
|
131
139
|
canvas.off("mouse:down", handleMouseDown);
|
|
140
|
+
canvas.off("selection:cleared", handleSelectionCleared);
|
|
132
141
|
};
|
|
133
142
|
// ── selectedIds REMOVED from deps — read via selectedIdsRef instead ──────
|
|
134
143
|
// Having selectedIds here caused the effect to re-register on every selection
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCanvasInit.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasInit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,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;
|
|
1
|
+
{"version":3,"file":"useCanvasInit.d.ts","sourceRoot":"","sources":["../../src/hooks/useCanvasInit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,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;CAC3C;AAID,eAAO,MAAM,aAAa,GAAI,mIAU3B,kBAAkB,SAuHpB,CAAC"}
|
|
@@ -4,7 +4,6 @@ import { initializeFabricCanvas, addWelcomeContent } from "../lib/fabric-utils";
|
|
|
4
4
|
// ─── Hook ─────────────────────────────────────────────────────────────────────
|
|
5
5
|
export const useCanvasInit = ({ canvasRef, fabricCanvasRef, activeTool, suppressHistoryRef, isRestoringRef, pushHistory, setTasks, setDocuments, initialData, }) => {
|
|
6
6
|
useEffect(() => {
|
|
7
|
-
// Guard: prevent re-initialization if canvas already exists
|
|
8
7
|
if (!canvasRef.current || fabricCanvasRef.current)
|
|
9
8
|
return;
|
|
10
9
|
const canvas = new Canvas(canvasRef.current, {
|
|
@@ -20,31 +19,44 @@ export const useCanvasInit = ({ canvasRef, fabricCanvasRef, activeTool, suppress
|
|
|
20
19
|
initializeFabricCanvas(canvas);
|
|
21
20
|
const canvasElement = canvas.getElement();
|
|
22
21
|
canvasElement.style.touchAction = "none";
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
22
|
+
// Tracks whether cleanup has run — prevents hydrateCanvas from
|
|
23
|
+
// running after canvas.dispose() if rAFs fire post-unmount
|
|
24
|
+
let disposed = false;
|
|
25
|
+
let raf1;
|
|
26
|
+
let raf2;
|
|
26
27
|
const hydrateCanvas = async () => {
|
|
28
|
+
// Guard 1: unmounted before rAF fired — canvas already disposed
|
|
29
|
+
if (disposed)
|
|
30
|
+
return;
|
|
31
|
+
// Guard 2: check Fabric's internal ctx directly.
|
|
32
|
+
// canvas.getContext() is NOT a valid Fabric v6 API — always undefined.
|
|
33
|
+
// contextContainer is the actual 2D ctx Fabric uses internally.
|
|
34
|
+
// If undefined, Fabric hasn't finished attaching to the DOM yet.
|
|
35
|
+
const fabricCtx = canvas.contextContainer;
|
|
36
|
+
if (!fabricCtx) {
|
|
37
|
+
console.warn("[useCanvasInit] Fabric ctx not ready, skipping hydration");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
27
40
|
const hasCanvas = !!initialData?.canvas;
|
|
28
41
|
const hasNodes = !!(initialData?.tasks || initialData?.documents);
|
|
29
42
|
if (hasCanvas || hasNodes) {
|
|
30
43
|
isRestoringRef.current = true;
|
|
31
44
|
if (hasCanvas) {
|
|
32
45
|
try {
|
|
33
|
-
// Guard: ensure the canvas context is available before loading
|
|
34
|
-
if (!canvas.getContext()) {
|
|
35
|
-
console.warn("[useCanvasInit] Canvas context not ready, skipping load");
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
46
|
await canvas.loadFromJSON(JSON.parse(initialData.canvas));
|
|
47
|
+
if (disposed)
|
|
48
|
+
return; // re-check after async gap — dispose may have run during await
|
|
39
49
|
canvas.renderAll();
|
|
40
50
|
pushHistory(JSON.stringify(canvas.toJSON()));
|
|
41
51
|
}
|
|
42
52
|
catch (err) {
|
|
43
53
|
console.error("[useCanvasInit] Canvas load failed:", err);
|
|
44
|
-
|
|
54
|
+
if (!disposed) {
|
|
55
|
+
addWelcomeContent(canvas, "https://res.cloudinary.com/dqyjffc9q/image/upload/v1773420161/easyflow-logo_nbpfd7.png");
|
|
56
|
+
}
|
|
45
57
|
}
|
|
46
58
|
}
|
|
47
|
-
if (hasNodes) {
|
|
59
|
+
if (hasNodes && !disposed) {
|
|
48
60
|
try {
|
|
49
61
|
if (initialData?.tasks)
|
|
50
62
|
setTasks(initialData.tasks);
|
|
@@ -58,13 +70,17 @@ export const useCanvasInit = ({ canvasRef, fabricCanvasRef, activeTool, suppress
|
|
|
58
70
|
isRestoringRef.current = false;
|
|
59
71
|
}
|
|
60
72
|
else {
|
|
61
|
-
|
|
62
|
-
|
|
73
|
+
if (!disposed) {
|
|
74
|
+
addWelcomeContent(canvas, "https://res.cloudinary.com/dqyjffc9q/image/upload/v1773420161/easyflow-logo_nbpfd7.png");
|
|
75
|
+
pushHistory(JSON.stringify(canvas.toJSON()));
|
|
76
|
+
}
|
|
63
77
|
}
|
|
64
78
|
};
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
// Double rAF ensures Fabric's internal ctx is fully attached:
|
|
80
|
+
// - raf1: fires when browser is about to paint (Fabric constructor done)
|
|
81
|
+
// - raf2: fires after first paint (contextContainer guaranteed attached)
|
|
82
|
+
raf1 = requestAnimationFrame(() => {
|
|
83
|
+
raf2 = requestAnimationFrame(() => {
|
|
68
84
|
hydrateCanvas();
|
|
69
85
|
});
|
|
70
86
|
});
|
|
@@ -83,11 +99,17 @@ export const useCanvasInit = ({ canvasRef, fabricCanvasRef, activeTool, suppress
|
|
|
83
99
|
canvasElement.addEventListener("touchmove", preventDefaults, { passive: false });
|
|
84
100
|
// ── Cleanup ───────────────────────────────────────────────────────────────
|
|
85
101
|
return () => {
|
|
102
|
+
// Set disposed FIRST and cancel rAFs BEFORE canvas.dispose().
|
|
103
|
+
// This ensures hydrateCanvas cannot run on an already-disposed canvas
|
|
104
|
+
// which is what causes the ctx/clearRect errors.
|
|
105
|
+
disposed = true;
|
|
106
|
+
cancelAnimationFrame(raf1);
|
|
107
|
+
cancelAnimationFrame(raf2);
|
|
86
108
|
window.removeEventListener("resize", handleResize);
|
|
87
109
|
canvasElement.removeEventListener("touchstart", preventDefaults);
|
|
88
110
|
canvasElement.removeEventListener("touchmove", preventDefaults);
|
|
89
111
|
canvas.dispose();
|
|
90
112
|
fabricCanvasRef.current = null;
|
|
91
113
|
};
|
|
92
|
-
}, []);
|
|
114
|
+
}, []);
|
|
93
115
|
};
|