@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,28 @@
1
+ import {useCart} from '@shopify/hydrogen/client';
2
+
3
+ import CartIcon from './CartIcon';
4
+
5
+ /**
6
+ * A client component that specifies the icon to use if a cart contains merchandise
7
+ */
8
+ export default function CartIconWithItems() {
9
+ const {totalQuantity} = useCart();
10
+
11
+ return (
12
+ <>
13
+ <div className="relative">
14
+ <CartIcon />
15
+
16
+ <div
17
+ className={`bg-blue-700 text-xs rounded-full leading-none text-white absolute bottom-3 right-1 flex items-center justify-center transform translate-y-1/2 transition-all ${
18
+ totalQuantity > 0 ? 'h-4 w-4' : 'h-0 w-0 overflow-hidden'
19
+ }`}
20
+ aria-hidden
21
+ >
22
+ {totalQuantity > 0 ? totalQuantity : null}
23
+ </div>
24
+ </div>
25
+ <span className="sr-only">Cart, {totalQuantity} items</span>
26
+ </>
27
+ );
28
+ }
@@ -0,0 +1,35 @@
1
+ import {useCallback} from 'react';
2
+ import {CartProvider as ShopifyCartProvider} from '@shopify/hydrogen/client';
3
+
4
+ import CartUIProvider, {useCartUI} from './CartUIProvider.client';
5
+
6
+ /**
7
+ * A client component that creates a cart object and provides callbacks that can be accessed by any descendent component using the `useCart` hook and related hooks
8
+ */
9
+ export default function CartProvider({children, numCartLines}) {
10
+ return (
11
+ <CartUIProvider>
12
+ <Provider numCartLines={numCartLines}>{children}</Provider>
13
+ </CartUIProvider>
14
+ );
15
+ }
16
+
17
+ function Provider({children, numCartLines}) {
18
+ const {openCart} = useCartUI();
19
+
20
+ const open = useCallback(() => {
21
+ openCart();
22
+ }, [openCart]);
23
+
24
+ return (
25
+ <>
26
+ <ShopifyCartProvider
27
+ numCartLines={numCartLines}
28
+ onLineAdd={open}
29
+ onCreate={open}
30
+ >
31
+ {children}
32
+ </ShopifyCartProvider>
33
+ </>
34
+ );
35
+ }
@@ -0,0 +1,30 @@
1
+ import {useCartUI} from './CartUIProvider.client';
2
+ import CartIconWithItems from './CartIconWithItems.client';
3
+
4
+ /**
5
+ * A client component that defines the behavior when a user toggles a cart
6
+ */
7
+ export default function CartToggle({handleClick}) {
8
+ const cartUI = useCartUI();
9
+
10
+ if (cartUI == null) {
11
+ throw new Error('CartToggle must be a descendent of a CartUIProvider');
12
+ }
13
+
14
+ const {isCartOpen, toggleCart} = cartUI;
15
+
16
+ return (
17
+ <button
18
+ type="button"
19
+ aria-expanded={isCartOpen}
20
+ aria-controls="cart"
21
+ onClick={() => {
22
+ toggleCart();
23
+ handleClick();
24
+ }}
25
+ >
26
+ <CartIconWithItems />
27
+ <span className="sr-only">Open cart</span>
28
+ </button>
29
+ );
30
+ }
@@ -0,0 +1,45 @@
1
+ import React, {
2
+ useState,
3
+ useMemo,
4
+ useCallback,
5
+ useContext,
6
+ createContext,
7
+ } from 'react';
8
+
9
+ export const CartContext = createContext(null);
10
+
11
+ /**
12
+ * A client component that defines the behavior that occurs when a user is interacting with a cart (for example, opening or closing it)
13
+ */
14
+ export default function CartUIProvider({children}) {
15
+ const [open, setOpen] = useState(false);
16
+
17
+ const openCart = useCallback(() => {
18
+ setOpen(true);
19
+ }, [setOpen]);
20
+
21
+ const closeCart = useCallback(() => {
22
+ setOpen(false);
23
+ }, [setOpen]);
24
+
25
+ const toggleCart = useCallback(() => {
26
+ setOpen(!open);
27
+ }, [setOpen, open]);
28
+
29
+ const contextValue = useMemo(() => {
30
+ return {
31
+ isCartOpen: open,
32
+ openCart,
33
+ closeCart,
34
+ toggleCart,
35
+ };
36
+ }, [open, openCart, closeCart, toggleCart]);
37
+
38
+ return (
39
+ <CartContext.Provider value={contextValue}>{children}</CartContext.Provider>
40
+ );
41
+ }
42
+
43
+ export function useCartUI() {
44
+ return useContext(CartContext);
45
+ }
@@ -0,0 +1,116 @@
1
+ import {useCallback, useMemo} from 'react';
2
+ import {useAvailableCountries, useCountry} from '@shopify/hydrogen/client';
3
+ import {Listbox} from '@headlessui/react';
4
+
5
+ /**
6
+ * A client component that selects the appropriate country to display for products on a website
7
+ */
8
+ export default function CountrySelector() {
9
+ const availableCountries = useAvailableCountries();
10
+
11
+ const countries = useMemo(
12
+ () => [...availableCountries].sort((a, b) => a.name.localeCompare(b.name)),
13
+ [availableCountries],
14
+ );
15
+
16
+ const [selectedCountry, setSelectedCountry] = useCountry();
17
+
18
+ const setCountry = useCallback(
19
+ (isoCode) => {
20
+ setSelectedCountry(
21
+ countries.find((country) => country.isoCode === isoCode),
22
+ );
23
+ },
24
+ [countries, setSelectedCountry],
25
+ );
26
+
27
+ return (
28
+ <div className="hidden lg:block">
29
+ <Listbox onChange={setCountry}>
30
+ {({open}) => (
31
+ <>
32
+ <Listbox.Button className="font-medium text-sm h-8 p-2 flex items-center">
33
+ <span className="mr-4">{selectedCountry.name}</span>
34
+ <ArrowIcon isOpen={open} />
35
+ </Listbox.Button>
36
+
37
+ <Listbox.Options className="absolute z-10 mt-2">
38
+ <div className="bg-white p-4 rounded-lg drop-shadow-2xl">
39
+ <Listbox.Option
40
+ disabled
41
+ className="p-2 text-md text-left font-medium uppercase"
42
+ >
43
+ Country
44
+ </Listbox.Option>
45
+ {countries.map((country) => {
46
+ const isSelected =
47
+ country.isoCode === selectedCountry.isoCode;
48
+ return (
49
+ <Listbox.Option
50
+ key={country.isoCode}
51
+ value={country.isoCode}
52
+ >
53
+ {({active}) => (
54
+ <div
55
+ className={`w-36 py-2 px-3 flex justify-between items-center text-left cursor-pointer rounded
56
+ ${active ? 'bg-gray-200' : null}`}
57
+ >
58
+ {country.name}
59
+ {isSelected ? <CheckIcon /> : null}
60
+ </div>
61
+ )}
62
+ </Listbox.Option>
63
+ );
64
+ })}
65
+ </div>
66
+ </Listbox.Options>
67
+ </>
68
+ )}
69
+ </Listbox>
70
+ </div>
71
+ );
72
+ }
73
+
74
+ export function CheckIcon() {
75
+ return (
76
+ <svg
77
+ width="20"
78
+ height="20"
79
+ viewBox="0 0 20 20"
80
+ fill="none"
81
+ xmlns="http://www.w3.org/2000/svg"
82
+ aria-hidden="true"
83
+ >
84
+ <path
85
+ d="M7 10L9 12L13 8M19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1C14.9706 1 19 5.02944 19 10Z"
86
+ stroke="#354CF6"
87
+ strokeWidth="2"
88
+ strokeLinecap="round"
89
+ strokeLinejoin="round"
90
+ />
91
+ </svg>
92
+ );
93
+ }
94
+
95
+ export function ArrowIcon({isOpen}) {
96
+ return (
97
+ <svg
98
+ className={`transition-transform duration-300 ${
99
+ isOpen ? 'rotate-180' : null
100
+ }`}
101
+ aria-hidden="true"
102
+ width="10"
103
+ height="6"
104
+ viewBox="0 0 10 6"
105
+ fill="none"
106
+ xmlns="http://www.w3.org/2000/svg"
107
+ >
108
+ <path
109
+ fillRule="evenodd"
110
+ clipRule="evenodd"
111
+ d="M0.292893 0.292893C0.683416 -0.097631 1.31658 -0.097631 1.7071 0.292893L4.99999 3.58579L8.29288 0.292893C8.6834 -0.0976311 9.31657 -0.0976311 9.70709 0.292893C10.0976 0.683417 10.0976 1.31658 9.70709 1.70711L5.7071 5.70711C5.31657 6.09763 4.68341 6.09763 4.29289 5.70711L0.292893 1.70711C-0.0976309 1.31658 -0.0976309 0.683417 0.292893 0.292893Z"
112
+ fill="#374151"
113
+ />
114
+ </svg>
115
+ );
116
+ }
@@ -0,0 +1,36 @@
1
+ import {useShopQuery, Seo, CacheDays} from '@shopify/hydrogen';
2
+ import gql from 'graphql-tag';
3
+
4
+ /**
5
+ * A server component that fetches a `shop.name` and sets default values and templates for every page on a website
6
+ */
7
+ export default function DefaultSeo() {
8
+ const {
9
+ data: {
10
+ shop: {name: shopName, description: shopDescription},
11
+ },
12
+ } = useShopQuery({
13
+ query: QUERY,
14
+ cache: CacheDays(),
15
+ preload: '*',
16
+ });
17
+
18
+ return (
19
+ <Seo
20
+ type="defaultSeo"
21
+ data={{
22
+ title: shopName,
23
+ description: shopDescription,
24
+ }}
25
+ />
26
+ );
27
+ }
28
+
29
+ const QUERY = gql`
30
+ query shopInfo {
31
+ shop {
32
+ name
33
+ description
34
+ }
35
+ }
36
+ `;
@@ -0,0 +1,26 @@
1
+ import {Image, Link} from '@shopify/hydrogen';
2
+
3
+ /**
4
+ * A shared component that defines a single featured collection to display on a storefront
5
+ */
6
+ export default function FeaturedCollection({collection}) {
7
+ return collection ? (
8
+ <div className="shadow-xl rounded-xl grid grid-cols-1 lg:grid-cols-2 items-center bg-white overflow-hidden">
9
+ {collection.image ? (
10
+ <Image width="622" height="465" data={collection.image} />
11
+ ) : null}
12
+ <div className="px-10 py-10 lg:py-0">
13
+ <h2 className="text-gray-700 text-3xl font-bold mb-5">
14
+ {collection.title}
15
+ </h2>
16
+ <p className="text-lg text-gray-500 mb-6">{collection.description}</p>
17
+ <Link
18
+ to={`/collections/${collection.handle}`}
19
+ className="inline-block bg-gray-900 text-white text-lg font-medium rounded-md py-4 px-16 uppercase"
20
+ >
21
+ Shop Collection
22
+ </Link>
23
+ </div>
24
+ </div>
25
+ ) : null;
26
+ }
@@ -0,0 +1,103 @@
1
+ import {Link} from '@shopify/hydrogen';
2
+
3
+ /**
4
+ * A server component that specifies the content of the footer on the website
5
+ */
6
+ export default function Footer({collection, product}) {
7
+ return (
8
+ <footer role="contentinfo">
9
+ <div className="relative bg-white border-t border-b border-black border-opacity-5">
10
+ <div className="mx-auto max-w-7xl px-4 py-14 md:px-8 grid grid-cols-1 md:grid-cols-3 gap-12">
11
+ <div>
12
+ <h2 className="text-md font-medium uppercase mb-4">Community</h2>
13
+ <ul className="mt-8 space-y-4">
14
+ <li className="text-sm font-medium text-gray-600 hover:text-gray-900">
15
+ <a
16
+ href="https://github.com/Shopify/hydrogen/discussions"
17
+ target="_blank"
18
+ rel="noreferrer"
19
+ className="flex items-center"
20
+ >
21
+ <svg
22
+ aria-hidden="true"
23
+ className="fill-current text-gray-600 mr-3"
24
+ width="26"
25
+ height="20"
26
+ viewBox="0 0 21 20"
27
+ fill="none"
28
+ xmlns="http://www.w3.org/2000/svg"
29
+ >
30
+ <path
31
+ fillRule="evenodd"
32
+ clipRule="evenodd"
33
+ d="M10.1319 0.000976562C4.60874 0.000976562 0.135254 4.58917 0.135254 10.2539C0.135254 14.7908 2.99679 18.6229 6.97045 19.9814C7.47028 20.0711 7.65772 19.7635 7.65772 19.4944C7.65772 19.2509 7.64522 18.4434 7.64522 17.5848C5.13357 18.059 4.48379 16.9568 4.28385 16.38C4.17139 16.0853 3.68406 15.1753 3.2592 14.9318C2.90932 14.7396 2.40949 14.2654 3.2467 14.2526C4.03394 14.2397 4.59625 14.9959 4.78369 15.3035C5.68338 16.8542 7.1204 16.4185 7.6952 16.1494C7.78267 15.4829 8.04508 15.0343 8.33249 14.778C6.10824 14.5217 3.78402 13.6374 3.78402 9.71564C3.78402 8.60063 4.17139 7.67786 4.80868 6.96016C4.70871 6.70383 4.35883 5.65291 4.90864 4.24313C4.90864 4.24313 5.74586 3.97399 7.65772 5.29406C8.45745 5.06336 9.30716 4.94802 10.1569 4.94802C11.0066 4.94802 11.8563 5.06336 12.656 5.29406C14.5679 3.96117 15.4051 4.24313 15.4051 4.24313C15.9549 5.65291 15.605 6.70383 15.5051 6.96016C16.1424 7.67786 16.5297 8.58781 16.5297 9.71564C16.5297 13.6502 14.193 14.5217 11.9688 14.778C12.3311 15.0984 12.6435 15.7136 12.6435 16.6748C12.6435 18.0461 12.631 19.1483 12.631 19.4944C12.631 19.7635 12.8185 20.0839 13.3183 19.9814C15.3028 19.2943 17.0273 17.9861 18.2489 16.2411C19.4706 14.4962 20.128 12.4022 20.1285 10.2539C20.1285 4.58917 15.655 0.000976562 10.1319 0.000976562Z"
34
+ />
35
+ </svg>
36
+ Github discussions
37
+ </a>
38
+ </li>
39
+ <li className="text-sm font-medium text-gray-600 hover:text-gray-900">
40
+ <a
41
+ href="https://discord.gg/ppSbThrFaS"
42
+ target="_blank"
43
+ rel="noreferrer"
44
+ className="flex items-center"
45
+ >
46
+ <svg
47
+ aria-hidden="true"
48
+ className="fill-current text-gray-600 mr-3"
49
+ width="26"
50
+ height="20"
51
+ viewBox="0 0 26 20"
52
+ fill="none"
53
+ xmlns="http://www.w3.org/2000/svg"
54
+ >
55
+ <path d="M21.3103 1.67597C19.691 0.893476 17.9595 0.324791 16.1494 0.000976562C15.9271 0.416095 15.6673 0.974439 15.4883 1.4186C13.564 1.11972 11.6574 1.11972 9.76855 1.4186C9.58952 0.974439 9.3239 0.416095 9.0996 0.000976562C7.28746 0.324791 5.55403 0.895566 3.93472 1.68012C0.668559 6.77767 -0.216844 11.7486 0.225859 16.649C2.39215 18.3198 4.49155 19.3348 6.55551 19.9989C7.06512 19.2745 7.51962 18.5045 7.91116 17.693C7.16546 17.4003 6.45123 17.0392 5.77638 16.6199C5.95541 16.4829 6.13054 16.3397 6.29973 16.1923C10.4159 18.1807 14.8882 18.1807 18.9551 16.1923C19.1263 16.3397 19.3014 16.4829 19.4785 16.6199C18.8016 17.0412 18.0855 17.4024 17.3398 17.6951C17.7313 18.5045 18.1838 19.2766 18.6954 20.001C20.7614 19.3368 22.8627 18.3219 25.029 16.649C25.5484 10.9682 24.1416 6.04292 21.3103 1.67597ZM8.47192 13.6353C7.2363 13.6353 6.22299 12.4439 6.22299 10.9931C6.22299 9.5423 7.21466 8.34886 8.47192 8.34886C9.72922 8.34886 10.7425 9.54021 10.7209 10.9931C10.7228 12.4439 9.72922 13.6353 8.47192 13.6353ZM16.7829 13.6353C15.5473 13.6353 14.534 12.4439 14.534 10.9931C14.534 9.5423 15.5256 8.34886 16.7829 8.34886C18.0402 8.34886 19.0535 9.54021 19.0319 10.9931C19.0319 12.4439 18.0402 13.6353 16.7829 13.6353Z" />
56
+ </svg>
57
+ Discord
58
+ </a>
59
+ </li>
60
+ </ul>
61
+ </div>
62
+ <div>
63
+ <h2 className="text-md font-medium uppercase mb-4">Templates</h2>
64
+ <ul className="mt-8 space-y-4">
65
+ <li className="flex items-center text-sm font-medium text-gray-600 hover:text-gray-900">
66
+ <Link to="/home">Home</Link>
67
+ </li>
68
+ <li className="flex items-center text-sm font-medium text-gray-600 hover:text-gray-900">
69
+ <Link to={`/products/${product?.handle}`}>Product</Link>
70
+ </li>
71
+ <li className="flex items-center text-sm font-medium text-gray-600 hover:text-gray-900">
72
+ <Link to={`/collections/${collection?.handle}`}>
73
+ Collection
74
+ </Link>
75
+ </li>
76
+ <li className="flex items-center text-sm font-medium text-gray-600 hover:text-gray-900">
77
+ <Link to="/404">404</Link>
78
+ </li>
79
+ </ul>
80
+ </div>
81
+ <div>
82
+ <h2 className="text-md font-medium uppercase mb-4">Docs</h2>
83
+ <ul className="mt-8 space-y-4">
84
+ <li className="flex items-center text-sm font-medium text-gray-600 hover:text-gray-900">
85
+ <a href="https://shopify.dev/custom-storefronts/hydrogen">
86
+ Hydrogen overview
87
+ </a>
88
+ </li>
89
+ <li className="flex items-center text-sm font-medium text-gray-600 hover:text-gray-900">
90
+ <a href="https://shopify.dev/custom-storefronts/hydrogen/reference">
91
+ Hydrogen reference
92
+ </a>
93
+ </li>
94
+ </ul>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ <div className="py-6 px-4 md:px-8 bg-gray-50">
99
+ <p className="text-gray-600">© {new Date().getFullYear()} Shopify</p>
100
+ </div>
101
+ </footer>
102
+ );
103
+ }
@@ -0,0 +1,66 @@
1
+ import {useProduct, MediaFile, Image} from '@shopify/hydrogen/client';
2
+
3
+ /**
4
+ * A client component that defines a media gallery for hosting images, 3D models, and videos of products
5
+ */
6
+ export default function Gallery() {
7
+ const {media, selectedVariant} = useProduct();
8
+
9
+ const featuredMedia = selectedVariant.image || media[0]?.image;
10
+ const featuredMediaSrc = featuredMedia?.url.split('?')[0];
11
+ const galleryMedia = media.filter((med) => {
12
+ if (
13
+ med.mediaContentType === MODEL_3D_TYPE ||
14
+ med.mediaContentType === VIDEO_TYPE ||
15
+ med.mediaContentType === EXTERNAL_VIDEO_TYPE
16
+ ) {
17
+ return true;
18
+ }
19
+
20
+ return !med.image.url.includes(featuredMediaSrc);
21
+ });
22
+
23
+ if (!media.length) {
24
+ return null;
25
+ }
26
+
27
+ return (
28
+ <div
29
+ className="gap-4 flex md:grid md:grid-cols-2 overflow-x-scroll no-scrollbar scroll-snap-x scroll-smooth h-[485px] md:h-auto place-content-start"
30
+ tabIndex="-1"
31
+ >
32
+ <Image
33
+ data={selectedVariant.image}
34
+ className="w-[80vw] md:w-full h-full md:h-auto object-cover object-center flex-shrink-0 md:flex-shrink-none snap-start md:col-span-2 border border-gray-200 rounded-lg"
35
+ />
36
+ {galleryMedia.map((med) => {
37
+ let extraProps = {};
38
+
39
+ if (med.mediaContentType === MODEL_3D_TYPE) {
40
+ extraProps = MODEL_3D_PROPS;
41
+ }
42
+
43
+ return (
44
+ <MediaFile
45
+ tabIndex="0"
46
+ key={med.id || med.image.id}
47
+ className="w-[80vw] md:w-auto h-full md:h-auto object-cover object-center transition-all snap-start border border-gray-200 flex-shrink-0 rounded-lg"
48
+ data={med}
49
+ options={{
50
+ height: '485',
51
+ crop: 'center',
52
+ }}
53
+ {...extraProps}
54
+ />
55
+ );
56
+ })}
57
+ </div>
58
+ );
59
+ }
60
+
61
+ const MODEL_3D_TYPE = 'MODEL_3D';
62
+ const MODEL_3D_PROPS = {
63
+ interactionPromptThreshold: '0',
64
+ };
65
+ const VIDEO_TYPE = 'VIDEO';
66
+ const EXTERNAL_VIDEO_TYPE = 'EXTERNAL_VIDEO';
@@ -0,0 +1,62 @@
1
+ import {useEffect, useState} from 'react';
2
+ import {Link} from '@shopify/hydrogen/client';
3
+
4
+ import CartToggle from './CartToggle.client';
5
+ import {useCartUI} from './CartUIProvider.client';
6
+ import CountrySelector from './CountrySelector.client';
7
+ import Navigation from './Navigation.client';
8
+ import MobileNavigation from './MobileNavigation.client';
9
+
10
+ /**
11
+ * A client component that specifies the content of the header on the website
12
+ */
13
+ export default function Header({collections, storeName}) {
14
+ const [isMobileNavOpen, setIsMobileNavOpen] = useState(false);
15
+ const [scrollbarWidth, setScrollbarWidth] = useState(0);
16
+ const {isCartOpen} = useCartUI();
17
+
18
+ useEffect(() => {
19
+ const scrollbarWidth =
20
+ window.innerWidth - document.documentElement.clientWidth;
21
+
22
+ setScrollbarWidth(scrollbarWidth);
23
+ }, [isCartOpen]);
24
+
25
+ return (
26
+ <header className="h-20 lg:h-32" role="banner">
27
+ <div
28
+ className={`fixed z-20 h-20 lg:h-32 w-full border-b border-gray-200 px-6 md:px-8 md:py-6 lg:pt-8 lg:pb-0 mx-auto bg-white ${
29
+ isMobileNavOpen ? '' : 'bg-opacity-95'
30
+ }`}
31
+ >
32
+ <div
33
+ className="h-full flex lg:flex-col place-content-between"
34
+ style={{
35
+ paddingRight: isCartOpen ? scrollbarWidth : 0,
36
+ }}
37
+ >
38
+ <div className="text-center w-full flex justify-between items-center">
39
+ <CountrySelector />
40
+ <MobileNavigation
41
+ collections={collections}
42
+ isOpen={isMobileNavOpen}
43
+ setIsOpen={setIsMobileNavOpen}
44
+ />
45
+ <Link
46
+ className="font-black uppercase text-3xl tracking-widest"
47
+ to="/"
48
+ >
49
+ {storeName}
50
+ </Link>
51
+ <CartToggle
52
+ handleClick={() => {
53
+ if (isMobileNavOpen) setIsMobileNavOpen(false);
54
+ }}
55
+ />
56
+ </div>
57
+ <Navigation collections={collections} storeName={storeName} />
58
+ </div>
59
+ </div>
60
+ </header>
61
+ );
62
+ }
@@ -0,0 +1,86 @@
1
+ import {
2
+ Image,
3
+ useShopQuery,
4
+ flattenConnection,
5
+ LocalizationProvider,
6
+ CacheHours,
7
+ } from '@shopify/hydrogen';
8
+ import gql from 'graphql-tag';
9
+
10
+ import Header from './Header.client';
11
+ import Footer from './Footer.server';
12
+ import Cart from './Cart.client';
13
+ import {Suspense} from 'react';
14
+
15
+ /**
16
+ * A server component that defines a structure and organization of a page that can be used in different parts of the Hydrogen app
17
+ */
18
+ export default function Layout({children, hero}) {
19
+ const {data} = useShopQuery({
20
+ query: QUERY,
21
+ variables: {
22
+ numCollections: 3,
23
+ },
24
+ cache: CacheHours(),
25
+ preload: '*',
26
+ });
27
+ const collections = data ? flattenConnection(data.collections) : null;
28
+ const products = data ? flattenConnection(data.products) : null;
29
+ const storeName = data ? data.shop.name : '';
30
+
31
+ return (
32
+ <LocalizationProvider preload="*">
33
+ <div className="absolute top-0 left-0">
34
+ <a
35
+ href="#mainContent"
36
+ className="p-4 focus:block sr-only focus:not-sr-only"
37
+ >
38
+ Skip to content
39
+ </a>
40
+ </div>
41
+ <div className="min-h-screen max-w-screen text-gray-700 font-sans">
42
+ {/* TODO: Find out why Suspense needs to be here to prevent hydration errors. */}
43
+ <Suspense fallback={null}>
44
+ <Header collections={collections} storeName={storeName} />
45
+ <Cart />
46
+ </Suspense>
47
+ <main role="main" id="mainContent" className="relative bg-gray-50">
48
+ {hero}
49
+ <div className="mx-auto max-w-7xl p-4 md:py-5 md:px-8">
50
+ {children}
51
+ </div>
52
+ </main>
53
+ <Footer collection={collections[0]} product={products[0]} />
54
+ </div>
55
+ </LocalizationProvider>
56
+ );
57
+ }
58
+
59
+ const QUERY = gql`
60
+ query layoutContent($numCollections: Int!) {
61
+ shop {
62
+ name
63
+ }
64
+ collections(first: $numCollections) {
65
+ edges {
66
+ node {
67
+ description
68
+ handle
69
+ id
70
+ title
71
+ image {
72
+ ...ImageFragment
73
+ }
74
+ }
75
+ }
76
+ }
77
+ products(first: 1) {
78
+ edges {
79
+ node {
80
+ handle
81
+ }
82
+ }
83
+ }
84
+ }
85
+ ${Image.Fragment}
86
+ `;