Skip to content

Commit

Permalink
add shortcuts modal
Browse files Browse the repository at this point in the history
unified place where the user can know what shorcuts we support
  • Loading branch information
nagy-nabil committed Sep 7, 2023
1 parent 1e3402f commit d861d46
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 83 deletions.
42 changes: 37 additions & 5 deletions src/components/DropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
DialogTrigger,
} from "@/components/ui/dialog";
import { ActionExportScene, DestOpts } from "@/actions/ExportScene";
import { SHORTCUTS } from "@/constants";

function ResetCanvasAlert() {
return (
Expand Down Expand Up @@ -176,6 +177,41 @@ function OpenSceneDialog() {
);
}

function ShortCutsModal() {
return (
<Dialog>
{/*FIX: open dialog/alert dialog inside dropdown or context menu
@url https://github.com/radix-ui/primitives/issues/1836#issuecomment-1674338372
*/}
<DialogTrigger asChild>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
<Keyboard className="mr-2 h-4 w-4" />
<span>Keyboard shortcuts</span>
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className="h-full w-full cursor-auto overflow-y-auto md:h-4/6 md:w-3/4">
<DialogHeader>
<DialogTitle>Keyboard Shortcuts</DialogTitle>
</DialogHeader>
{Object.keys(SHORTCUTS).map((group) => (
<>
<h1 className="text-4xl" key={group}>
{group}
</h1>
{Object.keys(SHORTCUTS[group]).map((key) => (

Check failure on line 202 in src/components/DropDown.tsx

View workflow job for this annotation

GitHub Actions / Deploy-Preview

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ readonly editor: { readonly copy: { readonly description: "copy selected"; readonly keys: readonly ["ControlLeft", "c"]; }; readonly paste: { readonly description: "paste into the canvas"; readonly keys: readonly ["ControlLeft", "v"]; }; readonly undo: { ...; }; readonly delete: { ...; }; }; readonly tools: { ...;...'.
<p>
{SHORTCUTS[group][key]["description"]}

Check failure on line 204 in src/components/DropDown.tsx

View workflow job for this annotation

GitHub Actions / Deploy-Preview

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ readonly editor: { readonly copy: { readonly description: "copy selected"; readonly keys: readonly ["ControlLeft", "c"]; }; readonly paste: { readonly description: "paste into the canvas"; readonly keys: readonly ["ControlLeft", "v"]; }; readonly undo: { ...; }; readonly delete: { ...; }; }; readonly tools: { ...;...'.
{" : "}
<span>{SHORTCUTS[group][key]["keys"].join(" + ")}</span>

Check failure on line 206 in src/components/DropDown.tsx

View workflow job for this annotation

GitHub Actions / Deploy-Preview

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ readonly editor: { readonly copy: { readonly description: "copy selected"; readonly keys: readonly ["ControlLeft", "c"]; }; readonly paste: { readonly description: "paste into the canvas"; readonly keys: readonly ["ControlLeft", "v"]; }; readonly undo: { ...; }; readonly delete: { ...; }; }; readonly tools: { ...;...'.
</p>
))}
</>
))}
</DialogContent>
</Dialog>
);
}
export function DropDown() {
return (
<div className="fixed left-2 top-2">
Expand All @@ -201,11 +237,7 @@ export function DropDown() {
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Keyboard className="mr-2 h-4 w-4" />
<span>Keyboard shortcuts</span>
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
</DropdownMenuItem>
<ShortCutsModal />
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ExportModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Image } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { BsDownload } from "react-icons/bs";
import { FiCopy } from "react-icons/fi";
// import { Switch } from "./form/switch";
Expand Down
4 changes: 2 additions & 2 deletions src/components/ToolbarElementConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { commandManager } from "@/actions/commandManager";
import { ActionDeleteSelected } from "@/actions";
import { ActionExportScene, DestOpts } from "@/actions/ExportScene";
import useKeyboardShortcut from "@/hooks/useShortcut";
import { SHORTCUTS } from "@/constants";
//import { BsTextCenter, BsTextLeft, BsTextRight } from "react-icons/bs";

const gen = rough.generator();
Expand Down Expand Up @@ -180,8 +181,7 @@ export default function ToolbarLeft() {
commandManager.executeCommand(new ActionExportScene(DestOpts.CLIPBOARD, true)),
orderMatters: true,
},
"ControlLeft",
"c",
...SHORTCUTS["editor"]["copy"]["keys"],
);
useKeyboardShortcut(
{
Expand Down
101 changes: 101 additions & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,104 @@ const PREVIEW_IMAGE_WIDTH = 200;
const PREVIEW_IMAGE_HEIGHT = 200;

export { CELL_SIZE, CACHE_CANVAS_SIZE_THRESHOLD, PREVIEW_IMAGE_WIDTH, PREVIEW_IMAGE_HEIGHT };

export const keysToCodes = {
Enter: "Enter",
Escape: "Escape",
ArrowUp: "ArrowUp",
ArrowDown: "ArrowDown",
ArrowLeft: "ArrowLeft",
ArrowRight: "ArrowRight",
Tab: "Tab",
Home: "Home",
End: "End",
PageUp: "PageUp",
PageDown: "PageDown",
Backspace: "Backspace",
Delete: "Delete",
Space: "Space",
ShiftLeft: "ShiftLeft",
ShiftRight: "ShiftRight",
ControlLeft: "ControlLeft",
ControlRight: "ControlRight",
AltLeft: "AltLeft",
AltRight: "AltRight",
Meta: "Meta",
a: "KeyA",
b: "KeyB",
c: "KeyC",
d: "KeyD",
e: "KeyE",
f: "KeyF",
g: "KeyG",
h: "KeyH",
i: "KeyI",
j: "KeyJ",
k: "KeyK",
l: "KeyL",
m: "KeyM",
n: "KeyN",
o: "KeyO",
p: "KeyP",
q: "KeyQ",
r: "KeyR",
s: "KeyS",
t: "KeyT",
u: "KeyU",
v: "KeyV",
w: "KeyW",
x: "KeyX",
y: "KeyY",
z: "KeyZ",
"0": "Digit0",
"1": "Digit1",
"2": "Digit2",
"3": "Digit3",
"4": "Digit4",
"5": "Digit5",
"6": "Digit6",
"7": "Digit7",
"8": "Digit8",
"9": "Digit9",
"`": "Backquote",
NumpadSubtract: "NumpadSubtract",
"-": "Minus",
"=": "Equal",
"[": "BracketLeft",
"]": "BracketRight",
"\\": "Backslash",
"'": "Quote",
",": "Comma",
".": "Period",
"/": "Slash",
";": "Semicolon",
NumpadMultiply: "NumpadMultiply",
NumpadAdd: "NumpadAdd",
} as const;
export type Keys = keyof typeof keysToCodes;
export type KeyCodes = (typeof keysToCodes)[keyof typeof keysToCodes];

/**
* {
* group: {name: {description, keys}}
* }
*/
export const SHORTCUTS = {
editor: {
copy: { description: "copy selected", keys: ["ControlLeft", "c"] },
paste: { description: "paste into the canvas", keys: ["ControlLeft", "v"] },
undo: { description: "undo", keys: ["ControlLeft", "z"] },
delete: { description: "delete selected", keys: ["Delete"] },
},
tools: {
select: { description: "select", keys: ["1"] },
hand: { description: "hand(move in the canvas)", keys: ["2"] },
pen: { description: "create free hand draw", keys: ["3"] },
rect: { description: "create rect", keys: ["4"] },
line: { description: "create line", keys: ["5"] },
text: { description: "create text", keys: ["6"] },
erase: { description: "erase", keys: ["7"] },
},
} as const satisfies {
[g: string]: { [k: string]: { description: string; keys: readonly [Keys, ...Keys[]] } };
};
76 changes: 1 addition & 75 deletions src/hooks/useShortcut.ts
Original file line number Diff line number Diff line change
@@ -1,80 +1,6 @@
import { useEffect, useMemo, useRef } from "react";
import { keysToCodes, Keys, KeyCodes } from "@/constants/index";

const keysToCodes = {
Enter: "Enter",
Escape: "Escape",
ArrowUp: "ArrowUp",
ArrowDown: "ArrowDown",
ArrowLeft: "ArrowLeft",
ArrowRight: "ArrowRight",
Tab: "Tab",
Home: "Home",
End: "End",
PageUp: "PageUp",
PageDown: "PageDown",
Backspace: "Backspace",
Delete: "Delete",
Space: "Space",
ShiftLeft: "ShiftLeft",
ShiftRight: "ShiftRight",
ControlLeft: "ControlLeft",
ControlRight: "ControlRight",
AltLeft: "AltLeft",
AltRight: "AltRight",
Meta: "Meta",
a: "KeyA",
b: "KeyB",
c: "KeyC",
d: "KeyD",
e: "KeyE",
f: "KeyF",
g: "KeyG",
h: "KeyH",
i: "KeyI",
j: "KeyJ",
k: "KeyK",
l: "KeyL",
m: "KeyM",
n: "KeyN",
o: "KeyO",
p: "KeyP",
q: "KeyQ",
r: "KeyR",
s: "KeyS",
t: "KeyT",
u: "KeyU",
v: "KeyV",
w: "KeyW",
x: "KeyX",
y: "KeyY",
z: "KeyZ",
"0": "Digit0",
"1": "Digit1",
"2": "Digit2",
"3": "Digit3",
"4": "Digit4",
"5": "Digit5",
"6": "Digit6",
"7": "Digit7",
"8": "Digit8",
"9": "Digit9",
"`": "Backquote",
NumpadSubtract: "NumpadSubtract",
"-": "Minus",
"=": "Equal",
"[": "BracketLeft",
"]": "BracketRight",
"\\": "Backslash",
"'": "Quote",
",": "Comma",
".": "Period",
"/": "Slash",
";": "Semicolon",
NumpadMultiply: "NumpadMultiply",
NumpadAdd: "NumpadAdd",
} as const;
type Keys = keyof typeof keysToCodes;
type KeyCodes = (typeof keysToCodes)[keyof typeof keysToCodes];
export default function useKeyboardShortcut(
options: {
element?: HTMLElement | Window;
Expand Down

0 comments on commit d861d46

Please sign in to comment.