import { Modal } from "antd"; import { Switch } from "@/components/ui/base/switch"; import { GripVertical, RotateCcw, X } from "lucide-react"; import { motion, useReducedMotion } from "motion/react"; import { useEffect, useRef, useState } from "react"; import { canvasThemes } from "@/lib/canvas-theme"; import { defaultToolbarPrefs, getToolbarTools, persistToolbarPrefs, readToolbarPrefs, type ToolbarId, type ToolbarPrefs, type ToolContext, type ToolDefinition } from "@/lib/canvas/tool-registry"; import { useThemeStore } from "@/stores/use-theme-store"; type ToolbarSettingsModalProps = { open: boolean; onClose: () => void; toolbar: ToolbarId; }; /** 设置面板用的最小化上下文——仅用于解析工具的 label/icon */ const settingsMockContext: ToolContext = { selectedCount: 0, selectedNodeTypes: new Set(), selectedVideoCount: 0, canvasTool: "move", workspaceMode: "professional", isProjectLinked: false, canUndo: false, canRedo: false, extractingVideoFrames: false, extractingAudio: false, trimmingVideo: false, mergingVideos: false, addPanelOpen: false, appearancePanelOpen: false, settingsPanelOpen: false, handlers: {} as ToolContext["handlers"], }; type SettingsItem = { id: string; label: string; icon: React.ReactNode; visible: boolean; }; export function ToolbarSettingsModal({ open, onClose, toolbar }: ToolbarSettingsModalProps) { const theme = canvasThemes[useThemeStore((state) => state.theme)]; const reducedMotion = useReducedMotion(); const [items, setItems] = useState([]); const [toolbarId, setToolbarId] = useState(toolbar); const draggedItemIdRef = useRef(null); const dragTargetIdRef = useRef(null); const [draggedItemId, setDraggedItemId] = useState(null); const visibleCount = items.filter((item) => item.visible).length; // 当 modal 打开或 toolbar 变化时,加载工具列表与偏好 useEffect(() => { if (!open) return; setToolbarId(toolbar); const tools = getToolbarTools(toolbar); const prefs = readToolbarPrefs(toolbar) ?? defaultToolbarPrefs(toolbar); const hiddenSet = new Set(prefs.hidden); const orderIndex = new Map(prefs.order.map((id, index) => [id, index])); const sorted = [...tools].sort((a, b) => { const ai = orderIndex.has(a.id) ? orderIndex.get(a.id)! : Number.MAX_SAFE_INTEGER; const bi = orderIndex.has(b.id) ? orderIndex.get(b.id)! : Number.MAX_SAFE_INTEGER; if (ai !== bi) return ai - bi; return a.defaultOrder - b.defaultOrder; }); setItems(sorted.map((tool) => ({ id: tool.id, label: resolveLabel(tool, settingsMockContext), icon: resolveIcon(tool, settingsMockContext), visible: !hiddenSet.has(tool.id), }))); }, [open, toolbar]); const handleDragStart = (id: string) => { draggedItemIdRef.current = id; dragTargetIdRef.current = id; setDraggedItemId(id); }; const handleDragEnter = (targetId: string) => { const sourceId = draggedItemIdRef.current; if (!sourceId || dragTargetIdRef.current === targetId) return; dragTargetIdRef.current = targetId; setItems((current) => { const sourceIndex = current.findIndex((item) => item.id === sourceId); const targetIndex = current.findIndex((item) => item.id === targetId); if (sourceIndex < 0 || targetIndex < 0) return current; const next = [...current]; const [movedItem] = next.splice(sourceIndex, 1); next.splice(targetIndex, 0, movedItem); persistCurrent(next); return next; }); }; const handleDragEnd = () => { draggedItemIdRef.current = null; dragTargetIdRef.current = null; setDraggedItemId(null); }; const handleToggleVisible = (id: string, visible: boolean) => { setItems((prev) => { const next = prev.map((item) => item.id === id ? { ...item, visible } : item); persistCurrent(next); return next; }); }; const handleReset = () => { const defaults = defaultToolbarPrefs(toolbarId); const tools = getToolbarTools(toolbarId); const hiddenSet = new Set(defaults.hidden); setItems(tools.map((tool) => ({ id: tool.id, label: resolveLabel(tool, settingsMockContext), icon: resolveIcon(tool, settingsMockContext), visible: !hiddenSet.has(tool.id), }))); persistToolbarPrefs(toolbarId, defaults); }; const persistCurrent = (currentItems: SettingsItem[]) => { const prefs: ToolbarPrefs = { order: currentItems.map((item) => item.id), hidden: currentItems.filter((item) => !item.visible).map((item) => item.id), }; persistToolbarPrefs(toolbarId, prefs); }; return (

工具栏设置

拖动调整顺序,关闭不常用入口

已显示 {visibleCount}/{items.length}
{items.map((item) => ( ))}
); } function ToolbarSettingsItem({ item, reducedMotion, theme, dragging, onToggleVisible, onDragStart, onDragEnter, onDragEnd }: { item: SettingsItem; reducedMotion: boolean; theme: (typeof canvasThemes)[keyof typeof canvasThemes]; dragging: boolean; onToggleVisible: (id: string, visible: boolean) => void; onDragStart: (id: string) => void; onDragEnter: (id: string) => void; onDragEnd: () => void }) { return ( onDragEnter(item.id)} onDragOver={(event) => event.preventDefault()} >
onToggleVisible(item.id, checked)} aria-label={`${item.visible ? "隐藏" : "显示"}${item.label}`} />
{item.icon} {item.label}
); } function resolveLabel(tool: ToolDefinition, ctx: ToolContext): string { return typeof tool.label === "function" ? tool.label(ctx) : tool.label; } function resolveIcon(tool: ToolDefinition, ctx: ToolContext): React.ReactNode { return typeof tool.icon === "function" ? tool.icon(ctx) : tool.icon; }