mirror of
https://github.com/vercel/commerce.git
synced 2025-06-16 12:21:20 +00:00
Update the query and ui to show the subitem of the menu
This commit is contained in:
parent
c799a5f53d
commit
6f16b74580
20
components/MegaMenu/mega-meu.tsx
Normal file
20
components/MegaMenu/mega-meu.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { MenuItem } from '../../lib/shopify/types';
|
||||||
|
import MenuItemComponent from './menu-item-component';
|
||||||
|
|
||||||
|
type MegaMenuProps = {
|
||||||
|
menu: MenuItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const MegaMenu = ({ menu }: MegaMenuProps) => {
|
||||||
|
return (
|
||||||
|
<div className="mt-4 hidden justify-center md:flex">
|
||||||
|
<ul className="text-md flex gap-6 font-semibold">
|
||||||
|
{menu.map((item) => (
|
||||||
|
<MenuItemComponent key={item.title} item={item} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MegaMenu;
|
38
components/MegaMenu/menu-item-component.tsx
Normal file
38
components/MegaMenu/menu-item-component.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { MenuItem } from '../../lib/shopify/types';
|
||||||
|
|
||||||
|
type MenuItemProps = {
|
||||||
|
item: MenuItem;
|
||||||
|
isSubMenu?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MenuItemComponent = ({ item, isSubMenu = false }: MenuItemProps) => {
|
||||||
|
return (
|
||||||
|
<li className={`group relative ${isSubMenu ? 'submenu-item p-2' : ''}`}>
|
||||||
|
<Link
|
||||||
|
href={item.path}
|
||||||
|
className="pl-2 font-semibold text-neutral-500 underline-offset-4 hover:text-black hover:underline dark:text-neutral-400 dark:hover:text-neutral-300"
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</Link>
|
||||||
|
{item.items.length > 0 && (
|
||||||
|
<ul className="absolute left-0 top-full z-10 hidden w-48 rounded-xl bg-white shadow-lg group-hover:block">
|
||||||
|
{item.items.map((subItem) => (
|
||||||
|
<MenuItemComponent key={subItem.title} item={subItem} isSubMenu={true} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
{isSubMenu && item.items.length > 0 && (
|
||||||
|
<ul className="absolute left-0 top-full z-10 hidden w-48 bg-white shadow-lg group-hover:block">
|
||||||
|
{item.items.map((subItem, index) =>
|
||||||
|
index === 2 ? (
|
||||||
|
<MenuItemComponent key={subItem.title} item={subItem} isSubMenu={true} />
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MenuItemComponent;
|
@ -1,9 +1,8 @@
|
|||||||
|
import MegaMenu from 'components/MegaMenu/mega-meu';
|
||||||
import Cart from 'components/cart';
|
import Cart from 'components/cart';
|
||||||
import OpenCart from 'components/cart/open-cart';
|
import OpenCart from 'components/cart/open-cart';
|
||||||
import LogoSquare from 'components/logo-square';
|
import LogoSquare from 'components/logo-square';
|
||||||
import { getMenu } from 'lib/shopify';
|
import { getMegaMenu } from 'lib/shopify';
|
||||||
import { Menu } from 'lib/shopify/types';
|
|
||||||
import { RemoveTheDomainFromUrl } from 'lib/utils';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import MobileMenu from './mobile-menu';
|
import MobileMenu from './mobile-menu';
|
||||||
@ -11,10 +10,11 @@ import Search, { SearchSkeleton } from './search';
|
|||||||
const { SITE_NAME } = process.env;
|
const { SITE_NAME } = process.env;
|
||||||
|
|
||||||
export default async function Navbar() {
|
export default async function Navbar() {
|
||||||
const menu = await getMenu('next-js-frontend-header-menu');
|
const menu = await getMegaMenu('next-js-frontend-header-menu');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="relative flex items-center justify-between p-4 lg:px-6">
|
<nav className="relative mx-auto max-w-screen-2xl p-4 lg:px-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
<div className="block flex-none md:hidden">
|
<div className="block flex-none md:hidden">
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<MobileMenu menu={menu} />
|
<MobileMenu menu={menu} />
|
||||||
@ -22,26 +22,15 @@ export default async function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex w-full items-center">
|
<div className="flex w-full items-center">
|
||||||
<div className="flex w-full md:w-1/3">
|
<div className="flex w-full md:w-1/3">
|
||||||
<Link href="/" className="mr-2 flex w-full items-center justify-center md:w-auto lg:mr-6">
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="mr-2 flex w-full items-center justify-center md:w-auto lg:mr-6"
|
||||||
|
>
|
||||||
<LogoSquare />
|
<LogoSquare />
|
||||||
<div className="ml-2 flex-none text-sm font-medium uppercase md:hidden lg:block">
|
<div className="ml-2 flex-none text-sm font-medium uppercase md:hidden lg:block">
|
||||||
{SITE_NAME}
|
{SITE_NAME}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
{menu.length ? (
|
|
||||||
<ul className="hidden gap-6 text-sm md:flex md:items-center">
|
|
||||||
{menu.map((item: Menu) => (
|
|
||||||
<li key={item.title}>
|
|
||||||
<Link
|
|
||||||
href={RemoveTheDomainFromUrl(item.path)}
|
|
||||||
className="text-neutral-500 underline-offset-4 hover:text-black hover:underline dark:text-neutral-400 dark:hover:text-neutral-300"
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden justify-center md:flex md:w-1/3">
|
<div className="hidden justify-center md:flex md:w-1/3">
|
||||||
<Suspense fallback={<SearchSkeleton />}>
|
<Suspense fallback={<SearchSkeleton />}>
|
||||||
@ -54,6 +43,8 @@ export default async function Navbar() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{menu.length ? <MegaMenu menu={menu} /> : null}
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
Connection,
|
Connection,
|
||||||
Image,
|
Image,
|
||||||
Menu,
|
Menu,
|
||||||
|
MenuItem,
|
||||||
Page,
|
Page,
|
||||||
Product,
|
Product,
|
||||||
ShopifyAddToCartOperation,
|
ShopifyAddToCartOperation,
|
||||||
@ -354,6 +355,30 @@ export async function getMenu(handle: string): Promise<Menu[]> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMegaMenu(handle: string): Promise<MenuItem[]> {
|
||||||
|
const res = await shopifyFetch<ShopifyMenuOperation>({
|
||||||
|
query: getMenuQuery,
|
||||||
|
tags: [TAGS.collections],
|
||||||
|
variables: {
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Function to map the menu items and their nested items
|
||||||
|
const mapMenuItems = (items: any): MenuItem[] => {
|
||||||
|
console.log(items);
|
||||||
|
return items.map((item: any) => ({
|
||||||
|
title: item.title,
|
||||||
|
path: item.url
|
||||||
|
? item.url.replace(domain, '').replace('/collections', '/search').replace('/pages', '')
|
||||||
|
: '',
|
||||||
|
items: item.items ? mapMenuItems(item.items) : []
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return res.body?.data?.menu?.items ? mapMenuItems(res.body.data.menu.items) : [];
|
||||||
|
}
|
||||||
|
|
||||||
export async function getPage(handle: string): Promise<Page> {
|
export async function getPage(handle: string): Promise<Page> {
|
||||||
const res = await shopifyFetch<ShopifyPageOperation>({
|
const res = await shopifyFetch<ShopifyPageOperation>({
|
||||||
query: getPageQuery,
|
query: getPageQuery,
|
||||||
|
@ -4,6 +4,14 @@ export const getMenuQuery = /* GraphQL */ `
|
|||||||
items {
|
items {
|
||||||
title
|
title
|
||||||
url
|
url
|
||||||
|
items {
|
||||||
|
title
|
||||||
|
url
|
||||||
|
items {
|
||||||
|
title
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,14 @@ export type Menu = {
|
|||||||
path: string;
|
path: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface MenuItem {
|
||||||
|
title: string;
|
||||||
|
path: string;
|
||||||
|
items: MenuItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MegaMenu = MenuItem[];
|
||||||
|
|
||||||
export type Money = {
|
export type Money = {
|
||||||
amount: string;
|
amount: string;
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
|
19
lib/utils.ts
19
lib/utils.ts
@ -1,5 +1,4 @@
|
|||||||
import { ReadonlyURLSearchParams } from 'next/navigation';
|
import { ReadonlyURLSearchParams } from 'next/navigation';
|
||||||
const { SHOPIFY_STORE_DOMAIN } = process.env;
|
|
||||||
|
|
||||||
export const createUrl = (pathname: string, params: URLSearchParams | ReadonlyURLSearchParams) => {
|
export const createUrl = (pathname: string, params: URLSearchParams | ReadonlyURLSearchParams) => {
|
||||||
const paramsString = params.toString();
|
const paramsString = params.toString();
|
||||||
@ -8,24 +7,6 @@ export const createUrl = (pathname: string, params: URLSearchParams | ReadonlyUR
|
|||||||
return `${pathname}${queryString}`;
|
return `${pathname}${queryString}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RemoveTheDomainFromUrl = (url: string) => {
|
|
||||||
const lowercaseString = `https://${SHOPIFY_STORE_DOMAIN}`.toLowerCase();
|
|
||||||
const modifiedUrl = url.replace(lowercaseString, '');
|
|
||||||
|
|
||||||
return modifiedUrl;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RemoveTheDomainFromArray = (menu: any) => {
|
|
||||||
const lowercaseString = `https://${SHOPIFY_STORE_DOMAIN}`.toLowerCase();
|
|
||||||
|
|
||||||
const modifiedUrls = menu.map((item: any) => {
|
|
||||||
const modifiedPath = item.path.replace(lowercaseString, '');
|
|
||||||
return { ...item, path: modifiedPath };
|
|
||||||
});
|
|
||||||
|
|
||||||
return modifiedUrls;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ensureStartsWith = (stringToCheck: string, startsWith: string) =>
|
export const ensureStartsWith = (stringToCheck: string, startsWith: string) =>
|
||||||
stringToCheck.startsWith(startsWith) ? stringToCheck : `${startsWith}${stringToCheck}`;
|
stringToCheck.startsWith(startsWith) ? stringToCheck : `${startsWith}${stringToCheck}`;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user