KeyboardShortcutsModal.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import { useEffect } from 'react';
  2. import { X, Keyboard } from 'lucide-react';
  3. import { useTranslation } from 'react-i18next';
  4. import { Card, CardContent } from './Card';
  5. interface NavItem {
  6. id: string;
  7. to: string;
  8. labelKey: string;
  9. }
  10. interface KeyboardShortcutsModalProps {
  11. onClose: () => void;
  12. navItems?: NavItem[];
  13. }
  14. function getShortcuts(navItems: NavItem[] | undefined, t: (key: string) => string) {
  15. const navShortcuts = navItems
  16. ? navItems.map((item, index) => ({
  17. keys: [String(index + 1)],
  18. description: `Go to ${t(item.labelKey)}`,
  19. }))
  20. : [
  21. { keys: ['1'], description: 'Go to Printers' },
  22. { keys: ['2'], description: 'Go to Archives' },
  23. { keys: ['3'], description: 'Go to Queue' },
  24. { keys: ['4'], description: 'Go to Statistics' },
  25. { keys: ['5'], description: 'Go to Cloud Profiles' },
  26. { keys: ['6'], description: 'Go to Settings' },
  27. ];
  28. return [
  29. { category: 'Navigation', items: navShortcuts },
  30. { category: 'Archives', items: [
  31. { keys: ['/'], description: 'Focus search' },
  32. { keys: ['U'], description: 'Open upload modal' },
  33. { keys: ['Esc'], description: 'Clear selection / blur input' },
  34. { keys: ['Right-click'], description: 'Context menu on cards' },
  35. ]},
  36. { category: 'K-Profiles', items: [
  37. { keys: ['R'], description: 'Refresh profiles' },
  38. { keys: ['N'], description: 'New profile' },
  39. { keys: ['Esc'], description: 'Exit selection mode' },
  40. ]},
  41. { category: 'General', items: [
  42. { keys: ['?'], description: 'Show this help' },
  43. ]},
  44. ];
  45. }
  46. function KeyBadge({ children }: { children: string }) {
  47. return (
  48. <kbd className="px-2 py-1 text-xs font-mono bg-bambu-dark border border-bambu-dark-tertiary rounded text-white">
  49. {children}
  50. </kbd>
  51. );
  52. }
  53. export function KeyboardShortcutsModal({ onClose, navItems }: KeyboardShortcutsModalProps) {
  54. const { t } = useTranslation();
  55. const shortcuts = getShortcuts(navItems, t);
  56. // Close on Escape key
  57. useEffect(() => {
  58. const handleKeyDown = (e: KeyboardEvent) => {
  59. if (e.key === 'Escape') onClose();
  60. };
  61. window.addEventListener('keydown', handleKeyDown);
  62. return () => window.removeEventListener('keydown', handleKeyDown);
  63. }, [onClose]);
  64. return (
  65. <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4" onClick={onClose}>
  66. <Card className="w-full max-w-md" onClick={(e) => e.stopPropagation()}>
  67. <CardContent className="p-0">
  68. {/* Header */}
  69. <div className="flex items-center justify-between p-4 border-b border-bambu-dark-tertiary">
  70. <div className="flex items-center gap-2">
  71. <Keyboard className="w-5 h-5 text-bambu-green" />
  72. <h2 className="text-xl font-semibold text-white">Keyboard Shortcuts</h2>
  73. </div>
  74. <button
  75. onClick={onClose}
  76. className="text-bambu-gray hover:text-white transition-colors"
  77. >
  78. <X className="w-5 h-5" />
  79. </button>
  80. </div>
  81. {/* Shortcuts List */}
  82. <div className="p-4 space-y-6 max-h-[60vh] overflow-y-auto">
  83. {shortcuts.map((section) => (
  84. <div key={section.category}>
  85. <h3 className="text-sm font-medium text-bambu-gray mb-3">{section.category}</h3>
  86. <div className="space-y-2">
  87. {section.items.map((shortcut) => (
  88. <div key={shortcut.description} className="flex items-center justify-between">
  89. <span className="text-white text-sm">{shortcut.description}</span>
  90. <div className="flex gap-1">
  91. {shortcut.keys.map((key) => (
  92. <KeyBadge key={key}>{key}</KeyBadge>
  93. ))}
  94. </div>
  95. </div>
  96. ))}
  97. </div>
  98. </div>
  99. ))}
  100. </div>
  101. {/* Footer */}
  102. <div className="p-4 border-t border-bambu-dark-tertiary">
  103. <p className="text-xs text-bambu-gray text-center">
  104. Press <KeyBadge>Esc</KeyBadge> or click outside to close
  105. </p>
  106. </div>
  107. </CardContent>
  108. </Card>
  109. </div>
  110. );
  111. }