feat: create design for 3bear and drsquatch hero banner, create variants cta section
This commit is contained in:
+2
-2
@@ -9,14 +9,14 @@ export default function HeroDefault({ node, context }: { node: LayoutComponentDe
|
||||
|
||||
if (imageDisplayer) {
|
||||
return (
|
||||
<div className="absolute inset-0 z-10 w-full h-full">
|
||||
<div className="absolute inset-0 z-auto w-full h-full">
|
||||
<VtCarousel nodes={{ config: imageDisplayer.config }} context={context} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 z-10 flex flex-col justify-center items-center text-center small:p-32 gap-6">
|
||||
<div className="absolute inset-0 z-auto flex flex-col justify-center items-center text-center small:p-32 gap-6">
|
||||
<span>
|
||||
<Heading
|
||||
level="h1"
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Github } from "@medusajs/icons"
|
||||
import { Button, clx, Heading } from "@medusajs/ui"
|
||||
import {
|
||||
LayoutComponentDefinition,
|
||||
LayoutContext,
|
||||
} from "@vibentec/component-map"
|
||||
import { VtCarousel } from "../vt-carousel"
|
||||
|
||||
interface Props {
|
||||
node: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}
|
||||
const HeroVibentec = ({ node, context }: Props) => {
|
||||
const props = node.config ?? {}
|
||||
const imageDisplayer = props.ImageDisplayer
|
||||
|
||||
return (
|
||||
<div className={clx(props.className)}>
|
||||
{imageDisplayer ? (
|
||||
<VtCarousel nodes={{ config: imageDisplayer.config }} context={context} />
|
||||
) : (
|
||||
<VtCarousel nodes={node} context={context} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HeroVibentec
|
||||
@@ -3,14 +3,9 @@ import {
|
||||
LayoutContext,
|
||||
} from "vibentec/component-map"
|
||||
import { clx } from "@medusajs/ui"
|
||||
import BannerHeroVibentec from "../hero/hero-vibentec"
|
||||
import BannerHeroDefault from "../hero/hero-default"
|
||||
import BannerHero from "./banner-hero"
|
||||
import { DynamicLayoutRenderer } from "vibentec/renderer"
|
||||
|
||||
interface BannerProps {
|
||||
variant: "vibentec" | "default"
|
||||
className?: string
|
||||
}
|
||||
export default async function Hero({
|
||||
nodes,
|
||||
context,
|
||||
@@ -18,7 +13,7 @@ export default async function Hero({
|
||||
nodes: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = (nodes.config as BannerProps) ?? {}
|
||||
const props = nodes.config ?? {}
|
||||
const left = nodes.config?.left ?? []
|
||||
const center = nodes.config?.center ?? []
|
||||
const right = nodes.config?.right ?? []
|
||||
@@ -26,29 +21,21 @@ export default async function Hero({
|
||||
"min-h-[30rem] w-full border-b border-ui-border-base relative",
|
||||
props.className
|
||||
)
|
||||
if (!props.variant) return null
|
||||
|
||||
const variants = {
|
||||
vibentec: BannerHeroVibentec,
|
||||
default: BannerHeroDefault,
|
||||
}
|
||||
|
||||
const Component = variants[props.variant]
|
||||
|
||||
return (
|
||||
<div className={heroClassName}>
|
||||
<Component node={nodes} context={context} />
|
||||
<div className="absolute inset-0 z-20 w-full h-full">
|
||||
<nav className="content-container txt-xsmall-plus flex items-center justify-between w-full h-full text-small-regular">
|
||||
<div className="flex items-center gap-x-4 w-full h-full">
|
||||
<BannerHero node={nodes} context={context} />
|
||||
<div className="absolute z-20">
|
||||
<nav className="content-container txt-xsmall-plus flex items-center justify-between text-small-regular">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{left && <DynamicLayoutRenderer nodes={left} context={context} />}
|
||||
</div>
|
||||
<div className="flex items-center gap-x-4 w-full h-full">
|
||||
<div className="flex items-center gap-x-4">
|
||||
{center && (
|
||||
<DynamicLayoutRenderer nodes={center} context={context} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-x-4 w-full h-full justify-end">
|
||||
<div className="flex items-center gap-x-4 justify-end">
|
||||
{right && <DynamicLayoutRenderer nodes={right} context={context} />}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
display: grid;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
grid-template-columns: auto 1fr;
|
||||
@@ -57,6 +58,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.embla__button {
|
||||
--text-high-contrast-rgb-value: 49, 49, 49;
|
||||
@@ -74,7 +76,7 @@
|
||||
margin: 0;
|
||||
width: 3.6rem;
|
||||
height: 3.6rem;
|
||||
z-index: 1;
|
||||
z-index: 1001;
|
||||
border-radius: 50%;
|
||||
color: var(--text-body);
|
||||
display: flex;
|
||||
@@ -98,6 +100,7 @@
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.embla__dot {
|
||||
--text-high-contrast-rgb-value: 49, 49, 49;
|
||||
|
||||
@@ -9,6 +9,8 @@ import Autoplay from "embla-carousel-autoplay"
|
||||
import { useMemo } from "react"
|
||||
import { DotButton, useDotButton } from "./carousel-dot-button"
|
||||
import { NextButton, PrevButton, usePrevNextButtons } from "./carousel-arrow-button"
|
||||
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||
|
||||
export function VtCarousel({
|
||||
nodes,
|
||||
context,
|
||||
@@ -18,13 +20,21 @@ export function VtCarousel({
|
||||
}) {
|
||||
const props = nodes.config ?? {}
|
||||
const { options } = props as any
|
||||
const images: string[] = (props as any).images ?? (props as any).slides ?? []
|
||||
const links: (string | undefined)[] = (props as any).links ?? []
|
||||
const images: string[] = props.images ?? props.slides ?? []
|
||||
const links: (string | undefined)[] = props.links ?? []
|
||||
const durationSeconds: number = props.duration ?? 4
|
||||
const showControls = images.length > 1
|
||||
|
||||
const plugins = useMemo(() => [
|
||||
Autoplay({ delay: Math.max(1, durationSeconds) * 1000, stopOnInteraction: false, stopOnMouseEnter: true, })
|
||||
], [durationSeconds])
|
||||
const plugins = useMemo(() => {
|
||||
if (!durationSeconds || durationSeconds <= 0) return []
|
||||
return [
|
||||
Autoplay({
|
||||
delay: durationSeconds * 1000,
|
||||
stopOnInteraction: false,
|
||||
stopOnMouseEnter: true,
|
||||
}),
|
||||
]
|
||||
}, [durationSeconds])
|
||||
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel(options, plugins)
|
||||
const { selectedIndex, scrollSnaps, onDotButtonClick } =
|
||||
@@ -41,12 +51,12 @@ export function VtCarousel({
|
||||
<div className={styles["embla__viewport"]} ref={emblaRef}>
|
||||
<div className={styles["embla__container"]}>
|
||||
{images && images.map((src: string, index: number) => (
|
||||
<div className={styles["embla__slide"]} key={index}>
|
||||
<div className={styles["embla__slide"]} key={index + src}>
|
||||
<div className={styles["embla__slide__number"]}>
|
||||
{links[index] ? (
|
||||
<a href={links[index]}>
|
||||
<LocalizedClientLink href={links[index]}>
|
||||
<img src={src} alt={`slide-${index + 1}`} className={styles["embla__slide__image"]} />
|
||||
</a>
|
||||
</LocalizedClientLink>
|
||||
) : (
|
||||
<img src={src} alt={`slide-${index + 1}`} className={styles["embla__slide__image"]} />
|
||||
)}
|
||||
@@ -56,25 +66,27 @@ export function VtCarousel({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles["embla__controls"]}>
|
||||
<div className={styles["embla__buttons"]}>
|
||||
<PrevButton onClick={onPrevButtonClick} disabled={prevBtnDisabled} />
|
||||
<NextButton onClick={onNextButtonClick} disabled={nextBtnDisabled} />
|
||||
</div>
|
||||
{showControls && (
|
||||
<div className={styles["embla__controls"]}>
|
||||
<div className={styles["embla__buttons"]}>
|
||||
<PrevButton onClick={onPrevButtonClick} disabled={prevBtnDisabled} />
|
||||
<NextButton onClick={onNextButtonClick} disabled={nextBtnDisabled} />
|
||||
</div>
|
||||
|
||||
<div className={styles["embla__dots"]}>
|
||||
{scrollSnaps.map((_, index) => (
|
||||
<DotButton
|
||||
key={index}
|
||||
onClick={() => onDotButtonClick(index)}
|
||||
className={[
|
||||
styles["embla__dot"],
|
||||
index === selectedIndex ? styles["embla__dot--selected"] : "",
|
||||
].filter(Boolean).join(" ")}
|
||||
/>
|
||||
))}
|
||||
<div className={styles["embla__dots"]}>
|
||||
{scrollSnaps.map((_, index) => (
|
||||
<DotButton
|
||||
key={index}
|
||||
onClick={() => onDotButtonClick(index)}
|
||||
className={[
|
||||
styles["embla__dot"],
|
||||
index === selectedIndex ? styles["embla__dot--selected"] : "",
|
||||
].filter(Boolean).join(" ")}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"use client"
|
||||
import { Button, clx } from "@medusajs/ui"
|
||||
import {
|
||||
LayoutComponentDefinition,
|
||||
LayoutContext,
|
||||
} from "@vibentec/component-map"
|
||||
|
||||
export function BearCtaBanner({
|
||||
nodes,
|
||||
context,
|
||||
}: {
|
||||
nodes: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = nodes.config ?? {}
|
||||
|
||||
return (
|
||||
<div className={clx(
|
||||
"relative w-full p-12 flex flex-col items-start justify-center text-left",
|
||||
props.className
|
||||
)}>
|
||||
<p className="text-[#0D382E] text-lg font-medium mb-2">
|
||||
{props.tagText ?? "So einfach kann Frühstück sein – mit unseren leckeren Overnight Oats."}
|
||||
</p>
|
||||
|
||||
<h1 className="text-[#0D382E] font-black leading-tight text-[64px] mb-8 tracking-tight">
|
||||
{props.titleText ?? "breakfast made easy."}
|
||||
</h1>
|
||||
|
||||
<Button className="inline-flex items-center justify-center bg-[#FCEE56] hover:bg-[#FCEE56]/90 text-[#0D382E] px-8 py-3 rounded-full font-bold text-lg shadow-none border-none">
|
||||
{props.buttonText ?? "Jetzt entdecken"}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
"use client"
|
||||
import { Button, clx } from "@medusajs/ui"
|
||||
import { ChevronRightMini } from "@medusajs/icons"
|
||||
import {
|
||||
LayoutComponentDefinition,
|
||||
LayoutContext,
|
||||
} from "@vibentec/component-map"
|
||||
|
||||
export function DefaultCtaBanner({
|
||||
nodes,
|
||||
context,
|
||||
}: {
|
||||
nodes: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = nodes.config ?? {}
|
||||
return (
|
||||
<div className={clx(
|
||||
"relative w-[544px] bg-white rounded-[24px] border border-[#E6EFFC] shadow-[0_12px_40px_rgba(17,49,78,0.10)] p-6",
|
||||
props.className
|
||||
)}>
|
||||
<div className={clx(
|
||||
"inline-flex items-center rounded-full bg-[#FCE9DA] text-[#E68445] px-3 py-1 text-sm font-medium",
|
||||
props.tagTextClassName
|
||||
)}>
|
||||
{props.tagText}
|
||||
</div>
|
||||
|
||||
<h1 className={clx(
|
||||
"mt-4 text-[#11314E] font-semibold leading-normal text-[56px]",
|
||||
props.titleTextClassName
|
||||
)}>
|
||||
{props.titleText}
|
||||
</h1>
|
||||
|
||||
<p className={clx(
|
||||
"mt-5 text-[#285A86] text-[16px] sm:text-xl opacity-80",
|
||||
props.descriptionTextClassName
|
||||
)}>
|
||||
{props.descriptionText}
|
||||
</p>
|
||||
|
||||
<Button className={clx(
|
||||
"mt-8 inline-flex items-center gap-2 bg-[#0F2740] hover:bg-[#173551] text-white px-6 py-3 rounded-[12px] shadow-md",
|
||||
props.buttonTextClassName
|
||||
)}>
|
||||
{props.buttonText}
|
||||
<ChevronRightMini />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client"
|
||||
import { Button, clx } from "@medusajs/ui"
|
||||
import { ChevronRightMini } from "@medusajs/icons"
|
||||
import {
|
||||
LayoutComponentDefinition,
|
||||
LayoutContext,
|
||||
} from "@vibentec/component-map"
|
||||
import { DefaultCtaBanner } from "./default-cta"
|
||||
import { BearCtaBanner } from "./bear-cta"
|
||||
|
||||
export function VtCtaBanner({
|
||||
nodes,
|
||||
@@ -14,27 +14,14 @@ export function VtCtaBanner({
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = nodes.config ?? {}
|
||||
return (
|
||||
<div className={clx(
|
||||
"relative w-[544px] bg-white rounded-[24px] border border-[#E6EFFC] shadow-[0_12px_40px_rgba(17,49,78,0.10)] p-6",
|
||||
props.className
|
||||
)}>
|
||||
<div className="inline-flex items-center rounded-full bg-[#FCE9DA] text-[#E68445] px-3 py-1 text-sm font-medium">
|
||||
{props.tagText}
|
||||
</div>
|
||||
const variant = props.variant ?? "default"
|
||||
|
||||
<h1 className="mt-4 text-[#11314E] font-semibold leading-tight text-[56px]">
|
||||
{props.titleText}
|
||||
</h1>
|
||||
const variants: Record<string, any> = {
|
||||
default: DefaultCtaBanner,
|
||||
"3bear": BearCtaBanner,
|
||||
}
|
||||
|
||||
<p className="mt-5 text-[#285A86] text-[16px] sm:text-xl opacity-80">
|
||||
{props.descriptionText}
|
||||
</p>
|
||||
const Component = variants[variant] || DefaultCtaBanner
|
||||
|
||||
<Button className="mt-8 inline-flex items-center gap-2 bg-[#0F2740] hover:bg-[#173551] text-white px-6 py-3 rounded-[12px] shadow-md">
|
||||
{props.buttonText}
|
||||
<ChevronRightMini />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
return <Component nodes={nodes} context={context} />
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user