Skip to content

Commit

Permalink
feat: update Feature type to support both image and video backgrounds
Browse files Browse the repository at this point in the history
  • Loading branch information
siamak committed Jan 26, 2025
1 parent 0b33854 commit 8d03607
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 64 deletions.
125 changes: 74 additions & 51 deletions apps/frontend/components/sections/sectionComponents/Features.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,73 @@ import { cx } from "cva";
import { useState } from "react";

export default function Features(props: FeaturesProps) {
const [videoReplay, setVideoReplay] = useState(
const [interactionState, setInteractionState] = useState(
props.features?.reduce((o, key) => ({ ...o, [key._key!]: false }), {}) ||
{},
);
const getSides = (index: number) => {
return {
right: true,
left: index === 1 || index === 3 || index === 4,
bottom: index === 3 || index === 4 || index === 5,
top: index === 1 || index === 2 || index === 3,
};
};

const getMobileSides = (index: number) => {
return {
right: true,
left: true,
bottom: index === 5,
top: true,
};
};
const getSides = (index: number) => ({
right: true,
left: index === 1 || index === 3 || index === 4,
bottom: index === 3 || index === 4 || index === 5,
top: index === 1 || index === 2 || index === 3,
});

const getMobileSides = (index: number) => ({
right: true,
left: true,
bottom: index === 5,
top: true,
});

function handleInteraction(_key: string) {
setVideoReplay((prev) => ({ ...prev, [_key]: true }));
setInteractionState((prev) => ({ ...prev, [_key]: true }));
setTimeout(() => {
setVideoReplay((prev) => ({ ...prev, [_key]: false }));
setInteractionState((prev) => ({ ...prev, [_key]: false }));
}, 1000);
}

const renderBackground = (
background: any,
replay: boolean,
isDark: boolean,
) => {
if (!background) return null;

const version = isDark ? background.dark : background.light;

if (version?.type === "video" && version.asset) {
return (
<MuxVideo
replay={replay}
playOnView
className={isDark ? "hidden dark:block" : "dark:hidden"}
video={version.asset}
/>
);
}

if (version?.type === "image" && version.image) {
return (
<img
className={
isDark
? "hidden dark:block w-full h-auto"
: "dark:hidden w-full h-auto"
}
src={version.image.asset?.url}
alt=""
/>
);
}

return null;
};

return (
<>
<div className="hidden w-full px-6 py-[60px] lg:grid lg:grid-cols-2 lg:px-20 lg:py-[80px] ">
{/* Desktop */}
<div className="hidden w-full px-6 py-[60px] lg:grid lg:grid-cols-2 lg:px-20 lg:py-[80px]">
{props?.features?.map((feature, index) => {
const size = index === 2 ? "large" : "small";
return (
Expand All @@ -53,27 +88,21 @@ export default function Features(props: FeaturesProps) {
)}
>
<div
className={cx("absolute z-10", {
className={cx("absolute z-10", {
"left-0 top-0 w-full": size === "small",
"right-0 top-1/2 my-auto w-1/2 -translate-y-1/2 transform":
size !== "small",
})}
>
{feature.bgVideo?.dark?.asset && (
<MuxVideo
replay={videoReplay[feature._key!]}
playOnView
className="hidden dark:block"
video={feature.bgVideo?.dark?.asset}
/>
{renderBackground(
feature.background,
interactionState[feature._key!],
true,
)}
{feature.bgVideo?.light?.asset && (
<MuxVideo
replay={videoReplay[feature._key!]}
playOnView
className="dark:hidden"
video={feature.bgVideo?.light.asset}
/>
{renderBackground(
feature.background,
interactionState[feature._key!],
false,
)}
<div className="absolute bottom-0 h-1/4 w-full bg-gradient-to-t from-white dark:from-background-dark" />
</div>
Expand All @@ -84,7 +113,7 @@ export default function Features(props: FeaturesProps) {
>
<div
className={cx(
"flex h-[600px] flex-col items-start p-l lg:p-xl",
"flex h-[600px] flex-col items-start p-l lg:p-xl",
size === "small"
? "justify-end lg:h-[586px]"
: "max-w-[460px] justify-center lg:h-[590px]",
Expand Down Expand Up @@ -127,9 +156,9 @@ export default function Features(props: FeaturesProps) {
})}
</div>

{/* Mobile */}
<div className="grid w-full grid-cols-2 px-6 py-[60px] lg:hidden lg:px-20 lg:py-[80px]">
{props?.features?.map((feature, index) => {
// const { cleaned: size } = vercelStegaSplit(feature?.size || "");
return (
<div key={feature._key} className={cx("col-span-2")}>
<GradientBorderBox
Expand All @@ -142,21 +171,15 @@ export default function Features(props: FeaturesProps) {
)}
>
<div className={cx("-mt-l w-full sm:-mx-l", {})}>
{feature.bgVideo?.dark?.asset && (
<MuxVideo
replay={videoReplay[feature._key!]}
playOnView
className="hidden dark:block"
video={feature.bgVideo?.dark?.asset}
/>
{renderBackground(
feature.background,
interactionState[feature._key!],
true,
)}
{feature.bgVideo?.light?.asset && (
<MuxVideo
replay={videoReplay[feature._key!]}
playOnView
className="dark:hidden"
video={feature.bgVideo?.light.asset}
/>
{renderBackground(
feature.background,
interactionState[feature._key!],
false,
)}
</div>
{feature?.tag ? (
Expand Down
28 changes: 23 additions & 5 deletions apps/frontend/data/sanity/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,35 @@ const SECTIONS_FRAGMENT = groq`
_id,
features {
...,
bgVideo {
light ${MUX_VIDEO_FRAGMENT},
dark ${MUX_VIDEO_FRAGMENT},
background {
light {
type,
asset ${MUX_VIDEO_FRAGMENT},
image {
...,
asset->{
url,
metadata
}
}
},
dark {
type,
asset ${MUX_VIDEO_FRAGMENT},
image {
...,
asset->{
url,
metadata
}
}
}
},
}[]
},
_type == "section.fullWidthMedia" => {
${FULL_WIDTH_MEDIA_FRAGMENT}
},
_type == "section.registry" => {
...,
"filterIconDictionary": *[_type == "filterIconDictionary"][0],
Expand Down
63 changes: 58 additions & 5 deletions apps/frontend/sanity/schemas/sections/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,73 @@ export const features = defineSection({
icon: BlockContentIcon,
fields: [
{
name: "bgVideo",
title: "Background Video",
name: "background",
title: "Background",
type: "object",
fields: [
{
name: "light",
title: "Light Version",
type: "mux.video",
type: "object",
fields: [
{
name: "type",
title: "Type",
type: "string",
options: {
list: [
{ title: "Image", value: "image" },
{ title: "Video", value: "video" },
],
layout: "radio",
},
validation: (Rule) => Rule.required(),
},
{
name: "asset",
title: "Asset",
type: "mux.video",
hidden: ({ parent }) => parent?.type !== "video",
},
{
name: "image",
title: "Image",
type: "image",
hidden: ({ parent }) => parent?.type !== "image",
},
],
},
{
name: "dark",
title: "Dark Version",
type: "mux.video",
type: "object",
fields: [
{
name: "type",
title: "Type",
type: "string",
options: {
list: [
{ title: "Image", value: "image" },
{ title: "Video", value: "video" },
],
layout: "radio",
},
validation: (Rule) => Rule.required(),
},
{
name: "asset",
title: "Asset",
type: "mux.video",
hidden: ({ parent }) => parent?.type !== "video",
},
{
name: "image",
title: "Image",
type: "image",
hidden: ({ parent }) => parent?.type !== "image",
},
],
},
],
},
Expand All @@ -62,7 +116,6 @@ export const features = defineSection({
name: "description",
title: "Description",
},

{
type: "string",
name: "snippet",
Expand Down
30 changes: 27 additions & 3 deletions apps/frontend/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,33 @@ export type Feature = {
snippet?: string;
toastText?: string;
cta?: SanityLinkType;
bgVideo?: {
light?: MuxVideo;
dark?: MuxVideo;
background?: {
light?: {
type: "image" | "video";
asset?: MuxVideo;
image?: {
asset: {
url: string;
metadata?: {
dimensions?: { width: number; height: number };
lqip?: string; // Low-quality image placeholder
};
};
};
};
dark?: {
type: "image" | "video";
asset?: MuxVideo;
image?: {
asset: {
url: string;
metadata?: {
dimensions?: { width: number; height: number };
lqip?: string; // Low-quality image placeholder
};
};
};
};
};
};

Expand Down

0 comments on commit 8d03607

Please sign in to comment.