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>
|
||||||
</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 */}
|
{/* 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="grid grid-cols-1 md:grid-cols-3 gap-6 pt-2 border-t border-white/5">
|
||||||
<div className="space-y-2">
|
<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 overlayOpacity = typeof payload.overlayOpacity === 'number' ? payload.overlayOpacity : 40;
|
||||||
const height = payload.height || 'large';
|
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) => {
|
const getHeightClass = (h: string) => {
|
||||||
switch (h) {
|
switch (h) {
|
||||||
case 'full':
|
case 'full':
|
||||||
@@ -66,13 +68,26 @@ export default function DynamicHeroVideo({ payload }: DynamicHeroVideoProps) {
|
|||||||
playsInline
|
playsInline
|
||||||
poster={posterImageUrl}
|
poster={posterImageUrl}
|
||||||
className="absolute inset-0 w-full h-full object-cover z-0"
|
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/mp4" />
|
||||||
<source src={videoUrl} type="video/webm" />
|
<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>
|
</video>
|
||||||
) : posterImageUrl ? (
|
) : 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}
|
) : null}
|
||||||
|
|
||||||
{/* Customizable Overlay Opacity */}
|
{/* 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 posterImageUrl = typeof payload.posterImageUrl === 'string' ? payload.posterImageUrl : undefined
|
||||||
const overlayOpacity = typeof payload.overlayOpacity === 'number' ? payload.overlayOpacity : 40
|
const overlayOpacity = typeof payload.overlayOpacity === 'number' ? payload.overlayOpacity : 40
|
||||||
const height = typeof payload.height === 'string' ? payload.height : 'large'
|
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) => {
|
const getHeightClass = (h: string) => {
|
||||||
switch (h) {
|
switch (h) {
|
||||||
@@ -192,13 +193,30 @@ function HeroVideoSection({ payload, lang }: { payload: Record<string, unknown>,
|
|||||||
playsInline
|
playsInline
|
||||||
poster={posterImageUrl}
|
poster={posterImageUrl}
|
||||||
className="absolute inset-0 w-full h-full object-cover z-0"
|
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/mp4" />
|
||||||
<source src={videoUrl} type="video/webm" />
|
<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>
|
</video>
|
||||||
) : posterImageUrl ? (
|
) : 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}
|
) : null}
|
||||||
|
|
||||||
<div className="absolute inset-0 z-10 bg-black transition-opacity" style={{ opacity: overlayOpacity / 100 }}></div>
|
<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),
|
loop: z.boolean().optional().default(true),
|
||||||
backgroundColor: z.string().optional(),
|
backgroundColor: z.string().optional(),
|
||||||
textColor: z.string().optional(),
|
textColor: z.string().optional(),
|
||||||
|
focalPoint: z.object({ x: z.string(), y: z.string() }).optional(),
|
||||||
});
|
});
|
||||||
export type HeroVideoSectionPayload = z.infer<typeof HeroVideoSectionPayloadSchema>;
|
export type HeroVideoSectionPayload = z.infer<typeof HeroVideoSectionPayloadSchema>;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user