diff --git a/components/form/task.tsx b/components/form/task.tsx index 6096bb8..4768345 100644 --- a/components/form/task.tsx +++ b/components/form/task.tsx @@ -2,10 +2,11 @@ import * as Dialog from "@radix-ui/react-dialog"; import { X } from "lucide-react"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useRef, useState } from "react"; import { Input } from "@/components/ui/input"; import { Kbd } from "@/components/ui/kbd"; import { cn } from "@/lib/utils"; +import { useKeyboardShortcut } from "@/hooks/use-keyboard-shortcut"; import { Button } from "../ui/button"; export default function InlineTaskForm({ @@ -17,29 +18,8 @@ export default function InlineTaskForm({ const [value, setValue] = useState(""); const inputRef = useRef(null); - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if ((e.key === "n" || e.key === "N") && !e.metaKey && !e.ctrlKey && !e.altKey && !e.repeat) { - const target = e.target as HTMLElement; - - // Check if we're in an input field, textarea, or any contentEditable element - if ( - target.tagName === "INPUT" || - target.tagName === "TEXTAREA" || - target.isContentEditable || - target.closest('[contenteditable="true"]') - ) { - return; - } - - e.preventDefault(); - setIsCreating(true); - } - }; - - document.addEventListener("keydown", handleKeyDown); - return () => document.removeEventListener("keydown", handleKeyDown); - }, []); + const openCreate = useCallback(() => setIsCreating(true), []); + useKeyboardShortcut("n", () => openCreate, !isCreating); const handleSubmit = useCallback(async () => { await action(value); @@ -58,6 +38,7 @@ export default function InlineTaskForm({ setIsCreating(true); }} className="flex items-center gap-2" + aria-keyshortcuts="N" > Add task N diff --git a/components/project/events/full-calendar.tsx b/components/project/events/full-calendar.tsx index 64fdfcb..6d1d698 100644 --- a/components/project/events/full-calendar.tsx +++ b/components/project/events/full-calendar.tsx @@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; +import { Kbd } from "@/components/ui/kbd"; import type { CalendarEvent } from "@/drizzle/types"; import { useIsMobile } from "@/hooks/use-mobile"; import { cn } from "@/lib/utils"; @@ -24,14 +25,11 @@ import { startOfToday, startOfWeek, } from "date-fns"; -import { - ChevronLeftIcon, - ChevronRightIcon, - PlusCircleIcon, -} from "lucide-react"; +import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; import { parseAsBoolean, useQueryState } from "nuqs"; import { useCallback, useMemo, useState } from "react"; import { rrulestr } from "rrule"; +import { useKeyboardShortcut } from "@/hooks/use-keyboard-shortcut"; interface Event { id: number; @@ -76,6 +74,9 @@ export function FullCalendar({ parseAsBoolean.withDefault(false), ); + const openCreate = useCallback(() => setCreate(true), [setCreate]); + useKeyboardShortcut("n", () => openCreate); + const [selectedDay, setSelectedDay] = useState(today); const [currentMonth, setCurrentMonth] = useState(format(today, "MMM-yyyy")); const firstDayCurrentMonth = useMemo( @@ -234,9 +235,10 @@ export function FullCalendar({ diff --git a/hooks/use-keyboard-shortcut.ts b/hooks/use-keyboard-shortcut.ts new file mode 100644 index 0000000..625df42 --- /dev/null +++ b/hooks/use-keyboard-shortcut.ts @@ -0,0 +1,39 @@ +import { useEffect } from "react"; + +export function useKeyboardShortcut( + key: string, + callback: () => void, + enabled = true, +) { + useEffect(() => { + if (!enabled) return; + + const handleKeyDown = (e: KeyboardEvent) => { + if ( + (e.key === key || e.key === key.toUpperCase()) && + !e.metaKey && + !e.ctrlKey && + !e.altKey && + !e.repeat + ) { + const target = e.target as HTMLElement; + + if ( + target.tagName === "INPUT" || + target.tagName === "TEXTAREA" || + target.isContentEditable || + target.closest('[contenteditable="true"]') + ) { + return; + } + + e.preventDefault(); + callback(); + } + }; + + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, [key, callback, enabled]); +} +