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