39948-vm/frontend/src/components/ElementSettings/GallerySettingsSectionCompact.tsx
2026-05-05 17:25:53 +02:00

246 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* GallerySettingsSectionCompact
*
* Compact gallery element settings for constructor sidebar.
* Header image, title, info spans, and card management.
* Note: Fonts, columns, and styling are now in the CSS tab.
*/
import React from 'react';
import type {
GalleryCard,
GalleryInfoSpan,
AssetOption,
} from '../../types/constructor';
import { addFallbackAssetOption } from '../../lib/constructorHelpers';
interface GallerySettingsSectionCompactProps {
// Header settings
galleryHeaderImageUrl: string;
galleryHeaderText: string;
galleryTitle: string;
galleryInfoSpans: GalleryInfoSpan[];
// Cards
galleryCards: GalleryCard[];
imageAssetOptions: AssetOption[];
iconAssetOptions: AssetOption[];
// Header handlers
onUpdateHeader: (patch: {
galleryHeaderImageUrl?: string;
galleryHeaderText?: string;
galleryTitle?: string;
}) => void;
onAddInfoSpan: () => void;
onUpdateInfoSpan: (spanId: string, patch: Partial<GalleryInfoSpan>) => void;
onRemoveInfoSpan: (spanId: string) => void;
// Card handlers
onAddCard: () => void;
onUpdateCard: (cardId: string, patch: Partial<GalleryCard>) => void;
onRemoveCard: (cardId: string) => void;
}
const GallerySettingsSectionCompact: React.FC<
GallerySettingsSectionCompactProps
> = ({
galleryHeaderImageUrl,
galleryHeaderText,
galleryTitle,
galleryInfoSpans,
galleryCards,
imageAssetOptions,
iconAssetOptions,
onUpdateHeader,
onAddInfoSpan,
onUpdateInfoSpan,
onRemoveInfoSpan,
onAddCard,
onUpdateCard,
onRemoveCard,
}) => {
return (
<div className='space-y-3'>
{/* Header Settings */}
<div className='rounded border border-white/20 p-2 space-y-2'>
<p className='text-[11px] font-semibold text-white/90'>
Gallery header
</p>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={galleryHeaderImageUrl}
onChange={(event) =>
onUpdateHeader({ galleryHeaderImageUrl: event.target.value })
}
>
<option value=''>Header image</option>
{addFallbackAssetOption(
imageAssetOptions,
galleryHeaderImageUrl,
`Current header`,
).map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
placeholder='Header text (if no image)'
value={galleryHeaderText}
onChange={(event) =>
onUpdateHeader({ galleryHeaderText: event.target.value })
}
/>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
placeholder='Title (location note)'
value={galleryTitle}
onChange={(event) =>
onUpdateHeader({ galleryTitle: event.target.value })
}
/>
</div>
{/* Info Spans */}
<div className='rounded border border-white/20 p-2 space-y-2'>
<div className='flex items-center justify-between'>
<p className='text-[11px] font-semibold text-white/90'>Info spans</p>
<button
type='button'
className='text-xs text-blue-700 hover:underline'
onClick={onAddInfoSpan}
>
+ Add span
</button>
</div>
{galleryInfoSpans.map((span, index) => (
<div key={span.id} className='space-y-1'>
<div className='flex items-center gap-1'>
<input
className='flex-1 rounded border border-gray-300 px-2 py-1 text-xs'
placeholder={`Span ${index + 1} text`}
value={span.text}
onChange={(event) =>
onUpdateInfoSpan(span.id, { text: event.target.value })
}
/>
<button
type='button'
className='text-xs text-red-600 hover:underline px-1'
onClick={() => onRemoveInfoSpan(span.id)}
>
×
</button>
</div>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={span.iconUrl || ''}
onChange={(event) =>
onUpdateInfoSpan(span.id, { iconUrl: event.target.value })
}
>
<option value=''>No icon (use text)</option>
{addFallbackAssetOption(
iconAssetOptions,
span.iconUrl,
`Current icon`,
).map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
))}
{galleryInfoSpans.length === 0 && (
<p className='text-[10px] text-white/60'>
Add spans for brief notes (capacity, price, icons, etc.)
</p>
)}
</div>
{/* Gallery Cards */}
<div className='flex items-center justify-between'>
<p className='text-[11px] font-semibold text-white/80'>Gallery cards</p>
<button
type='button'
className='text-xs text-blue-700 hover:underline'
onClick={onAddCard}
>
+ Add card
</button>
</div>
{galleryCards.map((card, index) => (
<div
key={card.id}
className='rounded border border-white/20 p-2 space-y-2'
>
<div className='flex items-center justify-between'>
<p className='text-[11px] font-semibold text-white/90'>
Card {index + 1}
</p>
<button
type='button'
className='text-xs text-red-600 hover:underline'
onClick={() => onRemoveCard(card.id)}
>
Remove
</button>
</div>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={card.imageUrl}
onChange={(event) =>
onUpdateCard(card.id, { imageUrl: event.target.value })
}
>
<option value=''>Image asset</option>
{addFallbackAssetOption(
imageAssetOptions,
card.imageUrl,
`Current image · ${card.imageUrl}`,
).map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
placeholder='Title'
value={card.title}
onChange={(event) =>
onUpdateCard(card.id, { title: event.target.value })
}
/>
<textarea
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
placeholder='Description'
rows={3}
value={card.description}
onChange={(event) =>
onUpdateCard(card.id, { description: event.target.value })
}
/>
</div>
))}
{galleryCards.length === 0 && (
<p className='text-[11px] text-white/60'>
No cards yet. Click &quot;+ Add card&quot; to create one.
</p>
)}
</div>
);
};
export default GallerySettingsSectionCompact;