Skip to content

Commit fd3cff2

Browse files
committed
feat: allow delete key to remove selected shape and prevent accidental shape creation on click
1 parent 4505f16 commit fd3cff2

File tree

1 file changed

+190
-136
lines changed

1 file changed

+190
-136
lines changed

apps/collabydraw/canvas-engine/CanvasEngine.ts

Lines changed: 190 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,6 @@ export class CanvasEngine {
205205
return;
206206
}
207207

208-
console.log(`Connecting to WebSocket with sessionId: ${this.token}`);
209-
210208
const url = `${WS_URL}?token=${encodeURIComponent(this.token!)}`;
211209
this.socket = new WebSocket(url);
212210

@@ -560,6 +558,7 @@ export class CanvasEngine {
560558
}
561559

562560
async init() {
561+
window.addEventListener("keydown", this.handleKeyDown);
563562
if (this.isStandalone) {
564563
try {
565564
const storedShapes = localStorage.getItem(LOCALSTORAGE_CANVAS_KEY);
@@ -1168,158 +1167,162 @@ export class CanvasEngine {
11681167
const width = x - this.startX;
11691168
const height = y - this.startY;
11701169

1171-
let shape: Shape | null = null;
1172-
switch (this.activeTool) {
1173-
case "rectangle":
1174-
shape = {
1175-
id: this.streamingShapeId || uuidv4(),
1176-
type: "rectangle",
1177-
x: this.startX,
1178-
y: this.startY,
1179-
width,
1180-
height,
1181-
strokeWidth: this.strokeWidth,
1182-
strokeFill: this.strokeFill,
1183-
bgFill: this.bgFill,
1184-
rounded: this.strokeEdge,
1185-
strokeStyle: this.strokeStyle,
1186-
roughStyle: this.roughStyle,
1187-
fillStyle: this.fillStyle,
1188-
};
1189-
break;
1190-
1191-
case "ellipse":
1192-
shape = {
1193-
id: this.streamingShapeId || uuidv4(),
1194-
type: "ellipse",
1195-
x: this.startX + width / 2,
1196-
y: this.startY + height / 2,
1197-
radX: Math.abs(width / 2),
1198-
radY: Math.abs(height / 2),
1199-
strokeWidth: this.strokeWidth,
1200-
strokeFill: this.strokeFill,
1201-
bgFill: this.bgFill,
1202-
strokeStyle: this.strokeStyle,
1203-
roughStyle: this.roughStyle,
1204-
fillStyle: this.fillStyle,
1205-
};
1206-
break;
1207-
1208-
case "diamond":
1209-
shape = {
1210-
id: this.streamingShapeId || uuidv4(),
1211-
type: "diamond",
1212-
x: this.startX,
1213-
y: this.startY,
1214-
width: Math.abs(x - this.startX) * 2,
1215-
height: Math.abs(y - this.startY) * 2,
1216-
strokeWidth: this.strokeWidth,
1217-
strokeFill: this.strokeFill,
1218-
bgFill: this.bgFill,
1219-
rounded: this.strokeEdge,
1220-
strokeStyle: this.strokeStyle,
1221-
roughStyle: this.roughStyle,
1222-
fillStyle: this.fillStyle,
1223-
};
1224-
break;
1225-
1226-
case "line":
1227-
shape = {
1228-
id: this.streamingShapeId || uuidv4(),
1229-
type: "line",
1230-
x: this.startX,
1231-
y: this.startY,
1232-
toX: x,
1233-
toY: y,
1234-
strokeWidth: this.strokeWidth,
1235-
strokeFill: this.strokeFill,
1236-
strokeStyle: this.strokeStyle,
1237-
roughStyle: this.roughStyle,
1238-
};
1239-
break;
1240-
1241-
case "arrow":
1242-
shape = {
1243-
id: this.streamingShapeId || uuidv4(),
1244-
type: "arrow",
1245-
x: this.startX,
1246-
y: this.startY,
1247-
toX: x,
1248-
toY: y,
1249-
strokeWidth: this.strokeWidth,
1250-
strokeFill: this.strokeFill,
1251-
strokeStyle: this.strokeStyle,
1252-
roughStyle: this.roughStyle,
1253-
};
1254-
break;
1170+
const isClick = Math.abs(width) > 5 && Math.abs(height) > 5;
12551171

1256-
case "free-draw":
1257-
const currentShape =
1258-
this.existingShapes[this.existingShapes.length - 1];
1259-
if (currentShape?.type === "free-draw") {
1172+
if (isClick) {
1173+
let shape: Shape | null = null;
1174+
switch (this.activeTool) {
1175+
case "rectangle":
12601176
shape = {
12611177
id: this.streamingShapeId || uuidv4(),
1262-
type: "free-draw",
1263-
points: currentShape.points,
1178+
type: "rectangle",
1179+
x: this.startX,
1180+
y: this.startY,
1181+
width,
1182+
height,
12641183
strokeWidth: this.strokeWidth,
12651184
strokeFill: this.strokeFill,
12661185
bgFill: this.bgFill,
1186+
rounded: this.strokeEdge,
12671187
strokeStyle: this.strokeStyle,
1188+
roughStyle: this.roughStyle,
12681189
fillStyle: this.fillStyle,
12691190
};
1270-
}
1271-
break;
1191+
break;
12721192

1273-
case "grab":
1274-
this.startX = e.clientX;
1275-
this.startY = e.clientY;
1276-
}
1193+
case "ellipse":
1194+
shape = {
1195+
id: this.streamingShapeId || uuidv4(),
1196+
type: "ellipse",
1197+
x: this.startX + width / 2,
1198+
y: this.startY + height / 2,
1199+
radX: Math.abs(width / 2),
1200+
radY: Math.abs(height / 2),
1201+
strokeWidth: this.strokeWidth,
1202+
strokeFill: this.strokeFill,
1203+
bgFill: this.bgFill,
1204+
strokeStyle: this.strokeStyle,
1205+
roughStyle: this.roughStyle,
1206+
fillStyle: this.fillStyle,
1207+
};
1208+
break;
12771209

1278-
if (!shape) {
1279-
return;
1280-
}
1210+
case "diamond":
1211+
shape = {
1212+
id: this.streamingShapeId || uuidv4(),
1213+
type: "diamond",
1214+
x: this.startX,
1215+
y: this.startY,
1216+
width: Math.abs(x - this.startX) * 2,
1217+
height: Math.abs(y - this.startY) * 2,
1218+
strokeWidth: this.strokeWidth,
1219+
strokeFill: this.strokeFill,
1220+
bgFill: this.bgFill,
1221+
rounded: this.strokeEdge,
1222+
strokeStyle: this.strokeStyle,
1223+
roughStyle: this.roughStyle,
1224+
fillStyle: this.fillStyle,
1225+
};
1226+
break;
12811227

1282-
this.existingShapes.push(shape);
1283-
this.notifyShapeCountChange();
1228+
case "line":
1229+
shape = {
1230+
id: this.streamingShapeId || uuidv4(),
1231+
type: "line",
1232+
x: this.startX,
1233+
y: this.startY,
1234+
toX: x,
1235+
toY: y,
1236+
strokeWidth: this.strokeWidth,
1237+
strokeFill: this.strokeFill,
1238+
strokeStyle: this.strokeStyle,
1239+
roughStyle: this.roughStyle,
1240+
};
1241+
break;
12841242

1285-
if (this.isStandalone) {
1286-
try {
1287-
localStorage.setItem(
1288-
LOCALSTORAGE_CANVAS_KEY,
1289-
JSON.stringify(this.existingShapes)
1290-
);
1291-
} catch (e) {
1292-
console.error("Error saving shapes to localStorage:", e);
1243+
case "arrow":
1244+
shape = {
1245+
id: this.streamingShapeId || uuidv4(),
1246+
type: "arrow",
1247+
x: this.startX,
1248+
y: this.startY,
1249+
toX: x,
1250+
toY: y,
1251+
strokeWidth: this.strokeWidth,
1252+
strokeFill: this.strokeFill,
1253+
strokeStyle: this.strokeStyle,
1254+
roughStyle: this.roughStyle,
1255+
};
1256+
break;
1257+
1258+
case "free-draw":
1259+
const currentShape =
1260+
this.existingShapes[this.existingShapes.length - 1];
1261+
if (currentShape?.type === "free-draw") {
1262+
shape = {
1263+
id: this.streamingShapeId || uuidv4(),
1264+
type: "free-draw",
1265+
points: currentShape.points,
1266+
strokeWidth: this.strokeWidth,
1267+
strokeFill: this.strokeFill,
1268+
bgFill: this.bgFill,
1269+
strokeStyle: this.strokeStyle,
1270+
fillStyle: this.fillStyle,
1271+
};
1272+
}
1273+
break;
1274+
1275+
case "grab":
1276+
this.startX = e.clientX;
1277+
this.startY = e.clientY;
12931278
}
1294-
} else if (this.sendMessage && this.roomId) {
1295-
this.clearCanvas();
12961279

1297-
const message = {
1298-
type: WsDataType.DRAW,
1299-
id: shape.id,
1300-
message: shape,
1301-
roomId: this.roomId,
1302-
};
1280+
if (!shape) {
1281+
return;
1282+
}
13031283

1304-
try {
1305-
this.sendMessage?.(JSON.stringify(message));
1306-
} catch (e) {
1307-
MessageQueue.enqueue({
1308-
type: WsDataType.UPDATE,
1284+
this.existingShapes.push(shape);
1285+
this.notifyShapeCountChange();
1286+
1287+
if (this.isStandalone) {
1288+
try {
1289+
localStorage.setItem(
1290+
LOCALSTORAGE_CANVAS_KEY,
1291+
JSON.stringify(this.existingShapes)
1292+
);
1293+
} catch (e) {
1294+
console.error("Error saving shapes to localStorage:", e);
1295+
}
1296+
} else if (this.sendMessage && this.roomId) {
1297+
this.clearCanvas();
1298+
1299+
const message = {
1300+
type: WsDataType.DRAW,
13091301
id: shape.id,
1310-
message: JSON.stringify(shape),
1311-
connectionId: this.connectionId!,
1302+
message: shape,
13121303
roomId: this.roomId,
1313-
userId: this.userId!,
1314-
userName: this.userName!,
1315-
timestamp: new Date().toISOString(),
1316-
participants: null,
1317-
});
1318-
console.error("Error sending shape update ws message", e);
1304+
};
1305+
1306+
try {
1307+
this.sendMessage?.(JSON.stringify(message));
1308+
} catch (e) {
1309+
MessageQueue.enqueue({
1310+
type: WsDataType.UPDATE,
1311+
id: shape.id,
1312+
message: JSON.stringify(shape),
1313+
connectionId: this.connectionId!,
1314+
roomId: this.roomId,
1315+
userId: this.userId!,
1316+
userName: this.userName!,
1317+
timestamp: new Date().toISOString(),
1318+
participants: null,
1319+
});
1320+
console.error("Error sending shape update ws message", e);
1321+
}
13191322
}
1323+
this.streamingShapeId = null;
1324+
this.clearCanvas();
13201325
}
1321-
this.streamingShapeId = null;
1322-
this.clearCanvas();
13231326
};
13241327

13251328
mouseWheelHandler = (e: WheelEvent) => {
@@ -2398,7 +2401,7 @@ export class CanvasEngine {
23982401
);
23992402
} catch (e) {
24002403
MessageQueue.enqueue({
2401-
type: WsDataType.UPDATE,
2404+
type: WsDataType.ERASER,
24022405
id: erasedShape.id,
24032406
message: null,
24042407
roomId: this.roomId,
@@ -2408,7 +2411,7 @@ export class CanvasEngine {
24082411
participants: null,
24092412
connectionId: this.connectionId!,
24102413
});
2411-
console.error("Error sending shape update ws message", e);
2414+
console.error("Error sending shape erase ws message", e);
24122415
}
24132416
}
24142417
}
@@ -2522,4 +2525,55 @@ export class CanvasEngine {
25222525
this.currentTheme = newTheme;
25232526
this.clearCanvas();
25242527
}
2528+
2529+
private handleKeyDown = (e: KeyboardEvent) => {
2530+
if (e.key === "Delete" || e.key === "Backspace") {
2531+
if (
2532+
this.activeTool === "selection" &&
2533+
this.SelectionController.isShapeSelected()
2534+
) {
2535+
const selectedShape = this.SelectionController.getSelectedShape();
2536+
if (selectedShape && selectedShape.id) {
2537+
this.removeShape(selectedShape.id);
2538+
this.SelectionController.setSelectedShape(null);
2539+
this.notifyShapeCountChange();
2540+
2541+
if (this.isStandalone) {
2542+
try {
2543+
localStorage.setItem(
2544+
LOCALSTORAGE_CANVAS_KEY,
2545+
JSON.stringify(this.existingShapes)
2546+
);
2547+
} catch (e) {
2548+
console.error("Error saving shapes to localStorage:", e);
2549+
}
2550+
} else if (this.sendMessage && this.roomId) {
2551+
try {
2552+
this.sendMessage?.(
2553+
JSON.stringify({
2554+
type: WsDataType.ERASER,
2555+
id: selectedShape.id,
2556+
roomId: this.roomId,
2557+
})
2558+
);
2559+
} catch (e) {
2560+
MessageQueue.enqueue({
2561+
type: WsDataType.ERASER,
2562+
id: selectedShape.id,
2563+
message: null,
2564+
roomId: this.roomId,
2565+
userId: this.userId!,
2566+
userName: this.userName!,
2567+
timestamp: new Date().toISOString(),
2568+
participants: null,
2569+
connectionId: this.connectionId!,
2570+
});
2571+
console.error("Error sending shape erase ws message", e);
2572+
}
2573+
}
2574+
this.clearCanvas();
2575+
}
2576+
}
2577+
}
2578+
};
25252579
}

0 commit comments

Comments
 (0)