fix: refactor keyboard shortcut context

This commit is contained in:
Jan-Henrik Bruhn 2025-10-10 11:54:51 +02:00
parent 75cb26a991
commit 4e335a8fde
5 changed files with 41 additions and 161 deletions

View file

@ -1,8 +1,8 @@
import React from 'react';
import CloseIcon from '@mui/icons-material/Close';
import KeyboardIcon from '@mui/icons-material/Keyboard';
import { useKeyboardShortcuts } from '../../contexts/KeyboardShortcutContext';
import type { ShortcutCategory } from '../../hooks/useKeyboardShortcutManager';
import { useKeyboardShortcuts } from '../../hooks/useKeyboardShortcuts';
import type { ShortcutCategory, KeyboardShortcut } from '../../hooks/useKeyboardShortcutManager';
/**
* KeyboardShortcutsHelp Component
@ -55,7 +55,7 @@ const KeyboardShortcutsHelp: React.FC<KeyboardShortcutsHelpProps> = ({
{categories.map(category => {
const categoryShortcuts = shortcuts
.getShortcutsByCategory(category)
.filter(s => s.enabled !== false);
.filter((s: KeyboardShortcut) => s.enabled !== false);
if (categoryShortcuts.length === 0) return null;
@ -65,7 +65,7 @@ const KeyboardShortcutsHelp: React.FC<KeyboardShortcutsHelpProps> = ({
{category}
</h3>
<div className="space-y-2">
{categoryShortcuts.map(shortcut => (
{categoryShortcuts.map((shortcut: KeyboardShortcut) => (
<div
key={shortcut.id}
className="flex items-center justify-between py-2 hover:bg-gray-50 px-2 rounded"

View file

@ -1,26 +1,14 @@
import React, { createContext, useContext, ReactNode } from 'react';
import { useKeyboardShortcutManager } from '../hooks/useKeyboardShortcutManager';
/**
* Keyboard Shortcut Context
*
* Provides centralized keyboard shortcut management throughout the application.
* Components can register shortcuts and the system handles conflicts and priorities.
*/
interface KeyboardShortcutContextValue {
shortcuts: ReturnType<typeof useKeyboardShortcutManager>;
}
const KeyboardShortcutContext = createContext<KeyboardShortcutContextValue | null>(null);
import React, { ReactNode } from "react";
import { useKeyboardShortcutManager } from "../hooks/useKeyboardShortcutManager";
import { KeyboardShortcutContext } from "./keyboardShortcut";
interface KeyboardShortcutProviderProps {
children: ReactNode;
}
export const KeyboardShortcutProvider: React.FC<KeyboardShortcutProviderProps> = ({
children,
}) => {
export const KeyboardShortcutProvider: React.FC<
KeyboardShortcutProviderProps
> = ({ children }) => {
const shortcuts = useKeyboardShortcutManager();
return (
@ -29,26 +17,3 @@ export const KeyboardShortcutProvider: React.FC<KeyboardShortcutProviderProps> =
</KeyboardShortcutContext.Provider>
);
};
/**
* Hook to access keyboard shortcut manager
*
* Usage:
* ```tsx
* const { shortcuts } = useKeyboardShortcuts();
*
* useEffect(() => {
* shortcuts.register({...});
* return () => shortcuts.unregister('id');
* }, []);
* ```
*/
export function useKeyboardShortcuts() {
const context = useContext(KeyboardShortcutContext);
if (!context) {
throw new Error(
'useKeyboardShortcuts must be used within KeyboardShortcutProvider'
);
}
return context;
}

View file

@ -0,0 +1,9 @@
import { createContext } from "react";
import { useKeyboardShortcutManager } from "../hooks/useKeyboardShortcutManager";
export interface KeyboardShortcutContextValue {
shortcuts: ReturnType<typeof useKeyboardShortcutManager>;
}
export const KeyboardShortcutContext =
createContext<KeyboardShortcutContextValue | null>(null);

View file

@ -1,5 +1,5 @@
import { useEffect } from "react";
import { useKeyboardShortcuts } from "../contexts/KeyboardShortcutContext";
import { useKeyboardShortcuts } from "../hooks/useKeyboardShortcuts";
import { useWorkspaceStore } from "../stores/workspaceStore";
import type { KeyboardShortcut } from "./useKeyboardShortcutManager";

View file

@ -1,119 +1,25 @@
import { useEffect } from 'react';
import { useWorkspaceStore } from '../stores/workspaceStore';
import { useContext } from "react";
import { KeyboardShortcutContext } from "../contexts/keyboardShortcut";
/**
* useKeyboardShortcuts Hook
* Hook to access keyboard shortcut manager
*
* Global keyboard shortcuts for the application:
* - Ctrl/Cmd + Tab: Next document
* - Ctrl/Cmd + Shift + Tab: Previous document
* - Ctrl/Cmd + W: Close current document
* - Ctrl/Cmd + N: New document
* - Ctrl/Cmd + S: Save current document (trigger save immediately)
* - Ctrl/Cmd + O: Open document manager
* - Ctrl/Cmd + Z: Undo (per-document)
* - Ctrl/Cmd + Y / Ctrl/Cmd + Shift + Z: Redo (per-document)
* Usage:
* ```tsx
* const { shortcuts } = useKeyboardShortcuts();
*
* useEffect(() => {
* shortcuts.register({...});
* return () => shortcuts.unregister('id');
* }, []);
* ```
*/
interface UseKeyboardShortcutsOptions {
onOpenDocumentManager?: () => void;
onUndo?: () => void;
onRedo?: () => void;
export function useKeyboardShortcuts() {
const context = useContext(KeyboardShortcutContext);
if (!context) {
throw new Error(
"useKeyboardShortcuts must be used within KeyboardShortcutProvider",
);
}
export function useKeyboardShortcuts(options?: UseKeyboardShortcutsOptions) {
const {
documentOrder,
activeDocumentId,
switchToDocument,
closeDocument,
createDocument,
saveDocument,
} = useWorkspaceStore();
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
const modifier = isMac ? e.metaKey : e.ctrlKey;
// Ctrl/Cmd + Tab - Next document
if (modifier && e.key === 'Tab' && !e.shiftKey) {
e.preventDefault();
const currentIndex = documentOrder.findIndex(id => id === activeDocumentId);
if (currentIndex !== -1) {
const nextIndex = (currentIndex + 1) % documentOrder.length;
switchToDocument(documentOrder[nextIndex]);
}
return;
}
// Ctrl/Cmd + Shift + Tab - Previous document
if (modifier && e.key === 'Tab' && e.shiftKey) {
e.preventDefault();
const currentIndex = documentOrder.findIndex(id => id === activeDocumentId);
if (currentIndex !== -1) {
const prevIndex = (currentIndex - 1 + documentOrder.length) % documentOrder.length;
switchToDocument(documentOrder[prevIndex]);
}
return;
}
// Ctrl/Cmd + W - Close current document
if (modifier && e.key === 'w') {
e.preventDefault();
if (activeDocumentId && documentOrder.length > 1) {
closeDocument(activeDocumentId);
}
return;
}
// Ctrl/Cmd + N - New document
if (modifier && e.key === 'n') {
e.preventDefault();
createDocument();
return;
}
// Ctrl/Cmd + S - Save current document
if (modifier && e.key === 's') {
e.preventDefault();
if (activeDocumentId) {
saveDocument(activeDocumentId);
}
return;
}
// Ctrl/Cmd + O - Open document manager
if (modifier && e.key === 'o') {
e.preventDefault();
options?.onOpenDocumentManager?.();
return;
}
// Ctrl/Cmd + Z - Undo
if (modifier && e.key === 'z' && !e.shiftKey) {
e.preventDefault();
options?.onUndo?.();
return;
}
// Ctrl/Cmd + Y or Ctrl/Cmd + Shift + Z - Redo
if (modifier && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
e.preventDefault();
options?.onRedo?.();
return;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [
documentOrder,
activeDocumentId,
switchToDocument,
closeDocument,
createDocument,
saveDocument,
options,
]);
return context;
}