Skip to content

Commit b1bf6aa

Browse files
authored
Merge pull request #18 from shivam7147/main
Implement Keyboard Shortcuts for Tools
2 parents a6b69ff + a6d44f9 commit b1bf6aa

File tree

2 files changed

+74
-44
lines changed

2 files changed

+74
-44
lines changed

client/src/components/Canvas.jsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const Canvas = () => {
1010
const [activeColor, setActiveColor] = useState("#000000");
1111
const [strokeWidth, setStrokeWidth] = useState(3);
1212
const [isDrawing, setIsDrawing] = useState(false);
13+
const [isCanvasFocused, setIsCanvasFocused] = useState(false); // 👈 new state
1314

1415
useEffect(() => {
1516
const canvas = canvasRef.current;
@@ -33,6 +34,22 @@ export const Canvas = () => {
3334
return () => window.removeEventListener("resize", handleResize);
3435
}, []);
3536

37+
// 🎹 Keyboard shortcut handling
38+
useEffect(() => {
39+
const handleKeyDown = (e) => {
40+
if (!isCanvasFocused) return; // only when canvas focused
41+
42+
if (e.key === "p" || e.key === "P" || e.key === "1") {
43+
handleToolChange("pen");
44+
} else if (e.key === "e" || e.key === "E" || e.key === "2") {
45+
handleToolChange("eraser");
46+
}
47+
};
48+
49+
window.addEventListener("keydown", handleKeyDown);
50+
return () => window.removeEventListener("keydown", handleKeyDown);
51+
}, [isCanvasFocused]);
52+
3653
const startDrawing = (e) => {
3754
if (activeTool !== "pen" && activeTool !== "eraser") return;
3855

@@ -92,11 +109,14 @@ export const Canvas = () => {
92109

93110
<canvas
94111
ref={canvasRef}
112+
tabIndex={0} // 👈 allows focus
113+
onFocus={() => setIsCanvasFocused(true)} // 👈 activate shortcuts
114+
onBlur={() => setIsCanvasFocused(false)} // 👈 deactivate shortcuts
95115
onMouseDown={startDrawing}
96116
onMouseMove={draw}
97117
onMouseUp={stopDrawing}
98118
onMouseLeave={stopDrawing}
99-
className="cursor-crosshair"
119+
className="cursor-crosshair focus:outline-2 focus:outline-primary"
100120
/>
101121

102122
<div className="fixed bottom-6 left-1/2 -translate-x-1/2 pointer-events-none">

client/src/components/Toolbar.jsx

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,62 @@
1-
import { MousePointer2, Pen, Eraser, Square, Circle, Minus, Trash2 } from "lucide-react";
1+
import {
2+
MousePointer2,
3+
Pen,
4+
Eraser,
5+
Square,
6+
Circle,
7+
Minus,
8+
Trash2,
9+
} from "lucide-react";
210
import { cn } from "../lib/utils";
311
import { Button } from "./ui/Button";
412
import { Separator } from "./ui/Separator";
513

614
export const Toolbar = ({ activeTool, onToolChange, onClear }) => {
7-
const tools = [
8-
{ type: "select", icon: MousePointer2 },
9-
{ type: "pen", icon: Pen },
10-
{ type: "eraser", icon: Eraser },
11-
{ type: "rectangle", icon: Square },
12-
{ type: "circle", icon: Circle },
13-
{ type: "line", icon: Minus },
14-
];
15+
const tools = [
16+
{ type: "select", icon: MousePointer2 },
17+
{ type: "pen", icon: Pen },
18+
{ type: "eraser", icon: Eraser },
19+
{ type: "rectangle", icon: Square },
20+
{ type: "circle", icon: Circle },
21+
{ type: "line", icon: Minus },
22+
];
1523

16-
return (
17-
<div className="fixed top-6 left-1/2 -translate-x-1/2 z-50">
18-
<div className="bg-toolbar border border-toolbar-border rounded-2xl shadow-xl backdrop-blur-sm p-2 flex items-center gap-2">
19-
{tools.map((tool) => {
20-
const Icon = tool.icon;
21-
return (
22-
<Button
23-
key={tool.type}
24-
variant="ghost"
25-
size="icon"
26-
onClick={() => onToolChange(tool.type)}
27-
className={cn(
28-
"h-10 w-10 transition-all duration-200 hover:bg-secondary",
29-
activeTool === tool.type && "bg-primary text-primary-foreground hover:bg-primary/90"
30-
)}
31-
aria-label={tool.type}
32-
>
33-
<Icon className="h-5 w-5" />
34-
</Button>
35-
);
36-
})}
24+
return (
25+
<div className="fixed top-6 left-1/2 -translate-x-1/2 z-50">
26+
<div className="bg-toolbar border border-toolbar-border rounded-2xl shadow-xl backdrop-blur-sm p-2 flex items-center gap-2">
27+
{tools.map((tool) => {
28+
const Icon = tool.icon;
29+
return (
30+
<Button
31+
key={tool.type}
32+
variant="ghost"
33+
size="icon"
34+
onClick={() => onToolChange(tool.type)}
35+
className={cn(
36+
"h-10 w-10 transition-all duration-200 hover:bg-secondary",
37+
activeTool === tool.type
38+
? "bg-primary text-primary-foreground ring-2 ring-offset-2 ring-primary"
39+
: ""
40+
)}
41+
aria-label={tool.type}
42+
>
43+
<Icon className="h-5 w-5" />
44+
</Button>
45+
);
46+
})}
3747

38-
<Separator orientation="vertical" className="h-8 mx-1" />
48+
<Separator orientation="vertical" className="h-8 mx-1" />
3949

40-
<Button
41-
variant="ghost"
42-
size="icon"
43-
onClick={onClear}
44-
className="h-10 w-10 transition-all duration-200 hover:bg-destructive/10 hover:text-destructive"
45-
aria-label="Clear canvas"
46-
>
47-
<Trash2 className="h-5 w-5" />
48-
</Button>
49-
</div>
50-
</div>
51-
);
50+
<Button
51+
variant="ghost"
52+
size="icon"
53+
onClick={onClear}
54+
className="h-10 w-10 transition-all duration-200 hover:bg-destructive/10 hover:text-destructive"
55+
aria-label="Clear canvas"
56+
>
57+
<Trash2 className="h-5 w-5" />
58+
</Button>
59+
</div>
60+
</div>
61+
);
5262
};

0 commit comments

Comments
 (0)