246 lines
7.4 KiB
TypeScript
246 lines
7.4 KiB
TypeScript
/**
|
||
* 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 "+ Add card" to create one.
|
||
</p>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default GallerySettingsSectionCompact;
|