@mhamz.01/easyflow-whiteboard 2.37.0 → 2.39.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/node/custom-node-overlay-layer.d.ts.map +1 -1
- package/dist/components/node/custom-node-overlay-layer.js +4 -2
- package/dist/store/whiteboard-store.d.ts +6 -22
- package/dist/store/whiteboard-store.d.ts.map +1 -1
- package/dist/store/whiteboard-store.js +50 -103
- package/package.json +1 -1
|
@@ -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,2CA6hBzB"}
|
|
@@ -116,8 +116,10 @@ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, on
|
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
118
118
|
// ── Read from ref — not stale closure ──
|
|
119
|
-
const
|
|
120
|
-
const
|
|
119
|
+
const activeObject = canvas.getActiveObject(); // the group/selection box
|
|
120
|
+
const activeObjects = canvas.getActiveObjects(); // individual objects inside it
|
|
121
|
+
const isTargetAlreadySelected = activeObjects.includes(target) || // clicked an individual selected object
|
|
122
|
+
activeObject === target; // clicked the selection box itself
|
|
121
123
|
if (!isTargetAlreadySelected) {
|
|
122
124
|
setSelectedIds(new Set());
|
|
123
125
|
}
|
|
@@ -62,38 +62,22 @@ interface WhiteboardState {
|
|
|
62
62
|
addCanvasObject: (obj: FabricObject) => void;
|
|
63
63
|
removeCanvasObject: (obj: FabricObject) => void;
|
|
64
64
|
clearCanvasObjects: () => void;
|
|
65
|
+
selectedObjects: FabricObject[];
|
|
66
|
+
setSelectedObjects: (objects: FabricObject[]) => void;
|
|
65
67
|
toolOptions: ToolOptions;
|
|
66
68
|
setToolOption: <T extends keyof ToolOptions>(tool: T, option: keyof ToolOptions[T], value: any) => void;
|
|
67
69
|
history: string[];
|
|
68
70
|
historyIndex: number;
|
|
69
71
|
canUndo: boolean;
|
|
70
72
|
canRedo: boolean;
|
|
71
|
-
pushHistory: (
|
|
73
|
+
pushHistory: (snapshot: string) => void;
|
|
72
74
|
undo: () => string | null;
|
|
73
75
|
redo: () => string | null;
|
|
74
|
-
setCanUndo: (
|
|
75
|
-
setCanRedo: (
|
|
76
|
+
setCanUndo: (v: boolean) => void;
|
|
77
|
+
setCanRedo: (v: boolean) => void;
|
|
76
78
|
zoom: number;
|
|
77
79
|
setZoom: (zoom: number) => void;
|
|
78
|
-
selectedObjects: FabricObject[];
|
|
79
|
-
setSelectedObjects: (objects: FabricObject[]) => void;
|
|
80
80
|
}
|
|
81
|
-
export declare const useWhiteboardStore: import("zustand").UseBoundStore<
|
|
82
|
-
setState(partial: WhiteboardState | Partial<WhiteboardState> | ((state: WhiteboardState) => WhiteboardState | Partial<WhiteboardState>), replace?: false | undefined, action?: (string | {
|
|
83
|
-
[x: string]: unknown;
|
|
84
|
-
[x: number]: unknown;
|
|
85
|
-
[x: symbol]: unknown;
|
|
86
|
-
type: string;
|
|
87
|
-
}) | undefined): void;
|
|
88
|
-
setState(state: WhiteboardState | ((state: WhiteboardState) => WhiteboardState), replace: true, action?: (string | {
|
|
89
|
-
[x: string]: unknown;
|
|
90
|
-
[x: number]: unknown;
|
|
91
|
-
[x: symbol]: unknown;
|
|
92
|
-
type: string;
|
|
93
|
-
}) | undefined): void;
|
|
94
|
-
devtools: {
|
|
95
|
-
cleanup: () => void;
|
|
96
|
-
};
|
|
97
|
-
}>;
|
|
81
|
+
export declare const useWhiteboardStore: import("zustand").UseBoundStore<import("zustand").StoreApi<WhiteboardState>>;
|
|
98
82
|
export {};
|
|
99
83
|
//# sourceMappingURL=whiteboard-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whiteboard-store.d.ts","sourceRoot":"","sources":["../../src/store/whiteboard-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"whiteboard-store.d.ts","sourceRoot":"","sources":["../../src/store/whiteboard-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,137 +1,84 @@
|
|
|
1
1
|
import { create } from "zustand";
|
|
2
|
-
|
|
2
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
3
|
+
// Each history entry is a full JSON canvas snapshot (can be 100KB+).
|
|
4
|
+
// Without a cap: 200 actions × 100KB = 20MB of undo history in memory.
|
|
5
|
+
const MAX_HISTORY = 50;
|
|
3
6
|
const defaultToolOptions = {
|
|
4
|
-
pen: {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
strokeWidth: 2,
|
|
14
|
-
strokeDashArray: null,
|
|
15
|
-
},
|
|
16
|
-
circle: {
|
|
17
|
-
fillColor: "transparent",
|
|
18
|
-
strokeColor: "#ffffff",
|
|
19
|
-
strokeWidth: 2,
|
|
20
|
-
strokeDashArray: null,
|
|
21
|
-
},
|
|
22
|
-
frame: {
|
|
23
|
-
fillColor: "#FFFFFF",
|
|
24
|
-
strokeColor: "#E5E7EB",
|
|
25
|
-
strokeWidth: 1,
|
|
26
|
-
strokeDashArray: null,
|
|
27
|
-
},
|
|
28
|
-
text: {
|
|
29
|
-
fontSize: 24,
|
|
30
|
-
fontFamily: "cursive",
|
|
31
|
-
color: "#ffffff",
|
|
32
|
-
fontWeight: "400",
|
|
33
|
-
textAlign: "left",
|
|
34
|
-
},
|
|
35
|
-
image: {
|
|
36
|
-
opacity: 1,
|
|
37
|
-
filters: [],
|
|
38
|
-
},
|
|
39
|
-
eraser: {
|
|
40
|
-
size: 20,
|
|
41
|
-
},
|
|
42
|
-
line: {
|
|
43
|
-
strokeColor: "#ffffff",
|
|
44
|
-
strokeWidth: 2,
|
|
45
|
-
strokeDashArray: null,
|
|
46
|
-
}, // ← ADD THIS
|
|
47
|
-
arrow: {
|
|
48
|
-
strokeColor: "#ffffff",
|
|
49
|
-
strokeWidth: 2,
|
|
50
|
-
strokeDashArray: null,
|
|
51
|
-
}, // ← ADD THIS
|
|
7
|
+
pen: { color: "#ffffff", strokeWidth: 2, opacity: 1, strokeDashArray: null },
|
|
8
|
+
rectangle: { fillColor: "transparent", strokeColor: "#ffffff", strokeWidth: 2, strokeDashArray: null },
|
|
9
|
+
circle: { fillColor: "transparent", strokeColor: "#ffffff", strokeWidth: 2, strokeDashArray: null },
|
|
10
|
+
frame: { fillColor: "#FFFFFF", strokeColor: "#E5E7EB", strokeWidth: 1, strokeDashArray: null },
|
|
11
|
+
text: { fontSize: 24, fontFamily: "cursive", color: "#ffffff", fontWeight: "400", textAlign: "left" },
|
|
12
|
+
image: { opacity: 1, filters: [] },
|
|
13
|
+
eraser: { size: 20 },
|
|
14
|
+
line: { strokeColor: "#ffffff", strokeWidth: 2, strokeDashArray: null },
|
|
15
|
+
arrow: { strokeColor: "#ffffff", strokeWidth: 2, strokeDashArray: null },
|
|
52
16
|
};
|
|
53
|
-
|
|
54
|
-
|
|
17
|
+
// ─── Store ────────────────────────────────────────────────────────────────────
|
|
18
|
+
export const useWhiteboardStore = create()((set, get) => ({
|
|
19
|
+
// ── Tool ──────────────────────────────────────────────────────────────────
|
|
55
20
|
activeTool: "select",
|
|
56
21
|
setActiveTool: (tool) => set({ activeTool: tool }),
|
|
57
|
-
//
|
|
22
|
+
// ── Dropdown ──────────────────────────────────────────────────────────────
|
|
23
|
+
activeDropdown: null,
|
|
24
|
+
setActiveDropdown: (id) => set({ activeDropdown: id }),
|
|
25
|
+
// ── Selected object type ──────────────────────────────────────────────────
|
|
58
26
|
selectedObjectType: null,
|
|
59
27
|
setSelectedObjectType: (type) => set({ selectedObjectType: type }),
|
|
60
|
-
// Canvas objects
|
|
28
|
+
// ── Canvas objects ────────────────────────────────────────────────────────
|
|
29
|
+
// NOTE: Fabric instances have circular refs and DOM refs — they cannot be
|
|
30
|
+
// serialized. If you add devtools back, exclude this slice or it will throw.
|
|
31
|
+
// Using concat() instead of spread avoids the intermediate spread allocation.
|
|
61
32
|
canvasObjects: [],
|
|
62
|
-
addCanvasObject: (obj) => set((
|
|
63
|
-
|
|
64
|
-
})),
|
|
65
|
-
removeCanvasObject: (obj) => set((state) => ({
|
|
66
|
-
canvasObjects: state.canvasObjects.filter((o) => o !== obj),
|
|
67
|
-
})),
|
|
33
|
+
addCanvasObject: (obj) => set((s) => ({ canvasObjects: s.canvasObjects.concat(obj) })),
|
|
34
|
+
removeCanvasObject: (obj) => set((s) => ({ canvasObjects: s.canvasObjects.filter((o) => o !== obj) })),
|
|
68
35
|
clearCanvasObjects: () => set({ canvasObjects: [] }),
|
|
69
|
-
//
|
|
36
|
+
// ── Selected objects ──────────────────────────────────────────────────────
|
|
37
|
+
selectedObjects: [],
|
|
38
|
+
setSelectedObjects: (objects) => set({ selectedObjects: objects }),
|
|
39
|
+
// ── Tool options ──────────────────────────────────────────────────────────
|
|
70
40
|
toolOptions: defaultToolOptions,
|
|
71
|
-
setToolOption: (tool, option, value) => set((
|
|
41
|
+
setToolOption: (tool, option, value) => set((s) => ({
|
|
72
42
|
toolOptions: {
|
|
73
|
-
...
|
|
74
|
-
[tool]: {
|
|
75
|
-
...state.toolOptions[tool],
|
|
76
|
-
[option]: value,
|
|
77
|
-
},
|
|
43
|
+
...s.toolOptions,
|
|
44
|
+
[tool]: { ...s.toolOptions[tool], [option]: value },
|
|
78
45
|
},
|
|
79
46
|
})),
|
|
80
|
-
// History
|
|
47
|
+
// ── History ───────────────────────────────────────────────────────────────
|
|
81
48
|
history: [],
|
|
82
49
|
historyIndex: -1,
|
|
83
50
|
canUndo: false,
|
|
84
51
|
canRedo: false,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// Can undo if there is at least 1 previous state (index > 0)
|
|
94
|
-
canUndo: newHistory.length > 1,
|
|
95
|
-
canRedo: false,
|
|
96
|
-
});
|
|
97
|
-
},
|
|
98
|
-
// Sets the selected dropdown (task/document) - used to auto-switch back to select tool when closing
|
|
99
|
-
activeDropdown: null,
|
|
100
|
-
setActiveDropdown: (id) => set({
|
|
101
|
-
activeDropdown: id,
|
|
52
|
+
// Uses set() updater (single atomic write) instead of get() + set() which
|
|
53
|
+
// could interleave in React concurrent mode.
|
|
54
|
+
pushHistory: (snapshot) => set((s) => {
|
|
55
|
+
const trimmed = s.history.slice(0, s.historyIndex + 1);
|
|
56
|
+
trimmed.push(snapshot);
|
|
57
|
+
const capped = trimmed.length > MAX_HISTORY ? trimmed.slice(-MAX_HISTORY) : trimmed;
|
|
58
|
+
const newIndex = capped.length - 1;
|
|
59
|
+
return { history: capped, historyIndex: newIndex, canUndo: newIndex > 0, canRedo: false };
|
|
102
60
|
}),
|
|
103
61
|
undo: () => {
|
|
104
62
|
const { history, historyIndex } = get();
|
|
105
|
-
// Must have a previous state to go back to
|
|
106
63
|
if (historyIndex > 0) {
|
|
107
|
-
const
|
|
108
|
-
set({
|
|
109
|
-
|
|
110
|
-
canUndo: newIndex > 0,
|
|
111
|
-
canRedo: true,
|
|
112
|
-
});
|
|
113
|
-
return history[newIndex];
|
|
64
|
+
const i = historyIndex - 1;
|
|
65
|
+
set({ historyIndex: i, canUndo: i > 0, canRedo: true });
|
|
66
|
+
return history[i];
|
|
114
67
|
}
|
|
115
68
|
return null;
|
|
116
69
|
},
|
|
117
70
|
redo: () => {
|
|
118
71
|
const { history, historyIndex } = get();
|
|
119
72
|
if (historyIndex < history.length - 1) {
|
|
120
|
-
const
|
|
121
|
-
set({
|
|
122
|
-
|
|
123
|
-
canUndo: true,
|
|
124
|
-
canRedo: newIndex < history.length - 1,
|
|
125
|
-
});
|
|
126
|
-
return history[newIndex];
|
|
73
|
+
const i = historyIndex + 1;
|
|
74
|
+
set({ historyIndex: i, canUndo: true, canRedo: i < history.length - 1 });
|
|
75
|
+
return history[i];
|
|
127
76
|
}
|
|
128
77
|
return null;
|
|
129
78
|
},
|
|
130
79
|
setCanUndo: (v) => set({ canUndo: v }),
|
|
131
80
|
setCanRedo: (v) => set({ canRedo: v }),
|
|
132
|
-
// Zoom
|
|
81
|
+
// ── Zoom ──────────────────────────────────────────────────────────────────
|
|
133
82
|
zoom: 1,
|
|
134
83
|
setZoom: (zoom) => set({ zoom }),
|
|
135
|
-
|
|
136
|
-
setSelectedObjects: (objects) => set({ selectedObjects: objects }),
|
|
137
|
-
}), { name: "fabric-whiteboard-store" }));
|
|
84
|
+
}));
|