@shopify/create-hydrogen 1.0.0-alpha.23 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +256 -0
  2. package/bin/run.cmd +3 -0
  3. package/bin/run.js +7 -0
  4. package/dist/commands/init.js +157943 -0
  5. package/dist/commands/init.js.map +1 -0
  6. package/dist/index.js +151598 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +64 -16
  9. package/templates/template-hydrogen-default/.devcontainer/devcontainer.json +18 -0
  10. package/templates/template-hydrogen-default/.eslintrc.js +3 -0
  11. package/templates/template-hydrogen-default/.gitignore +7 -0
  12. package/{template-hydrogen → templates/template-hydrogen-default}/.stylelintrc.js +1 -1
  13. package/templates/template-hydrogen-default/.vscode/extensions.json +8 -0
  14. package/{template-hydrogen → templates/template-hydrogen-default}/README.md +11 -2
  15. package/{template-hydrogen → templates/template-hydrogen-default}/_gitignore +0 -0
  16. package/templates/template-hydrogen-default/index.html +20 -0
  17. package/templates/template-hydrogen-default/package.json.liquid +48 -0
  18. package/{template-hydrogen → templates/template-hydrogen-default}/postcss.config.js +0 -0
  19. package/templates/template-hydrogen-default/public/favicon.ico +0 -0
  20. package/{template-hydrogen → templates/template-hydrogen-default}/shopify.config.js +1 -2
  21. package/templates/template-hydrogen-default/src/App.server.jsx +27 -0
  22. package/templates/template-hydrogen-default/src/components/Button.client.jsx +65 -0
  23. package/templates/template-hydrogen-default/src/components/Cart.client.jsx +265 -0
  24. package/templates/template-hydrogen-default/src/components/CartIcon.jsx +33 -0
  25. package/templates/template-hydrogen-default/src/components/CartIconWithItems.client.jsx +28 -0
  26. package/templates/template-hydrogen-default/src/components/CartProvider.client.jsx +35 -0
  27. package/templates/template-hydrogen-default/src/components/CartToggle.client.jsx +30 -0
  28. package/templates/template-hydrogen-default/src/components/CartUIProvider.client.jsx +45 -0
  29. package/templates/template-hydrogen-default/src/components/CountrySelector.client.jsx +116 -0
  30. package/templates/template-hydrogen-default/src/components/DefaultSeo.server.jsx +36 -0
  31. package/templates/template-hydrogen-default/src/components/FeaturedCollection.jsx +26 -0
  32. package/templates/template-hydrogen-default/src/components/Footer.server.jsx +103 -0
  33. package/templates/template-hydrogen-default/src/components/Gallery.client.jsx +66 -0
  34. package/templates/template-hydrogen-default/src/components/Header.client.jsx +62 -0
  35. package/templates/template-hydrogen-default/src/components/Layout.server.jsx +86 -0
  36. package/templates/template-hydrogen-default/src/components/LoadMoreProducts.client.jsx +56 -0
  37. package/templates/template-hydrogen-default/src/components/LoadingFallback.jsx +26 -0
  38. package/templates/template-hydrogen-default/src/components/MobileCountrySelector.client.jsx +64 -0
  39. package/templates/template-hydrogen-default/src/components/MobileNavigation.client.jsx +98 -0
  40. package/templates/template-hydrogen-default/src/components/MoneyCompareAtPrice.client.jsx +14 -0
  41. package/templates/template-hydrogen-default/src/components/MoneyPrice.client.jsx +15 -0
  42. package/templates/template-hydrogen-default/src/components/Navigation.client.jsx +23 -0
  43. package/templates/template-hydrogen-default/src/components/NotFound.server.jsx +93 -0
  44. package/templates/template-hydrogen-default/src/components/OpenIcon.jsx +33 -0
  45. package/templates/template-hydrogen-default/src/components/ProductCard.jsx +50 -0
  46. package/templates/template-hydrogen-default/src/components/ProductDetails.client.jsx +233 -0
  47. package/{template-hydrogen → templates/template-hydrogen-default}/src/components/ProductOptions.client.jsx +7 -4
  48. package/templates/template-hydrogen-default/src/components/Welcome.server.jsx +188 -0
  49. package/{template-hydrogen → templates/template-hydrogen-default}/src/index.css +31 -0
  50. package/templates/template-hydrogen-default/src/pages/collections/[handle].server.jsx +105 -0
  51. package/templates/template-hydrogen-default/src/pages/index.server.jsx +241 -0
  52. package/{template-hydrogen → templates/template-hydrogen-default}/src/pages/pages/[handle].server.jsx +14 -5
  53. package/templates/template-hydrogen-default/src/pages/products/[handle].server.jsx +66 -0
  54. package/templates/template-hydrogen-default/src/pages/redirect.server.jsx +4 -0
  55. package/templates/template-hydrogen-default/src/pages/robots.txt.server.js +40 -0
  56. package/templates/template-hydrogen-default/src/pages/sitemap.xml.server.jsx +151 -0
  57. package/templates/template-hydrogen-default/src/routes/collections/[handle].server.jsx +105 -0
  58. package/templates/template-hydrogen-default/src/routes/index.server.jsx +241 -0
  59. package/templates/template-hydrogen-default/src/routes/pages/[handle].server.jsx +37 -0
  60. package/templates/template-hydrogen-default/src/routes/products/[handle].server.jsx +66 -0
  61. package/templates/template-hydrogen-default/src/routes/redirect.server.jsx +4 -0
  62. package/templates/template-hydrogen-default/src/routes/robots.txt.server.js +40 -0
  63. package/templates/template-hydrogen-default/src/routes/sitemap.xml.server.jsx +151 -0
  64. package/templates/template-hydrogen-default/tailwind.config.js +26 -0
  65. package/{template-hydrogen → templates/template-hydrogen-default}/vite.config.js +1 -0
  66. package/templates/template-hydrogen-minimal/README.md +8 -0
  67. package/templates/template-hydrogen-minimal/package.json +14 -0
  68. package/index.js +0 -201
  69. package/scripts/tmp-copy-template-from-dev.js +0 -21
  70. package/scripts/utils.js +0 -29
  71. package/template-hydrogen/.eslintrc.js +0 -41
  72. package/template-hydrogen/.vscode/extensions.json +0 -3
  73. package/template-hydrogen/Dockerfile +0 -15
  74. package/template-hydrogen/index.html +0 -14
  75. package/template-hydrogen/package.json +0 -43
  76. package/template-hydrogen/server.js +0 -40
  77. package/template-hydrogen/src/App.server.jsx +0 -42
  78. package/template-hydrogen/src/components/Cart.client.jsx +0 -287
  79. package/template-hydrogen/src/components/CartProvider.client.jsx +0 -15
  80. package/template-hydrogen/src/components/DefaultSeo.server.jsx +0 -22
  81. package/template-hydrogen/src/components/Footer.jsx +0 -12
  82. package/template-hydrogen/src/components/Gallery.client.jsx +0 -36
  83. package/template-hydrogen/src/components/Header.client.jsx +0 -46
  84. package/template-hydrogen/src/components/HightlightedProduct.client.jsx +0 -54
  85. package/template-hydrogen/src/components/Layout.client.jsx +0 -61
  86. package/template-hydrogen/src/components/MediaPlaceholder.jsx +0 -21
  87. package/template-hydrogen/src/components/NotFound.server.jsx +0 -104
  88. package/template-hydrogen/src/components/ProductCard.client.jsx +0 -39
  89. package/template-hydrogen/src/components/ProductDetails.client.jsx +0 -184
  90. package/template-hydrogen/src/components/Seo.client.jsx +0 -75
  91. package/template-hydrogen/src/entry-client.jsx +0 -12
  92. package/template-hydrogen/src/entry-server.jsx +0 -7
  93. package/template-hydrogen/src/favicon.svg +0 -15
  94. package/template-hydrogen/src/pages/Index.server.jsx +0 -186
  95. package/template-hydrogen/src/pages/blogs/[handle]/[articleHandle].server.jsx +0 -49
  96. package/template-hydrogen/src/pages/blogs/[handle].server.jsx +0 -76
  97. package/template-hydrogen/src/pages/collections/[handle].server.jsx +0 -69
  98. package/template-hydrogen/src/pages/products/[handle].server.jsx +0 -56
  99. package/template-hydrogen/src/pages/search.server.jsx +0 -107
  100. package/template-hydrogen/src/pages/sitemap.xml.server.jsx +0 -64
  101. package/template-hydrogen/tailwind.config.js +0 -9
  102. package/template-hydrogen/worker.js +0 -22
@@ -0,0 +1,56 @@
1
+ import {useServerState} from '@shopify/hydrogen/client';
2
+
3
+ /**
4
+ * A client component that provides functionality to initially show a subset of products and a button to load more products
5
+ */
6
+ export default function LoadMoreProducts({startingCount}) {
7
+ const {pending, serverState, setServerState} = useServerState();
8
+
9
+ return (
10
+ <div className="flex justify-center h-14">
11
+ {pending ? (
12
+ <SpinnerIcon />
13
+ ) : (
14
+ <button
15
+ type="button"
16
+ disabled={pending}
17
+ className={`uppercase border-4 bg-white border-black text-black text-center px-5 py-3 font-mono font-bold drop-shadow-lg active:drop-shadow-none hover:bg-black hover:text-white hover:border-white ${
18
+ pending ? 'opacity-50' : undefined
19
+ }`}
20
+ onClick={() => {
21
+ setServerState(
22
+ 'collectionProductCount',
23
+ serverState.collectionProductCount
24
+ ? serverState.collectionProductCount + 24
25
+ : startingCount + 1,
26
+ );
27
+ }}
28
+ >
29
+ Load more
30
+ </button>
31
+ )}
32
+ </div>
33
+ );
34
+ }
35
+
36
+ function SpinnerIcon() {
37
+ return (
38
+ <svg
39
+ width="54"
40
+ height="54"
41
+ viewBox="0 0 54 54"
42
+ fill="none"
43
+ xmlns="http://www.w3.org/2000/svg"
44
+ className="animate-spin"
45
+ >
46
+ <path
47
+ d="M43.6663 27.0002C43.6663 36.2049 36.2044 43.6668 26.9997 43.6668C17.7949 43.6668 10.333 36.2049 10.333 27.0002C10.333 17.7954 17.7949 10.3335 26.9997 10.3335C36.2044 10.3335 43.6663 17.7954 43.6663 27.0002ZM13.6663 27.0002C13.6663 34.364 19.6359 40.3335 26.9997 40.3335C34.3635 40.3335 40.333 34.364 40.333 27.0002C40.333 19.6364 34.3635 13.6668 26.9997 13.6668C19.6359 13.6668 13.6663 19.6364 13.6663 27.0002Z"
48
+ fill="#E6E7EB"
49
+ />
50
+ <path
51
+ d="M26.9997 10.3332C29.1884 10.3332 31.3556 10.7643 33.3777 11.6018C35.3998 12.4394 37.2371 13.6671 38.7848 15.2147C40.3324 16.7624 41.5601 18.5997 42.3977 20.6218C43.2352 22.6439 43.6663 24.8111 43.6663 26.9998L40.333 26.9998C40.333 25.2489 39.9881 23.5151 39.3181 21.8974C38.648 20.2797 37.6659 18.8099 36.4278 17.5717C35.1896 16.3336 33.7198 15.3515 32.1021 14.6814C30.4844 14.0114 28.7506 13.6665 26.9997 13.6665L26.9997 10.3332Z"
52
+ fill="black"
53
+ />
54
+ </svg>
55
+ );
56
+ }
@@ -0,0 +1,26 @@
1
+ import CartIcon from './CartIcon';
2
+ import OpenIcon from './OpenIcon';
3
+
4
+ /**
5
+ * A shared component and Suspense call that's used in `App.server.jsx` to let your app wait for code to load while declaring a loading state
6
+ */
7
+ export default function LoadingFallback() {
8
+ return (
9
+ <header className="h-20 lg:h-32 max-w-screen text-gray-700">
10
+ <div className="fixed z-10 h-20 lg:h-32 w-full bg-white/90 border-b border-black border-opacity-5 px-6 md:px-8 md:py-6 lg:pt-8 lg:pb-0 mx-auto">
11
+ <div className="h-full flex lg:flex-col place-content-between">
12
+ <div className="text-center w-full flex justify-between items-center">
13
+ <div className="hidden lg:block w-16" />
14
+ <div className="lg:hidden flex justify-center items-center w-7 h-full">
15
+ <OpenIcon />
16
+ </div>
17
+ <p className="font-black uppercase text-3xl tracking-widest">
18
+ Snowdevil
19
+ </p>
20
+ <CartIcon />
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </header>
25
+ );
26
+ }
@@ -0,0 +1,64 @@
1
+ import {useCallback} from 'react';
2
+ import {useAvailableCountries, useCountry} from '@shopify/hydrogen/client';
3
+ import {Listbox} from '@headlessui/react';
4
+
5
+ import {ArrowIcon, CheckIcon} from './CountrySelector.client';
6
+
7
+ /**
8
+ * A client component that selects the appropriate country to display for products on a mobile storefront
9
+ */
10
+ export default function MobileCountrySelector() {
11
+ const countries = [...useAvailableCountries()].sort((a, b) =>
12
+ a.name.localeCompare(b.name),
13
+ );
14
+ const [selectedCountry, setSelectedCountry] = useCountry();
15
+
16
+ const setCountry = useCallback(
17
+ (isoCode) => {
18
+ setSelectedCountry(
19
+ countries.find((country) => country.isoCode === isoCode),
20
+ );
21
+ },
22
+ [countries, setSelectedCountry],
23
+ );
24
+
25
+ return (
26
+ <div className="mt-8 rounded border border-gray-200 w-full">
27
+ <Listbox onChange={setCountry}>
28
+ {({open}) => (
29
+ <>
30
+ <Listbox.Button className="w-full flex justify-between text-sm items-center py-5 px-7">
31
+ {selectedCountry.name}
32
+ <ArrowIcon isOpen={open} />
33
+ </Listbox.Button>
34
+ <Listbox.Options className="w-full px-3 pb-2 text-lg">
35
+ <Listbox.Option
36
+ disabled
37
+ className="font-medium px-4 pb-4 w-full text-left uppercase"
38
+ >
39
+ Country
40
+ </Listbox.Option>
41
+ {countries.map((country) => {
42
+ const isSelected = country.isoCode === selectedCountry.isoCode;
43
+ return (
44
+ <Listbox.Option key={country.isoCode} value={country.isoCode}>
45
+ {({active}) => (
46
+ <div
47
+ className={`py-2 px-4 rounded flex justify-between items-center text-left w-full cursor-pointer ${
48
+ active ? 'bg-gray-100' : null
49
+ }`}
50
+ >
51
+ {country.name}
52
+ {isSelected ? <CheckIcon /> : null}
53
+ </div>
54
+ )}
55
+ </Listbox.Option>
56
+ );
57
+ })}
58
+ </Listbox.Options>
59
+ </>
60
+ )}
61
+ </Listbox>
62
+ </div>
63
+ );
64
+ }
@@ -0,0 +1,98 @@
1
+ import {Fragment, useEffect, useState} from 'react';
2
+ import {Link} from '@shopify/hydrogen/client';
3
+ import {FocusTrap} from '@headlessui/react';
4
+
5
+ import MobileCountrySelector from './MobileCountrySelector.client';
6
+ import OpenIcon from './OpenIcon';
7
+
8
+ /**
9
+ * A client component that defines the navigation for a mobile storefront
10
+ */
11
+ export default function MobileNavigation({collections, isOpen, setIsOpen}) {
12
+ const OpenFocusTrap = isOpen ? FocusTrap : Fragment;
13
+
14
+ const [topScrollOffset, setTopScrollOffset] = useState(0);
15
+
16
+ useEffect(() => {
17
+ if (isOpen) {
18
+ setTopScrollOffset(window.scrollY);
19
+ document.body.style.position = 'fixed';
20
+ } else {
21
+ document.body.style.position = '';
22
+ window.scrollTo(0, parseInt(topScrollOffset, 10));
23
+ }
24
+ }, [isOpen, topScrollOffset]);
25
+
26
+ return (
27
+ <div className="lg:hidden">
28
+ <OpenFocusTrap>
29
+ <button
30
+ type="button"
31
+ className="flex justify-center items-center w-7 h-full"
32
+ onClick={() => setIsOpen((previousIsOpen) => !previousIsOpen)}
33
+ >
34
+ {isOpen ? <CloseIcon /> : <OpenIcon />}
35
+ </button>
36
+ {isOpen ? (
37
+ <div className="fixed -left-0 top-20 w-full h-screen z-10 bg-gray-50 px-4 md:px-12 py-7">
38
+ <ul>
39
+ {collections.map((collection) => (
40
+ <li className="border-b border-gray-200" key={collection.id}>
41
+ <Link
42
+ className="group py-5 text-gray-700 flex items-center justify-between"
43
+ to={`/collections/${collection.handle}`}
44
+ onClick={() => setIsOpen(false)}
45
+ >
46
+ {collection.title}
47
+ <ArrowRightIcon className="hidden group-hover:block" />
48
+ </Link>
49
+ </li>
50
+ ))}
51
+ </ul>
52
+ <MobileCountrySelector />
53
+ </div>
54
+ ) : null}
55
+ </OpenFocusTrap>
56
+ </div>
57
+ );
58
+ }
59
+
60
+ function CloseIcon() {
61
+ return (
62
+ <svg
63
+ width="18"
64
+ height="18"
65
+ viewBox="0 0 18 18"
66
+ fill="none"
67
+ xmlns="http://www.w3.org/2000/svg"
68
+ >
69
+ <path
70
+ d="M1 17L17 1M1 1L17 17"
71
+ stroke="black"
72
+ strokeWidth="2"
73
+ strokeLinecap="round"
74
+ strokeLinejoin="round"
75
+ />
76
+ </svg>
77
+ );
78
+ }
79
+
80
+ function ArrowRightIcon({className}) {
81
+ return (
82
+ <svg
83
+ className={className}
84
+ width="7"
85
+ height="12"
86
+ viewBox="0 0 7 12"
87
+ fill="none"
88
+ xmlns="http://www.w3.org/2000/svg"
89
+ >
90
+ <path
91
+ fillRule="evenodd"
92
+ clipRule="evenodd"
93
+ d="M0.999762 12C0.743762 12 0.487762 11.902 0.292762 11.707C-0.0982383 11.316 -0.0982383 10.684 0.292762 10.293L4.58576 6.00001L0.292762 1.70701C-0.0982383 1.31601 -0.0982383 0.684006 0.292762 0.293006C0.683762 -0.0979941 1.31576 -0.0979941 1.70676 0.293006L6.70676 5.29301C7.09776 5.68401 7.09776 6.31601 6.70676 6.70701L1.70676 11.707C1.51176 11.902 1.25576 12 0.999762 12Z"
94
+ fill="black"
95
+ />
96
+ </svg>
97
+ );
98
+ }
@@ -0,0 +1,14 @@
1
+ import {useMoney} from '@shopify/hydrogen/client';
2
+
3
+ /**
4
+ * A client component that renders a product's compare at price
5
+ */
6
+ export default function MoneyCompareAtPrice({money}) {
7
+ const {amount, currencyNarrowSymbol} = useMoney(money);
8
+ return (
9
+ <span className="line-through text-lg mr-2.5 text-gray-500">
10
+ {currencyNarrowSymbol}
11
+ {amount}
12
+ </span>
13
+ );
14
+ }
@@ -0,0 +1,15 @@
1
+ import {useMoney} from '@shopify/hydrogen/client';
2
+
3
+ /**
4
+ * A client component that defines the currency code, currency symbol, and amount of a product
5
+ */
6
+ export default function MoneyPrice({money}) {
7
+ const {currencyCode, currencyNarrowSymbol, amount} = useMoney(money);
8
+ return (
9
+ <span className="text-black text-md">
10
+ {currencyCode}
11
+ {currencyNarrowSymbol}
12
+ {amount}
13
+ </span>
14
+ );
15
+ }
@@ -0,0 +1,23 @@
1
+ import {Link} from '@shopify/hydrogen/client';
2
+
3
+ /**
4
+ * A client component that defines the navigation for a web storefront
5
+ */
6
+ export default function Navigation({collections}) {
7
+ return (
8
+ <nav className="hidden lg:block text-center">
9
+ <ul className="md:flex items-center justify-center">
10
+ {collections.map((collection) => (
11
+ <li key={collection.id}>
12
+ <Link
13
+ to={`/collections/${collection.handle}`}
14
+ className="block p-4 hover:opacity-80"
15
+ >
16
+ {collection.title}
17
+ </Link>
18
+ </li>
19
+ ))}
20
+ </ul>
21
+ </nav>
22
+ );
23
+ }
@@ -0,0 +1,93 @@
1
+ import {
2
+ useShopQuery,
3
+ ProductProviderFragment,
4
+ flattenConnection,
5
+ } from '@shopify/hydrogen';
6
+ import gql from 'graphql-tag';
7
+
8
+ import Layout from './Layout.server';
9
+ import Button from './Button.client';
10
+ import ProductCard from './ProductCard';
11
+
12
+ /**
13
+ * A server component that defines the content to display when a page isn't found (404 error)
14
+ */
15
+ function NotFoundHero() {
16
+ return (
17
+ <div className="py-10 border-b border-gray-200">
18
+ <div className="max-w-3xl text-center mx-4 md:mx-auto">
19
+ <h1 className="font-bold text-4xl md:text-5xl text-gray-900 mb-6 mt-6">
20
+ We&#39;ve lost this page
21
+ </h1>
22
+ <p className="text-lg m-8 text-gray-500">
23
+ We couldn’t find the page you’re looking for. Try checking the URL or
24
+ heading back to the home page.
25
+ </p>
26
+ <Button
27
+ className="w-full md:mx-auto md:w-96"
28
+ url="/"
29
+ label="Take me to the home page"
30
+ />
31
+ </div>
32
+ </div>
33
+ );
34
+ }
35
+
36
+ export default function NotFound({country = {isoCode: 'US'}}) {
37
+ const {data} = useShopQuery({
38
+ query: QUERY,
39
+ variables: {
40
+ country: country.isoCode,
41
+ numProductMetafields: 0,
42
+ numProductVariants: 250,
43
+ numProductMedia: 0,
44
+ numProductVariantMetafields: 0,
45
+ numProductVariantSellingPlanAllocations: 0,
46
+ numProductSellingPlanGroups: 0,
47
+ numProductSellingPlans: 0,
48
+ },
49
+ });
50
+ const products = data ? flattenConnection(data.products) : [];
51
+
52
+ return (
53
+ <Layout>
54
+ <NotFoundHero />
55
+ <div className="my-8">
56
+ <p className="mb-8 text-lg text-black font-medium uppercase">
57
+ Products you might like
58
+ </p>
59
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
60
+ {products.map((product) => (
61
+ <div key={product.id}>
62
+ <ProductCard product={product} />
63
+ </div>
64
+ ))}
65
+ </div>
66
+ </div>
67
+ </Layout>
68
+ );
69
+ }
70
+
71
+ const QUERY = gql`
72
+ query NotFoundProductDetails(
73
+ $country: CountryCode
74
+ $includeReferenceMetafieldDetails: Boolean = false
75
+ $numProductMetafields: Int!
76
+ $numProductVariants: Int!
77
+ $numProductMedia: Int!
78
+ $numProductVariantMetafields: Int!
79
+ $numProductVariantSellingPlanAllocations: Int!
80
+ $numProductSellingPlanGroups: Int!
81
+ $numProductSellingPlans: Int!
82
+ ) @inContext(country: $country) {
83
+ products(first: 3) {
84
+ edges {
85
+ node {
86
+ ...ProductProviderFragment
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ ${ProductProviderFragment}
93
+ `;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * A shared component that specifies the icon to render when opened
3
+ */
4
+ export default function OpenIcon() {
5
+ return (
6
+ <svg
7
+ width="24"
8
+ height="20"
9
+ viewBox="0 0 24 20"
10
+ fill="none"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ >
13
+ <path
14
+ fillRule="evenodd"
15
+ clipRule="evenodd"
16
+ d="M0.800049 1.9999C0.800049 1.11625 1.51639 0.399902 2.40005 0.399902H21.6C22.4837 0.399902 23.2001 1.11625 23.2001 1.9999C23.2001 2.88356 22.4837 3.5999 21.6 3.5999H2.40005C1.51639 3.5999 0.800049 2.88356 0.800049 1.9999Z"
17
+ fill="black"
18
+ />
19
+ <path
20
+ fillRule="evenodd"
21
+ clipRule="evenodd"
22
+ d="M0.800049 9.9999C0.800049 9.11625 1.51639 8.3999 2.40005 8.3999H21.6C22.4837 8.3999 23.2001 9.11625 23.2001 9.9999C23.2001 10.8836 22.4837 11.5999 21.6 11.5999H2.40005C1.51639 11.5999 0.800049 10.8836 0.800049 9.9999Z"
23
+ fill="black"
24
+ />
25
+ <path
26
+ fillRule="evenodd"
27
+ clipRule="evenodd"
28
+ d="M0.800049 17.9999C0.800049 17.1162 1.51639 16.3999 2.40005 16.3999H21.6C22.4837 16.3999 23.2001 17.1162 23.2001 17.9999C23.2001 18.8836 22.4837 19.5999 21.6 19.5999H2.40005C1.51639 19.5999 0.800049 18.8836 0.800049 17.9999Z"
29
+ fill="black"
30
+ />
31
+ </svg>
32
+ );
33
+ }
@@ -0,0 +1,50 @@
1
+ import {Image, Link} from '@shopify/hydrogen';
2
+
3
+ import MoneyCompareAtPrice from './MoneyCompareAtPrice.client';
4
+ import MoneyPrice from './MoneyPrice.client';
5
+
6
+ /**
7
+ * A shared component that displays a single product to allow buyers to quickly identify a particular item of interest
8
+ */
9
+ export default function ProductCard({product}) {
10
+ const selectedVariant = product.variants.edges[0].node;
11
+
12
+ if (selectedVariant == null) {
13
+ return null;
14
+ }
15
+
16
+ return (
17
+ <div className="text-md mb-4 relative">
18
+ <Link to={`/products/${product.handle}`}>
19
+ <div className="rounded-lg border-2 border-gray-200 mb-2 relative flex items-center justify-center overflow-hidden object-cover h-96">
20
+ {selectedVariant.image ? (
21
+ <Image
22
+ className="bg-white absolute w-full h-full transition-all duration-500 ease-in-out transform bg-center bg-cover object-center object-contain hover:scale-110"
23
+ data={selectedVariant.image}
24
+ />
25
+ ) : null}
26
+ {!selectedVariant?.availableForSale && (
27
+ <div className="absolute top-3 left-3 rounded-3xl text-xs bg-black text-white py-3 px-4">
28
+ Out of stock
29
+ </div>
30
+ )}
31
+ </div>
32
+
33
+ <span className="text-black font-semibold mb-0.5">{product.title}</span>
34
+
35
+ {product.vendor && (
36
+ <p className="text-gray-900 font-medium text-sm mb-0.5">
37
+ {product.vendor}
38
+ </p>
39
+ )}
40
+
41
+ <div className="flex ">
42
+ {selectedVariant.compareAtPriceV2 && (
43
+ <MoneyCompareAtPrice money={selectedVariant.compareAtPriceV2} />
44
+ )}
45
+ <MoneyPrice money={selectedVariant.priceV2} />
46
+ </div>
47
+ </Link>
48
+ </div>
49
+ );
50
+ }