import React from 'react';
import { AbsoluteFill, useCurrentFrame } from 'remotion';
interface VerticalLayoutProps {
topContent?: React.ReactNode;
bottomContent?: React.ReactNode;
captionBar?: React.ReactNode;
startFrame: number;
durationInFrames: number;
}
/**
* VerticalLayout - Optimized for 9:16 aspect ratio (Shorts/TikTok/Reels)
*
* Layouts available via config.layout:
* - "top-bottom" (default): Top content, bottom facecam/overlay
* - "caption-content": Caption bar at top, content below
* - "content-caption": Content on top, caption bar at bottom
* - "split-vertical": 50/50 vertical split
*
* Perfect for:
* - YouTube Shorts
* - TikTok videos
* - Instagram Reels
* - Vertical format reposts
*/
export const VerticalLayout: React.FC<VerticalLayoutProps> = ({
topContent,
bottomContent,
captionBar,
startFrame,
durationInFrames
}) => {
const frame = useCurrentFrame();
const relativeFrame = frame - startFrame;
// Don't render if outside the time range
if (frame < startFrame || frame >= startFrame + durationInFrames) {
return null;
}
const layout = 'top-bottom';
const topHeight = 70; // Percentage
const bottomHeight = 100 - topHeight;
const captionBarHeight = 15; // Percentage for caption layouts
const renderTopBottom = () => (
<>
{/* Top Content */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: `${topHeight}%`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
}}
>
{topContent}
</div>
{/* Bottom Content */}
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: `${bottomHeight}%`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
}}
>
{bottomContent}
</div>
</>
);
const renderCaptionContent = () => (
<>
{/* Caption Bar (Top) */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: `${captionBarHeight}%`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '20px',
background: '#0A0E1A',
}}
>
{captionBar}
</div>
{/* Main Content */}
<div
style={{
position: 'absolute',
top: `${captionBarHeight}%`,
left: 0,
right: 0,
bottom: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
}}
>
{topContent}
</div>
</>
);
const renderContentCaption = () => (
<>
{/* Main Content */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: `${captionBarHeight}%`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
}}
>
{topContent}
</div>
{/* Caption Bar (Bottom) */}
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: `${captionBarHeight}%`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '20px',
background: '#0A0E1A',
}}
>
{captionBar}
</div>
</>
);
const renderSplitVertical = () => (
<>
{/* Top Half */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
}}
>
{topContent}
</div>
{/* Bottom Half */}
<div
style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
}}
>
{bottomContent}
</div>
</>
);
return (
<AbsoluteFill style={{ pointerEvents: 'none' }}>
{layout === 'caption-content' && renderCaptionContent()}
{layout === 'content-caption' && renderContentCaption()}
{layout === 'split-vertical' && renderSplitVertical()}
{layout === 'top-bottom' && renderTopBottom()}
</AbsoluteFill>
);
};