- Refactoring: Error Boundaries, apiFetch Wrapper, Socket Status-Tracking - Refactoring: UI Kontrast (theme-aware colors), unused imports bereinigt - Symbol-Verwaltung: Neues Split-Panel (Meine Symbole + Bibliothek) - Symbol-Verwaltung: Umbenennen (TLF rot/blau), Duplikate erlaubt - Symbol-Verwaltung: Karten-Sidebar zeigt eigene Symbole bevorzugt - Schlauch-Labels: Groessere Schrift (13px/10px), verschiebbar (Drag) - Schema: TenantSymbol customName, sortOrder, unique constraint entfernt - Open Source Referenz entfernt (kostenloses Projekt)
75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
import { useEffect, useCallback } from 'react'
|
|
import type { DrawMode, DrawFeature } from '@/types'
|
|
|
|
interface UseKeyboardShortcutsOptions {
|
|
featuresRef: React.MutableRefObject<DrawFeature[]>
|
|
onUndo: () => void
|
|
onRedo: () => void
|
|
onSave: () => void
|
|
onDelete: (newFeatures: DrawFeature[]) => void
|
|
onToolChange: (mode: DrawMode) => void
|
|
onHelpOpen: () => void
|
|
}
|
|
|
|
const TOOL_SHORTCUTS: Record<string, DrawMode> = {
|
|
'v': 'select', 's': 'select',
|
|
'p': 'point',
|
|
'l': 'linestring',
|
|
'g': 'polygon',
|
|
'r': 'rectangle',
|
|
'c': 'circle',
|
|
'f': 'freehand',
|
|
'a': 'arrow',
|
|
't': 'text',
|
|
'e': 'eraser',
|
|
'm': 'measure',
|
|
'd': 'dangerzone',
|
|
}
|
|
|
|
export function useKeyboardShortcuts({
|
|
featuresRef,
|
|
onUndo,
|
|
onRedo,
|
|
onSave,
|
|
onDelete,
|
|
onToolChange,
|
|
onHelpOpen,
|
|
}: UseKeyboardShortcutsOptions) {
|
|
useEffect(() => {
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
// Ignore when typing in inputs/textareas
|
|
const tag = (e.target as HTMLElement)?.tagName
|
|
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || (e.target as HTMLElement)?.isContentEditable) return
|
|
|
|
// ? or F1 → help
|
|
if (e.key === '?' || e.key === 'F1') { e.preventDefault(); onHelpOpen(); return }
|
|
|
|
// DEL / Backspace → delete selected feature(s)
|
|
if (e.key === 'Delete' || e.key === 'Backspace') {
|
|
e.preventDefault()
|
|
const current = featuresRef.current
|
|
const selected = current.filter(f => f.properties?._selected)
|
|
if (selected.length > 0) {
|
|
onDelete(current.filter(f => !f.properties?._selected))
|
|
}
|
|
return
|
|
}
|
|
|
|
// Ctrl/Cmd shortcuts
|
|
if (e.ctrlKey || e.metaKey) {
|
|
if (e.key === 'z' && e.shiftKey) { e.preventDefault(); onRedo(); return }
|
|
if (e.key === 'z') { e.preventDefault(); onUndo(); return }
|
|
if (e.key === 'y') { e.preventDefault(); onRedo(); return }
|
|
if (e.key === 's') { e.preventDefault(); onSave(); return }
|
|
return
|
|
}
|
|
|
|
// Tool shortcuts (single key, no modifier)
|
|
const mode = TOOL_SHORTCUTS[e.key.toLowerCase()]
|
|
if (mode) { e.preventDefault(); onToolChange(mode); return }
|
|
}
|
|
window.addEventListener('keydown', handleKeyDown)
|
|
return () => window.removeEventListener('keydown', handleKeyDown)
|
|
}, [featuresRef, onUndo, onRedo, onSave, onDelete, onToolChange, onHelpOpen])
|
|
}
|