@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,273 @@
1
+ import { FabricObject, Line, Group, IText, PencilBrush, FabricImage, util } from "fabric";
2
+ import { Text } from "fabric";
3
+ // Initialize canvas with default settings
4
+ export const initializeFabricCanvas = (canvas) => {
5
+ // Set default object properties
6
+ FabricObject.prototype.set({
7
+ transparentCorners: false,
8
+ borderColor: "#3b82f6",
9
+ cornerColor: "#3b82f6",
10
+ cornerSize: 8,
11
+ cornerStyle: "circle",
12
+ padding: 4,
13
+ });
14
+ // Enable object caching for better performance
15
+ FabricObject.prototype.objectCaching = true;
16
+ // Initialize drawing brush
17
+ canvas.freeDrawingBrush = new PencilBrush(canvas);
18
+ return canvas;
19
+ };
20
+ // Update shape dimensions during drag-to-draw
21
+ export const updateDrawingObject = (shape, toolType, startPoint, currentPoint) => {
22
+ const width = currentPoint.x - startPoint.x;
23
+ const height = currentPoint.y - startPoint.y;
24
+ switch (toolType) {
25
+ case "rectangle":
26
+ case "frame": {
27
+ const rect = shape; // or Frame
28
+ const dx = currentPoint.x - startPoint.x;
29
+ const dy = currentPoint.y - startPoint.y;
30
+ // 1. Calculate the center point (Midpoint)
31
+ const midX = startPoint.x + dx / 2;
32
+ const midY = startPoint.y + dy / 2;
33
+ // 2. Calculate dimensions
34
+ // We subtract a tiny fraction of the strokeWidth if you want the
35
+ // outer visual edge to be exactly under the pixel of the cursor.
36
+ const strokeW = rect.strokeWidth || 0;
37
+ const width = Math.abs(dx) - strokeW;
38
+ const height = Math.abs(dy) - strokeW;
39
+ rect.set({
40
+ left: midX,
41
+ top: midY,
42
+ width: Math.max(0, width),
43
+ height: Math.max(0, height),
44
+ originX: "center",
45
+ originY: "center",
46
+ });
47
+ rect.setCoords();
48
+ break;
49
+ }
50
+ case "circle": {
51
+ const circle = shape;
52
+ const dx = currentPoint.x - startPoint.x;
53
+ const dy = currentPoint.y - startPoint.y;
54
+ // Radius from diagonal distance
55
+ const radius = Math.sqrt(dx * dx + dy * dy) / 2;
56
+ // Center at midpoint
57
+ circle.set({
58
+ left: startPoint.x + dx / 2,
59
+ top: startPoint.y + dy / 2,
60
+ radius: Math.abs(radius),
61
+ originX: "center",
62
+ originY: "center",
63
+ });
64
+ circle.setCoords();
65
+ break;
66
+ }
67
+ case "line":
68
+ case "arrow":
69
+ if (shape instanceof Line) {
70
+ shape.set({
71
+ x1: startPoint.x,
72
+ y1: startPoint.y,
73
+ x2: currentPoint.x,
74
+ y2: currentPoint.y,
75
+ });
76
+ }
77
+ break;
78
+ }
79
+ shape.setCoords();
80
+ };
81
+ // Delete selected objects
82
+ export const deleteSelectedObjects = (canvas) => {
83
+ const activeObjects = canvas.getActiveObjects();
84
+ if (activeObjects.length) {
85
+ activeObjects.forEach((obj) => {
86
+ canvas.remove(obj);
87
+ });
88
+ canvas.discardActiveObject();
89
+ canvas.renderAll();
90
+ }
91
+ };
92
+ // Export canvas as JSON (save to database)
93
+ export const exportCanvasToJSON = (canvas) => {
94
+ // return JSON.stringify(canvas.toJSON(["data"]));
95
+ };
96
+ // Import canvas from JSON
97
+ export const importCanvasFromJSON = (canvas, json, callback) => {
98
+ canvas.loadFromJSON(json).then(() => {
99
+ canvas.renderAll();
100
+ if (callback)
101
+ callback();
102
+ });
103
+ };
104
+ // Export canvas as image
105
+ export const exportCanvasToImage = (canvas, format = "png") => {
106
+ return canvas.toDataURL({
107
+ format,
108
+ quality: 1,
109
+ multiplier: 2, // 2x resolution for better quality
110
+ });
111
+ };
112
+ // lib/fabric-utils.ts
113
+ export const calculateDashArray = (strokeDashArray, strokeWidth) => {
114
+ if (!strokeDashArray)
115
+ return undefined;
116
+ const multiplier = Math.max(strokeWidth, 1);
117
+ return strokeDashArray.map(value => value * multiplier);
118
+ };
119
+ // Add text at specific position
120
+ export const addText = (canvas, options) => {
121
+ const text = new IText(options.text || "Double click to edit", {
122
+ left: options.x || 100,
123
+ top: options.y || 100,
124
+ fontSize: options.fontSize || 16,
125
+ fontFamily: options.fontFamily || "Arial",
126
+ fontWeight: options.fontWeight || "400",
127
+ fill: options.color || "#000000",
128
+ // textAlign: options.textAlign || "left",
129
+ });
130
+ canvas.add(text);
131
+ canvas.setActiveObject(text);
132
+ canvas.renderAll();
133
+ // Auto-enter edit mode
134
+ text.enterEditing();
135
+ text.selectAll();
136
+ };
137
+ export const addImageToCanvas = async (canvas, url) => {
138
+ if (!canvas)
139
+ return;
140
+ // 1. Get the current Viewport Transform (the Pan and Zoom matrix)
141
+ const vpt = canvas.viewportTransform;
142
+ const zoom = canvas.getZoom();
143
+ // 2. Calculate the exact Scene coordinates for the center
144
+ // Formula: (-PanOffset / Zoom) + (CanvasDimension / 2 / Zoom)
145
+ const centerX = (-vpt[4] + canvas.width / 2) / zoom;
146
+ const centerY = (-vpt[5] + canvas.height / 2) / zoom;
147
+ try {
148
+ const img = await FabricImage.fromURL(url);
149
+ // 3. Dynamic Scaling (Make it fit roughly 40% of the screen height)
150
+ const targetHeight = (canvas.height / zoom) * 0.4;
151
+ img.scaleToHeight(targetHeight);
152
+ img.set({
153
+ left: centerX,
154
+ top: centerY,
155
+ originX: "center",
156
+ originY: "center",
157
+ // cornerStyle: "circle",
158
+ // transparentCorners: false,
159
+ // cornerColor: "#3b82f6", // Nice modern blue
160
+ // cornerStrokeColor: "#ffffff",
161
+ padding: 10
162
+ });
163
+ canvas.add(img);
164
+ canvas.setActiveObject(img);
165
+ canvas.renderAll();
166
+ return img;
167
+ }
168
+ catch (error) {
169
+ console.error("Image load failed", error);
170
+ }
171
+ };
172
+ // Clear entire canvas
173
+ export const clearCanvas = (canvas) => {
174
+ canvas.clear();
175
+ canvas.backgroundColor = "transparent";
176
+ canvas.renderAll();
177
+ };
178
+ // Zoom canvas
179
+ export const zoomCanvas = (canvas, zoomLevel) => {
180
+ canvas.setZoom(zoomLevel);
181
+ canvas.renderAll();
182
+ };
183
+ // Reset view to fit all objects
184
+ export const fitCanvasToObjects = (canvas) => {
185
+ const objects = canvas.getObjects();
186
+ if (objects.length === 0)
187
+ return;
188
+ const group = new Group(objects);
189
+ const groupWidth = group.width || 0;
190
+ const groupHeight = group.height || 0;
191
+ const canvasWidth = canvas.getWidth();
192
+ const canvasHeight = canvas.getHeight();
193
+ const zoom = Math.min(canvasWidth / (groupWidth + 100), canvasHeight / (groupHeight + 100));
194
+ canvas.setZoom(zoom);
195
+ canvas.viewportCenterObject(group);
196
+ // group.destroy();
197
+ canvas.renderAll();
198
+ };
199
+ export const addWelcomeContent = async (canvas, logoUrl) => {
200
+ const centerX = canvas.getWidth() / 2;
201
+ const centerY = canvas.getHeight() / 2;
202
+ // 1. Load the Logo Image
203
+ const logoImg = await FabricImage.fromURL(logoUrl, {
204
+ crossOrigin: 'anonymous'
205
+ });
206
+ // Scale logo to fit (e.g., 80px width)
207
+ const scale = 60 / logoImg.width;
208
+ logoImg.set({
209
+ scaleX: scale,
210
+ scaleY: scale,
211
+ originX: "center",
212
+ originY: "center",
213
+ top: -120, // Relative to group center
214
+ });
215
+ // 2. Creative Background "Aura" (The Glow)
216
+ // const aura = new Circle({
217
+ // radius: 100,
218
+ // fill: "radial-gradient(circle, rgba(99, 102, 241, 0.15) 0%, rgba(255, 255, 255, 0) 70%)",
219
+ // originX: "center",
220
+ // originY: "center",
221
+ // top: -120,
222
+ // selectable: false,
223
+ // evented: false,
224
+ // });
225
+ // 3. High-End Typography
226
+ const mainTitle = new Text("EasyFlow", {
227
+ top: -20,
228
+ fontSize: 84,
229
+ fontFamily: "'cursive', 'Segoe UI', Roboto, sans-serif",
230
+ fontWeight: "700",
231
+ fill: "#029AFF",
232
+ charSpacing: -20, // Tight kerning for a modern look
233
+ originX: "center",
234
+ });
235
+ const subTitle = new Text("Whiteboard", {
236
+ top: 55,
237
+ fontSize: 38,
238
+ fontFamily: "'Inter', 'Segoe UI', Roboto, sans-serif",
239
+ fontWeight: "300", // Thin contrast against the bold "EasyFlow"
240
+ fill: "#fff",
241
+ originX: "center",
242
+ });
243
+ const hint = new Text("SELECT A TOOL TO START CREATING", {
244
+ top: 110,
245
+ fontSize: 12,
246
+ fontFamily: "monospace", // Techy/Creative feel
247
+ fontWeight: "600",
248
+ fill: "#9ca3af",
249
+ charSpacing: 200, // Wide spacing for the hint
250
+ originX: "center",
251
+ });
252
+ // 4. Create Master Hero Group
253
+ const heroGroup = new Group([logoImg, mainTitle, subTitle, hint], {
254
+ left: centerX,
255
+ top: centerY + 20, // Start slightly lower for "float-up" animation
256
+ originX: "center",
257
+ originY: "center",
258
+ selectable: false,
259
+ evented: false,
260
+ opacity: 0,
261
+ });
262
+ heroGroup.id = "welcome-hero";
263
+ canvas.add(heroGroup);
264
+ // 5. "Float & Fade" Animation
265
+ heroGroup.animate({
266
+ opacity: 1,
267
+ top: centerY // Moves up 20px while fading in
268
+ }, {
269
+ duration: 1200,
270
+ easing: util.ease.easeOutQuart,
271
+ onChange: () => canvas.requestRenderAll(),
272
+ });
273
+ };
@@ -0,0 +1,3 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAA;AAG5C,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
@@ -0,0 +1,5 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -0,0 +1,99 @@
1
+ import { FabricObject } from "fabric";
2
+ export type Tool = "select" | "pan" | "pen" | "rectangle" | "circle" | "line" | "arrow" | "frame" | "text" | "image" | "eraser" | "connector" | "undo" | "redo";
3
+ export type ActiveDropdown = "task" | "document" | null;
4
+ export interface ToolOptions {
5
+ pen: {
6
+ color: string;
7
+ strokeWidth: number;
8
+ opacity: number;
9
+ strokeDashArray: number[] | null;
10
+ };
11
+ rectangle: {
12
+ fillColor: string;
13
+ strokeColor: string;
14
+ strokeWidth: number;
15
+ strokeDashArray: number[] | null;
16
+ };
17
+ circle: {
18
+ fillColor: string;
19
+ strokeColor: string;
20
+ strokeWidth: number;
21
+ strokeDashArray: number[] | null;
22
+ };
23
+ frame: {
24
+ fillColor: string;
25
+ strokeColor: string;
26
+ strokeWidth: number;
27
+ strokeDashArray: number[] | null;
28
+ };
29
+ text: {
30
+ fontSize: number;
31
+ fontFamily: string;
32
+ fontWeight: string;
33
+ textAlign: string;
34
+ color: string;
35
+ };
36
+ image: {
37
+ opacity: number;
38
+ filters: string[];
39
+ };
40
+ eraser: {
41
+ size: number;
42
+ };
43
+ line: {
44
+ strokeColor: string;
45
+ strokeWidth: number;
46
+ strokeDashArray: number[] | null;
47
+ };
48
+ arrow: {
49
+ strokeColor: string;
50
+ strokeWidth: number;
51
+ strokeDashArray: number[] | null;
52
+ };
53
+ }
54
+ interface WhiteboardState {
55
+ activeTool: Tool;
56
+ setActiveTool: (tool: Tool) => void;
57
+ activeDropdown: ActiveDropdown;
58
+ setActiveDropdown: (id: ActiveDropdown) => void;
59
+ selectedObjectType: string | null;
60
+ setSelectedObjectType: (type: string | null) => void;
61
+ canvasObjects: FabricObject[];
62
+ addCanvasObject: (obj: FabricObject) => void;
63
+ removeCanvasObject: (obj: FabricObject) => void;
64
+ clearCanvasObjects: () => void;
65
+ toolOptions: ToolOptions;
66
+ setToolOption: <T extends keyof ToolOptions>(tool: T, option: keyof ToolOptions[T], value: any) => void;
67
+ history: string[];
68
+ historyIndex: number;
69
+ canUndo: boolean;
70
+ canRedo: boolean;
71
+ pushHistory: (state: string) => void;
72
+ undo: () => string | null;
73
+ redo: () => string | null;
74
+ setCanUndo: (canUndo: boolean) => void;
75
+ setCanRedo: (canRedo: boolean) => void;
76
+ zoom: number;
77
+ setZoom: (zoom: number) => void;
78
+ selectedObjects: FabricObject[];
79
+ setSelectedObjects: (objects: FabricObject[]) => void;
80
+ }
81
+ export declare const useWhiteboardStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<WhiteboardState>, "setState" | "devtools"> & {
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
+ }>;
98
+ export {};
99
+ //# sourceMappingURL=whiteboard-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whiteboard-store.d.ts","sourceRoot":"","sources":["../../src/store/whiteboard-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ import { create } from "zustand";
2
+ import { devtools } from "zustand/middleware";
3
+ const defaultToolOptions = {
4
+ pen: {
5
+ color: "#ffffff",
6
+ strokeWidth: 2,
7
+ opacity: 1,
8
+ strokeDashArray: null,
9
+ },
10
+ rectangle: {
11
+ fillColor: "transparent",
12
+ strokeColor: "#ffffff",
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
52
+ };
53
+ export const useWhiteboardStore = create()(devtools((set, get) => ({
54
+ // Tool state
55
+ activeTool: "select",
56
+ setActiveTool: (tool) => set({ activeTool: tool }),
57
+ // Selected object type (for editing)
58
+ selectedObjectType: null,
59
+ setSelectedObjectType: (type) => set({ selectedObjectType: type }),
60
+ // Canvas objects
61
+ canvasObjects: [],
62
+ addCanvasObject: (obj) => set((state) => ({
63
+ canvasObjects: [...state.canvasObjects, obj],
64
+ })),
65
+ removeCanvasObject: (obj) => set((state) => ({
66
+ canvasObjects: state.canvasObjects.filter((o) => o !== obj),
67
+ })),
68
+ clearCanvasObjects: () => set({ canvasObjects: [] }),
69
+ // Tool options
70
+ toolOptions: defaultToolOptions,
71
+ setToolOption: (tool, option, value) => set((state) => ({
72
+ toolOptions: {
73
+ ...state.toolOptions,
74
+ [tool]: {
75
+ ...state.toolOptions[tool],
76
+ [option]: value,
77
+ },
78
+ },
79
+ })),
80
+ // History
81
+ history: [],
82
+ historyIndex: -1,
83
+ canUndo: false,
84
+ canRedo: false,
85
+ pushHistory: (state) => {
86
+ const { history, historyIndex } = get();
87
+ // Slice off any redo states ahead of current index
88
+ const newHistory = history.slice(0, historyIndex + 1);
89
+ newHistory.push(state);
90
+ set({
91
+ history: newHistory,
92
+ historyIndex: newHistory.length - 1,
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,
102
+ }),
103
+ undo: () => {
104
+ const { history, historyIndex } = get();
105
+ // Must have a previous state to go back to
106
+ if (historyIndex > 0) {
107
+ const newIndex = historyIndex - 1;
108
+ set({
109
+ historyIndex: newIndex,
110
+ canUndo: newIndex > 0,
111
+ canRedo: true,
112
+ });
113
+ return history[newIndex];
114
+ }
115
+ return null;
116
+ },
117
+ redo: () => {
118
+ const { history, historyIndex } = get();
119
+ if (historyIndex < history.length - 1) {
120
+ const newIndex = historyIndex + 1;
121
+ set({
122
+ historyIndex: newIndex,
123
+ canUndo: true,
124
+ canRedo: newIndex < history.length - 1,
125
+ });
126
+ return history[newIndex];
127
+ }
128
+ return null;
129
+ },
130
+ setCanUndo: (v) => set({ canUndo: v }),
131
+ setCanRedo: (v) => set({ canRedo: v }),
132
+ // Zoom
133
+ zoom: 1,
134
+ setZoom: (zoom) => set({ zoom }),
135
+ selectedObjects: [],
136
+ setSelectedObjects: (objects) => set({ selectedObjects: objects }),
137
+ }), { name: "fabric-whiteboard-store" }));
@@ -0,0 +1,24 @@
1
+ export interface BaseNode {
2
+ id: string;
3
+ x: number;
4
+ y: number;
5
+ }
6
+ export interface TaskNodeData extends BaseNode {
7
+ type: "task";
8
+ title: string;
9
+ status: "todo" | "in-progress" | "done";
10
+ assignee?: string;
11
+ project?: string;
12
+ priority?: "low" | "medium" | "high";
13
+ dueDate?: string;
14
+ }
15
+ export interface DocumentNodeData extends BaseNode {
16
+ type: "document";
17
+ title: string;
18
+ project: string;
19
+ breadcrumb?: string[];
20
+ preview: string;
21
+ updatedAt?: string;
22
+ }
23
+ export type CanvasNode = TaskNodeData | DocumentNodeData;
24
+ //# sourceMappingURL=canvas-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-node.d.ts","sourceRoot":"","sources":["../../src/types/canvas-node.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACxC,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,gBAAiB,SAAQ,QAAQ;IAChD,IAAI,EAAE,UAAU,CAAC;IACjB,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;CACpB;AAED,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,gBAAgB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@mhamz.01/easyflow-whiteboard",
3
+ "version": "1.0.0",
4
+ "description": "A feature-rich whiteboard component built with Fabric.js and React",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": ["dist"],
8
+ "keywords": ["whiteboard", "canvas", "fabric", "react", "drawing"],
9
+ "author": "Your Name",
10
+ "license": "MIT",
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "peerDependencies": {
16
+ "react": "^18.0.0 || ^19.0.0",
17
+ "react-dom": "^18.0.0 || ^19.0.0",
18
+ "fabric": "^7.0.0",
19
+ "zustand": "^5.0.0"
20
+ },
21
+ "dependencies": {
22
+ "lucide-react": "^0.563.0",
23
+ "clsx": "^2.1.1",
24
+ "tailwind-merge": "^3.4.0",
25
+ "class-variance-authority": "^0.7.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/react": "^19.0.0",
29
+ "@types/react-dom": "^19.0.0",
30
+ "@types/fabric": "^5.3.11",
31
+ "@types/node": "^20.0.0",
32
+ "typescript": "^5.0.0"
33
+ }
34
+ }