mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
feat: replace animated backgrounds with Aceternity UI system
- Add 16 animated background components (aurora, sparkles, vortex, shooting-stars, beams, beams-collision, gradient-animation, wavy, lines, boxes, meteors, grid, dots, spotlight, noise, ripple, gemini-effect) - Add full admin BackgroundEditor with live preview, type gallery, per-type settings - Add BackgroundRenderer with lazy loading, localStorage cache, mobile reduction - Add input sanitization (sanitizeColor, clampNumber) across all components - Add translations for en, ru, zh, fa locales - Remove old Aurora (WebGL/OGL) and AnimatedBackground (CSS wave-blob) - Remove ogl dependency, add simplex-noise
This commit is contained in:
92
src/components/backgrounds/BackgroundRenderer.tsx
Normal file
92
src/components/backgrounds/BackgroundRenderer.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import { Suspense, useMemo } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { brandingApi } from '@/api/branding';
|
||||
import type { AnimationConfig, BackgroundType } from '@/components/ui/backgrounds/types';
|
||||
import { DEFAULT_ANIMATION_CONFIG } from '@/components/ui/backgrounds/types';
|
||||
import { backgroundComponents } from '@/components/ui/backgrounds/registry';
|
||||
|
||||
const ANIMATION_CACHE_KEY = 'cabinet_animation_config';
|
||||
|
||||
function getCachedConfig(): AnimationConfig | null {
|
||||
try {
|
||||
const cached = localStorage.getItem(ANIMATION_CACHE_KEY);
|
||||
return cached ? JSON.parse(cached) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setCachedConfig(config: AnimationConfig) {
|
||||
try {
|
||||
localStorage.setItem(ANIMATION_CACHE_KEY, JSON.stringify(config));
|
||||
} catch {
|
||||
// localStorage not available
|
||||
}
|
||||
}
|
||||
|
||||
export function setCachedAnimationConfig(config: AnimationConfig) {
|
||||
setCachedConfig(config);
|
||||
}
|
||||
|
||||
function reduceMobileSettings(settings: Record<string, unknown>): Record<string, unknown> {
|
||||
const reduced = { ...settings };
|
||||
if (typeof reduced.particleCount === 'number')
|
||||
reduced.particleCount = Math.floor((reduced.particleCount as number) / 2);
|
||||
if (typeof reduced.particleDensity === 'number')
|
||||
reduced.particleDensity = Math.floor((reduced.particleDensity as number) / 2);
|
||||
if (typeof reduced.number === 'number')
|
||||
reduced.number = Math.floor((reduced.number as number) / 2);
|
||||
if ('interactive' in reduced) reduced.interactive = false;
|
||||
return reduced;
|
||||
}
|
||||
|
||||
export function BackgroundRenderer() {
|
||||
const prefersReducedMotion = useMemo(
|
||||
() => window.matchMedia('(prefers-reduced-motion: reduce)').matches,
|
||||
[],
|
||||
);
|
||||
|
||||
const { data: config } = useQuery({
|
||||
queryKey: ['animation-config'],
|
||||
queryFn: async () => {
|
||||
const result = await brandingApi.getAnimationConfig();
|
||||
setCachedConfig(result);
|
||||
return result;
|
||||
},
|
||||
initialData: getCachedConfig() ?? undefined,
|
||||
staleTime: 60_000,
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
const effectiveConfig = config ?? DEFAULT_ANIMATION_CONFIG;
|
||||
|
||||
if (!effectiveConfig.enabled || effectiveConfig.type === 'none' || prefersReducedMotion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const bgType = effectiveConfig.type as Exclude<BackgroundType, 'none'>;
|
||||
const Component = backgroundComponents[bgType];
|
||||
|
||||
if (!Component) return null;
|
||||
|
||||
const isMobile = window.innerWidth < 768;
|
||||
const settings =
|
||||
effectiveConfig.reducedOnMobile && isMobile
|
||||
? reduceMobileSettings(effectiveConfig.settings)
|
||||
: effectiveConfig.settings;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="pointer-events-none fixed inset-0 z-0"
|
||||
style={{
|
||||
opacity: effectiveConfig.opacity,
|
||||
filter: effectiveConfig.blur > 0 ? `blur(${effectiveConfig.blur}px)` : undefined,
|
||||
contain: 'strict',
|
||||
}}
|
||||
>
|
||||
<Suspense fallback={null}>
|
||||
<Component settings={settings} />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user