import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { backgroundRegistry } from '@/components/ui/backgrounds/registry'; import { BackgroundPreview } from '@/components/backgrounds/BackgroundPreview'; import type { AnimationConfig, BackgroundType, SettingDefinition, } from '@/components/ui/backgrounds/types'; import { Toggle } from './Toggle'; import { cn } from '@/lib/utils'; function SettingField({ def, value, onChange, t, }: { def: SettingDefinition; value: unknown; onChange: (val: unknown) => void; t: (key: string) => string; }) { if (def.type === 'number') { const numVal = (value as number) ?? (def.default as number); const displayVal = numVal < 0.01 ? numVal.toExponential(1) : String(numVal); return (
onChange(parseFloat(e.target.value))} className="w-24 accent-accent-500" /> {displayVal}
); } if (def.type === 'color') { const colorVal = (value as string) ?? (def.default as string); const hexForInput = /^#[0-9a-fA-F]{3,8}$/.test(colorVal) ? colorVal : '#818cf8'; return (
onChange(e.target.value)} className="h-7 w-10 cursor-pointer rounded border border-dark-600 bg-transparent" /> {colorVal}
); } if (def.type === 'boolean') { const boolVal = (value as boolean) ?? (def.default as boolean); return (
onChange(!boolVal)} />
); } if (def.type === 'select' && def.options) { const selectVal = (value as string) ?? (def.default as string); return (
); } return null; } interface BackgroundConfigEditorProps { value: AnimationConfig; onChange: (config: AnimationConfig) => void; } export function BackgroundConfigEditor({ value: config, onChange }: BackgroundConfigEditorProps) { const { t } = useTranslation(); const updateConfig = (patch: Partial) => { onChange({ ...config, ...patch }); }; const updateSetting = (key: string, val: unknown) => { onChange({ ...config, settings: { ...config.settings, [key]: val } }); }; const handleTypeChange = (type: BackgroundType) => { const def = backgroundRegistry.find((d) => d.type === type); const defaults: Record = {}; if (def) { for (const s of def.settings) { defaults[s.key] = s.default; } } onChange({ ...config, type, settings: defaults }); }; const currentDef = useMemo( () => backgroundRegistry.find((d) => d.type === config.type), [config.type], ); const categories = useMemo(() => { const cats = new Map(); for (const def of backgroundRegistry) { const list = cats.get(def.category) ?? []; list.push(def); cats.set(def.category, list); } return cats; }, []); return (
{/* Header with enable toggle */}

{t('admin.backgrounds.title')}

{t('admin.backgrounds.description')}

updateConfig({ enabled: !config.enabled })} />
{config.enabled && ( <> {/* Preview */}
{/* Type selector gallery */}
{/* None option */} {/* Background types by category */}
{Array.from(categories.entries()).map(([category, defs]) => (
{t(`admin.backgrounds.category${category.toUpperCase()}`)}
{defs.map((def) => ( ))}
))}
{/* Per-type settings */} {currentDef && currentDef.settings.length > 0 && (

{t('admin.backgrounds.settings')}

{currentDef.settings.map((def) => ( updateSetting(def.key, val)} t={t} /> ))}
)} {/* Global settings */}
updateConfig({ opacity: parseFloat(e.target.value) })} className="w-24 accent-accent-500" /> {config.opacity}
updateConfig({ blur: Number(e.target.value) })} className="w-24 accent-accent-500" /> {config.blur}px

{t('admin.backgrounds.reducedOnMobileDesc')}

updateConfig({ reducedOnMobile: !config.reducedOnMobile })} />
)}
); }