Autosave: 20260317-153203

This commit is contained in:
Flatlogic Bot 2026-03-17 15:32:03 +00:00
parent 42684051c3
commit 2680417aae

View File

@ -109,7 +109,7 @@ type TransitionPreviewState = {
title: string;
};
type EditorMenuItem = 'none' | 'background_image' | 'background_video' | 'background_audio';
type EditorMenuItem = 'none' | 'background_image' | 'background_video' | 'background_audio' | 'create_transition';
const parseJsonObject = <T,>(value?: string, fallback?: T): T => {
if (!value) return (fallback || ({} as T)) as T;
@ -334,6 +334,12 @@ const ConstructorPage = () => {
return String(value || '');
}, [router.query.pageId]);
const sourceTypeFromRoute = useMemo(() => {
const value = router.query.sourceType;
if (Array.isArray(value)) return value[0] || '';
return String(value || '');
}, [router.query.sourceType]);
const [pages, setPages] = useState<TourPage[]>([]);
const [assets, setAssets] = useState<ProjectAsset[]>([]);
const [activePageId, setActivePageId] = useState('');
@ -369,6 +375,7 @@ const ConstructorPage = () => {
const transitionVideoRef = useRef<HTMLVideoElement | null>(null);
const reverseAnimationFrame = useRef<number | null>(null);
const didSetInitialCanvasFocus = useRef(false);
const didHandleSourceType = useRef(false);
const activePage = useMemo(() => pages.find((item) => item.id === activePageId) || null, [activePageId, pages]);
const pageNameById = useMemo(() => {
@ -527,6 +534,18 @@ const ConstructorPage = () => {
});
}, [isAuthReady, isLoading, router.isReady]);
useEffect(() => {
if (!router.isReady || isLoading) return;
if (didHandleSourceType.current) return;
didHandleSourceType.current = true;
if (sourceTypeFromRoute !== 'transition') return;
setSelectedElementId('');
setSelectedMenuItem('create_transition');
setIsMenuOpen(true);
}, [isLoading, router.isReady, sourceTypeFromRoute]);
useEffect(() => {
if (!activePage) {
setElements([]);
@ -1051,6 +1070,50 @@ const ConstructorPage = () => {
return getElementButtonTitle(element);
};
const renderCreateTransitionForm = () => (
<div className='rounded border border-gray-200 p-2 space-y-2'>
<p className='text-[11px] font-semibold text-gray-600'>Create next page transition</p>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
placeholder='Name'
value={newTransitionName}
onChange={(event) => setNewTransitionName(event.target.value)}
/>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={newTransitionVideoUrl}
onChange={(event) => setNewTransitionVideoUrl(event.target.value)}
>
<option value=''>Transition video asset</option>
{transitionVideoAssetOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<label className='flex items-center gap-2 text-[11px] text-gray-700'>
<input
type='checkbox'
checked={newTransitionSupportsReverse}
onChange={(event) => setNewTransitionSupportsReverse(event.target.checked)}
/>
Supports reverse playback
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
type='number'
min='0.2'
step='0.1'
value={newTransitionDurationSec}
onChange={(event) => setNewTransitionDurationSec(Number(event.target.value || 0.7))}
/>
<button type='button' className='menu-action-btn' onClick={createTransition} disabled={isCreatingTransition}>
<BaseIcon path={mdiSwapHorizontal} size={16} />
<span>{isCreatingTransition ? 'Creating Transition...' : 'Create Transition'}</span>
</button>
</div>
);
const canvasBackgroundStyle: React.CSSProperties = {};
const backgroundImageSrc = resolveAssetPlaybackUrl(backgroundImageUrl);
const backgroundVideoSrc = resolveAssetPlaybackUrl(backgroundVideoUrl);
@ -1070,7 +1133,9 @@ const ConstructorPage = () => {
? 'Background video'
: selectedMenuItem === 'background_audio'
? 'Background audio'
: selectedElement?.label || 'Element editor';
: selectedMenuItem === 'create_transition'
? 'Create transition'
: selectedElement?.label || 'Element editor';
if (backgroundImageSrc) {
canvasBackgroundStyle.backgroundImage = `url("${backgroundImageSrc}")`;
@ -1339,6 +1404,8 @@ const ConstructorPage = () => {
</div>
)}
{selectedMenuItem === 'create_transition' && renderCreateTransitionForm()}
{selectedElement && (
<div className='mb-2'>
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>Label</label>
@ -1447,52 +1514,7 @@ const ConstructorPage = () => {
<BaseButton small color='lightDark' label='Preview Forward' onClick={() => openTransitionPreview('forward')} />
<BaseButton small color='lightDark' label='Preview Back' onClick={() => openTransitionPreview('back')} />
</div>
<div className='rounded border border-gray-200 p-2 space-y-2'>
<p className='text-[11px] font-semibold text-gray-600'>Create next page transition</p>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
placeholder='Name'
value={newTransitionName}
onChange={(event) => setNewTransitionName(event.target.value)}
/>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={newTransitionVideoUrl}
onChange={(event) => setNewTransitionVideoUrl(event.target.value)}
>
<option value=''>Transition video asset</option>
{transitionVideoAssetOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<label className='flex items-center gap-2 text-[11px] text-gray-700'>
<input
type='checkbox'
checked={newTransitionSupportsReverse}
onChange={(event) => setNewTransitionSupportsReverse(event.target.checked)}
/>
Supports reverse playback
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
type='number'
min='0.2'
step='0.1'
value={newTransitionDurationSec}
onChange={(event) => setNewTransitionDurationSec(Number(event.target.value || 0.7))}
/>
<button
type='button'
className='menu-action-btn'
onClick={createTransition}
disabled={isCreatingTransition}
>
<BaseIcon path={mdiSwapHorizontal} size={16} />
<span>{isCreatingTransition ? 'Creating Transition...' : 'Create Transition'}</span>
</button>
</div>
{renderCreateTransitionForm()}
</div>
)}
@ -1729,6 +1751,10 @@ const ConstructorPage = () => {
<BaseIcon path={mdiSwapHorizontal} size={16} />
<span>Add Navigation</span>
</button>
<button type='button' className='menu-action-btn' onClick={() => selectMenuItemForEdit('create_transition')}>
<BaseIcon path={mdiSwapHorizontal} size={16} />
<span>Add Transition</span>
</button>
<button type='button' className='menu-action-btn' onClick={() => addElement('gallery')}>
<BaseIcon path={mdiImageMultiple} size={16} />
<span>Add Gallery</span>