feat(timeline): add keyboard navigation with shortcut overlay

- Arrow left/right scrolls the timeline by 1 day (Shift: 1 week)
- Delete/Backspace deletes selected allocations
- ? toggles a keyboard shortcut overlay
- Floating ? button in bottom-right corner provides persistent access
- (Del) hint added to the FloatingActionBar delete button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 13:20:32 +02:00
parent 05f6eba5d8
commit fa54ef4cbd
4 changed files with 184 additions and 2 deletions
@@ -0,0 +1,50 @@
"use client";
const SHORTCUTS: { keys: string; description: string }[] = [
{ keys: "← / →", description: "Scroll timeline 1 day" },
{ keys: "Shift + ← / →", description: "Scroll timeline 1 week" },
{ keys: "Delete / Backspace", description: "Delete selected allocations" },
{ keys: "Ctrl / Cmd + Z", description: "Undo last action" },
{ keys: "Ctrl / Cmd + Shift + Z", description: "Redo" },
{ keys: "Shift + Wheel", description: "Horizontal scroll" },
{ keys: "ESC", description: "Close popover / clear selection" },
{ keys: "?", description: "Toggle this overlay" },
];
interface KeyboardShortcutOverlayProps {
onClose: () => void;
}
export function KeyboardShortcutOverlay({ onClose }: KeyboardShortcutOverlayProps) {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40" onClick={onClose}>
<div
className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl border border-gray-200 dark:border-gray-700 w-full max-w-sm mx-4 overflow-hidden"
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between px-5 py-4 border-b border-gray-100 dark:border-gray-700">
<h2 className="text-sm font-semibold text-gray-900 dark:text-gray-100">Keyboard Shortcuts</h2>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 text-lg leading-none"
>
&times;
</button>
</div>
<ul className="divide-y divide-gray-50 dark:divide-gray-700/50">
{SHORTCUTS.map((s) => (
<li key={s.keys} className="flex items-center justify-between gap-4 px-5 py-2.5">
<span className="text-xs text-gray-500 dark:text-gray-400">{s.description}</span>
<kbd className="shrink-0 rounded-md border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 px-2 py-0.5 font-mono text-[11px] text-gray-700 dark:text-gray-300">
{s.keys}
</kbd>
</li>
))}
</ul>
<div className="px-5 py-3 border-t border-gray-100 dark:border-gray-700 text-[11px] text-gray-400 dark:text-gray-500 text-center">
Press <kbd className="font-mono">?</kbd> to toggle
</div>
</div>
</div>
);
}