2025-10-17 21:00:14 +08:00

194 lines
8.6 KiB
TypeScript

"use client";
import { useState, useRef } from 'react';
import { PhotoPreviewSection } from '../PhotoPreview';
import type { PhotoItem, PhotoType } from '../PhotoPreview/types';
import {
PlusOutlined,
UserOutlined,
CameraOutlined,
BulbOutlined,
ArrowRightOutlined,
} from '@ant-design/icons';
import { Dropdown, Menu } from 'antd';
import { ConfigPanel } from './ConfigPanel';
import { defaultConfig } from './config-options';
import type { ConfigOptions } from './config-options';
import { useDeviceType } from '@/hooks/useDeviceType';
export default function VideoCreationForm() {
const [photos, setPhotos] = useState<PhotoItem[]>([]);
const [inputText, setInputText] = useState('');
const [configOptions, setConfigOptions] = useState<ConfigOptions>(defaultConfig);
const { isMobile, isDesktop } = useDeviceType();
const characterInputRef = useRef<HTMLInputElement>(null);
const sceneInputRef = useRef<HTMLInputElement>(null);
const propInputRef = useRef<HTMLInputElement>(null);
/** Handle photo deletion */
const handleDeletePhoto = (index: number) => {
setPhotos(prevPhotos => prevPhotos.filter((_, i) => i !== index));
};
/** Handle file upload */
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>, type: PhotoType) => {
const files = event.target.files;
if (!files || files.length === 0) return;
const newPhotos: PhotoItem[] = Array.from(files).map((file, index) => ({
url: URL.createObjectURL(file),
type,
id: `${type}-${Date.now()}-${index}`,
}));
setPhotos(prevPhotos => [...prevPhotos, ...newPhotos]);
event.target.value = '';
};
/** Handle configuration change */
const handleConfigChange = <K extends keyof ConfigOptions>(
key: K,
value: ConfigOptions[K]
) => {
setConfigOptions(prev => ({ ...prev, [key]: value }));
};
/** Handle video creation */
const handleCreate = () => {
console.log({
text: inputText,
photos,
config: configOptions,
});
// TODO: Implement video creation logic
};
return (
<div data-alt="video-creation-form" className="w-full h-full flex flex-col">
{/* Main Content Area with Border */}
<div
data-alt="content-container"
className="flex-1 border border-white/10 rounded-3xl bg-gradient-to-br from-[#1a1a1a]/50 to-[#0a0a0a]/50 backdrop-blur-sm overflow-hidden flex flex-col"
>
{/* Photo Preview Section - Top */}
{photos.length > 0 && (
<div data-alt="photo-preview-wrapper" className="p-4 pb-0">
<PhotoPreviewSection
photos={photos}
onDelete={handleDeletePhoto}
/>
</div>
)}
{/* Text Input Area - Middle */}
<div data-alt="text-input-wrapper" className="flex-1 px-4 py-4">
<textarea
data-alt="main-text-input"
className="w-full h-full bg-transparent text-gray-300 text-base placeholder-gray-500 resize-none outline-none border-none"
placeholder="Share a topic, idea, or instructions with Video Agent to produce a full avatar video"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
/>
</div>
{/* Control Panel - Bottom */}
<div data-alt="control-panel" className="px-4 py-4 flex items-center justify-between gap-2">
{/* Left Side - Upload and Options */}
<div data-alt="left-controls" className="flex items-center gap-2">
{/* Upload Button with Dropdown */}
<Dropdown
overlay={
<Menu className="bg-[#1a1a1a] border border-white/10">
<Menu.Item
key="character"
icon={<UserOutlined className="text-cyan-400" />}
onClick={() => characterInputRef.current?.click()}
>
<span className="text-gray-300">Character</span>
</Menu.Item>
<Menu.Item
key="scene"
icon={<CameraOutlined className="text-cyan-400" />}
onClick={() => sceneInputRef.current?.click()}
>
<span className="text-gray-300">Scene</span>
</Menu.Item>
<Menu.Item
key="prop"
icon={<BulbOutlined className="text-cyan-400" />}
onClick={() => propInputRef.current?.click()}
>
<span className="text-gray-300">Prop</span>
</Menu.Item>
</Menu>
}
trigger={['click']}
>
<button
data-alt="upload-button"
className="w-8 h-8 rounded-full border border-white/20 bg-transparent hover:bg-white/5 hover:border-cyan-400/60 transition-all duration-200 flex items-center justify-center text-gray-300 hover:text-cyan-400"
>
<PlusOutlined className="text-base font-bold" />
</button>
</Dropdown>
{/* Hidden file inputs */}
<input
ref={characterInputRef}
type="file"
accept="image/*"
multiple
className="hidden"
onChange={(e) => handleFileUpload(e, 'character')}
/>
<input
ref={sceneInputRef}
type="file"
accept="image/*"
multiple
className="hidden"
onChange={(e) => handleFileUpload(e, 'scene')}
/>
<input
ref={propInputRef}
type="file"
accept="image/*"
multiple
className="hidden"
onChange={(e) => handleFileUpload(e, 'prop')}
/>
{/* Mention Button */}
<button
data-alt="mention-button"
className="w-8 h-8 rounded-full border border-white/20 bg-transparent hover:bg-white/5 hover:border-cyan-400/60 transition-all duration-200 flex items-center justify-center text-gray-300 hover:text-cyan-400"
>
<span className="text-base font-bold">@</span>
</button>
{/* Configuration Panel */}
<ConfigPanel
configOptions={configOptions}
onConfigChange={handleConfigChange}
isMobile={isMobile}
isDesktop={isDesktop}
/>
</div>
{/* Right Side - Create Button */}
<button
data-alt="create-button"
className="w-8 h-8 rounded-full bg-black hover:bg-gray-900 border border-white/20 hover:border-cyan-400/60 transition-all duration-200 flex items-center justify-center text-white shadow-lg hover:shadow-cyan-400/20"
onClick={handleCreate}
>
<ArrowRightOutlined className="text-base font-bold" />
</button>
</div>
</div>
</div>
);
}