@shopify/create-hydrogen 0.2.0 → 0.16.0
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/CHANGELOG.md +13 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/commands/init.js +76397 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -14
- package/templates/template-hydrogen/.devcontainer/devcontainer.json +18 -0
- package/templates/template-hydrogen/.eslintrc.js +3 -0
- package/templates/template-hydrogen/.gitignore +7 -0
- package/templates/template-hydrogen/.stackblitzrc +4 -0
- package/{template-hydrogen → templates/template-hydrogen}/.stylelintrc.js +1 -1
- package/templates/template-hydrogen/.vscode/extensions.json +8 -0
- package/templates/template-hydrogen/Dockerfile +15 -0
- package/{template-hydrogen → templates/template-hydrogen}/README.md +11 -2
- package/{template-hydrogen → templates/template-hydrogen}/_gitignore +0 -0
- package/templates/template-hydrogen/index.html +20 -0
- package/{template-hydrogen → templates/template-hydrogen}/package.json +20 -16
- package/{template-hydrogen → templates/template-hydrogen}/postcss.config.js +0 -0
- package/{template-hydrogen/src → templates/template-hydrogen/public}/favicon.svg +0 -0
- package/{template-hydrogen → templates/template-hydrogen}/server.js +4 -2
- package/{template-hydrogen → templates/template-hydrogen}/shopify.config.js +1 -2
- package/templates/template-hydrogen/src/App.client.jsx +8 -0
- package/templates/template-hydrogen/src/App.server.jsx +27 -0
- package/templates/template-hydrogen/src/components/Button.client.jsx +65 -0
- package/templates/template-hydrogen/src/components/Cart.client.jsx +265 -0
- package/templates/template-hydrogen/src/components/CartIcon.jsx +33 -0
- package/templates/template-hydrogen/src/components/CartIconWithItems.client.jsx +28 -0
- package/templates/template-hydrogen/src/components/CartProvider.client.jsx +35 -0
- package/templates/template-hydrogen/src/components/CartToggle.client.jsx +30 -0
- package/{template-hydrogen → templates/template-hydrogen}/src/components/CartUIProvider.client.jsx +3 -0
- package/templates/template-hydrogen/src/components/CountrySelector.client.jsx +116 -0
- package/templates/template-hydrogen/src/components/DefaultSeo.server.jsx +35 -0
- package/templates/template-hydrogen/src/components/FeaturedCollection.jsx +26 -0
- package/templates/template-hydrogen/src/components/Footer.server.jsx +103 -0
- package/templates/template-hydrogen/src/components/Gallery.client.jsx +65 -0
- package/templates/template-hydrogen/src/components/Header.client.jsx +62 -0
- package/templates/template-hydrogen/src/components/Layout.server.jsx +87 -0
- package/templates/template-hydrogen/src/components/LoadMoreProducts.client.jsx +56 -0
- package/templates/template-hydrogen/src/components/LoadingFallback.jsx +26 -0
- package/templates/template-hydrogen/src/components/MobileCountrySelector.client.jsx +64 -0
- package/templates/template-hydrogen/src/components/MobileNavigation.client.jsx +98 -0
- package/templates/template-hydrogen/src/components/MoneyCompareAtPrice.client.jsx +14 -0
- package/templates/template-hydrogen/src/components/MoneyPrice.client.jsx +15 -0
- package/templates/template-hydrogen/src/components/Navigation.client.jsx +23 -0
- package/templates/template-hydrogen/src/components/NotFound.server.jsx +93 -0
- package/templates/template-hydrogen/src/components/OpenIcon.jsx +33 -0
- package/templates/template-hydrogen/src/components/ProductCard.jsx +50 -0
- package/templates/template-hydrogen/src/components/ProductDetails.client.jsx +242 -0
- package/{template-hydrogen → templates/template-hydrogen}/src/components/ProductOptions.client.jsx +7 -4
- package/templates/template-hydrogen/src/components/Welcome.server.jsx +188 -0
- package/templates/template-hydrogen/src/entry-client.jsx +7 -0
- package/templates/template-hydrogen/src/entry-server.jsx +6 -0
- package/{template-hydrogen → templates/template-hydrogen}/src/index.css +31 -0
- package/templates/template-hydrogen/src/pages/collections/[handle].server.jsx +105 -0
- package/templates/template-hydrogen/src/pages/index.server.jsx +237 -0
- package/{template-hydrogen → templates/template-hydrogen}/src/pages/pages/[handle].server.jsx +14 -5
- package/templates/template-hydrogen/src/pages/products/[handle].server.jsx +69 -0
- package/templates/template-hydrogen/src/pages/redirect.server.jsx +4 -0
- package/templates/template-hydrogen/src/pages/robots.txt.server.js +10 -0
- package/{template-hydrogen → templates/template-hydrogen}/src/pages/sitemap.xml.server.jsx +2 -2
- package/templates/template-hydrogen/tailwind.config.js +26 -0
- package/{template-hydrogen → templates/template-hydrogen}/vite.config.js +1 -0
- package/{template-hydrogen → templates/template-hydrogen}/worker.js +1 -0
- package/README.md +0 -3
- package/index.js +0 -205
- package/scripts/tmp-copy-template-from-dev.js +0 -21
- package/scripts/utils.js +0 -29
- package/template-hydrogen/.eslintrc.js +0 -41
- package/template-hydrogen/.vscode/extensions.json +0 -3
- package/template-hydrogen/Dockerfile +0 -15
- package/template-hydrogen/index.html +0 -14
- package/template-hydrogen/src/App.server.jsx +0 -42
- package/template-hydrogen/src/components/Cart.client.jsx +0 -296
- package/template-hydrogen/src/components/CartProvider.client.jsx +0 -40
- package/template-hydrogen/src/components/DefaultSeo.server.jsx +0 -22
- package/template-hydrogen/src/components/Footer.jsx +0 -12
- package/template-hydrogen/src/components/Gallery.client.jsx +0 -36
- package/template-hydrogen/src/components/Header.client.jsx +0 -103
- package/template-hydrogen/src/components/HightlightedProduct.client.jsx +0 -54
- package/template-hydrogen/src/components/Layout.client.jsx +0 -41
- package/template-hydrogen/src/components/MediaPlaceholder.jsx +0 -21
- package/template-hydrogen/src/components/NotFound.server.jsx +0 -104
- package/template-hydrogen/src/components/ProductCard.client.jsx +0 -39
- package/template-hydrogen/src/components/ProductDetails.client.jsx +0 -184
- package/template-hydrogen/src/components/Seo.client.jsx +0 -75
- package/template-hydrogen/src/entry-client.jsx +0 -12
- package/template-hydrogen/src/entry-server.jsx +0 -7
- package/template-hydrogen/src/pages/Index.server.jsx +0 -199
- package/template-hydrogen/src/pages/blogs/[handle]/[articleHandle].server.jsx +0 -49
- package/template-hydrogen/src/pages/blogs/[handle].server.jsx +0 -76
- package/template-hydrogen/src/pages/collections/[handle].server.jsx +0 -69
- package/template-hydrogen/src/pages/products/[handle].server.jsx +0 -62
- package/template-hydrogen/src/pages/search.server.jsx +0 -107
- package/template-hydrogen/tailwind.config.js +0 -9
package/index.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-check
|
|
3
|
-
|
|
4
|
-
// Inspired by and borrowed from https://github.com/vitejs/vite/blob/main/packages/create-app/index.js
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const argv = require('minimist')(process.argv.slice(2));
|
|
9
|
-
const {prompt} = require('enquirer');
|
|
10
|
-
const {yellow} = require('kolorist');
|
|
11
|
-
const {copy} = require('./scripts/utils.js');
|
|
12
|
-
|
|
13
|
-
const cwd = process.cwd();
|
|
14
|
-
|
|
15
|
-
const TEMPLATES = ['hydrogen'];
|
|
16
|
-
|
|
17
|
-
const renameFiles = {
|
|
18
|
-
_gitignore: '.gitignore',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
console.warn(
|
|
22
|
-
'DEPRECATED: Please use `npm init hydrogen-app@latest` or `yarn create hydrogen-app` instead.'
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
async function init() {
|
|
26
|
-
let targetDir = argv._[0];
|
|
27
|
-
if (!targetDir) {
|
|
28
|
-
/**
|
|
29
|
-
* @type {{ projectName: string }}
|
|
30
|
-
*/
|
|
31
|
-
const {projectName} = await prompt({
|
|
32
|
-
type: 'input',
|
|
33
|
-
name: 'projectName',
|
|
34
|
-
message: `Project name:`,
|
|
35
|
-
initial: 'hydrogen-app',
|
|
36
|
-
});
|
|
37
|
-
targetDir = projectName;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const packageName = await getValidPackageName(targetDir);
|
|
41
|
-
const root = path.join(cwd, targetDir);
|
|
42
|
-
console.log(`\nScaffolding Hydrogen app in ${root}...`);
|
|
43
|
-
|
|
44
|
-
if (!fs.existsSync(root)) {
|
|
45
|
-
fs.mkdirSync(root, {recursive: true});
|
|
46
|
-
} else {
|
|
47
|
-
const existing = fs.readdirSync(root);
|
|
48
|
-
if (existing.length) {
|
|
49
|
-
/**
|
|
50
|
-
* @type {{ yes: boolean }}
|
|
51
|
-
*/
|
|
52
|
-
const {yes} = await prompt({
|
|
53
|
-
type: 'confirm',
|
|
54
|
-
name: 'yes',
|
|
55
|
-
initial: 'Y',
|
|
56
|
-
message:
|
|
57
|
-
`Target directory ${targetDir} is not empty.\n` +
|
|
58
|
-
`Remove existing files and continue?`,
|
|
59
|
-
});
|
|
60
|
-
if (yes) {
|
|
61
|
-
emptyDir(root);
|
|
62
|
-
} else {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Determine template
|
|
69
|
-
let template = argv.t || argv.template;
|
|
70
|
-
let message = 'Select a template:';
|
|
71
|
-
let isValidTemplate = false;
|
|
72
|
-
|
|
73
|
-
// --template expects a value
|
|
74
|
-
if (typeof template === 'string') {
|
|
75
|
-
isValidTemplate = TEMPLATES.includes(template);
|
|
76
|
-
message = `${template} isn't a valid template. Please choose from below:`;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!template || !isValidTemplate) {
|
|
80
|
-
/**
|
|
81
|
-
* @type {{ t: string }}
|
|
82
|
-
*/
|
|
83
|
-
const {t} = await prompt({
|
|
84
|
-
type: 'select',
|
|
85
|
-
name: 't',
|
|
86
|
-
message,
|
|
87
|
-
choices: TEMPLATES,
|
|
88
|
-
});
|
|
89
|
-
template = t;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const templateDir = path.join(__dirname, `template-${template}`);
|
|
93
|
-
|
|
94
|
-
const write = (file, content) => {
|
|
95
|
-
const targetPath = renameFiles[file]
|
|
96
|
-
? path.join(root, renameFiles[file])
|
|
97
|
-
: path.join(root, file);
|
|
98
|
-
if (content) {
|
|
99
|
-
fs.writeFileSync(targetPath, content);
|
|
100
|
-
} else {
|
|
101
|
-
copy(path.join(templateDir, file), targetPath);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const files = fs.readdirSync(templateDir);
|
|
106
|
-
const skipFiles = ['package.json', 'node_modules', 'dist'];
|
|
107
|
-
for (const file of files.filter((f) => !skipFiles.includes(f))) {
|
|
108
|
-
write(file);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const pkg = require(path.join(templateDir, `package.json`));
|
|
112
|
-
|
|
113
|
-
pkg.name = packageName;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* When the user is running a LOCAL version of hydrogen external from the
|
|
117
|
-
* monorepo, they expect to use the local version of the library instead
|
|
118
|
-
* of the registry version. We need to use a file reference here because
|
|
119
|
-
* yarn fails to link scoped packages.
|
|
120
|
-
**/
|
|
121
|
-
|
|
122
|
-
if (process.env.LOCAL) {
|
|
123
|
-
pkg.dependencies['@shopify/hydrogen'] =
|
|
124
|
-
'file:../../Shopify/hydrogen/packages/hydrogen';
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Rewrite the build:client task to strip out the custom HYDROGEN_PATH
|
|
129
|
-
* we add for use in the monorepo.
|
|
130
|
-
*/
|
|
131
|
-
pkg.scripts['build:client'] =
|
|
132
|
-
pkg.scripts['build:client'].match(/(vite .+)$/)[0];
|
|
133
|
-
|
|
134
|
-
write('package.json', JSON.stringify(pkg, null, 2));
|
|
135
|
-
|
|
136
|
-
const pkgManager = /yarn/.test(process.env.npm_execpath) ? 'yarn' : 'npm';
|
|
137
|
-
|
|
138
|
-
console.log(`\nDone. Now:\n`);
|
|
139
|
-
console.log(
|
|
140
|
-
` Update ${yellow(
|
|
141
|
-
packageName + '/shopify.config.js'
|
|
142
|
-
)} with the values for your storefront.`
|
|
143
|
-
);
|
|
144
|
-
console.log(`\nand then run:\n`);
|
|
145
|
-
if (root !== cwd) {
|
|
146
|
-
console.log(` cd ${path.relative(cwd, root)}`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* The LOCAL option only works with Yarn due to issues with NPM
|
|
151
|
-
* and symlinking yarn monorepos.
|
|
152
|
-
*/
|
|
153
|
-
const usesYarn = pkgManager === 'yarn' || process.env.LOCAL;
|
|
154
|
-
|
|
155
|
-
console.log(` ${usesYarn ? `yarn` : `npm install`}`);
|
|
156
|
-
console.log(` ${usesYarn ? `yarn dev` : `npm run dev`}`);
|
|
157
|
-
console.log();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async function getValidPackageName(projectName) {
|
|
161
|
-
const packageNameRegExp =
|
|
162
|
-
/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
163
|
-
if (packageNameRegExp.test(projectName)) {
|
|
164
|
-
return projectName;
|
|
165
|
-
} else {
|
|
166
|
-
const suggestedPackageName = projectName
|
|
167
|
-
.trim()
|
|
168
|
-
.toLowerCase()
|
|
169
|
-
.replace(/\s+/g, '-')
|
|
170
|
-
.replace(/^[._]/, '')
|
|
171
|
-
.replace(/[^a-z0-9-~]+/g, '-');
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* @type {{ inputPackageName: string }}
|
|
175
|
-
*/
|
|
176
|
-
const {inputPackageName} = await prompt({
|
|
177
|
-
type: 'input',
|
|
178
|
-
name: 'inputPackageName',
|
|
179
|
-
message: `Package name:`,
|
|
180
|
-
initial: suggestedPackageName,
|
|
181
|
-
validate: (input) =>
|
|
182
|
-
packageNameRegExp.test(input) ? true : 'Invalid package.json name',
|
|
183
|
-
});
|
|
184
|
-
return inputPackageName;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function emptyDir(dir) {
|
|
189
|
-
if (!fs.existsSync(dir)) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
for (const file of fs.readdirSync(dir)) {
|
|
193
|
-
const abs = path.resolve(dir, file);
|
|
194
|
-
if (fs.lstatSync(abs).isDirectory()) {
|
|
195
|
-
emptyDir(abs);
|
|
196
|
-
fs.rmdirSync(abs);
|
|
197
|
-
} else {
|
|
198
|
-
fs.unlinkSync(abs);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
init().catch((e) => {
|
|
204
|
-
console.error(e);
|
|
205
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This is a temporary script meant to copy `packages/dev` to `./template-hydrogen`
|
|
5
|
-
* while we are actively developing H2 in `dev`. Eventually, the template will just
|
|
6
|
-
* live in this folder.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const {copy} = require('./utils');
|
|
12
|
-
|
|
13
|
-
const devPath = path.resolve(__dirname, '..', '..', 'dev');
|
|
14
|
-
const templatePath = path.resolve(__dirname, '..', './template-hydrogen');
|
|
15
|
-
const skipFiles = ['node_modules', 'dist'];
|
|
16
|
-
|
|
17
|
-
// Remove the symlink and replace it with a folder
|
|
18
|
-
fs.unlinkSync(templatePath);
|
|
19
|
-
fs.mkdirSync(templatePath, {recursive: true});
|
|
20
|
-
|
|
21
|
-
copy(devPath, templatePath, skipFiles);
|
package/scripts/utils.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
function copyDir(srcDir, destDir, skipFiles = []) {
|
|
6
|
-
fs.mkdirSync(destDir, {recursive: true});
|
|
7
|
-
for (const file of fs.readdirSync(srcDir)) {
|
|
8
|
-
const srcFile = path.resolve(srcDir, file);
|
|
9
|
-
const destFile = path.resolve(destDir, file);
|
|
10
|
-
copy(srcFile, destFile, skipFiles);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function copy(src, dest, skipFiles = []) {
|
|
15
|
-
if (skipFiles.some((file) => src.includes(file))) return;
|
|
16
|
-
|
|
17
|
-
const stat = fs.statSync(src);
|
|
18
|
-
|
|
19
|
-
if (stat.isDirectory()) {
|
|
20
|
-
copyDir(src, dest, skipFiles);
|
|
21
|
-
} else {
|
|
22
|
-
fs.copyFileSync(src, dest);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = {
|
|
27
|
-
copy,
|
|
28
|
-
copyDir,
|
|
29
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
extends: [
|
|
3
|
-
'plugin:@shopify/typescript',
|
|
4
|
-
'plugin:@shopify/react',
|
|
5
|
-
'plugin:@shopify/node',
|
|
6
|
-
'plugin:@shopify/prettier',
|
|
7
|
-
],
|
|
8
|
-
rules: {
|
|
9
|
-
'@shopify/jsx-no-hardcoded-content': 'off',
|
|
10
|
-
'@shopify/jsx-no-complex-expressions': 'off',
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* React overrides
|
|
14
|
-
*/
|
|
15
|
-
'react/react-in-jsx-scope': 'off',
|
|
16
|
-
'react/jsx-filename-extension': ['error', {extensions: ['.tsx', '.jsx']}],
|
|
17
|
-
'react/prop-types': 'off',
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Import overrides
|
|
21
|
-
*/
|
|
22
|
-
'import/no-unresolved': ['error', {ignore: ['@shopify/hydrogen']}],
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* ESlint overrides
|
|
26
|
-
*/
|
|
27
|
-
'no-use-before-define': 'off',
|
|
28
|
-
'no-warning-comments': 'off',
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* jsx-a11y overrides
|
|
32
|
-
*/
|
|
33
|
-
'jsx-a11y/click-events-have-key-events': 'off',
|
|
34
|
-
'jsx-a11y/no-noninteractive-element-interactions': 'off',
|
|
35
|
-
|
|
36
|
-
// These two rules result in a significant number of false positives so we
|
|
37
|
-
// need to keep them disabled.
|
|
38
|
-
'jsx-a11y/label-has-for': 'off',
|
|
39
|
-
'jsx-a11y/control-has-associated-label': 'off',
|
|
40
|
-
},
|
|
41
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
FROM node:14 AS build-env
|
|
2
|
-
ADD . /app
|
|
3
|
-
|
|
4
|
-
WORKDIR /app
|
|
5
|
-
RUN npm install
|
|
6
|
-
RUN npm run build
|
|
7
|
-
|
|
8
|
-
FROM gcr.io/distroless/nodejs:14 AS run-env
|
|
9
|
-
ENV NODE_ENV production
|
|
10
|
-
COPY --from=build-env /app /app
|
|
11
|
-
|
|
12
|
-
EXPOSE 8080
|
|
13
|
-
|
|
14
|
-
WORKDIR /app
|
|
15
|
-
CMD ["server.js"]
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>Hydrogen App</title>
|
|
8
|
-
<link rel="stylesheet" href="/src/index.css" />
|
|
9
|
-
</head>
|
|
10
|
-
<body>
|
|
11
|
-
<div id="root"></div>
|
|
12
|
-
<script type="module" src="/src/entry-client.jsx"></script>
|
|
13
|
-
</body>
|
|
14
|
-
</html>
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ShopifyServerProvider,
|
|
3
|
-
DefaultRoutes,
|
|
4
|
-
CartServerProvider,
|
|
5
|
-
} from '@shopify/hydrogen';
|
|
6
|
-
import {Switch} from 'react-router-dom';
|
|
7
|
-
import {Suspense} from 'react';
|
|
8
|
-
|
|
9
|
-
import shopifyConfig from '../shopify.config';
|
|
10
|
-
|
|
11
|
-
import DefaultSeo from './components/DefaultSeo.server';
|
|
12
|
-
import NotFound from './components/NotFound.server';
|
|
13
|
-
import CartProvider from './components/CartProvider.client';
|
|
14
|
-
|
|
15
|
-
export default function App({...serverState}) {
|
|
16
|
-
const pages = import.meta.globEager('./pages/**/*.server.(jsx|tsx)');
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<ShopifyServerProvider shopifyConfig={shopifyConfig} {...serverState}>
|
|
20
|
-
{/* START: Workaround for CartContext */}
|
|
21
|
-
<CartServerProvider request={serverState.request}>
|
|
22
|
-
{({cart, numCartLines}) => {
|
|
23
|
-
return (
|
|
24
|
-
<CartProvider cart={cart} numCartLines={numCartLines}>
|
|
25
|
-
{/* END: Workaround for CartContext */}
|
|
26
|
-
<Suspense fallback="Loading...">
|
|
27
|
-
<DefaultSeo />
|
|
28
|
-
<Switch>
|
|
29
|
-
<DefaultRoutes
|
|
30
|
-
pages={pages}
|
|
31
|
-
serverState={serverState}
|
|
32
|
-
fallback={<NotFound />}
|
|
33
|
-
/>
|
|
34
|
-
</Switch>
|
|
35
|
-
</Suspense>
|
|
36
|
-
</CartProvider>
|
|
37
|
-
);
|
|
38
|
-
}}
|
|
39
|
-
</CartServerProvider>
|
|
40
|
-
</ShopifyServerProvider>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
import FocusTrap from 'focus-trap-react';
|
|
2
|
-
import {
|
|
3
|
-
useCart,
|
|
4
|
-
useCartLinesTotalQuantity,
|
|
5
|
-
CartCheckoutButton,
|
|
6
|
-
Link,
|
|
7
|
-
CartLines,
|
|
8
|
-
CartLine,
|
|
9
|
-
CartShopPayButton,
|
|
10
|
-
CartEstimatedCost,
|
|
11
|
-
} from '@shopify/hydrogen/client';
|
|
12
|
-
|
|
13
|
-
import {useCartUI} from './CartUIProvider.client';
|
|
14
|
-
|
|
15
|
-
export default function Cart() {
|
|
16
|
-
const {isCartOpen} = useCartUI();
|
|
17
|
-
const itemCount = useCartLinesTotalQuantity();
|
|
18
|
-
const {error} = useCart();
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<FocusTrap active={isCartOpen} focusTrapOptions={{allowOutsideClick: true}}>
|
|
22
|
-
<aside
|
|
23
|
-
className={`pointer-events-none z-50 fixed right-0 top-0 bottom-0 md:p-5 flex flex-col w-full max-w-md min-w-sm transition-transform duration-500 transform-gpu ${
|
|
24
|
-
isCartOpen ? 'right-0' : 'translate-x-full'
|
|
25
|
-
}`}
|
|
26
|
-
id="cart"
|
|
27
|
-
tabIndex={-1}
|
|
28
|
-
aria-hidden={!isCartOpen}
|
|
29
|
-
aria-label="Cart"
|
|
30
|
-
>
|
|
31
|
-
<div className="overflow-hidden md:h-auto pointer-events-auto">
|
|
32
|
-
<div className="flex flex-col shadow-xl max-h-full">
|
|
33
|
-
<header className="bg-white px-4 md:px-8 md:h-20 border-b border-gray-300 border-solid rounded-t-md flex flex-shrink-0 items-center justify-between">
|
|
34
|
-
<CartHeader />
|
|
35
|
-
</header>
|
|
36
|
-
|
|
37
|
-
<div className="bg-white px-4 md:px-8 overflow-y-scroll md:max-h-96">
|
|
38
|
-
{itemCount > 0 ? (
|
|
39
|
-
<CartLineItems />
|
|
40
|
-
) : (
|
|
41
|
-
<p className="text-center text-gray-600 my-8">
|
|
42
|
-
Your cart is empty
|
|
43
|
-
</p>
|
|
44
|
-
)}
|
|
45
|
-
</div>
|
|
46
|
-
|
|
47
|
-
{error ? (
|
|
48
|
-
<div
|
|
49
|
-
className="border bg-red-200 border-red-400 text-red-800 mb-4 mx-8 px-4 py-3 rounded relative"
|
|
50
|
-
role="alert"
|
|
51
|
-
>
|
|
52
|
-
{error}
|
|
53
|
-
</div>
|
|
54
|
-
) : null}
|
|
55
|
-
|
|
56
|
-
<footer
|
|
57
|
-
className={`${
|
|
58
|
-
itemCount > 0 ? 'border-t border-solid border-gray-300' : ''
|
|
59
|
-
} bg-white p-4 md:p-8 space-y-4 flex-shrink-0 rounded-b-md`}
|
|
60
|
-
>
|
|
61
|
-
{itemCount > 0 ? <CartFooter /> : null}
|
|
62
|
-
</footer>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</aside>
|
|
66
|
-
</FocusTrap>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function CartHeader() {
|
|
71
|
-
const {isCartOpen, toggleCart} = useCartUI();
|
|
72
|
-
const itemCount = useCartLinesTotalQuantity();
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<>
|
|
76
|
-
<button
|
|
77
|
-
type="button"
|
|
78
|
-
aria-expanded={isCartOpen}
|
|
79
|
-
aria-controls="cart"
|
|
80
|
-
onClick={toggleCart}
|
|
81
|
-
>
|
|
82
|
-
<div>
|
|
83
|
-
<svg
|
|
84
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
85
|
-
className="h-5 w-5 text-gray-600"
|
|
86
|
-
viewBox="0 0 20 20"
|
|
87
|
-
fill="currentColor"
|
|
88
|
-
>
|
|
89
|
-
<path
|
|
90
|
-
fillRule="evenodd"
|
|
91
|
-
d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
|
|
92
|
-
clipRule="evenodd"
|
|
93
|
-
/>
|
|
94
|
-
</svg>
|
|
95
|
-
</div>
|
|
96
|
-
<span className="sr-only">Close cart</span>
|
|
97
|
-
</button>
|
|
98
|
-
<div className="h-12 w-12 p-2 md:h-7 md:w-7 md:p-0">
|
|
99
|
-
<div className="relative">
|
|
100
|
-
<svg
|
|
101
|
-
width="19"
|
|
102
|
-
height="24"
|
|
103
|
-
viewBox="0 0 19 24"
|
|
104
|
-
fill="none"
|
|
105
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
106
|
-
>
|
|
107
|
-
<path
|
|
108
|
-
d="M15.5894 7H3.41063C2.89451 7 2.46318 7.39279 2.415 7.90666L1.205 20.8133C1.09502 21.9865 2.01796 23 3.19627 23H15.8037C16.982 23 17.905 21.9865 17.795 20.8133L16.585 7.90666C16.5368 7.39279 16.1055 7 15.5894 7Z"
|
|
109
|
-
stroke="#1F2937"
|
|
110
|
-
strokeWidth="2"
|
|
111
|
-
strokeMiterlimit="10"
|
|
112
|
-
strokeLinecap="round"
|
|
113
|
-
/>
|
|
114
|
-
<path
|
|
115
|
-
d="M6 7V9.98952C6 12.0075 7.63589 13.6434 9.65386 13.6434V13.6434C11.6718 13.6434 13.3077 12.0075 13.3077 9.98952V7"
|
|
116
|
-
stroke="#1F2937"
|
|
117
|
-
strokeWidth="2"
|
|
118
|
-
/>
|
|
119
|
-
<path
|
|
120
|
-
d="M13 6L13 4.5C13 2.567 11.433 1 9.5 1V1C7.567 1 6 2.567 6 4.5L6 6"
|
|
121
|
-
stroke="#1F2937"
|
|
122
|
-
strokeWidth="2"
|
|
123
|
-
className={`${itemCount > 0 ? 'block' : 'hidden'}`}
|
|
124
|
-
/>
|
|
125
|
-
</svg>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
</>
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function CartLineItems() {
|
|
133
|
-
return (
|
|
134
|
-
<div role="table" aria-label="Shopping cart">
|
|
135
|
-
<div role="row" className="sr-only">
|
|
136
|
-
<div role="columnheader">Image</div>
|
|
137
|
-
<div role="columnheader">Item details</div>
|
|
138
|
-
<div role="columnheader">Price</div>
|
|
139
|
-
</div>
|
|
140
|
-
<CartLines>
|
|
141
|
-
{({merchandise}) => (
|
|
142
|
-
<div
|
|
143
|
-
role="row"
|
|
144
|
-
className="pt-8 pb-8 border-b border-solid border-gray-300 last:border-0"
|
|
145
|
-
>
|
|
146
|
-
<div className="flex space-x-8 relative">
|
|
147
|
-
<div role="cell">
|
|
148
|
-
<div className="w-20 h-20 relative">
|
|
149
|
-
<Link to={`/products/${merchandise.product.handle}`}>
|
|
150
|
-
<CartLine.Image className="bg-white rounded w-full h-full object-cover" />
|
|
151
|
-
</Link>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
<div
|
|
155
|
-
role="cell"
|
|
156
|
-
className="flex-grow flex flex-col justify-between"
|
|
157
|
-
>
|
|
158
|
-
<div className="flex gap-2">
|
|
159
|
-
<div className="flex-grow">
|
|
160
|
-
<CartLine.ProductTitle className="text-gray-900 font-semibold" />
|
|
161
|
-
<CartLine.SelectedOptions className="text-sm">
|
|
162
|
-
{({name, value}) => (
|
|
163
|
-
<>
|
|
164
|
-
{name}: {value}
|
|
165
|
-
</>
|
|
166
|
-
)}
|
|
167
|
-
</CartLine.SelectedOptions>
|
|
168
|
-
<CartLine.Attributes className="text-sm">
|
|
169
|
-
{({key, value}) => (
|
|
170
|
-
<>
|
|
171
|
-
{key}: {value}
|
|
172
|
-
</>
|
|
173
|
-
)}
|
|
174
|
-
</CartLine.Attributes>
|
|
175
|
-
</div>
|
|
176
|
-
<div className="flex-shrink">
|
|
177
|
-
<CartLine.QuantityAdjustButton
|
|
178
|
-
adjust="remove"
|
|
179
|
-
aria-label="Remove from cart"
|
|
180
|
-
>
|
|
181
|
-
<svg
|
|
182
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
183
|
-
className="h-5 w-5"
|
|
184
|
-
viewBox="0 0 20 20"
|
|
185
|
-
fill="currentColor"
|
|
186
|
-
>
|
|
187
|
-
<path
|
|
188
|
-
fillRule="evenodd"
|
|
189
|
-
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
|
190
|
-
clipRule="evenodd"
|
|
191
|
-
/>
|
|
192
|
-
</svg>
|
|
193
|
-
</CartLine.QuantityAdjustButton>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
<div className="flex mt-2">
|
|
197
|
-
<div className="flex-grow">
|
|
198
|
-
<div className="border border-solid border-gray-300 inline-flex items-center text-gray-500 rounded">
|
|
199
|
-
<CartLine.QuantityAdjustButton
|
|
200
|
-
adjust="decrease"
|
|
201
|
-
className="p-2"
|
|
202
|
-
aria-label="Decrease quantity"
|
|
203
|
-
>
|
|
204
|
-
<svg
|
|
205
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
206
|
-
className="h-5 w-5"
|
|
207
|
-
viewBox="0 0 20 20"
|
|
208
|
-
fill="currentColor"
|
|
209
|
-
>
|
|
210
|
-
<path
|
|
211
|
-
fillRule="evenodd"
|
|
212
|
-
d="M5 10a1 1 0 011-1h8a1 1 0 110 2H6a1 1 0 01-1-1z"
|
|
213
|
-
clipRule="evenodd"
|
|
214
|
-
/>
|
|
215
|
-
</svg>
|
|
216
|
-
</CartLine.QuantityAdjustButton>
|
|
217
|
-
<CartLine.Quantity
|
|
218
|
-
as="div"
|
|
219
|
-
className="p-2 text-gray-900 text-center"
|
|
220
|
-
/>
|
|
221
|
-
<CartLine.QuantityAdjustButton
|
|
222
|
-
adjust="increase"
|
|
223
|
-
className="p-2"
|
|
224
|
-
aria-label="Increase quantity"
|
|
225
|
-
>
|
|
226
|
-
<svg
|
|
227
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
228
|
-
className="h-5 w-5"
|
|
229
|
-
viewBox="0 0 20 20"
|
|
230
|
-
fill="currentColor"
|
|
231
|
-
>
|
|
232
|
-
<path
|
|
233
|
-
fillRule="evenodd"
|
|
234
|
-
d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z"
|
|
235
|
-
clipRule="evenodd"
|
|
236
|
-
/>
|
|
237
|
-
</svg>
|
|
238
|
-
</CartLine.QuantityAdjustButton>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
242
|
-
<CartLine.Price
|
|
243
|
-
role="cell"
|
|
244
|
-
className="absolute bottom-0 right-0 mb-3"
|
|
245
|
-
/>
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
</div>
|
|
249
|
-
)}
|
|
250
|
-
</CartLines>
|
|
251
|
-
</div>
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function CartFooter() {
|
|
256
|
-
return (
|
|
257
|
-
<>
|
|
258
|
-
<div role="table" className="w-full" aria-label="Cost summary">
|
|
259
|
-
<div role="row" className="flex items-center justify-between">
|
|
260
|
-
<div role="rowheader" className="font-semibold">
|
|
261
|
-
Subtotal
|
|
262
|
-
</div>
|
|
263
|
-
<CartEstimatedCost
|
|
264
|
-
amountType="subtotal"
|
|
265
|
-
role="cell"
|
|
266
|
-
className="text-right"
|
|
267
|
-
/>
|
|
268
|
-
</div>
|
|
269
|
-
<div role="row" className="flex items-center justify-between">
|
|
270
|
-
<div role="rowheader" className="font-semibold">
|
|
271
|
-
Shipping
|
|
272
|
-
</div>
|
|
273
|
-
<div role="cell" className="text-right">
|
|
274
|
-
Free
|
|
275
|
-
</div>
|
|
276
|
-
</div>
|
|
277
|
-
<div role="row" className="flex items-center justify-between">
|
|
278
|
-
<div role="rowheader" className="font-semibold">
|
|
279
|
-
Total
|
|
280
|
-
</div>
|
|
281
|
-
<CartEstimatedCost
|
|
282
|
-
amountType="total"
|
|
283
|
-
role="cell"
|
|
284
|
-
className="text-right"
|
|
285
|
-
/>
|
|
286
|
-
</div>
|
|
287
|
-
</div>
|
|
288
|
-
<div className="space-y-2">
|
|
289
|
-
<CartShopPayButton className="flex justify-center w-full" />
|
|
290
|
-
<CartCheckoutButton className="block w-full text-white uppercase text-sm rounded-md md:mb-8 bg-black px-3 py-4 disabled:cursor-wait disabled:opacity-60">
|
|
291
|
-
Checkout
|
|
292
|
-
</CartCheckoutButton>
|
|
293
|
-
</div>
|
|
294
|
-
</>
|
|
295
|
-
);
|
|
296
|
-
}
|