diff --git a/apps/web/src/pages/editing-planner/components/PipConfigPanel/hooks/usePipConfigPanel.ts b/apps/web/src/pages/editing-planner/components/PipConfigPanel/hooks/usePipConfigPanel.ts new file mode 100644 index 000000000..ac230681a --- /dev/null +++ b/apps/web/src/pages/editing-planner/components/PipConfigPanel/hooks/usePipConfigPanel.ts @@ -0,0 +1,123 @@ +import { useState, useCallback, useMemo } from "react" +import type { PipConfig, PipLayer, PipGridPosition } from "../types" +import { DEFAULT_PIP_LAYER, DEFAULT_PIP_CONFIG } from "../types" +import { GRID_POSITION_MAP, genLayerId } from "../constants" + +interface UsePipConfigPanelOptions { + config: PipConfig + onChange: (config: PipConfig) => void +} + +export const usePipConfigPanel = ({ config, onChange }: UsePipConfigPanelOptions) => { + const [selectedId, setSelectedId] = useState("") + + const selectedLayer = useMemo( + () => config.layers.find((l) => l.id === selectedId) ?? null, + [config.layers, selectedId], + ) + + /* ── 添加图层 ── */ + const handleAddLayer = useCallback(() => { + const newLayer: PipLayer = { + ...DEFAULT_PIP_LAYER, + id: genLayerId(), + name: `图层 ${config.layers.length + 1}`, + z_index: config.layers.length + 1, + } + onChange({ + ...config, + layers: [...config.layers, newLayer], + }) + setSelectedId(newLayer.id) + }, [config, onChange]) + + /* ── 删除图层 ── */ + const handleDeleteLayer = useCallback( + (id: string) => { + const newLayers = config.layers.filter((l) => l.id !== id) + onChange({ ...config, layers: newLayers }) + if (selectedId === id) { + setSelectedId(newLayers.length > 0 ? newLayers[0].id : "") + } + }, + [config, onChange, selectedId], + ) + + /* ── 更新图层 ── */ + const updateLayer = useCallback( + (id: string, partial: Partial) => { + onChange({ + ...config, + layers: config.layers.map((l) => (l.id === id ? { ...l, ...partial } : l)), + }) + }, + [config, onChange], + ) + + /* ── 切换启用 ── */ + const handleEnableToggle = useCallback( + (checked: boolean) => { + onChange({ ...config, enabled: checked }) + }, + [config, onChange], + ) + + /* ── 重置 ── */ + const handleReset = useCallback(() => { + onChange({ ...DEFAULT_PIP_CONFIG }) + setSelectedId("") + }, [onChange]) + + /* ── 九宫格点击 ── */ + const handleGridClick = useCallback( + (pos: PipGridPosition) => { + if (!selectedLayer) return + const coords = GRID_POSITION_MAP[pos] + updateLayer(selectedLayer.id, { + grid_position: pos, + x: coords.x, + y: coords.y, + }) + }, + [selectedLayer, updateLayer], + ) + + /* ── 宽高变化 ── */ + const handleWidthChange = useCallback( + (val: number) => { + if (!selectedLayer) return + const partial: Partial = { width: val } + if (selectedLayer.aspect_lock) { + partial.height = val + } + updateLayer(selectedLayer.id, partial) + }, + [selectedLayer, updateLayer], + ) + + const handleHeightChange = useCallback( + (val: number) => { + if (!selectedLayer) return + const partial: Partial = { height: val } + if (selectedLayer.aspect_lock) { + partial.width = val + } + updateLayer(selectedLayer.id, partial) + }, + [selectedLayer, updateLayer], + ) + + return { + selectedId, + setSelectedId, + selectedLayer, + handleAddLayer, + handleDeleteLayer, + updateLayer, + handleEnableToggle, + handleReset, + handleGridClick, + handleWidthChange, + handleHeightChange, + } +}