refactor: add some component in footer and implement function for component
This commit is contained in:
@@ -25,17 +25,17 @@ export default async function VtFooter({
|
||||
return (
|
||||
<footer className={props?.className ?? ""}>
|
||||
{props?.left && (
|
||||
<div className={clx("flex gap-x-4 h-full", props?.leftClassName)}>
|
||||
<div className={clx("flex h-full", props?.leftClassName)}>
|
||||
<DynamicLayoutRenderer nodes={props?.left} context={context} />
|
||||
</div>
|
||||
)}
|
||||
{props?.center && (
|
||||
<div className={clx("flex gap-x-4 h-full", props?.centerClassName)}>
|
||||
<div className={clx("flex h-full", props?.centerClassName)}>
|
||||
<DynamicLayoutRenderer nodes={props?.center} context={context} />
|
||||
</div>
|
||||
)}
|
||||
{props?.right && (
|
||||
<div className={clx("flex gap-x-4 h-full", props?.rightClassName)}>
|
||||
<div className={clx("flex h-full", props?.rightClassName)}>
|
||||
<DynamicLayoutRenderer nodes={props?.right} context={context} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as MedusaIcons from "@medusajs/icons"
|
||||
import { clx } from "@medusajs/ui"
|
||||
import { LayoutComponentDefinition, LayoutContext } from "@vibentec/component-map"
|
||||
import {
|
||||
LayoutComponentDefinition,
|
||||
LayoutContext,
|
||||
} from "@vibentec/component-map"
|
||||
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||
|
||||
export default function VtFooterBottom({
|
||||
nodes,
|
||||
@@ -9,23 +13,62 @@ export default function VtFooterBottom({
|
||||
nodes: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = (nodes.config) ?? {}
|
||||
const props = nodes.config ?? {}
|
||||
|
||||
const renderIcon = (name: string, idx: number) => {
|
||||
const IconComp = (MedusaIcons as Record<string, any>)[name]
|
||||
if (!IconComp) return null
|
||||
return <IconComp key={`${name}-${idx}`} className="shadow-none" />
|
||||
}
|
||||
|
||||
const renderLink = (item: any, idx: number, total: number) => {
|
||||
const LinkEl = (
|
||||
<LocalizedClientLink
|
||||
href={item?.href ?? "#"}
|
||||
className={clx(
|
||||
"hover:underline",
|
||||
props.linkClassName
|
||||
)}
|
||||
>
|
||||
{item?.label ?? ""}
|
||||
</LocalizedClientLink>
|
||||
)
|
||||
|
||||
return (
|
||||
<IconComp key={`${name}-${idx}`} className="shadow-none" />
|
||||
<span
|
||||
key={`link-${idx}`}
|
||||
className={clx("flex items-center", props.linkItemClassName)}
|
||||
>
|
||||
{LinkEl}
|
||||
{idx < total - 1 && (
|
||||
<span className={clx("mx-2 text-white", props.separatorClassName)}>|</span>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clx("flex w-full items-center justify-between", props.className)}>
|
||||
<span className={clx("text-[14px] font-[400] pt-2", props.textClassName)}>{props.text}</span>
|
||||
<div className={clx("flex items-center gap-2", props.iconsClassName)}>
|
||||
{(props.icons ?? []).map(renderIcon)}
|
||||
<div
|
||||
className={clx(
|
||||
"flex w-full items-center justify-between",
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
<span className={clx("text-[14px] font-[400] pt-2", props.textClassName)}>
|
||||
{props.text}
|
||||
</span>
|
||||
<div className={clx("flex items-center gap-4")}>
|
||||
{props.links && props.links.length > 0 && (
|
||||
<div className={clx("flex items-center", props.linksClassName)}>
|
||||
{props.links.map((l: any, idx: number) =>
|
||||
renderLink(l, idx, props.links.length)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className={clx("flex items-center gap-2", props.iconsClassName)}>
|
||||
{(props.icons ?? []).map(renderIcon)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { clx } from "@medusajs/ui"
|
||||
import { ChevronRightMini } from "@medusajs/icons"
|
||||
import { LayoutComponentDefinition, LayoutContext } from "@vibentec/component-map"
|
||||
import {
|
||||
LayoutComponentDefinition,
|
||||
LayoutContext,
|
||||
} from "@vibentec/component-map"
|
||||
import * as MedusaIcons from "@medusajs/icons"
|
||||
import * as CustomIcons from "@modules/common/icons"
|
||||
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||
|
||||
interface VtFooterHeroConfig {
|
||||
logoSrc: string
|
||||
logoAlt?: string
|
||||
title: string
|
||||
description: string
|
||||
cta?: { label: string; href: string }
|
||||
interface SocialIcon {
|
||||
href?: string
|
||||
icon?: string
|
||||
imgSrc?: string
|
||||
className?: string
|
||||
logoClassName?: string
|
||||
titleClassName?: string
|
||||
descriptionClassName?: string
|
||||
ctaClassName?: string
|
||||
}
|
||||
|
||||
export default function VtFooterHero({
|
||||
@@ -23,8 +22,49 @@ export default function VtFooterHero({
|
||||
nodes: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = (nodes.config as VtFooterHeroConfig) ?? {}
|
||||
const props = nodes.config ?? {}
|
||||
const IconComp = (name?: string) => {
|
||||
if (!name) return null
|
||||
return (
|
||||
(MedusaIcons as Record<string, any>)[name] ||
|
||||
(CustomIcons as Record<string, any>)[name] ||
|
||||
null
|
||||
)
|
||||
}
|
||||
const renderSocialContent = (item: SocialIcon) => {
|
||||
const Icon = IconComp(item.icon)
|
||||
if (Icon) return <Icon className={item.className} />
|
||||
if (item.imgSrc)
|
||||
return (
|
||||
<img
|
||||
src={item.imgSrc}
|
||||
alt={item.icon ?? "social"}
|
||||
className={item.className}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<span className="w-4 h-4 rounded-md bg-white/10 text-white flex items-center justify-center text-sm">
|
||||
{item.icon?.[0] ?? "?"}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const renderSocialIcon = (icon: SocialIcon, idx: number) => {
|
||||
const content = renderSocialContent(icon)
|
||||
return icon.href ? (
|
||||
<a
|
||||
key={idx}
|
||||
href={icon.href}
|
||||
className={clx("hover:opacity-80", icon.className)}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
) : (
|
||||
<span key={idx} className={clx("", icon.className)}>
|
||||
{content}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className={clx("flex flex-col items-start", props.className)}>
|
||||
{props.logoSrc && (
|
||||
@@ -35,12 +75,22 @@ export default function VtFooterHero({
|
||||
/>
|
||||
)}
|
||||
{props.title && (
|
||||
<h2 className={clx("mt-4 w-[320px] text-[24px] font-semibold text-[#11314E]", props.titleClassName)}>
|
||||
<h2
|
||||
className={clx(
|
||||
"mt-4 w-[320px] text-[24px] font-semibold text-[#11314E]",
|
||||
props.titleClassName
|
||||
)}
|
||||
>
|
||||
{props.title}
|
||||
</h2>
|
||||
)}
|
||||
{props.description && (
|
||||
<p className={clx("mt-2 text-ui-fg-subtle txt-small", props.descriptionClassName)}>
|
||||
<p
|
||||
className={clx(
|
||||
"mt-2 text-ui-fg-subtle txt-small",
|
||||
props.descriptionClassName
|
||||
)}
|
||||
>
|
||||
{props.description}
|
||||
</p>
|
||||
)}
|
||||
@@ -56,7 +106,53 @@ export default function VtFooterHero({
|
||||
<ChevronRightMini className="order-1" />
|
||||
</LocalizedClientLink>
|
||||
)}
|
||||
|
||||
{props.email && (
|
||||
<form
|
||||
className={clx(
|
||||
"mt-4 w-full max-w-[500px]",
|
||||
props.email?.emailInputClassName
|
||||
)}
|
||||
>
|
||||
<div className="relative flex items-center justify-between border border-white/40 rounded-md px-4 py-3">
|
||||
<input
|
||||
id="vt-footer-hero-email"
|
||||
type="email"
|
||||
name="contact[email]"
|
||||
placeholder=" "
|
||||
autoComplete="email"
|
||||
required
|
||||
className="peer bg-transparent outline-none text-white/90 placeholder-transparent flex-1"
|
||||
/>
|
||||
<label
|
||||
htmlFor="vt-footer-hero-email"
|
||||
className="absolute left-4 text-white/60 pointer-events-none transition-all duration-300
|
||||
peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:text-white/50
|
||||
peer-focus:top-3 peer-focus:-translate-y-2 peer-focus:left-3 peer-focus:text-white
|
||||
peer-[:not(:placeholder-shown)]:translate-y-[-0.85rem]"
|
||||
>
|
||||
E-Mail
|
||||
</label>
|
||||
<button
|
||||
type="submit"
|
||||
className="ml-4 w-8 h-8 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center"
|
||||
>
|
||||
<span className="sr-only">Abonnieren</span>
|
||||
<ChevronRightMini />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
{props.socials && props.socials.length > 0 ? (
|
||||
<div
|
||||
className={clx(
|
||||
"flex items-center gap-4 mt-2",
|
||||
props.socialsClassName
|
||||
)}
|
||||
>
|
||||
{props.socials.map(renderSocialIcon)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
"use client"
|
||||
import { Button, Input } from "@medusajs/ui"
|
||||
import { clx } from "@medusajs/ui"
|
||||
import * as MedusaIcons from "@medusajs/icons"
|
||||
import * as CustomIcons from "@modules/common/icons"
|
||||
import { LayoutComponentDefinition, LayoutContext } from "@vibentec/component-map"
|
||||
import React, { useState } from "react"
|
||||
|
||||
interface SocialIcon {
|
||||
href?: string
|
||||
icon?: string
|
||||
imgSrc?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface VtFooterSignupConfig {
|
||||
title?: string
|
||||
placeholder?: string
|
||||
buttonLabel?: string
|
||||
className?: string
|
||||
titleClassName?: string
|
||||
formClassName?: string
|
||||
inputClassName?: string
|
||||
buttonClassName?: string
|
||||
socialsClassName?: string
|
||||
socials?: SocialIcon[]
|
||||
}
|
||||
|
||||
export default function VtFooterSignUp({
|
||||
nodes,
|
||||
context,
|
||||
}: {
|
||||
nodes: LayoutComponentDefinition
|
||||
context: LayoutContext
|
||||
}) {
|
||||
const props = (nodes.config as VtFooterSignupConfig) ?? {}
|
||||
const [email, setEmail] = useState("")
|
||||
|
||||
const IconComp = (name?: string) => {
|
||||
if (!name) return null
|
||||
return (
|
||||
(MedusaIcons as Record<string, any>)[name] ||
|
||||
(CustomIcons as Record<string, any>)[name] ||
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
const onSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
console.log("newsletter_signup", { email })
|
||||
}
|
||||
|
||||
const renderSocialContent = (item: SocialIcon) => {
|
||||
const Icon = IconComp(item.icon)
|
||||
if (Icon) return <Icon className={item.className} />
|
||||
if (item.imgSrc) return <img src={item.imgSrc} alt={item.icon ?? "social"} className={item.className} />
|
||||
return (
|
||||
<span className="w-4 h-4 rounded-md bg-white/10 text-white flex items-center justify-center text-sm">
|
||||
{item.icon?.[0] ?? "?"}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const renderSocialIcon = (icon: SocialIcon, idx: number) => {
|
||||
const content = renderSocialContent(icon)
|
||||
return icon.href ? (
|
||||
<a key={idx} href={icon.href} className={clx("hover:opacity-80", icon.className)}>
|
||||
{content}
|
||||
</a>
|
||||
) : (
|
||||
<span key={idx} className={clx("", icon.className)}>
|
||||
{content}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clx("flex flex-col gap-4", props.className)}>
|
||||
{props.title && (
|
||||
<p className={clx("text-white text-[18px]", props.titleClassName)}>
|
||||
{props.title}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<form onSubmit={onSubmit} className={clx("flex items-center gap-4", props.formClassName)}>
|
||||
<input
|
||||
type="email"
|
||||
placeholder={props.placeholder ?? "Email"}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className={clx(
|
||||
"h-[48px] rounded-lg bg-white text-black px-4",
|
||||
props.inputClassName
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
className={clx(
|
||||
"h-[48px] rounded-lg bg-[#C4622C] text-white",
|
||||
props.buttonClassName
|
||||
)}
|
||||
>
|
||||
{props.buttonLabel ?? "Sign Up"}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{props.socials && props.socials.length > 0 ? (
|
||||
<div className={clx("flex items-center gap-4 mt-2", props.socialsClassName)}>
|
||||
{props.socials.map(renderSocialIcon)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user