feat: create navigation menu for 3bear design
This commit is contained in:
parent
f21a4251e3
commit
f35fd8b09a
|
|
@ -24,7 +24,7 @@
|
||||||
"className": ""
|
"className": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"className": "bg-[#009b93] text-white"
|
"className": "sticky top-0 z-20 bg-[#009b93] text-white"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -55,79 +55,195 @@
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"DropdownMenus": {
|
"NavMenu": {
|
||||||
"props": {
|
"props": {
|
||||||
"label": "Shop",
|
"label": "Shop",
|
||||||
"className": "hover:text-ui-fg-base flex items-center gap-2",
|
"className": "",
|
||||||
"data-testid": "nav-categories-link",
|
"data-testid": "nav-categories-link",
|
||||||
"isShowArrow": true
|
"isShowArrow": true
|
||||||
},
|
},
|
||||||
"children": [
|
"menuItems": [
|
||||||
{
|
{
|
||||||
"DropdownMenuItems": {
|
"title": {
|
||||||
"props": {
|
"text": "Categories",
|
||||||
"label": "All Categories",
|
"className": "text-[#003F31] text-xl font-bold"
|
||||||
"className": "hover:text-ui-fg-base",
|
},
|
||||||
"data-testid": "nav-all-categories-link"
|
"links": [
|
||||||
}
|
{
|
||||||
}
|
"label": "Overnight Oats",
|
||||||
|
"href": "/categories/overnight-oats",
|
||||||
|
"className": "text-red-500"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DropdownMenuItems": {
|
"label": "Porridge",
|
||||||
"props": {
|
"href": "/categories/porridge"
|
||||||
"label": "New Arrivals",
|
|
||||||
"className": "hover:text-ui-fg-base flex items-center gap-2",
|
|
||||||
"data-testid": "nav-new-arrivals-link"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DropdownMenuItems": {
|
"label": "Cereals",
|
||||||
"props": {
|
"href": "/categories/cereals"
|
||||||
"label": "Best Sellers",
|
},
|
||||||
"className": "hover:text-ui-fg-base flex items-center gap-2",
|
{
|
||||||
"data-testid": "nav-best-sellers-link"
|
"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",
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": {
|
||||||
|
"text": "All products",
|
||||||
|
"className": "text-[#003F31] text-xl font-bold"
|
||||||
|
},
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"label": "Shop all",
|
||||||
|
"href": "/store"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DropdownMenus": {
|
"NavMenu": {
|
||||||
"props": {
|
"props": {
|
||||||
"label": "About us",
|
"label": "About us",
|
||||||
"className": "hover:text-ui-fg-base flex items-center gap-2",
|
"className": "",
|
||||||
"data-testid": "nav-categories-link",
|
"data-testid": "nav-categories-link",
|
||||||
"isShowArrow": true
|
"isShowArrow": true
|
||||||
},
|
},
|
||||||
"children": [
|
"menuItems": [
|
||||||
{
|
{
|
||||||
"DropdownMenuItems": {
|
"title": {
|
||||||
"props": {
|
"text": "About us",
|
||||||
"label": "All Categories",
|
"className": "text-[#003F31] text-xl font-bold"
|
||||||
"className": "hover:text-ui-fg-base flex items-center gap-2",
|
},
|
||||||
"data-testid": "nav-all-categories-link"
|
"links": [
|
||||||
}
|
{
|
||||||
}
|
"label": "Check",
|
||||||
|
"href": "/categories/overnight-oats",
|
||||||
|
"className": "text-red-500"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DropdownMenuItems": {
|
"label": "Porridge",
|
||||||
"props": {
|
"href": "/categories/porridge"
|
||||||
"label": "New Arrivals",
|
|
||||||
"className": "hover:text-ui-fg-base flex items-center gap-2",
|
|
||||||
"data-testid": "nav-new-arrivals-link"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DropdownMenuItems": {
|
"label": "Cereals",
|
||||||
"props": {
|
"href": "/categories/cereals"
|
||||||
"label": "Best Sellers",
|
},
|
||||||
"className": "hover:text-ui-fg-base",
|
{
|
||||||
"data-testid": "nav-best-sellers-link"
|
"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",
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": {
|
||||||
|
"text": "All products",
|
||||||
|
"className": "text-[#003F31] text-xl font-bold"
|
||||||
|
},
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"label": "Shop all",
|
||||||
|
"href": "/store"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ export default async function VtNav({ nodes, context, className }: DynamicLayout
|
||||||
const regions = await listRegions().then((regions: StoreRegion[]) => regions)
|
const regions = await listRegions().then((regions: StoreRegion[]) => regions)
|
||||||
|
|
||||||
return (
|
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") }>
|
<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">
|
<nav className="flex justify-between w-full items-center h-full">
|
||||||
{/* <div className="flex-1 basis-0 h-full flex items-center">
|
{/* <div className="flex-1 basis-0 h-full flex items-center">
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { User, MagnifyingGlassMini, Heart } from "@medusajs/icons"
|
||||||
import DropdownMenuComponent from "@modules/layout/templates/dropdown-menu/dropdown-menu"
|
import DropdownMenuComponent from "@modules/layout/templates/dropdown-menu/dropdown-menu"
|
||||||
import AnnouncementBannerVibenTec from "@modules/layout/templates/vibentec-template/announcement-bar"
|
import AnnouncementBannerVibenTec from "@modules/layout/templates/vibentec-template/announcement-bar"
|
||||||
import SearchButton from "@modules/layout/components/search-button"
|
import SearchButton from "@modules/layout/components/search-button"
|
||||||
|
import NavigationMenu from "@modules/layout/components/navigation-menu"
|
||||||
|
|
||||||
export interface LayoutComponentDefinition {
|
export interface LayoutComponentDefinition {
|
||||||
props?: Record<string, any>
|
props?: Record<string, any>
|
||||||
|
|
@ -92,6 +93,11 @@ export const componentMap: Record<string, ComponentRenderer> = {
|
||||||
<DropdownMenuComponent {...entry} />
|
<DropdownMenuComponent {...entry} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
NavMenu: {
|
||||||
|
render: (entry: any, ctx: LayoutContext) => (
|
||||||
|
<NavigationMenu props={entry.props} menuItems={entry.menuItems} />
|
||||||
|
),
|
||||||
|
},
|
||||||
LocalizedClientLink: {
|
LocalizedClientLink: {
|
||||||
render: (entry: any) => (
|
render: (entry: any) => (
|
||||||
<LocalizedClientLink {...entry.props}>
|
<LocalizedClientLink {...entry.props}>
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,38 @@ module.exports = {
|
||||||
"0%": { transform: "translateY(-100%)" },
|
"0%": { transform: "translateY(-100%)" },
|
||||||
"100%": { transform: "translateY(0)" },
|
"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: {
|
animation: {
|
||||||
ring: "ring 2.2s cubic-bezier(0.5, 0, 0.5, 1) infinite",
|
ring: "ring 2.2s cubic-bezier(0.5, 0, 0.5, 1) infinite",
|
||||||
|
|
@ -156,6 +188,14 @@ module.exports = {
|
||||||
enter: "enter 200ms ease-out",
|
enter: "enter 200ms ease-out",
|
||||||
"slide-in": "slide-in 1.2s cubic-bezier(.41,.73,.51,1.02)",
|
"slide-in": "slide-in 1.2s cubic-bezier(.41,.73,.51,1.02)",
|
||||||
leave: "leave 150ms ease-in forwards",
|
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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue