mirror of
https://github.com/vercel/commerce.git
synced 2025-05-08 18:57:51 +00:00
add: GridItem & Grid
This commit is contained in:
parent
3bc03dd7c8
commit
a17e00f875
265
app/data.ts
Normal file
265
app/data.ts
Normal file
@ -0,0 +1,265 @@
|
||||
import { Product } from 'lib/shopify/types';
|
||||
|
||||
//temp: for ProductGridItems test
|
||||
export const mockProducts: Product[] = [
|
||||
{
|
||||
id: 'prod_001',
|
||||
handle: 'product-1',
|
||||
availableForSale: true,
|
||||
title: 'Product 1',
|
||||
description: 'This is the description for Product 1',
|
||||
descriptionHtml: '<p>This is the <strong>HTML</strong> description for Product 1</p>',
|
||||
options: [
|
||||
{
|
||||
id: 'option_001',
|
||||
name: 'Size',
|
||||
values: ['S', 'M', 'L']
|
||||
}
|
||||
],
|
||||
priceRange: {
|
||||
maxVariantPrice: {
|
||||
amount: '100.00',
|
||||
currencyCode: 'USD'
|
||||
},
|
||||
minVariantPrice: {
|
||||
amount: '80.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
},
|
||||
featuredImage: {
|
||||
url: 'https://cdn.shopify.com/static/sample-images/bath.jpeg',
|
||||
altText: 'Product 1 Featured Image',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
seo: {
|
||||
title: 'Product 1 SEO Title',
|
||||
description: 'This is the SEO description for Product 1'
|
||||
},
|
||||
tags: ['tag1', 'tag2'],
|
||||
updatedAt: new Date().toISOString(),
|
||||
variants: [
|
||||
{
|
||||
id: 'variant_001',
|
||||
title: 'Variant 1',
|
||||
availableForSale: true,
|
||||
selectedOptions: [
|
||||
{
|
||||
name: 'Size',
|
||||
value: 'M'
|
||||
}
|
||||
],
|
||||
price: {
|
||||
amount: '90.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
}
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/bath.jpeg',
|
||||
altText: 'Product 1 Image 1',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/bath.jpeg',
|
||||
altText: 'Product 1 Image 2',
|
||||
width: 400,
|
||||
height: 400
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'prod_002',
|
||||
handle: 'product-2',
|
||||
availableForSale: false,
|
||||
title: 'Product 2',
|
||||
description: 'This is the description for Product 2',
|
||||
descriptionHtml: '<p>This is the <strong>HTML</strong> description for Product 2</p>',
|
||||
options: [
|
||||
{
|
||||
id: 'option_002',
|
||||
name: 'Color',
|
||||
values: ['Red', 'Blue', 'Green']
|
||||
}
|
||||
],
|
||||
priceRange: {
|
||||
maxVariantPrice: {
|
||||
amount: '120.00',
|
||||
currencyCode: 'USD'
|
||||
},
|
||||
minVariantPrice: {
|
||||
amount: '100.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
},
|
||||
featuredImage: {
|
||||
url: 'https://cdn.shopify.com/static/sample-images/teapot.jpg',
|
||||
altText: 'Product 2 Featured Image',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
seo: {
|
||||
title: 'Product 2 SEO Title',
|
||||
description: 'This is the SEO description for Product 2'
|
||||
},
|
||||
tags: ['tag3', 'tag4'],
|
||||
updatedAt: new Date().toISOString(),
|
||||
variants: [
|
||||
{
|
||||
id: 'variant_002',
|
||||
title: 'Variant 2',
|
||||
availableForSale: false,
|
||||
selectedOptions: [
|
||||
{
|
||||
name: 'Color',
|
||||
value: 'Red'
|
||||
}
|
||||
],
|
||||
price: {
|
||||
amount: '110.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
}
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/teapot.jpg',
|
||||
altText: 'Product 2 Image 1',
|
||||
width: 500,
|
||||
height: 500
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'prod_003',
|
||||
handle: 'product-3',
|
||||
availableForSale: true,
|
||||
title: 'Product 3',
|
||||
description: 'This is the description for Product 3',
|
||||
descriptionHtml: '<p>This is the <strong>HTML</strong> description for Product 3</p>',
|
||||
options: [
|
||||
{
|
||||
id: 'option_003',
|
||||
name: 'Size',
|
||||
values: ['S', 'M', 'L']
|
||||
}
|
||||
],
|
||||
priceRange: {
|
||||
maxVariantPrice: {
|
||||
amount: '300.00',
|
||||
currencyCode: 'USD'
|
||||
},
|
||||
minVariantPrice: {
|
||||
amount: '80.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
},
|
||||
featuredImage: {
|
||||
url: 'https://cdn.shopify.com/static/sample-images/bath.jpeg',
|
||||
altText: 'Product 3 Featured Image',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
seo: {
|
||||
title: 'Product 3 SEO Title',
|
||||
description: 'This is the SEO description for Product 3'
|
||||
},
|
||||
tags: ['tag3', 'tag2'],
|
||||
updatedAt: new Date().toISOString(),
|
||||
variants: [
|
||||
{
|
||||
id: 'variant_003',
|
||||
title: 'Variant 3',
|
||||
availableForSale: true,
|
||||
selectedOptions: [
|
||||
{
|
||||
name: 'Size',
|
||||
value: 'M'
|
||||
}
|
||||
],
|
||||
price: {
|
||||
amount: '90.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
}
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/bath.jpeg',
|
||||
altText: 'Product 3 Image 1',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/bath.jpeg',
|
||||
altText: 'Product 3 Image 2',
|
||||
width: 400,
|
||||
height: 400
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'prod_004',
|
||||
handle: 'product-4',
|
||||
availableForSale: false,
|
||||
title: 'Product 4',
|
||||
description: 'This is the description for Product 4',
|
||||
descriptionHtml: '<p>This is the <strong>HTML</strong> description for Product 4</p>',
|
||||
options: [
|
||||
{
|
||||
id: 'option_004',
|
||||
name: 'Color',
|
||||
values: ['Red', 'Blue', 'Green']
|
||||
}
|
||||
],
|
||||
priceRange: {
|
||||
maxVariantPrice: {
|
||||
amount: '140.00',
|
||||
currencyCode: 'USD'
|
||||
},
|
||||
minVariantPrice: {
|
||||
amount: '100.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
},
|
||||
featuredImage: {
|
||||
url: 'https://cdn.shopify.com/static/sample-images/teapot.jpg',
|
||||
altText: 'Product 4 Featured Image',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
seo: {
|
||||
title: 'Product 4 SEO Title',
|
||||
description: 'This is the SEO description for Product 4'
|
||||
},
|
||||
tags: ['tag3', 'tag4'],
|
||||
updatedAt: new Date().toISOString(),
|
||||
variants: [
|
||||
{
|
||||
id: 'variant_004',
|
||||
title: 'Variant 4',
|
||||
availableForSale: false,
|
||||
selectedOptions: [
|
||||
{
|
||||
name: 'Color',
|
||||
value: 'Red'
|
||||
}
|
||||
],
|
||||
price: {
|
||||
amount: '110.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
}
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/teapot.jpg',
|
||||
altText: 'Product 4 Image 1',
|
||||
width: 500,
|
||||
height: 500
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
@ -1,3 +1,4 @@
|
||||
import Footer from 'components/layout/footer';
|
||||
import { Navbar } from 'components/layout/navbar';
|
||||
import { GeistSans } from 'geist/font/sans';
|
||||
import { getCart } from 'lib/shopify';
|
||||
@ -43,6 +44,7 @@ export default async function RootLayout({ children }: { children: ReactNode })
|
||||
<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>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
174
app/page.tsx
174
app/page.tsx
@ -1,9 +1,10 @@
|
||||
import { mockProducts } from 'app/data';
|
||||
import Error from 'app/error';
|
||||
import Footer from 'components/layout/footer';
|
||||
import Grid from 'components/grid';
|
||||
import { Search } from 'components/layout/search';
|
||||
import { PriceBox } from 'components/price-box';
|
||||
import ProductGridItems from 'components/product/product-grid-items';
|
||||
import { getCollectionProducts } from 'lib/shopify';
|
||||
import type { Product } from 'lib/shopify/types';
|
||||
import Image from 'next/image';
|
||||
|
||||
//Todo: change to proper metadata
|
||||
@ -30,157 +31,48 @@ export default async function HomePage() {
|
||||
}
|
||||
alt={products[0].featuredImage.altText || 'Main product'}
|
||||
fill
|
||||
objectFit="cover"
|
||||
quality={100}
|
||||
priority
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute bottom-20 flex w-full flex-col items-center text-lightText">
|
||||
<h1 className="text-xl">{products[0].title}</h1>
|
||||
<span className="mb-6 mt-1 text-sm text-lightText/80">Read more</span>
|
||||
<div className="text-mainBg flex w-[384px] justify-center gap-[10px]">
|
||||
<div className="flex w-[384px] justify-center gap-[10px] text-mainBg">
|
||||
<PriceBox title="Box of 20" price={2460} />
|
||||
<PriceBox title="Single Cigar" price={120} />
|
||||
</div>
|
||||
</div>
|
||||
<Search />
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
<Search />
|
||||
<Grid className="grid-cols-2 sm:grid-cols-4">
|
||||
{mockProducts.slice(0, 4).map(({ featuredImage, id, title, handle }) => (
|
||||
<ProductGridItems
|
||||
key={id}
|
||||
src={featuredImage.url}
|
||||
title={title}
|
||||
handle={handle}
|
||||
ratio="2/3"
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
<Grid className="grid-cols-1 sm:grid-cols-3">
|
||||
{mockProducts.slice(0, 3).map(({ featuredImage, id, title, handle }) => (
|
||||
<ProductGridItems
|
||||
key={id}
|
||||
src={featuredImage.url}
|
||||
title={title}
|
||||
handle={handle}
|
||||
ratio="2/3"
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
<Grid className="grid-cols-1 sm:grid-cols-2">
|
||||
{mockProducts.slice(0, 2).map(({ featuredImage, id, title, handle }) => (
|
||||
<ProductGridItems key={id} src={featuredImage.url} title={title} handle={handle} />
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
//temp: for ProductGridItems test
|
||||
const mockProducts: Product[] = [
|
||||
{
|
||||
id: 'prod_001',
|
||||
handle: 'product-1',
|
||||
availableForSale: true,
|
||||
title: 'Product 1',
|
||||
description: 'This is the description for Product 1',
|
||||
descriptionHtml: '<p>This is the <strong>HTML</strong> description for Product 1</p>',
|
||||
options: [
|
||||
{
|
||||
id: 'option_001',
|
||||
name: 'Size',
|
||||
values: ['S', 'M', 'L']
|
||||
}
|
||||
],
|
||||
priceRange: {
|
||||
maxVariantPrice: {
|
||||
amount: '100.00',
|
||||
currencyCode: 'USD'
|
||||
},
|
||||
minVariantPrice: {
|
||||
amount: '80.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
},
|
||||
featuredImage: {
|
||||
url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
|
||||
altText: 'Product 1 Featured Image',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
seo: {
|
||||
title: 'Product 1 SEO Title',
|
||||
description: 'This is the SEO description for Product 1'
|
||||
},
|
||||
tags: ['tag1', 'tag2'],
|
||||
updatedAt: new Date().toISOString(),
|
||||
variants: [
|
||||
{
|
||||
id: 'variant_001',
|
||||
title: 'Variant 1',
|
||||
availableForSale: true,
|
||||
selectedOptions: [
|
||||
{
|
||||
name: 'Size',
|
||||
value: 'M'
|
||||
}
|
||||
],
|
||||
price: {
|
||||
amount: '90.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
}
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
|
||||
altText: 'Product 1 Image 1',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
|
||||
altText: 'Product 1 Image 2',
|
||||
width: 400,
|
||||
height: 400
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'prod_002',
|
||||
handle: 'product-2',
|
||||
availableForSale: false,
|
||||
title: 'Product 2',
|
||||
description: 'This is the description for Product 2',
|
||||
descriptionHtml: '<p>This is the <strong>HTML</strong> description for Product 2</p>',
|
||||
options: [
|
||||
{
|
||||
id: 'option_002',
|
||||
name: 'Color',
|
||||
values: ['Red', 'Blue', 'Green']
|
||||
}
|
||||
],
|
||||
priceRange: {
|
||||
maxVariantPrice: {
|
||||
amount: '120.00',
|
||||
currencyCode: 'USD'
|
||||
},
|
||||
minVariantPrice: {
|
||||
amount: '100.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
},
|
||||
featuredImage: {
|
||||
url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
|
||||
altText: 'Product 2 Featured Image',
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
seo: {
|
||||
title: 'Product 2 SEO Title',
|
||||
description: 'This is the SEO description for Product 2'
|
||||
},
|
||||
tags: ['tag3', 'tag4'],
|
||||
updatedAt: new Date().toISOString(),
|
||||
variants: [
|
||||
{
|
||||
id: 'variant_002',
|
||||
title: 'Variant 2',
|
||||
availableForSale: false,
|
||||
selectedOptions: [
|
||||
{
|
||||
name: 'Color',
|
||||
value: 'Red'
|
||||
}
|
||||
],
|
||||
price: {
|
||||
amount: '110.00',
|
||||
currencyCode: 'USD'
|
||||
}
|
||||
}
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
|
||||
altText: 'Product 2 Image 1',
|
||||
width: 500,
|
||||
height: 500
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Footer from 'components/layout/footer';
|
||||
import Collections from 'components/layout/search/collections';
|
||||
import FilterList from 'components/layout/search/filter';
|
||||
import Collections from 'components/layout/search/*not-in-use/collections';
|
||||
import FilterList from 'components/layout/search/*not-in-use/filter';
|
||||
import { sorting } from 'lib/constants';
|
||||
import ChildrenWrapper from './children-wrapper';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import clsx from 'clsx';
|
||||
|
||||
function Grid(props: React.ComponentProps<'ul'>) {
|
||||
return (
|
||||
<ul {...props} className={clsx('grid grid-flow-row gap-4', props.className)}>
|
||||
<ul {...props} className={clsx('grid grid-flow-row', props.className)}>
|
||||
{props.children}
|
||||
</ul>
|
||||
);
|
||||
@ -10,7 +10,7 @@ function Grid(props: React.ComponentProps<'ul'>) {
|
||||
|
||||
function GridItem(props: React.ComponentProps<'li'>) {
|
||||
return (
|
||||
<li {...props} className={clsx('aspect-square transition-opacity', props.className)}>
|
||||
<li {...props} className={clsx(props.className)}>
|
||||
{props.children}
|
||||
</li>
|
||||
);
|
||||
|
@ -1,61 +0,0 @@
|
||||
import { GridTileImage } from 'components/grid/tile';
|
||||
import { getCollectionProducts } from 'lib/shopify';
|
||||
import type { Product } from 'lib/shopify/types';
|
||||
import Link from 'next/link';
|
||||
|
||||
function ThreeItemGridItem({
|
||||
item,
|
||||
size,
|
||||
priority
|
||||
}: {
|
||||
item: Product;
|
||||
size: 'full' | 'half';
|
||||
priority?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={size === 'full' ? 'md:col-span-4 md:row-span-2' : 'md:col-span-2 md:row-span-1'}
|
||||
>
|
||||
<Link
|
||||
className="relative block aspect-square h-full w-full"
|
||||
href={`/product/${item.handle}`}
|
||||
prefetch={true}
|
||||
>
|
||||
<GridTileImage
|
||||
src={item.featuredImage.url}
|
||||
fill
|
||||
sizes={
|
||||
size === 'full' ? '(min-width: 768px) 66vw, 100vw' : '(min-width: 768px) 33vw, 100vw'
|
||||
}
|
||||
priority={priority}
|
||||
alt={item.title}
|
||||
label={{
|
||||
position: size === 'full' ? 'center' : 'bottom',
|
||||
title: item.title as string,
|
||||
amount: item.priceRange.maxVariantPrice.amount,
|
||||
currencyCode: item.priceRange.maxVariantPrice.currencyCode
|
||||
}}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function ThreeItemGrid() {
|
||||
// Collections that start with `hidden-*` are hidden from the search page.
|
||||
const homepageItems = await getCollectionProducts({
|
||||
collection: 'hidden-homepage-featured-items'
|
||||
});
|
||||
|
||||
if (!homepageItems[0] || !homepageItems[1] || !homepageItems[2]) return null;
|
||||
|
||||
const [firstProduct, secondProduct, thirdProduct] = homepageItems;
|
||||
|
||||
return (
|
||||
<section className="mx-auto grid max-w-screen-2xl gap-4 px-4 pb-4 md:grid-cols-6 md:grid-rows-2 lg:max-h-[calc(100vh-200px)]">
|
||||
<ThreeItemGridItem size="full" item={firstProduct} priority={true} />
|
||||
<ThreeItemGridItem size="half" item={secondProduct} priority={true} />
|
||||
<ThreeItemGridItem size="half" item={thirdProduct} />
|
||||
</section>
|
||||
);
|
||||
}
|
@ -1,49 +1,19 @@
|
||||
import clsx from 'clsx';
|
||||
import Image from 'next/image';
|
||||
import Label from '../label';
|
||||
|
||||
export function GridTileImage({
|
||||
isInteractive = true,
|
||||
active,
|
||||
label,
|
||||
title,
|
||||
...props
|
||||
}: {
|
||||
isInteractive?: boolean;
|
||||
active?: boolean;
|
||||
label?: {
|
||||
title: string;
|
||||
amount: string;
|
||||
currencyCode: string;
|
||||
position?: 'bottom' | 'center';
|
||||
};
|
||||
title?: string;
|
||||
} & React.ComponentProps<typeof Image>) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'group flex h-full w-full items-center justify-center overflow-hidden rounded-lg border bg-white hover:border-blue-600 dark:bg-black',
|
||||
{
|
||||
relative: label,
|
||||
'border-2 border-blue-600': active,
|
||||
'border-neutral-200 dark:border-neutral-800': !active
|
||||
}
|
||||
)}
|
||||
>
|
||||
{props.src ? (
|
||||
<Image
|
||||
className={clsx('relative h-full w-full object-contain', {
|
||||
'transition duration-300 ease-in-out group-hover:scale-105': isInteractive
|
||||
})}
|
||||
{...props}
|
||||
/>
|
||||
<>
|
||||
{props.src ? <Image fill className="h-full w-full object-cover" {...props} /> : null}
|
||||
{title ? (
|
||||
<h3 className="absolute bottom-0 w-full p-10 text-center text-[15px] text-lightText">
|
||||
{title}
|
||||
</h3>
|
||||
) : null}
|
||||
{label ? (
|
||||
<Label
|
||||
title={label.title}
|
||||
amount={label.amount}
|
||||
currencyCode={label.currencyCode}
|
||||
position={label.position}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
import Grid from 'components/grid';
|
||||
import { GridTileImage } from 'components/grid/tile';
|
||||
import { Product } from 'lib/shopify/types';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function ProductGridItems({ products }: { products: Product[] }) {
|
||||
return (
|
||||
<>
|
||||
{products.map((product) => (
|
||||
<Grid.Item key={product.handle} className="animate-fadeIn">
|
||||
<Link
|
||||
className="relative inline-block h-full w-full"
|
||||
href={`/product/${product.handle}`}
|
||||
prefetch={true}
|
||||
>
|
||||
<GridTileImage
|
||||
alt={product.title}
|
||||
label={{
|
||||
title: product.title,
|
||||
amount: product.priceRange.maxVariantPrice.amount,
|
||||
currencyCode: product.priceRange.maxVariantPrice.currencyCode
|
||||
}}
|
||||
src={product.featuredImage?.url}
|
||||
fill
|
||||
sizes="(min-width: 768px) 33vw, (min-width: 640px) 50vw, 100vw"
|
||||
/>
|
||||
</Link>
|
||||
</Grid.Item>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
27
components/product/product-grid-items.tsx
Normal file
27
components/product/product-grid-items.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { clsx } from 'clsx';
|
||||
import Grid from 'components/grid';
|
||||
import { GridTileImage } from 'components/grid/tile';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function ProductGridItems({
|
||||
src,
|
||||
title,
|
||||
handle,
|
||||
ratio
|
||||
}: {
|
||||
src: string;
|
||||
title: string;
|
||||
handle: string;
|
||||
ratio?: '2/3' | 'square';
|
||||
}) {
|
||||
return (
|
||||
<Grid.Item
|
||||
key={handle}
|
||||
className={clsx('relative w-full', ratio === '2/3' ? `aspect-[2/3]` : 'aspect-square')}
|
||||
>
|
||||
<Link className="h-full w-full" href={`/product/${handle}`} prefetch={true}>
|
||||
<GridTileImage alt={title} title={title} src={src} fill />
|
||||
</Link>
|
||||
</Grid.Item>
|
||||
);
|
||||
}
|
@ -5,8 +5,8 @@ module.exports = {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'cdn.shopify.com',
|
||||
pathname: '/s/files/**'
|
||||
hostname: 'cdn.shopify.com'
|
||||
// pathname: '/s/files/**'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user