Compare commits

..

2 Commits

7 changed files with 344 additions and 51 deletions

View File

@ -24,7 +24,7 @@
"className": ""
}
],
"className": "bg-[#009b93] text-white"
"className": "sticky top-0 z-20 bg-[#009b93] text-white"
}
}
},
@ -55,79 +55,195 @@
},
"children": [
{
"DropdownMenus": {
"NavMenu": {
"props": {
"label": "Shop",
"className": "hover:text-ui-fg-base flex items-center gap-2",
"className": "",
"data-testid": "nav-categories-link",
"isShowArrow": true
},
"children": [
"menuItems": [
{
"DropdownMenuItems": {
"props": {
"label": "All Categories",
"className": "hover:text-ui-fg-base",
"data-testid": "nav-all-categories-link"
"title": {
"text": "Categories",
"className": "text-[#003F31] text-xl font-bold"
},
"links": [
{
"label": "Overnight Oats",
"href": "/categories/overnight-oats",
"className": "text-red-500"
},
{
"label": "Porridge",
"href": "/categories/porridge"
},
{
"label": "Cereals",
"href": "/categories/cereals"
},
{
"label": "Granola",
"href": "/categories/granola"
},
{
"label": "Glasses & Bowls",
"href": "/categories/glasses-bowls"
},
{
"label": "Oat Bars",
"href": "/categories/oat-bars"
},
{
"label": "Nut butters",
"href": "/categories/nut-butters"
}
}
]
},
{
"DropdownMenuItems": {
"props": {
"label": "New Arrivals",
"className": "hover:text-ui-fg-base flex items-center gap-2",
"data-testid": "nav-new-arrivals-link"
"title": {
"text": "Specials",
"className": "text-[#003F31] text-xl font-bold"
},
"links": [
{
"label": "Advent calendar ✨",
"href": "/collections/advent-calendar"
},
{
"label": "Saver subscription",
"href": "/collections/saver-subscription"
},
{
"label": "bestseller",
"href": "/collections/bestseller"
},
{
"label": "New 🔥",
"href": "/collections/new"
},
{
"label": "Bluey Kidsrange",
"href": "/collections/bluey-kidsrange"
},
{
"label": "Value sets",
"href": "/collections/value-sets"
},
{
"label": "Sale",
"href": "/collections/sale"
}
}
]
},
{
"DropdownMenuItems": {
"props": {
"label": "Best Sellers",
"className": "hover:text-ui-fg-base flex items-center gap-2",
"data-testid": "nav-best-sellers-link"
"title": {
"text": "All products",
"className": "text-[#003F31] text-xl font-bold"
},
"links": [
{
"label": "Shop all",
"href": "/store"
}
}
]
}
]
}
},
{
"DropdownMenus": {
"NavMenu": {
"props": {
"label": "About us",
"className": "hover:text-ui-fg-base flex items-center gap-2",
"className": "",
"data-testid": "nav-categories-link",
"isShowArrow": true
},
"children": [
"menuItems": [
{
"DropdownMenuItems": {
"props": {
"label": "All Categories",
"className": "hover:text-ui-fg-base flex items-center gap-2",
"data-testid": "nav-all-categories-link"
"title": {
"text": "About us",
"className": "text-[#003F31] text-xl font-bold"
},
"links": [
{
"label": "Check",
"href": "/categories/overnight-oats",
"className": "text-red-500"
},
{
"label": "Porridge",
"href": "/categories/porridge"
},
{
"label": "Cereals",
"href": "/categories/cereals"
},
{
"label": "Granola",
"href": "/categories/granola"
},
{
"label": "Glasses & Bowls",
"href": "/categories/glasses-bowls"
},
{
"label": "Oat Bars",
"href": "/categories/oat-bars"
},
{
"label": "Nut butters",
"href": "/categories/nut-butters"
}
}
]
},
{
"DropdownMenuItems": {
"props": {
"label": "New Arrivals",
"className": "hover:text-ui-fg-base flex items-center gap-2",
"data-testid": "nav-new-arrivals-link"
"title": {
"text": "Specials",
"className": "text-[#003F31] text-xl font-bold"
},
"links": [
{
"label": "Advent calendar ✨",
"href": "/collections/advent-calendar"
},
{
"label": "Saver subscription",
"href": "/collections/saver-subscription"
},
{
"label": "bestseller",
"href": "/collections/bestseller"
},
{
"label": "New 🔥",
"href": "/collections/new"
},
{
"label": "Bluey Kidsrange",
"href": "/collections/bluey-kidsrange"
},
{
"label": "Value sets",
"href": "/collections/value-sets"
},
{
"label": "Sale",
"href": "/collections/sale"
}
}
]
},
{
"DropdownMenuItems": {
"props": {
"label": "Best Sellers",
"className": "hover:text-ui-fg-base",
"data-testid": "nav-best-sellers-link"
"title": {
"text": "All products",
"className": "text-[#003F31] text-xl font-bold"
},
"links": [
{
"label": "Shop all",
"href": "/store"
}
}
]
}
]
}

View File

@ -0,0 +1,125 @@
"use client"
import * as React from "react"
import LocalizedClientLink from "@modules/common/components/localized-client-link"
import { clx } from "@medusajs/ui"
import { ChevronDownMini } from "@medusajs/icons"
interface NavigationMenuProps {
props: {
label: string,
className: string,
"data-testid": string,
isShowArrow: boolean
},
menuItems: MenuSection[]
}
type MenuSection = {
title: { text: string; className?: string }
links: MenuLink[]
}
type MenuLink = { label: string; href: string; className?: string }
// Structured menu data used to map UI
const menuSections: MenuSection[] = [
{
title: { text: "Categories", className: "text-red" },
links: [
{
label: "Overnight Oats",
href: "/categories/overnight-oats",
className: "text-red",
},
{ label: "Porridge", href: "/categories/porridge" },
{ label: "Cereals", href: "/categories/cereals" },
{ label: "Granola", href: "/categories/granola" },
{ label: "Glasses & Bowls", href: "/categories/glasses-bowls" },
{ label: "Oat Bars", href: "/categories/oat-bars" },
{ label: "Nut butters", href: "/categories/nut-butters" },
],
},
{
title: { text: "Specials" },
links: [
{ label: "Advent calendar ✨", href: "/collections/advent-calendar" },
{ label: "Saver subscription", href: "/collections/saver-subscription" },
{ label: "bestseller", href: "/collections/bestseller" },
{ label: "New 🔥", href: "/collections/new" },
{ label: "Bluey Kidsrange", href: "/collections/bluey-kidsrange" },
{ label: "Value sets", href: "/collections/value-sets" },
{ label: "Sale", href: "/collections/sale" },
],
},
{
title: { text: "All products" },
links: [{ label: "Shop all", href: "/store" }],
},
]
const MenuLinkItem = ({ href, label, className }: MenuLink) => (
<li key={label}>
<LocalizedClientLink
href={href}
className={clx(
"block rounded-md py-2 hover:bg-ui-bg-subtle ",
className ?? ""
)}
role="menuitem"
>
<div className="txt-small text-ui-fg-muted">{label}</div>
</LocalizedClientLink>
</li>
)
const MenuSectionItem = ({ title, links }: MenuSection) => (
<div key={title.text} className="space-y-1">
<div className={clx("txt-small text-ui-fg-muted", title.className ?? "")}>
{title.text}
</div>
<ul className="space-y-1">
{links.map((link) => (
<MenuLinkItem
key={link.label}
href={link.href}
label={link.label}
className={link.className ?? ""}
/>
))}
</ul>
</div>
)
export default function NavigationMenu({
props,
menuItems = menuSections,
}: NavigationMenuProps) {
return (
<div className="group relative h-full flex items-center">
<LocalizedClientLink
href="/"
className="txt-compact-xlarge-plus text-ui-fg-subtle hover:text-ui-fg-base group-hover:text-ui-fg-base flex items-center gap-2 transition-colors duration-200"
>
{props.label} {props?.isShowArrow ? (
<span className="transition-transform duration-400 ease-out group-hover:rotate-180">
<ChevronDownMini />
</span>
) : null}
</LocalizedClientLink>
<div
role="menu"
className="pointer-events-none left-0 fixed top-[180px] invisible opacity-0 transform-gpu translate-y-2 transition-all duration-600 ease-out group-hover:visible group-hover:opacity-100 group-hover:translate-y-0 group-hover:pointer-events-auto group-focus-within:visible group-focus-within:opacity-100 group-focus-within:translate-y-0 group-focus-within:pointer-events-auto"
>
<div className="w-[100vw] bg-white shadow-borders-base py-4 px-20">
<div className="grid grid-cols-1 small:grid-cols-3 gap-6">
{menuItems.map((section) => (
<MenuSectionItem
key={section.title.text}
title={section.title}
links={section.links}
/>
))}
</div>
</div>
</div>
</div>
)
}

View File

@ -1,17 +1,19 @@
'use client'
import { DropdownMenu } from "@medusajs/ui"
import { ChevronDownMini } from "@medusajs/icons"
interface DropdownMenuProps {
className?: string
"data-testid"?: string
label?: string
isShowArrow?: boolean
}
export default function DropdownMenuComponent({
children,
props,
}: {
children?: React.ReactNode
props?: {
className?: string
"data-testid"?: string
label?: string
isShowArrow?: boolean
}
props?: DropdownMenuProps
}) {
const itemNodes = Array.isArray(children) ? children : []
@ -26,7 +28,7 @@ export default function DropdownMenuComponent({
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Content className="abc">
{itemNodes.map((item, index) => {
const props = item?.DropdownMenuItems?.props
return props ? <DropdownMenu.Item key={props.label ?? index} className={props.className} data-testid={props["data-testid"]}>{props.label}</DropdownMenu.Item> : null

View File

@ -9,7 +9,7 @@ export default async function VtNav({ nodes, context, className }: DynamicLayout
const regions = await listRegions().then((regions: StoreRegion[]) => regions)
return (
<div className="sticky top-0 inset-x-0 z-50 group">
<div className="sticky top-[40px] inset-x-0 z-50">
<header className={clx("relative mx-auto border-b duration-200 border-ui-border-base", className ?? "bg-white") }>
<nav className="flex justify-between w-full items-center h-full">
{/* <div className="flex-1 basis-0 h-full flex items-center">

View File

@ -110,3 +110,7 @@
@apply text-[32px] leading-[44px] font-semibold;
}
}
[data-radix-popper-content-wrapper]{
z-index: 100 !important;
}

View File

@ -14,6 +14,7 @@ import { User, MagnifyingGlassMini, Heart } from "@medusajs/icons"
import DropdownMenuComponent from "@modules/layout/templates/dropdown-menu/dropdown-menu"
import AnnouncementBannerVibenTec from "@modules/layout/templates/vibentec-template/announcement-bar"
import SearchButton from "@modules/layout/components/search-button"
import NavigationMenu from "@modules/layout/components/navigation-menu"
export interface LayoutComponentDefinition {
props?: Record<string, any>
@ -92,6 +93,11 @@ export const componentMap: Record<string, ComponentRenderer> = {
<DropdownMenuComponent {...entry} />
),
},
NavMenu: {
render: (entry: any, ctx: LayoutContext) => (
<NavigationMenu props={entry.props} menuItems={entry.menuItems} />
),
},
LocalizedClientLink: {
render: (entry: any) => (
<LocalizedClientLink {...entry.props}>

View File

@ -141,6 +141,38 @@ module.exports = {
"0%": { transform: "translateY(-100%)" },
"100%": { transform: "translateY(0)" },
},
enterFromRight: {
from: { opacity: "0", transform: "translateX(200px)" },
to: { opacity: "1", transform: "translateX(0)" },
},
enterFromLeft: {
from: { opacity: "0", transform: "translateX(-200px)" },
to: { opacity: "1", transform: "translateX(0)" },
},
exitToRight: {
from: { opacity: "1", transform: "translateX(0)" },
to: { opacity: "0", transform: "translateX(200px)" },
},
exitToLeft: {
from: { opacity: "1", transform: "translateX(0)" },
to: { opacity: "0", transform: "translateX(-200px)" },
},
scaleIn: {
from: { opacity: "0", transform: "rotateX(-10deg) scale(0.9)" },
to: { opacity: "1", transform: "rotateX(0deg) scale(1)" },
},
scaleOut: {
from: { opacity: "1", transform: "rotateX(0deg) scale(1)" },
to: { opacity: "0", transform: "rotateX(-10deg) scale(0.95)" },
},
fadeIn: {
from: { opacity: "0" },
to: { opacity: "1" },
},
fadeOut: {
from: { opacity: "1" },
to: { opacity: "0" },
},
},
animation: {
ring: "ring 2.2s cubic-bezier(0.5, 0, 0.5, 1) infinite",
@ -156,6 +188,14 @@ module.exports = {
enter: "enter 200ms ease-out",
"slide-in": "slide-in 1.2s cubic-bezier(.41,.73,.51,1.02)",
leave: "leave 150ms ease-in forwards",
scaleIn: "scaleIn 200ms ease",
scaleOut: "scaleOut 200ms ease",
fadeIn: "fadeIn 200ms ease",
fadeOut: "fadeOut 200ms ease",
enterFromLeft: "enterFromLeft 250ms ease",
enterFromRight: "enterFromRight 250ms ease",
exitToLeft: "exitToLeft 250ms ease",
exitToRight: "exitToRight 250ms ease",
},
},
},