feat: create components and map with data json file of 3bear design
This commit is contained in:
parent
c8853bac1c
commit
b76719fb32
|
|
@ -55,6 +55,138 @@
|
||||||
"objectFit": "contain"
|
"objectFit": "contain"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"VtMegaMenu": {
|
||||||
|
"config": {
|
||||||
|
"navLabel": {
|
||||||
|
"text": "Shop",
|
||||||
|
"className": "font-bold text-[1rem] text-[#003F31] flex items-center mr-8 gap-1 hover:text-[#009b93]",
|
||||||
|
"isShowArrow": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Dropdown": {
|
||||||
|
"config": {
|
||||||
|
"trigger": {
|
||||||
|
"text": "Über Uns",
|
||||||
|
"className": "font-bold text-[1rem] text-[#003F31] flex items-center mr-8 gap-1 hover:text-[#009b93]",
|
||||||
|
"isShowArrow": true
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"text": "Unser Unternehmen",
|
||||||
|
"href": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Loren ipsum",
|
||||||
|
"href": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Not a Link"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Dropdown": {
|
||||||
|
"config": {
|
||||||
|
"trigger": {
|
||||||
|
"text": "Über unsere Produkte",
|
||||||
|
"className": "font-bold text-[1rem] text-[#003F31] flex items-center mr-8 gap-1 hover:text-[#009b93]",
|
||||||
|
"isShowArrow": true
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"text": "Unser Unternehmen",
|
||||||
|
"href": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Loren ipsum",
|
||||||
|
"href": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Not a Link"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Link": {
|
||||||
|
"config": {
|
||||||
|
"label": "Rezepte",
|
||||||
|
"href": "/",
|
||||||
|
"className": "font-bold text-[1rem] text-[#003F31] flex items-center mr-8 gap-1 hover:text-[#009b93]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Link": {
|
||||||
|
"config": {
|
||||||
|
"label": "Triff Harry Kane",
|
||||||
|
"href": "/",
|
||||||
|
"className": "font-bold text-[1rem] text-[#003F31] flex items-center gap-1 hover:text-[#009b93]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"right": [
|
||||||
|
{
|
||||||
|
"Dropdown": {
|
||||||
|
"config": {
|
||||||
|
"trigger": {
|
||||||
|
"icon": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Germany.svg/1200px-Flag_of_Germany.svg.png",
|
||||||
|
"text": "Germany (EUR)",
|
||||||
|
"className": "font-bold text-[1rem] text-[#003F31] flex items-center gap-1 hover:text-[#009b93]",
|
||||||
|
"isShowArrow": true
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"icon": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Flag_of_Germany.svg/1200px-Flag_of_Germany.svg.png",
|
||||||
|
"text": "Germany (EUR)",
|
||||||
|
"href": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "https://upload.wikimedia.org/wikipedia/commons/2/20/Flag_of_the_Netherlands.svg",
|
||||||
|
"text": "Netherlands (EUR)",
|
||||||
|
"href": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "https://upload.wikimedia.org/wikipedia/commons/8/88/Flag_of_Australia_%28converted%29.svg",
|
||||||
|
"text": "Australia (AUD)",
|
||||||
|
"href": "/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IconButton": {
|
||||||
|
"config": {
|
||||||
|
"variant": "search",
|
||||||
|
"className": "shadow-none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IconButton": {
|
||||||
|
"config": {
|
||||||
|
"variant": "user",
|
||||||
|
"className": "shadow-none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"VtCartButton": {
|
||||||
|
"config": {
|
||||||
|
"variant": "button",
|
||||||
|
"className": "shadow-none"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,38 @@
|
||||||
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||||
import { LayoutComponentDefinition, LayoutContext } from "vibentec/component-map";
|
import {
|
||||||
import { clx } from "@medusajs/ui"
|
LayoutComponentDefinition,
|
||||||
import { Suspense } from "react";
|
LayoutContext,
|
||||||
import CartButton from "@modules/layout/components/cart-button";
|
} from "vibentec/component-map"
|
||||||
|
import { clx, IconButton } from "@medusajs/ui"
|
||||||
|
import { Suspense } from "react"
|
||||||
|
import CartButton from "@modules/layout/components/cart-button"
|
||||||
|
import { ShoppingBag } from "@medusajs/icons"
|
||||||
|
|
||||||
export const VtCartButton = ({ nodes, context }: { nodes: LayoutComponentDefinition; context: LayoutContext }) => {
|
const CartIconButtonComponent = ({ className }: { className?: string }) => {
|
||||||
|
return (
|
||||||
|
<IconButton className={className}>
|
||||||
|
<ShoppingBag />
|
||||||
|
</IconButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const VtCartButton = ({
|
||||||
|
nodes,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
nodes: LayoutComponentDefinition
|
||||||
|
context: LayoutContext
|
||||||
|
}) => {
|
||||||
const props = nodes.config ?? {}
|
const props = nodes.config ?? {}
|
||||||
const className = clx("hover:text-ui-fg-base flex gap-2", props.className)
|
const className = clx("hover:text-ui-fg-base flex gap-2", props.className)
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
link: <CartButton />,
|
||||||
|
button: <CartIconButtonComponent className={className} />,
|
||||||
|
}
|
||||||
|
if (!props.variant) return null
|
||||||
|
const fallBackComp = variants[props.variant as keyof typeof variants]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense
|
<Suspense
|
||||||
fallback={
|
fallback={
|
||||||
|
|
@ -20,7 +45,7 @@ export const VtCartButton = ({ nodes, context }: { nodes: LayoutComponentDefinit
|
||||||
</LocalizedClientLink>
|
</LocalizedClientLink>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CartButton />
|
{fallBackComp}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
"use client"
|
||||||
|
import { DropdownMenu } from "@medusajs/ui"
|
||||||
|
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||||
|
import ChevronDown from "@modules/common/icons/chevron-down"
|
||||||
|
import {
|
||||||
|
LayoutComponentDefinition,
|
||||||
|
LayoutContext,
|
||||||
|
} from "@vibentec/component-map"
|
||||||
|
|
||||||
|
export default function VtDropdown({
|
||||||
|
nodes,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
nodes: LayoutComponentDefinition
|
||||||
|
context: LayoutContext
|
||||||
|
}) {
|
||||||
|
const props = nodes.config ?? {}
|
||||||
|
if (!props.trigger.text || props.items.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenu.Trigger
|
||||||
|
className={props.trigger.className + " flex items-center gap-1"}
|
||||||
|
>
|
||||||
|
{props.trigger.icon && (
|
||||||
|
<img
|
||||||
|
src={props.trigger.icon}
|
||||||
|
alt={props.trigger.text}
|
||||||
|
className="w-5 h-5 rounded-[50%] mr-3"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{props.trigger.text} {props.trigger.isShowArrow && <ChevronDown />}
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Content>
|
||||||
|
{props.items.map(
|
||||||
|
(
|
||||||
|
item: {
|
||||||
|
text: string
|
||||||
|
className?: string
|
||||||
|
href?: string
|
||||||
|
icon?: string
|
||||||
|
},
|
||||||
|
index: number
|
||||||
|
) => (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
key={item.text + index}
|
||||||
|
className={item.className || ""}
|
||||||
|
>
|
||||||
|
{item.icon && (
|
||||||
|
<img
|
||||||
|
src={item.icon}
|
||||||
|
alt={item.text}
|
||||||
|
className="w-5 h-5 rounded-[50%] mr-3"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item.href ? (
|
||||||
|
<LocalizedClientLink href={item.href}>
|
||||||
|
{item.text}
|
||||||
|
</LocalizedClientLink>
|
||||||
|
) : (
|
||||||
|
item.text
|
||||||
|
)}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,34 @@
|
||||||
import { LayoutComponentDefinition, LayoutContext } from "vibentec/component-map";
|
import {
|
||||||
import React, { Suspense } from "react";
|
LayoutComponentDefinition,
|
||||||
import SkeletonMegaMenu from "@modules/skeletons/components/vt-skeleton-mega-menu";
|
LayoutContext,
|
||||||
import MegaMenuWrapper from "@modules/layout/components/vt-mega-menu/mega-menu-wrapper";
|
} from "vibentec/component-map"
|
||||||
|
import React, { Suspense } from "react"
|
||||||
|
import SkeletonMegaMenu from "@modules/skeletons/components/vt-skeleton-mega-menu"
|
||||||
|
import MegaMenuWrapper from "@modules/layout/components/vt-mega-menu/mega-menu-wrapper"
|
||||||
|
|
||||||
export default function VtMegaMenu({ nodes, context }: { nodes: LayoutComponentDefinition; context: LayoutContext }) {
|
interface MegaMenuProps {
|
||||||
|
navLabel: {
|
||||||
|
text: string
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default function VtMegaMenu({
|
||||||
|
nodes,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
nodes: LayoutComponentDefinition
|
||||||
|
context: LayoutContext
|
||||||
|
}) {
|
||||||
|
const { navLabel } = nodes.config as MegaMenuProps ?? {}
|
||||||
return (
|
return (
|
||||||
<nav>
|
<nav>
|
||||||
<ul className="space-x-4 hidden small:flex">
|
<ul className="space-x-4 hidden small:flex">
|
||||||
<li>
|
<li>
|
||||||
<Suspense fallback={<SkeletonMegaMenu />}>
|
<Suspense fallback={<SkeletonMegaMenu />}>
|
||||||
<MegaMenuWrapper />
|
<MegaMenuWrapper navLabel={navLabel} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import { listCategories } from "@lib/data/categories"
|
import { listCategories } from "@lib/data/categories"
|
||||||
import MegaMenu from "./mega-menu"
|
import MegaMenu from "./mega-menu"
|
||||||
|
|
||||||
export async function MegaMenuWrapper() {
|
|
||||||
|
|
||||||
|
export async function MegaMenuWrapper({ navLabel }: { navLabel: { text: string; className?: string } }) {
|
||||||
const categories = await listCategories().catch(() => [])
|
const categories = await listCategories().catch(() => [])
|
||||||
|
|
||||||
return <MegaMenu categories={categories} />
|
return <MegaMenu navLabel={navLabel} categories={categories} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MegaMenuWrapper
|
export default MegaMenuWrapper
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,16 @@
|
||||||
import { HttpTypes } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { clx } from "@medusajs/ui"
|
import { clx } from "@medusajs/ui"
|
||||||
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||||
|
import ChevronDown from "@modules/common/icons/chevron-down"
|
||||||
import { usePathname } from "next/navigation"
|
import { usePathname } from "next/navigation"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
|
||||||
const MegaMenu = ({
|
const MegaMenu = ({
|
||||||
|
navLabel,
|
||||||
categories,
|
categories,
|
||||||
}: {
|
}: {
|
||||||
|
navLabel: { text: string; className?: string, isShowArrow?: boolean },
|
||||||
categories: HttpTypes.StoreProductCategory[]
|
categories: HttpTypes.StoreProductCategory[]
|
||||||
}) => {
|
}) => {
|
||||||
const [isHovered, setIsHovered] = useState(false)
|
const [isHovered, setIsHovered] = useState(false)
|
||||||
|
|
@ -81,10 +85,13 @@ const MegaMenu = ({
|
||||||
className="z-50"
|
className="z-50"
|
||||||
>
|
>
|
||||||
<LocalizedClientLink
|
<LocalizedClientLink
|
||||||
className="hover:text-ui-fg-base hover:bg-neutral-100 rounded-full px-3 py-2"
|
className={clx(
|
||||||
|
"hover:text-ui-fg-base hover:bg-neutral-100 rounded-full px-3 py-2",
|
||||||
|
navLabel.className
|
||||||
|
)}
|
||||||
href="/store"
|
href="/store"
|
||||||
>
|
>
|
||||||
Products
|
{navLabel.text} {navLabel.isShowArrow && <ChevronDown />}
|
||||||
</LocalizedClientLink>
|
</LocalizedClientLink>
|
||||||
{isHovered && (
|
{isHovered && (
|
||||||
<div className="absolute top-full left-0 right-0 flex gap-32 py-10 px-20 bg-white border-b border-neutral-200 ">
|
<div className="absolute top-full left-0 right-0 flex gap-32 py-10 px-20 bg-white border-b border-neutral-200 ">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { IconButton } from "@medusajs/ui"
|
||||||
|
import { MagnifyingGlass, User, ShoppingBag } from "@medusajs/icons"
|
||||||
|
import {
|
||||||
|
LayoutComponentDefinition,
|
||||||
|
LayoutContext,
|
||||||
|
} from "@vibentec/component-map"
|
||||||
|
|
||||||
|
export default function VtIconButton({
|
||||||
|
nodes,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
nodes: LayoutComponentDefinition
|
||||||
|
context: LayoutContext
|
||||||
|
}) {
|
||||||
|
const props = nodes.config || {}
|
||||||
|
const variantsIcon = {
|
||||||
|
search: MagnifyingGlass,
|
||||||
|
user: User,
|
||||||
|
cart: ShoppingBag
|
||||||
|
}
|
||||||
|
if (!props.variant) return null
|
||||||
|
const Icon = variantsIcon[props.variant as keyof typeof variantsIcon]
|
||||||
|
return (
|
||||||
|
<IconButton className={props?.className ?? ""}>
|
||||||
|
<Icon />
|
||||||
|
</IconButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -51,7 +51,8 @@
|
||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
.content-container {
|
.content-container {
|
||||||
@apply max-w-[1440px] w-full mx-auto px-6;
|
/* @apply max-w-[1440px] w-full mx-auto px-6; */
|
||||||
|
@apply w-full mx-auto px-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contrast-btn {
|
.contrast-btn {
|
||||||
|
|
@ -110,3 +111,6 @@
|
||||||
@apply text-[32px] leading-[44px] font-semibold;
|
@apply text-[32px] leading-[44px] font-semibold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[data-radix-popper-content-wrapper]{
|
||||||
|
z-index: 51 !important;
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,8 @@ import VtMegaMenu from "@modules/layout/components/vt-mega-menu"
|
||||||
import VtLink from "@modules/layout/components/vt-linkbutton"
|
import VtLink from "@modules/layout/components/vt-linkbutton"
|
||||||
import VtSideMenu from "@modules/layout/components/vt-sidemenu"
|
import VtSideMenu from "@modules/layout/components/vt-sidemenu"
|
||||||
import VtImage from "@modules/layout/templates/vt-image"
|
import VtImage from "@modules/layout/templates/vt-image"
|
||||||
|
import VtDropdown from "@modules/layout/components/vt-dropdown"
|
||||||
|
import VtIconButton from "@modules/layout/templates/vt-icon-button"
|
||||||
|
|
||||||
type ComponentConfig = Record<string, any>;
|
type ComponentConfig = Record<string, any>;
|
||||||
|
|
||||||
|
|
@ -54,9 +56,11 @@ export const componentMap: Record<string, ComponentRenderer> = {
|
||||||
Banner: nodesContextRenderer(Banner),
|
Banner: nodesContextRenderer(Banner),
|
||||||
HomeButton: nodesContextRenderer(HomeButton),
|
HomeButton: nodesContextRenderer(HomeButton),
|
||||||
AccountButton: nodesContextRenderer(AccountButton),
|
AccountButton: nodesContextRenderer(AccountButton),
|
||||||
|
IconButton: nodesContextRenderer(VtIconButton),
|
||||||
VtCartButton: nodesContextRenderer(VtCartButton),
|
VtCartButton: nodesContextRenderer(VtCartButton),
|
||||||
Link: nodesContextRenderer(VtLink),
|
Link: nodesContextRenderer(VtLink),
|
||||||
Image: nodesContextRenderer(VtImage),
|
Image: nodesContextRenderer(VtImage),
|
||||||
|
Dropdown: nodesContextRenderer(VtDropdown),
|
||||||
CartMismatchBanner: configOnly(CartMismatchBanner),
|
CartMismatchBanner: configOnly(CartMismatchBanner),
|
||||||
FreeShippingPriceNudge: configOnly(FreeShippingPriceNudge),
|
FreeShippingPriceNudge: configOnly(FreeShippingPriceNudge),
|
||||||
PropsChildren: {
|
PropsChildren: {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue