constructor (UI elements basic)
This commit is contained in:
parent
b4fe0dde81
commit
1b5c13c8ae
@ -61,8 +61,11 @@ type CanvasElement = {
|
||||
label: string;
|
||||
xPercent: number;
|
||||
yPercent: number;
|
||||
iconUrl?: string;
|
||||
galleryCards?: GalleryCard[];
|
||||
carouselSlides?: CarouselSlide[];
|
||||
carouselPrevIconUrl?: string;
|
||||
carouselNextIconUrl?: string;
|
||||
tooltipTitle?: string;
|
||||
tooltipText?: string;
|
||||
descriptionTitle?: string;
|
||||
@ -258,12 +261,15 @@ const createDefaultElement = (type: CanvasElementType, index: number): CanvasEle
|
||||
return {
|
||||
...base,
|
||||
carouselSlides: [{ id: createLocalId(), imageUrl: '', caption: 'Slide 1' }],
|
||||
carouselPrevIconUrl: '',
|
||||
carouselNextIconUrl: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'tooltip') {
|
||||
return {
|
||||
...base,
|
||||
iconUrl: '',
|
||||
tooltipTitle: 'Tooltip title',
|
||||
tooltipText: 'Tooltip text',
|
||||
};
|
||||
@ -272,6 +278,7 @@ const createDefaultElement = (type: CanvasElementType, index: number): CanvasEle
|
||||
if (type === 'description') {
|
||||
return {
|
||||
...base,
|
||||
iconUrl: '',
|
||||
descriptionTitle: 'Description title',
|
||||
descriptionText: 'Description text',
|
||||
};
|
||||
@ -281,6 +288,7 @@ const createDefaultElement = (type: CanvasElementType, index: number): CanvasEle
|
||||
return {
|
||||
...base,
|
||||
navLabel: type === 'navigation_next' ? 'Forward' : 'Back',
|
||||
iconUrl: '',
|
||||
transitionReverseMode: 'auto_reverse',
|
||||
transitionDurationSec: 0.7,
|
||||
};
|
||||
@ -429,6 +437,13 @@ const ConstructorPage = () => {
|
||||
|
||||
return videoAssetOptions;
|
||||
}, [assets, videoAssetOptions]);
|
||||
const iconAssetOptions = useMemo(
|
||||
() =>
|
||||
assets
|
||||
.filter((asset) => asset.type === 'icon' && asset.asset_type === 'image' && getAssetSourceValue(asset))
|
||||
.map((asset) => ({ value: getAssetSourceValue(asset), label: getAssetLabel(asset) })),
|
||||
[assets],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (newTransitionVideoUrl) return;
|
||||
@ -570,6 +585,9 @@ const ConstructorPage = () => {
|
||||
caption: String(slide?.caption || `Slide ${index + 1}`),
|
||||
}))
|
||||
: undefined,
|
||||
iconUrl: typeof item.iconUrl === 'string' ? item.iconUrl : '',
|
||||
carouselPrevIconUrl: typeof item.carouselPrevIconUrl === 'string' ? item.carouselPrevIconUrl : '',
|
||||
carouselNextIconUrl: typeof item.carouselNextIconUrl === 'string' ? item.carouselNextIconUrl : '',
|
||||
tooltipTitle: typeof item.tooltipTitle === 'string' ? item.tooltipTitle : '',
|
||||
tooltipText: typeof item.tooltipText === 'string' ? item.tooltipText : '',
|
||||
descriptionTitle: typeof item.descriptionTitle === 'string' ? item.descriptionTitle : '',
|
||||
@ -951,7 +969,13 @@ const ConstructorPage = () => {
|
||||
const targetPageName = element.targetPageId ? pageNameById[element.targetPageId] : '';
|
||||
return (
|
||||
<div className='flex flex-col items-start gap-1'>
|
||||
<span>{element.navLabel || (element.type === 'navigation_next' ? 'Forward' : 'Back')}</span>
|
||||
<div className='flex items-center gap-2'>
|
||||
{element.iconUrl ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img src={resolveAssetPlaybackUrl(element.iconUrl)} alt='Navigation icon' className='h-4 w-4 object-contain' />
|
||||
) : null}
|
||||
<span>{element.navLabel || (element.type === 'navigation_next' ? 'Forward' : 'Back')}</span>
|
||||
</div>
|
||||
{targetPageName ? <span className='text-[10px] text-gray-500'>To: {targetPageName}</span> : null}
|
||||
</div>
|
||||
);
|
||||
@ -960,6 +984,10 @@ const ConstructorPage = () => {
|
||||
if (element.type === 'tooltip') {
|
||||
return (
|
||||
<div className='max-w-[200px] text-left'>
|
||||
{element.iconUrl ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img src={resolveAssetPlaybackUrl(element.iconUrl)} alt='Tooltip icon' className='mb-1 h-5 w-5 object-contain' />
|
||||
) : null}
|
||||
<p className='text-[11px] font-bold'>{element.tooltipTitle || 'Tooltip title'}</p>
|
||||
<p className='text-[10px] text-gray-600 line-clamp-3'>{element.tooltipText || 'Tooltip text'}</p>
|
||||
</div>
|
||||
@ -969,6 +997,10 @@ const ConstructorPage = () => {
|
||||
if (element.type === 'description') {
|
||||
return (
|
||||
<div className='max-w-[220px] text-left'>
|
||||
{element.iconUrl ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img src={resolveAssetPlaybackUrl(element.iconUrl)} alt='Description icon' className='mb-1 h-5 w-5 object-contain' />
|
||||
) : null}
|
||||
<p className='text-[11px] font-bold'>{element.descriptionTitle || 'Description title'}</p>
|
||||
<p className='text-[10px] text-gray-600 line-clamp-4'>{element.descriptionText || 'Description text'}</p>
|
||||
</div>
|
||||
@ -1018,6 +1050,32 @@ const ConstructorPage = () => {
|
||||
)}
|
||||
</div>
|
||||
<p className='mt-1 text-[10px] text-gray-600 line-clamp-1'>{firstSlide?.caption || 'No caption'}</p>
|
||||
{(element.carouselPrevIconUrl || element.carouselNextIconUrl) && (
|
||||
<div className='mt-1 flex items-center justify-between text-[9px] text-gray-500'>
|
||||
<span className='flex items-center gap-1'>
|
||||
{element.carouselPrevIconUrl ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={resolveAssetPlaybackUrl(element.carouselPrevIconUrl)}
|
||||
alt='Previous icon'
|
||||
className='h-3 w-3 object-contain'
|
||||
/>
|
||||
) : null}
|
||||
Prev
|
||||
</span>
|
||||
<span className='flex items-center gap-1'>
|
||||
Next
|
||||
{element.carouselNextIconUrl ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={resolveAssetPlaybackUrl(element.carouselNextIconUrl)}
|
||||
alt='Next icon'
|
||||
className='h-3 w-3 object-contain'
|
||||
/>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1419,6 +1477,25 @@ const ConstructorPage = () => {
|
||||
onChange={(event) => updateSelectedElement({ navLabel: event.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>Icon</label>
|
||||
<select
|
||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||
value={selectedElement.iconUrl || ''}
|
||||
onChange={(event) => updateSelectedElement({ iconUrl: event.target.value })}
|
||||
>
|
||||
<option value=''>Not selected</option>
|
||||
{addFallbackAssetOption(
|
||||
iconAssetOptions,
|
||||
selectedElement.iconUrl,
|
||||
`Current icon · ${selectedElement.iconUrl || ''}`,
|
||||
).map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>Target page</label>
|
||||
<select
|
||||
@ -1511,6 +1588,25 @@ const ConstructorPage = () => {
|
||||
|
||||
{selectedElement && selectedElement.type === 'tooltip' && (
|
||||
<div className='space-y-2'>
|
||||
<div>
|
||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>Icon</label>
|
||||
<select
|
||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||
value={selectedElement.iconUrl || ''}
|
||||
onChange={(event) => updateSelectedElement({ iconUrl: event.target.value })}
|
||||
>
|
||||
<option value=''>Not selected</option>
|
||||
{addFallbackAssetOption(
|
||||
iconAssetOptions,
|
||||
selectedElement.iconUrl,
|
||||
`Current icon · ${selectedElement.iconUrl || ''}`,
|
||||
).map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>Tooltip title</label>
|
||||
<input
|
||||
@ -1533,6 +1629,25 @@ const ConstructorPage = () => {
|
||||
|
||||
{selectedElement && selectedElement.type === 'description' && (
|
||||
<div className='space-y-2'>
|
||||
<div>
|
||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>Icon</label>
|
||||
<select
|
||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||
value={selectedElement.iconUrl || ''}
|
||||
onChange={(event) => updateSelectedElement({ iconUrl: event.target.value })}
|
||||
>
|
||||
<option value=''>Not selected</option>
|
||||
{addFallbackAssetOption(
|
||||
iconAssetOptions,
|
||||
selectedElement.iconUrl,
|
||||
`Current icon · ${selectedElement.iconUrl || ''}`,
|
||||
).map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>Description title</label>
|
||||
<input
|
||||
@ -1653,6 +1768,41 @@ const ConstructorPage = () => {
|
||||
|
||||
{selectedElement && selectedElement.type === 'carousel' && (
|
||||
<div className='space-y-2'>
|
||||
<div className='rounded border border-gray-200 p-2 space-y-2'>
|
||||
<p className='text-[11px] font-semibold text-gray-700'>Navigation icons</p>
|
||||
<select
|
||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||
value={selectedElement.carouselPrevIconUrl || ''}
|
||||
onChange={(event) => updateSelectedElement({ carouselPrevIconUrl: event.target.value })}
|
||||
>
|
||||
<option value=''>Previous icon</option>
|
||||
{addFallbackAssetOption(
|
||||
iconAssetOptions,
|
||||
selectedElement.carouselPrevIconUrl,
|
||||
`Current prev icon · ${selectedElement.carouselPrevIconUrl || ''}`,
|
||||
).map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||
value={selectedElement.carouselNextIconUrl || ''}
|
||||
onChange={(event) => updateSelectedElement({ carouselNextIconUrl: event.target.value })}
|
||||
>
|
||||
<option value=''>Next icon</option>
|
||||
{addFallbackAssetOption(
|
||||
iconAssetOptions,
|
||||
selectedElement.carouselNextIconUrl,
|
||||
`Current next icon · ${selectedElement.carouselNextIconUrl || ''}`,
|
||||
).map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className='flex items-center justify-between'>
|
||||
<p className='text-[11px] font-semibold text-gray-600'>Carousel slides</p>
|
||||
<button type='button' className='text-xs text-blue-700 hover:underline' onClick={addCarouselSlide}>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user