196 lines
5.8 KiB
TypeScript
196 lines
5.8 KiB
TypeScript
/**
|
|
* ConstructorMenu Component
|
|
*
|
|
* Draggable menu panel with actions for adding elements, backgrounds, etc.
|
|
*/
|
|
|
|
import React, { forwardRef } from 'react';
|
|
import BaseIcon from '../BaseIcon';
|
|
import BaseButton from '../BaseButton';
|
|
import {
|
|
mdiMenu,
|
|
mdiImageMultiple,
|
|
mdiViewCarousel,
|
|
mdiTooltipText,
|
|
mdiSwapHorizontal,
|
|
mdiText,
|
|
mdiPlus,
|
|
mdiExitToApp,
|
|
} from '@mdi/js';
|
|
import MenuActionButton from './MenuActionButton';
|
|
import dataFormatter from '../../helpers/dataFormatter';
|
|
import type {
|
|
Position,
|
|
CanvasElementType,
|
|
NavigationElementType,
|
|
} from './types';
|
|
import type { EditorMenuItem } from '../../types/constructor';
|
|
|
|
interface ConstructorMenuProps {
|
|
position: Position;
|
|
isOpen: boolean;
|
|
allowedNavigationTypes: NavigationElementType[];
|
|
isCreatingPage: boolean;
|
|
isSaving: boolean;
|
|
isSavingToStage: boolean;
|
|
onDragStart: (event: React.MouseEvent) => void;
|
|
onToggleOpen: () => void;
|
|
onSelectMenuItem: (item: EditorMenuItem) => void;
|
|
onAddElement: (type: CanvasElementType) => void;
|
|
onCreatePage: () => void;
|
|
onSave: () => void;
|
|
onSaveToStage: () => void;
|
|
onExit: () => void;
|
|
/** Page's last saved timestamp (updatedAt from tour_pages) */
|
|
lastSavedAt?: string | null;
|
|
/** Last save-to-stage timestamp */
|
|
lastSavedToStageAt?: string | null;
|
|
}
|
|
|
|
const ConstructorMenu = forwardRef<HTMLDivElement, ConstructorMenuProps>(
|
|
(
|
|
{
|
|
position,
|
|
isOpen,
|
|
allowedNavigationTypes,
|
|
isCreatingPage,
|
|
isSaving,
|
|
isSavingToStage,
|
|
onDragStart,
|
|
onToggleOpen,
|
|
onSelectMenuItem,
|
|
onAddElement,
|
|
onCreatePage,
|
|
onSave,
|
|
onSaveToStage,
|
|
onExit,
|
|
lastSavedAt,
|
|
lastSavedToStageAt,
|
|
},
|
|
ref,
|
|
) => {
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className='fixed z-[1000] w-60 border border-gray-200 rounded-lg bg-white shadow-xl'
|
|
style={{ left: position.x, top: position.y }}
|
|
>
|
|
<div
|
|
className='flex items-center justify-between px-3 py-2 border-b border-gray-200 cursor-move bg-gray-50 rounded-t-lg'
|
|
onMouseDown={onDragStart}
|
|
>
|
|
<span className='text-xs font-bold uppercase'>Constructor Menu</span>
|
|
<button type='button' onClick={onToggleOpen}>
|
|
<BaseIcon path={mdiMenu} size={18} />
|
|
</button>
|
|
</div>
|
|
|
|
{isOpen && (
|
|
<div className='p-2 space-y-1 max-h-[calc(100vh-120px)] overflow-y-auto'>
|
|
<MenuActionButton
|
|
icon={mdiImageMultiple}
|
|
label='Background Image'
|
|
onClick={() => onSelectMenuItem('background_image')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiViewCarousel}
|
|
label='Background Video'
|
|
onClick={() => onSelectMenuItem('background_video')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiTooltipText}
|
|
label='Background Audio'
|
|
onClick={() => onSelectMenuItem('background_audio')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiSwapHorizontal}
|
|
label='Add Navigation Button'
|
|
onClick={() => onAddElement(allowedNavigationTypes[0])}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiSwapHorizontal}
|
|
label='Add Transition'
|
|
onClick={() => onSelectMenuItem('create_transition')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiImageMultiple}
|
|
label='Add Gallery'
|
|
onClick={() => onAddElement('gallery')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiViewCarousel}
|
|
label='Add Carousel'
|
|
onClick={() => onAddElement('carousel')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiTooltipText}
|
|
label='Add Tooltip'
|
|
onClick={() => onAddElement('tooltip')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiText}
|
|
label='Add Description'
|
|
onClick={() => onAddElement('description')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiViewCarousel}
|
|
label='Add Video Player'
|
|
onClick={() => onAddElement('video_player')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiTooltipText}
|
|
label='Add Audio Player'
|
|
onClick={() => onAddElement('audio_player')}
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiPlus}
|
|
label={isCreatingPage ? 'Creating Page...' : 'Create New Page'}
|
|
onClick={onCreatePage}
|
|
disabled={isCreatingPage}
|
|
/>
|
|
|
|
<div className='pt-2 border-t border-gray-200 space-y-1'>
|
|
<BaseButton
|
|
small
|
|
color='info'
|
|
label={isSaving ? 'Saving...' : 'Save'}
|
|
subtitle={
|
|
lastSavedAt
|
|
? dataFormatter.relativeTimestamp(lastSavedAt)
|
|
: undefined
|
|
}
|
|
onClick={onSave}
|
|
disabled={isSaving}
|
|
className='w-full'
|
|
/>
|
|
<BaseButton
|
|
small
|
|
color='success'
|
|
label={isSavingToStage ? 'Saving...' : 'Save to Stage'}
|
|
subtitle={
|
|
lastSavedToStageAt
|
|
? dataFormatter.relativeTimestamp(lastSavedToStageAt)
|
|
: undefined
|
|
}
|
|
onClick={onSaveToStage}
|
|
disabled={isSavingToStage}
|
|
className='w-full'
|
|
/>
|
|
<MenuActionButton
|
|
icon={mdiExitToApp}
|
|
label='Exit'
|
|
onClick={onExit}
|
|
className='!text-red-700'
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
);
|
|
|
|
ConstructorMenu.displayName = 'ConstructorMenu';
|
|
|
|
export default ConstructorMenu;
|