Fix mobile responsiveness and implement enhanced theme system

- Fixed mobile gallery login box sizing and layout
- Fixed header button layout for mobile screens
- Fixed duplicate logo issue on logout
- Fixed '0' rendering when upload button is hidden
- Fixed horizontal scrolling on small screens

- Implemented comprehensive theme system with gallery layouts
- Added 6 different gallery layouts: Grid, Masonry, Carousel, Timeline, Hero, Mosaic
- Created enhanced theme customizer with layout selection
- Added theme presets for different event types
- Updated event creation with theme preview and customization
- Fixed all TypeScript compilation errors

- Added missing translation keys for create event page
- Added translations for theme customization features

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-09 11:26:27 +02:00
parent d8fb4c9565
commit 6438374258
23 changed files with 2798 additions and 168 deletions
+47 -76
View File
@@ -1,78 +1,6 @@
import React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react';
import type { ReactNode } from 'react';
export interface ThemeConfig {
primaryColor?: string;
accentColor?: string;
backgroundColor?: string;
textColor?: string;
fontFamily?: string;
borderRadius?: 'none' | 'sm' | 'md' | 'lg';
logoUrl?: string;
customCss?: string;
}
export interface EventTheme {
name: string;
config: ThemeConfig;
}
// Predefined themes
export const PRESET_THEMES: Record<string, EventTheme> = {
default: {
name: 'Default',
config: {
primaryColor: '#5C8762',
accentColor: '#22c55e',
backgroundColor: '#fafafa',
textColor: '#171717',
borderRadius: 'md',
}
},
wedding: {
name: 'Wedding',
config: {
primaryColor: '#c9a961',
accentColor: '#e6ddd4',
backgroundColor: '#fdfcfb',
textColor: '#3f3f3f',
borderRadius: 'lg',
fontFamily: 'Georgia, serif',
}
},
birthday: {
name: 'Birthday',
config: {
primaryColor: '#ec4899',
accentColor: '#fbbf24',
backgroundColor: '#fef3c7',
textColor: '#451a03',
borderRadius: 'lg',
}
},
corporate: {
name: 'Corporate',
config: {
primaryColor: '#3b82f6',
accentColor: '#1e40af',
backgroundColor: '#f8fafc',
textColor: '#0f172a',
borderRadius: 'sm',
fontFamily: 'Inter, sans-serif',
}
},
minimal: {
name: 'Minimal',
config: {
primaryColor: '#000000',
accentColor: '#666666',
backgroundColor: '#ffffff',
textColor: '#000000',
borderRadius: 'none',
fontFamily: 'Helvetica, Arial, sans-serif',
}
}
};
import { ThemeConfig, EventTheme, GALLERY_THEME_PRESETS } from '../types/theme.types';
interface ThemeContextType {
theme: ThemeConfig;
@@ -101,7 +29,7 @@ interface ThemeProviderProps {
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
children,
initialTheme = PRESET_THEMES.default.config,
initialTheme = GALLERY_THEME_PRESETS.default.config,
initialThemeName = 'default'
}) => {
const [theme, setTheme] = useState<ThemeConfig>(initialTheme);
@@ -134,6 +62,10 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
root.style.setProperty('--font-family', themeConfig.fontFamily);
}
if (themeConfig.headingFontFamily) {
root.style.setProperty('--heading-font-family', themeConfig.headingFontFamily);
}
if (themeConfig.borderRadius) {
const radiusMap = {
none: '0',
@@ -144,6 +76,41 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
root.style.setProperty('--border-radius', radiusMap[themeConfig.borderRadius]);
}
// Apply font size
if (themeConfig.fontSize) {
const sizeMap = {
small: '14px',
normal: '16px',
large: '18px',
};
root.style.setProperty('--font-size-base', sizeMap[themeConfig.fontSize]);
}
// Apply shadow style
if (themeConfig.shadowStyle) {
const shadowMap = {
none: 'none',
subtle: '0 1px 3px rgba(0,0,0,0.12)',
normal: '0 4px 6px rgba(0,0,0,0.1)',
dramatic: '0 10px 25px rgba(0,0,0,0.15)',
};
root.style.setProperty('--shadow-default', shadowMap[themeConfig.shadowStyle]);
}
// Apply background pattern
if (themeConfig.backgroundPattern && themeConfig.backgroundPattern !== 'none') {
const patternMap = {
dots: `radial-gradient(circle, ${themeConfig.textColor}20 1px, transparent 1px)`,
grid: `linear-gradient(${themeConfig.textColor}10 1px, transparent 1px), linear-gradient(90deg, ${themeConfig.textColor}10 1px, transparent 1px)`,
waves: `url("data:image/svg+xml,%3Csvg width='100' height='20' viewBox='0 0 100 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.184 20c.357-.13.72-.264 1.088-.402l1.768-.661C33.64 15.347 39.647 14 50 14c10.271 0 15.362 1.222 24.629 4.928.955.383 1.869.74 2.75 1.072h6.225c-2.51-.73-5.139-1.691-8.233-2.928C65.888 13.278 60.562 12 50 12c-10.626 0-16.855 1.397-26.66 5.063l-1.767.662c-2.475.923-4.66 1.674-6.724 2.275h6.335zm0-20C13.258 2.892 8.077 4 0 4V2c5.744 0 9.951-.574 14.85-2h6.334zM77.38 0C85.239 2.966 90.502 4 100 4V2c-6.842 0-11.386-.542-16.396-2h-6.225zM0 14c8.44 0 13.718-1.21 22.272-4.402l1.768-.661C33.64 5.347 39.647 4 50 4c10.271 0 15.362 1.222 24.629 4.928C84.112 12.722 89.438 14 100 14v-2c-10.271 0-15.362-1.222-24.629-4.928C65.888 3.278 60.562 2 50 2 39.374 2 33.145 3.397 23.34 7.063l-1.767.662C13.223 10.84 8.163 12 0 12v2z' fill='${themeConfig.textColor}' fill-opacity='0.05'/%3E%3C/svg%3E")`,
};
root.style.setProperty('--background-pattern', patternMap[themeConfig.backgroundPattern]);
root.style.setProperty('--background-pattern-size', themeConfig.backgroundPattern === 'dots' ? '20px 20px' : themeConfig.backgroundPattern === 'grid' ? '20px 20px' : '100px 20px');
} else {
root.style.removeProperty('--background-pattern');
root.style.removeProperty('--background-pattern-size');
}
// Apply custom CSS if provided
if (themeConfig.customCss) {
let styleElement = document.getElementById('custom-theme-styles');
@@ -162,7 +129,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
}, [applyTheme]);
const setThemeByName = useCallback((name: string) => {
const presetTheme = PRESET_THEMES[name];
const presetTheme = GALLERY_THEME_PRESETS[name];
if (presetTheme) {
setThemeName(name);
setTheme(presetTheme.config);
@@ -250,4 +217,8 @@ function darkenColor(color: string, percent: number): string {
return '#' + (0x1000000 + (R > 0 ? R : 0) * 0x10000 +
(G > 0 ? G : 0) * 0x100 +
(B > 0 ? B : 0)).toString(16).slice(1);
}
}
// Re-export types
export type { ThemeConfig, EventTheme };
export { GALLERY_THEME_PRESETS };
+2 -1
View File
@@ -1,5 +1,6 @@
export { GalleryAuthProvider, useGalleryAuth } from './GalleryAuthContext';
export { AdminAuthProvider, useAdminAuth } from './AdminAuthContext';
export { ThemeProvider, useTheme, PRESET_THEMES } from './ThemeContext';
export { ThemeProvider, useTheme, GALLERY_THEME_PRESETS } from './ThemeContext';
export type { ThemeConfig, EventTheme } from './ThemeContext';
export { GALLERY_THEME_PRESETS as PRESET_THEMES } from './ThemeContext'; // For backward compatibility
export { MaintenanceProvider, useMaintenanceMode } from './MaintenanceContext';