Skip to content

Commit 7a317c4

Browse files
committed
Added whiteboard
1 parent 14fadea commit 7a317c4

File tree

6 files changed

+1343
-0
lines changed

6 files changed

+1343
-0
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import React, { useState, useRef, useCallback } from 'react';
2+
import PlayHeader from 'common/playlists/PlayHeader';
3+
import Toolbar from './components/Toolbar';
4+
import Canvas from './components/Canvas';
5+
import LayerPanel from './components/LayerPanel';
6+
import './styles.css';
7+
8+
function VirtualWhiteboard(props) {
9+
// Drawing state
10+
const [tool, setTool] = useState('pen');
11+
const [color, setColor] = useState('#000000');
12+
const [lineWidth, setLineWidth] = useState(2);
13+
const [fillColor, setFillColor] = useState('#ffffff');
14+
const [fontSize, setFontSize] = useState(16);
15+
16+
// Layer state
17+
const [layers, setLayers] = useState([
18+
{ id: 1, name: 'Layer 1', visible: true, locked: false, data: [] }
19+
]);
20+
const [activeLayerId, setActiveLayerId] = useState(1);
21+
22+
// History state for undo/redo
23+
const [history, setHistory] = useState([]);
24+
const [historyStep, setHistoryStep] = useState(-1);
25+
26+
// Refs
27+
const canvasRef = useRef(null);
28+
29+
// Save state to history
30+
const saveToHistory = useCallback(() => {
31+
const newHistory = history.slice(0, historyStep + 1);
32+
newHistory.push(JSON.parse(JSON.stringify(layers)));
33+
setHistory(newHistory);
34+
setHistoryStep(newHistory.length - 1);
35+
}, [history, historyStep, layers]);
36+
37+
// Undo
38+
const handleUndo = useCallback(() => {
39+
if (historyStep > 0) {
40+
setHistoryStep(historyStep - 1);
41+
setLayers(JSON.parse(JSON.stringify(history[historyStep - 1])));
42+
}
43+
}, [historyStep, history]);
44+
45+
// Redo
46+
const handleRedo = useCallback(() => {
47+
if (historyStep < history.length - 1) {
48+
setHistoryStep(historyStep + 1);
49+
setLayers(JSON.parse(JSON.stringify(history[historyStep + 1])));
50+
}
51+
}, [historyStep, history]);
52+
53+
// Layer management
54+
const addLayer = () => {
55+
const newLayer = {
56+
id: Date.now(),
57+
name: `Layer ${layers.length + 1}`,
58+
visible: true,
59+
locked: false,
60+
data: []
61+
};
62+
setLayers([...layers, newLayer]);
63+
setActiveLayerId(newLayer.id);
64+
};
65+
66+
const deleteLayer = (layerId) => {
67+
if (layers.length === 1) return; // Keep at least one layer
68+
const newLayers = layers.filter((layer) => layer.id !== layerId);
69+
setLayers(newLayers);
70+
if (activeLayerId === layerId) {
71+
setActiveLayerId(newLayers[0].id);
72+
}
73+
};
74+
75+
const toggleLayerVisibility = (layerId) => {
76+
setLayers(
77+
layers.map((layer) => (layer.id === layerId ? { ...layer, visible: !layer.visible } : layer))
78+
);
79+
};
80+
81+
const toggleLayerLock = (layerId) => {
82+
setLayers(
83+
layers.map((layer) => (layer.id === layerId ? { ...layer, locked: !layer.locked } : layer))
84+
);
85+
};
86+
87+
const renameLayer = (layerId, newName) => {
88+
setLayers(layers.map((layer) => (layer.id === layerId ? { ...layer, name: newName } : layer)));
89+
};
90+
91+
// Update layer data
92+
const updateLayerData = (layerId, newData) => {
93+
setLayers(layers.map((layer) => (layer.id === layerId ? { ...layer, data: newData } : layer)));
94+
};
95+
96+
// Clear canvas
97+
const handleClear = () => {
98+
if (window.confirm('Clear all layers? This cannot be undone.')) {
99+
setLayers([{ id: Date.now(), name: 'Layer 1', visible: true, locked: false, data: [] }]);
100+
setHistory([]);
101+
setHistoryStep(-1);
102+
}
103+
};
104+
105+
// Export to image
106+
const handleExportImage = () => {
107+
if (canvasRef.current) {
108+
const canvas = canvasRef.current;
109+
const link = document.createElement('a');
110+
link.download = `whiteboard-${Date.now()}.png`;
111+
link.href = canvas.toDataURL();
112+
link.click();
113+
}
114+
};
115+
116+
// Export to PDF (using html2canvas approach)
117+
const handleExportPDF = async () => {
118+
if (canvasRef.current) {
119+
try {
120+
const canvas = canvasRef.current;
121+
const imgData = canvas.toDataURL('image/png');
122+
123+
// Create a simple PDF export using a new window
124+
const pdfWindow = window.open('', '_blank');
125+
pdfWindow.document.write(`
126+
<html>
127+
<head><title>Whiteboard Export</title></head>
128+
<body style="margin:0;padding:20px;">
129+
<img src="${imgData}" style="max-width:100%;height:auto;" />
130+
<script>
131+
window.onload = function() {
132+
window.print();
133+
};
134+
</script>
135+
</body>
136+
</html>
137+
`);
138+
pdfWindow.document.close();
139+
} catch (error) {
140+
console.error('Export failed:', error);
141+
alert('Export failed. Please try again.');
142+
}
143+
}
144+
};
145+
146+
return (
147+
<div className="play-details">
148+
<PlayHeader play={props} />
149+
<div className="play-details-body virtual-whiteboard">
150+
<div className="whiteboard-container">
151+
<Toolbar
152+
canRedo={historyStep < history.length - 1}
153+
canUndo={historyStep > 0}
154+
color={color}
155+
fillColor={fillColor}
156+
fontSize={fontSize}
157+
lineWidth={lineWidth}
158+
setColor={setColor}
159+
setFillColor={setFillColor}
160+
setFontSize={setFontSize}
161+
setLineWidth={setLineWidth}
162+
setTool={setTool}
163+
tool={tool}
164+
onClear={handleClear}
165+
onExportImage={handleExportImage}
166+
onExportPDF={handleExportPDF}
167+
onRedo={handleRedo}
168+
onUndo={handleUndo}
169+
/>
170+
171+
<div className="whiteboard-main">
172+
<Canvas
173+
activeLayerId={activeLayerId}
174+
color={color}
175+
fillColor={fillColor}
176+
fontSize={fontSize}
177+
layers={layers}
178+
lineWidth={lineWidth}
179+
ref={canvasRef}
180+
saveToHistory={saveToHistory}
181+
tool={tool}
182+
updateLayerData={updateLayerData}
183+
/>
184+
185+
<LayerPanel
186+
activeLayerId={activeLayerId}
187+
layers={layers}
188+
setActiveLayerId={setActiveLayerId}
189+
onAddLayer={addLayer}
190+
onDeleteLayer={deleteLayer}
191+
onRenameLayer={renameLayer}
192+
onToggleLock={toggleLayerLock}
193+
onToggleVisibility={toggleLayerVisibility}
194+
/>
195+
</div>
196+
</div>
197+
</div>
198+
</div>
199+
);
200+
}
201+
202+
export default VirtualWhiteboard;

0 commit comments

Comments
 (0)