mirror of
https://github.com/vercel/commerce.git
synced 2025-04-27 13:27:50 +00:00
working on nav bar
This commit is contained in:
parent
ad785c9f9a
commit
d9625e21bd
@ -1,16 +1,16 @@
|
||||
import type { Metadata } from 'next';
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import Prose from 'components/prose';
|
||||
import { getPage } from 'lib/shopify';
|
||||
import { notFound } from 'next/navigation';
|
||||
import Prose from 'components/prose'
|
||||
import { getPage } from 'lib/shopify'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
export async function generateMetadata(props: {
|
||||
params: Promise<{ page: string }>;
|
||||
params: Promise<{ page: string }>
|
||||
}): Promise<Metadata> {
|
||||
const params = await props.params;
|
||||
const page = await getPage(params.page);
|
||||
const params = await props.params
|
||||
const page = await getPage(params.page)
|
||||
|
||||
if (!page) return notFound();
|
||||
if (!page) return notFound()
|
||||
|
||||
return {
|
||||
title: page.seo?.title || page.title,
|
||||
@ -18,28 +18,34 @@ export async function generateMetadata(props: {
|
||||
openGraph: {
|
||||
publishedTime: page.createdAt,
|
||||
modifiedTime: page.updatedAt,
|
||||
type: 'article'
|
||||
}
|
||||
};
|
||||
type: 'article',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Page(props: { params: Promise<{ page: string }> }) {
|
||||
const params = await props.params;
|
||||
const page = await getPage(params.page);
|
||||
export default async function Page(props: {
|
||||
params: Promise<{ page: string }>
|
||||
}) {
|
||||
const params = await props.params
|
||||
const page = await getPage(params.page)
|
||||
|
||||
if (!page) return notFound();
|
||||
console.log('page', params)
|
||||
if (!page) return notFound()
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="mb-8 text-5xl font-bold">{page.title}</h1>
|
||||
<Prose className="mb-8" html={page.body} />
|
||||
<p className="text-sm italic">
|
||||
{`This document was last updated on ${new Intl.DateTimeFormat(undefined, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
}).format(new Date(page.updatedAt))}.`}
|
||||
{`This document was last updated on ${new Intl.DateTimeFormat(
|
||||
undefined,
|
||||
{
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
},
|
||||
).format(new Date(page.updatedAt))}.`}
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
@ -30,8 +30,9 @@
|
||||
--accent-7: #333333;
|
||||
--accent-8: #111111;
|
||||
--accent-9: #000;
|
||||
--font-sans: -apple-system, system-ui, BlinkMacSystemFont, 'Helvetica Neue',
|
||||
'Helvetica', sans-serif;
|
||||
--font-sans:
|
||||
-apple-system, system-ui, BlinkMacSystemFont, 'Helvetica Neue', 'Helvetica',
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
|
@ -1,3 +1,2 @@
|
||||
@import './base.css';
|
||||
@import './components.css';
|
||||
@import './utilities.css';
|
||||
|
@ -1,40 +1,45 @@
|
||||
import { CartProvider } from 'components/cart/cart-context';
|
||||
import { Navbar } from 'components/layout/navbar';
|
||||
import { WelcomeToast } from 'components/welcome-toast';
|
||||
import { GeistSans } from 'geist/font/sans';
|
||||
import { getCart } from 'lib/shopify';
|
||||
import { ReactNode } from 'react';
|
||||
import { Toaster } from 'sonner';
|
||||
import './globals.css';
|
||||
import { baseUrl } from 'lib/utils';
|
||||
import { CartProvider } from 'components/cart/cart-context'
|
||||
// import { Navbar } from 'components/layout/navbar'
|
||||
import NavbarV2 from 'components/layout/navbar/NavbarV2'
|
||||
import { WelcomeToast } from 'components/welcome-toast'
|
||||
import { GeistSans } from 'geist/font/sans'
|
||||
import { getCart } from 'lib/shopify'
|
||||
import { ReactNode } from 'react'
|
||||
import { Toaster } from 'sonner'
|
||||
import './globals.css'
|
||||
import './assets/main.css'
|
||||
import '../components/layout/navbar/Navbar.css'
|
||||
|
||||
const { SITE_NAME } = process.env;
|
||||
import { baseUrl } from 'lib/utils'
|
||||
|
||||
const { SITE_NAME } = process.env
|
||||
|
||||
export const metadata = {
|
||||
metadataBase: new URL(baseUrl),
|
||||
title: {
|
||||
default: SITE_NAME!,
|
||||
template: `%s | ${SITE_NAME}`
|
||||
template: `%s | ${SITE_NAME}`,
|
||||
},
|
||||
robots: {
|
||||
follow: true,
|
||||
index: true
|
||||
}
|
||||
};
|
||||
index: true,
|
||||
},
|
||||
}
|
||||
|
||||
export default async function RootLayout({
|
||||
children
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
children: ReactNode
|
||||
}) {
|
||||
// Don't await the fetch, pass the Promise to the context provider
|
||||
const cart = getCart();
|
||||
const cart = getCart()
|
||||
|
||||
return (
|
||||
<html lang="en" className={GeistSans.variable}>
|
||||
<body className="bg-neutral-50 text-black selection:bg-teal-300 dark:bg-neutral-900 dark:text-white dark:selection:bg-pink-500 dark:selection:text-white">
|
||||
<CartProvider cartPromise={cart}>
|
||||
<Navbar />
|
||||
{/* <Navbar /> */}
|
||||
<NavbarV2 />
|
||||
<main>
|
||||
{children}
|
||||
<Toaster closeButton />
|
||||
@ -43,5 +48,5 @@ export default async function RootLayout({
|
||||
</CartProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ export const metadata = {
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<>
|
||||
{/* <ThreeItemGrid /> */}
|
||||
{/* <Carousel /> */}
|
||||
{/* <Footer /> */}
|
||||
<ThreeItemGrid />
|
||||
<Carousel />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { notFound } from 'next/navigation';
|
||||
import type { Metadata } from 'next'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
import { GridTileImage } from 'components/grid/tile';
|
||||
import Footer from 'components/layout/footer';
|
||||
import { Gallery } from 'components/product/gallery';
|
||||
import { ProductProvider } from 'components/product/product-context';
|
||||
import { ProductDescription } from 'components/product/product-description';
|
||||
import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
|
||||
import { getProduct, getProductRecommendations } from 'lib/shopify';
|
||||
import { Image } from 'lib/shopify/types';
|
||||
import Link from 'next/link';
|
||||
import { Suspense } from 'react';
|
||||
import { GridTileImage } from 'components/grid/tile'
|
||||
import Footer from 'components/layout/footer'
|
||||
import { Gallery } from 'components/product/gallery'
|
||||
import { ProductProvider } from 'components/product/product-context'
|
||||
import { ProductDescription } from 'components/product/product-description'
|
||||
import { HIDDEN_PRODUCT_TAG } from 'lib/constants'
|
||||
import { getProduct, getProductRecommendations } from 'lib/shopify'
|
||||
import { Image } from 'lib/shopify/types'
|
||||
import Link from 'next/link'
|
||||
import { Suspense } from 'react'
|
||||
|
||||
export async function generateMetadata(props: {
|
||||
params: Promise<{ handle: string }>;
|
||||
params: Promise<{ handle: string }>
|
||||
}): Promise<Metadata> {
|
||||
const params = await props.params;
|
||||
const product = await getProduct(params.handle);
|
||||
const params = await props.params
|
||||
const product = await getProduct(params.handle)
|
||||
|
||||
if (!product) return notFound();
|
||||
if (!product) return notFound()
|
||||
|
||||
const { url, width, height, altText: alt } = product.featuredImage || {};
|
||||
const indexable = !product.tags.includes(HIDDEN_PRODUCT_TAG);
|
||||
const { url, width, height, altText: alt } = product.featuredImage || {}
|
||||
const indexable = !product.tags.includes(HIDDEN_PRODUCT_TAG)
|
||||
|
||||
return {
|
||||
title: product.seo.title || product.title,
|
||||
@ -46,16 +46,16 @@ export async function generateMetadata(props: {
|
||||
],
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default async function ProductPage(props: {
|
||||
params: Promise<{ handle: string }>;
|
||||
params: Promise<{ handle: string }>
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const product = await getProduct(params.handle);
|
||||
const params = await props.params
|
||||
const product = await getProduct(params.handle)
|
||||
|
||||
if (!product) return notFound();
|
||||
if (!product) return notFound()
|
||||
|
||||
const productJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
@ -72,22 +72,22 @@ export default async function ProductPage(props: {
|
||||
highPrice: product.priceRange.maxVariantPrice.amount,
|
||||
lowPrice: product.priceRange.minVariantPrice.amount,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<ProductProvider>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify(productJsonLd),
|
||||
}}
|
||||
/>
|
||||
<div className='mx-auto max-w-(--breakpoint-2xl) px-4'>
|
||||
<div className='flex flex-col rounded-lg border border-neutral-200 bg-white p-8 md:p-12 lg:flex-row lg:gap-8 dark:border-neutral-800 dark:bg-black'>
|
||||
<div className='h-full w-full basis-full lg:basis-4/6'>
|
||||
<div className="mx-auto max-w-(--breakpoint-2xl) px-4">
|
||||
<div className="flex flex-col rounded-lg border border-neutral-200 bg-white p-8 md:p-12 lg:flex-row lg:gap-8 dark:border-neutral-800 dark:bg-black">
|
||||
<div className="h-full w-full basis-full lg:basis-4/6">
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className='relative aspect-square h-full max-h-[550px] w-full overflow-hidden' />
|
||||
<div className="relative aspect-square h-full max-h-[550px] w-full overflow-hidden" />
|
||||
}
|
||||
>
|
||||
<Gallery
|
||||
@ -99,7 +99,7 @@ export default async function ProductPage(props: {
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
<div className='basis-full lg:basis-2/6'>
|
||||
<div className="basis-full lg:basis-2/6">
|
||||
<Suspense fallback={null}>
|
||||
<ProductDescription product={product} />
|
||||
</Suspense>
|
||||
@ -109,25 +109,25 @@ export default async function ProductPage(props: {
|
||||
</div>
|
||||
<Footer />
|
||||
</ProductProvider>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
async function RelatedProducts({ id }: { id: string }) {
|
||||
const relatedProducts = await getProductRecommendations(id);
|
||||
const relatedProducts = await getProductRecommendations(id)
|
||||
|
||||
if (!relatedProducts.length) return null;
|
||||
if (!relatedProducts.length) return null
|
||||
|
||||
return (
|
||||
<div className='py-8'>
|
||||
<h2 className='mb-4 text-2xl font-bold'>Related Products</h2>
|
||||
<ul className='flex w-full gap-4 overflow-x-auto pt-1'>
|
||||
<div className="py-8">
|
||||
<h2 className="mb-4 text-2xl font-bold">Related Products</h2>
|
||||
<ul className="flex w-full gap-4 overflow-x-auto pt-1">
|
||||
{relatedProducts.map((product) => (
|
||||
<li
|
||||
key={product.handle}
|
||||
className='aspect-square w-full flex-none min-[475px]:w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/5'
|
||||
className="aspect-square w-full flex-none min-[475px]:w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/5"
|
||||
>
|
||||
<Link
|
||||
className='relative h-full w-full'
|
||||
className="relative h-full w-full"
|
||||
href={`/product/${product.handle}`}
|
||||
prefetch={true}
|
||||
>
|
||||
@ -140,12 +140,12 @@ async function RelatedProducts({ id }: { id: string }) {
|
||||
}}
|
||||
src={product.featuredImage?.url}
|
||||
fill
|
||||
sizes='(min-width: 1024px) 20vw, (min-width: 768px) 25vw, (min-width: 640px) 33vw, (min-width: 475px) 50vw, 100vw'
|
||||
sizes="(min-width: 1024px) 20vw, (min-width: 768px) 25vw, (min-width: 640px) 33vw, (min-width: 475px) 50vw, 100vw"
|
||||
/>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
@ -1,35 +1,42 @@
|
||||
import { getCollection, getCollectionProducts } from 'lib/shopify';
|
||||
import { Metadata } from 'next';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { getCollection, getCollectionProducts } from 'lib/shopify'
|
||||
import { Metadata } from 'next'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
import Grid from 'components/grid';
|
||||
import ProductGridItems from 'components/layout/product-grid-items';
|
||||
import { defaultSort, sorting } from 'lib/constants';
|
||||
import Grid from 'components/grid'
|
||||
import ProductGridItems from 'components/layout/product-grid-items'
|
||||
import { defaultSort, sorting } from 'lib/constants'
|
||||
|
||||
export async function generateMetadata(props: {
|
||||
params: Promise<{ collection: string }>;
|
||||
params: Promise<{ collection: string }>
|
||||
}): Promise<Metadata> {
|
||||
const params = await props.params;
|
||||
const collection = await getCollection(params.collection);
|
||||
const params = await props.params
|
||||
const collection = await getCollection(params.collection)
|
||||
|
||||
if (!collection) return notFound();
|
||||
if (!collection) return notFound()
|
||||
|
||||
return {
|
||||
title: collection.seo?.title || collection.title,
|
||||
description:
|
||||
collection.seo?.description || collection.description || `${collection.title} products`
|
||||
};
|
||||
collection.seo?.description ||
|
||||
collection.description ||
|
||||
`${collection.title} products`,
|
||||
}
|
||||
}
|
||||
|
||||
export default async function CategoryPage(props: {
|
||||
params: Promise<{ collection: string }>;
|
||||
searchParams?: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||
params: Promise<{ collection: string }>
|
||||
searchParams?: Promise<{ [key: string]: string | string[] | undefined }>
|
||||
}) {
|
||||
const searchParams = await props.searchParams;
|
||||
const params = await props.params;
|
||||
const { sort } = searchParams as { [key: string]: string };
|
||||
const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort;
|
||||
const products = await getCollectionProducts({ collection: params.collection, sortKey, reverse });
|
||||
const searchParams = await props.searchParams
|
||||
const params = await props.params
|
||||
const { sort } = searchParams as { [key: string]: string }
|
||||
const { sortKey, reverse } =
|
||||
sorting.find((item) => item.slug === sort) || defaultSort
|
||||
const products = await getCollectionProducts({
|
||||
collection: params.collection,
|
||||
sortKey,
|
||||
reverse,
|
||||
})
|
||||
|
||||
return (
|
||||
<section>
|
||||
@ -41,5 +48,5 @@ export default async function CategoryPage(props: {
|
||||
</Grid>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
.root {
|
||||
@apply inset-0 fixed;
|
||||
left: 72px;
|
||||
z-index: 10;
|
||||
height: 100vh;
|
||||
min-width: 100vw;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@media screen(lg) {
|
||||
.root {
|
||||
@apply static;
|
||||
min-width: inherit;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
@apply text-primary flex cursor-pointer px-6 py-3
|
||||
transition ease-in-out duration-150 leading-6
|
||||
font-medium items-center capitalize w-full box-border
|
||||
outline-0;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
@apply bg-accent-1 outline-none;
|
||||
}
|
||||
|
||||
.link.active {
|
||||
@apply font-bold bg-accent-2;
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
import cn from 'clsx'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Moon, Sun } from '@components/icons'
|
||||
import s from './CustomerMenuContent.module.css'
|
||||
import useLogout from '@framework/auth/use-logout'
|
||||
import {
|
||||
DropdownContent,
|
||||
DropdownMenuItem,
|
||||
} from '@components/ui/Dropdown/Dropdown'
|
||||
|
||||
const LINKS = [
|
||||
{
|
||||
name: 'My Orders',
|
||||
href: '/orders',
|
||||
},
|
||||
{
|
||||
name: 'My Profile',
|
||||
href: '/profile',
|
||||
},
|
||||
{
|
||||
name: 'My Cart',
|
||||
href: '/cart',
|
||||
},
|
||||
]
|
||||
|
||||
export default function CustomerMenuContent() {
|
||||
const router = useRouter()
|
||||
const logout = useLogout()
|
||||
const { pathname } = useRouter()
|
||||
const { theme, setTheme } = useTheme()
|
||||
|
||||
function handleClick(_: React.MouseEvent<HTMLAnchorElement>, href: string) {
|
||||
router.push(href)
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownContent
|
||||
asChild
|
||||
side="bottom"
|
||||
sideOffset={10}
|
||||
className={s.root}
|
||||
id="CustomerMenuContent"
|
||||
>
|
||||
{LINKS.map(({ name, href }) => (
|
||||
<DropdownMenuItem key={href}>
|
||||
<a
|
||||
className={cn(s.link, {
|
||||
[s.active]: pathname === href,
|
||||
})}
|
||||
onClick={(e) => handleClick(e, href)}
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
<DropdownMenuItem>
|
||||
<a
|
||||
className={cn(s.link, 'justify-between')}
|
||||
onClick={() => {
|
||||
setTheme(theme === 'dark' ? 'light' : 'dark')
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
Theme: <strong>{theme}</strong>{' '}
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
{theme == 'dark' ? (
|
||||
<Moon width={20} height={20} />
|
||||
) : (
|
||||
<Sun width={20} height={20} />
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<a
|
||||
className={cn(s.link, 'border-t border-accent-2 mt-4')}
|
||||
onClick={() => logout()}
|
||||
>
|
||||
Logout
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
</DropdownContent>
|
||||
)
|
||||
}
|
1
components/layout/UserNav/CustomerMenuContent/index.ts
Normal file
1
components/layout/UserNav/CustomerMenuContent/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './CustomerMenuContent'
|
@ -0,0 +1,7 @@
|
||||
.root {
|
||||
@apply px-4 sm:px-6 sm:w-full flex-1 z-20;
|
||||
}
|
||||
|
||||
.item {
|
||||
@apply text-xl font-bold py-2;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import Link from 'next/link'
|
||||
import s from './MenuSidebarView.module.css'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import SidebarLayout from '@components/common/SidebarLayout'
|
||||
import type { Link as LinkProps } from './index'
|
||||
|
||||
export default function MenuSidebarView({
|
||||
links = [],
|
||||
}: {
|
||||
links?: LinkProps[]
|
||||
}) {
|
||||
const { closeSidebar } = useUI()
|
||||
|
||||
return (
|
||||
<SidebarLayout handleClose={() => closeSidebar()}>
|
||||
<div className={s.root}>
|
||||
<nav>
|
||||
<ul>
|
||||
<li className={s.item} onClick={() => closeSidebar()}>
|
||||
<Link href="/search">All</Link>
|
||||
</li>
|
||||
{links.map((l: any) => (
|
||||
<li
|
||||
key={l.href}
|
||||
className={s.item}
|
||||
onClick={() => closeSidebar()}
|
||||
>
|
||||
<Link href={l.href}>{l.label}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</SidebarLayout>
|
||||
)
|
||||
}
|
||||
|
||||
MenuSidebarView
|
5
components/layout/UserNav/MenuSidebarView/index.ts
Normal file
5
components/layout/UserNav/MenuSidebarView/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export { default } from './MenuSidebarView'
|
||||
export interface Link {
|
||||
href: string
|
||||
label: string
|
||||
}
|
59
components/layout/UserNav/UserNav.module.css
Normal file
59
components/layout/UserNav/UserNav.module.css
Normal file
@ -0,0 +1,59 @@
|
||||
.root {
|
||||
@apply relative flex items-center;
|
||||
}
|
||||
|
||||
.list {
|
||||
@apply flex flex-row items-center justify-items-end h-full;
|
||||
}
|
||||
|
||||
.item {
|
||||
@apply ml-6 cursor-pointer relative transition ease-in-out
|
||||
duration-100 flex items-center outline-none text-primary;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
@apply text-accent-6 transition scale-110 duration-100;
|
||||
}
|
||||
|
||||
.item:first-child {
|
||||
@apply ml-0;
|
||||
}
|
||||
|
||||
.item:focus,
|
||||
.item:active {
|
||||
@apply outline-none;
|
||||
}
|
||||
|
||||
.bagCount {
|
||||
@apply border border-black bg-secondary text-secondary absolute rounded-full right-3 top-3 flex items-center justify-center font-bold text-xs;
|
||||
padding-left: 2.5px;
|
||||
padding-right: 2.5px;
|
||||
min-width: 1.25rem;
|
||||
min-height: 1.25rem;
|
||||
}
|
||||
|
||||
.avatarButton {
|
||||
@apply inline-flex justify-center rounded-full;
|
||||
}
|
||||
|
||||
.mobileMenu {
|
||||
@apply flex lg:hidden ml-6 text-white;
|
||||
}
|
||||
|
||||
.avatarButton:focus,
|
||||
.mobileMenu:focus {
|
||||
@apply outline-none;
|
||||
}
|
||||
|
||||
.dropdownDesktop {
|
||||
@apply hidden -z-10;
|
||||
}
|
||||
|
||||
@media screen(lg) {
|
||||
.dropdownDesktop {
|
||||
@apply block;
|
||||
}
|
||||
.dropdownMobile {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
66
components/layout/UserNav/UserNav.tsx
Normal file
66
components/layout/UserNav/UserNav.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
// import cn from 'clsx'
|
||||
// import Link from 'next/link'
|
||||
// import s from './UserNav.module.css'
|
||||
// import useCart from '@framework/cart/use-cart'
|
||||
// import { useUI } from '@components/ui/context'
|
||||
// import { Heart, Bag, Menu } from '@components/icons'
|
||||
// import CustomerMenuContent from './CustomerMenuContent'
|
||||
// import useCustomer from '@framework/customer/use-customer'
|
||||
// import React from 'react'
|
||||
// import {
|
||||
// Dropdown,
|
||||
// DropdownTrigger as DropdownTriggerInst,
|
||||
// Button,
|
||||
// } from '@components/ui'
|
||||
|
||||
// import type { LineItem } from '@commerce/types/cart'
|
||||
|
||||
// const countItem = (count: number, item: LineItem) => count + item.quantity
|
||||
|
||||
// const UserNav: React.FC<{
|
||||
// className?: string
|
||||
// }> = ({ className }) => {
|
||||
// const { data } = useCart()
|
||||
// const { data: isCustomerLoggedIn } = useCustomer()
|
||||
// const {
|
||||
// toggleSidebar,
|
||||
// closeSidebarIfPresent,
|
||||
// openModal,
|
||||
// setSidebarView,
|
||||
// openSidebar,
|
||||
// displaySidebar,
|
||||
// } = useUI()
|
||||
|
||||
// const itemsCount = data?.lineItems?.reduce(countItem, 0) ?? 0
|
||||
// const DropdownTrigger = isCustomerLoggedIn
|
||||
// ? DropdownTriggerInst
|
||||
// : React.Fragment
|
||||
|
||||
// return (
|
||||
// <nav className={cn(s.root, className)}>
|
||||
// <ul className={s.list}>
|
||||
// {process.env.COMMERCE_CART_ENABLED && (
|
||||
// <li className={s.item}>
|
||||
// <Button
|
||||
// className={s.item}
|
||||
// variant="naked"
|
||||
// onClick={() => {
|
||||
// setSidebarView('CART_VIEW')
|
||||
// openSidebar()
|
||||
// }}
|
||||
// aria-label={`Cart items: ${itemsCount}`}
|
||||
// >
|
||||
// <Bag className={displaySidebar ? undefined : 'text-white'} />
|
||||
|
||||
// {itemsCount > 0 && (
|
||||
// <span className={s.bagCount}>{itemsCount}</span>
|
||||
// )}
|
||||
// </Button>
|
||||
// </li>
|
||||
// )}
|
||||
// </ul>
|
||||
// </nav>
|
||||
// )
|
||||
// }
|
||||
|
||||
// export default UserNav
|
3
components/layout/UserNav/index.ts
Normal file
3
components/layout/UserNav/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
// export { default } from './UserNav'
|
||||
export { default as MenuSidebarView } from './MenuSidebarView'
|
||||
export { default as CustomerMenuContent } from './CustomerMenuContent'
|
113
components/layout/navbar/Navbar.css
Normal file
113
components/layout/navbar/Navbar.css
Normal file
@ -0,0 +1,113 @@
|
||||
header.main {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 50;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 300ms;
|
||||
}
|
||||
|
||||
header.main.is-hidden {
|
||||
transform: translateY(-120px);
|
||||
opacity: 0;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 300ms;
|
||||
}
|
||||
|
||||
header.main nav > div:first-child {
|
||||
background-color: rgba(0, 0, 0, 0.85);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
header.main nav > div:nth-child(2) {
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
.sub-nav a.sub-nav__link img {
|
||||
transition: transform 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav a.sub-nav__link:hover img {
|
||||
transform: scale(1.1);
|
||||
transition: transform 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav a.sub-nav__link:hover h3 {
|
||||
color: #075985;
|
||||
}
|
||||
|
||||
.sub-nav {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sub-nav a.sub-nav__link {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
/* .sub-nav a.sub-nav__link.is-active {
|
||||
pointer-events: none;
|
||||
} */
|
||||
|
||||
/* img */
|
||||
.sub-nav:hover a.sub-nav__link img {
|
||||
filter: blur(3px);
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
/* h3 */
|
||||
.sub-nav:hover a.sub-nav__link h3 {
|
||||
filter: blur(3px);
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav.has-active a.sub-nav__link h3 {
|
||||
opacity: 0.5;
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav.has-active a.sub-nav__link:hover h3 {
|
||||
opacity: 1;
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav.has-active a.sub-nav__link.is-active h3 {
|
||||
opacity: 1;
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav:hover a.sub-nav__link:hover {
|
||||
opacity: 1;
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
/* img */
|
||||
.sub-nav:hover a.sub-nav__link:hover img {
|
||||
filter: blur(0px);
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav.has-active a.sub-nav__link img {
|
||||
opacity: 0.5;
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav.has-active a.sub-nav__link:hover img {
|
||||
opacity: 1;
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.sub-nav.has-active a.sub-nav__link.is-active img {
|
||||
opacity: 1;
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
/* h3 */
|
||||
.sub-nav:hover a.sub-nav__link:hover h3 {
|
||||
filter: blur(0px);
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
35
components/layout/navbar/Navbar.module.css
Normal file
35
components/layout/navbar/Navbar.module.css
Normal file
@ -0,0 +1,35 @@
|
||||
.root {
|
||||
@apply sticky top-0 bg-primary z-40 transition-all duration-150;
|
||||
min-height: 74px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
@apply relative flex flex-row justify-between py-4 md:py-4;
|
||||
}
|
||||
|
||||
.navMenu {
|
||||
@apply hidden ml-6 space-x-4 lg:block;
|
||||
}
|
||||
|
||||
.link {
|
||||
@apply inline-flex items-center leading-6
|
||||
transition ease-in-out duration-75 cursor-pointer
|
||||
text-accent-5;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
@apply text-accent-9;
|
||||
}
|
||||
|
||||
.link:focus {
|
||||
@apply outline-none text-accent-8;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@apply cursor-pointer rounded-full border transform duration-100 ease-in-out;
|
||||
|
||||
&:hover {
|
||||
@apply shadow-md;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
703
components/layout/navbar/NavbarV2.tsx
Normal file
703
components/layout/navbar/NavbarV2.tsx
Normal file
@ -0,0 +1,703 @@
|
||||
'use client'
|
||||
|
||||
import { FC, useEffect, useMemo, useRef } from 'react'
|
||||
import Link from 'next/link'
|
||||
// import { UserNav } from '@components/common'
|
||||
import { useRouter, usePathname } from 'next/navigation'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { Fragment, useState } from 'react'
|
||||
import { Dialog, Tab, Transition } from '@headlessui/react'
|
||||
import {
|
||||
Bars3Icon,
|
||||
MagnifyingGlassIcon,
|
||||
QuestionMarkCircleIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/react/24/outline'
|
||||
|
||||
// Product data
|
||||
import { GLOVES_DATA } from 'data/Gloves'
|
||||
import { INDUSTRIAL_DATA } from 'data/Industrial'
|
||||
|
||||
let _scrollTopValue: number | null = null
|
||||
|
||||
const navigation = {
|
||||
pages: [
|
||||
{ name: 'Industrial', href: '/collections/industrial' },
|
||||
{ name: 'Abrasive', href: '/collections/abrasives' },
|
||||
{ name: 'Adhesive', href: '/collections/adhesives' },
|
||||
{ name: 'Gloves', href: '/collections/gloves' },
|
||||
{
|
||||
name: 'Oscillating Accessories',
|
||||
href: '/collections/oscillating-accessories',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
interface Link {
|
||||
href: string
|
||||
label: string
|
||||
}
|
||||
|
||||
interface NavbarProps {
|
||||
links?: Link[]
|
||||
}
|
||||
|
||||
const NAVIGATION = [
|
||||
{
|
||||
category: 'Industrial',
|
||||
link: '/collections/industrial',
|
||||
// hidden: true,
|
||||
subMenus: [
|
||||
{
|
||||
name: 'Warehouse Racks',
|
||||
link: '/collections/industrial/warehouse-racks',
|
||||
image: INDUSTRIAL_DATA.WAREHOUSE_RACKS.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Fences',
|
||||
link: '/collections/industrial/fences',
|
||||
image: INDUSTRIAL_DATA.FENCES.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Pallet Trucks & Forklifts',
|
||||
link: '/collections/industrial/pallet-trucks-forklifts',
|
||||
image: INDUSTRIAL_DATA.FORKLIFTS.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Warehouse Accessories',
|
||||
link: '/collections/industrial/warehouse-accessories',
|
||||
image: INDUSTRIAL_DATA.WAREHOUSE_ACCESSORIES.navbarImage,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Abrasives',
|
||||
link: '/collections/abrasives',
|
||||
subMenus: [
|
||||
{
|
||||
name: 'Cut off Discs',
|
||||
link: '/collections/abrasives/cut-off-discs',
|
||||
image: 'https://linconson-a.netlify.app/Abrasive.jpg',
|
||||
},
|
||||
{
|
||||
name: 'Flap Discs',
|
||||
link: '/collections/abrasives/flap-discs',
|
||||
image: 'https://linconson-a.netlify.app/flap.jpg',
|
||||
},
|
||||
{
|
||||
name: 'Grinding Wheels',
|
||||
link: '/collections/abrasives/metal-grinding-wheel',
|
||||
image: '/assets/navbar/grinding_wheel.jpeg',
|
||||
},
|
||||
{
|
||||
name: 'Sanding Discs',
|
||||
link: '/collections/abrasives/sanding-discs',
|
||||
image: '/assets/navbar/sanding_disc.jpeg',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Adhesives',
|
||||
link: '/collections/adhesives',
|
||||
subMenus: [
|
||||
{
|
||||
name: 'Grip Tape',
|
||||
link: '/collections/adhesives/grip-tape',
|
||||
// image: 'https://linconson-a.netlify.app/gtape.jpg',
|
||||
image: '/assets/navbar/grip_tape.jpeg',
|
||||
},
|
||||
{
|
||||
name: 'Tread Tape',
|
||||
link: '/collections/adhesives/tread-tape',
|
||||
image: '/assets/navbar/tread_tape.jpg',
|
||||
},
|
||||
{
|
||||
name: 'Carpet Tread',
|
||||
link: '/collections/adhesives/carpet-tread',
|
||||
image: '/assets/navbar/carpet_tread.jpg',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Gloves',
|
||||
link: '/collections/gloves',
|
||||
subMenus: [
|
||||
{
|
||||
name: 'Latex foam',
|
||||
link: '#latex-foam',
|
||||
image: GLOVES_DATA.LATEX_FOAM.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Wrinkled Foam',
|
||||
link: '#wrinkled-foam',
|
||||
image: GLOVES_DATA.WRINKLED_FOAM.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'PU Coated',
|
||||
link: '#pu-coated',
|
||||
image: GLOVES_DATA.PU_COATED.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'PU Cut-resistant',
|
||||
link: '#pu-cut-resistant',
|
||||
image: GLOVES_DATA.PU_CUT_RESISTANT.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Nitrile Cut-resistant',
|
||||
link: '#nitrile-cut-resistant',
|
||||
image: GLOVES_DATA.NITRILE_CUT_RESISTANT.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Latex Cut-resistant',
|
||||
link: '#latex-cut-resistant',
|
||||
image: GLOVES_DATA.LATEX_CUT_RESISTANT.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Nitrile Coated',
|
||||
link: '#nitrile-coated',
|
||||
image: GLOVES_DATA.NITRILE_COATED.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Spandex',
|
||||
link: '#spandex',
|
||||
image: GLOVES_DATA.SPANDEX.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Cotton Latex',
|
||||
link: '#cotton-latex',
|
||||
image: GLOVES_DATA.COTTON_LATEX.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Winter',
|
||||
link: '#winter',
|
||||
image: GLOVES_DATA.WINTER.navbarImage,
|
||||
},
|
||||
{
|
||||
name: 'Leather',
|
||||
link: '#leather',
|
||||
image: GLOVES_DATA.LEATHER.navbarImage,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: 'Oscillating Accessories',
|
||||
link: '/collections/oscillating-accessories',
|
||||
},
|
||||
]
|
||||
|
||||
const Navbar: FC<NavbarProps> = ({ links }) => {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const [isMounted, setIsMounted] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true)
|
||||
}, [])
|
||||
|
||||
const correspondingNavItem = useMemo(
|
||||
() =>
|
||||
isMounted
|
||||
? NAVIGATION.find((_item) => pathname?.includes(_item.link))
|
||||
: null,
|
||||
[pathname, isMounted],
|
||||
)
|
||||
|
||||
const correspondingNavSubMenu = useMemo(() => {
|
||||
if (!isMounted) return null
|
||||
if (correspondingNavItem && Array.isArray(correspondingNavItem.subMenus)) {
|
||||
return (correspondingNavItem.subMenus as object[]).find((subMenu: any) =>
|
||||
pathname?.includes(subMenu.link),
|
||||
)
|
||||
}
|
||||
}, [correspondingNavItem, pathname, isMounted])
|
||||
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||
|
||||
const isHomePage = useMemo(
|
||||
() => isMounted && pathname === '/',
|
||||
[pathname, isMounted],
|
||||
)
|
||||
|
||||
/*
|
||||
* Fixed nav positioning
|
||||
*/
|
||||
const headerRef = useRef<HTMLElement>(null)
|
||||
const [showNav, setShowNav] = useState(true)
|
||||
const trackScrolling = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
||||
if (
|
||||
(_scrollTopValue === null || scrollTop > _scrollTopValue) &&
|
||||
scrollTop > 400
|
||||
) {
|
||||
setShowNav(false)
|
||||
} else {
|
||||
setShowNav(true)
|
||||
}
|
||||
|
||||
_scrollTopValue = scrollTop <= 0 ? 0 : scrollTop
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('scroll', trackScrolling, { passive: true })
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('resize', trackScrolling, { passive: true })
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Mobile menu */}
|
||||
<Transition.Root show={mobileMenuOpen} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="relative z-50 lg:hidden"
|
||||
onClose={setMobileMenuOpen}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition-opacity ease-linear duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="transition-opacity ease-linear duration-300"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-50 flex">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition ease-in-out duration-300 transform"
|
||||
enterFrom="-translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition ease-in-out duration-300 transform"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="-translate-x-full"
|
||||
>
|
||||
<Dialog.Panel className="relative flex w-full max-w-xs flex-col overflow-y-auto bg-white pb-12 shadow-xl">
|
||||
<div className="flex px-4 pt-5 pb-2">
|
||||
<button
|
||||
type="button"
|
||||
className="-m-2 inline-flex items-center justify-center rounded-md p-2 text-gray-400"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
<span className="sr-only">Close menu</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
<Tab.Group as="div" className="mt-2">
|
||||
<div className="border-b border-gray-200">
|
||||
<Tab.List className="-mb-px flex space-x-8 px-4">
|
||||
{/* {navigation.categories.map((category) => (
|
||||
<Tab
|
||||
key={category.name}
|
||||
className={({ selected }) =>
|
||||
classNames(
|
||||
selected
|
||||
? 'text-indigo-600 border-indigo-600'
|
||||
: 'text-gray-900 border-transparent',
|
||||
'flex-1 whitespace-nowrap border-b-2 py-4 px-1 text-base font-medium'
|
||||
)
|
||||
}
|
||||
>
|
||||
{category.name}
|
||||
</Tab>
|
||||
))} */}
|
||||
</Tab.List>
|
||||
</div>
|
||||
<Tab.Panels as={Fragment}>
|
||||
{/* {navigation.categories.map((category) => (
|
||||
<Tab.Panel
|
||||
key={category.name}
|
||||
className="space-y-12 px-4 py-6"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-x-4 gap-y-10">
|
||||
{category.featured.map((item) => (
|
||||
<div key={item.name} className="group relative">
|
||||
<div className="aspect-w-1 aspect-h-1 overflow-hidden rounded-md bg-gray-100 group-hover:opacity-75">
|
||||
<img
|
||||
src={item.imageSrc}
|
||||
alt={item.imageAlt}
|
||||
className="object-cover object-center"
|
||||
/>
|
||||
</div>
|
||||
<a
|
||||
href={item.href}
|
||||
className="mt-6 block text-sm font-medium text-gray-900"
|
||||
>
|
||||
<span
|
||||
className="absolute inset-0 z-10"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{item.name}
|
||||
</a>
|
||||
<p
|
||||
aria-hidden="true"
|
||||
className="mt-1 text-sm text-gray-500"
|
||||
>
|
||||
Shop now
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
))} */}
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
|
||||
<div className="space-y-6 border-t border-gray-200 py-6 px-4">
|
||||
{navigation.pages.map((page) => (
|
||||
<div key={page.name} className="flow-root">
|
||||
<Link
|
||||
href={page.href}
|
||||
className="-m-2 block p-2 font-medium text-gray-900"
|
||||
onClick={(e) => {
|
||||
setMobileMenuOpen(false)
|
||||
}}
|
||||
>
|
||||
{page.name}
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/*
|
||||
<div className="space-y-6 border-t border-gray-200 py-6 px-4">
|
||||
<div className="flow-root">
|
||||
<a
|
||||
href="#"
|
||||
className="-m-2 block p-2 font-medium text-gray-900"
|
||||
>
|
||||
Create an account
|
||||
</a>
|
||||
</div>
|
||||
<div className="flow-root">
|
||||
<a
|
||||
href="#"
|
||||
className="-m-2 block p-2 font-medium text-gray-900"
|
||||
>
|
||||
Sign in
|
||||
</a>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* <div className="space-y-6 border-t border-gray-200 py-6 px-4">
|
||||
<form>
|
||||
<div className="inline-block">
|
||||
<label htmlFor="mobile-currency" className="sr-only">
|
||||
Currency
|
||||
</label>
|
||||
<div className="group relative -ml-2 rounded-md border-transparent focus-within:ring-2 focus-within:ring-white">
|
||||
<select
|
||||
id="mobile-currency"
|
||||
name="currency"
|
||||
className="flex items-center rounded-md border-transparent bg-none py-0.5 pl-2 pr-5 text-sm font-medium text-gray-700 focus:border-transparent focus:outline-none focus:ring-0 group-hover:text-gray-800"
|
||||
>
|
||||
{currencies.map((currency) => (
|
||||
<option key={currency}>{currency}</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center">
|
||||
<ChevronDownIcon
|
||||
className="h-5 w-5 text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div> */}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
|
||||
{/* Navigation */}
|
||||
<header className={`main ${!showNav ? 'is-hidden' : ''}`} ref={headerRef}>
|
||||
<nav aria-label="Top">
|
||||
{/* Top navigation */}
|
||||
<div className="">
|
||||
<div className="mx-auto flex h-10 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center space-x-6">
|
||||
{/* <a
|
||||
href="#"
|
||||
className="text-sm font-medium text-white hover:text-gray-100"
|
||||
>
|
||||
Sign in
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="text-sm font-medium text-white hover:text-gray-100"
|
||||
>
|
||||
Create an account
|
||||
</a> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Secondary navigation */}
|
||||
<div
|
||||
className="backdrop-blur-md backdrop-filter"
|
||||
style={{
|
||||
// backgroundColor: isHomePage ? undefined : '#192231',
|
||||
border: isHomePage
|
||||
? '1px solid rgb(166 180 204 / 21%)'
|
||||
: '1px solid rgb(166 180 204 / 15%)',
|
||||
borderRight: 'none',
|
||||
borderLeft: 'none',
|
||||
}}
|
||||
>
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div>
|
||||
<div className="flex h-16 items-center justify-between">
|
||||
{/* Logo (lg+) */}
|
||||
<div className="hidden lg:flex lg:flex-1 lg:items-center">
|
||||
<Link href="/">
|
||||
<span className="sr-only">Linconson</span>
|
||||
|
||||
<Image
|
||||
className="h-8 w-auto"
|
||||
src={
|
||||
pathname?.indexOf('/collections/industrial') === 0
|
||||
? '/logo-industrial.png'
|
||||
: '/logo.png'
|
||||
}
|
||||
width="100"
|
||||
height="30"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="hidden h-full lg:flex">
|
||||
<div className="flex h-full justify-center space-x-8">
|
||||
{NAVIGATION.map((menuContent) => (
|
||||
<Link
|
||||
key={menuContent.category}
|
||||
href={menuContent.link}
|
||||
className={`flex items-center text-sm font-medium text-white ${
|
||||
correspondingNavItem
|
||||
? menuContent.link.includes(
|
||||
correspondingNavItem.link,
|
||||
)
|
||||
? ''
|
||||
: 'text-opacity-50'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
{menuContent.category}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu and search (lg-) */}
|
||||
<div className="flex flex-1 items-center lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
className="-ml-2 p-2 text-white"
|
||||
onClick={() => setMobileMenuOpen(true)}
|
||||
>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
{/* Search */}
|
||||
<Link href="/search" className="ml-2 p-2 text-white">
|
||||
<span className="sr-only">Search</span>
|
||||
<MagnifyingGlassIcon
|
||||
className="h-6 w-6"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Logo (lg-) */}
|
||||
<Link href="/" className="lg:hidden">
|
||||
<span className="sr-only">Linconson</span>
|
||||
|
||||
<Image
|
||||
className="h-8 w-auto"
|
||||
src={
|
||||
pathname?.indexOf('/collections/industrial') === 0
|
||||
? '/logo-industrial.png'
|
||||
: '/logo.png'
|
||||
}
|
||||
width="100"
|
||||
height="30"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-1 items-center justify-end">
|
||||
<Link
|
||||
href="/search"
|
||||
className="hidden text-sm font-medium text-white lg:flex items-center"
|
||||
>
|
||||
Search
|
||||
<MagnifyingGlassIcon
|
||||
className="h-4 w-4 ml-2"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center lg:ml-8">
|
||||
{/* Help */}
|
||||
<Link
|
||||
href="/contact-us"
|
||||
className="p-2 text-white lg:hidden"
|
||||
>
|
||||
<span className="sr-only">Help</span>
|
||||
|
||||
<QuestionMarkCircleIcon
|
||||
className="h-6 w-6"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/contact-us"
|
||||
className="hidden text-sm font-medium text-white lg:block"
|
||||
>
|
||||
Help
|
||||
</Link>
|
||||
|
||||
{/* Cart */}
|
||||
<div className="ml-4 flow-root lg:ml-8">
|
||||
{/* <UserNav /> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
{/* <NavbarRoot>
|
||||
<Container clean className="mx-auto max-w-8xl px-6">
|
||||
<div className={s.nav}>
|
||||
<div className="flex items-center flex-1">
|
||||
<Link href="/" className={s.logo} aria-label="Logo">
|
||||
<Logo />
|
||||
</Link>
|
||||
|
||||
<nav className={s.navMenu}>
|
||||
{NAVIGATION.map((menuContent) => (
|
||||
<Link
|
||||
key={menuContent.category}
|
||||
href={menuContent.link}
|
||||
className={s.link}
|
||||
>
|
||||
{menuContent.category}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div> */}
|
||||
|
||||
{/* {process.env.COMMERCE_SEARCH_ENABLED && (
|
||||
<div className="justify-center flex-1 hidden lg:flex">
|
||||
<Searchbar />
|
||||
</div>
|
||||
)} */}
|
||||
|
||||
{/* <div className="flex items-center justify-end flex-1 space-x-8">
|
||||
<UserNav />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{process.env.COMMERCE_SEARCH_ENABLED && (
|
||||
<div className="flex pb-4 lg:px-6 lg:hidden">
|
||||
<Searchbar id="mobile-search" />
|
||||
</div>
|
||||
)}
|
||||
</Container>
|
||||
</NavbarRoot> */}
|
||||
|
||||
<div
|
||||
style={{
|
||||
height:
|
||||
pathname === '/'
|
||||
? '100vh'
|
||||
: headerRef.current
|
||||
? headerRef.current.offsetHeight
|
||||
: 106,
|
||||
}}
|
||||
></div>
|
||||
|
||||
{correspondingNavItem && correspondingNavItem.subMenus && (
|
||||
<div
|
||||
className={`bg-gray-100 sub-nav ${
|
||||
correspondingNavSubMenu ? 'has-active' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="mx-auto max-w-7xl text-white flex flex-wrap justify-center py-8 gap-y-6">
|
||||
{correspondingNavItem.subMenus?.map((subMenu) => {
|
||||
const _isActive = pathname?.includes(subMenu.link)
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={subMenu.name}
|
||||
className={`flex-none w-auto iphone:w-1/2 py-0 sub-nav__link ${
|
||||
_isActive ? 'is-active' : ''
|
||||
}`}
|
||||
href={subMenu.link}
|
||||
onClick={
|
||||
subMenu.link && subMenu.link[0] === '#'
|
||||
? (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
// Smooth scroll to item
|
||||
if (subMenu.link && document) {
|
||||
const elementToScrollTo = document.getElementById(
|
||||
subMenu.link.replace('#', ''),
|
||||
)
|
||||
|
||||
if (elementToScrollTo) {
|
||||
elementToScrollTo.scrollIntoView()
|
||||
}
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<div className="mx-auto max-w-xs items-center px-2 lg:max-w-none lg:px-4 text-center">
|
||||
<div style={{ height: '3.5rem' }}>
|
||||
<Image
|
||||
src={subMenu.image}
|
||||
alt=""
|
||||
className="h-14 w-auto mx-auto"
|
||||
width={60}
|
||||
height={60}
|
||||
style={{ mixBlendMode: 'darken' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3
|
||||
className={`text-gray-900 mt-4 text-xs ${
|
||||
_isActive ? 'font-semibold text-sky-800' : ''
|
||||
}`}
|
||||
>
|
||||
{subMenu.name}
|
||||
</h3>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navbar
|
@ -49,7 +49,7 @@ export async function Navbar() {
|
||||
</div>
|
||||
<div className="hidden justify-center md:flex md:w-1/3">
|
||||
<Suspense fallback={<SearchSkeleton />}>
|
||||
<Search />
|
||||
{/* <Search /> */}
|
||||
<SearchButton />
|
||||
</Suspense>
|
||||
</div>
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'
|
||||
import Form from 'next/form'
|
||||
import Link from 'next/link'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
|
||||
export default function Search() {
|
||||
@ -44,8 +45,14 @@ export function SearchSkeleton() {
|
||||
|
||||
export function SearchButton() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Search</h1>
|
||||
<div className="flex flex-1 items-center justify-end">
|
||||
<Link
|
||||
href="/search"
|
||||
className="hidden text-sm font-medium text-white lg:flex items-center"
|
||||
>
|
||||
Search
|
||||
<MagnifyingGlassIcon className="h-4 w-4 ml-2" aria-hidden="true" />
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
72
data/Gloves.tsx
Normal file
72
data/Gloves.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import LATEX_FOAM from '../app/images/gloves/top_navbar/latex_foam.jpeg'
|
||||
import WRINKLED_FOAM from '../app/images/gloves/top_navbar/wrinkled_foam.jpeg'
|
||||
import PU_COATED from '../app/images/gloves/top_navbar/pu_coated.jpeg'
|
||||
import PU_CUT_RESISTANT from '../app/images/gloves/top_navbar/cut_resistant.jpeg'
|
||||
import NITRILE_CUT_RESISTANT from '../app/images/gloves/top_navbar/nitrile_cut_resistant.jpeg'
|
||||
import LATEX_CUT_RESISTANT from '../app/images/gloves/top_navbar/latex_cut_resistant.jpeg'
|
||||
import NITRILE_COATED from '../app/images/gloves/top_navbar/nitrile_coated.jpeg'
|
||||
import SPANDEX from '../app/images/gloves/top_navbar/spandex.jpeg'
|
||||
import COTTON_LATEX from '../app/images/gloves/top_navbar/latex.jpeg'
|
||||
import WINTER from '../app/images/gloves/top_navbar/winter.jpeg'
|
||||
import LEATHER from '../app/images/gloves/top_navbar/leather.jpeg'
|
||||
|
||||
import LATEX_FOAM_CONTENT_IMAGE from '../app/images/gloves/content/latex_foam.jpeg'
|
||||
import WRINKLED_FOAM_CONTENT_IMAGE from '../app/images/gloves/content/wrinkled_foam.jpeg'
|
||||
import PU_COATED_CONTENT_IMAGE from '../app/images/gloves/content/pu_coated.jpeg'
|
||||
import PU_CUT_RESISTANT_CONTENT_IMAGE from '../app/images/gloves/content/cut_resistant.jpeg'
|
||||
import NITRILE_CUT_RESISTANT_CONTENT_IMAGE from '../app/images/gloves/content/nitrile_cut_resistant.jpeg'
|
||||
import LATEX_CUT_RESISTANT_CONTENT_IMAGE from '../app/images/gloves/content/latex_cut_resistant.jpeg'
|
||||
import NITRILE_COATED_CONTENT_IMAGE from '../app/images/gloves/content/nitrile_coated.jpeg'
|
||||
import SPANDEX_CONTENT_IMAGE from '../app/images/gloves/content/spandex.jpeg'
|
||||
import COTTON_LATEX_CONTENT_IMAGE from '../app/images/gloves/content/cotton_latex.jpeg'
|
||||
import WINTER_CONTENT_IMAGE from '../app/images/gloves/content/winter.jpeg'
|
||||
import LEATHER_CONTENT_IMAGE from '../app/images/gloves/content/leather.jpeg'
|
||||
|
||||
// app/images/gloves/content/cotton_latex.jpeg
|
||||
|
||||
export const GLOVES_DATA = {
|
||||
LATEX_FOAM: {
|
||||
navbarImage: LATEX_FOAM,
|
||||
contentImage: LATEX_FOAM_CONTENT_IMAGE,
|
||||
},
|
||||
WRINKLED_FOAM: {
|
||||
navbarImage: WRINKLED_FOAM,
|
||||
contentImage: WRINKLED_FOAM_CONTENT_IMAGE,
|
||||
},
|
||||
PU_COATED: {
|
||||
navbarImage: PU_COATED,
|
||||
contentImage: PU_COATED_CONTENT_IMAGE,
|
||||
},
|
||||
PU_CUT_RESISTANT: {
|
||||
navbarImage: PU_CUT_RESISTANT,
|
||||
contentImage: PU_CUT_RESISTANT_CONTENT_IMAGE,
|
||||
},
|
||||
NITRILE_CUT_RESISTANT: {
|
||||
navbarImage: NITRILE_CUT_RESISTANT,
|
||||
contentImage: NITRILE_CUT_RESISTANT_CONTENT_IMAGE,
|
||||
},
|
||||
LATEX_CUT_RESISTANT: {
|
||||
navbarImage: LATEX_CUT_RESISTANT,
|
||||
contentImage: LATEX_CUT_RESISTANT_CONTENT_IMAGE,
|
||||
},
|
||||
NITRILE_COATED: {
|
||||
navbarImage: NITRILE_COATED,
|
||||
contentImage: NITRILE_COATED_CONTENT_IMAGE,
|
||||
},
|
||||
SPANDEX: {
|
||||
navbarImage: SPANDEX,
|
||||
contentImage: SPANDEX_CONTENT_IMAGE,
|
||||
},
|
||||
COTTON_LATEX: {
|
||||
navbarImage: COTTON_LATEX,
|
||||
contentImage: COTTON_LATEX_CONTENT_IMAGE,
|
||||
},
|
||||
WINTER: {
|
||||
navbarImage: WINTER,
|
||||
contentImage: WINTER_CONTENT_IMAGE,
|
||||
},
|
||||
LEATHER: {
|
||||
navbarImage: LEATHER,
|
||||
contentImage: LEATHER_CONTENT_IMAGE,
|
||||
},
|
||||
}
|
26
data/Industrial.tsx
Normal file
26
data/Industrial.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import PALLET_TRUCKS from '../app/images/industrial/top_navbar/pallet_trucks.jpg'
|
||||
import WAREHOUSE_RACKS from '../app/images/industrial/top_navbar/warehouse_racks.png'
|
||||
import WAREHOUSE_ACCESSORIES from '../app/images/industrial/top_navbar/warehouse_acc.png'
|
||||
import FORKLIFTS from '../app/images/industrial/top_navbar/forklifts.png'
|
||||
import FENCES from '../app/images/industrial/top_navbar/fences.png'
|
||||
|
||||
//data/Industrial.tsx
|
||||
|
||||
export const INDUSTRIAL_DATA = {
|
||||
PALLET_TRUCKS: {
|
||||
navbarImage: PALLET_TRUCKS,
|
||||
// contentImage: LATEX_FOAM_CONTENT_IMAGE,
|
||||
},
|
||||
WAREHOUSE_RACKS: {
|
||||
navbarImage: WAREHOUSE_RACKS,
|
||||
},
|
||||
WAREHOUSE_ACCESSORIES: {
|
||||
navbarImage: WAREHOUSE_ACCESSORIES,
|
||||
},
|
||||
FORKLIFTS: {
|
||||
navbarImage: FORKLIFTS,
|
||||
},
|
||||
FENCES: {
|
||||
navbarImage: FENCES,
|
||||
},
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"clsx": "^2.1.1",
|
||||
"geist": "^1.3.1",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"next": "15.3.0-canary.13",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
|
1381
pnpm-lock.yaml
generated
1381
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user