import { Lock, Unlock } from "lucide-react" import { useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { Button } from "@/components/ui/button" import { DialogFooter } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { useModal } from "@/features/modals/services/modal-provider" import { ASPECT_RATIOS, COLOR_SPACES, ColorSpace, FRAME_RATES, FrameRate, getDefaultResolutionForAspectRatio, getResolutionsForAspectRatio, ResolutionOption, } from "@/features/project-settings/types/project" import { useProjectSettings } from "../hooks/use-project-settings" import { getAspectRatioLabel, getAspectRatioString } from "../utils" /** * Модальное окно настроек проекта * Позволяет пользователю настраивать параметры проекта, такие как: * - Соотношение сторон * - Разрешение * - Частота кадров * - Цветовое пространство * * @returns {JSX.Element} Компонент модального окна настроек проекта */ export function ProjectSettingsModal() { const { t } = useTranslation() // Хук для интернационализации const { closeModal } = useModal() // Хук для управления модальными окнами const { settings, updateSettings } = useProjectSettings() // Хук для доступа к настройкам проекта // Состояние для хранения доступных разрешений для выбранного соотношения сторон const [availableResolutions, setAvailableResolutions] = useState([]) // Состояние для пользовательских значений ширины и высоты const [customWidth, setCustomWidth] = useState(1920) const [customHeight, setCustomHeight] = useState(1080) // Флаг блокировки соотношения сторон (при изменении ширины/высоты) const [aspectRatioLocked, setAspectRatioLocked] = useState(true) /** * Эффект для обновления доступных разрешений при изменении соотношения сторон * Загружает список разрешений, соответствующих выбранному соотношению сторон */ useEffect(() => { // Получаем список доступных разрешений для текущего соотношения сторон const resolutions = getResolutionsForAspectRatio(settings.aspectRatio.label) setAvailableResolutions(resolutions) // Обновляем значения пользовательской ширины и высоты // в соответствии с текущими настройками проекта setCustomWidth(settings.aspectRatio.value.width) setCustomHeight(settings.aspectRatio.value.height) console.log("[ProjectSettingsDialog] Доступные разрешения обновлены:", resolutions) }, [settings.aspectRatio]) // Зависимость от соотношения сторон /** * Функция для обновления соотношения сторон и автоматического обновления разрешения * Вызывается при выборе нового соотношения сторон в выпадающем списке * * @param {string} value - Выбранное соотношение сторон (например, "16:9", "1:1", "custom") */ const handleAspectRatioChange = (value: string) => { // Находим объект соотношения сторон по выбранному значению const newAspectRatio = ASPECT_RATIOS.find((item) => item.label === value) if (newAspectRatio) { // Сохраняем текущее состояние блокировки соотношения сторон // даже для пользовательского соотношения // Получаем рекомендуемое разрешение для нового соотношения сторон const recommendedResolution = getDefaultResolutionForAspectRatio(value) // Создаем новый объект настроек с обновленным соотношением сторон и разрешением const newSettings = { ...settings, aspectRatio: newAspectRatio, // Для пользовательского соотношения используем "custom", иначе рекомендуемое разрешение resolution: value === "custom" ? "custom" : recommendedResolution.value, } // Обновляем размеры в соответствии с выбранным соотношением сторон if (value === "custom") { // Для пользовательского соотношения используем текущие значения ширины и высоты newSettings.aspectRatio = { ...newSettings.aspectRatio, value: { ...newSettings.aspectRatio.value, width: customWidth, height: customHeight, }, } } else { // Для стандартных соотношений используем рекомендуемое разрешение newSettings.aspectRatio = { ...newSettings.aspectRatio, value: { ...newSettings.aspectRatio.value, width: recommendedResolution.width, height: recommendedResolution.height, }, } // Обновляем значения пользовательской ширины и высоты // для отображения в полях ввода setCustomWidth(recommendedResolution.width) setCustomHeight(recommendedResolution.height) } // Применяем новые настройки через контекст void updateSettings(newSettings) // Отладочный вывод console.log("[ProjectSettingsDialog] Соотношение сторон изменено:", { aspectRatio: newAspectRatio.label, resolution: newSettings.resolution, width: newSettings.aspectRatio.value.width, height: newSettings.aspectRatio.value.height, }) // Принудительно обновляем компоненты, зависящие от размеров // через событие изменения размера окна setTimeout(() => { if (typeof window !== "undefined") { window.dispatchEvent(new Event("resize")) } }, 50) } } return (
{/* Выбор соотношения сторон */}
{/* Выбор разрешения */}
{/* Поля для ввода пользовательских размеров (ширина и высота) */}
{/* Поле ввода ширины */} { const width = Number.parseInt(e.target.value, 10) if (!Number.isNaN(width) && width > 0) { setCustomWidth(width) // Если соотношение сторон заблокировано, обновляем высоту пропорционально // для сохранения выбранного соотношения сторон if (aspectRatioLocked) { // Вычисляем текущее соотношение сторон const aspectRatio = settings.aspectRatio.value.width / settings.aspectRatio.value.height // Вычисляем новую высоту на основе соотношения сторон const newHeight = Math.round(width / aspectRatio) setCustomHeight(newHeight) // Обновляем настройки проекта с новыми размерами, // сохраняя соотношение сторон const newSettings = { ...settings, aspectRatio: { ...settings.aspectRatio, value: { ...settings.aspectRatio.value, width, height: newHeight, }, }, resolution: `${width}x${newHeight}`, } void updateSettings(newSettings) } else { // Если соотношение сторон не заблокировано или пользовательское, // просто обновляем ширину без изменения высоты const newSettings = { ...settings, aspectRatio: { ...settings.aspectRatio, value: { ...settings.aspectRatio.value, width, }, }, resolution: `${width}x${customHeight}`, } void updateSettings(newSettings) } } }} className="w-20 text-center" min={320} // Минимальная ширина max={7680} // Максимальная ширина (8K) /> {/* Разделитель между полями ширины и высоты */} x {/* Поле ввода высоты */} { const height = Number.parseInt(e.target.value, 10) if (!Number.isNaN(height) && height > 0) { setCustomHeight(height) // Если соотношение сторон заблокировано, обновляем ширину пропорционально // для сохранения выбранного соотношения сторон if (aspectRatioLocked) { // Вычисляем текущее соотношение сторон const aspectRatio = settings.aspectRatio.value.width / settings.aspectRatio.value.height // Вычисляем новую ширину на основе соотношения сторон const newWidth = Math.round(height * aspectRatio) setCustomWidth(newWidth) // Обновляем настройки проекта с новыми размерами, // сохраняя соотношение сторон const newSettings = { ...settings, aspectRatio: { ...settings.aspectRatio, value: { ...settings.aspectRatio.value, width: newWidth, height, }, }, resolution: `${newWidth}x${height}`, } void updateSettings(newSettings) } else { // Если соотношение сторон не заблокировано или пользовательское, // просто обновляем высоту без изменения ширины const newSettings = { ...settings, aspectRatio: { ...settings.aspectRatio, value: { ...settings.aspectRatio.value, height, }, }, resolution: `${customWidth}x${height}`, } void updateSettings(newSettings) } } }} className="w-20 text-center" min={240} // Минимальная высота max={4320} // Максимальная высота (4K) /> {/* Кнопка блокировки/разблокировки соотношения сторон */} {/* Отображается для всех соотношений сторон */}
{/* Информация о текущем соотношении сторон */}
{aspectRatioLocked ? ( // Заблокированное соотношение сторон <> {settings.aspectRatio.label !== "custom" ? t("dialogs.projectSettings.aspectRatioLocked", { ratio: settings.aspectRatio.label, }) : t("dialogs.projectSettings.aspectRatioLocked", { ratio: getAspectRatioString(customWidth, customHeight), })} ) : ( // Разблокированное соотношение сторон <> {settings.aspectRatio.label !== "custom" ? t("dialogs.projectSettings.aspectRatioUnlocked", { ratio: settings.aspectRatio.label, }) : t("dialogs.projectSettings.aspectRatioUnlocked", { ratio: getAspectRatioString(customWidth, customHeight), })} )}
{/* Выбор частоты кадров */}
{/* Выбор цветового пространства */}
{/* Кнопки действий в нижней части модального окна */} {/* Кнопка отмены */} {/* Кнопка сохранения */}
) }