feat: remove keyboard shortcuts from media app
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ArrowRight, ArrowDown, Keyboard } from 'lucide-react';
|
import { ArrowRight, ArrowDown } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { FileUpload } from './FileUpload';
|
import { FileUpload } from './FileUpload';
|
||||||
@@ -22,8 +22,6 @@ import { convertWithImageMagick } from '@/lib/media/converters/imagemagickServic
|
|||||||
import { addToHistory } from '@/lib/media/storage/history';
|
import { addToHistory } from '@/lib/media/storage/history';
|
||||||
import { downloadBlobsAsZip, generateOutputFilename } from '@/lib/media/utils/fileUtils';
|
import { downloadBlobsAsZip, generateOutputFilename } from '@/lib/media/utils/fileUtils';
|
||||||
import { getPresetById, type FormatPreset } from '@/lib/media/utils/formatPresets';
|
import { getPresetById, type FormatPreset } from '@/lib/media/utils/formatPresets';
|
||||||
import { useKeyboardShortcuts, type KeyboardShortcut } from '@/lib/media/hooks/useKeyboardShortcuts';
|
|
||||||
import { KeyboardShortcutsModal } from '@/components/ui/KeyboardShortcutsModal';
|
|
||||||
import type { ConversionJob, ConversionFormat, ConversionOptions } from '@/types/media';
|
import type { ConversionJob, ConversionFormat, ConversionOptions } from '@/types/media';
|
||||||
|
|
||||||
export function FileConverter() {
|
export function FileConverter() {
|
||||||
@@ -33,7 +31,6 @@ export function FileConverter() {
|
|||||||
const [compatibleFormats, setCompatibleFormats] = React.useState<ConversionFormat[]>([]);
|
const [compatibleFormats, setCompatibleFormats] = React.useState<ConversionFormat[]>([]);
|
||||||
const [conversionJobs, setConversionJobs] = React.useState<ConversionJob[]>([]);
|
const [conversionJobs, setConversionJobs] = React.useState<ConversionJob[]>([]);
|
||||||
const [conversionOptions, setConversionOptions] = React.useState<ConversionOptions>({});
|
const [conversionOptions, setConversionOptions] = React.useState<ConversionOptions>({});
|
||||||
const [showShortcutsModal, setShowShortcutsModal] = React.useState(false);
|
|
||||||
|
|
||||||
const fileInputRef = React.useRef<HTMLInputElement>(null);
|
const fileInputRef = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@@ -377,69 +374,6 @@ export function FileConverter() {
|
|||||||
const isConvertDisabled = selectedFiles.length === 0 || !outputFormat || isConverting;
|
const isConvertDisabled = selectedFiles.length === 0 || !outputFormat || isConverting;
|
||||||
const completedCount = conversionJobs.filter(job => job.status === 'completed').length;
|
const completedCount = conversionJobs.filter(job => job.status === 'completed').length;
|
||||||
|
|
||||||
// Define keyboard shortcuts
|
|
||||||
const shortcuts: KeyboardShortcut[] = [
|
|
||||||
{
|
|
||||||
key: 'o',
|
|
||||||
ctrl: true,
|
|
||||||
description: 'Open file dialog',
|
|
||||||
action: () => {
|
|
||||||
if (!isConverting) {
|
|
||||||
fileInputRef.current?.click();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'Enter',
|
|
||||||
ctrl: true,
|
|
||||||
description: 'Start conversion',
|
|
||||||
action: () => {
|
|
||||||
if (!isConvertDisabled) {
|
|
||||||
handleConvert();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 's',
|
|
||||||
ctrl: true,
|
|
||||||
description: 'Download results',
|
|
||||||
action: () => {
|
|
||||||
if (completedCount > 0) {
|
|
||||||
handleDownloadAll();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'r',
|
|
||||||
ctrl: true,
|
|
||||||
description: 'Reset converter',
|
|
||||||
action: () => {
|
|
||||||
if (!isConverting) {
|
|
||||||
handleReset();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '/',
|
|
||||||
ctrl: true,
|
|
||||||
description: 'Show keyboard shortcuts',
|
|
||||||
action: () => setShowShortcutsModal(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'Escape',
|
|
||||||
description: 'Close shortcuts modal',
|
|
||||||
action: () => setShowShortcutsModal(false),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '?',
|
|
||||||
description: 'Show keyboard shortcuts',
|
|
||||||
action: () => setShowShortcutsModal(true),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Enable keyboard shortcuts
|
|
||||||
useKeyboardShortcuts(shortcuts, !showShortcutsModal);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full max-w-4xl mx-auto space-y-6">
|
<div className="w-full max-w-4xl mx-auto space-y-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
@@ -566,22 +500,6 @@ export function FileConverter() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Keyboard Shortcuts Button */}
|
|
||||||
<Button
|
|
||||||
onClick={() => setShowShortcutsModal(true)}
|
|
||||||
className="fixed bottom-6 right-6 rounded-full w-12 h-12 p-0 shadow-lg"
|
|
||||||
title="Keyboard Shortcuts (Ctrl+/)"
|
|
||||||
>
|
|
||||||
<Keyboard className="h-5 w-5" />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{/* Keyboard Shortcuts Modal */}
|
|
||||||
<KeyboardShortcutsModal
|
|
||||||
shortcuts={shortcuts}
|
|
||||||
isOpen={showShortcutsModal}
|
|
||||||
onClose={() => setShowShortcutsModal(false)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import { X } from 'lucide-react';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
|
|
||||||
import { formatShortcut, type KeyboardShortcut } from '@/lib/media/hooks/useKeyboardShortcuts';
|
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
|
|
||||||
interface KeyboardShortcutsModalProps {
|
|
||||||
shortcuts: KeyboardShortcut[];
|
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function KeyboardShortcutsModal({ shortcuts, isOpen, onClose }: KeyboardShortcutsModalProps) {
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (isOpen) {
|
|
||||||
document.body.style.overflow = 'hidden';
|
|
||||||
} else {
|
|
||||||
document.body.style.overflow = '';
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
document.body.style.overflow = '';
|
|
||||||
};
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm animate-in fade-in duration-200">
|
|
||||||
<div
|
|
||||||
className="absolute inset-0"
|
|
||||||
onClick={onClose}
|
|
||||||
/>
|
|
||||||
<Card className="relative w-full max-w-md shadow-2xl animate-in zoom-in-95 duration-200">
|
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
||||||
<CardTitle className="text-xl font-bold">Keyboard Shortcuts</CardTitle>
|
|
||||||
<Button variant="ghost" size="icon" onClick={onClose} className="rounded-full">
|
|
||||||
<X className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="pt-4">
|
|
||||||
<div className="space-y-4">
|
|
||||||
{shortcuts.map((shortcut, index) => (
|
|
||||||
<div key={index} className="flex items-center justify-between gap-4">
|
|
||||||
<span className="text-sm text-muted-foreground">{shortcut.description}</span>
|
|
||||||
<kbd className="pointer-events-none inline-flex h-6 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
|
|
||||||
{formatShortcut(shortcut)}
|
|
||||||
</kbd>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mt-6 flex justify-end">
|
|
||||||
<Button onClick={onClose}>Close</Button>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
export interface KeyboardShortcut {
|
|
||||||
key: string;
|
|
||||||
ctrl?: boolean;
|
|
||||||
alt?: boolean;
|
|
||||||
shift?: boolean;
|
|
||||||
description: string;
|
|
||||||
action: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook for managing keyboard shortcuts
|
|
||||||
*/
|
|
||||||
export function useKeyboardShortcuts(shortcuts: KeyboardShortcut[], enabled: boolean = true) {
|
|
||||||
useEffect(() => {
|
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
|
||||||
// Find matching shortcut
|
|
||||||
const shortcut = shortcuts.find((s) => {
|
|
||||||
const keyMatch = s.key.toLowerCase() === event.key.toLowerCase();
|
|
||||||
const ctrlMatch = s.ctrl ? (event.ctrlKey || event.metaKey) : !event.ctrlKey && !event.metaKey;
|
|
||||||
const altMatch = s.alt ? event.altKey : !event.altKey;
|
|
||||||
const shiftMatch = s.shift ? event.shiftKey : !event.shiftKey;
|
|
||||||
|
|
||||||
return keyMatch && ctrlMatch && altMatch && shiftMatch;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (shortcut) {
|
|
||||||
event.preventDefault();
|
|
||||||
shortcut.action();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
|
||||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
||||||
}, [shortcuts, enabled]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format shortcut key combination for display
|
|
||||||
*/
|
|
||||||
export function formatShortcut(shortcut: KeyboardShortcut): string {
|
|
||||||
const parts: string[] = [];
|
|
||||||
|
|
||||||
// Use Cmd on Mac, Ctrl on others
|
|
||||||
const isMac = typeof window !== 'undefined' && /Mac|iPhone|iPod|iPad/.test(navigator.platform);
|
|
||||||
|
|
||||||
if (shortcut.ctrl) {
|
|
||||||
parts.push(isMac ? '⌘' : 'Ctrl');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shortcut.alt) {
|
|
||||||
parts.push(isMac ? '⌥' : 'Alt');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shortcut.shift) {
|
|
||||||
parts.push(isMac ? '⇧' : 'Shift');
|
|
||||||
}
|
|
||||||
|
|
||||||
parts.push(shortcut.key.toUpperCase());
|
|
||||||
|
|
||||||
return parts.join(' + ');
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user