forked from 77media/video-flow
153 lines
6.9 KiB
TypeScript
153 lines
6.9 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
GlobalOutlined,
|
|
ClockCircleOutlined,
|
|
DownOutlined
|
|
} from '@ant-design/icons';
|
|
import { WandSparkles, RectangleHorizontal, RectangleVertical } from 'lucide-react';
|
|
import { Dropdown, Tooltip } from 'antd';
|
|
import { LanguageOptions, VideoDurationOptions } from './config-options';
|
|
import type { ConfigOptions, LanguageValue, VideoDurationValue } from './config-options';
|
|
import { PortraitAnimeSelector } from './PortraitAnimeSelector';
|
|
|
|
interface ConfigPanelProps {
|
|
/** Current configuration options */
|
|
configOptions: ConfigOptions;
|
|
/** Handler for configuration changes */
|
|
onConfigChange: <K extends keyof ConfigOptions>(key: K, value: ConfigOptions[K], exclude?: boolean) => void;
|
|
/** Whether it's mobile device */
|
|
isMobile?: boolean;
|
|
/** Whether it's desktop device */
|
|
isDesktop?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Configuration panel component for video creation settings.
|
|
* Includes language, auto script, duration, and aspect ratio selectors.
|
|
* @param {ConfigOptions} configOptions - current configuration
|
|
* @param {Function} onConfigChange - handler for config changes
|
|
* @param {boolean} isMobile - whether it's mobile device
|
|
* @param {boolean} isDesktop - whether it's desktop device
|
|
* @returns {JSX.Element}
|
|
*/
|
|
export const ConfigPanel = ({
|
|
configOptions,
|
|
onConfigChange,
|
|
isMobile = false,
|
|
isDesktop = true,
|
|
}: ConfigPanelProps) => {
|
|
/** Language dropdown menu items */
|
|
const languageMenuItems = LanguageOptions.map((option) => ({
|
|
key: option.value,
|
|
label: <span className="text-gray-300">{option.label}</span>,
|
|
}));
|
|
|
|
/** Duration dropdown menu items */
|
|
const durationMenuItems = VideoDurationOptions.map((option) => ({
|
|
key: option.value,
|
|
label: <span className="text-gray-300">{option.label}</span>,
|
|
}));
|
|
|
|
const currentLanguage = LanguageOptions.find((option) => option.value === configOptions.language);
|
|
const currentDuration = configOptions.videoDuration === 'unlimited' ? 'auto' : configOptions.videoDuration;
|
|
|
|
return (
|
|
<div data-alt="config-panel" className="flex items-center gap-2">
|
|
{/* Language selector */}
|
|
<Dropdown
|
|
menu={{
|
|
items: languageMenuItems,
|
|
onClick: ({ key }) => onConfigChange('language', key as LanguageValue),
|
|
className: 'bg-[#1a1a1a] border border-white/10'
|
|
}}
|
|
trigger={['click']}
|
|
>
|
|
<button
|
|
data-alt="config-language"
|
|
className="h-8 px-2 rounded-full border border-white/20 bg-transparent text-gray-300 hover:bg-white/5 hover:border-cyan-400/60 transition-all duration-200 flex items-center gap-2 hover:text-cyan-400"
|
|
>
|
|
<GlobalOutlined className="text-base" />
|
|
<span className="text-sm">{currentLanguage?.code}</span>
|
|
<DownOutlined className="text-xs" />
|
|
</button>
|
|
</Dropdown>
|
|
|
|
{/* Auto script toggle */}
|
|
<Tooltip title="AI Story Copilot" placement="top">
|
|
<button
|
|
data-alt="config-expansion-mode"
|
|
className={`h-8 px-2 rounded-full border transition-all duration-200 flex items-center gap-2 text-sm ${
|
|
configOptions.videoDuration === '8s'
|
|
? 'opacity-40 cursor-not-allowed border-white/20 bg-transparent text-gray-400'
|
|
: configOptions.expansion_mode
|
|
? 'border-cyan-400 bg-cyan-400/10 text-cyan-400 hover:bg-cyan-400/20'
|
|
: 'border-white/20 bg-transparent text-gray-300 hover:bg-white/5 hover:border-cyan-400/60 hover:text-cyan-400'
|
|
}`}
|
|
disabled={configOptions.videoDuration === '8s'}
|
|
onClick={() => {
|
|
if (configOptions.videoDuration !== '8s') {
|
|
onConfigChange('expansion_mode', !configOptions.expansion_mode);
|
|
}
|
|
}}
|
|
>
|
|
<WandSparkles className="w-4 h-4" />
|
|
<span>AutoScript</span>
|
|
</button>
|
|
</Tooltip>
|
|
|
|
{/* Duration selector */}
|
|
<Dropdown
|
|
menu={{
|
|
items: durationMenuItems,
|
|
onClick: ({ key }) => onConfigChange('videoDuration', key as VideoDurationValue),
|
|
className: 'bg-[#1a1a1a] border border-white/10'
|
|
}}
|
|
trigger={['click']}
|
|
>
|
|
<button
|
|
data-alt="config-video-duration"
|
|
className="h-8 px-2 rounded-full border border-white/20 bg-transparent hover:bg-white/5 hover:border-cyan-400/60 transition-all duration-200 flex items-center gap-2 text-gray-300 hover:text-cyan-400"
|
|
>
|
|
<ClockCircleOutlined className="text-base" />
|
|
<span className="text-sm capitalize">{currentDuration}</span>
|
|
<DownOutlined className="text-xs" />
|
|
</button>
|
|
</Dropdown>
|
|
|
|
{/* Aspect ratio toggles */}
|
|
<div data-alt="aspect-ratio-controls" className="h-8 p-1 flex items-center rounded-full border border-white/20 hover:border-cyan-400/60">
|
|
<button
|
|
data-alt="portrait-button"
|
|
className={`w-8 h-6 rounded-full transition-all duration-200 flex items-center justify-center ${
|
|
configOptions.aspect_ratio === 'VIDEO_ASPECT_RATIO_PORTRAIT'
|
|
? 'bg-white/10 text-cyan-400 shadow-sm'
|
|
: 'bg-transparent text-gray-400 hover:text-gray-300'
|
|
}`}
|
|
onClick={() => onConfigChange('aspect_ratio', 'VIDEO_ASPECT_RATIO_PORTRAIT')}
|
|
>
|
|
<RectangleVertical className="w-4 h-4" />
|
|
</button>
|
|
<button
|
|
data-alt="landscape-button"
|
|
className={`w-8 h-6 rounded-full transition-all duration-200 flex items-center justify-center ${
|
|
configOptions.aspect_ratio === 'VIDEO_ASPECT_RATIO_LANDSCAPE'
|
|
? 'bg-white/10 text-cyan-400 shadow-sm'
|
|
: 'bg-transparent text-gray-400 hover:text-gray-300'
|
|
}`}
|
|
onClick={() => onConfigChange('aspect_ratio', 'VIDEO_ASPECT_RATIO_LANDSCAPE')}
|
|
>
|
|
<RectangleHorizontal className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Portrait/Anime selector */}
|
|
<PortraitAnimeSelector
|
|
value={configOptions.pcode}
|
|
onChange={(v) => onConfigChange('pcode', v, true)}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|