Skip to content

Commit

Permalink
Cooking
Browse files Browse the repository at this point in the history
  • Loading branch information
leerob committed May 30, 2024
1 parent bb3bc33 commit cf60d88
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 93 deletions.
3 changes: 3 additions & 0 deletions app/@navbar/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Navbar from 'app/@navbar/page';

export default Navbar;
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
import { Dialog, Transition } from '@headlessui/react';
import Link from 'next/link';
import { usePathname, useSearchParams } from 'next/navigation';
import { Fragment, Suspense, useEffect, useState } from 'react';
import { Fragment, useEffect, useState } from 'react';

import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline';
import { Menu } from 'lib/shopify/types';
import Search, { SearchSkeleton } from './search';

export default function MobileMenu({ menu }: { menu: Menu[] }) {
export default function MobileMenu({
menu,
children
}: {
menu: Menu[];
children: React.ReactNode;
}) {
const pathname = usePathname();
const searchParams = useSearchParams();
const [isOpen, setIsOpen] = useState(false);
Expand Down Expand Up @@ -71,11 +76,7 @@ export default function MobileMenu({ menu }: { menu: Menu[] }) {
<XMarkIcon className="h-6" />
</button>

<div className="mb-4 w-full">
<Suspense fallback={<SearchSkeleton />}>
<Search />
</Suspense>
</div>
<div className="mb-4 w-full">{children}</div>
{menu.length ? (
<ul className="flex w-full flex-col">
{menu.map((item: Menu) => (
Expand Down
18 changes: 12 additions & 6 deletions components/layout/navbar/index.tsx → app/@navbar/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,25 @@ import { Menu } from 'lib/shopify/types';
import Link from 'next/link';
import { Suspense } from 'react';
import MobileMenu from './mobile-menu';
import Search, { SearchSkeleton } from './search';
import Search from './search';

const { SITE_NAME } = process.env;

export default async function Navbar() {
export default async function Navbar({
searchParams
}: {
searchParams: { [key: string]: string | string[] | undefined };
}) {
const menu = await getMenu('next-js-frontend-header-menu');
const search = searchParams.q ? (searchParams.q as string) : '';

return (
<nav className="relative flex items-center justify-between p-4 lg:px-6">
<div className="block flex-none md:hidden">
<Suspense fallback={null}>
<MobileMenu menu={menu} />
<MobileMenu menu={menu}>
<Search value={search} />
</MobileMenu>
</Suspense>
</div>
<div className="flex w-full items-center">
Expand All @@ -43,9 +51,7 @@ export default async function Navbar() {
) : null}
</div>
<div className="hidden justify-center md:flex md:w-1/3">
<Suspense fallback={<SearchSkeleton />}>
<Search />
</Suspense>
<Search value={search} />
</div>
<div className="flex justify-end md:w-1/3">
<Suspense fallback={<OpenCart />}>
Expand Down
35 changes: 35 additions & 0 deletions app/@navbar/search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client';

import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useGlobalTransition } from 'lib/transition';
import { useRouter } from 'next/navigation';

export default function Search({ value }: { value: string }) {
const router = useRouter();
const { startTransition } = useGlobalTransition();

function searchAction(formData: FormData) {
const search = formData.get('search') as string;
console.log('starting transition...');
startTransition(() => {
router.push(`/search?q=${search}`);
});
}

return (
<form action={searchAction} className="w-max-[550px] relative w-full lg:w-80 xl:w-full">
<input
key={value}
type="text"
name="search"
placeholder="Search for products..."
autoComplete="off"
defaultValue={value}
className="w-full rounded-lg border bg-white px-4 py-2 text-sm text-black placeholder:text-neutral-500 dark:border-neutral-800 dark:bg-transparent dark:text-white dark:placeholder:text-neutral-400"
/>
<div className="absolute right-0 top-0 mr-3 flex h-full items-center">
<MagnifyingGlassIcon className="h-4" />
</div>
</form>
);
}
20 changes: 14 additions & 6 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Navbar from 'components/layout/navbar';
import { GeistSans } from 'geist/font/sans';
import { GlobalTransitionProvider } from 'lib/transition';
import { ensureStartsWith } from 'lib/utils';
import { ReactNode } from 'react';
import './globals.css';
Expand Down Expand Up @@ -31,13 +31,21 @@ export const metadata = {
})
};

export default async function RootLayout({ children }: { children: ReactNode }) {
export default async function RootLayout({
children,
navbar
}: {
children: ReactNode;
navbar: ReactNode;
}) {
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">
<Navbar />
<main>{children}</main>
</body>
<GlobalTransitionProvider>
<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">
{navbar}
<main>{children}</main>
</body>
</GlobalTransitionProvider>
</html>
);
}
2 changes: 1 addition & 1 deletion app/search/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function Loading() {
.fill(0)
.map((_, index) => {
return (
<Grid.Item key={index} className="animate-pulse bg-neutral-100 dark:bg-neutral-900" />
<Grid.Item key={index} className="animate-pulse bg-neutral-100 dark:bg-neutral-800" />
);
})}
</Grid>
Expand Down
57 changes: 0 additions & 57 deletions components/layout/navbar/search.tsx

This file was deleted.

26 changes: 26 additions & 0 deletions components/layout/product-grid-items.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
'use client';

import Grid from 'components/grid';
import { GridTileImage } from 'components/grid/tile';
import { Product } from 'lib/shopify/types';
import { useGlobalTransition } from 'lib/transition';
import Link from 'next/link';

function Loading() {
return (
<>
{Array(12)
.fill(0)
.map((_, index) => {
return (
<Grid.Item
key={index}
className="h-full w-full animate-pulse bg-neutral-100 dark:bg-neutral-800"
/>
);
})}
</>
);
}

export default function ProductGridItems({ products }: { products: Product[] }) {
const { isPending } = useGlobalTransition();

if (isPending) {
return <Loading />;
}

return (
<>
{products.map((product) => (
Expand Down
22 changes: 7 additions & 15 deletions components/product/gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { GridTileImage } from 'components/grid/tile';
import { createUrl } from 'lib/utils';
import Image from 'next/image';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useOptimistic, useTransition } from 'react';
import { useOptimistic } from 'react';

export function Gallery({ images }: { images: { src: string; altText: string }[] }) {
const router = useRouter();
Expand All @@ -14,8 +14,6 @@ export function Gallery({ images }: { images: { src: string; altText: string }[]
const imageSearchParam = searchParams.get('image');
const imageIndex = imageSearchParam ? parseInt(imageSearchParam) : 0;
const [optimisticIndex, setOptimisticIndex] = useOptimistic(imageIndex);
// eslint-disable-next-line no-unused-vars
const [pending, startTransition] = useTransition();

const buttonClassName =
'h-full px-6 transition-all ease-in-out hover:scale-110 hover:text-black dark:hover:text-white flex items-center justify-center';
Expand Down Expand Up @@ -46,10 +44,8 @@ export function Gallery({ images }: { images: { src: string; altText: string }[]
<div className="mx-auto flex h-11 items-center rounded-full border border-white bg-neutral-50/80 text-neutral-500 backdrop-blur dark:border-black dark:bg-neutral-900/80">
<button
aria-label="Previous product image"
onClick={() => {
startTransition(() => {
updateIndex(optimisticIndex - 1);
});
formAction={() => {
updateIndex(optimisticIndex - 1);
}}
className={buttonClassName}
>
Expand All @@ -58,10 +54,8 @@ export function Gallery({ images }: { images: { src: string; altText: string }[]
<div className="mx-1 h-6 w-px bg-neutral-500"></div>
<button
aria-label="Next product image"
onClick={() => {
startTransition(() => {
updateIndex(optimisticIndex + 1);
});
formAction={() => {
updateIndex(optimisticIndex + 1);
}}
className={buttonClassName}
>
Expand All @@ -82,10 +76,8 @@ export function Gallery({ images }: { images: { src: string; altText: string }[]
<button
aria-label="Select product image"
className="h-full w-full"
onClick={() => {
startTransition(() => {
updateIndex(index);
});
formAction={() => {
updateIndex(index);
}}
>
<GridTileImage
Expand Down
32 changes: 32 additions & 0 deletions lib/transition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use client';

import { ReactNode, createContext, use, useTransition } from 'react';

const GlobalTransitionContext = createContext<ReturnType<
typeof useGlobalTransitionInternal
> | null>(null);

export function useGlobalTransition() {
const transition = use(GlobalTransitionContext);

if (transition === null) {
throw new Error('Make sure to use `GlobalTransitionProvider` first.');
}

return transition;
}

function useGlobalTransitionInternal() {
const [isPending, startTransition] = useTransition();

return { isPending, startTransition };
}

export function GlobalTransitionProvider({ children }: { children: ReactNode }) {
const transition = useGlobalTransitionInternal();
return (
<GlobalTransitionContext.Provider value={transition}>
{children}
</GlobalTransitionContext.Provider>
);
}

0 comments on commit cf60d88

Please sign in to comment.