@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.
- package/dist/assets/hydrogen/i18n/domains.ts +4 -11
- package/dist/assets/hydrogen/i18n/mock-i18n-types.ts +4 -2
- package/dist/assets/hydrogen/i18n/subdomains.ts +4 -11
- package/dist/assets/hydrogen/i18n/subfolders.ts +4 -11
- package/dist/assets/hydrogen/starter/CHANGELOG.md +165 -0
- package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +5 -2
- package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +2 -2
- package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +65 -19
- package/dist/assets/hydrogen/starter/app/components/PaginatedResourceSection.tsx +42 -0
- package/dist/assets/hydrogen/starter/app/components/SearchForm.tsx +68 -0
- package/dist/assets/hydrogen/starter/app/components/SearchFormPredictive.tsx +76 -0
- package/dist/assets/hydrogen/starter/app/components/SearchResults.tsx +164 -0
- package/dist/assets/hydrogen/starter/app/components/SearchResultsPredictive.tsx +322 -0
- package/dist/assets/hydrogen/starter/app/entry.client.tsx +10 -8
- package/dist/assets/hydrogen/starter/app/entry.server.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/lib/context.ts +43 -0
- package/dist/assets/hydrogen/starter/app/lib/fragments.ts +53 -0
- package/dist/assets/hydrogen/starter/app/lib/search.ts +74 -24
- package/dist/assets/hydrogen/starter/app/root.tsx +4 -7
- package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +2 -3
- package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +5 -19
- package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +11 -24
- package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +14 -27
- package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +12 -30
- package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +13 -27
- package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +9 -31
- package/dist/assets/hydrogen/starter/app/routes/search.tsx +312 -73
- package/dist/assets/hydrogen/starter/app/styles/reset.css +12 -2
- package/dist/assets/hydrogen/starter/env.d.ts +11 -30
- package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.jpg +0 -0
- package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.md +391 -0
- package/dist/assets/hydrogen/starter/guides/search/search.jpg +0 -0
- package/dist/assets/hydrogen/starter/guides/search/search.md +333 -0
- package/dist/assets/hydrogen/starter/package.json +4 -4
- package/dist/assets/hydrogen/starter/server.ts +18 -74
- package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +242 -172
- package/dist/assets/hydrogen/virtual-routes/components/{PageLayout.jsx → Layout.jsx} +2 -2
- package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +7 -6
- package/dist/{chunk-H42RFZDD.js → chunk-3ABSSTBQ.js} +4 -4
- package/dist/{chunk-M7WMYV4S.js → chunk-3D4VZQOH.js} +2 -2
- package/dist/{chunk-J7BYFGNJ.js → chunk-3GSKXZGY.js} +2 -2
- package/dist/{chunk-TDWX3KIR.js → chunk-3LDWVYMD.js} +2 -2
- package/dist/{chunk-N2BXKOJG.js → chunk-646BIVHE.js} +4 -4
- package/dist/{chunk-CZ3SHYYH.js → chunk-7WAEFADN.js} +4 -4
- package/dist/{chunk-EKT2GUGH.js → chunk-7WGBIPDW.js} +2 -2
- package/dist/{chunk-M6KGRVDD.js → chunk-AX77SAMU.js} +3 -3
- package/dist/{chunk-4HAEQQTQ.js → chunk-BQBBVYYU.js} +4 -4
- package/dist/{chunk-5YD4FDOS.js → chunk-BZLNTDGG.js} +3 -3
- package/dist/{chunk-VWALMO2Z.js → chunk-CSCEGIBZ.js} +3 -3
- package/dist/{chunk-F2Y7KYHZ.js → chunk-EIUQV76I.js} +5 -5
- package/dist/{chunk-MODBIZ4R.js → chunk-GN74L7IW.js} +2 -2
- package/dist/{chunk-5EAVIJTQ.js → chunk-HYCRESCR.js} +2 -2
- package/dist/{chunk-GDARYUPU.js → chunk-K7KD247K.js} +188 -243
- package/dist/{chunk-PZM45AUI.js → chunk-KIUXMPTX.js} +3 -3
- package/dist/{chunk-PYMSCBPA.js → chunk-LAJ4OEME.js} +2 -2
- package/dist/{chunk-YVHV3H5H.js → chunk-MIQBXNSN.js} +4 -4
- package/dist/{chunk-BLKDGMHM.js → chunk-MV6A3QHA.js} +4 -4
- package/dist/{chunk-CFFAWVDL.js → chunk-N3YORLAS.js} +2 -2
- package/dist/{chunk-EU5ZOEUT.js → chunk-NBTEOGQW.js} +2 -2
- package/dist/{chunk-ZXJU6UP4.js → chunk-O3JOUAA5.js} +4 -4
- package/dist/{chunk-EZ5DG73H.js → chunk-PEAIOYXD.js} +4 -4
- package/dist/{chunk-YDS7NZBQ.js → chunk-R5GT4GBL.js} +4 -4
- package/dist/{chunk-6M65VRAT.js → chunk-S7FJTFYR.js} +5 -5
- package/dist/{chunk-DX2RXOQ5.js → chunk-S7RH664J.js} +3 -3
- package/dist/{chunk-WMECC32P.js → chunk-SKF2SKWO.js} +3 -3
- package/dist/{chunk-27HGZPUX.js → chunk-SMKCVFDT.js} +3 -3
- package/dist/{chunk-EID6L4PR.js → chunk-T4Y7NDNJ.js} +2 -2
- package/dist/{chunk-PY33KMCK.js → chunk-TWWJNMTO.js} +2 -2
- package/dist/{chunk-YXPGPWR2.js → chunk-U2PN6QZ2.js} +5 -5
- package/dist/{chunk-3REVOIEW.js → chunk-UBCH575K.js} +5 -5
- package/dist/{chunk-A4NQWDPT.js → chunk-XLURAR5E.js} +3 -3
- package/dist/{chunk-ZZKUI3DP.js → chunk-YPG7LXPN.js} +3 -3
- package/dist/cli/commands/auth/logout.js +10 -10
- package/dist/cli/commands/auth/logout.test.js +11 -11
- package/dist/cli/commands/debug/command-flags.js +9 -9
- package/dist/cli/commands/demo/catalog.js +10 -10
- package/dist/cli/commands/demo/generate-file.js +10 -10
- package/dist/cli/commands/demo/index.js +10 -10
- package/dist/cli/commands/demo/print-ai-prompt.js +10 -10
- package/dist/cli/commands/docs/generate.js +9 -9
- package/dist/cli/commands/docs/generate.test.js +9 -9
- package/dist/cli/commands/help.js +9 -9
- package/dist/cli/commands/kitchen-sink/async.js +10 -10
- package/dist/cli/commands/kitchen-sink/async.test.js +10 -10
- package/dist/cli/commands/kitchen-sink/index.js +12 -12
- package/dist/cli/commands/kitchen-sink/index.test.js +12 -12
- package/dist/cli/commands/kitchen-sink/prompts.js +10 -10
- package/dist/cli/commands/kitchen-sink/prompts.test.js +10 -10
- package/dist/cli/commands/kitchen-sink/static.js +10 -10
- package/dist/cli/commands/kitchen-sink/static.test.js +10 -10
- package/dist/cli/commands/search.js +10 -10
- package/dist/cli/commands/upgrade.js +9 -9
- package/dist/cli/commands/version.js +10 -10
- package/dist/cli/commands/version.test.js +10 -10
- package/dist/cli/services/commands/search.js +2 -2
- package/dist/cli/services/commands/search.test.js +2 -2
- package/dist/cli/services/commands/version.js +4 -4
- package/dist/cli/services/commands/version.test.js +5 -5
- package/dist/cli/services/demo.js +2 -2
- package/dist/cli/services/demo.test.js +2 -2
- package/dist/cli/services/kitchen-sink/async.js +2 -2
- package/dist/cli/services/kitchen-sink/prompts.js +2 -2
- package/dist/cli/services/kitchen-sink/static.js +2 -2
- package/dist/cli/services/upgrade.js +3 -3
- package/dist/cli/services/upgrade.test.js +5 -5
- package/dist/{custom-oclif-loader-JHNX2EGV.js → custom-oclif-loader-BT7EH2NN.js} +3 -3
- package/dist/{error-handler-4UJ6363X.js → error-handler-OSEY6KVA.js} +8 -8
- package/dist/hooks/postrun.js +6 -6
- package/dist/hooks/prerun.js +4 -4
- package/dist/index.js +1333 -1279
- package/dist/{local-V7RONWNU.js → local-OQXN5NM2.js} +2 -2
- package/dist/{morph-DN4AZJZW.js → morph-IQTWRBBT.js} +16 -12
- package/dist/{node-3H4OKRLA.js → node-YQVH3Y7J.js} +13 -13
- package/dist/{node-package-manager-XM7EXHQA.js → node-package-manager-VW2DN7R4.js} +3 -3
- package/dist/{system-F63VIZ5U.js → system-347PZWVP.js} +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/{ui-BXWWRIFS.js → ui-S7L55PBH.js} +2 -2
- package/dist/{workerd-A5NCF6UA.js → workerd-OLKE7G4X.js} +12 -12
- package/oclif.manifest.json +39 -2
- package/package.json +7 -7
- package/dist/assets/hydrogen/starter/app/components/Search.tsx +0 -514
- package/dist/assets/hydrogen/starter/app/routes/api.predictive-search.tsx +0 -318
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
renderText,
|
|
18
18
|
renderTextPrompt,
|
|
19
19
|
renderWarning
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-T4Y7NDNJ.js";
|
|
21
21
|
import "./chunk-JIJ65JT6.js";
|
|
22
22
|
import "./chunk-UMWKJFTM.js";
|
|
23
23
|
import "./chunk-GEN4RXTD.js";
|
|
@@ -54,4 +54,4 @@ export {
|
|
|
54
54
|
renderTextPrompt,
|
|
55
55
|
renderWarning
|
|
56
56
|
};
|
|
57
|
-
//# sourceMappingURL=ui-
|
|
57
|
+
//# sourceMappingURL=ui-S7L55PBH.js.map
|
|
@@ -10,19 +10,19 @@ import {
|
|
|
10
10
|
importLocal,
|
|
11
11
|
logRequestLine,
|
|
12
12
|
setConstructors
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-K7KD247K.js";
|
|
14
|
+
import "./chunk-U2PN6QZ2.js";
|
|
15
15
|
import "./chunk-NPH2SXRO.js";
|
|
16
16
|
import "./chunk-EJITPGUJ.js";
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-PEAIOYXD.js";
|
|
18
|
+
import "./chunk-EIUQV76I.js";
|
|
19
|
+
import "./chunk-UBCH575K.js";
|
|
20
20
|
import "./chunk-DL3MKXVR.js";
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-
|
|
21
|
+
import "./chunk-O3JOUAA5.js";
|
|
22
|
+
import "./chunk-N3YORLAS.js";
|
|
23
23
|
import "./chunk-FMKVOTQK.js";
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-3LDWVYMD.js";
|
|
25
|
+
import "./chunk-3GSKXZGY.js";
|
|
26
26
|
import "./chunk-PRJ3U7MR.js";
|
|
27
27
|
import {
|
|
28
28
|
AbortError,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
readFile,
|
|
32
32
|
renderSuccess,
|
|
33
33
|
source_default
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-T4Y7NDNJ.js";
|
|
35
35
|
import "./chunk-JIJ65JT6.js";
|
|
36
36
|
import "./chunk-UMWKJFTM.js";
|
|
37
37
|
import "./chunk-GEN4RXTD.js";
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
init_cjs_shims
|
|
55
55
|
} from "./chunk-ZPL24Y2D.js";
|
|
56
56
|
|
|
57
|
-
// ../../node_modules/.pnpm/@shopify+cli-hydrogen@8.
|
|
57
|
+
// ../../node_modules/.pnpm/@shopify+cli-hydrogen@8.4.0_@graphql-codegen+cli@5.0.2_react-dom@17.0.2_react@17.0.2/node_modules/@shopify/cli-hydrogen/dist/lib/mini-oxygen/workerd.js
|
|
58
58
|
init_cjs_shims();
|
|
59
59
|
import { createRequire } from "node:module";
|
|
60
60
|
async function startWorkerdServer({
|
|
@@ -191,4 +191,4 @@ async function startWorkerdServer({
|
|
|
191
191
|
export {
|
|
192
192
|
startWorkerdServer
|
|
193
193
|
};
|
|
194
|
-
//# sourceMappingURL=workerd-
|
|
194
|
+
//# sourceMappingURL=workerd-OLKE7G4X.js.map
|
package/oclif.manifest.json
CHANGED
|
@@ -2714,6 +2714,14 @@
|
|
|
2714
2714
|
"required": false,
|
|
2715
2715
|
"type": "option"
|
|
2716
2716
|
},
|
|
2717
|
+
"diff": {
|
|
2718
|
+
"allowNo": false,
|
|
2719
|
+
"description": "Applies the current files on top of Hydrogen's starter template in a temporary directory.",
|
|
2720
|
+
"hidden": true,
|
|
2721
|
+
"name": "diff",
|
|
2722
|
+
"required": false,
|
|
2723
|
+
"type": "boolean"
|
|
2724
|
+
},
|
|
2717
2725
|
"force-sfapi-version": {
|
|
2718
2726
|
"description": "Force generating Storefront API types for a specific version instead of using the one provided in Hydrogen. A token can also be provided with this format: `<version>:<token>`.",
|
|
2719
2727
|
"hasDynamicHelp": false,
|
|
@@ -3153,6 +3161,15 @@
|
|
|
3153
3161
|
"name": "env-branch",
|
|
3154
3162
|
"type": "option"
|
|
3155
3163
|
},
|
|
3164
|
+
"env-file": {
|
|
3165
|
+
"default": ".env",
|
|
3166
|
+
"description": "Path to an environment file to override existing environment variables. Defaults to the '.env' located in your project path `--path`.",
|
|
3167
|
+
"hasDynamicHelp": false,
|
|
3168
|
+
"multiple": false,
|
|
3169
|
+
"name": "env-file",
|
|
3170
|
+
"required": false,
|
|
3171
|
+
"type": "option"
|
|
3172
|
+
},
|
|
3156
3173
|
"host": {
|
|
3157
3174
|
"allowNo": false,
|
|
3158
3175
|
"description": "Expose the server to the local network",
|
|
@@ -3282,6 +3299,15 @@
|
|
|
3282
3299
|
"name": "env-branch",
|
|
3283
3300
|
"type": "option"
|
|
3284
3301
|
},
|
|
3302
|
+
"env-file": {
|
|
3303
|
+
"default": ".env",
|
|
3304
|
+
"description": "Path to an environment file to override existing environment variables. Defaults to the '.env' located in your project path `--path`.",
|
|
3305
|
+
"hasDynamicHelp": false,
|
|
3306
|
+
"multiple": false,
|
|
3307
|
+
"name": "env-file",
|
|
3308
|
+
"required": false,
|
|
3309
|
+
"type": "option"
|
|
3310
|
+
},
|
|
3285
3311
|
"force": {
|
|
3286
3312
|
"allowNo": false,
|
|
3287
3313
|
"char": "f",
|
|
@@ -3328,10 +3354,12 @@
|
|
|
3328
3354
|
"type": "option"
|
|
3329
3355
|
},
|
|
3330
3356
|
"env-file": {
|
|
3331
|
-
"
|
|
3357
|
+
"default": ".env",
|
|
3358
|
+
"description": "Path to an environment file to override existing environment variables. Defaults to the '.env' located in your project path `--path`.",
|
|
3332
3359
|
"hasDynamicHelp": false,
|
|
3333
3360
|
"multiple": false,
|
|
3334
3361
|
"name": "env-file",
|
|
3362
|
+
"required": false,
|
|
3335
3363
|
"type": "option"
|
|
3336
3364
|
},
|
|
3337
3365
|
"path": {
|
|
@@ -3857,6 +3885,15 @@
|
|
|
3857
3885
|
"name": "env-branch",
|
|
3858
3886
|
"type": "option"
|
|
3859
3887
|
},
|
|
3888
|
+
"env-file": {
|
|
3889
|
+
"default": ".env",
|
|
3890
|
+
"description": "Path to an environment file to override existing environment variables. Defaults to the '.env' located in your project path `--path`.",
|
|
3891
|
+
"hasDynamicHelp": false,
|
|
3892
|
+
"multiple": false,
|
|
3893
|
+
"name": "env-file",
|
|
3894
|
+
"required": false,
|
|
3895
|
+
"type": "option"
|
|
3896
|
+
},
|
|
3860
3897
|
"inspector-port": {
|
|
3861
3898
|
"description": "The port where the inspector is available. Defaults to 9229.",
|
|
3862
3899
|
"env": "SHOPIFY_HYDROGEN_FLAG_INSPECTOR_PORT",
|
|
@@ -6536,5 +6573,5 @@
|
|
|
6536
6573
|
"summary": "Trigger delivery of a sample webhook topic payload to a designated address."
|
|
6537
6574
|
}
|
|
6538
6575
|
},
|
|
6539
|
-
"version": "3.65.
|
|
6576
|
+
"version": "3.65.3"
|
|
6540
6577
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopify/cli",
|
|
3
|
-
"version": "3.65.
|
|
3
|
+
"version": "3.65.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A CLI tool to build for the Shopify platform",
|
|
6
6
|
"keywords": [
|
|
@@ -92,12 +92,12 @@
|
|
|
92
92
|
"@oclif/core": "3.26.5",
|
|
93
93
|
"@oclif/plugin-commands": "3.3.2",
|
|
94
94
|
"@oclif/plugin-plugins": "5.1.2",
|
|
95
|
-
"@shopify/app": "3.65.
|
|
96
|
-
"@shopify/cli-kit": "3.65.
|
|
97
|
-
"@shopify/plugin-cloudflare": "3.65.
|
|
98
|
-
"@shopify/plugin-did-you-mean": "3.65.
|
|
99
|
-
"@shopify/theme": "3.65.
|
|
100
|
-
"@shopify/cli-hydrogen": "8.
|
|
95
|
+
"@shopify/app": "3.65.3",
|
|
96
|
+
"@shopify/cli-kit": "3.65.3",
|
|
97
|
+
"@shopify/plugin-cloudflare": "3.65.3",
|
|
98
|
+
"@shopify/plugin-did-you-mean": "3.65.3",
|
|
99
|
+
"@shopify/theme": "3.65.3",
|
|
100
|
+
"@shopify/cli-hydrogen": "8.4.0",
|
|
101
101
|
"@types/node": "18.19.3",
|
|
102
102
|
"@vitest/coverage-istanbul": "^1.6.0",
|
|
103
103
|
"esbuild-plugin-copy": "^2.1.1",
|
|
@@ -1,514 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Link,
|
|
3
|
-
Form,
|
|
4
|
-
useParams,
|
|
5
|
-
useFetcher,
|
|
6
|
-
type FormProps,
|
|
7
|
-
} from '@remix-run/react';
|
|
8
|
-
import {Image, Money, Pagination} from '@shopify/hydrogen';
|
|
9
|
-
import React, {useRef, useEffect} from 'react';
|
|
10
|
-
import {applyTrackingParams} from '~/lib/search';
|
|
11
|
-
|
|
12
|
-
import type {
|
|
13
|
-
PredictiveProductFragment,
|
|
14
|
-
PredictiveCollectionFragment,
|
|
15
|
-
PredictiveArticleFragment,
|
|
16
|
-
SearchQuery,
|
|
17
|
-
} from 'storefrontapi.generated';
|
|
18
|
-
|
|
19
|
-
import type {PredictiveSearchAPILoader} from '../routes/api.predictive-search';
|
|
20
|
-
|
|
21
|
-
type PredicticeSearchResultItemImage =
|
|
22
|
-
| PredictiveCollectionFragment['image']
|
|
23
|
-
| PredictiveArticleFragment['image']
|
|
24
|
-
| PredictiveProductFragment['variants']['nodes'][0]['image'];
|
|
25
|
-
|
|
26
|
-
type PredictiveSearchResultItemPrice =
|
|
27
|
-
| PredictiveProductFragment['variants']['nodes'][0]['price'];
|
|
28
|
-
|
|
29
|
-
export type NormalizedPredictiveSearchResultItem = {
|
|
30
|
-
__typename: string | undefined;
|
|
31
|
-
handle: string;
|
|
32
|
-
id: string;
|
|
33
|
-
image?: PredicticeSearchResultItemImage;
|
|
34
|
-
price?: PredictiveSearchResultItemPrice;
|
|
35
|
-
styledTitle?: string;
|
|
36
|
-
title: string;
|
|
37
|
-
url: string;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export type NormalizedPredictiveSearchResults = Array<
|
|
41
|
-
| {type: 'queries'; items: Array<NormalizedPredictiveSearchResultItem>}
|
|
42
|
-
| {type: 'products'; items: Array<NormalizedPredictiveSearchResultItem>}
|
|
43
|
-
| {type: 'collections'; items: Array<NormalizedPredictiveSearchResultItem>}
|
|
44
|
-
| {type: 'pages'; items: Array<NormalizedPredictiveSearchResultItem>}
|
|
45
|
-
| {type: 'articles'; items: Array<NormalizedPredictiveSearchResultItem>}
|
|
46
|
-
>;
|
|
47
|
-
|
|
48
|
-
export type NormalizedPredictiveSearch = {
|
|
49
|
-
results: NormalizedPredictiveSearchResults;
|
|
50
|
-
totalResults: number;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
type FetchSearchResultsReturn = {
|
|
54
|
-
searchResults: {
|
|
55
|
-
results: SearchQuery | null;
|
|
56
|
-
totalResults: number;
|
|
57
|
-
};
|
|
58
|
-
searchTerm: string;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const NO_PREDICTIVE_SEARCH_RESULTS: NormalizedPredictiveSearchResults = [
|
|
62
|
-
{type: 'queries', items: []},
|
|
63
|
-
{type: 'products', items: []},
|
|
64
|
-
{type: 'collections', items: []},
|
|
65
|
-
{type: 'pages', items: []},
|
|
66
|
-
{type: 'articles', items: []},
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
export function SearchForm({searchTerm}: {searchTerm: string}) {
|
|
70
|
-
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
71
|
-
|
|
72
|
-
// focus the input when cmd+k is pressed
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
function handleKeyDown(event: KeyboardEvent) {
|
|
75
|
-
if (event.key === 'k' && event.metaKey) {
|
|
76
|
-
event.preventDefault();
|
|
77
|
-
inputRef.current?.focus();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (event.key === 'Escape') {
|
|
81
|
-
inputRef.current?.blur();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
86
|
-
|
|
87
|
-
return () => {
|
|
88
|
-
document.removeEventListener('keydown', handleKeyDown);
|
|
89
|
-
};
|
|
90
|
-
}, []);
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<Form method="get">
|
|
94
|
-
<input
|
|
95
|
-
defaultValue={searchTerm}
|
|
96
|
-
name="q"
|
|
97
|
-
placeholder="Search…"
|
|
98
|
-
ref={inputRef}
|
|
99
|
-
type="search"
|
|
100
|
-
/>
|
|
101
|
-
|
|
102
|
-
<button type="submit">Search</button>
|
|
103
|
-
</Form>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function SearchResults({
|
|
108
|
-
results,
|
|
109
|
-
searchTerm,
|
|
110
|
-
}: Pick<FetchSearchResultsReturn['searchResults'], 'results'> & {
|
|
111
|
-
searchTerm: string;
|
|
112
|
-
}) {
|
|
113
|
-
if (!results) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
const keys = Object.keys(results) as Array<keyof typeof results>;
|
|
117
|
-
return (
|
|
118
|
-
<div>
|
|
119
|
-
{results &&
|
|
120
|
-
keys.map((type) => {
|
|
121
|
-
const resourceResults = results[type];
|
|
122
|
-
|
|
123
|
-
if (resourceResults.nodes[0]?.__typename === 'Page') {
|
|
124
|
-
const pageResults = resourceResults as SearchQuery['pages'];
|
|
125
|
-
return resourceResults.nodes.length ? (
|
|
126
|
-
<SearchResultPageGrid key="pages" pages={pageResults} />
|
|
127
|
-
) : null;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (resourceResults.nodes[0]?.__typename === 'Product') {
|
|
131
|
-
const productResults = resourceResults as SearchQuery['products'];
|
|
132
|
-
return resourceResults.nodes.length ? (
|
|
133
|
-
<SearchResultsProductsGrid
|
|
134
|
-
key="products"
|
|
135
|
-
products={productResults}
|
|
136
|
-
searchTerm={searchTerm}
|
|
137
|
-
/>
|
|
138
|
-
) : null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (resourceResults.nodes[0]?.__typename === 'Article') {
|
|
142
|
-
const articleResults = resourceResults as SearchQuery['articles'];
|
|
143
|
-
return resourceResults.nodes.length ? (
|
|
144
|
-
<SearchResultArticleGrid
|
|
145
|
-
key="articles"
|
|
146
|
-
articles={articleResults}
|
|
147
|
-
/>
|
|
148
|
-
) : null;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return null;
|
|
152
|
-
})}
|
|
153
|
-
</div>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function SearchResultsProductsGrid({
|
|
158
|
-
products,
|
|
159
|
-
searchTerm,
|
|
160
|
-
}: Pick<SearchQuery, 'products'> & {searchTerm: string}) {
|
|
161
|
-
return (
|
|
162
|
-
<div className="search-result">
|
|
163
|
-
<h2>Products</h2>
|
|
164
|
-
<Pagination connection={products}>
|
|
165
|
-
{({nodes, isLoading, NextLink, PreviousLink}) => {
|
|
166
|
-
const ItemsMarkup = nodes.map((product) => {
|
|
167
|
-
const trackingParams = applyTrackingParams(
|
|
168
|
-
product,
|
|
169
|
-
`q=${encodeURIComponent(searchTerm)}`,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
return (
|
|
173
|
-
<div className="search-results-item" key={product.id}>
|
|
174
|
-
<Link
|
|
175
|
-
prefetch="intent"
|
|
176
|
-
to={`/products/${product.handle}${trackingParams}`}
|
|
177
|
-
>
|
|
178
|
-
{product.variants.nodes[0].image && (
|
|
179
|
-
<Image
|
|
180
|
-
data={product.variants.nodes[0].image}
|
|
181
|
-
alt={product.title}
|
|
182
|
-
width={50}
|
|
183
|
-
/>
|
|
184
|
-
)}
|
|
185
|
-
<div>
|
|
186
|
-
<p>{product.title}</p>
|
|
187
|
-
<small>
|
|
188
|
-
<Money data={product.variants.nodes[0].price} />
|
|
189
|
-
</small>
|
|
190
|
-
</div>
|
|
191
|
-
</Link>
|
|
192
|
-
</div>
|
|
193
|
-
);
|
|
194
|
-
});
|
|
195
|
-
return (
|
|
196
|
-
<div>
|
|
197
|
-
<div>
|
|
198
|
-
<PreviousLink>
|
|
199
|
-
{isLoading ? 'Loading...' : <span>↑ Load previous</span>}
|
|
200
|
-
</PreviousLink>
|
|
201
|
-
</div>
|
|
202
|
-
<div>
|
|
203
|
-
{ItemsMarkup}
|
|
204
|
-
<br />
|
|
205
|
-
</div>
|
|
206
|
-
<div>
|
|
207
|
-
<NextLink>
|
|
208
|
-
{isLoading ? 'Loading...' : <span>Load more ↓</span>}
|
|
209
|
-
</NextLink>
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
212
|
-
);
|
|
213
|
-
}}
|
|
214
|
-
</Pagination>
|
|
215
|
-
<br />
|
|
216
|
-
</div>
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function SearchResultPageGrid({pages}: Pick<SearchQuery, 'pages'>) {
|
|
221
|
-
return (
|
|
222
|
-
<div className="search-result">
|
|
223
|
-
<h2>Pages</h2>
|
|
224
|
-
<div>
|
|
225
|
-
{pages?.nodes?.map((page) => (
|
|
226
|
-
<div className="search-results-item" key={page.id}>
|
|
227
|
-
<Link prefetch="intent" to={`/pages/${page.handle}`}>
|
|
228
|
-
{page.title}
|
|
229
|
-
</Link>
|
|
230
|
-
</div>
|
|
231
|
-
))}
|
|
232
|
-
</div>
|
|
233
|
-
<br />
|
|
234
|
-
</div>
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function SearchResultArticleGrid({articles}: Pick<SearchQuery, 'articles'>) {
|
|
239
|
-
return (
|
|
240
|
-
<div className="search-result">
|
|
241
|
-
<h2>Articles</h2>
|
|
242
|
-
<div>
|
|
243
|
-
{articles?.nodes?.map((article) => (
|
|
244
|
-
<div className="search-results-item" key={article.id}>
|
|
245
|
-
<Link prefetch="intent" to={`/blogs/${article.handle}`}>
|
|
246
|
-
{article.title}
|
|
247
|
-
</Link>
|
|
248
|
-
</div>
|
|
249
|
-
))}
|
|
250
|
-
</div>
|
|
251
|
-
<br />
|
|
252
|
-
</div>
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export function NoSearchResults() {
|
|
257
|
-
return <p>No results, try a different search.</p>;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
type ChildrenRenderProps = {
|
|
261
|
-
fetchResults: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
262
|
-
fetcher: ReturnType<typeof useFetcher<PredictiveSearchAPILoader>>;
|
|
263
|
-
inputRef: React.MutableRefObject<HTMLInputElement | null>;
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
type SearchFromProps = {
|
|
267
|
-
action?: FormProps['action'];
|
|
268
|
-
className?: string;
|
|
269
|
-
children: (passedProps: ChildrenRenderProps) => React.ReactNode;
|
|
270
|
-
[key: string]: unknown;
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Search form component that sends search requests to the `/search` route
|
|
275
|
-
**/
|
|
276
|
-
export function PredictiveSearchForm({
|
|
277
|
-
action,
|
|
278
|
-
children,
|
|
279
|
-
className = 'predictive-search-form',
|
|
280
|
-
...props
|
|
281
|
-
}: SearchFromProps) {
|
|
282
|
-
const params = useParams();
|
|
283
|
-
const fetcher = useFetcher<PredictiveSearchAPILoader>({
|
|
284
|
-
key: 'search',
|
|
285
|
-
});
|
|
286
|
-
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
287
|
-
|
|
288
|
-
function fetchResults(event: React.ChangeEvent<HTMLInputElement>) {
|
|
289
|
-
const searchAction = action ?? '/api/predictive-search';
|
|
290
|
-
const newSearchTerm = event.target.value || '';
|
|
291
|
-
const localizedAction = params.locale
|
|
292
|
-
? `/${params.locale}${searchAction}`
|
|
293
|
-
: searchAction;
|
|
294
|
-
|
|
295
|
-
fetcher.submit(
|
|
296
|
-
{q: newSearchTerm, limit: '6'},
|
|
297
|
-
{method: 'GET', action: localizedAction},
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// ensure the passed input has a type of search, because SearchResults
|
|
302
|
-
// will select the element based on the input
|
|
303
|
-
useEffect(() => {
|
|
304
|
-
inputRef?.current?.setAttribute('type', 'search');
|
|
305
|
-
}, []);
|
|
306
|
-
|
|
307
|
-
return (
|
|
308
|
-
<fetcher.Form
|
|
309
|
-
{...props}
|
|
310
|
-
className={className}
|
|
311
|
-
onSubmit={(event) => {
|
|
312
|
-
event.preventDefault();
|
|
313
|
-
event.stopPropagation();
|
|
314
|
-
if (!inputRef?.current || inputRef.current.value === '') {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
inputRef.current.blur();
|
|
318
|
-
}}
|
|
319
|
-
>
|
|
320
|
-
{children({fetchResults, inputRef, fetcher})}
|
|
321
|
-
</fetcher.Form>
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export function PredictiveSearchResults() {
|
|
326
|
-
const {results, totalResults, searchInputRef, searchTerm, state} =
|
|
327
|
-
usePredictiveSearch();
|
|
328
|
-
|
|
329
|
-
function goToSearchResult(event: React.MouseEvent<HTMLAnchorElement>) {
|
|
330
|
-
if (!searchInputRef.current) return;
|
|
331
|
-
searchInputRef.current.blur();
|
|
332
|
-
searchInputRef.current.value = '';
|
|
333
|
-
// close the aside
|
|
334
|
-
window.location.href = event.currentTarget.href;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (state === 'loading') {
|
|
338
|
-
return <div>Loading...</div>;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (!totalResults) {
|
|
342
|
-
return <NoPredictiveSearchResults searchTerm={searchTerm} />;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return (
|
|
346
|
-
<div className="predictive-search-results">
|
|
347
|
-
<div>
|
|
348
|
-
{results.map(({type, items}) => (
|
|
349
|
-
<PredictiveSearchResult
|
|
350
|
-
goToSearchResult={goToSearchResult}
|
|
351
|
-
items={items}
|
|
352
|
-
key={type}
|
|
353
|
-
searchTerm={searchTerm}
|
|
354
|
-
type={type}
|
|
355
|
-
/>
|
|
356
|
-
))}
|
|
357
|
-
</div>
|
|
358
|
-
{searchTerm.current && (
|
|
359
|
-
<Link onClick={goToSearchResult} to={`/search?q=${searchTerm.current}`}>
|
|
360
|
-
<p>
|
|
361
|
-
View all results for <q>{searchTerm.current}</q>
|
|
362
|
-
→
|
|
363
|
-
</p>
|
|
364
|
-
</Link>
|
|
365
|
-
)}
|
|
366
|
-
</div>
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
function NoPredictiveSearchResults({
|
|
371
|
-
searchTerm,
|
|
372
|
-
}: {
|
|
373
|
-
searchTerm: React.MutableRefObject<string>;
|
|
374
|
-
}) {
|
|
375
|
-
if (!searchTerm.current) {
|
|
376
|
-
return null;
|
|
377
|
-
}
|
|
378
|
-
return (
|
|
379
|
-
<p>
|
|
380
|
-
No results found for <q>{searchTerm.current}</q>
|
|
381
|
-
</p>
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
type SearchResultTypeProps = {
|
|
386
|
-
goToSearchResult: (event: React.MouseEvent<HTMLAnchorElement>) => void;
|
|
387
|
-
items: NormalizedPredictiveSearchResultItem[];
|
|
388
|
-
searchTerm: UseSearchReturn['searchTerm'];
|
|
389
|
-
type: NormalizedPredictiveSearchResults[number]['type'];
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
function PredictiveSearchResult({
|
|
393
|
-
goToSearchResult,
|
|
394
|
-
items,
|
|
395
|
-
searchTerm,
|
|
396
|
-
type,
|
|
397
|
-
}: SearchResultTypeProps) {
|
|
398
|
-
const isSuggestions = type === 'queries';
|
|
399
|
-
const categoryUrl = `/search?q=${
|
|
400
|
-
searchTerm.current
|
|
401
|
-
}&type=${pluralToSingularSearchType(type)}`;
|
|
402
|
-
|
|
403
|
-
return (
|
|
404
|
-
<div className="predictive-search-result" key={type}>
|
|
405
|
-
<Link prefetch="intent" to={categoryUrl} onClick={goToSearchResult}>
|
|
406
|
-
<h5>{isSuggestions ? 'Suggestions' : type}</h5>
|
|
407
|
-
</Link>
|
|
408
|
-
<ul>
|
|
409
|
-
{items.map((item: NormalizedPredictiveSearchResultItem) => (
|
|
410
|
-
<SearchResultItem
|
|
411
|
-
goToSearchResult={goToSearchResult}
|
|
412
|
-
item={item}
|
|
413
|
-
key={item.id}
|
|
414
|
-
/>
|
|
415
|
-
))}
|
|
416
|
-
</ul>
|
|
417
|
-
</div>
|
|
418
|
-
);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
type SearchResultItemProps = Pick<SearchResultTypeProps, 'goToSearchResult'> & {
|
|
422
|
-
item: NormalizedPredictiveSearchResultItem;
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
function SearchResultItem({goToSearchResult, item}: SearchResultItemProps) {
|
|
426
|
-
return (
|
|
427
|
-
<li className="predictive-search-result-item" key={item.id}>
|
|
428
|
-
<Link onClick={goToSearchResult} to={item.url}>
|
|
429
|
-
{item.image?.url && (
|
|
430
|
-
<Image
|
|
431
|
-
alt={item.image.altText ?? ''}
|
|
432
|
-
src={item.image.url}
|
|
433
|
-
width={50}
|
|
434
|
-
height={50}
|
|
435
|
-
/>
|
|
436
|
-
)}
|
|
437
|
-
<div>
|
|
438
|
-
{item.styledTitle ? (
|
|
439
|
-
<div
|
|
440
|
-
dangerouslySetInnerHTML={{
|
|
441
|
-
__html: item.styledTitle,
|
|
442
|
-
}}
|
|
443
|
-
/>
|
|
444
|
-
) : (
|
|
445
|
-
<span>{item.title}</span>
|
|
446
|
-
)}
|
|
447
|
-
{item?.price && (
|
|
448
|
-
<small>
|
|
449
|
-
<Money data={item.price} />
|
|
450
|
-
</small>
|
|
451
|
-
)}
|
|
452
|
-
</div>
|
|
453
|
-
</Link>
|
|
454
|
-
</li>
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
type UseSearchReturn = NormalizedPredictiveSearch & {
|
|
459
|
-
searchInputRef: React.MutableRefObject<HTMLInputElement | null>;
|
|
460
|
-
searchTerm: React.MutableRefObject<string>;
|
|
461
|
-
state: ReturnType<typeof useFetcher<PredictiveSearchAPILoader>>['state'];
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
function usePredictiveSearch(): UseSearchReturn {
|
|
465
|
-
const searchFetcher = useFetcher<FetchSearchResultsReturn>({key: 'search'});
|
|
466
|
-
const searchTerm = useRef<string>('');
|
|
467
|
-
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
|
468
|
-
|
|
469
|
-
if (searchFetcher?.state === 'loading') {
|
|
470
|
-
searchTerm.current = (searchFetcher.formData?.get('q') || '') as string;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const search = (searchFetcher?.data?.searchResults || {
|
|
474
|
-
results: NO_PREDICTIVE_SEARCH_RESULTS,
|
|
475
|
-
totalResults: 0,
|
|
476
|
-
}) as NormalizedPredictiveSearch;
|
|
477
|
-
|
|
478
|
-
// capture the search input element as a ref
|
|
479
|
-
useEffect(() => {
|
|
480
|
-
if (searchInputRef.current) return;
|
|
481
|
-
searchInputRef.current = document.querySelector('input[type="search"]');
|
|
482
|
-
}, []);
|
|
483
|
-
|
|
484
|
-
return {...search, searchInputRef, searchTerm, state: searchFetcher.state};
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Converts a plural search type to a singular search type
|
|
489
|
-
*
|
|
490
|
-
* @example
|
|
491
|
-
* ```js
|
|
492
|
-
* pluralToSingularSearchType('articles'); // => 'ARTICLE'
|
|
493
|
-
* pluralToSingularSearchType(['articles', 'products']); // => 'ARTICLE,PRODUCT'
|
|
494
|
-
* ```
|
|
495
|
-
*/
|
|
496
|
-
function pluralToSingularSearchType(
|
|
497
|
-
type:
|
|
498
|
-
| NormalizedPredictiveSearchResults[number]['type']
|
|
499
|
-
| Array<NormalizedPredictiveSearchResults[number]['type']>,
|
|
500
|
-
) {
|
|
501
|
-
const plural = {
|
|
502
|
-
articles: 'ARTICLE',
|
|
503
|
-
collections: 'COLLECTION',
|
|
504
|
-
pages: 'PAGE',
|
|
505
|
-
products: 'PRODUCT',
|
|
506
|
-
queries: 'QUERY',
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
if (typeof type === 'string') {
|
|
510
|
-
return plural[type];
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return type.map((t) => plural[t]).join(',');
|
|
514
|
-
}
|