import { useState } from "react"; import { type AppSettings, DEFAULT_SETTINGS, PRESETS } from "../types"; interface SettingsPanelProps { settings: AppSettings; onChange: (newSettings: AppSettings) => void; isOpen: boolean; onToggle: () => void; } type TabType = "general" | "physics" | "mouse" | "colors"; export default function SettingsPanel({ settings, onChange, isOpen, onToggle, }: SettingsPanelProps) { const [activeTab, setActiveTab] = useState("general"); const [draggedIndex, setDraggedIndex] = useState(null); const [dragOverIndex, setDragOverIndex] = useState(null); const updateSetting = (key: K, value: AppSettings[K]) => { onChange({ ...settings, [key]: value, }); }; const handleColorChange = (index: number, newColor: string) => { const newColors = [...settings.colors] as [string, string, string, string]; newColors[index] = newColor; updateSetting("colors", newColors); }; const handleDragStart = (e: React.DragEvent, index: number) => { setDraggedIndex(index); e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/plain", index.toString()); }; const handleDragOver = (e: React.DragEvent, index: number) => { e.preventDefault(); if (draggedIndex !== null && draggedIndex !== index) { setDragOverIndex(index); } }; const handleDragLeave = (index: number) => { if (dragOverIndex === index) { setDragOverIndex(null); } }; const handleDrop = (e: React.DragEvent, targetIndex: number) => { e.preventDefault(); const sourceIndexStr = e.dataTransfer.getData("text/plain"); const sourceIndex = sourceIndexStr ? parseInt(sourceIndexStr, 10) : draggedIndex; if (sourceIndex !== null && sourceIndex !== targetIndex && sourceIndex !== undefined) { const newColors = [...settings.colors] as [string, string, string, string]; const temp = newColors[sourceIndex]; newColors[sourceIndex] = newColors[targetIndex]; newColors[targetIndex] = temp; updateSetting("colors", newColors); } setDraggedIndex(null); setDragOverIndex(null); }; const handleDragEnd = () => { setDraggedIndex(null); setDragOverIndex(null); }; const applyPreset = (presetKey: string) => { const preset = PRESETS[presetKey]; if (preset) { onChange({ ...settings, ...preset, }); } }; if (!isOpen) return null; return (
{/* Header */}

Canvas Settings

{/* Tabs */}
{(["general", "physics", "mouse", "colors"] as TabType[]).map((tab) => ( ))}
{/* Body */}
{/* GENERAL TAB */} {activeTab === "general" && ( <> {/* Presets */}
Quick-configure physics presets for different canvas behaviors.
{/* Theme Toggle */}
Light Theme Toggle white/black background.
{/* Scroll Snap Toggle */}
Scroll Snapping Snap sections to viewport.
{/* Scroll Snap Dead Zone Toggle */}
Morph Stability Zone Stays fully formed near snap points.
{/* Scroll Snap Dead Zone Size */} {settings.deadZoneEnabled && (
updateSetting("deadZonePercentage", parseInt(e.target.value))} />
)} )} {/* PHYSICS TAB */} {activeTab === "physics" && ( <> {/* Particle Size */}
updateSetting("particleSize", parseFloat(e.target.value))} />
{/* Particle Opacity */}
updateSetting("particleOpacity", parseFloat(e.target.value))} />
{/* Spring Stiffness */}
updateSetting("springStiffness", parseFloat(e.target.value))} />
{/* Damping */}
updateSetting("damping", parseFloat(e.target.value))} />
{/* Auto Rotate Speed */}
updateSetting("autoRotateSpeed", parseFloat(e.target.value))} />
)} {/* MOUSE TAB */} {activeTab === "mouse" && ( <> {/* Interaction Mode */}
{/* Interaction Force */} {settings.interactionMode !== "disabled" && (
updateSetting("interactionForce", parseFloat(e.target.value))} />
)} {/* Proximity Radius */} {settings.interactionMode !== "disabled" && (
updateSetting("interactionRadius", parseInt(e.target.value))} />
)} {/* Gyro Toggle */}
Gyroscope Interaction Interact via device rotation.
{/* Gyro Sensitivity */} {settings.gyroEnabled && (
updateSetting("gyroSensitivity", parseFloat(e.target.value))} />
)} )} {/* COLORS TAB */} {activeTab === "colors" && ( <>
Particle Palette Click circles to pick colors. They align bottom-to-top in 3D.
{settings.colors.map((color, index) => (
handleDragStart(e, index)} onDragOver={(e) => handleDragOver(e, index)} onDragLeave={() => handleDragLeave(index)} onDrop={(e) => handleDrop(e, index)} onDragEnd={handleDragEnd} >
handleColorChange(index, e.target.value)} />
{color.toUpperCase()}
))}
)}
{/* Footer */}
); }