Merge branch 'v2-shop' into v2
@@ -16,6 +16,10 @@ VITE_API_URL_PROD="https://api.housesof.world"
|
||||
VITE_API_GRAPHQL_PATH="/graphql"
|
||||
VITE_API_TOKEN="efa40490-152c-49d7-a75b-30a6427439b1"
|
||||
|
||||
# Shop
|
||||
VITE_SWELL_STORE_ID="houses-of"
|
||||
VITE_SWELL_API_TOKEN="v3BiXcZP5jpmhL80i4eUy6iXxcpN9cIq"
|
||||
|
||||
# Analytics
|
||||
VITE_ANALYTICS_KEY="c01e378821e6ba7bf9a9f947b107500bfcbd4ae8"
|
||||
VITE_ANALYTICS_URL="https://stats.flayks.com"
|
||||
@@ -26,14 +26,15 @@
|
||||
"@sveltejs/adapter-node": "next",
|
||||
"@sveltejs/adapter-vercel": "next",
|
||||
"@sveltejs/kit": "next",
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||
"@typescript-eslint/parser": "^5.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.1",
|
||||
"@typescript-eslint/parser": "^5.3.1",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint-plugin-svelte3": "^3.2.1",
|
||||
"sass": "^1.43.4",
|
||||
"svelte": "^3.44.1",
|
||||
"svelte-check": "^2.2.8",
|
||||
"svelte-preprocess": "^4.9.8",
|
||||
"swell-node": "^4.0.6",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
|
||||
234
pnpm-lock.yaml
generated
@@ -4,8 +4,8 @@ specifiers:
|
||||
'@sveltejs/adapter-node': next
|
||||
'@sveltejs/adapter-vercel': next
|
||||
'@sveltejs/kit': next
|
||||
'@typescript-eslint/eslint-plugin': ^5.3.0
|
||||
'@typescript-eslint/parser': ^5.3.0
|
||||
'@typescript-eslint/eslint-plugin': ^5.3.1
|
||||
'@typescript-eslint/parser': ^5.3.1
|
||||
dayjs: ^1.10.7
|
||||
embla-carousel: ^5.0.1
|
||||
eslint: ^8.2.0
|
||||
@@ -16,6 +16,7 @@ specifiers:
|
||||
svelte: ^3.44.1
|
||||
svelte-check: ^2.2.8
|
||||
svelte-preprocess: ^4.9.8
|
||||
swell-node: ^4.0.6
|
||||
tslib: ^2.3.1
|
||||
typescript: ^4.4.4
|
||||
|
||||
@@ -29,14 +30,15 @@ devDependencies:
|
||||
'@sveltejs/adapter-node': 1.0.0-next.55
|
||||
'@sveltejs/adapter-vercel': 1.0.0-next.31
|
||||
'@sveltejs/kit': 1.0.0-next.195_sass@1.43.4+svelte@3.44.1
|
||||
'@typescript-eslint/eslint-plugin': 5.3.0_c2f57e23dad63fa3be14ade7e20e2af7
|
||||
'@typescript-eslint/parser': 5.3.0_eslint@8.2.0+typescript@4.4.4
|
||||
'@typescript-eslint/eslint-plugin': 5.3.1_4653b7803b7453f5f37717b7e1448517
|
||||
'@typescript-eslint/parser': 5.3.1_eslint@8.2.0+typescript@4.4.4
|
||||
eslint: 8.2.0
|
||||
eslint-plugin-svelte3: 3.2.1_eslint@8.2.0+svelte@3.44.1
|
||||
sass: 1.43.4
|
||||
svelte: 3.44.1
|
||||
svelte-check: 2.2.8_sass@1.43.4+svelte@3.44.1
|
||||
svelte-preprocess: 4.9.8_6627cbae993b0086cf4555994e082905
|
||||
swell-node: 4.0.6
|
||||
tslib: 2.3.1
|
||||
typescript: 4.4.4
|
||||
|
||||
@@ -106,14 +108,14 @@ packages:
|
||||
/@sveltejs/adapter-node/1.0.0-next.55:
|
||||
resolution: {integrity: sha512-Kmh8lx8kIY7W6rkqjC78y4dQTyjiAHD9D1WfmUTtYuDW1jAIG+YbZmPC9127kH5KOSGK4+tI8mpReDVB1lgf8g==}
|
||||
dependencies:
|
||||
esbuild: 0.13.12
|
||||
esbuild: 0.13.13
|
||||
tiny-glob: 0.2.9
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-vercel/1.0.0-next.31:
|
||||
resolution: {integrity: sha512-W8p/U00B6ihVrDpwMkgEexfVUzaLmn4MRtXj//Gw4NDFfsrZR4P5wBidrOAIkCYBMvqCOHD+vbAvIiAMOaN23g==}
|
||||
dependencies:
|
||||
esbuild: 0.13.12
|
||||
esbuild: 0.13.13
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.195_sass@1.43.4+svelte@3.44.1:
|
||||
@@ -123,11 +125,11 @@ packages:
|
||||
peerDependencies:
|
||||
svelte: ^3.44.0
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 1.0.0-next.30_svelte@3.44.1+vite@2.6.13
|
||||
'@sveltejs/vite-plugin-svelte': 1.0.0-next.30_svelte@3.44.1+vite@2.6.14
|
||||
cheap-watch: 1.0.4
|
||||
sade: 1.7.4
|
||||
svelte: 3.44.1
|
||||
vite: 2.6.13_sass@1.43.4
|
||||
vite: 2.6.14_sass@1.43.4
|
||||
transitivePeerDependencies:
|
||||
- diff-match-patch
|
||||
- less
|
||||
@@ -136,7 +138,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sveltejs/vite-plugin-svelte/1.0.0-next.30_svelte@3.44.1+vite@2.6.13:
|
||||
/@sveltejs/vite-plugin-svelte/1.0.0-next.30_svelte@3.44.1+vite@2.6.14:
|
||||
resolution: {integrity: sha512-YQqdMxjL1VgSFk4/+IY3yLwuRRapPafPiZTiaGEq1psbJYSNYUWx9F1zMm32GMsnogg3zn99mGJOqe3ld3HZSg==}
|
||||
engines: {node: ^14.13.1 || >= 16}
|
||||
peerDependencies:
|
||||
@@ -154,7 +156,7 @@ packages:
|
||||
require-relative: 0.8.7
|
||||
svelte: 3.44.1
|
||||
svelte-hmr: 0.14.7_svelte@3.44.1
|
||||
vite: 2.6.13_sass@1.43.4
|
||||
vite: 2.6.14_sass@1.43.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -163,8 +165,8 @@ packages:
|
||||
resolution: {integrity: sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==}
|
||||
dev: true
|
||||
|
||||
/@types/node/16.11.6:
|
||||
resolution: {integrity: sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==}
|
||||
/@types/node/16.11.7:
|
||||
resolution: {integrity: sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==}
|
||||
dev: true
|
||||
|
||||
/@types/pug/2.0.5:
|
||||
@@ -174,11 +176,11 @@ packages:
|
||||
/@types/sass/1.43.0:
|
||||
resolution: {integrity: sha512-DPSXNJ1rYLo88GyF9tuB4bsYGfpKI1a4+wOQmc+LI1SUoocm9QLRSpz0GxxuyjmJsYFIQo/dDlRSSpIXngff+w==}
|
||||
dependencies:
|
||||
'@types/node': 16.11.6
|
||||
'@types/node': 16.11.7
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/5.3.0_c2f57e23dad63fa3be14ade7e20e2af7:
|
||||
resolution: {integrity: sha512-ARUEJHJrq85aaiCqez7SANeahDsJTD3AEua34EoQN9pHS6S5Bq9emcIaGGySt/4X2zSi+vF5hAH52sEen7IO7g==}
|
||||
/@typescript-eslint/eslint-plugin/5.3.1_4653b7803b7453f5f37717b7e1448517:
|
||||
resolution: {integrity: sha512-cFImaoIr5Ojj358xI/SDhjog57OK2NqlpxwdcgyxDA3bJlZcJq5CPzUXtpD7CxI2Hm6ATU7w5fQnnkVnmwpHqw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^5.0.0
|
||||
@@ -188,9 +190,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/experimental-utils': 5.3.0_eslint@8.2.0+typescript@4.4.4
|
||||
'@typescript-eslint/parser': 5.3.0_eslint@8.2.0+typescript@4.4.4
|
||||
'@typescript-eslint/scope-manager': 5.3.0
|
||||
'@typescript-eslint/experimental-utils': 5.3.1_eslint@8.2.0+typescript@4.4.4
|
||||
'@typescript-eslint/parser': 5.3.1_eslint@8.2.0+typescript@4.4.4
|
||||
'@typescript-eslint/scope-manager': 5.3.1
|
||||
debug: 4.3.2
|
||||
eslint: 8.2.0
|
||||
functional-red-black-tree: 1.0.1
|
||||
@@ -203,16 +205,16 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/experimental-utils/5.3.0_eslint@8.2.0+typescript@4.4.4:
|
||||
resolution: {integrity: sha512-NFVxYTjKj69qB0FM+piah1x3G/63WB8vCBMnlnEHUsiLzXSTWb9FmFn36FD9Zb4APKBLY3xRArOGSMQkuzTF1w==}
|
||||
/@typescript-eslint/experimental-utils/5.3.1_eslint@8.2.0+typescript@4.4.4:
|
||||
resolution: {integrity: sha512-RgFn5asjZ5daUhbK5Sp0peq0SSMytqcrkNfU4pnDma2D8P3ElZ6JbYjY8IMSFfZAJ0f3x3tnO3vXHweYg0g59w==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: '*'
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.9
|
||||
'@typescript-eslint/scope-manager': 5.3.0
|
||||
'@typescript-eslint/types': 5.3.0
|
||||
'@typescript-eslint/typescript-estree': 5.3.0_typescript@4.4.4
|
||||
'@typescript-eslint/scope-manager': 5.3.1
|
||||
'@typescript-eslint/types': 5.3.1
|
||||
'@typescript-eslint/typescript-estree': 5.3.1_typescript@4.4.4
|
||||
eslint: 8.2.0
|
||||
eslint-scope: 5.1.1
|
||||
eslint-utils: 3.0.0_eslint@8.2.0
|
||||
@@ -221,8 +223,8 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/5.3.0_eslint@8.2.0+typescript@4.4.4:
|
||||
resolution: {integrity: sha512-rKu/yAReip7ovx8UwOAszJVO5MgBquo8WjIQcp1gx4pYQCwYzag+I5nVNHO4MqyMkAo0gWt2gWUi+36gWAVKcw==}
|
||||
/@typescript-eslint/parser/5.3.1_eslint@8.2.0+typescript@4.4.4:
|
||||
resolution: {integrity: sha512-TD+ONlx5c+Qhk21x9gsJAMRohWAUMavSOmJgv3JGy9dgPhuBd5Wok0lmMClZDyJNLLZK1JRKiATzCKZNUmoyfw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
@@ -231,9 +233,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 5.3.0
|
||||
'@typescript-eslint/types': 5.3.0
|
||||
'@typescript-eslint/typescript-estree': 5.3.0_typescript@4.4.4
|
||||
'@typescript-eslint/scope-manager': 5.3.1
|
||||
'@typescript-eslint/types': 5.3.1
|
||||
'@typescript-eslint/typescript-estree': 5.3.1_typescript@4.4.4
|
||||
debug: 4.3.2
|
||||
eslint: 8.2.0
|
||||
typescript: 4.4.4
|
||||
@@ -241,21 +243,21 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager/5.3.0:
|
||||
resolution: {integrity: sha512-22Uic9oRlTsPppy5Tcwfj+QET5RWEnZ5414Prby465XxQrQFZ6nnm5KnXgnsAJefG4hEgMnaxTB3kNEyjdjj6A==}
|
||||
/@typescript-eslint/scope-manager/5.3.1:
|
||||
resolution: {integrity: sha512-XksFVBgAq0Y9H40BDbuPOTUIp7dn4u8oOuhcgGq7EoDP50eqcafkMVGrypyVGvDYHzjhdUCUwuwVUK4JhkMAMg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.3.0
|
||||
'@typescript-eslint/visitor-keys': 5.3.0
|
||||
'@typescript-eslint/types': 5.3.1
|
||||
'@typescript-eslint/visitor-keys': 5.3.1
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types/5.3.0:
|
||||
resolution: {integrity: sha512-fce5pG41/w8O6ahQEhXmMV+xuh4+GayzqEogN24EK+vECA3I6pUwKuLi5QbXO721EMitpQne5VKXofPonYlAQg==}
|
||||
/@typescript-eslint/types/5.3.1:
|
||||
resolution: {integrity: sha512-bG7HeBLolxKHtdHG54Uac750eXuQQPpdJfCYuw4ZI3bZ7+GgKClMWM8jExBtp7NSP4m8PmLRM8+lhzkYnSmSxQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree/5.3.0_typescript@4.4.4:
|
||||
resolution: {integrity: sha512-FJ0nqcaUOpn/6Z4Jwbtf+o0valjBLkqc3MWkMvrhA2TvzFXtcclIM8F4MBEmYa2kgcI8EZeSAzwoSrIC8JYkug==}
|
||||
/@typescript-eslint/typescript-estree/5.3.1_typescript@4.4.4:
|
||||
resolution: {integrity: sha512-PwFbh/PKDVo/Wct6N3w+E4rLZxUDgsoII/GrWM2A62ETOzJd4M6s0Mu7w4CWsZraTbaC5UQI+dLeyOIFF1PquQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
@@ -263,8 +265,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.3.0
|
||||
'@typescript-eslint/visitor-keys': 5.3.0
|
||||
'@typescript-eslint/types': 5.3.1
|
||||
'@typescript-eslint/visitor-keys': 5.3.1
|
||||
debug: 4.3.2
|
||||
globby: 11.0.4
|
||||
is-glob: 4.0.3
|
||||
@@ -275,12 +277,12 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys/5.3.0:
|
||||
resolution: {integrity: sha512-oVIAfIQuq0x2TFDNLVavUn548WL+7hdhxYn+9j3YdJJXB7mH9dAmZNJsPDa7Jc+B9WGqoiex7GUDbyMxV0a/aw==}
|
||||
/@typescript-eslint/visitor-keys/5.3.1:
|
||||
resolution: {integrity: sha512-3cHUzUuVTuNHx0Gjjt5pEHa87+lzyqOiHXy/Gz+SJOCW1mpw9xQHIIEwnKn+Thph1mgWyZ90nboOcSuZr/jTTQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.3.0
|
||||
eslint-visitor-keys: 3.0.0
|
||||
'@typescript-eslint/types': 5.3.1
|
||||
eslint-visitor-keys: 3.1.0
|
||||
dev: true
|
||||
|
||||
/acorn-jsx/5.3.2_acorn@8.5.0:
|
||||
@@ -478,164 +480,147 @@ packages:
|
||||
resolution: {integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=}
|
||||
dev: true
|
||||
|
||||
/esbuild-android-arm64/0.13.12:
|
||||
resolution: {integrity: sha512-TSVZVrb4EIXz6KaYjXfTzPyyRpXV5zgYIADXtQsIenjZ78myvDGaPi11o4ZSaHIwFHsuwkB6ne5SZRBwAQ7maw==}
|
||||
/esbuild-android-arm64/0.13.13:
|
||||
resolution: {integrity: sha512-T02aneWWguJrF082jZworjU6vm8f4UQ+IH2K3HREtlqoY9voiJUwHLRL6khRlsNLzVglqgqb7a3HfGx7hAADCQ==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-darwin-64/0.13.12:
|
||||
resolution: {integrity: sha512-c51C+N+UHySoV2lgfWSwwmlnLnL0JWj/LzuZt9Ltk9ub1s2Y8cr6SQV5W3mqVH1egUceew6KZ8GyI4nwu+fhsw==}
|
||||
/esbuild-darwin-64/0.13.13:
|
||||
resolution: {integrity: sha512-wkaiGAsN/09X9kDlkxFfbbIgR78SNjMOfUhoel3CqKBDsi9uZhw7HBNHNxTzYUK8X8LAKFpbODgcRB3b/I8gHA==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-darwin-arm64/0.13.12:
|
||||
resolution: {integrity: sha512-JvAMtshP45Hd8A8wOzjkY1xAnTKTYuP/QUaKp5eUQGX+76GIie3fCdUUr2ZEKdvpSImNqxiZSIMziEiGB5oUmQ==}
|
||||
/esbuild-darwin-arm64/0.13.13:
|
||||
resolution: {integrity: sha512-b02/nNKGSV85Gw9pUCI5B48AYjk0vFggDeom0S6QMP/cEDtjSh1WVfoIFNAaLA0MHWfue8KBwoGVsN7rBshs4g==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-freebsd-64/0.13.12:
|
||||
resolution: {integrity: sha512-r6On/Skv9f0ZjTu6PW5o7pdXr8aOgtFOEURJZYf1XAJs0IQ+gW+o1DzXjVkIoT+n1cm3N/t1KRJfX71MPg/ZUA==}
|
||||
/esbuild-freebsd-64/0.13.13:
|
||||
resolution: {integrity: sha512-ALgXYNYDzk9YPVk80A+G4vz2D22Gv4j4y25exDBGgqTcwrVQP8rf/rjwUjHoh9apP76oLbUZTmUmvCMuTI1V9A==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-freebsd-arm64/0.13.12:
|
||||
resolution: {integrity: sha512-F6LmI2Q1gii073kmBE3NOTt/6zLL5zvZsxNLF8PMAwdHc+iBhD1vzfI8uQZMJA1IgXa3ocr3L3DJH9fLGXy6Yw==}
|
||||
/esbuild-freebsd-arm64/0.13.13:
|
||||
resolution: {integrity: sha512-uFvkCpsZ1yqWQuonw5T1WZ4j59xP/PCvtu6I4pbLejhNo4nwjW6YalqnBvBSORq5/Ifo9S/wsIlVHzkzEwdtlw==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-32/0.13.12:
|
||||
resolution: {integrity: sha512-U1UZwG3UIwF7/V4tCVAo/nkBV9ag5KJiJTt+gaCmLVWH3bPLX7y+fNlhIWZy8raTMnXhMKfaTvWZ9TtmXzvkuQ==}
|
||||
/esbuild-linux-32/0.13.13:
|
||||
resolution: {integrity: sha512-yxR9BBwEPs9acVEwTrEE2JJNHYVuPQC9YGjRfbNqtyfK/vVBQYuw8JaeRFAvFs3pVJdQD0C2BNP4q9d62SCP4w==}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-64/0.13.12:
|
||||
resolution: {integrity: sha512-YpXSwtu2NxN3N4ifJxEdsgd6Q5d8LYqskrAwjmoCT6yQnEHJSF5uWcxv783HWN7lnGpJi9KUtDvYsnMdyGw71Q==}
|
||||
/esbuild-linux-64/0.13.13:
|
||||
resolution: {integrity: sha512-kzhjlrlJ+6ESRB/n12WTGll94+y+HFeyoWsOrLo/Si0s0f+Vip4b8vlnG0GSiS6JTsWYAtGHReGczFOaETlKIw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-arm/0.13.12:
|
||||
resolution: {integrity: sha512-SyiT/JKxU6J+DY2qUiSLZJqCAftIt3uoGejZ0HDnUM2MGJqEGSGh7p1ecVL2gna3PxS4P+j6WAehCwgkBPXNIw==}
|
||||
/esbuild-linux-arm/0.13.13:
|
||||
resolution: {integrity: sha512-hXub4pcEds+U1TfvLp1maJ+GHRw7oizvzbGRdUvVDwtITtjq8qpHV5Q5hWNNn6Q+b3b2UxF03JcgnpzCw96nUQ==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-arm64/0.13.12:
|
||||
resolution: {integrity: sha512-sgDNb8kb3BVodtAlcFGgwk+43KFCYjnFOaOfJibXnnIojNWuJHpL6aQJ4mumzNWw8Rt1xEtDQyuGK9f+Y24jGA==}
|
||||
/esbuild-linux-arm64/0.13.13:
|
||||
resolution: {integrity: sha512-KMrEfnVbmmJxT3vfTnPv/AiXpBFbbyExH13BsUGy1HZRPFMi5Gev5gk8kJIZCQSRfNR17aqq8sO5Crm2KpZkng==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-mips64le/0.13.12:
|
||||
resolution: {integrity: sha512-qQJHlZBG+QwVIA8AbTEtbvF084QgDi4DaUsUnA+EolY1bxrG+UyOuGflM2ZritGhfS/k7THFjJbjH2wIeoKA2g==}
|
||||
/esbuild-linux-mips64le/0.13.13:
|
||||
resolution: {integrity: sha512-cJT9O1LYljqnnqlHaS0hdG73t7hHzF3zcN0BPsjvBq+5Ad47VJun+/IG4inPhk8ta0aEDK6LdP+F9299xa483w==}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-ppc64le/0.13.12:
|
||||
resolution: {integrity: sha512-2dSnm1ldL7Lppwlo04CGQUpwNn5hGqXI38OzaoPOkRsBRWFBozyGxTFSee/zHFS+Pdh3b28JJbRK3owrrRgWNw==}
|
||||
/esbuild-linux-ppc64le/0.13.13:
|
||||
resolution: {integrity: sha512-+rghW8st6/7O6QJqAjVK3eXzKkZqYAw6LgHv7yTMiJ6ASnNvghSeOcIvXFep3W2oaJc35SgSPf21Ugh0o777qQ==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-netbsd-64/0.13.12:
|
||||
resolution: {integrity: sha512-D4raxr02dcRiQNbxOLzpqBzcJNFAdsDNxjUbKkDMZBkL54Z0vZh4LRndycdZAMcIdizC/l/Yp/ZsBdAFxc5nbA==}
|
||||
/esbuild-netbsd-64/0.13.13:
|
||||
resolution: {integrity: sha512-A/B7rwmzPdzF8c3mht5TukbnNwY5qMJqes09ou0RSzA5/jm7Jwl/8z853ofujTFOLhkNHUf002EAgokzSgEMpQ==}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-openbsd-64/0.13.12:
|
||||
resolution: {integrity: sha512-KuLCmYMb2kh05QuPJ+va60bKIH5wHL8ypDkmpy47lzwmdxNsuySeCMHuTv5o2Af1RUn5KLO5ZxaZeq4GEY7DaQ==}
|
||||
/esbuild-openbsd-64/0.13.13:
|
||||
resolution: {integrity: sha512-szwtuRA4rXKT3BbwoGpsff6G7nGxdKgUbW9LQo6nm0TVCCjDNDC/LXxT994duIW8Tyq04xZzzZSW7x7ttDiw1w==}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-sunos-64/0.13.12:
|
||||
resolution: {integrity: sha512-jBsF+e0woK3miKI8ufGWKG3o3rY9DpHvCVRn5eburMIIE+2c+y3IZ1srsthKyKI6kkXLvV4Cf/E7w56kLipMXw==}
|
||||
/esbuild-sunos-64/0.13.13:
|
||||
resolution: {integrity: sha512-ihyds9O48tVOYF48iaHYUK/boU5zRaLOXFS+OOL3ceD39AyHo46HVmsJLc7A2ez0AxNZCxuhu+P9OxfPfycTYQ==}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-32/0.13.12:
|
||||
resolution: {integrity: sha512-L9m4lLFQrFeR7F+eLZXG82SbXZfUhyfu6CexZEil6vm+lc7GDCE0Q8DiNutkpzjv1+RAbIGVva9muItQ7HVTkQ==}
|
||||
/esbuild-windows-32/0.13.13:
|
||||
resolution: {integrity: sha512-h2RTYwpG4ldGVJlbmORObmilzL8EECy8BFiF8trWE1ZPHLpECE9//J3Bi+W3eDUuv/TqUbiNpGrq4t/odbayUw==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-64/0.13.12:
|
||||
resolution: {integrity: sha512-k4tX4uJlSbSkfs78W5d9+I9gpd+7N95W7H2bgOMFPsYREVJs31+Q2gLLHlsnlY95zBoPQMIzHooUIsixQIBjaQ==}
|
||||
/esbuild-windows-64/0.13.13:
|
||||
resolution: {integrity: sha512-oMrgjP4CjONvDHe7IZXHrMk3wX5Lof/IwFEIbwbhgbXGBaN2dke9PkViTiXC3zGJSGpMvATXVplEhlInJ0drHA==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-arm64/0.13.12:
|
||||
resolution: {integrity: sha512-2tTv/BpYRIvuwHpp2M960nG7uvL+d78LFW/ikPItO+2GfK51CswIKSetSpDii+cjz8e9iSPgs+BU4o8nWICBwQ==}
|
||||
/esbuild-windows-arm64/0.13.13:
|
||||
resolution: {integrity: sha512-6fsDfTuTvltYB5k+QPah/x7LrI2+OLAJLE3bWLDiZI6E8wXMQU+wLqtEO/U/RvJgVY1loPs5eMpUBpVajczh1A==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild/0.13.12:
|
||||
resolution: {integrity: sha512-vTKKUt+yoz61U/BbrnmlG9XIjwpdIxmHB8DlPR0AAW6OdS+nBQBci6LUHU2q9WbBobMEIQxxDpKbkmOGYvxsow==}
|
||||
/esbuild/0.13.13:
|
||||
resolution: {integrity: sha512-Z17A/R6D0b4s3MousytQ/5i7mTCbaF+Ua/yPfoe71vdTv4KBvVAvQ/6ytMngM2DwGJosl8WxaD75NOQl2QF26Q==}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
esbuild-android-arm64: 0.13.12
|
||||
esbuild-darwin-64: 0.13.12
|
||||
esbuild-darwin-arm64: 0.13.12
|
||||
esbuild-freebsd-64: 0.13.12
|
||||
esbuild-freebsd-arm64: 0.13.12
|
||||
esbuild-linux-32: 0.13.12
|
||||
esbuild-linux-64: 0.13.12
|
||||
esbuild-linux-arm: 0.13.12
|
||||
esbuild-linux-arm64: 0.13.12
|
||||
esbuild-linux-mips64le: 0.13.12
|
||||
esbuild-linux-ppc64le: 0.13.12
|
||||
esbuild-netbsd-64: 0.13.12
|
||||
esbuild-openbsd-64: 0.13.12
|
||||
esbuild-sunos-64: 0.13.12
|
||||
esbuild-windows-32: 0.13.12
|
||||
esbuild-windows-64: 0.13.12
|
||||
esbuild-windows-arm64: 0.13.12
|
||||
esbuild-android-arm64: 0.13.13
|
||||
esbuild-darwin-64: 0.13.13
|
||||
esbuild-darwin-arm64: 0.13.13
|
||||
esbuild-freebsd-64: 0.13.13
|
||||
esbuild-freebsd-arm64: 0.13.13
|
||||
esbuild-linux-32: 0.13.13
|
||||
esbuild-linux-64: 0.13.13
|
||||
esbuild-linux-arm: 0.13.13
|
||||
esbuild-linux-arm64: 0.13.13
|
||||
esbuild-linux-mips64le: 0.13.13
|
||||
esbuild-linux-ppc64le: 0.13.13
|
||||
esbuild-netbsd-64: 0.13.13
|
||||
esbuild-openbsd-64: 0.13.13
|
||||
esbuild-sunos-64: 0.13.13
|
||||
esbuild-windows-32: 0.13.13
|
||||
esbuild-windows-64: 0.13.13
|
||||
esbuild-windows-arm64: 0.13.13
|
||||
dev: true
|
||||
|
||||
/escape-string-regexp/4.0.0:
|
||||
@@ -685,8 +670,8 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/eslint-visitor-keys/3.0.0:
|
||||
resolution: {integrity: sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==}
|
||||
/eslint-visitor-keys/3.1.0:
|
||||
resolution: {integrity: sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
@@ -706,7 +691,7 @@ packages:
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 6.0.0
|
||||
eslint-utils: 3.0.0_eslint@8.2.0
|
||||
eslint-visitor-keys: 3.0.0
|
||||
eslint-visitor-keys: 3.1.0
|
||||
espree: 9.0.0
|
||||
esquery: 1.4.0
|
||||
esutils: 2.0.3
|
||||
@@ -743,7 +728,7 @@ packages:
|
||||
dependencies:
|
||||
acorn: 8.5.0
|
||||
acorn-jsx: 5.3.2_acorn@8.5.0
|
||||
eslint-visitor-keys: 3.0.0
|
||||
eslint-visitor-keys: 3.1.0
|
||||
dev: true
|
||||
|
||||
/esquery/1.4.0:
|
||||
@@ -826,12 +811,12 @@ packages:
|
||||
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
dependencies:
|
||||
flatted: 3.2.2
|
||||
flatted: 3.2.4
|
||||
rimraf: 3.0.2
|
||||
dev: true
|
||||
|
||||
/flatted/3.2.2:
|
||||
resolution: {integrity: sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==}
|
||||
/flatted/3.2.4:
|
||||
resolution: {integrity: sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==}
|
||||
dev: true
|
||||
|
||||
/focus-visible/5.2.0:
|
||||
@@ -1437,6 +1422,11 @@ packages:
|
||||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/swell-node/4.0.6:
|
||||
resolution: {integrity: sha512-9eAjxse63TL2J3R7RdyD3VoykSffkY/z4jpIpwqjGUhbJYhpqXwbAive2U+6dvdqxGdjovBM8siTeld2Ud9LVw==}
|
||||
engines: {node: '>= v12.21.0'}
|
||||
dev: true
|
||||
|
||||
/text-table/0.2.0:
|
||||
resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=}
|
||||
dev: true
|
||||
@@ -1501,8 +1491,8 @@ packages:
|
||||
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
|
||||
dev: true
|
||||
|
||||
/vite/2.6.13_sass@1.43.4:
|
||||
resolution: {integrity: sha512-+tGZ1OxozRirTudl4M3N3UTNJOlxdVo/qBl2IlDEy/ZpTFcskp+k5ncNjayR3bRYTCbqSOFz2JWGN1UmuDMScA==}
|
||||
/vite/2.6.14_sass@1.43.4:
|
||||
resolution: {integrity: sha512-2HA9xGyi+EhY2MXo0+A2dRsqsAG3eFNEVIo12olkWhOmc8LfiM+eMdrXf+Ruje9gdXgvSqjLI9freec1RUM5EA==}
|
||||
engines: {node: '>=12.2.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -1517,7 +1507,7 @@ packages:
|
||||
stylus:
|
||||
optional: true
|
||||
dependencies:
|
||||
esbuild: 0.13.12
|
||||
esbuild: 0.13.13
|
||||
postcss: 8.3.11
|
||||
resolve: 1.20.0
|
||||
rollup: 2.59.0
|
||||
|
||||
19
src/components/SVGSprite.svelte
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$components/atoms/Icon.svelte'
|
||||
|
||||
export let icon: string
|
||||
export let alt: string
|
||||
export let label: string
|
||||
@@ -7,7 +9,7 @@
|
||||
|
||||
<a href={url} class="box-cta">
|
||||
<div class="icon">
|
||||
<img src={icon} alt={alt} width={48} height={48} loading="lazy">
|
||||
<Icon icon={icon} />
|
||||
</div>
|
||||
<span class="text-label">
|
||||
{label}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
href={url} class={classes}
|
||||
{rel} {target}
|
||||
sveltekit:prefetch={url && isExternal ? null : true}
|
||||
sveltekit:noscroll={isExternal ? null : true}
|
||||
disabled={disabled}
|
||||
on:click
|
||||
>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { cartOpen, cartAmount } from '$utils/store'
|
||||
import ButtonCircle from './ButtonCircle.svelte'
|
||||
import { scale } from 'svelte/transition'
|
||||
import { quartOut } from 'svelte/easing'
|
||||
import { cartOpen, cartAmount } from '$utils/stores/shop'
|
||||
// Components
|
||||
import Icon from '$components/atoms/Icon.svelte'
|
||||
import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
|
||||
const openCart = () => {
|
||||
$cartOpen = true
|
||||
@@ -9,10 +13,10 @@
|
||||
|
||||
<div class="button-cart">
|
||||
<ButtonCircle color="purple" on:click={openCart}>
|
||||
<img src="/images/icons/bag.svg" width={27} height={22} alt="Cart icon">
|
||||
<Icon icon="bag" label="Cart icon" />
|
||||
</ButtonCircle>
|
||||
|
||||
{#if $cartAmount > 0}
|
||||
<span class="quantity">{$cartAmount}</span>
|
||||
<span class="quantity" transition:scale={{ start: 0.6, duration: 400, easing: quartOut }}>{$cartAmount}</span>
|
||||
{/if}
|
||||
</div>
|
||||
12
src/components/atoms/Icon.svelte
Normal file
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
export let icon: string
|
||||
export let label: string = undefined
|
||||
|
||||
const classes = [
|
||||
$$props.class
|
||||
].join(' ').trim()
|
||||
</script>
|
||||
|
||||
<svg class={classes} aria-label={label}>
|
||||
<use xlink:href="#icon-{icon}" />
|
||||
</svg>
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { cartId, cartData } from '$utils/stores/shop'
|
||||
import { addToCart } from '$utils/functions/shop'
|
||||
import { capitalizeFirstLetter } from '$utils/functions'
|
||||
// Components
|
||||
import Button from '$components/atoms/Button.svelte'
|
||||
@@ -6,6 +8,7 @@
|
||||
import Carousel from '$components/organisms/Carousel.svelte'
|
||||
|
||||
export let product: any
|
||||
export let shopProduct: any
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,6 +20,7 @@
|
||||
lastPreviewPhoto = product.photos_preview[product.photos_preview.length - 1].directus_files_id
|
||||
}
|
||||
|
||||
// Images sizes
|
||||
const photosPreview = [
|
||||
{
|
||||
sizes: {
|
||||
@@ -62,11 +66,12 @@
|
||||
<div class="poster-layout__info">
|
||||
<dl>
|
||||
<dt>{capitalizeFirstLetter(product.type)}</dt>
|
||||
<dd>{product.name} – 30€</dd>
|
||||
<dd>{shopProduct.name} – {shopProduct.price}€</dd>
|
||||
</dl>
|
||||
<Button
|
||||
text="Add to cart"
|
||||
color="pinklight"
|
||||
on:click={() => addToCart($cartId, shopProduct)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
63
src/components/molecules/CartItem.svelte
Normal file
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
// Components
|
||||
import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
import Select from '$components/molecules/Select.svelte'
|
||||
|
||||
export let item: any
|
||||
export let index: number
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const quantityLimit = 5
|
||||
|
||||
|
||||
// When changing item quantity
|
||||
const updateQuantity = ({ detail }: any) => {
|
||||
dispatch('updatedQuantity', {
|
||||
id: item.id,
|
||||
quantity: Number(detail)
|
||||
})
|
||||
}
|
||||
|
||||
// When removing item
|
||||
const removeItem = () => {
|
||||
dispatch('removed', item.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="cart-item shadow-small">
|
||||
<div class="cart-item__left">
|
||||
<img src={item.product.images[0].file.url} width={200} height={300} alt={item.product.name}>
|
||||
</div>
|
||||
<div class="cart-item__right">
|
||||
<h3>Poster</h3>
|
||||
<p>{item.product.name} – {item.price_total}€</p>
|
||||
|
||||
{#if item && item.quantity}
|
||||
<Select
|
||||
name="sort" id="filter_sort"
|
||||
options={[...Array(item.quantity <= quantityLimit ? quantityLimit : item.quantity)].map((_, index) => {
|
||||
return {
|
||||
value: `${index + 1}`,
|
||||
name: `${index + 1}`,
|
||||
default: index === 0,
|
||||
selected: index + 1 === item.quantity,
|
||||
}
|
||||
})}
|
||||
on:change={updateQuantity}
|
||||
value={String(item.quantity)}
|
||||
>
|
||||
<span>Quantity:</span>
|
||||
</Select>
|
||||
{/if}
|
||||
|
||||
<ButtonCircle class="remove"
|
||||
size="tiny" color="gray"
|
||||
on:click={removeItem}
|
||||
>
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.81 1.104a.647.647 0 1 0-.914-.915L4 3.085 1.104.19a.647.647 0 1 0-.915.915L3.085 4 .19 6.896a.647.647 0 1 0 .915.915L4 4.915 6.896 7.81a.647.647 0 1 0 .915-.915L4.915 4 7.81 1.104Z" fill="#666"/>
|
||||
</svg>
|
||||
</ButtonCircle>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,19 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
// Components
|
||||
import Button from '$components/atoms/Button.svelte'
|
||||
import Image from '$components/atoms/Image.svelte'
|
||||
import { fly } from 'svelte/transition'
|
||||
import { quartOut } from 'svelte/easing'
|
||||
import { cartOpen } from '$utils/stores/shop'
|
||||
|
||||
const { locations, shop } = getContext('global')
|
||||
export let title: string
|
||||
export let name: string
|
||||
export let image: string
|
||||
|
||||
|
||||
const closeNotification = () => {
|
||||
// Open cart
|
||||
$cartOpen = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<aside class="notification-cart shadow-small">
|
||||
<div class="notification-cart shadow-small"
|
||||
on:click={closeNotification}
|
||||
transition:fly={{ y: 20, duration: 700, easing: quartOut }}
|
||||
>
|
||||
<div class="notification-cart__left">
|
||||
<img src="/images/issue-1.jpg" width={58} height={88} alt="">
|
||||
<img src={image} width={58} height={88} alt={title}>
|
||||
</div>
|
||||
<div class="notification-cart__right">
|
||||
<h3>Added to cart</h3>
|
||||
<p>Houses Of Melbourne</p>
|
||||
<h3>{title}</h3>
|
||||
<p>{name}</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
@@ -1,7 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { cartId } from '$utils/stores/shop'
|
||||
import { addToCart } from '$utils/functions/shop'
|
||||
// Components
|
||||
import Button from '$components/atoms/Button.svelte'
|
||||
import Image from '$components/atoms/Image.svelte'
|
||||
|
||||
export let product: any
|
||||
export let location: { name: string, slug: string }
|
||||
export let image: any
|
||||
</script>
|
||||
@@ -33,6 +37,7 @@
|
||||
text="Add to cart"
|
||||
color="pinklight"
|
||||
size="xsmall"
|
||||
on:click={() => addToCart($cartId, product)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,37 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
// Components
|
||||
import Select from './Select.svelte'
|
||||
import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
|
||||
const { locations, shop } = getContext('global')
|
||||
|
||||
</script>
|
||||
|
||||
<aside class="poster-cart">
|
||||
<div class="poster-cart__left">
|
||||
<img src="/images/issue-1.jpg" alt="">
|
||||
</div>
|
||||
<div class="poster-cart__right">
|
||||
<div class="remove">
|
||||
<ButtonCircle size="tiny" color="gray">
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.81 1.104a.647.647 0 1 0-.914-.915L4 3.085 1.104.19a.647.647 0 1 0-.915.915L3.085 4 .19 6.896a.647.647 0 1 0 .915.915L4 4.915 6.896 7.81a.647.647 0 1 0 .915-.915L4.915 4 7.81 1.104Z" fill="#666"/>
|
||||
</svg>
|
||||
</ButtonCircle>
|
||||
</div>
|
||||
<h3>Poster</h3>
|
||||
<p>Houses Of Melbourne – 30€</p>
|
||||
<Select
|
||||
name="quantity" id="filter_country"
|
||||
options={[
|
||||
{
|
||||
value: '1',
|
||||
name: '1',
|
||||
default: true,
|
||||
selected: true,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
34
src/components/molecules/ShopLocationSwitcher.svelte
Normal file
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores'
|
||||
import { goto } from '$app/navigation'
|
||||
import { shopLocations } from '$utils/stores/shop'
|
||||
|
||||
export let isOver: boolean = false
|
||||
|
||||
const classes = [
|
||||
'shop-locationswitcher',
|
||||
isOver && 'is-over',
|
||||
$$props.class
|
||||
].join(' ').trim()
|
||||
|
||||
|
||||
// Quick location change
|
||||
const quickLocationChange = ({ target: { value }}: any) => {
|
||||
const newPath = $page.path.split('-')[0] + `-${value}`
|
||||
goto(newPath, { replaceState: true, noscroll: true, keepfocus: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
<dl class={classes}>
|
||||
<dt class="text-label">Shop your city</dt>
|
||||
<dd>
|
||||
<svg width="14" height="17" viewBox="0 0 14 17" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.1 11.18a.52.52 0 0 1-.92 0L3.55 6.05a3.46 3.46 0 1 1 6.19 0L7.1 11.18Zm1.2-6.62c0-.91-.73-1.63-1.65-1.63a1.63 1.63 0 1 0 0 3.26c.92 0 1.65-.72 1.65-1.63Zm-2.88 8.18.03.05-.08 1.1 2.98.95v-3.48l.96-1.8v5.28l2.98-.95-.41-5.65-2.31.84.62-1.14 1.91-.7c.3-.09.52.16.55.46l.61 6c.04.42-.21.8-.61.93l-3.82 1.21-3.96-1.21-4.25 1.21a.54.54 0 0 1-.45-.08.4.4 0 0 1-.17-.38l.95-6.53c.04-.16.15-.3.32-.35l1.78-.57.4.77-1.59.51-.82 5.63 3.4-.97.2-2.76c.47.95.78 1.63.78 1.63Z"/>
|
||||
</svg>
|
||||
<select on:change={quickLocationChange}>
|
||||
{#each $shopLocations as { name, slug }}
|
||||
<option value={slug} selected={slug === $page.params.name}>{name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</dd>
|
||||
</dl>
|
||||
@@ -1,25 +1,27 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$components/atoms/Icon.svelte'
|
||||
|
||||
let isOpen: boolean = false
|
||||
|
||||
// Links
|
||||
const links = [
|
||||
{
|
||||
"icon": "globe",
|
||||
"alt": "Globe",
|
||||
"url": "/locations",
|
||||
"text": "Discover locations"
|
||||
icon: "globe",
|
||||
iconLabel: "Globe icon",
|
||||
url: "/locations",
|
||||
text: "Discover locations"
|
||||
},
|
||||
{
|
||||
"icon": "photos",
|
||||
"alt": "Photos",
|
||||
"url": "/photos",
|
||||
"text": "Browse all photos"
|
||||
icon: "photos",
|
||||
iconLabel: "Photos icon",
|
||||
url: "/photos",
|
||||
text: "Browse all photos"
|
||||
},
|
||||
{
|
||||
"icon": "bag",
|
||||
"alt": "Shopping bag",
|
||||
"url": "/shop",
|
||||
"text": "Shop the prints"
|
||||
icon: "bag",
|
||||
iconLabel: "Bag icon",
|
||||
url: "/shop",
|
||||
text: "Shop the prints"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -35,10 +37,10 @@
|
||||
<aside class="switcher" class:is-open={isOpen}>
|
||||
<nav class="switcher__links">
|
||||
<ul>
|
||||
{#each links as { icon, alt, url, text }}
|
||||
{#each links as { icon, iconLabel, url, text }}
|
||||
<li>
|
||||
<a href={url} on:click={toggleSwitcher} sveltekit:prefetch>
|
||||
<img src="/images/icons/{icon}.svg" alt={alt} class="icon" width="32" height="32" loading="lazy">
|
||||
<Icon class="icon" icon={icon} label={iconLabel} />
|
||||
<span>{text}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
const handleArrowClick = () => {
|
||||
if (!carousel.clickAllowed()) return
|
||||
|
||||
// TODO: Clicking should also define arrowDirection? Can click without moving and won't change direction
|
||||
|
||||
// Click only if carousel if being dragged
|
||||
if (arrowDirection === 'prev') {
|
||||
carousel.scrollPrev()
|
||||
|
||||
@@ -1,49 +1,173 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import { fade, fly } from 'svelte/transition'
|
||||
import { cartOpen } from '$utils/store'
|
||||
import { quartOut } from 'svelte/easing'
|
||||
import { cartOpen, cartId, cartData, cartAmount, cartIsUpdating } from '$utils/stores/shop'
|
||||
// Components
|
||||
import Button from '$components/atoms/Button.svelte'
|
||||
import PosterCart from '$components/molecules/PosterCart.svelte'
|
||||
import Icon from '$components/atoms/Icon.svelte'
|
||||
import CartItem from '$components/molecules/CartItem.svelte'
|
||||
import ShopLocationSwitcher from '$components/molecules/ShopLocationSwitcher.svelte'
|
||||
|
||||
|
||||
onMount(async () => {
|
||||
// Cart already exists
|
||||
if ($cartId && $cartId !== 'null') {
|
||||
// Fetch stored cart
|
||||
const existantCart = await fetch('/api/swell', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
action: 'fetchCart',
|
||||
cartId: $cartId
|
||||
})
|
||||
})
|
||||
if (existantCart.ok) {
|
||||
const cart = await existantCart.json()
|
||||
$cartId = cart.id
|
||||
$cartData = cart
|
||||
// console.log('Fetched existant cart:', $cartId, $cartData)
|
||||
}
|
||||
}
|
||||
// Cart doesn't exists
|
||||
else {
|
||||
// Create a new cart and store it
|
||||
const newCart = await fetch('/api/swell', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
action: 'createCart'
|
||||
})
|
||||
})
|
||||
if (newCart.ok) {
|
||||
const cart = await newCart.json()
|
||||
$cartId = cart.id
|
||||
$cartData = cart
|
||||
// console.log('Created new cart:', localStorage.getItem('cartId'))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Closing the cart
|
||||
const handleCloseCart = () => {
|
||||
$cartOpen = false
|
||||
}
|
||||
|
||||
// Item quantity changed
|
||||
const changedQuantity = async ({ detail: { id, quantity } }) => {
|
||||
// Cart is now updating
|
||||
$cartIsUpdating = true
|
||||
|
||||
// Get updated cart
|
||||
const updatedCart = await fetch('/api/swell', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
action: 'updateCartItem',
|
||||
cartId: $cartId,
|
||||
productId: id,
|
||||
quantity,
|
||||
})
|
||||
})
|
||||
if (updatedCart.ok) {
|
||||
const cart = await updatedCart.json()
|
||||
$cartData = cart
|
||||
// Cart is updated
|
||||
$cartIsUpdating = false
|
||||
}
|
||||
}
|
||||
|
||||
// Item removed
|
||||
const removedItem = async ({ detail: id }) => {
|
||||
// Cart is now updating
|
||||
$cartIsUpdating = true
|
||||
|
||||
// Remove item from cart
|
||||
const updatedCart = await fetch('/api/swell', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
action: 'removeCartItem',
|
||||
cartId: $cartId,
|
||||
productId: id,
|
||||
})
|
||||
})
|
||||
if (updatedCart.ok) {
|
||||
const cart = await updatedCart.json()
|
||||
$cartData = cart
|
||||
// Cart is updated
|
||||
$cartIsUpdating = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<aside class="cart"
|
||||
transition:fly={{ x: 48, duration: 600, easing: quartOut }}
|
||||
>
|
||||
<header class="cart__heading">
|
||||
<h2>Cart</h2>
|
||||
<button class="text-label" on:click={handleCloseCart}>Close</button>
|
||||
</header>
|
||||
|
||||
<div class="cart__content">
|
||||
<PosterCart />
|
||||
<PosterCart />
|
||||
{#if $cartOpen}
|
||||
<div class="cart-switcher"
|
||||
in:fly={{ y: -24, duration: 1000, easing: quartOut }}
|
||||
out:fly={{ y: -24, duration: 1000, easing: quartOut }}
|
||||
>
|
||||
<ShopLocationSwitcher isOver={true} />
|
||||
</div>
|
||||
|
||||
<footer class="cart__total">
|
||||
<div class="cart__total--sum">
|
||||
<h3>Total</h3>
|
||||
<span>3 articles</span>
|
||||
<p>90€</p>
|
||||
</div>
|
||||
<div class="cart__total--checkout">
|
||||
<p>Shipping will be calculated from the delivery address during the checkout process</p>
|
||||
<Button
|
||||
text="Checkout"
|
||||
color="pink"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</footer>
|
||||
</aside>
|
||||
<aside class="cart shadow-box-dark"
|
||||
class:is-updating={$cartIsUpdating}
|
||||
transition:fly={{ x: 48, duration: 600, easing: quartOut }}
|
||||
>
|
||||
<header class="cart__heading">
|
||||
<h2>Cart</h2>
|
||||
<button class="text-label" on:click={handleCloseCart}>Close</button>
|
||||
</header>
|
||||
|
||||
<div class="cart-overlay"
|
||||
transition:fade={{ duration: 600, easing: quartOut }}
|
||||
on:click={handleCloseCart}
|
||||
/>
|
||||
<div class="cart__content">
|
||||
{#if $cartAmount > 0}
|
||||
{#each $cartData.items as item, index}
|
||||
<CartItem {item} {index}
|
||||
on:updatedQuantity={changedQuantity}
|
||||
on:removed={removedItem}
|
||||
/>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="cart__empty shadow-small">
|
||||
<div class="icon">
|
||||
<Icon icon="bag" label="Shopping bag icon" />
|
||||
</div>
|
||||
<p>Your cart is empty</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !$cartData || $cartIsUpdating}
|
||||
<div class="cart__update">
|
||||
<p>Updating…</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<footer class="cart__total">
|
||||
<div class="cart__total--sum">
|
||||
<h3>Total</h3>
|
||||
{#if $cartData}
|
||||
<span>{$cartAmount} item{$cartAmount > 1 ? 's' : ''}</span>
|
||||
<p>{$cartData.sub_total ? $cartData.sub_total : 0}€</p>
|
||||
{:else}
|
||||
<span>0 item</span>
|
||||
<p>0€</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="cart__total--checkout">
|
||||
<p>Shipping will be calculated from the delivery address during the checkout process</p>
|
||||
{#if $cartData && $cartAmount > 0 && $cartData.checkout_url}
|
||||
<div transition:fly={{ y: 8, duration: 600, easing: quartOut }}>
|
||||
<Button
|
||||
url={$cartData && $cartData.checkout_url}
|
||||
text="Checkout"
|
||||
color="pink"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</footer>
|
||||
</aside>
|
||||
|
||||
<div class="cart-overlay"
|
||||
transition:fade={{ duration: 600, easing: quartOut }}
|
||||
on:click={handleCloseCart}
|
||||
/>
|
||||
{/if}
|
||||
@@ -29,7 +29,7 @@
|
||||
<h3 class="title-medium">{shop.module_title}</h3>
|
||||
<p class="text-small">{shop.module_text}</p>
|
||||
{#if shop.enabled}
|
||||
<Button url="/shop" text="Shop" color="lightpink" />
|
||||
<Button url="/shop" text="Shop" color="pinklight" />
|
||||
{/if}
|
||||
<p class="detail">
|
||||
Posters available for {locationsWithPoster.join(', ').replace(/,(?!.*,)/gmi, ' and')}.
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores'
|
||||
import { getAssetUrlKey } from '$utils/helpers'
|
||||
import dayjs from 'dayjs'
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat.js'
|
||||
import { getAssetUrlKey } from '$utils/helpers'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
import Image from '$components/atoms/Image.svelte'
|
||||
import Icon from '$components/atoms/Icon.svelte'
|
||||
import IconArrow from '$components/atoms/IconArrow.svelte'
|
||||
import ButtonCircle from '$components/atoms/ButtonCircle.svelte'
|
||||
|
||||
@@ -20,13 +21,12 @@
|
||||
$: isLast = currentIndex === totalPhotos - 1
|
||||
$: isFirst = currentIndex === 0
|
||||
// Define current photo
|
||||
// $: currentPhoto = photos.find((photo: any) => photo.slug === $page.params.photo)
|
||||
$: currentPhoto = photos[photos.findIndex((photo: any) => photo.slug === $page.params.photo)]
|
||||
// Reactive photos showns
|
||||
$: shownPhotos = [
|
||||
// Reactive photos
|
||||
$: displayedPhotos = [
|
||||
...photos.filter((photo: any, index: number) => {
|
||||
// Grab the 4 prev and next photos depending the index
|
||||
console.log(index)
|
||||
// Grab the 4 prev and next photos depending on the index
|
||||
// console.log(index)
|
||||
// console.log(index >= currentIndex - 4 && index < currentIndex + 4)
|
||||
})
|
||||
]
|
||||
@@ -120,7 +120,7 @@
|
||||
<h1 class="title-medium">{currentPhoto.title}</h1>
|
||||
|
||||
<div class="detail text-date">
|
||||
<img src="/images/icons/map-pin.svg" width={16} height={18} alt="Map icon"><span>{location.name}, {location.country.name}</span> <span class="sep">·</span> <time datetime={dayjs(currentPhoto.date_taken).format('YYYY-MM-DD')}>{dayjs(currentPhoto.date_taken).format('MMMM, Do YYYY')}</time>
|
||||
<Icon class="icon" icon="map-pin" label="Map pin" /><span>{location.name}, {location.country.name}</span> <span class="sep">·</span> <time datetime={dayjs(currentPhoto.date_taken).format('YYYY-MM-DD')}>{dayjs(currentPhoto.date_taken).format('MMMM, Do YYYY')}</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
<Button url="/locations" text="Change location" class="shadow-small">
|
||||
<IconEarth />
|
||||
</Button>
|
||||
<Button url="/shop" text="Buy the poster" color="lightpink" class="shadow-small">
|
||||
<Button url="/shop/poster-{location.slug}" text="Buy the poster" color="pinklight" class="shadow-small">
|
||||
<!-- <IconEarth /> -->
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { setContext } from 'svelte'
|
||||
import '$utils/polyfills'
|
||||
// Components
|
||||
import SVGSprite from '$components/SVGSprite.svelte'
|
||||
import Switcher from '$components/molecules/Switcher.svelte'
|
||||
import Footer from '$components/organisms/Footer.svelte'
|
||||
|
||||
@@ -25,6 +26,8 @@
|
||||
<Footer />
|
||||
{/if}
|
||||
|
||||
<SVGSprite />
|
||||
|
||||
<script context="module" lang="ts">
|
||||
import { fetchAPI } from '$utils/api'
|
||||
|
||||
|
||||
74
src/routes/api/swell.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
getProducts,
|
||||
getProduct,
|
||||
createCart,
|
||||
fetchCart,
|
||||
addToCart,
|
||||
updateCartItem,
|
||||
removeCartItem,
|
||||
} from '$utils/functions/swell'
|
||||
|
||||
|
||||
// Block GET requests
|
||||
export async function get ({ body, query }) {
|
||||
return {
|
||||
status: 403,
|
||||
body: 'nope!'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* POST request
|
||||
*/
|
||||
export async function post ({ headers, query, body, params, ...rest }) {
|
||||
try {
|
||||
const bodyParsed = JSON.parse(Buffer.from(body).toString())
|
||||
const { action, cartId, productId } = bodyParsed
|
||||
let result = {}
|
||||
|
||||
if (bodyParsed) {
|
||||
switch (action) {
|
||||
case 'getProducts': {
|
||||
result = await getProducts(bodyParsed.category)
|
||||
break
|
||||
}
|
||||
case 'getProduct': {
|
||||
result = await getProduct(productId)
|
||||
break
|
||||
}
|
||||
case 'createCart': {
|
||||
result = await createCart()
|
||||
break
|
||||
}
|
||||
case 'fetchCart': {
|
||||
result = await fetchCart(cartId)
|
||||
break
|
||||
}
|
||||
case 'addToCart': {
|
||||
result = await addToCart(cartId, productId, bodyParsed.quantity)
|
||||
break
|
||||
}
|
||||
case 'updateCartItem': {
|
||||
result = await updateCartItem(cartId, productId, bodyParsed.quantity)
|
||||
break
|
||||
}
|
||||
case 'removeCartItem': {
|
||||
result = await removeCartItem(cartId, productId)
|
||||
break
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: result,
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
status: error.status || 500,
|
||||
body: error.message || error.text || `Can't fetch query`
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,19 +58,19 @@
|
||||
<div class="cards">
|
||||
<BoxCTA
|
||||
url="{path}#locations"
|
||||
icon="/images/icons/globe.svg"
|
||||
icon="globe"
|
||||
label="Discover locations"
|
||||
alt="Globe"
|
||||
/>
|
||||
<BoxCTA
|
||||
url="/photos"
|
||||
icon="/images/icons/photos.svg"
|
||||
icon="photos"
|
||||
label="Browse all photos"
|
||||
alt="Photos"
|
||||
/>
|
||||
<BoxCTA
|
||||
url="/shop"
|
||||
icon="/images/icons/bag.svg"
|
||||
icon="bag"
|
||||
label="Shop our products"
|
||||
alt="Shopping bag"
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import PosterLayout from '$components/layouts/PosterLayout.svelte'
|
||||
|
||||
export let product: any
|
||||
export let shopProduct: any
|
||||
</script>
|
||||
|
||||
<PosterLayout
|
||||
product={product}
|
||||
shopProduct={shopProduct}
|
||||
/>
|
||||
|
||||
<script context="module" lang="ts">
|
||||
export async function load ({ page, fetch, session, stuff }) {
|
||||
// Get content from stuff
|
||||
return {
|
||||
props: {
|
||||
product: stuff.product,
|
||||
shopProduct: stuff.shopProduct,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,31 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { cartOpen } from '$utils/store'
|
||||
import { onMount } from 'svelte'
|
||||
import { shopLocations, cartOpen, cartNotifications } from '$utils/stores/shop'
|
||||
// Components
|
||||
import Metas from '$components/Metas.svelte'
|
||||
import SiteTitle from '$components/atoms/SiteTitle.svelte'
|
||||
import Image from '$components/atoms/Image.svelte'
|
||||
import ButtonCart from '$components/atoms/ButtonCart.svelte'
|
||||
import PosterLayout from '$components/layouts/PosterLayout.svelte'
|
||||
import Poster from '$components/molecules/Poster.svelte'
|
||||
import NotificationCart from '$components/molecules/NotificationCart.svelte'
|
||||
import EmailForm from '$components/molecules/EmailForm.svelte'
|
||||
import ShopLocationSwitcher from '$components/molecules/ShopLocationSwitcher.svelte'
|
||||
import Cart from '$components/organisms/Cart.svelte'
|
||||
|
||||
export let shop: any
|
||||
export let locations: any
|
||||
export let posters: any
|
||||
export let product: any
|
||||
export let shopProducts: any
|
||||
|
||||
let navEl: HTMLElement, introEl: HTMLElement
|
||||
let introEl: HTMLElement
|
||||
let navObserver: IntersectionObserver
|
||||
let scrolledPastIntro = false
|
||||
|
||||
|
||||
// Locations with an existing poster product
|
||||
$shopLocations = locations.filter(({ slug }: any) => {
|
||||
if (posters.find((poster: any) => poster.location.slug === slug)) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
onMount(async () => {
|
||||
// Reveal the nav past the Intro
|
||||
navObserver = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
navEl.classList.toggle('is-visible', !entry.isIntersecting)
|
||||
scrolledPastIntro = !entry.isIntersecting
|
||||
})
|
||||
}, {
|
||||
threshold: 0,
|
||||
@@ -48,38 +58,42 @@
|
||||
/>
|
||||
|
||||
<main class="shop-page">
|
||||
{#if $cartOpen}
|
||||
<Cart />
|
||||
{/if}
|
||||
<section class="shop-page__intro">
|
||||
<Cart />
|
||||
|
||||
<section class="shop-page__intro" bind:this={introEl}>
|
||||
<a href="/" class="back">
|
||||
Back to Houses Of
|
||||
</a>
|
||||
<span class="shop-title">Shop</span>
|
||||
<div class="top container">
|
||||
<a href="/" class="back">
|
||||
<svg width="5" height="8" viewBox="0 0 5 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 1 1 4l3 3" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span>Back to site</span>
|
||||
</a>
|
||||
|
||||
<span class="shop-title">Shop</span>
|
||||
</div>
|
||||
|
||||
<SiteTitle
|
||||
variant="inline"
|
||||
/>
|
||||
|
||||
<header class="shop-page__header">
|
||||
<nav class="shop-page__nav">
|
||||
<div class="container">
|
||||
<p class="text-label">Shop your city</p>
|
||||
<nav>
|
||||
<ul>
|
||||
{#each locations as { name, slug }}
|
||||
{#if posters.find(poster => poster.location.slug === slug )}
|
||||
<li class:is-active={slug === product.location.slug}>
|
||||
<a href="/shop/poster-{slug}">{name}</a>
|
||||
</li>
|
||||
{/if}
|
||||
{#each $shopLocations as { name, slug }}
|
||||
<li class:is-active={slug === product.location.slug}>
|
||||
<a href="/shop/poster-{slug}" sveltekit:noscroll sveltekit:prefetch>
|
||||
{name}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<ButtonCart />
|
||||
</div>
|
||||
</header>
|
||||
</nav>
|
||||
|
||||
<Image
|
||||
id={shop.page_heroimage.id}
|
||||
@@ -93,55 +107,61 @@
|
||||
/>
|
||||
</section>
|
||||
|
||||
<nav class="shop-location" bind:this={navEl}>
|
||||
<dl class="shop-location__left">
|
||||
<dt class="text-label">shop your city</dt>
|
||||
<dd>
|
||||
<img src="/images/icons/pin.svg" alt="">
|
||||
<select name="" id="">
|
||||
<option value="melbourne">Melbourne</option>
|
||||
</select>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<nav class="shop-location"
|
||||
class:is-visible={scrolledPastIntro}
|
||||
class:is-overlaid={$cartOpen}
|
||||
>
|
||||
<ShopLocationSwitcher />
|
||||
<ButtonCart />
|
||||
|
||||
<div class="shop-location__notifications">
|
||||
<NotificationCart />
|
||||
{#each $cartNotifications as { id, title, name, image } (id)}
|
||||
<NotificationCart
|
||||
title={title}
|
||||
name={name}
|
||||
image={image}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="shop-page__about grid">
|
||||
<p class="description text-normal">{shop.about}</p>
|
||||
</section>
|
||||
|
||||
<PosterLayout {product} />
|
||||
|
||||
<section class="shop-page__posters grid">
|
||||
<h3>View all of our available posters</h3>
|
||||
<div class="set">
|
||||
{#each posters as { location, photos_product }}
|
||||
<Poster
|
||||
location={location}
|
||||
image={photos_product.length && photos_product[1].directus_files_id}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="subscribe">
|
||||
<p>Subscribe to be notified when new posters become available</p>
|
||||
<EmailForm />
|
||||
<div class="description grid text-normal">
|
||||
<div class="description__inner">
|
||||
{@html shop.about}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<slot />
|
||||
|
||||
{#if posters}
|
||||
<section class="shop-page__posters grid">
|
||||
<h3>View all of our available posters</h3>
|
||||
<div class="set">
|
||||
{#each posters as { location, photos_product }}
|
||||
<Poster
|
||||
location={location}
|
||||
image={photos_product.length && photos_product[1].directus_files_id}
|
||||
product={shopProducts.find(item => item.slug.includes(location.slug))}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="subscribe">
|
||||
<p>Subscribe to be notified when new posters become available</p>
|
||||
<EmailForm />
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<slot />
|
||||
|
||||
|
||||
<script context="module" lang="ts">
|
||||
import { fetchAPI } from '$utils/api'
|
||||
import { getRandomElement } from '$utils/functions'
|
||||
|
||||
export async function load ({ page, fetch, session, stuff }) {
|
||||
// Get content from API
|
||||
const res = await fetchAPI(`
|
||||
query {
|
||||
shop {
|
||||
@@ -166,6 +186,7 @@
|
||||
name
|
||||
slug
|
||||
}
|
||||
product_id
|
||||
photos_product {
|
||||
directus_files_id {
|
||||
id
|
||||
@@ -184,26 +205,58 @@
|
||||
|
||||
const { data } = res
|
||||
|
||||
|
||||
/**
|
||||
* Define product
|
||||
*/
|
||||
let product: any
|
||||
|
||||
if (!page.params.type && !page.params.name) {
|
||||
const productAPI = (!page.params.type && !page.params.name)
|
||||
// Get a random product
|
||||
product = data.posters[getRandomElement(data.posters)]
|
||||
} else {
|
||||
? data.posters[getRandomElement(data.posters)]
|
||||
// Get the current product from slug
|
||||
product = data.posters.find(({ location }: any) => location.slug === page.params.name)
|
||||
: data.posters.find(({ location }: any) => location.slug === page.params.name)
|
||||
|
||||
|
||||
/**
|
||||
* Get products data from Swell
|
||||
*/
|
||||
let shopProducts: any
|
||||
let shopProduct: any
|
||||
|
||||
const shopProductRes = await fetch('/api/swell', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
action: 'getProducts',
|
||||
category: 'posters',
|
||||
})
|
||||
})
|
||||
if (shopProductRes.ok) {
|
||||
// Set all products
|
||||
const { results } = await shopProductRes.json()
|
||||
if (results) {
|
||||
shopProducts = results
|
||||
}
|
||||
|
||||
// Define current product
|
||||
const product = results.find((result: any) => result.slug.includes(productAPI.location.slug))
|
||||
if (product) {
|
||||
shopProduct = product
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
props: {
|
||||
shop: data.shop,
|
||||
locations: data.location,
|
||||
posters: data.posters,
|
||||
product,
|
||||
product: productAPI,
|
||||
shopProducts,
|
||||
shopProduct,
|
||||
},
|
||||
stuff: {
|
||||
product: productAPI,
|
||||
shopProduct,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import PosterLayout from '$components/layouts/PosterLayout.svelte'
|
||||
|
||||
export let product: any
|
||||
export let shopProduct: any
|
||||
</script>
|
||||
|
||||
<PosterLayout
|
||||
product={product}
|
||||
shopProduct={shopProduct}
|
||||
/>
|
||||
|
||||
<script context="module" lang="ts">
|
||||
export async function load ({ page, fetch, session, stuff }) {
|
||||
// Get content from stuff
|
||||
return {
|
||||
props: {
|
||||
product: stuff.product,
|
||||
shopProduct: stuff.shopProduct,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -21,6 +21,7 @@
|
||||
.icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.6s var(--ease-quart);
|
||||
|
||||
@@ -30,11 +31,10 @@
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
img {
|
||||
svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
span {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
}
|
||||
|
||||
// Icon
|
||||
img {
|
||||
svg {
|
||||
color: #fff;
|
||||
display: block;
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
|
||||
@@ -118,4 +118,4 @@
|
||||
background-color: #D2D2D2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
text-decoration: none;
|
||||
transition: background-color 0.55s var(--ease-quart), color 0.55s var(--ease-quart);
|
||||
|
||||
@include bp (sm) {
|
||||
@include bp (md) {
|
||||
height: 48px;
|
||||
padding: 1px 24px 0;
|
||||
font-size: rem(18px);
|
||||
@@ -44,7 +44,7 @@
|
||||
height: 32px;
|
||||
padding: 0 6px;
|
||||
|
||||
@include bp (sm) {
|
||||
@include bp (md) {
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
font-size: rem(14px);
|
||||
@@ -56,7 +56,7 @@
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
|
||||
@include bp (sm) {
|
||||
@include bp (md) {
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
@@ -67,7 +67,7 @@
|
||||
height: 56px;
|
||||
font-size: rem(16px);
|
||||
|
||||
@include bp (sm) {
|
||||
@include bp (md) {
|
||||
height: 72px;
|
||||
height: 72px;
|
||||
padding: 0 40px;
|
||||
@@ -108,7 +108,6 @@
|
||||
}
|
||||
.text-split__line {
|
||||
&:last-child {
|
||||
color: $color-text;
|
||||
color: $color-primary-dark;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: 0.333em;
|
||||
margin: 0 16px 0 20px;
|
||||
margin: 0 10px 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 2 / span calc(var(--columns) - 1);
|
||||
text-align: center;
|
||||
margin-bottom: 48px;
|
||||
margin-bottom: 88px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
122
src/style/molecules/_cart-item.scss
Normal file
@@ -0,0 +1,122 @@
|
||||
// Cart item
|
||||
.cart-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
background: #fff;
|
||||
color: $color-gray;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.3s var(--ease-quart);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
// Left Image
|
||||
&__left {
|
||||
width: 100px;
|
||||
height: 150px;
|
||||
margin-right: 20px;
|
||||
|
||||
@include bp (sm) {
|
||||
width: 124px;
|
||||
height: 180px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
// Details
|
||||
&__right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// Poster Title
|
||||
h3 {
|
||||
font-family: $font-serif;
|
||||
color: $color-secondary;
|
||||
font-size: rem(20px);
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(28px);
|
||||
}
|
||||
}
|
||||
// Text
|
||||
p {
|
||||
max-width: 124px;
|
||||
margin: 8px 0 20px;
|
||||
font-size: rem(12px);
|
||||
line-height: 1.4;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(13px);
|
||||
}
|
||||
}
|
||||
// Select
|
||||
.select {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
margin-right: auto;
|
||||
padding: 0 12px;
|
||||
border: 1px solid rgba($color-lightgray, 0.3);
|
||||
border-radius: 100vh;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.4s var(--ease-quart);
|
||||
|
||||
span {
|
||||
display: block;
|
||||
font-size: rem(12px);
|
||||
color: $color-text;
|
||||
|
||||
& + span {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
select {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 8px;
|
||||
height: 5px;
|
||||
margin-left: 4px;
|
||||
color: red;
|
||||
background: url('data:image/svg+xml; utf8, <svg width="8" height="5" viewBox="0 0 8 5" fill="none" xmlns="http://www.w3.org/2000/svg">\
|
||||
<path d="m1 1 3 3 3-3" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>\
|
||||
</svg>');
|
||||
}
|
||||
|
||||
// Hover
|
||||
&:hover {
|
||||
border-color: $color-secondary-light;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove Icon
|
||||
.remove {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/style/molecules/_shop-locationswitcher.scss
Normal file
@@ -0,0 +1,43 @@
|
||||
.shop-locationswitcher {
|
||||
dt {
|
||||
color: $color-primary;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
font-size: rem(12px);
|
||||
}
|
||||
dd {
|
||||
color: $color-secondary;
|
||||
|
||||
svg {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 4px;
|
||||
|
||||
@include bp (sm) {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
select {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: rem(18px);
|
||||
color: $color-secondary;
|
||||
font-family: $font-serif;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(24px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overlaid
|
||||
&.is-over {
|
||||
dt {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@
|
||||
height: 32px;
|
||||
object-fit: contain;
|
||||
margin-right: 16px;
|
||||
color: $color-secondary-light;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
.cart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
padding: 20px 20px 24px;
|
||||
background-color: $color-cream;
|
||||
border-radius: 8px;
|
||||
padding: 20px 20px 24px;
|
||||
|
||||
@include bp (sm) {
|
||||
padding: 24px 32px;
|
||||
padding: 24px 32px 32px;
|
||||
}
|
||||
|
||||
// Heading
|
||||
@@ -14,8 +15,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #E1D0C0;
|
||||
|
||||
@include bp (sm) {
|
||||
@@ -33,98 +34,45 @@
|
||||
font-size: rem(48px);
|
||||
}
|
||||
}
|
||||
// Close
|
||||
a {
|
||||
// Close button
|
||||
button {
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
margin-top: 8px;
|
||||
margin-right: -12px;
|
||||
color: $color-gray;
|
||||
text-decoration: none;
|
||||
transition: color 0.4s;
|
||||
|
||||
&:hover {
|
||||
color: $color-secondary-bright;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Poster Cart
|
||||
.poster-cart {
|
||||
// Content
|
||||
&__content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
color: $color-gray;
|
||||
margin-bottom: 24px;
|
||||
border-radius: 6px;
|
||||
align-items: center;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
// Left Image
|
||||
&__left {
|
||||
margin-right: 20px;
|
||||
width: 100px;
|
||||
height: 150px;
|
||||
|
||||
@include bp (sm) {
|
||||
margin-right: 32px;
|
||||
width: 124px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&__right {
|
||||
// Remove Icon
|
||||
.remove {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
// Poster Title
|
||||
h3 {
|
||||
font-family: $font-serif;
|
||||
color: $color-secondary;
|
||||
font-size: rem(20px);
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(28px);
|
||||
}
|
||||
}
|
||||
// Text
|
||||
p {
|
||||
font-size: rem(12px);
|
||||
line-height: 1.4;
|
||||
max-width: 124px;
|
||||
margin: 8px 0 20px;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(13px);
|
||||
}
|
||||
}
|
||||
}
|
||||
flex: 1;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
// Total
|
||||
&__total {
|
||||
color: $color-gray;
|
||||
margin-bottom: 10px;
|
||||
margin-top: auto;
|
||||
padding-top: 20px;
|
||||
color: $color-gray;
|
||||
|
||||
// Sum
|
||||
&--sum {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 17px;
|
||||
border-bottom: 1px solid #E1D0C0;
|
||||
|
||||
@include bp (sm) {
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@@ -137,18 +85,18 @@
|
||||
}
|
||||
}
|
||||
span {
|
||||
font-size: rem(12px);
|
||||
margin-left: 20px;
|
||||
font-size: rem(12px);
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(13px);
|
||||
}
|
||||
}
|
||||
p {
|
||||
margin-left: auto;
|
||||
color: $color-secondary;
|
||||
font-family: $font-serif;
|
||||
font-size: rem(26px);
|
||||
margin-left: auto;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(32px);
|
||||
@@ -161,21 +109,107 @@
|
||||
display: flex;
|
||||
|
||||
p {
|
||||
max-width: 180px;
|
||||
margin-right: auto;
|
||||
font-size: rem(11px);
|
||||
line-height: 1.5;
|
||||
color: $color-gray;
|
||||
max-width: 180px;
|
||||
margin-right: auto;
|
||||
|
||||
@include bp (sm) {
|
||||
max-width: 304px;
|
||||
font-size: rem(12px);
|
||||
line-height: 1.6;
|
||||
max-width: 190px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Empty content
|
||||
&__empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 24px;
|
||||
font-size: rem(16px);
|
||||
font-weight: 200;
|
||||
background: #fff;
|
||||
color: $color-gray;
|
||||
border-radius: 6px;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(20px);
|
||||
}
|
||||
|
||||
// Icon
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 3.6em;
|
||||
height: 3.6em;
|
||||
margin-bottom: 16px;
|
||||
color: #FF6C89;
|
||||
background: $color-cream;
|
||||
border-radius: 100%;
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: 50%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updating message
|
||||
&__update {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: rem(20px);
|
||||
color: $color-secondary;
|
||||
transition: opacity 0.6s var(--ease-quart), transform 0.6s var(--ease-quart);
|
||||
opacity: 0;
|
||||
transform: translate3d(0, -8px, 0);
|
||||
}
|
||||
|
||||
|
||||
// Updating state
|
||||
&.is-updating {
|
||||
.cart-item {
|
||||
opacity: 0.1;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
.cart__update {
|
||||
opacity: 1;
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
// Location switcher
|
||||
&-switcher {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
will-change: transform, opacity;
|
||||
|
||||
@include bp (sm) {
|
||||
top: 32px;
|
||||
left: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay
|
||||
&-overlay {
|
||||
position: fixed;
|
||||
@@ -186,4 +220,4 @@
|
||||
height: 100%;
|
||||
background: rgba($color-primary, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,62 @@
|
||||
.shop-page {
|
||||
position: relative;
|
||||
|
||||
// Sections
|
||||
@import "shop/intro";
|
||||
@import "shop/about";
|
||||
@import "shop/posters";
|
||||
|
||||
|
||||
// Nav
|
||||
.shop-location {
|
||||
--inset: 20px;
|
||||
display: flex;
|
||||
position: fixed;
|
||||
z-index: 20;
|
||||
top: var(--inset);
|
||||
left: var(--inset);
|
||||
right: var(--inset);
|
||||
justify-content: space-between;
|
||||
transform: translate3d(0, -88px, 0);
|
||||
transition: transform 1s var(--ease-quart);
|
||||
transition-delay: 100ms;
|
||||
|
||||
@include bp (sm) {
|
||||
--inset: 32px;
|
||||
}
|
||||
|
||||
// Notifications
|
||||
&__notifications {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
top: 72px;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.7s var(--ease-quart);
|
||||
pointer-events: none;
|
||||
|
||||
& > * {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Visible state
|
||||
&.is-visible {
|
||||
transform: translate3d(0,0,0);
|
||||
.shop-location__notifications {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cart
|
||||
.cart {
|
||||
// display: flex;
|
||||
// display: none;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
z-index: 101;
|
||||
top: 72px;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
@@ -20,406 +70,4 @@
|
||||
max-height: 1000px;
|
||||
}
|
||||
}
|
||||
|
||||
// Nav
|
||||
.shop-location {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
z-index: 20;
|
||||
top: 18px;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
justify-content: space-between;
|
||||
transform: translate3d(0, -96px, 0);
|
||||
transition: transform 1s var(--ease-quart);
|
||||
transition-delay: 100ms;
|
||||
|
||||
@include bp (sm) {
|
||||
top: 32px;
|
||||
left: 32px;
|
||||
right: 32px;
|
||||
}
|
||||
|
||||
// Visible state
|
||||
&.is-visible {
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
|
||||
// Left
|
||||
&__left {
|
||||
dt {
|
||||
color: $color-primary;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
font-size: rem(12px);
|
||||
color: #fff;
|
||||
}
|
||||
dd {
|
||||
img {
|
||||
display: none;
|
||||
|
||||
@include bp (sm) {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: rem(18px);
|
||||
color: $color-secondary;
|
||||
font-family: $font-serif;
|
||||
appearance: none;
|
||||
line-height: 1;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(24px);
|
||||
}
|
||||
|
||||
|
||||
option {
|
||||
font-size: rem(16px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notifications
|
||||
&__notifications {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
top: 72px;
|
||||
right: 0;
|
||||
|
||||
& > * {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Intro
|
||||
&__intro {
|
||||
position: relative;
|
||||
z-index: 30;
|
||||
height: 100vh;
|
||||
min-height: 800px;
|
||||
overflow: hidden;
|
||||
|
||||
// Top Menu
|
||||
// Back
|
||||
.back {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
left: 32px;
|
||||
top: 32px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: rem(14px);
|
||||
color: $color-cream;
|
||||
max-width: 76px;
|
||||
|
||||
@include bp (sm) {
|
||||
max-width: none;
|
||||
font-size: rem(18px);
|
||||
}
|
||||
}
|
||||
// Shop
|
||||
.shop-title {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 32px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: rem(14px);
|
||||
color: $color-cream;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(18px);
|
||||
}
|
||||
}
|
||||
|
||||
// Site Title
|
||||
h1 {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
font-size: clamp(#{rem(88px)}, 10vw, #{rem(140px)});
|
||||
text-align: center;
|
||||
|
||||
span, strong {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
// Background Image
|
||||
picture {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: $color-primary-darker;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
|
||||
img {
|
||||
opacity: 0.55;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
// Gradient
|
||||
&:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, rgba(45, 4, 88, 0) 84.2%, #1E0538 100%);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation
|
||||
header {
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
@include bp (md) {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.container {
|
||||
@include bp (md) {
|
||||
display: grid;
|
||||
grid-template-columns: 15% auto 15%;
|
||||
align-items: flex-end;
|
||||
gap: 16px;
|
||||
height: 100%;
|
||||
padding: 0 32px 32px;
|
||||
}
|
||||
}
|
||||
|
||||
// Shop
|
||||
p {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
// Navigation
|
||||
nav {
|
||||
ul {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-size: rem(22px);
|
||||
font-family: $font-serif;
|
||||
color: $color-tertiary;
|
||||
margin: 0 10px;
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: $color-secondary;
|
||||
}
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(24px);
|
||||
margin: 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
// Active
|
||||
.is-active {
|
||||
a {
|
||||
color: $color-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// About
|
||||
&__about {
|
||||
display: flex;
|
||||
background-color: $color-cream;
|
||||
color: $color-text;
|
||||
padding: 100px 0 56px;
|
||||
|
||||
@include bp (sm) {
|
||||
display: grid;
|
||||
padding: 120px 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-weight: 300;
|
||||
max-width: 450px;
|
||||
margin-left: 20px;
|
||||
padding-right: 20px;
|
||||
font-size: rem(18px);
|
||||
white-space: pre-line;
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 3 / span 12;
|
||||
max-width: 560px;
|
||||
margin-left: 0;
|
||||
padding: 0;
|
||||
font-size: rem(22px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Posters
|
||||
&__posters {
|
||||
background-color: $color-primary-darker;
|
||||
padding: 0 20px 72px;
|
||||
border-bottom: solid 1px $color-primary-tertiary20 ;
|
||||
|
||||
@include bp (sm) {
|
||||
padding: 120px 0;
|
||||
}
|
||||
|
||||
// Title
|
||||
h3 {
|
||||
grid-column: 2 / span 6;
|
||||
font-family: $font-serif;
|
||||
color: $color-cream;
|
||||
font-size: rem(32px);
|
||||
line-height: 1.1;
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 3 / span 14;
|
||||
font-size: rem(48px);
|
||||
text-align: left;
|
||||
max-width: 360px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Posters Set
|
||||
.set {
|
||||
--gap: 24px;
|
||||
grid-column: 1 / span 8;
|
||||
display: block;
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 2 / span 22;
|
||||
}
|
||||
|
||||
@include bp (mob-lg) {
|
||||
--gap: 32px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--gap);
|
||||
}
|
||||
@include bp (md) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
@include bp (sd) {
|
||||
--gap: 48px;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
// Poster
|
||||
.poster {
|
||||
margin: 0 auto auto;
|
||||
|
||||
// 2 columns
|
||||
&:nth-child(2n + 1) {
|
||||
@include bp (sm, max) {
|
||||
margin-top: calc(var(--gap) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 3 columns
|
||||
&:nth-child(3n + 1) {
|
||||
@include bp (sd, max) {
|
||||
margin-top: calc(var(--gap) * 2);
|
||||
}
|
||||
}
|
||||
&:nth-child(3n + 2) {
|
||||
@include bp (sd, max) {
|
||||
margin-top: var(--gap);
|
||||
}
|
||||
}
|
||||
|
||||
// 4 columns
|
||||
&:nth-child(4n + 1) {
|
||||
@include bp (sd) {
|
||||
margin-top: 64px * 3;
|
||||
}
|
||||
}
|
||||
&:nth-child(4n + 2) {
|
||||
@include bp (sd) {
|
||||
margin-top: 64px * 2;
|
||||
}
|
||||
}
|
||||
&:nth-child(4n + 3) {
|
||||
@include bp (sd) {
|
||||
margin-top: 64px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe
|
||||
.subscribe {
|
||||
grid-column: 1 / span var(--columns);
|
||||
max-width: 380px;
|
||||
margin: 72px auto 0;
|
||||
text-align: center;
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 14 / span 8;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: rem(18px);
|
||||
color: $color-cream;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 24px;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(22px);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.newsletter-form {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
|
||||
@include bp (sm) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.newsletter-form__bottom {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,8 +185,10 @@
|
||||
}
|
||||
|
||||
// Icon
|
||||
img {
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
margin-top: -5px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
46
src/style/pages/shop/_about.scss
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
** Shop: About
|
||||
*/
|
||||
&__about {
|
||||
background-color: $color-cream;
|
||||
color: $color-text;
|
||||
|
||||
.description {
|
||||
--columns: 8;
|
||||
grid-column: 1 / span var(--columns);
|
||||
margin-bottom: -96px;
|
||||
padding: 88px 0 160px;
|
||||
font-size: rem(18px);
|
||||
font-weight: 200;
|
||||
white-space: pre-line;
|
||||
background: #fff;
|
||||
border-radius: 0 0 12px 0;
|
||||
box-shadow: 0 0 16px rgba(#000, 0.05);
|
||||
|
||||
@include bp (mob-lg) {
|
||||
--columns: 7;
|
||||
}
|
||||
@include bp (sm) {
|
||||
--columns: 17;
|
||||
margin-bottom: -160px;
|
||||
padding: 160px 0 274px;
|
||||
font-size: rem(22px);
|
||||
}
|
||||
@include bp (lg) {
|
||||
--columns: 13;
|
||||
}
|
||||
|
||||
&__inner {
|
||||
grid-column: 2 / span calc(var(--columns) - 2);
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 3 / span calc(var(--columns) - 5);
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 400;
|
||||
color: $color-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
201
src/style/pages/shop/_intro.scss
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
** Shop: Intro
|
||||
*/
|
||||
&__intro {
|
||||
position: relative;
|
||||
z-index: 30;
|
||||
height: 100vh;
|
||||
min-height: 600px;
|
||||
max-height: 1400px;
|
||||
overflow: hidden;
|
||||
|
||||
// Top Menu
|
||||
.top {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 20px;
|
||||
left: 0;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
|
||||
@include bp (sm) {
|
||||
top: 32px;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
// Back
|
||||
.back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-size: rem(14px);
|
||||
color: $color-cream;
|
||||
text-decoration: none;
|
||||
transition: color 0.4s var(--ease-quart);
|
||||
|
||||
@include bp (sm) {
|
||||
max-width: none;
|
||||
font-size: rem(18px);
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-right: 8px;
|
||||
transition: transform 0.4s var(--ease-quart);
|
||||
}
|
||||
|
||||
// Hover
|
||||
&:hover {
|
||||
color: $color-tertiary;
|
||||
|
||||
svg {
|
||||
transform: translate3d(-4px, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Shop
|
||||
.shop-title {
|
||||
font-size: rem(14px);
|
||||
color: $color-cream;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(18px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Site Title
|
||||
h1 {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
// font-size: clamp(#{rem(88px)}, 10vw, #{rem(140px)});
|
||||
text-shadow: 0px 8px 12px rgba(#000, 0.25);
|
||||
text-align: center;
|
||||
|
||||
|
||||
@include bp (sm) {
|
||||
top: clamp(#{rem(40px)}, 16vw, #{rem(144px)});
|
||||
left: 0;
|
||||
font-size: clamp(#{rem(40px)}, 8vw, #{rem(96px)});
|
||||
transform: none;
|
||||
}
|
||||
@include bp (md) {
|
||||
top: clamp(#{rem(40px)}, 10vw, #{rem(96px)});
|
||||
font-size: clamp(#{rem(40px)}, 6vw, #{rem(96px)});
|
||||
}
|
||||
|
||||
span, strong {
|
||||
color: #fff;
|
||||
}
|
||||
span {
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
// Background Image
|
||||
picture {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: $color-primary-darker;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
|
||||
img {
|
||||
opacity: 0.55;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
// Gradient
|
||||
&:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, rgba(45, 4, 88, 0) 84.2%, #1E0538 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Intro: Navigation
|
||||
&__nav {
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
.container {
|
||||
padding: 0 16px 24px;
|
||||
|
||||
@include bp (md) {
|
||||
display: grid;
|
||||
grid-template-columns: 15% auto 15%;
|
||||
align-items: flex-end;
|
||||
gap: 16px;
|
||||
height: 100%;
|
||||
padding: 0 32px 32px;
|
||||
}
|
||||
}
|
||||
|
||||
// Shop
|
||||
p {
|
||||
text-align: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@include bp (md) {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation
|
||||
nav {
|
||||
ul {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-size: rem(22px);
|
||||
font-family: $font-serif;
|
||||
color: $color-tertiary;
|
||||
margin: 0 10px;
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: $color-secondary;
|
||||
}
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(24px);
|
||||
margin: 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
// Active
|
||||
.is-active {
|
||||
a {
|
||||
color: $color-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
137
src/style/pages/shop/_posters.scss
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
** Shop: Posters
|
||||
*/
|
||||
&__posters {
|
||||
background-color: $color-primary-darker;
|
||||
padding: 0 20px 72px;
|
||||
border-bottom: solid 1px $color-primary-tertiary20 ;
|
||||
|
||||
@include bp (sm) {
|
||||
padding: 120px 0;
|
||||
}
|
||||
|
||||
// Title
|
||||
h3 {
|
||||
grid-column: 2 / span 6;
|
||||
font-family: $font-serif;
|
||||
color: $color-cream;
|
||||
font-size: rem(32px);
|
||||
line-height: 1.1;
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 3 / span 14;
|
||||
font-size: rem(48px);
|
||||
text-align: left;
|
||||
max-width: 360px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Posters Set
|
||||
.set {
|
||||
--gap: 24px;
|
||||
grid-column: 1 / span 8;
|
||||
display: block;
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 2 / span 22;
|
||||
}
|
||||
|
||||
@include bp (mob-lg) {
|
||||
--gap: 32px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: var(--gap);
|
||||
}
|
||||
@include bp (md) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
@include bp (sd) {
|
||||
--gap: 48px;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
// Poster
|
||||
.poster {
|
||||
margin: 0 auto auto;
|
||||
|
||||
// 2 columns
|
||||
&:nth-child(2n + 1) {
|
||||
@include bp (sm, max) {
|
||||
margin-top: calc(var(--gap) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 3 columns
|
||||
&:nth-child(3n + 1) {
|
||||
@include bp (sd, max) {
|
||||
margin-top: calc(var(--gap) * 2);
|
||||
}
|
||||
}
|
||||
&:nth-child(3n + 2) {
|
||||
@include bp (sd, max) {
|
||||
margin-top: var(--gap);
|
||||
}
|
||||
}
|
||||
|
||||
// 4 columns
|
||||
&:nth-child(4n + 1) {
|
||||
@include bp (sd) {
|
||||
margin-top: 64px * 3;
|
||||
}
|
||||
}
|
||||
&:nth-child(4n + 2) {
|
||||
@include bp (sd) {
|
||||
margin-top: 64px * 2;
|
||||
}
|
||||
}
|
||||
&:nth-child(4n + 3) {
|
||||
@include bp (sd) {
|
||||
margin-top: 64px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe
|
||||
.subscribe {
|
||||
grid-column: 1 / span var(--columns);
|
||||
max-width: 380px;
|
||||
margin: 72px auto 0;
|
||||
text-align: center;
|
||||
|
||||
@include bp (sm) {
|
||||
grid-column: 14 / span 8;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: rem(18px);
|
||||
color: $color-cream;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 24px;
|
||||
|
||||
@include bp (sm) {
|
||||
font-size: rem(22px);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.newsletter-form {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
|
||||
@include bp (sm) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.newsletter-form__bottom {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,8 @@
|
||||
@import "molecules/newsletter-form";
|
||||
@import "molecules/poster";
|
||||
@import "molecules/notification-cart";
|
||||
@import "molecules/cart-item";
|
||||
@import "molecules/shop-locationswitcher";
|
||||
|
||||
// Organisms
|
||||
@import "organisms/locations";
|
||||
@@ -72,4 +74,4 @@
|
||||
@import "pages/viewer-photo";
|
||||
|
||||
// Misc
|
||||
@import "animations";
|
||||
@import "animations";
|
||||
|
||||
@@ -93,7 +93,6 @@ export const clamp = (num: number, a: number, b: number) => {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a random element from an array
|
||||
*/
|
||||
40
src/utils/functions/notifications.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { cartNotifications } from '$utils/stores/shop'
|
||||
|
||||
interface Notification {
|
||||
title: string
|
||||
name: string
|
||||
image: string
|
||||
timeout?: number
|
||||
id?: number
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a notification
|
||||
*/
|
||||
export const addNotification = (notification: Notification) => {
|
||||
const id = Math.floor(Math.random() * 10000)
|
||||
|
||||
// Add ID and default timeout
|
||||
notification = {
|
||||
id,
|
||||
timeout: notification.timeout || 3000,
|
||||
...notification,
|
||||
}
|
||||
|
||||
// Push the notifications to the top of the list of notificationss
|
||||
cartNotifications.update((all) => [notification, ...all])
|
||||
|
||||
// If notification is dismissible, dismiss it after an amount of time (timeout)
|
||||
if (notification.timeout) {
|
||||
setTimeout(() => dismissNotification(id), notification.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dismiss a notification
|
||||
*/
|
||||
export const dismissNotification = (id: number) => {
|
||||
cartNotifications.update((all) => all.filter((t) => t.id !== id))
|
||||
}
|
||||
34
src/utils/functions/shop.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { addNotification } from '$utils/functions/notifications'
|
||||
import { cartData } from '$utils/stores/shop'
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a product to a cart
|
||||
*/
|
||||
export const addToCart = async (cartId: string, product: any, quantity: number = 1) => {
|
||||
if (cartId && cartId !== 'null') {
|
||||
const updatedCart = await fetch('/api/swell', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
action: 'addToCart',
|
||||
cartId,
|
||||
productId: product.id,
|
||||
quantity,
|
||||
})
|
||||
})
|
||||
if (updatedCart.ok) {
|
||||
const newCart = await updatedCart.json()
|
||||
cartData.set(newCart)
|
||||
|
||||
// Show notification
|
||||
addNotification({
|
||||
title: 'Added to cart',
|
||||
name: `${product.name} - x1`,
|
||||
image: product.images[0].file.url,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.log('No active cart')
|
||||
}
|
||||
}
|
||||
156
src/utils/functions/swell.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import swell from 'swell-node'
|
||||
|
||||
// Init Swell
|
||||
swell.init(import.meta.env.VITE_SWELL_STORE_ID, import.meta.env.VITE_SWELL_API_TOKEN)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fetch products
|
||||
*/
|
||||
export const getProducts = async (category?: string, limit: number = 25, page: number = 1) => {
|
||||
const products = await swell.get('/products', {
|
||||
where: {
|
||||
active: true,
|
||||
},
|
||||
category,
|
||||
limit,
|
||||
page
|
||||
})
|
||||
|
||||
if (products) {
|
||||
return products
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a product
|
||||
*/
|
||||
export const getProduct = async (id: string) => {
|
||||
const product = await swell.get('/products/{id}', {
|
||||
id
|
||||
})
|
||||
|
||||
if (product) {
|
||||
return product
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a cart
|
||||
*/
|
||||
export const createCart = async () => {
|
||||
const cart = await swell.post('/carts')
|
||||
|
||||
if (cart) {
|
||||
return cart
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve cart
|
||||
*/
|
||||
export const fetchCart = async (id: string) => {
|
||||
const cart = await swell.get('/carts/{id}', {
|
||||
id,
|
||||
expand: [
|
||||
'items.product',
|
||||
'items.variant',
|
||||
]
|
||||
})
|
||||
|
||||
if (cart) {
|
||||
return cart
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add product to cart
|
||||
*/
|
||||
export const addToCart = async (cartId: string, productId: string, quantity: number) => {
|
||||
// Fetch current cart data
|
||||
const currentCart = await fetchCart(cartId)
|
||||
|
||||
// Updated cart with new items
|
||||
const updatedCart = await swell.put('/carts/{id}', {
|
||||
id: cartId,
|
||||
items: [
|
||||
...currentCart.items || [],
|
||||
{
|
||||
product_id: productId,
|
||||
quantity,
|
||||
}
|
||||
],
|
||||
})
|
||||
if (updatedCart) {
|
||||
// Fetch latest cart with updates
|
||||
const cart = await fetchCart(cartId)
|
||||
if (cart) {
|
||||
return cart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update cart item
|
||||
*/
|
||||
export const updateCartItem = async (cartId: string, productId: string, quantity: number) => {
|
||||
// Fetch current cart data
|
||||
const currentCart = await fetchCart(cartId)
|
||||
|
||||
// Updated items with replacing new item quantity
|
||||
const updatedCartItems = currentCart.items.map((item: any) => {
|
||||
// Replace item quantity with selected one
|
||||
if (item.id === productId) {
|
||||
item.quantity = quantity
|
||||
}
|
||||
return item
|
||||
})
|
||||
|
||||
const updatedCart = await swell.put('/carts/{id}', {
|
||||
id: cartId,
|
||||
$set: {
|
||||
items: updatedCartItems,
|
||||
}
|
||||
})
|
||||
if (updatedCart) {
|
||||
// Fetch latest cart with updates
|
||||
const cart = await fetchCart(cartId)
|
||||
if (cart) {
|
||||
return cart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove cart item
|
||||
*/
|
||||
export const removeCartItem = async (cartId: string, productId: string) => {
|
||||
// Fetch current cart data
|
||||
const currentCart = await fetchCart(cartId)
|
||||
|
||||
// Updated items and remove selected item
|
||||
const updatedCartItems = [...currentCart.items.filter((item: any) => {
|
||||
return item.id !== productId
|
||||
})]
|
||||
|
||||
const updatedCart = await swell.put('/carts/{id}', {
|
||||
id: cartId,
|
||||
$set: {
|
||||
items: updatedCartItems,
|
||||
}
|
||||
})
|
||||
if (updatedCart) {
|
||||
// Fetch latest cart with updates
|
||||
const cart = await fetchCart(cartId)
|
||||
if (cart) {
|
||||
return cart
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
// Shop
|
||||
export const cartOpen = writable(false)
|
||||
export const cartAmount = writable(3)
|
||||
0
src/utils/stores/index.ts
Normal file
43
src/utils/stores/shop.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { writable, derived } from 'svelte/store'
|
||||
|
||||
|
||||
/**
|
||||
* Shop
|
||||
*/
|
||||
export const shopLocations = writable([])
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Cart
|
||||
*/
|
||||
/** Cart open state */
|
||||
export const cartOpen = writable(false)
|
||||
|
||||
/** Cart open state */
|
||||
export const cartId = writable(null)
|
||||
|
||||
// Write to localStorage when changing cartId
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
if (localStorage.getItem('cartId')) {
|
||||
// console.log('existant', localStorage.getItem('cartId'))
|
||||
cartId.set(localStorage.getItem('cartId'))
|
||||
}
|
||||
cartId.subscribe(value => localStorage.setItem('cartId', value))
|
||||
}
|
||||
|
||||
/** Raw Cart data */
|
||||
export const cartData = writable(null)
|
||||
|
||||
/** Cart data is being updated */
|
||||
export const cartIsUpdating = writable(false)
|
||||
|
||||
/** Amount of products present in cart */
|
||||
export const cartAmount = derived(cartData, ($cart) => {
|
||||
return $cart && $cart.item_quantity > 0 ? $cart.item_quantity : 0
|
||||
})
|
||||
|
||||
/**
|
||||
* Notifications
|
||||
*/
|
||||
export const cartNotifications = writable([])
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="#fff" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.57 13.193c.026-2.257.748-4.033 1.908-5.254A5.869 5.869 0 0 1 22.802 6.1c3.173.026 6.16 2.558 6.216 7.092h6.374c.925 0 1.655.783 1.586 1.702l-.037.485 4.13 27.93c.161 1.087-.903 1.95-1.938 1.571l-1.579-.576a.137.137 0 0 0-.121.014l-2.829 1.849a1.588 1.588 0 0 1-1.571 1.34H8.605a1.587 1.587 0 0 1-1.585-1.703l2.359-31.148a1.588 1.588 0 0 1 1.586-1.464h5.605Zm1.848 0c.026-1.858.614-3.16 1.403-3.99a4.017 4.017 0 0 1 2.965-1.262c2.073.017 4.332 1.644 4.384 5.252h-8.752Zm10.6 1.84h6.096l-.03.403h-.003l.002.01-2.29 30.221H8.884l2.32-30.634h5.366v5.233a2.111 2.111 0 0 0-1.197 1.901c0 1.166.95 2.112 2.12 2.112a2.111 2.111 0 0 0 .924-4.013v-5.233h8.755v5.233a2.111 2.111 0 0 0-1.197 1.901c0 1.166.95 2.112 2.12 2.112a2.111 2.111 0 0 0 .924-4.013v-5.233Zm5.767 28.815 1.523-20.115 2.839 19.193-.956-.349a1.991 1.991 0 0 0-1.772.203l-1.634 1.068Zm-16.789-21.68a.502.502 0 1 1-1.005-.003.502.502 0 0 1 1.005.002Zm10.099.5a.502.502 0 1 0 .002-1.004.502.502 0 0 0-.002 1.005Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="16" height="19" viewBox="0 0 16 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.51787 13.244C8.29206 13.6955 7.64287 13.6955 7.41707 13.244C7.41707 13.244 5.27191 9.01059 4.25578 7.03501C3.23965 5.05943 4.02998 2.49118 6.09046 1.44695C8.15094 0.402716 10.663 1.22117 11.6792 3.28141C12.3001 4.52321 12.2719 5.84967 11.6792 7.03501C11.0864 8.22036 8.51787 13.244 8.51787 13.244ZM9.95738 5.22877C9.95738 4.12809 9.08239 3.25319 7.98158 3.25319C6.88078 3.25319 6.00578 4.15631 6.00578 5.22877C6.00578 6.32945 6.88078 7.20435 7.98158 7.20435C9.08239 7.20435 9.95738 6.32945 9.95738 5.22877ZM6.50898 15.1335C6.51939 15.1526 6.53135 15.171 6.54476 15.1888L6.44888 16.5251L10.0169 17.6779V13.4566C10.3586 12.8149 10.7654 12.0515 11.1765 11.2816V17.6779L14.7445 16.5251L14.2539 9.68778L11.4829 10.7079C11.7389 10.2289 11.9905 9.75854 12.223 9.32507L14.5215 8.47535C14.8705 8.37166 15.1411 8.6724 15.178 9.03754L15.9127 16.3004C15.9636 16.8029 15.6579 17.2724 15.1805 17.425L10.5967 18.8903L5.84678 17.4195L0.740081 18.8903C0.561681 18.9301 0.360981 18.9102 0.204881 18.7909C0.0487812 18.6717 -0.0181187 18.5127 0.00418125 18.3338L1.14148 10.4232C1.18608 10.2244 1.31988 10.0654 1.52058 10.0058L3.65978 9.3043C3.81969 9.61333 3.9812 9.92989 4.14174 10.248L2.23418 10.8605L1.25298 17.6779L5.33388 16.5052L5.57503 13.1639C6.1283 14.315 6.50898 15.1335 6.50898 15.1335Z" fill="#FFE0C5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="#fff" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.018 11.594a3.209 3.209 0 0 1 2.856-3.527l29.012-3.05a3.209 3.209 0 0 1 3.523 2.825l5.566.32a3.209 3.209 0 0 1 3.016 3.437l-1.633 22.378a3.209 3.209 0 0 1-3.322 2.973l-.57-.021a3.209 3.209 0 0 1-2.86 3.484l-29.012 3.05a3.209 3.209 0 0 1-3.527-2.857l-3.05-29.012Zm38.255 23.493L37.603 9.69l5.267.302a1.375 1.375 0 0 1 1.293 1.473l-1.634 22.379a1.375 1.375 0 0 1-1.424 1.274l-.832-.031ZM5.065 9.89a1.375 1.375 0 0 0-1.224 1.511L5.9 30.982l4.415-.464-.596-5.668a2.06 2.06 0 0 1 .584-1.665l6.249-6.322c.36-.365.83-.57 1.312-.607a.915.915 0 0 1 .302-.085l5.84-.614c.302-.032.605.043.858.211l7.154 4.78c.793.53.725 1.61.074 2.112l.597 5.506 4.912-.516a.959.959 0 0 1 .047-.004L35.59 8.066a1.375 1.375 0 0 0-1.511-1.225L5.065 9.891Zm26.23 22.435-1.007-9.288-2.845.3.953 9.064c.008.076.011.152.011.227l2.887-.303Zm-8.187 2.704 2.82-.297h.01l6.082-.64a1.298 1.298 0 0 0 1.155-1.43l-.29-2.674 4.907-.515a.957.957 0 0 0 .047-.006l.8 7.61a1.375 1.375 0 0 1-1.224 1.511L8.402 41.64a1.375 1.375 0 0 1-1.511-1.224l-.8-7.61 4.414-.463.202 1.919a2.06 2.06 0 0 0 2.265 1.834l3.307-.347a2.061 2.061 0 0 0 1.834-2.266l-.42-4.003c-.073-.685.446-1.353 1.229-1.435.782-.082 1.429.462 1.5 1.148l.421 4.004a2.06 2.06 0 0 0 2.265 1.834ZM22.246 29l.42 4.004a.227.227 0 0 0 .25.202l3.454-.363a.228.228 0 0 0 .202-.25l-.99-9.415a.227.227 0 0 0-.1-.165l-7.34-4.89a.227.227 0 0 0-.287.029l-6.249 6.322a.227.227 0 0 0-.064.184l.989 9.411a.227.227 0 0 0 .25.203l3.306-.348a.227.227 0 0 0 .202-.25l-.42-4.003c-.184-1.749 1.128-3.269 2.861-3.45 1.733-.183 3.332 1.03 3.516 2.78Zm4.382-7.42 3.139-.33-5.762-3.85-3.143.331 5.637 3.756c.044.03.087.06.129.093Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 403 KiB |