first
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { FolderPlus, Upload, FolderUp, Scissors, Copy, ClipboardPaste, Trash2, X, Check } from 'lucide-react';
|
||||
|
||||
type ToolbarProps = {
|
||||
onCreateDir: (name: string) => void;
|
||||
onUpload: (files: FileList) => void;
|
||||
selectionCount: number;
|
||||
hasClipboard: boolean;
|
||||
onCut: () => void;
|
||||
onCopy: () => void;
|
||||
onPaste: () => void;
|
||||
onDeleteSelected: () => void;
|
||||
onClearSelection: () => void;
|
||||
};
|
||||
|
||||
export const Toolbar = ({
|
||||
onCreateDir,
|
||||
onUpload,
|
||||
selectionCount,
|
||||
hasClipboard,
|
||||
onCut,
|
||||
onCopy,
|
||||
onPaste,
|
||||
onDeleteSelected,
|
||||
onClearSelection,
|
||||
}: ToolbarProps) => {
|
||||
const [showInput, setShowInput] = useState(false);
|
||||
const [folderName, setFolderName] = useState('');
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const folderInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const input = folderInputRef.current;
|
||||
if (!input) return;
|
||||
input.setAttribute('webkitdirectory', '');
|
||||
}, []);
|
||||
|
||||
const handleCreate = () => {
|
||||
const name = folderName.trim();
|
||||
if (!name) return;
|
||||
onCreateDir(name);
|
||||
setFolderName('');
|
||||
setShowInput(false);
|
||||
};
|
||||
|
||||
if (selectionCount > 0) {
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-sm font-medium text-duck-dark/70 mr-1">
|
||||
{selectionCount}
|
||||
<span className="hidden md:inline"> selected</span>
|
||||
</span>
|
||||
<button
|
||||
onClick={onCut}
|
||||
title="Cut"
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<Scissors className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={onCopy}
|
||||
title="Copy"
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</button>
|
||||
{hasClipboard && (
|
||||
<button
|
||||
onClick={onPaste}
|
||||
title="Paste"
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<ClipboardPaste className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={onDeleteSelected}
|
||||
title="Delete"
|
||||
className="p-1.5 rounded-md text-red-500 hover:bg-red-50 cursor-pointer transition-colors"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={onClearSelection}
|
||||
title="Clear selection"
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
{showInput ? (
|
||||
<form
|
||||
onSubmit={(ev) => {
|
||||
ev.preventDefault();
|
||||
handleCreate();
|
||||
}}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<input
|
||||
autoFocus
|
||||
value={folderName}
|
||||
onChange={(ev) => setFolderName(ev.target.value)}
|
||||
placeholder="Folder name"
|
||||
className="h-8 w-40 text-sm rounded-md border border-duck-dark/20 bg-white/60 text-duck-dark placeholder:text-duck-dark/40 outline-none focus:border-duck-teal/50 px-2"
|
||||
onKeyDown={(ev) => {
|
||||
if (ev.key === 'Escape') {
|
||||
setShowInput(false);
|
||||
setFolderName('');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="p-1.5 rounded-md text-duck-teal hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
title="Create"
|
||||
>
|
||||
<Check className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShowInput(false);
|
||||
setFolderName('');
|
||||
}}
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
title="Cancel"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</form>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setShowInput(true)}
|
||||
title="New folder"
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<FolderPlus className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
title="Upload files"
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<Upload className="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => folderInputRef.current?.click()}
|
||||
title="Upload folder"
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<FolderUp className="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
{hasClipboard && (
|
||||
<button
|
||||
onClick={onPaste}
|
||||
title="Paste"
|
||||
className="p-1.5 rounded-md text-duck-dark/50 hover:bg-duck-dark/5 cursor-pointer transition-colors"
|
||||
>
|
||||
<ClipboardPaste className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
multiple
|
||||
className="hidden"
|
||||
onChange={(ev) => {
|
||||
if (ev.target.files?.length) {
|
||||
onUpload(ev.target.files);
|
||||
ev.target.value = '';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
ref={folderInputRef}
|
||||
type="file"
|
||||
className="hidden"
|
||||
onChange={(ev) => {
|
||||
if (ev.target.files?.length) {
|
||||
onUpload(ev.target.files);
|
||||
ev.target.value = '';
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user