"use client" import { useState, useEffect, useRef } from "react" import { Plus, X, ChevronDown, ChevronUp, ToggleLeft, ToggleRight } from "lucide-react" import { motion, AnimatePresence } from "motion/react" export interface SettingItem { title: string value: string | number | boolean } export interface SettingCategory { category: string settings: SettingItem[] } const DEFAULT_CATEGORIES: SettingCategory[] = [ { category: "Graphics", settings: [ { title: "Texture Quality", value: "High" }, { title: "Shadow Quality", value: "Medium" }, { title: "Anti-Aliasing", value: "TAA" }, { title: "Effects Quality", value: "High" }, ], }, { category: "Display", settings: [ { title: "Resolution", value: "1280x800" }, { title: "Refresh Rate", value: 60 }, { title: "VSync", value: false }, { title: "Fullscreen", value: true }, ], }, { category: "Audio", settings: [ { title: "Master Volume", value: 80 }, { title: "SFX Volume", value: 70 }, ], }, ] const typeOptions = [ { value: "text", label: "Abc" }, { value: "number", label: "123" }, { value: "boolean", label: "\u2713/\u2717" }, ] as const interface SettingsEditorProps { value: SettingCategory[] onChange: (categories: SettingCategory[]) => void className?: string } export function SettingsEditor({ value, onChange, className = "", }: SettingsEditorProps) { const [collapsedCategories, setCollapsedCategories] = useState>( new Set() ) const [newCategoryName, setNewCategoryName] = useState("") const [newSettingNames, setNewSettingNames] = useState>({}) const [newSettingTypes, setNewSettingTypes] = useState>({}) const addSettingInputRefs = useRef>({}) const toggleCategory = (category: string) => { setCollapsedCategories((prev) => { const next = new Set(prev) if (next.has(category)) { next.delete(category) } else { next.add(category) } return next }) } const addCategory = () => { const name = newCategoryName.trim() if (!name) return if (value.some((c) => c.category === name)) return onChange([...value, { category: name, settings: [] }]) setNewCategoryName("") } const removeCategory = (category: string) => { onChange(value.filter((c) => c.category !== category)) setCollapsedCategories((prev) => { const next = new Set(prev) next.delete(category) return next }) } const addSetting = (category: string) => { const name = (newSettingNames[category] || "").trim() if (!name) return const type = newSettingTypes[category] || "text" const defaultValue = type === "boolean" ? false : type === "number" ? 0 : "" onChange( value.map((c) => { if (c.category !== category) return c if (c.settings.some((s) => s.title === name)) return c return { ...c, settings: [...c.settings, { title: name, value: defaultValue }], } }) ) setNewSettingNames((prev) => ({ ...prev, [category]: "" })) // Refocus after React re-renders setTimeout(() => addSettingInputRefs.current[category]?.focus(), 0) } const moveCategoryUp = (category: string) => { const idx = value.findIndex((c) => c.category === category) if (idx <= 0) return const next = [...value] ;[next[idx - 1], next[idx]] = [next[idx], next[idx - 1]] onChange(next) } const moveCategoryDown = (category: string) => { const idx = value.findIndex((c) => c.category === category) if (idx >= value.length - 1) return const next = [...value] ;[next[idx], next[idx + 1]] = [next[idx + 1], next[idx]] onChange(next) } const moveSettingUp = (category: string, settingTitle: string) => { onChange( value.map((c) => { if (c.category !== category) return c const idx = c.settings.findIndex((s) => s.title === settingTitle) if (idx <= 0) return c const next = [...c.settings] ;[next[idx - 1], next[idx]] = [next[idx], next[idx - 1]] return { ...c, settings: next } }) ) } const moveSettingDown = (category: string, settingTitle: string) => { onChange( value.map((c) => { if (c.category !== category) return c const idx = c.settings.findIndex((s) => s.title === settingTitle) if (idx >= c.settings.length - 1) return c const next = [...c.settings] ;[next[idx], next[idx + 1]] = [next[idx + 1], next[idx]] return { ...c, settings: next } }) ) } const removeSetting = (category: string, settingTitle: string) => { onChange( value.map((c) => { if (c.category !== category) return c return { ...c, settings: c.settings.filter((s) => s.title !== settingTitle), } }) ) } const updateSetting = ( category: string, settingTitle: string, newValue: string | number | boolean ) => { onChange( value.map((c) => { if (c.category !== category) return c return { ...c, settings: c.settings.map((s) => s.title === settingTitle ? { ...s, value: newValue } : s ), } }) ) } const loadDefaults = () => { onChange(DEFAULT_CATEGORIES) setCollapsedCategories(new Set()) } const isEmpty = value.length === 0 const [showCustomInput, setShowCustomInput] = useState(false) const customInputRef = useRef(null) useEffect(() => { if (showCustomInput && customInputRef.current) { customInputRef.current.focus() } }, [showCustomInput]) return (
{isEmpty && !showCustomInput && (

No settings configured

)} {isEmpty && showCustomInput && (
setNewCategoryName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault() if (newCategoryName.trim()) { addCategory() setShowCustomInput(false) } } }} placeholder="e.g. Graphics, Audio, Display..." className="flex-1 px-4 py-2.5 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors" />
)} {value.map((cat, index) => { const isCollapsed = collapsedCategories.has(cat.category) return (
{!isCollapsed && (
{/* Add setting row at the top */}
{typeOptions.map((opt) => ( ))}
{ addSettingInputRefs.current[cat.category] = el }} type="text" value={newSettingNames[cat.category] || ""} onChange={(e) => setNewSettingNames((prev) => ({ ...prev, [cat.category]: e.target.value, })) } onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault() addSetting(cat.category) } }} placeholder="Add setting..." className="flex-1 px-3 py-1.5 rounded-md border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors" />
{cat.settings.map((setting) => ( {setting.title} {typeof setting.value === "boolean" ? ( ) : ( { const raw = e.target.value const updated = typeof setting.value === "number" ? raw === "" ? 0 : Number(raw) : raw updateSetting( cat.category, setting.title, updated ) }} className="flex-1 min-w-0 px-3 py-1.5 rounded-md border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors" /> )}
))}
)}
) })}
{!isEmpty && (
setNewCategoryName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault() addCategory() } }} placeholder="New category name..." className="flex-1 px-4 py-2.5 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors" />
)}
) }