Autosave: 20260317-153203
This commit is contained in:
parent
42684051c3
commit
2680417aae
@ -109,7 +109,7 @@ type TransitionPreviewState = {
|
|||||||
title: string;
|
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 => {
|
const parseJsonObject = <T,>(value?: string, fallback?: T): T => {
|
||||||
if (!value) return (fallback || ({} as T)) as T;
|
if (!value) return (fallback || ({} as T)) as T;
|
||||||
@ -334,6 +334,12 @@ const ConstructorPage = () => {
|
|||||||
return String(value || '');
|
return String(value || '');
|
||||||
}, [router.query.pageId]);
|
}, [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 [pages, setPages] = useState<TourPage[]>([]);
|
||||||
const [assets, setAssets] = useState<ProjectAsset[]>([]);
|
const [assets, setAssets] = useState<ProjectAsset[]>([]);
|
||||||
const [activePageId, setActivePageId] = useState('');
|
const [activePageId, setActivePageId] = useState('');
|
||||||
@ -369,6 +375,7 @@ const ConstructorPage = () => {
|
|||||||
const transitionVideoRef = useRef<HTMLVideoElement | null>(null);
|
const transitionVideoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
const reverseAnimationFrame = useRef<number | null>(null);
|
const reverseAnimationFrame = useRef<number | null>(null);
|
||||||
const didSetInitialCanvasFocus = useRef(false);
|
const didSetInitialCanvasFocus = useRef(false);
|
||||||
|
const didHandleSourceType = useRef(false);
|
||||||
|
|
||||||
const activePage = useMemo(() => pages.find((item) => item.id === activePageId) || null, [activePageId, pages]);
|
const activePage = useMemo(() => pages.find((item) => item.id === activePageId) || null, [activePageId, pages]);
|
||||||
const pageNameById = useMemo(() => {
|
const pageNameById = useMemo(() => {
|
||||||
@ -527,6 +534,18 @@ const ConstructorPage = () => {
|
|||||||
});
|
});
|
||||||
}, [isAuthReady, isLoading, router.isReady]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
if (!activePage) {
|
if (!activePage) {
|
||||||
setElements([]);
|
setElements([]);
|
||||||
@ -1051,6 +1070,50 @@ const ConstructorPage = () => {
|
|||||||
return getElementButtonTitle(element);
|
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 canvasBackgroundStyle: React.CSSProperties = {};
|
||||||
const backgroundImageSrc = resolveAssetPlaybackUrl(backgroundImageUrl);
|
const backgroundImageSrc = resolveAssetPlaybackUrl(backgroundImageUrl);
|
||||||
const backgroundVideoSrc = resolveAssetPlaybackUrl(backgroundVideoUrl);
|
const backgroundVideoSrc = resolveAssetPlaybackUrl(backgroundVideoUrl);
|
||||||
@ -1070,7 +1133,9 @@ const ConstructorPage = () => {
|
|||||||
? 'Background video'
|
? 'Background video'
|
||||||
: selectedMenuItem === 'background_audio'
|
: selectedMenuItem === 'background_audio'
|
||||||
? 'Background audio'
|
? 'Background audio'
|
||||||
: selectedElement?.label || 'Element editor';
|
: selectedMenuItem === 'create_transition'
|
||||||
|
? 'Create transition'
|
||||||
|
: selectedElement?.label || 'Element editor';
|
||||||
|
|
||||||
if (backgroundImageSrc) {
|
if (backgroundImageSrc) {
|
||||||
canvasBackgroundStyle.backgroundImage = `url("${backgroundImageSrc}")`;
|
canvasBackgroundStyle.backgroundImage = `url("${backgroundImageSrc}")`;
|
||||||
@ -1339,6 +1404,8 @@ const ConstructorPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{selectedMenuItem === 'create_transition' && renderCreateTransitionForm()}
|
||||||
|
|
||||||
{selectedElement && (
|
{selectedElement && (
|
||||||
<div className='mb-2'>
|
<div className='mb-2'>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>Label</label>
|
<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 Forward' onClick={() => openTransitionPreview('forward')} />
|
||||||
<BaseButton small color='lightDark' label='Preview Back' onClick={() => openTransitionPreview('back')} />
|
<BaseButton small color='lightDark' label='Preview Back' onClick={() => openTransitionPreview('back')} />
|
||||||
</div>
|
</div>
|
||||||
<div className='rounded border border-gray-200 p-2 space-y-2'>
|
{renderCreateTransitionForm()}
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -1729,6 +1751,10 @@ const ConstructorPage = () => {
|
|||||||
<BaseIcon path={mdiSwapHorizontal} size={16} />
|
<BaseIcon path={mdiSwapHorizontal} size={16} />
|
||||||
<span>Add Navigation</span>
|
<span>Add Navigation</span>
|
||||||
</button>
|
</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')}>
|
<button type='button' className='menu-action-btn' onClick={() => addElement('gallery')}>
|
||||||
<BaseIcon path={mdiImageMultiple} size={16} />
|
<BaseIcon path={mdiImageMultiple} size={16} />
|
||||||
<span>Add Gallery</span>
|
<span>Add Gallery</span>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user