@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,44 @@
1
+ import { FabricObject, Canvas } from "fabric";
2
+ export interface Task {
3
+ id: string;
4
+ title: string;
5
+ status: "todo" | "in-progress" | "done";
6
+ x: number;
7
+ y: number;
8
+ assignee?: string;
9
+ project?: string;
10
+ priority?: "low" | "medium" | "high";
11
+ dueDate?: string;
12
+ }
13
+ export interface Document {
14
+ id: string;
15
+ title: string;
16
+ project: string;
17
+ breadcrumb?: string[];
18
+ preview: string;
19
+ updatedAt?: string;
20
+ x: number;
21
+ y: number;
22
+ }
23
+ interface CanvasOverlayLayerProps {
24
+ tasks: Task[];
25
+ documents: Document[];
26
+ onTasksUpdate?: (tasks: Task[]) => void;
27
+ onDocumentsUpdate?: (documents: Document[]) => void;
28
+ canvasZoom?: number;
29
+ canvasViewport?: {
30
+ x: number;
31
+ y: number;
32
+ };
33
+ selectionBox?: {
34
+ x1: number;
35
+ y1: number;
36
+ x2: number;
37
+ y2: number;
38
+ } | null;
39
+ selectedCanvasObjects?: FabricObject[];
40
+ fabricCanvas?: React.RefObject<Canvas | null>;
41
+ }
42
+ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, onDocumentsUpdate, canvasZoom, canvasViewport, selectionBox, selectedCanvasObjects, fabricCanvas, }: CanvasOverlayLayerProps): import("react/jsx-runtime").JSX.Element;
43
+ export {};
44
+ //# sourceMappingURL=custom-node-overlay-layer.d.ts.map
@@ -0,0 +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;AAM9C,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,2CA+czB"}
@@ -0,0 +1,353 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect, useRef } from "react";
4
+ import TaskNode from "./custom-node";
5
+ import DocumentNode from "./document-node";
6
+ // ─── Component ────────────────────────────────────────────────────────────────
7
+ export default function CanvasOverlayLayer({ tasks, documents, onTasksUpdate, onDocumentsUpdate, canvasZoom = 1, canvasViewport = { x: 0, y: 0 }, selectionBox = null, selectedCanvasObjects = [], fabricCanvas, }) {
8
+ const [localTasks, setLocalTasks] = useState(tasks);
9
+ const [localDocuments, setLocalDocuments] = useState(documents);
10
+ const [selectedIds, setSelectedIds] = useState(new Set());
11
+ const [dragging, setDragging] = useState(null);
12
+ const dragStateRef = useRef({
13
+ isDragging: false,
14
+ itemIds: [],
15
+ startPositions: new Map(),
16
+ canvasObjectsStartPos: new Map(),
17
+ offsetX: 0,
18
+ offsetY: 0,
19
+ });
20
+ const rafIdRef = useRef(null);
21
+ const overlayRef = useRef(null);
22
+ // ── Sync props → local state ────────────────────────────────────────────────
23
+ useEffect(() => { setLocalTasks(tasks); }, [tasks]);
24
+ useEffect(() => { setLocalDocuments(documents); }, [documents]);
25
+ // ── Event Forwarding (Fixes Zooming on Nodes) ───────────────────────────────
26
+ const handleOverlayWheel = (e) => {
27
+ if (e.ctrlKey || e.metaKey || e.shiftKey) {
28
+ const canvas = fabricCanvas?.current;
29
+ if (!canvas)
30
+ return;
31
+ const nativeEvent = e.nativeEvent;
32
+ // getScenePoint handles the transformation from screen to canvas space
33
+ const scenePoint = canvas.getScenePoint(nativeEvent);
34
+ // Viewport point is simply the mouse position relative to the canvas element
35
+ const rect = canvas.getElement().getBoundingClientRect();
36
+ const viewportPoint = {
37
+ x: nativeEvent.clientX - rect.left,
38
+ y: nativeEvent.clientY - rect.top,
39
+ };
40
+ // We cast to 'any' here because we are manually triggering an internal
41
+ // event bus, and Fabric's internal types for .fire() can be overly strict.
42
+ canvas.fire("mouse:wheel", {
43
+ e: nativeEvent,
44
+ scenePoint,
45
+ viewportPoint,
46
+ });
47
+ e.preventDefault();
48
+ e.stopPropagation();
49
+ }
50
+ };
51
+ useEffect(() => {
52
+ const overlayEl = overlayRef.current;
53
+ const canvas = fabricCanvas?.current;
54
+ if (!overlayEl || !canvas)
55
+ return;
56
+ const handleGlobalWheel = (e) => {
57
+ // Check if the user is hovering over an element that has pointer-events: auto
58
+ // (meaning they are hovering over a Task or Document)
59
+ const target = e.target;
60
+ const isOverNode = target !== overlayEl;
61
+ if ((e.ctrlKey || e.metaKey) && isOverNode) {
62
+ // 1. Prevent Browser Zoom immediately
63
+ e.preventDefault();
64
+ e.stopPropagation();
65
+ // 2. Calculate coordinates for Fabric
66
+ const scenePoint = canvas.getScenePoint(e);
67
+ const rect = canvas.getElement().getBoundingClientRect();
68
+ const viewportPoint = {
69
+ x: e.clientX - rect.left,
70
+ y: e.clientY - rect.top,
71
+ };
72
+ // 3. Manually fire the event into Fabric
73
+ canvas.fire("mouse:wheel", {
74
+ e: e,
75
+ scenePoint,
76
+ viewportPoint,
77
+ });
78
+ }
79
+ };
80
+ // CRITICAL: { passive: false } allows us to cancel the browser's zoom
81
+ overlayEl.addEventListener("wheel", handleGlobalWheel, { passive: false });
82
+ return () => {
83
+ overlayEl.removeEventListener("wheel", handleGlobalWheel);
84
+ };
85
+ }, [fabricCanvas, canvasZoom]); // Re-bind when zoom changes to keep closure fresh
86
+ // ── Fabric → Overlay Sync (Fixes Dragging from Fabric area) ──────────────────
87
+ useEffect(() => {
88
+ const canvas = fabricCanvas?.current;
89
+ if (!canvas)
90
+ return;
91
+ const handleObjectMoving = (e) => {
92
+ const target = e.transform?.target || e.target;
93
+ if (!target)
94
+ return;
95
+ // 1. Calculate delta in raw Scene Coordinates
96
+ // We do NOT divide by zoom here because target.left/top are world units.
97
+ const deltaX = target.left - (target._prevLeft ?? target.left);
98
+ const deltaY = target.top - (target._prevTop ?? target.top);
99
+ target._prevLeft = target.left;
100
+ target._prevTop = target.top;
101
+ if (deltaX === 0 && deltaY === 0)
102
+ return;
103
+ // 2. Apply the raw delta to HTML items
104
+ setLocalTasks((prev) => prev.map((t) => (selectedIds.has(t.id) ? { ...t, x: t.x + deltaX, y: t.y + deltaY } : t)));
105
+ setLocalDocuments((prev) => prev.map((d) => (selectedIds.has(d.id) ? { ...d, x: d.x + deltaX, y: d.y + deltaY } : d)));
106
+ };
107
+ const handleMouseDown = (e) => {
108
+ const target = e.target;
109
+ if (target) {
110
+ target._prevLeft = target.left;
111
+ target._prevTop = target.top;
112
+ }
113
+ };
114
+ canvas.on("object:moving", handleObjectMoving);
115
+ canvas.on("mouse:down", handleMouseDown);
116
+ return () => {
117
+ canvas.off("object:moving", handleObjectMoving);
118
+ canvas.off("mouse:down", handleMouseDown);
119
+ };
120
+ }, [canvasZoom, selectedIds, fabricCanvas]);
121
+ // ── Helpers ─────────────────────────────────────────────────────────────────
122
+ const getItemPosition = (id) => {
123
+ const task = localTasks.find((t) => t.id === id);
124
+ if (task)
125
+ return { x: task.x, y: task.y };
126
+ const doc = localDocuments.find((d) => d.id === id);
127
+ if (doc)
128
+ return { x: doc.x, y: doc.y };
129
+ return undefined;
130
+ };
131
+ const isItemInSelectionBox = (x, y, width, height, box) => {
132
+ const itemX1 = x * canvasZoom + canvasViewport.x;
133
+ const itemY1 = y * canvasZoom + canvasViewport.y;
134
+ const itemX2 = itemX1 + width * canvasZoom;
135
+ const itemY2 = itemY1 + height * canvasZoom;
136
+ const boxX1 = Math.min(box.x1, box.x2);
137
+ const boxY1 = Math.min(box.y1, box.y2);
138
+ const boxX2 = Math.max(box.x1, box.x2);
139
+ const boxY2 = Math.max(box.y1, box.y2);
140
+ return !(boxX2 < itemX1 || boxX1 > itemX2 || boxY2 < itemY1 || boxY1 > itemY2);
141
+ };
142
+ // ── Selection box detection ──────────────────────────────────────────────────
143
+ useEffect(() => {
144
+ if (!selectionBox)
145
+ return;
146
+ const newSelected = new Set();
147
+ localTasks.forEach((task) => {
148
+ if (isItemInSelectionBox(task.x, task.y, 300, 140, selectionBox))
149
+ newSelected.add(task.id);
150
+ });
151
+ localDocuments.forEach((doc) => {
152
+ if (isItemInSelectionBox(doc.x, doc.y, 300, 160, selectionBox))
153
+ newSelected.add(doc.id);
154
+ });
155
+ setSelectedIds(newSelected);
156
+ }, [selectionBox, localTasks, localDocuments, canvasZoom, canvasViewport]);
157
+ // ── Drag start (HTML Node side) ──────────────────────────────────────────────
158
+ // Helper to extract coordinates regardless of event type
159
+ const getPointerEvent = (e) => {
160
+ if ('touches' in e && e.touches.length > 0)
161
+ return e.touches[0];
162
+ return e;
163
+ };
164
+ const handleDragStart = (itemId, e) => {
165
+ // Prevent browser scrolling while dragging
166
+ if (e.cancelable)
167
+ e.preventDefault();
168
+ const pointer = getPointerEvent(e);
169
+ let itemsToDrag;
170
+ if (selectedIds.has(itemId)) {
171
+ itemsToDrag = Array.from(selectedIds);
172
+ }
173
+ else {
174
+ itemsToDrag = [itemId];
175
+ setSelectedIds(new Set([itemId]));
176
+ }
177
+ const startPositions = new Map();
178
+ itemsToDrag.forEach((id) => {
179
+ const pos = getItemPosition(id);
180
+ if (pos)
181
+ startPositions.set(id, pos);
182
+ });
183
+ const canvasObjectsStartPos = new Map();
184
+ selectedCanvasObjects.forEach((obj) => {
185
+ canvasObjectsStartPos.set(obj, { left: obj.left || 0, top: obj.top || 0 });
186
+ });
187
+ const clickedPos = getItemPosition(itemId);
188
+ if (!clickedPos)
189
+ return;
190
+ const screenX = clickedPos.x * canvasZoom + canvasViewport.x;
191
+ const screenY = clickedPos.y * canvasZoom + canvasViewport.y;
192
+ dragStateRef.current = {
193
+ isDragging: true,
194
+ itemIds: itemsToDrag,
195
+ startPositions,
196
+ canvasObjectsStartPos,
197
+ offsetX: pointer.clientX - screenX,
198
+ offsetY: pointer.clientY - screenY,
199
+ };
200
+ setDragging({ itemIds: itemsToDrag });
201
+ document.body.style.cursor = "grabbing";
202
+ document.body.style.userSelect = "none";
203
+ // Prevents mobile pull-to-refresh
204
+ document.body.style.touchAction = "none";
205
+ };
206
+ // ── Drag move (HTML Node side) ───────────────────────────────────────────────
207
+ useEffect(() => {
208
+ if (!dragging)
209
+ return;
210
+ const handleMove = (e) => {
211
+ if (!dragStateRef.current.isDragging)
212
+ return;
213
+ // Prevent mobile scrolling
214
+ if (e.cancelable)
215
+ e.preventDefault();
216
+ const pointer = getPointerEvent(e);
217
+ if (rafIdRef.current !== null)
218
+ cancelAnimationFrame(rafIdRef.current);
219
+ rafIdRef.current = requestAnimationFrame(() => {
220
+ const { itemIds, startPositions, canvasObjectsStartPos, offsetX, offsetY } = dragStateRef.current;
221
+ const firstId = itemIds[0];
222
+ const firstStart = startPositions.get(firstId);
223
+ if (!firstStart)
224
+ return;
225
+ // Calculate new world coordinates
226
+ const newX = (pointer.clientX - offsetX - canvasViewport.x) / canvasZoom;
227
+ const newY = (pointer.clientY - offsetY - canvasViewport.y) / canvasZoom;
228
+ const deltaX = newX - firstStart.x;
229
+ const deltaY = newY - firstStart.y;
230
+ // Batch state updates for React
231
+ setLocalTasks((prev) => prev.map((t) => (itemIds.includes(t.id) ? {
232
+ ...t,
233
+ x: (startPositions.get(t.id)?.x || 0) + deltaX,
234
+ y: (startPositions.get(t.id)?.y || 0) + deltaY
235
+ } : t)));
236
+ setLocalDocuments((prev) => prev.map((d) => (itemIds.includes(d.id) ? {
237
+ ...d,
238
+ x: (startPositions.get(d.id)?.x || 0) + deltaX,
239
+ y: (startPositions.get(d.id)?.y || 0) + deltaY
240
+ } : d)));
241
+ if (fabricCanvas?.current) {
242
+ canvasObjectsStartPos.forEach((startPos, obj) => {
243
+ obj.set({
244
+ left: startPos.left + deltaX,
245
+ top: startPos.top + deltaY,
246
+ });
247
+ obj.setCoords();
248
+ });
249
+ fabricCanvas.current.requestRenderAll();
250
+ }
251
+ });
252
+ };
253
+ const handleEnd = () => {
254
+ if (rafIdRef.current !== null)
255
+ cancelAnimationFrame(rafIdRef.current);
256
+ dragStateRef.current.isDragging = false;
257
+ setDragging(null);
258
+ document.body.style.cursor = "";
259
+ document.body.style.userSelect = "";
260
+ document.body.style.touchAction = "";
261
+ onTasksUpdate?.(localTasks);
262
+ onDocumentsUpdate?.(localDocuments);
263
+ };
264
+ window.addEventListener("mousemove", handleMove, { passive: false });
265
+ window.addEventListener("mouseup", handleEnd);
266
+ window.addEventListener("touchmove", handleMove, { passive: false });
267
+ window.addEventListener("touchend", handleEnd);
268
+ window.addEventListener("touchcancel", handleEnd);
269
+ return () => {
270
+ window.removeEventListener("mousemove", handleMove);
271
+ window.removeEventListener("mouseup", handleEnd);
272
+ window.removeEventListener("touchmove", handleMove);
273
+ window.removeEventListener("touchend", handleEnd);
274
+ window.removeEventListener("touchcancel", handleEnd);
275
+ };
276
+ }, [dragging, canvasZoom, canvasViewport, localTasks, localDocuments, fabricCanvas]);
277
+ // ── Selection, Status, Keyboard Logic ────────────────────────────────────────
278
+ const handleSelect = (id, e) => {
279
+ if (e?.shiftKey || e?.ctrlKey || e?.metaKey) {
280
+ setSelectedIds((prev) => {
281
+ const next = new Set(prev);
282
+ next.has(id) ? next.delete(id) : next.add(id);
283
+ return next;
284
+ });
285
+ }
286
+ else {
287
+ setSelectedIds(new Set([id]));
288
+ }
289
+ };
290
+ const handleStatusChange = (taskId, newStatus) => {
291
+ const updated = localTasks.map((t) => (t.id === taskId ? { ...t, status: newStatus } : t));
292
+ setLocalTasks(updated);
293
+ onTasksUpdate?.(updated);
294
+ };
295
+ useEffect(() => {
296
+ const handleKeyDown = (e) => {
297
+ // Don't trigger if typing in input
298
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
299
+ return;
300
+ // Select All
301
+ if ((e.ctrlKey || e.metaKey) && e.key === "a") {
302
+ e.preventDefault();
303
+ setSelectedIds(new Set([...localTasks.map((t) => t.id), ...localDocuments.map((d) => d.id)]));
304
+ }
305
+ // Clear selection
306
+ if (e.key === "Escape") {
307
+ setSelectedIds(new Set());
308
+ }
309
+ // ← ADD THIS: Delete selected nodes
310
+ if ((e.key === "Delete" || e.key === "Backspace") && selectedIds.size > 0) {
311
+ e.preventDefault();
312
+ const updatedTasks = localTasks.filter((t) => !selectedIds.has(t.id));
313
+ const updatedDocs = localDocuments.filter((d) => !selectedIds.has(d.id));
314
+ setLocalTasks(updatedTasks);
315
+ setLocalDocuments(updatedDocs);
316
+ setSelectedIds(new Set());
317
+ onTasksUpdate?.(updatedTasks);
318
+ onDocumentsUpdate?.(updatedDocs);
319
+ }
320
+ };
321
+ window.addEventListener("keydown", handleKeyDown);
322
+ return () => window.removeEventListener("keydown", handleKeyDown);
323
+ }, [localTasks, localDocuments, selectedIds, onTasksUpdate, onDocumentsUpdate]);
324
+ // ── Render helper ────────────────────────────────────────────────────────────
325
+ const renderItem = (id, x, y, children) => {
326
+ const screenX = x * canvasZoom;
327
+ const screenY = y * canvasZoom;
328
+ // 1. Detect if the user is interacting with the canvas at all
329
+ // 'dragging' is your existing state.
330
+ // You might want to pass 'isZooming' or 'isPanning' from your main canvas component here.
331
+ const isDragging = dragging?.itemIds.includes(id);
332
+ return (_jsx("div", { className: "pointer-events-auto absolute", style: {
333
+ left: 0,
334
+ top: 0,
335
+ // 2. Use translate3d for GPU performance
336
+ transform: `translate3d(${screenX}px, ${screenY}px, 0) scale(${canvasZoom})`,
337
+ transformOrigin: "top left",
338
+ // 3. THE FIX: Remove transition entirely during any viewport change
339
+ // Any 'ease' during zoom causes the "shaking" behavior.
340
+ transition: "none",
341
+ // 4. Optimization
342
+ willChange: "transform",
343
+ zIndex: isDragging ? 1000 : 1,
344
+ }, children: children }, id));
345
+ };
346
+ return (_jsx("div", { ref: overlayRef, className: "absolute inset-0 pointer-events-none", style: { zIndex: 50 }, onWheel: handleOverlayWheel, onClick: (e) => {
347
+ if (e.target === e.currentTarget)
348
+ setSelectedIds(new Set());
349
+ }, children: _jsxs("div", { className: "absolute top-0 left-0 pointer-events-none", style: {
350
+ transform: `translate(${canvasViewport.x}px, ${canvasViewport.y}px)`,
351
+ transformOrigin: "top left",
352
+ }, children: [localTasks.map((task) => renderItem(task.id, task.x, task.y, _jsx(TaskNode, { ...task, isSelected: selectedIds.has(task.id), onSelect: handleSelect, onDragStart: handleDragStart, onStatusChange: handleStatusChange, zoom: 1 }))), localDocuments.map((doc) => renderItem(doc.id, doc.x, doc.y, _jsx(DocumentNode, { ...doc, isSelected: selectedIds.has(doc.id), onSelect: handleSelect, onDragStart: handleDragStart })))] }) }));
353
+ }
@@ -0,0 +1,17 @@
1
+ interface TaskNodeProps {
2
+ id: string;
3
+ title: string;
4
+ status: "todo" | "in-progress" | "done";
5
+ assignee?: string;
6
+ project?: string;
7
+ priority?: "low" | "medium" | "high";
8
+ dueDate?: string;
9
+ isSelected?: boolean;
10
+ onSelect?: (id: string, e?: React.MouseEvent) => void;
11
+ onDragStart?: (id: string, e: React.MouseEvent | React.TouchEvent) => void;
12
+ onStatusChange?: (id: string, newStatus: "todo" | "in-progress" | "done") => void;
13
+ zoom?: number;
14
+ }
15
+ export default function TaskNode({ id, title, status, assignee, project, priority, dueDate, isSelected, onSelect, onDragStart, onStatusChange, zoom, }: TaskNodeProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
17
+ //# sourceMappingURL=custom-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-node.d.ts","sourceRoot":"","sources":["../../../src/components/node/custom-node.tsx"],"names":[],"mappings":"AAYA,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,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;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACtD,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC3E,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,KAAK,IAAI,CAAC;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAC/B,EAAE,EACF,KAAK,EACL,MAAM,EACN,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,OAAO,EACP,UAAkB,EAClB,QAAQ,EACR,WAAW,EACX,cAAc,EACd,IAAQ,GACT,EAAE,aAAa,2CAgJf"}
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
4
+ import { Check, ListCheck, Calendar, User2, AlertCircle } from "lucide-react";
5
+ export default function TaskNode({ id, title, status, assignee, project, priority, dueDate, isSelected = false, onSelect, onDragStart, onStatusChange, zoom = 1, // Default to 1:1 scale
6
+ }) {
7
+ const [isDragging, setIsDragging] = useState(false);
8
+ const statusConfig = {
9
+ todo: { accent: "bg-[#3A3A3C]", pill: "bg-[#2C2C2E] text-gray-400 border-gray-700", label: "To Do" },
10
+ "in-progress": { accent: "bg-[#029AFF]", pill: "bg-[#029AFF]/10 text-[#029AFF] border-[#029AFF]/20", label: "In Progress" },
11
+ done: { accent: "bg-[#10B981]", pill: "bg-[#10B981]/10 text-[#10B981] border-[#10B981]/20", label: "Completed" },
12
+ };
13
+ const currentStatus = statusConfig[status];
14
+ // Logic: Increase border thickness if zoomed out to keep it visible
15
+ const dynamicBorderWidth = zoom < 0.6 ? "2px" : "1px";
16
+ const focusRingSize = isSelected ? (zoom < 0.5 ? "6px" : "3px") : "0px";
17
+ // 2. Update the internal handler
18
+ const handleStart = (e) => {
19
+ const target = e.target;
20
+ // Don't drag if clicking buttons or the scrollable text
21
+ if (target.closest('.interactive-element'))
22
+ return;
23
+ // For touch events, we don't want the browser to scroll the page
24
+ // but we only preventDefault if it's not an interactive element
25
+ if (e.type === 'touchstart' && e.cancelable) {
26
+ // We don't preventDefault here because it might block clicks on
27
+ // internal non-interactive elements, let handleDragStart handle it.
28
+ }
29
+ setIsDragging(true);
30
+ if (onDragStart)
31
+ onDragStart(id, e);
32
+ if (onSelect)
33
+ onSelect(id, e); // Cast for compatibility
34
+ };
35
+ // 3. Update the cleanup effect
36
+ useEffect(() => {
37
+ const handleUp = () => setIsDragging(false);
38
+ if (isDragging) {
39
+ window.addEventListener("mouseup", handleUp);
40
+ window.addEventListener("touchend", handleUp); // Add touch cleanup
41
+ return () => {
42
+ window.removeEventListener("mouseup", handleUp);
43
+ window.removeEventListener("touchend", handleUp);
44
+ };
45
+ }
46
+ }, [isDragging]);
47
+ return (_jsxs("div", { onMouseDown: handleStart, onTouchStart: handleStart, className: `
48
+ relative w-[310px] bg-[#161617] rounded-2xl transition-all duration-300 ease-out
49
+ ${isDragging ? "cursor-grabbing scale-[1.04] z-[100] shadow-2xl" : "cursor-grab shadow-xl"}
50
+ `, style: {
51
+ border: `${dynamicBorderWidth} solid ${isSelected ? "#10B981" : "#2C2C2E"}`,
52
+ boxShadow: isSelected ? `0 0 25px rgba(16,185,129, ${0.2 / zoom})` : 'none',
53
+ outline: `${focusRingSize} solid rgba(16,185,129, 0.2)`
54
+ }, children: [_jsx("div", { className: `absolute left-0 top-6 bottom-6 w-1 rounded-r-full transition-colors duration-500 ${currentStatus.accent}` }), _jsxs("div", { className: "p-5 pl-6", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "w-5 h-5 flex items-center justify-center rounded-md bg-[#10B981]/10 border border-[#10B981]/20", children: _jsx(ListCheck, { className: "w-3 h-3 text-[#10B981]" }) }), zoom > 0.4 && (_jsx("span", { className: "text-[10px] font-bold text-gray-500 uppercase tracking-widest truncate max-w-[120px]", children: project || "Task" }))] }), _jsx("div", { className: `interactive-element px-2 py-0.5 rounded-lg text-[9px] font-bold border uppercase ${currentStatus.pill}`, children: currentStatus.label })] }), _jsx("div", { className: "mb-4", children: _jsx("h3", { className: `text-[15px] font-semibold leading-snug transition-all duration-500 ${status === "done" ? "text-gray-600 line-through opacity-70" : "text-white"}`, children: title }) }), zoom > 0.5 && (_jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [priority && (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(AlertCircle, { className: `w-3 h-3 ${priority === 'high' ? 'text-red-500' : 'text-gray-500'}` }), _jsx("span", { className: "text-[11px] text-gray-400 font-medium capitalize", children: priority })] })), dueDate && (_jsxs("div", { className: "flex items-center gap-1.5 text-gray-500", children: [_jsx(Calendar, { className: "w-3 h-3" }), _jsx("span", { className: "text-[11px] font-medium", children: dueDate })] }))] })), _jsxs("div", { className: "mt-5 pt-4 border-t border-[#252526] flex items-center justify-between", children: [_jsx("div", { className: "flex items-center gap-2", children: assignee ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "w-6 h-6 rounded-full bg-[#10B981] flex items-center justify-center text-[10px] font-bold text-black ring-2 ring-[#161617]", children: assignee.charAt(0) }), zoom > 0.6 && _jsx("span", { className: "text-[11px] text-gray-400 font-medium", children: assignee })] })) : (_jsx(User2, { className: "w-3.5 h-3.5 text-gray-600" })) }), _jsx("button", { onClick: (e) => {
55
+ e.stopPropagation();
56
+ onStatusChange?.(id, status === "done" ? "todo" : "done");
57
+ }, className: `
58
+ interactive-element w-8 h-8 rounded-xl border flex items-center justify-center transition-all duration-300
59
+ ${status === "done"
60
+ ? "bg-[#10B981] border-[#10B981] shadow-lg shadow-[#10B981]/20"
61
+ : "bg-transparent border-[#333] hover:border-[#10B981] text-transparent hover:text-[#10B981]/50"}
62
+ `, children: _jsx(Check, { className: `w-4 h-4 ${status === "done" ? "text-black" : "text-current"}`, strokeWidth: 3 }) })] })] })] }));
63
+ }
@@ -0,0 +1,14 @@
1
+ interface DocumentNodeProps {
2
+ id: string;
3
+ title: string;
4
+ project: string;
5
+ breadcrumb?: string[];
6
+ preview: string;
7
+ updatedAt?: string;
8
+ isSelected?: boolean;
9
+ onSelect?: (id: string, e?: React.MouseEvent) => void;
10
+ onDragStart?: (id: string, e: React.MouseEvent | React.TouchEvent) => void;
11
+ }
12
+ export default function DocumentNode({ id, title, project, breadcrumb, preview, updatedAt, isSelected, onSelect, onDragStart, }: DocumentNodeProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=document-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-node.d.ts","sourceRoot":"","sources":["../../../src/components/node/document-node.tsx"],"names":[],"mappings":"AAKA,UAAU,iBAAiB;IACzB,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,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACtD,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;CAC5E;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,EAAE,EACF,KAAK,EACL,OAAO,EACP,UAAe,EACf,OAAO,EACP,SAAS,EACT,UAAkB,EAClB,QAAQ,EACR,WAAW,GACZ,EAAE,iBAAiB,2CAuJnB"}
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
4
+ import { ChevronDown, ChevronUp, Clock, ExternalLink, MoreHorizontal, Layers } from "lucide-react";
5
+ export default function DocumentNode({ id, title, project, breadcrumb = [], preview, updatedAt, isSelected = false, onSelect, onDragStart, }) {
6
+ const [isDragging, setIsDragging] = useState(false);
7
+ const [isExpanded, setIsExpanded] = useState(false);
8
+ // 2. Update the internal handler
9
+ const handleStart = (e) => {
10
+ const target = e.target;
11
+ // Don't drag if clicking buttons or the scrollable text
12
+ if (target.closest('.interactive-element'))
13
+ return;
14
+ // For touch events, we don't want the browser to scroll the page
15
+ // but we only preventDefault if it's not an interactive element
16
+ if (e.type === 'touchstart' && e.cancelable) {
17
+ // We don't preventDefault here because it might block clicks on
18
+ // internal non-interactive elements, let handleDragStart handle it.
19
+ }
20
+ setIsDragging(true);
21
+ if (onDragStart)
22
+ onDragStart(id, e);
23
+ if (onSelect)
24
+ onSelect(id, e); // Cast for compatibility
25
+ };
26
+ // 3. Update the cleanup effect
27
+ useEffect(() => {
28
+ const handleUp = () => setIsDragging(false);
29
+ if (isDragging) {
30
+ window.addEventListener("mouseup", handleUp);
31
+ window.addEventListener("touchend", handleUp); // Add touch cleanup
32
+ return () => {
33
+ window.removeEventListener("mouseup", handleUp);
34
+ window.removeEventListener("touchend", handleUp);
35
+ };
36
+ }
37
+ }, [isDragging]);
38
+ return (_jsxs("div", { onMouseDown: handleStart, onTouchStart: handleStart, className: `
39
+ relative w-[320px] bg-[#121214] rounded-2xl border transition-all duration-500 ease-[cubic-bezier(0.23,1,0.32,1)]
40
+ ${isDragging ? "cursor-grabbing scale-[1.03] z-50 shadow-2xl" : "cursor-grab shadow-lg hover:border-[#3e3e42]"}
41
+ ${isSelected ? "border-[#029AFF] ring-1 ring-[#029AFF]" : "border-[#28282b]"}
42
+ ${isExpanded ? "w-[400px]" : "w-[320px]"}
43
+ `, children: [isSelected && (_jsx("div", { className: "absolute -inset-px bg-gradient-to-r from-[#029AFF]/20 to-[#7000FF]/20 rounded-2xl blur-md -z-10" })), _jsxs("div", { className: "flex items-center justify-between p-3 pb-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "p-1.5 bg-[#029AFF]/10 rounded-lg", children: _jsx(Layers, { className: "w-3.5 h-3.5 text-[#029AFF]" }) }), _jsx("span", { className: "text-[10px] font-bold text-gray-500 tracking-tighter uppercase", children: project })] }), _jsx("button", { className: "interactive-element p-1 hover:bg-white/5 rounded-md text-gray-600 transition-colors", children: _jsx(MoreHorizontal, { className: "w-4 h-4" }) })] }), _jsxs("div", { className: "p-4 pt-2", children: [_jsxs("div", { onClick: () => setIsExpanded(!isExpanded), className: "interactive-element flex items-start justify-between group/title cursor-pointer", children: [_jsx("h3", { className: "text-[16px] font-semibold text-white leading-tight group-hover/title:text-[#029AFF] transition-colors pr-2", children: title }), _jsx("div", { className: "mt-1", children: isExpanded ?
44
+ _jsx(ChevronUp, { className: "w-4 h-4 text-gray-500" }) :
45
+ _jsx(ChevronDown, { className: "w-4 h-4 text-gray-500" }) })] }), _jsx("div", { className: "flex items-center gap-2 mt-2 overflow-x-auto no-scrollbar pb-1", children: breadcrumb.map((item, i) => (_jsx("div", { className: "flex items-center gap-2 flex-shrink-0", children: _jsx("span", { className: "text-[10px] px-2 py-0.5 bg-[#1C1C1E] border border-[#2C2C2E] text-gray-400 rounded-full", children: item }) }, i))) }), _jsxs("div", { className: `
46
+ mt-4 relative transition-all duration-500 ease-in-out overflow-hidden
47
+ ${isExpanded ? "h-[300px]" : "h-[100px]"}
48
+ `, children: [_jsxs("div", { onMouseDown: (e) => e.stopPropagation(), className: "interactive-element h-full w-full bg-[#09090A] border border-[#232326] rounded-xl overflow-y-auto p-3 text-[13px] text-gray-400 leading-relaxed custom-scrollbar selection:bg-[#029AFF]/40", children: [preview, isExpanded && (_jsxs("div", { className: "mt-4 pt-4 border-t border-[#232326] flex items-center justify-between", children: [_jsx("span", { className: "text-[11px] text-gray-500 italic", children: "End of document preview" }), _jsxs("button", { className: "flex items-center gap-2 text-[11px] text-[#029AFF] hover:underline font-medium", children: ["Open Full File ", _jsx(ExternalLink, { className: "w-3 h-3" })] })] }))] }), !isExpanded && (_jsx("div", { className: "absolute bottom-0 left-0 right-0 h-10 bg-gradient-to-t from-[#09090A] to-transparent pointer-events-none" }))] }), _jsxs("div", { className: "mt-4 flex items-center justify-between opacity-60", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[10px] text-gray-400", children: [_jsx(Clock, { className: "w-3 h-3" }), _jsxs("span", { children: ["Updated ", updatedAt || "recently"] })] }), _jsx("div", { className: "flex -space-x-1.5", children: [1, 2].map(i => (_jsxs("div", { className: "w-5 h-5 rounded-full border-2 border-[#121214] bg-[#2C2C2E] flex items-center justify-center text-[8px] text-white", children: ["U", i] }, i))) })] })] }), _jsx("style", { children: `
49
+ .no-scrollbar::-webkit-scrollbar { display: none; }
50
+ .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
51
+ .custom-scrollbar::-webkit-scrollbar { width: 5px; }
52
+ .custom-scrollbar::-webkit-scrollbar-thumb {
53
+ background: #232326;
54
+ border-radius: 10px;
55
+ }
56
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: #029AFF; }
57
+ ` })] }));
58
+ }
@@ -0,0 +1,14 @@
1
+ export interface DocumentTemplate {
2
+ id: string;
3
+ title: string;
4
+ project: string;
5
+ breadcrumb?: string[];
6
+ preview: string;
7
+ updatedAt?: string;
8
+ }
9
+ interface DocumentDropdownProps {
10
+ onAddDocument: (doc: DocumentTemplate) => void;
11
+ }
12
+ export default function DocumentDropdown({ onAddDocument, }: DocumentDropdownProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=document-dropdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-dropdown.d.ts","sourceRoot":"","sources":["../../../src/components/toolbar/document-dropdown.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,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;CACpB;AAED,UAAU,qBAAqB;IAC7B,aAAa,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAChD;AAkDD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,aAAa,GACd,EAAE,qBAAqB,2CA2FvB"}