@shopify/cli 3.65.1 → 3.65.3

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 (122) hide show
  1. package/dist/assets/hydrogen/i18n/domains.ts +4 -11
  2. package/dist/assets/hydrogen/i18n/mock-i18n-types.ts +4 -2
  3. package/dist/assets/hydrogen/i18n/subdomains.ts +4 -11
  4. package/dist/assets/hydrogen/i18n/subfolders.ts +4 -11
  5. package/dist/assets/hydrogen/starter/CHANGELOG.md +165 -0
  6. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +5 -2
  7. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +2 -2
  8. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +65 -19
  9. package/dist/assets/hydrogen/starter/app/components/PaginatedResourceSection.tsx +42 -0
  10. package/dist/assets/hydrogen/starter/app/components/SearchForm.tsx +68 -0
  11. package/dist/assets/hydrogen/starter/app/components/SearchFormPredictive.tsx +76 -0
  12. package/dist/assets/hydrogen/starter/app/components/SearchResults.tsx +164 -0
  13. package/dist/assets/hydrogen/starter/app/components/SearchResultsPredictive.tsx +322 -0
  14. package/dist/assets/hydrogen/starter/app/entry.client.tsx +10 -8
  15. package/dist/assets/hydrogen/starter/app/entry.server.tsx +1 -1
  16. package/dist/assets/hydrogen/starter/app/lib/context.ts +43 -0
  17. package/dist/assets/hydrogen/starter/app/lib/fragments.ts +53 -0
  18. package/dist/assets/hydrogen/starter/app/lib/search.ts +74 -24
  19. package/dist/assets/hydrogen/starter/app/root.tsx +4 -7
  20. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +2 -3
  21. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +5 -19
  22. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +11 -24
  23. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +14 -27
  24. package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +12 -30
  25. package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +13 -27
  26. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +9 -31
  27. package/dist/assets/hydrogen/starter/app/routes/search.tsx +312 -73
  28. package/dist/assets/hydrogen/starter/app/styles/reset.css +12 -2
  29. package/dist/assets/hydrogen/starter/env.d.ts +11 -30
  30. package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.jpg +0 -0
  31. package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.md +391 -0
  32. package/dist/assets/hydrogen/starter/guides/search/search.jpg +0 -0
  33. package/dist/assets/hydrogen/starter/guides/search/search.md +333 -0
  34. package/dist/assets/hydrogen/starter/package.json +4 -4
  35. package/dist/assets/hydrogen/starter/server.ts +18 -74
  36. package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +242 -172
  37. package/dist/assets/hydrogen/virtual-routes/components/{PageLayout.jsx → Layout.jsx} +2 -2
  38. package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +7 -6
  39. package/dist/{chunk-H42RFZDD.js → chunk-3ABSSTBQ.js} +4 -4
  40. package/dist/{chunk-M7WMYV4S.js → chunk-3D4VZQOH.js} +2 -2
  41. package/dist/{chunk-J7BYFGNJ.js → chunk-3GSKXZGY.js} +2 -2
  42. package/dist/{chunk-TDWX3KIR.js → chunk-3LDWVYMD.js} +2 -2
  43. package/dist/{chunk-N2BXKOJG.js → chunk-646BIVHE.js} +4 -4
  44. package/dist/{chunk-CZ3SHYYH.js → chunk-7WAEFADN.js} +4 -4
  45. package/dist/{chunk-EKT2GUGH.js → chunk-7WGBIPDW.js} +2 -2
  46. package/dist/{chunk-M6KGRVDD.js → chunk-AX77SAMU.js} +3 -3
  47. package/dist/{chunk-4HAEQQTQ.js → chunk-BQBBVYYU.js} +4 -4
  48. package/dist/{chunk-5YD4FDOS.js → chunk-BZLNTDGG.js} +3 -3
  49. package/dist/{chunk-VWALMO2Z.js → chunk-CSCEGIBZ.js} +3 -3
  50. package/dist/{chunk-F2Y7KYHZ.js → chunk-EIUQV76I.js} +5 -5
  51. package/dist/{chunk-MODBIZ4R.js → chunk-GN74L7IW.js} +2 -2
  52. package/dist/{chunk-5EAVIJTQ.js → chunk-HYCRESCR.js} +2 -2
  53. package/dist/{chunk-GDARYUPU.js → chunk-K7KD247K.js} +188 -243
  54. package/dist/{chunk-PZM45AUI.js → chunk-KIUXMPTX.js} +3 -3
  55. package/dist/{chunk-PYMSCBPA.js → chunk-LAJ4OEME.js} +2 -2
  56. package/dist/{chunk-YVHV3H5H.js → chunk-MIQBXNSN.js} +4 -4
  57. package/dist/{chunk-BLKDGMHM.js → chunk-MV6A3QHA.js} +4 -4
  58. package/dist/{chunk-CFFAWVDL.js → chunk-N3YORLAS.js} +2 -2
  59. package/dist/{chunk-EU5ZOEUT.js → chunk-NBTEOGQW.js} +2 -2
  60. package/dist/{chunk-ZXJU6UP4.js → chunk-O3JOUAA5.js} +4 -4
  61. package/dist/{chunk-EZ5DG73H.js → chunk-PEAIOYXD.js} +4 -4
  62. package/dist/{chunk-YDS7NZBQ.js → chunk-R5GT4GBL.js} +4 -4
  63. package/dist/{chunk-6M65VRAT.js → chunk-S7FJTFYR.js} +5 -5
  64. package/dist/{chunk-DX2RXOQ5.js → chunk-S7RH664J.js} +3 -3
  65. package/dist/{chunk-WMECC32P.js → chunk-SKF2SKWO.js} +3 -3
  66. package/dist/{chunk-27HGZPUX.js → chunk-SMKCVFDT.js} +3 -3
  67. package/dist/{chunk-EID6L4PR.js → chunk-T4Y7NDNJ.js} +2 -2
  68. package/dist/{chunk-PY33KMCK.js → chunk-TWWJNMTO.js} +2 -2
  69. package/dist/{chunk-YXPGPWR2.js → chunk-U2PN6QZ2.js} +5 -5
  70. package/dist/{chunk-3REVOIEW.js → chunk-UBCH575K.js} +5 -5
  71. package/dist/{chunk-A4NQWDPT.js → chunk-XLURAR5E.js} +3 -3
  72. package/dist/{chunk-ZZKUI3DP.js → chunk-YPG7LXPN.js} +3 -3
  73. package/dist/cli/commands/auth/logout.js +10 -10
  74. package/dist/cli/commands/auth/logout.test.js +11 -11
  75. package/dist/cli/commands/debug/command-flags.js +9 -9
  76. package/dist/cli/commands/demo/catalog.js +10 -10
  77. package/dist/cli/commands/demo/generate-file.js +10 -10
  78. package/dist/cli/commands/demo/index.js +10 -10
  79. package/dist/cli/commands/demo/print-ai-prompt.js +10 -10
  80. package/dist/cli/commands/docs/generate.js +9 -9
  81. package/dist/cli/commands/docs/generate.test.js +9 -9
  82. package/dist/cli/commands/help.js +9 -9
  83. package/dist/cli/commands/kitchen-sink/async.js +10 -10
  84. package/dist/cli/commands/kitchen-sink/async.test.js +10 -10
  85. package/dist/cli/commands/kitchen-sink/index.js +12 -12
  86. package/dist/cli/commands/kitchen-sink/index.test.js +12 -12
  87. package/dist/cli/commands/kitchen-sink/prompts.js +10 -10
  88. package/dist/cli/commands/kitchen-sink/prompts.test.js +10 -10
  89. package/dist/cli/commands/kitchen-sink/static.js +10 -10
  90. package/dist/cli/commands/kitchen-sink/static.test.js +10 -10
  91. package/dist/cli/commands/search.js +10 -10
  92. package/dist/cli/commands/upgrade.js +9 -9
  93. package/dist/cli/commands/version.js +10 -10
  94. package/dist/cli/commands/version.test.js +10 -10
  95. package/dist/cli/services/commands/search.js +2 -2
  96. package/dist/cli/services/commands/search.test.js +2 -2
  97. package/dist/cli/services/commands/version.js +4 -4
  98. package/dist/cli/services/commands/version.test.js +5 -5
  99. package/dist/cli/services/demo.js +2 -2
  100. package/dist/cli/services/demo.test.js +2 -2
  101. package/dist/cli/services/kitchen-sink/async.js +2 -2
  102. package/dist/cli/services/kitchen-sink/prompts.js +2 -2
  103. package/dist/cli/services/kitchen-sink/static.js +2 -2
  104. package/dist/cli/services/upgrade.js +3 -3
  105. package/dist/cli/services/upgrade.test.js +5 -5
  106. package/dist/{custom-oclif-loader-JHNX2EGV.js → custom-oclif-loader-BT7EH2NN.js} +3 -3
  107. package/dist/{error-handler-4UJ6363X.js → error-handler-OSEY6KVA.js} +8 -8
  108. package/dist/hooks/postrun.js +6 -6
  109. package/dist/hooks/prerun.js +4 -4
  110. package/dist/index.js +1333 -1279
  111. package/dist/{local-V7RONWNU.js → local-OQXN5NM2.js} +2 -2
  112. package/dist/{morph-DN4AZJZW.js → morph-IQTWRBBT.js} +16 -12
  113. package/dist/{node-3H4OKRLA.js → node-YQVH3Y7J.js} +13 -13
  114. package/dist/{node-package-manager-XM7EXHQA.js → node-package-manager-VW2DN7R4.js} +3 -3
  115. package/dist/{system-F63VIZ5U.js → system-347PZWVP.js} +2 -2
  116. package/dist/tsconfig.tsbuildinfo +1 -1
  117. package/dist/{ui-BXWWRIFS.js → ui-S7L55PBH.js} +2 -2
  118. package/dist/{workerd-A5NCF6UA.js → workerd-OLKE7G4X.js} +12 -12
  119. package/oclif.manifest.json +39 -2
  120. package/package.json +7 -7
  121. package/dist/assets/hydrogen/starter/app/components/Search.tsx +0 -514
  122. package/dist/assets/hydrogen/starter/app/routes/api.predictive-search.tsx +0 -318
@@ -1,29 +1,79 @@
1
1
  import type {
2
- PredictiveQueryFragment,
3
- SearchProductFragment,
4
- PredictiveProductFragment,
5
- PredictiveCollectionFragment,
6
- PredictivePageFragment,
7
- PredictiveArticleFragment,
2
+ PredictiveSearchQuery,
3
+ RegularSearchQuery,
8
4
  } from 'storefrontapi.generated';
9
5
 
10
- export function applyTrackingParams(
11
- resource:
12
- | PredictiveQueryFragment
13
- | SearchProductFragment
14
- | PredictiveProductFragment
15
- | PredictiveCollectionFragment
16
- | PredictiveArticleFragment
17
- | PredictivePageFragment,
18
- params?: string,
19
- ) {
20
- if (params) {
21
- return resource?.trackingParameters
22
- ? `?${params}&${resource.trackingParameters}`
23
- : `?${params}`;
24
- } else {
25
- return resource?.trackingParameters
26
- ? `?${resource.trackingParameters}`
27
- : '';
6
+ type ResultWithItems<Type extends 'predictive' | 'regular', Items> = {
7
+ type: Type;
8
+ term: string;
9
+ error?: string;
10
+ result: {total: number; items: Items};
11
+ };
12
+
13
+ export type RegularSearchReturn = ResultWithItems<
14
+ 'regular',
15
+ RegularSearchQuery
16
+ >;
17
+ export type PredictiveSearchReturn = ResultWithItems<
18
+ 'predictive',
19
+ NonNullable<PredictiveSearchQuery['predictiveSearch']>
20
+ >;
21
+
22
+ /**
23
+ * Returns the empty state of a predictive search result to reset the search state.
24
+ */
25
+ export function getEmptyPredictiveSearchResult(): PredictiveSearchReturn['result'] {
26
+ return {
27
+ total: 0,
28
+ items: {
29
+ articles: [],
30
+ collections: [],
31
+ products: [],
32
+ pages: [],
33
+ queries: [],
34
+ },
35
+ };
36
+ }
37
+
38
+ interface UrlWithTrackingParams {
39
+ /** The base URL to which the tracking parameters will be appended. */
40
+ baseUrl: string;
41
+ /** The trackingParams returned by the Storefront API. */
42
+ trackingParams?: string | null;
43
+ /** Any additional query parameters to be appended to the URL. */
44
+ params?: Record<string, string>;
45
+ /** The search term to be appended to the URL. */
46
+ term: string;
47
+ }
48
+
49
+ /**
50
+ * A utility function that appends tracking parameters to a URL. Tracking parameters are
51
+ * used internally by shopify to enhance search results and admin dashboards.
52
+ * @example
53
+ * ```ts
54
+ * const url = 'www.example.com';
55
+ * const trackingParams = 'utm_source=shopify&utm_medium=shopify_app&utm_campaign=storefront';
56
+ * const params = { foo: 'bar' };
57
+ * const term = 'search term';
58
+ * const url = urlWithTrackingParams({ baseUrl, trackingParams, params, term });
59
+ * console.log(url);
60
+ * // Output: 'https://www.example.com?foo=bar&q=search%20term&utm_source=shopify&utm_medium=shopify_app&utm_campaign=storefront'
61
+ * ```
62
+ */
63
+ export function urlWithTrackingParams({
64
+ baseUrl,
65
+ trackingParams,
66
+ params: extraParams,
67
+ term,
68
+ }: UrlWithTrackingParams) {
69
+ let search = new URLSearchParams({
70
+ ...extraParams,
71
+ q: encodeURIComponent(term),
72
+ }).toString();
73
+
74
+ if (trackingParams) {
75
+ search = `${search}&${trackingParams}`;
28
76
  }
77
+
78
+ return `${baseUrl}?${search}`;
29
79
  }
@@ -26,18 +26,15 @@ export const shouldRevalidate: ShouldRevalidateFunction = ({
26
26
  formMethod,
27
27
  currentUrl,
28
28
  nextUrl,
29
+ defaultShouldRevalidate,
29
30
  }) => {
30
31
  // revalidate when a mutation is performed e.g add to cart, login...
31
- if (formMethod && formMethod !== 'GET') {
32
- return true;
33
- }
32
+ if (formMethod && formMethod !== 'GET') return true;
34
33
 
35
34
  // revalidate when manually revalidating via useRevalidator
36
- if (currentUrl.toString() === nextUrl.toString()) {
37
- return true;
38
- }
35
+ if (currentUrl.toString() === nextUrl.toString()) return true;
39
36
 
40
- return false;
37
+ return defaultShouldRevalidate;
41
38
  };
42
39
 
43
40
  export function links() {
@@ -14,6 +14,7 @@ import {
14
14
  useNavigation,
15
15
  useOutletContext,
16
16
  type MetaFunction,
17
+ type Fetcher,
17
18
  } from '@remix-run/react';
18
19
  import {
19
20
  UPDATE_ADDRESS_MUTATION,
@@ -364,9 +365,7 @@ export function AddressForm({
364
365
  address: CustomerAddressInput;
365
366
  defaultAddress: CustomerFragment['defaultAddress'];
366
367
  children: (props: {
367
- stateForMethod: (
368
- method: 'PUT' | 'POST' | 'DELETE',
369
- ) => ReturnType<typeof useNavigation>['state'];
368
+ stateForMethod: (method: 'PUT' | 'POST' | 'DELETE') => Fetcher['state'];
370
369
  }) => React.ReactNode;
371
370
  }) {
372
371
  const {state, formMethod} = useNavigation();
@@ -1,16 +1,16 @@
1
1
  import {Link, useLoaderData, type MetaFunction} from '@remix-run/react';
2
2
  import {
3
3
  Money,
4
- Pagination,
5
4
  getPaginationVariables,
6
5
  flattenConnection,
7
6
  } from '@shopify/hydrogen';
8
- import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
7
+ import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
9
8
  import {CUSTOMER_ORDERS_QUERY} from '~/graphql/customer-account/CustomerOrdersQuery';
10
9
  import type {
11
10
  CustomerOrdersFragment,
12
11
  OrderItemFragment,
13
12
  } from 'customer-accountapi.generated';
13
+ import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
14
14
 
15
15
  export const meta: MetaFunction = () => {
16
16
  return [{title: 'Orders'}];
@@ -51,23 +51,9 @@ function OrdersTable({orders}: Pick<CustomerOrdersFragment, 'orders'>) {
51
51
  return (
52
52
  <div className="acccount-orders">
53
53
  {orders?.nodes.length ? (
54
- <Pagination connection={orders}>
55
- {({nodes, isLoading, PreviousLink, NextLink}) => {
56
- return (
57
- <>
58
- <PreviousLink>
59
- {isLoading ? 'Loading...' : <span>↑ Load previous</span>}
60
- </PreviousLink>
61
- {nodes.map((order) => {
62
- return <OrderItem key={order.id} order={order} />;
63
- })}
64
- <NextLink>
65
- {isLoading ? 'Loading...' : <span>Load more ↓</span>}
66
- </NextLink>
67
- </>
68
- );
69
- }}
70
- </Pagination>
54
+ <PaginatedResourceSection connection={orders}>
55
+ {({node: order}) => <OrderItem key={order.id} order={order} />}
56
+ </PaginatedResourceSection>
71
57
  ) : (
72
58
  <EmptyOrders />
73
59
  )}
@@ -1,7 +1,8 @@
1
1
  import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {Link, useLoaderData, type MetaFunction} from '@remix-run/react';
3
- import {Image, Pagination, getPaginationVariables} from '@shopify/hydrogen';
3
+ import {Image, getPaginationVariables} from '@shopify/hydrogen';
4
4
  import type {ArticleItemFragment} from 'storefrontapi.generated';
5
+ import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
5
6
 
6
7
  export const meta: MetaFunction<typeof loader> = ({data}) => {
7
8
  return [{title: `Hydrogen | ${data?.blog.title ?? ''} blog`}];
@@ -68,29 +69,15 @@ export default function Blog() {
68
69
  <div className="blog">
69
70
  <h1>{blog.title}</h1>
70
71
  <div className="blog-grid">
71
- <Pagination connection={articles}>
72
- {({nodes, isLoading, PreviousLink, NextLink}) => {
73
- return (
74
- <>
75
- <PreviousLink>
76
- {isLoading ? 'Loading...' : <span>↑ Load previous</span>}
77
- </PreviousLink>
78
- {nodes.map((article, index) => {
79
- return (
80
- <ArticleItem
81
- article={article}
82
- key={article.id}
83
- loading={index < 2 ? 'eager' : 'lazy'}
84
- />
85
- );
86
- })}
87
- <NextLink>
88
- {isLoading ? 'Loading...' : <span>Load more ↓</span>}
89
- </NextLink>
90
- </>
91
- );
92
- }}
93
- </Pagination>
72
+ <PaginatedResourceSection connection={articles}>
73
+ {({node: article, index}) => (
74
+ <ArticleItem
75
+ article={article}
76
+ key={article.id}
77
+ loading={index < 2 ? 'eager' : 'lazy'}
78
+ />
79
+ )}
80
+ </PaginatedResourceSection>
94
81
  </div>
95
82
  </div>
96
83
  );
@@ -1,6 +1,7 @@
1
1
  import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {Link, useLoaderData, type MetaFunction} from '@remix-run/react';
3
- import {Pagination, getPaginationVariables} from '@shopify/hydrogen';
3
+ import {getPaginationVariables} from '@shopify/hydrogen';
4
+ import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
4
5
 
5
6
  export const meta: MetaFunction = () => {
6
7
  return [{title: `Hydrogen | Blogs`}];
@@ -53,32 +54,18 @@ export default function Blogs() {
53
54
  <div className="blogs">
54
55
  <h1>Blogs</h1>
55
56
  <div className="blogs-grid">
56
- <Pagination connection={blogs}>
57
- {({nodes, isLoading, PreviousLink, NextLink}) => {
58
- return (
59
- <>
60
- <PreviousLink>
61
- {isLoading ? 'Loading...' : <span>↑ Load previous</span>}
62
- </PreviousLink>
63
- {nodes.map((blog) => {
64
- return (
65
- <Link
66
- className="blog"
67
- key={blog.handle}
68
- prefetch="intent"
69
- to={`/blogs/${blog.handle}`}
70
- >
71
- <h2>{blog.title}</h2>
72
- </Link>
73
- );
74
- })}
75
- <NextLink>
76
- {isLoading ? 'Loading...' : <span>Load more ↓</span>}
77
- </NextLink>
78
- </>
79
- );
80
- }}
81
- </Pagination>
57
+ <PaginatedResourceSection connection={blogs}>
58
+ {({node: blog}) => (
59
+ <Link
60
+ className="blog"
61
+ key={blog.handle}
62
+ prefetch="intent"
63
+ to={`/blogs/${blog.handle}`}
64
+ >
65
+ <h2>{blog.title}</h2>
66
+ </Link>
67
+ )}
68
+ </PaginatedResourceSection>
82
69
  </div>
83
70
  </div>
84
71
  );
@@ -1,7 +1,6 @@
1
1
  import {defer, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {useLoaderData, Link, type MetaFunction} from '@remix-run/react';
3
3
  import {
4
- Pagination,
5
4
  getPaginationVariables,
6
5
  Image,
7
6
  Money,
@@ -9,6 +8,7 @@ import {
9
8
  } from '@shopify/hydrogen';
10
9
  import type {ProductItemFragment} from 'storefrontapi.generated';
11
10
  import {useVariantUrl} from '~/lib/variants';
11
+ import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
12
12
 
13
13
  export const meta: MetaFunction<typeof loader> = ({data}) => {
14
14
  return [{title: `Hydrogen | ${data?.collection.title ?? ''} Collection`}];
@@ -77,20 +77,18 @@ export default function Collection() {
77
77
  <div className="collection">
78
78
  <h1>{collection.title}</h1>
79
79
  <p className="collection-description">{collection.description}</p>
80
- <Pagination connection={collection.products}>
81
- {({nodes, isLoading, PreviousLink, NextLink}) => (
82
- <>
83
- <PreviousLink>
84
- {isLoading ? 'Loading...' : <span>↑ Load previous</span>}
85
- </PreviousLink>
86
- <ProductsGrid products={nodes} />
87
- <br />
88
- <NextLink>
89
- {isLoading ? 'Loading...' : <span>Load more ↓</span>}
90
- </NextLink>
91
- </>
80
+ <PaginatedResourceSection
81
+ connection={collection.products}
82
+ resourcesClassName="products-grid"
83
+ >
84
+ {({node: product, index}) => (
85
+ <ProductItem
86
+ key={product.id}
87
+ product={product}
88
+ loading={index < 8 ? 'eager' : undefined}
89
+ />
92
90
  )}
93
- </Pagination>
91
+ </PaginatedResourceSection>
94
92
  <Analytics.CollectionView
95
93
  data={{
96
94
  collection: {
@@ -103,22 +101,6 @@ export default function Collection() {
103
101
  );
104
102
  }
105
103
 
106
- function ProductsGrid({products}: {products: ProductItemFragment[]}) {
107
- return (
108
- <div className="products-grid">
109
- {products.map((product, index) => {
110
- return (
111
- <ProductItem
112
- key={product.id}
113
- product={product}
114
- loading={index < 8 ? 'eager' : undefined}
115
- />
116
- );
117
- })}
118
- </div>
119
- );
120
- }
121
-
122
104
  function ProductItem({
123
105
  product,
124
106
  loading,
@@ -1,7 +1,8 @@
1
1
  import {useLoaderData, Link} from '@remix-run/react';
2
2
  import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
3
- import {Pagination, getPaginationVariables, Image} from '@shopify/hydrogen';
3
+ import {getPaginationVariables, Image} from '@shopify/hydrogen';
4
4
  import type {CollectionFragment} from 'storefrontapi.generated';
5
+ import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
5
6
 
6
7
  export async function loader(args: LoaderFunctionArgs) {
7
8
  // Start fetching non-critical data without blocking time to first byte
@@ -47,33 +48,18 @@ export default function Collections() {
47
48
  return (
48
49
  <div className="collections">
49
50
  <h1>Collections</h1>
50
- <Pagination connection={collections}>
51
- {({nodes, isLoading, PreviousLink, NextLink}) => (
52
- <div>
53
- <PreviousLink>
54
- {isLoading ? 'Loading...' : <span>↑ Load previous</span>}
55
- </PreviousLink>
56
- <CollectionsGrid collections={nodes} />
57
- <NextLink>
58
- {isLoading ? 'Loading...' : <span>Load more ↓</span>}
59
- </NextLink>
60
- </div>
51
+ <PaginatedResourceSection
52
+ connection={collections}
53
+ resourcesClassName="collections-grid"
54
+ >
55
+ {({node: collection, index}) => (
56
+ <CollectionItem
57
+ key={collection.id}
58
+ collection={collection}
59
+ index={index}
60
+ />
61
61
  )}
62
- </Pagination>
63
- </div>
64
- );
65
- }
66
-
67
- function CollectionsGrid({collections}: {collections: CollectionFragment[]}) {
68
- return (
69
- <div className="collections-grid">
70
- {collections.map((collection, index) => (
71
- <CollectionItem
72
- key={collection.id}
73
- collection={collection}
74
- index={index}
75
- />
76
- ))}
62
+ </PaginatedResourceSection>
77
63
  </div>
78
64
  );
79
65
  }
@@ -1,13 +1,9 @@
1
1
  import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {useLoaderData, Link, type MetaFunction} from '@remix-run/react';
3
- import {
4
- Pagination,
5
- getPaginationVariables,
6
- Image,
7
- Money,
8
- } from '@shopify/hydrogen';
3
+ import {getPaginationVariables, Image, Money} from '@shopify/hydrogen';
9
4
  import type {ProductItemFragment} from 'storefrontapi.generated';
10
5
  import {useVariantUrl} from '~/lib/variants';
6
+ import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
11
7
 
12
8
  export const meta: MetaFunction<typeof loader> = () => {
13
9
  return [{title: `Hydrogen | Products`}];
@@ -57,36 +53,18 @@ export default function Collection() {
57
53
  return (
58
54
  <div className="collection">
59
55
  <h1>Products</h1>
60
- <Pagination connection={products}>
61
- {({nodes, isLoading, PreviousLink, NextLink}) => (
62
- <>
63
- <PreviousLink>
64
- {isLoading ? 'Loading...' : <span>↑ Load previous</span>}
65
- </PreviousLink>
66
- <ProductsGrid products={nodes} />
67
- <br />
68
- <NextLink>
69
- {isLoading ? 'Loading...' : <span>Load more ↓</span>}
70
- </NextLink>
71
- </>
72
- )}
73
- </Pagination>
74
- </div>
75
- );
76
- }
77
-
78
- function ProductsGrid({products}: {products: ProductItemFragment[]}) {
79
- return (
80
- <div className="products-grid">
81
- {products.map((product, index) => {
82
- return (
56
+ <PaginatedResourceSection
57
+ connection={products}
58
+ resourcesClassName="products-grid"
59
+ >
60
+ {({node: product, index}) => (
83
61
  <ProductItem
84
62
  key={product.id}
85
63
  product={product}
86
64
  loading={index < 8 ? 'eager' : undefined}
87
65
  />
88
- );
89
- })}
66
+ )}
67
+ </PaginatedResourceSection>
90
68
  </div>
91
69
  );
92
70
  }