x
This commit is contained in:
1 parent
a2da091b15
commit
b6caa0abfe
4 files changed
+93
-4
No files matched your search
@@ -89,6 +89,61 @@ export default function HeroVideoForm({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Interactive Focal Point Picker */}
|
||||
<div className="space-y-2 pt-2 border-t border-white/5">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">
|
||||
Video & Poster Focal Point Alignment (Mobile View Focus)
|
||||
</label>
|
||||
<span className="text-[10px] font-mono text-cyan-glow">
|
||||
{payload.focalPoint ? `X: ${payload.focalPoint.x}, Y: ${payload.focalPoint.y}` : 'Center (50% 50%)'}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="relative h-32 w-full rounded-xl overflow-hidden border border-white/10 bg-black/60 cursor-crosshair group shadow-inner"
|
||||
onClick={(e) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const x = Math.round(((e.clientX - rect.left) / rect.width) * 100) + '%';
|
||||
const y = Math.round(((e.clientY - rect.top) / rect.height) * 100) + '%';
|
||||
updatePayloadValue('focalPoint', { x, y });
|
||||
}}
|
||||
>
|
||||
{payload.posterImageUrl ? (
|
||||
<img
|
||||
src={payload.posterImageUrl}
|
||||
alt="Poster focal preview"
|
||||
className="w-full h-full object-cover opacity-70"
|
||||
style={{ objectPosition: payload.focalPoint ? `${payload.focalPoint.x} ${payload.focalPoint.y}` : '50% 50%' }}
|
||||
/>
|
||||
) : payload.videoUrl ? (
|
||||
<video
|
||||
src={payload.videoUrl}
|
||||
className="w-full h-full object-cover opacity-70"
|
||||
muted
|
||||
preload="metadata"
|
||||
style={{ objectPosition: payload.focalPoint ? `${payload.focalPoint.x} ${payload.focalPoint.y}` : '50% 50%' }}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-slate-500 text-xs font-mono">
|
||||
Click anywhere in box to set Focal Point
|
||||
</div>
|
||||
)}
|
||||
{/* Focal Point Pin Marker */}
|
||||
<div
|
||||
className="absolute w-6 h-6 -ml-3 -mt-3 border-2 border-cyan-glow rounded-full bg-cyan-glow/30 pointer-events-none transition-all flex items-center justify-center shadow-[0_0_12px_rgba(34,211,238,0.8)]"
|
||||
style={{
|
||||
left: payload.focalPoint?.x || '50%',
|
||||
top: payload.focalPoint?.y || '50%'
|
||||
}}
|
||||
>
|
||||
<div className="w-1.5 h-1.5 bg-white rounded-full"></div>
|
||||
</div>
|
||||
<div className="absolute inset-x-0 bottom-1 text-center text-[9px] font-bold text-white/70 uppercase tracking-widest pointer-events-none drop-shadow">
|
||||
Click media box above to move Focus Pin
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Video Settings: Opacity & Height */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 pt-2 border-t border-white/5">
|
||||
<div className="space-y-2">
|
||||
|
||||
@@ -22,6 +22,8 @@ export default function DynamicHeroVideo({ payload }: DynamicHeroVideoProps) {
|
||||
const overlayOpacity = typeof payload.overlayOpacity === 'number' ? payload.overlayOpacity : 40;
|
||||
const height = payload.height || 'large';
|
||||
|
||||
const focalPoint = typeof payload.focalPoint === 'object' && payload.focalPoint ? (payload.focalPoint as { x: string, y: string }) : undefined;
|
||||
|
||||
const getHeightClass = (h: string) => {
|
||||
switch (h) {
|
||||
case 'full':
|
||||
@@ -66,13 +68,26 @@ export default function DynamicHeroVideo({ payload }: DynamicHeroVideoProps) {
|
||||
playsInline
|
||||
poster={posterImageUrl}
|
||||
className="absolute inset-0 w-full h-full object-cover z-0"
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
>
|
||||
<source src={videoUrl} type="video/mp4" />
|
||||
<source src={videoUrl} type="video/webm" />
|
||||
{posterImageUrl && <img src={posterImageUrl} alt="Hero fallback" className="w-full h-full object-cover" />}
|
||||
{posterImageUrl && (
|
||||
<img
|
||||
src={posterImageUrl}
|
||||
alt="Hero fallback"
|
||||
className="w-full h-full object-cover"
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
/>
|
||||
)}
|
||||
</video>
|
||||
) : posterImageUrl ? (
|
||||
<img src={posterImageUrl} alt="Hero Poster" className="absolute inset-0 w-full h-full object-cover z-0" />
|
||||
<img
|
||||
src={posterImageUrl}
|
||||
alt="Hero Poster"
|
||||
className="absolute inset-0 w-full h-full object-cover z-0"
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{/* Customizable Overlay Opacity */}
|
||||
|
||||
@@ -169,6 +169,7 @@ function HeroVideoSection({ payload, lang }: { payload: Record<string, unknown>,
|
||||
const posterImageUrl = typeof payload.posterImageUrl === 'string' ? payload.posterImageUrl : undefined
|
||||
const overlayOpacity = typeof payload.overlayOpacity === 'number' ? payload.overlayOpacity : 40
|
||||
const height = typeof payload.height === 'string' ? payload.height : 'large'
|
||||
const focalPoint = typeof payload.focalPoint === 'object' && payload.focalPoint ? (payload.focalPoint as { x: string, y: string }) : undefined
|
||||
|
||||
const getHeightClass = (h: string) => {
|
||||
switch (h) {
|
||||
@@ -192,13 +193,30 @@ function HeroVideoSection({ payload, lang }: { payload: Record<string, unknown>,
|
||||
playsInline
|
||||
poster={posterImageUrl}
|
||||
className="absolute inset-0 w-full h-full object-cover z-0"
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
>
|
||||
<source src={videoUrl} type="video/mp4" />
|
||||
<source src={videoUrl} type="video/webm" />
|
||||
{posterImageUrl && <Image src={posterImageUrl} alt="Hero fallback" fill unoptimized className="object-cover" />}
|
||||
{posterImageUrl && (
|
||||
<Image
|
||||
src={posterImageUrl}
|
||||
alt="Hero fallback"
|
||||
fill
|
||||
unoptimized
|
||||
className="object-cover"
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
/>
|
||||
)}
|
||||
</video>
|
||||
) : posterImageUrl ? (
|
||||
<Image src={posterImageUrl} alt="Hero Poster" fill unoptimized className="object-cover z-0" />
|
||||
<Image
|
||||
src={posterImageUrl}
|
||||
alt="Hero Poster"
|
||||
fill
|
||||
unoptimized
|
||||
className="object-cover z-0"
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="absolute inset-0 z-10 bg-black transition-opacity" style={{ opacity: overlayOpacity / 100 }}></div>
|
||||
|
||||
@@ -444,6 +444,7 @@ export const HeroVideoSectionPayloadSchema = z.object({
|
||||
loop: z.boolean().optional().default(true),
|
||||
backgroundColor: z.string().optional(),
|
||||
textColor: z.string().optional(),
|
||||
focalPoint: z.object({ x: z.string(), y: z.string() }).optional(),
|
||||
});
|
||||
export type HeroVideoSectionPayload = z.infer<typeof HeroVideoSectionPayloadSchema>;
|
||||
|
||||
|
||||
Reference in new issue
Block a user