From f554f463b3492346dba067c0980b52ef42dd5e70 Mon Sep 17 00:00:00 2001 From: Paul Nothaft Date: Mon, 2 Feb 2026 23:09:36 +0100 Subject: [PATCH] fix: hero header state and preview in admin theme editor (#158) - Add hero header rendering to GalleryPreview component with divider styles - Support event-specific header_style prop in GalleryLayout - Pass header_style from event data to GalleryLayout in GalleryView - Divider options now properly show/hide when switching header styles This ensures the live preview accurately reflects hero header changes and event-specific header styles are respected in the gallery view. --- .../src/components/admin/GalleryPreview.tsx | 163 ++++++++++++++---- .../src/components/gallery/GalleryLayout.tsx | 6 +- .../src/components/gallery/GalleryView.tsx | 1 + 3 files changed, 131 insertions(+), 39 deletions(-) diff --git a/frontend/src/components/admin/GalleryPreview.tsx b/frontend/src/components/admin/GalleryPreview.tsx index ac7d4c71..b2fa2e92 100644 --- a/frontend/src/components/admin/GalleryPreview.tsx +++ b/frontend/src/components/admin/GalleryPreview.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; -import { Camera } from 'lucide-react'; -import { ThemeConfig, GalleryLayoutType } from '../../types/theme.types'; +import { Camera, Calendar } from 'lucide-react'; +import { ThemeConfig, GalleryLayoutType, HeroDividerStyle } from '../../types/theme.types'; import { buildResourceUrl } from '../../utils/url'; interface GalleryPreviewBranding { @@ -92,6 +92,39 @@ export const GalleryPreview: React.FC = ({ ? 'justify-end text-right flex-row-reverse' : 'justify-start text-left'; + // Check if hero header style is selected + const isHeroHeader = theme.headerStyle === 'hero'; + const heroDividerStyle: HeroDividerStyle = theme.heroDividerStyle || 'wave'; + + // Render hero divider based on style + const renderHeroDivider = () => { + const bgColor = theme.backgroundColor || '#fafafa'; + switch (heroDividerStyle) { + case 'wave': + return ( + + + + ); + case 'curve': + return ( + + + + ); + case 'angle': + return ( + + + + ); + case 'straight': + case 'none': + default: + return null; + } + }; + const renderLayout = () => { const spacing = theme.gallerySettings?.spacing || 'normal'; const gapClass = spacing === 'tight' ? 'gap-1' : spacing === 'relaxed' ? 'gap-4' : 'gap-2'; @@ -169,7 +202,7 @@ export const GalleryPreview: React.FC = ({ }; return ( -
= ({ fontFamily: theme.fontFamily || 'Inter, sans-serif', }} > - {/* Preview Header */} -
-
- {showLogo && ( - resolvedLogoUrl ? ( - {brandName} - ) : ( -
- -
- ) - )} - {showText && ( -
-

{brandName}

- {brandTagline && ( -

{brandTagline}

+ {/* Hero Header - shown when headerStyle is 'hero' */} + {isHeroHeader && ( +
+
+
+ {/* Logo in Hero */} + {showLogo && ( +
+ {resolvedLogoUrl ? ( + {brandName} + ) : ( +
+ +
+ )} +
)} + {/* Event Name */} +

+ Sample Event +

+ {/* Event Date */} +
+ + January 15, 2026 +
- )} - {!showLogo && !showText && ( -

{brandName}

- )} +
+ {/* Divider */} +
+ {renderHeroDivider()} +
-
- Gallery preview - {activeLayout} layout + )} + + {/* Standard Header - shown when headerStyle is NOT 'hero' */} + {!isHeroHeader && ( +
+
+ {showLogo && ( + resolvedLogoUrl ? ( + {brandName} + ) : ( +
+ +
+ ) + )} + {showText && ( +
+

{brandName}

+ {brandTagline && ( +

{brandTagline}

+ )} +
+ )} + {!showLogo && !showText && ( +

{brandName}

+ )} +
+ )} + + {/* Layout info bar */} +
+ Gallery preview + {isHeroHeader ? `Hero + ${activeLayout}` : `${activeLayout} layout`}
- + {/* Preview Content */}
{renderLayout()} diff --git a/frontend/src/components/gallery/GalleryLayout.tsx b/frontend/src/components/gallery/GalleryLayout.tsx index efa4bd16..d9d5ec3d 100644 --- a/frontend/src/components/gallery/GalleryLayout.tsx +++ b/frontend/src/components/gallery/GalleryLayout.tsx @@ -39,6 +39,7 @@ interface GalleryLayoutProps { isDownloading?: boolean; headerExtra?: React.ReactNode; menuButton?: React.ReactNode; + headerStyle?: HeaderStyleType; children: React.ReactNode; } @@ -52,14 +53,15 @@ export const GalleryLayout: React.FC = ({ isDownloading = false, headerExtra, menuButton, + headerStyle: headerStyleProp, children, }) => { const { t } = useTranslation(); const { format } = useLocalizedDate(); const { theme } = useTheme(); - // Determine header style - check theme.headerStyle first, then fall back to legacy behavior - const headerStyle: HeaderStyleType = theme.headerStyle || 'standard'; + // Determine header style - use prop first (from event data), then theme, then fall back to 'standard' + const headerStyle: HeaderStyleType = headerStyleProp || theme.headerStyle || 'standard'; const isHeroHeader = headerStyle === 'hero'; // Non-grid layouts that need the sidebar (excluding layouts using hero header) diff --git a/frontend/src/components/gallery/GalleryView.tsx b/frontend/src/components/gallery/GalleryView.tsx index 0083f2f4..c71d19e0 100644 --- a/frontend/src/components/gallery/GalleryView.tsx +++ b/frontend/src/components/gallery/GalleryView.tsx @@ -584,6 +584,7 @@ export const GalleryView: React.FC = ({ slug, event }) => {