omnidev / components /file-explorer /create-folder-dialog.tsx
kalhdrawi's picture
ุฃูˆู„ ุฑูุน ู„ู„ู…ู„ูุงุช ุฅู„ู‰ ุงู„ุณุจูŠุณ kalhdrawi/omnidev
1cf8f01
raw
history blame
10.1 kB
"use client";
import React, { useState, useEffect } from 'react';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
// Select,
// SelectContent,
// SelectItem,
// SelectTrigger,
// SelectValue,
} from '@/components/ui/select';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Badge } from '@/components/ui/badge';
import {
// Folder,
FolderPlus,
Palette,
Tag,
// Settings
} from 'lucide-react';
export interface CreateFolderDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onFolderCreate: (folderData: {
name: string;
parentFolderId?: string;
metadata?: {
description?: string;
color?: string;
icon?: string;
tags?: string[];
};
}) => void;
parentFolderId?: string | null;
}
const FOLDER_COLORS = [
{ name: 'Blue', value: '#3B82F6', class: 'bg-blue-500' },
{ name: 'Green', value: '#10B981', class: 'bg-green-500' },
{ name: 'Yellow', value: '#F59E0B', class: 'bg-yellow-500' },
{ name: 'Red', value: '#EF4444', class: 'bg-red-500' },
{ name: 'Purple', value: '#8B5CF6', class: 'bg-purple-500' },
{ name: 'Pink', value: '#EC4899', class: 'bg-pink-500' },
{ name: 'Indigo', value: '#6366F1', class: 'bg-indigo-500' },
{ name: 'Gray', value: '#6B7280', class: 'bg-gray-500' },
];
const FOLDER_ICONS = [
'๐Ÿ“', '๐Ÿ“‚', '๐Ÿ—‚๏ธ', '๐Ÿ“‹', '๐Ÿ“Š', '๐Ÿ“ˆ', '๐Ÿ“‰', '๐Ÿ“ฆ',
'๐ŸŽจ', '๐Ÿ–ผ๏ธ', '๐ŸŽต', '๐ŸŽฌ', '๐Ÿ“ท', '๐ŸŽฎ', 'โš™๏ธ', '๐Ÿ”ง',
'๐Ÿ’ผ', '๐Ÿ ', '๐ŸŒŸ', 'โค๏ธ', '๐Ÿ”ฅ', 'โšก', '๐Ÿš€', '๐Ÿ’ก'
];
const COMMON_FOLDER_NAMES = [
'components', 'pages', 'styles', 'assets', 'images', 'scripts',
'utils', 'helpers', 'hooks', 'services', 'api', 'data',
'tests', 'docs', 'config', 'public', 'src', 'lib'
];
export function CreateFolderDialog({
open,
onOpenChange,
onFolderCreate,
parentFolderId
}: CreateFolderDialogProps) {
const [folderName, setFolderName] = useState('');
const [description, setDescription] = useState('');
const [selectedColor, setSelectedColor] = useState(FOLDER_COLORS[0].value);
const [selectedIcon, setSelectedIcon] = useState('๐Ÿ“');
const [tags, setTags] = useState<string[]>([]);
const [tagInput, setTagInput] = useState('');
const [isCreating, setIsCreating] = useState(false);
// Reset form when dialog opens
useEffect(() => {
if (open) {
setFolderName('');
setDescription('');
setSelectedColor(FOLDER_COLORS[0].value);
setSelectedIcon('๐Ÿ“');
setTags([]);
setTagInput('');
}
}, [open]);
const handleCreate = async () => {
if (!folderName.trim()) {
return;
}
setIsCreating(true);
try {
onFolderCreate({
name: folderName.trim(),
parentFolderId: parentFolderId || undefined,
metadata: {
description: description.trim() || undefined,
color: selectedColor,
icon: selectedIcon,
tags: tags.length > 0 ? tags : undefined
}
});
onOpenChange(false);
} catch (error) {
console.error('Error creating folder:', error);
} finally {
setIsCreating(false);
}
};
const handleAddTag = () => {
const tag = tagInput.trim().toLowerCase();
if (tag && !tags.includes(tag)) {
setTags([...tags, tag]);
setTagInput('');
}
};
const handleRemoveTag = (tagToRemove: string) => {
setTags(tags.filter(tag => tag !== tagToRemove));
};
const handleTagInputKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
handleAddTag();
}
};
const handleQuickName = (name: string) => {
setFolderName(name);
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<FolderPlus className="h-5 w-5" />
Create New Folder
</DialogTitle>
<DialogDescription>
Create a new folder to organize your files
{parentFolderId && " in the selected folder"}.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
{/* Folder Name */}
<div className="space-y-2">
<Label htmlFor="folderName">Folder Name</Label>
<Input
id="folderName"
placeholder="Enter folder name..."
value={folderName}
onChange={(e) => setFolderName(e.target.value)}
/>
{/* Quick Name Suggestions */}
<div className="flex flex-wrap gap-1">
{COMMON_FOLDER_NAMES.slice(0, 8).map(name => (
<Badge
key={name}
variant="outline"
className="cursor-pointer text-xs hover:bg-neutral-700"
onClick={() => handleQuickName(name)}
>
{name}
</Badge>
))}
</div>
</div>
{/* Description */}
<div className="space-y-2">
<Label htmlFor="description">Description (Optional)</Label>
<Textarea
id="description"
placeholder="Describe what this folder contains..."
value={description}
onChange={(e) => setDescription(e.target.value)}
className="min-h-[60px] resize-none"
/>
</div>
{/* Appearance */}
<div className="space-y-3">
<Label className="flex items-center gap-2">
<Palette className="h-4 w-4" />
Appearance
</Label>
{/* Color Selection */}
<div className="space-y-2">
<div className="text-sm text-neutral-400">Color</div>
<div className="flex flex-wrap gap-2">
{FOLDER_COLORS.map(color => (
<button
key={color.value}
className={`
w-6 h-6 rounded-full border-2 transition-all
${selectedColor === color.value
? 'border-white scale-110'
: 'border-neutral-600 hover:border-neutral-400'
}
${color.class}
`}
onClick={() => setSelectedColor(color.value)}
title={color.name}
/>
))}
</div>
</div>
{/* Icon Selection */}
<div className="space-y-2">
<div className="text-sm text-neutral-400">Icon</div>
<div className="grid grid-cols-8 gap-2">
{FOLDER_ICONS.map(icon => (
<button
key={icon}
className={`
w-8 h-8 rounded border text-lg transition-all
${selectedIcon === icon
? 'border-blue-500 bg-blue-500/20'
: 'border-neutral-600 hover:border-neutral-400 hover:bg-neutral-800'
}
`}
onClick={() => setSelectedIcon(icon)}
>
{icon}
</button>
))}
</div>
</div>
</div>
{/* Tags */}
<div className="space-y-2">
<Label className="flex items-center gap-2">
<Tag className="h-4 w-4" />
Tags (Optional)
</Label>
<div className="flex gap-2">
<Input
placeholder="Add tag..."
value={tagInput}
onChange={(e) => setTagInput(e.target.value)}
onKeyDown={handleTagInputKeyDown}
className="flex-1"
/>
<Button
variant="outline"
size="sm"
onClick={handleAddTag}
disabled={!tagInput.trim()}
>
Add
</Button>
</div>
{/* Display Tags */}
{tags.length > 0 && (
<div className="flex flex-wrap gap-1">
{tags.map(tag => (
<Badge
key={tag}
variant="secondary"
className="text-xs cursor-pointer hover:bg-red-500/20"
onClick={() => handleRemoveTag(tag)}
>
{tag} ร—
</Badge>
))}
</div>
)}
</div>
{/* Preview */}
<div className="p-3 bg-neutral-800 rounded-lg">
<div className="text-sm text-neutral-400 mb-2">Preview</div>
<div className="flex items-center gap-2">
<span style={{ color: selectedColor }}>{selectedIcon}</span>
<span className="text-white font-medium">{folderName || 'Folder Name'}</span>
{tags.length > 0 && (
<Badge variant="outline" className="text-xs">
{tags.length} tag{tags.length !== 1 ? 's' : ''}
</Badge>
)}
</div>
{description && (
<div className="text-xs text-neutral-400 mt-1">
{description}
</div>
)}
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => onOpenChange(false)}>
Cancel
</Button>
<Button
onClick={handleCreate}
disabled={!folderName.trim() || isCreating}
>
{isCreating ? 'Creating...' : 'Create Folder'}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}