From 15d5d570325521e72f36ac3ff9d2e50f28a36424 Mon Sep 17 00:00:00 2001 From: Thomas Cristina de Carvalho Date: Mon, 8 Jan 2024 14:55:39 -0500 Subject: [PATCH] Add carousel section Override slide gap --- pnpm-lock.yaml | 284 +++------------- .../hydrogen-theme/app/components/Button.tsx | 56 +++ .../app/components/Carousel.tsx | 319 ++++++++++++++++++ .../app/components/icons/IconArrow.tsx | 39 +++ .../components/sections/CarouselSection.tsx | 84 +++++ .../app/components/sections/CtaSection.tsx | 2 +- .../app/lib/sectionRelsolver.ts | 5 + templates/hydrogen-theme/app/lib/utils.ts | 7 + templates/hydrogen-theme/app/qroq/sections.ts | 24 ++ templates/hydrogen-theme/package.json | 4 + .../hydrogen-theme/studio/schemas/index.ts | 2 + .../schemas/objects/global/sectionsList.ts | 3 + .../objects/sections/carouselSection.tsx | 94 ++++++ .../studio/static/assets/carouselSection.png | Bin 0 -> 21964 bytes 14 files changed, 692 insertions(+), 231 deletions(-) create mode 100644 templates/hydrogen-theme/app/components/Button.tsx create mode 100644 templates/hydrogen-theme/app/components/Carousel.tsx create mode 100644 templates/hydrogen-theme/app/components/icons/IconArrow.tsx create mode 100644 templates/hydrogen-theme/app/components/sections/CarouselSection.tsx create mode 100644 templates/hydrogen-theme/studio/schemas/objects/sections/carouselSection.tsx create mode 100644 templates/hydrogen-theme/studio/static/assets/carouselSection.png diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 031ec92..2c4d47f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,7 +67,7 @@ importers: version: 2.2.3 sanity: specifier: ^3.23.4 - version: 3.23.4(react-dom@18.2.0)(react@18.2.0)(styled-components@6.1.7) + version: 3.23.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)(styled-components@6.1.7) tar-fs: specifier: ^3.0.4 version: 3.0.4 @@ -105,6 +105,9 @@ importers: '@radix-ui/react-navigation-menu': specifier: ^1.1.4 version: 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': + specifier: ^1.0.2 + version: 1.0.2(@types/react@18.2.47)(react@18.2.0) '@remix-run/react': specifier: 2.4.1 version: 2.4.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3) @@ -141,6 +144,15 @@ importers: cva: specifier: 1.0.0-beta.1 version: 1.0.0-beta.1(typescript@5.3.3) + embla-carousel: + specifier: 8.0.0-rc19 + version: 8.0.0-rc19 + embla-carousel-autoplay: + specifier: 8.0.0-rc19 + version: 8.0.0-rc19(embla-carousel@8.0.0-rc19) + embla-carousel-react: + specifier: 8.0.0-rc19 + version: 8.0.0-rc19(react@18.2.0) framer-motion: specifier: ^10.17.9 version: 10.17.9(react-dom@18.2.0)(react@18.2.0) @@ -418,14 +430,6 @@ packages: - encoding dev: false - /@asamuzakjp/dom-selector@2.0.1: - resolution: {integrity: sha512-QJAJffmCiymkv6YyQ7voyQb5caCth6jzZsQncYCpHXrJ7RqdYG5y43+is8mnFcYubdOkr7cn1+na9BdFMxqw7w==} - dependencies: - bidi-js: 1.0.3 - css-tree: 2.3.1 - is-potential-custom-element-name: 1.0.1 - dev: false - /@ast-grep/napi-darwin-arm64@0.11.0: resolution: {integrity: sha512-IxY3b102tNNm+cYLngZvUKzM1fNKCpDDWz69Yt+QnKCZNx10Hvd7mqrYE2aXTtkaNalmg/p1n6kMA8KmshGgCA==} engines: {node: '>= 10'} @@ -1952,20 +1956,6 @@ packages: regenerator-runtime: 0.14.0 dev: false - /@babel/runtime@7.23.5: - resolution: {integrity: sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.0 - dev: false - - /@babel/runtime@7.23.6: - resolution: {integrity: sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.0 - dev: false - /@babel/runtime@7.23.7: resolution: {integrity: sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==} engines: {node: '>=6.9.0'} @@ -2091,7 +2081,7 @@ packages: /@changesets/apply-release-plan@6.1.4: resolution: {integrity: sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew==} dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.23.7 '@changesets/config': 2.3.1 '@changesets/get-version-range-type': 0.3.2 '@changesets/git': 2.0.0 @@ -2109,7 +2099,7 @@ packages: /@changesets/assemble-release-plan@5.2.4: resolution: {integrity: sha512-xJkWX+1/CUaOUWTguXEbCDTyWJFECEhmdtbkjhn5GVBGxdP/JwaHBIU9sW3FR6gD07UwZ7ovpiPclQZs+j+mvg==} dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.23.7 '@changesets/errors': 0.1.4 '@changesets/get-dependents-graph': 1.3.6 '@changesets/types': 5.2.1 @@ -2193,7 +2183,7 @@ packages: /@changesets/get-release-plan@3.0.17: resolution: {integrity: sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==} dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.23.7 '@changesets/assemble-release-plan': 5.2.4 '@changesets/config': 2.3.1 '@changesets/pre': 1.0.14 @@ -2209,7 +2199,7 @@ packages: /@changesets/git@2.0.0: resolution: {integrity: sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==} dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.23.7 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 @@ -2234,7 +2224,7 @@ packages: /@changesets/pre@1.0.14: resolution: {integrity: sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==} dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.23.7 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 @@ -2244,7 +2234,7 @@ packages: /@changesets/read@0.5.9: resolution: {integrity: sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==} dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.23.7 '@changesets/git': 2.0.0 '@changesets/logger': 0.0.5 '@changesets/parse': 0.3.16 @@ -2265,7 +2255,7 @@ packages: /@changesets/write@0.2.3: resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==} dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.23.7 '@changesets/types': 5.2.1 fs-extra: 7.0.1 human-id: 1.0.2 @@ -4368,7 +4358,7 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: graphql: 16.8.1 - tslib: 2.5.3 + tslib: 2.6.2 dev: false /@graphql-tools/prisma-loader@8.0.2(@types/node@20.10.7)(graphql@16.8.1): @@ -4413,7 +4403,7 @@ packages: '@ardatan/relay-compiler': 12.0.0(graphql@16.8.1) '@graphql-tools/utils': 10.0.12(graphql@16.8.1) graphql: 16.8.1 - tslib: 2.5.3 + tslib: 2.6.2 transitivePeerDependencies: - encoding - supports-color @@ -4794,7 +4784,7 @@ packages: /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@babel/runtime': 7.23.6 + '@babel/runtime': 7.23.7 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -4812,7 +4802,7 @@ packages: /@manypkg/get-packages@1.1.3: resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.23.7 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -10060,12 +10050,6 @@ packages: is-windows: 1.0.2 dev: false - /bidi-js@1.0.3: - resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} - dependencies: - require-from-string: 2.0.2 - dev: false - /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -11061,14 +11045,6 @@ packages: source-map: 0.6.1 dev: false - /css-tree@2.3.1: - resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - dependencies: - mdn-data: 2.0.30 - source-map-js: 1.0.2 - dev: false - /css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} @@ -11576,6 +11552,36 @@ packages: /electron-to-chromium@1.4.605: resolution: {integrity: sha512-V52j+P5z6cdRqTjPR/bYNxx7ETCHIkm5VIGuyCy3CMrfSnbEpIlLnk5oHmZo7gYvDfh2TfHeanB6rawyQ23ktg==} + /embla-carousel-autoplay@8.0.0-rc19(embla-carousel@8.0.0-rc19): + resolution: {integrity: sha512-c1pxsGHuWbYD3outH5y4L+kzg15smyHKFIDmXLaXlI6rCiizzf6hWMW0ZgxJLV4y4nUwDrYhM6TtzxvvOcsfUw==} + peerDependencies: + embla-carousel: 8.0.0-rc19 + dependencies: + embla-carousel: 8.0.0-rc19 + dev: false + + /embla-carousel-react@8.0.0-rc19(react@18.2.0): + resolution: {integrity: sha512-4BBj1HvlUqhWXFyDJOL/JbQ74jtekfdH646B1wQzM9QmWn6CEcbD/SmovKqc6B5jYTKaaGEGEEw7bpUJRajA8w==} + peerDependencies: + react: ^16.8.0 || ^17.0.1 || ^18.0.0 + dependencies: + embla-carousel: 8.0.0-rc19 + embla-carousel-reactive-utils: 8.0.0-rc19(embla-carousel@8.0.0-rc19) + react: 18.2.0 + dev: false + + /embla-carousel-reactive-utils@8.0.0-rc19(embla-carousel@8.0.0-rc19): + resolution: {integrity: sha512-ed9NppY0OxTtrSIwTCYNcMLlQfSNcNy8Zsw8uIG0te3qrhvQ2ePPsbcElK2SRAV8VMU6G7JQweQIb6amzYMDXA==} + peerDependencies: + embla-carousel: 8.0.0-rc19 + dependencies: + embla-carousel: 8.0.0-rc19 + dev: false + + /embla-carousel@8.0.0-rc19: + resolution: {integrity: sha512-PAChVyYoVZo8subkBN8LjZ7+0vk4CmVvMnxH0Y2ux76VUEUBl1wk5xDo8+MUhH5MXU6ZrgkBpMe++bKob1Z+2g==} + dev: false + /emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} dev: false @@ -14692,14 +14698,6 @@ packages: jsdom: 23.1.0 dev: false - /jsdom-global@3.0.2(jsdom@23.2.0): - resolution: {integrity: sha512-t1KMcBkz/pT5JrvcJbpUR2u/w1kO9jXctaaGJ0vZDzwFnIvGWw9IDSRciT83kIs8Bnw4qpOl8bQK08V01YgMPg==} - peerDependencies: - jsdom: '>=10.0.0' - dependencies: - jsdom: 23.2.0 - dev: false - /jsdom@23.1.0: resolution: {integrity: sha512-wRscu8dBFxi7O65Cvi0jFRDv0Qa7XEHPix8Qg/vlXHLAMQsRWV1EDeQHBermzXf4Dt7JtFgBLbva3iTcBZDXEQ==} engines: {node: '>=18'} @@ -14736,42 +14734,6 @@ packages: - utf-8-validate dev: false - /jsdom@23.2.0: - resolution: {integrity: sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==} - engines: {node: '>=18'} - peerDependencies: - canvas: ^2.11.2 - peerDependenciesMeta: - canvas: - optional: true - dependencies: - '@asamuzakjp/dom-selector': 2.0.1 - cssstyle: 4.0.1 - data-urls: 5.0.0 - decimal.js: 10.4.3 - form-data: 4.0.0 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.0 - https-proxy-agent: 7.0.2 - is-potential-custom-element-name: 1.0.1 - parse5: 7.1.2 - rrweb-cssom: 0.6.0 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.3 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 - ws: 8.16.0 - xml-name-validator: 5.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: false - /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -15506,10 +15468,6 @@ packages: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: false - /mdn-data@2.0.30: - resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} - dev: false - /media-query-parser@2.0.2: resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} dependencies: @@ -19142,140 +19100,6 @@ packages: dev: false /sanity@3.23.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)(styled-components@6.1.7): - resolution: {integrity: sha512-KUzyA4+1/bLiL24HQNBMYtOwzZOaKh/dGDZ/Y9FVPsUFr2iyr8Mer6T1e/UT/4Lw2iQB6myfF4IfnEaqdvuWJA==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - react: ^18 - react-dom: ^18 - styled-components: ^5.2 || ^6 - dependencies: - '@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0) - '@dnd-kit/modifiers': 6.0.1(@dnd-kit/core@6.1.0)(react@18.2.0) - '@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.1.0)(react@18.2.0) - '@dnd-kit/utilities': 3.2.2(react@18.2.0) - '@juggle/resize-observer': 3.4.0 - '@portabletext/react': 3.0.11(react@18.2.0) - '@rexxars/react-json-inspector': 8.0.1(react@18.2.0) - '@sanity/asset-utils': 1.3.0 - '@sanity/bifur-client': 0.3.1 - '@sanity/block-tools': 3.23.4 - '@sanity/cli': 3.23.4 - '@sanity/client': 6.10.0 - '@sanity/color': 3.0.0-beta.9 - '@sanity/diff': 3.23.4 - '@sanity/diff-match-patch': 3.1.1 - '@sanity/eventsource': 5.0.1 - '@sanity/export': 3.23.4 - '@sanity/generate-help-url': 3.0.0 - '@sanity/icons': 2.8.0(react@18.2.0) - '@sanity/image-url': 1.0.2 - '@sanity/import': 3.23.4 - '@sanity/logos': 2.1.4(@sanity/color@3.0.0-beta.9)(react@18.2.0) - '@sanity/mutator': 3.23.4 - '@sanity/portable-text-editor': 3.23.4(react-dom@18.2.0)(react@18.2.0)(rxjs@7.8.1)(styled-components@6.1.7) - '@sanity/presentation': 1.4.0(@sanity/client@6.10.0)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.23.4)(styled-components@6.1.7) - '@sanity/schema': 3.23.4 - '@sanity/telemetry': 0.7.5 - '@sanity/types': 3.23.4 - '@sanity/ui': 2.0.0-beta.13(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@6.1.7) - '@sanity/util': 3.23.4 - '@sanity/uuid': 3.0.2 - '@tanstack/react-virtual': 3.0.0-beta.54(react@18.2.0) - '@types/is-hotkey': 0.1.10 - '@types/react-copy-to-clipboard': 5.0.7 - '@types/react-is': 18.2.4 - '@types/shallow-equals': 1.0.3 - '@types/speakingurl': 13.0.6 - '@types/use-sync-external-store': 0.0.5 - '@vitejs/plugin-react': 4.2.1(vite@4.5.1) - chalk: 4.1.2 - chokidar: 3.5.3 - classnames: 2.5.1 - color2k: 2.0.3 - configstore: 5.0.1 - connect-history-api-fallback: 1.6.0 - console-table-printer: 2.12.0 - dataloader: 2.2.2 - date-fns: 2.30.0 - debug: 3.2.7 - esbuild: 0.19.11 - esbuild-register: 3.5.0(esbuild@0.19.11) - execa: 2.1.0 - exif-component: 1.0.1 - framer-motion: 10.17.9(react-dom@18.2.0)(react@18.2.0) - get-it: 8.4.4 - get-random-values-esm: 1.0.0 - groq-js: 1.3.0 - hashlru: 2.3.0 - history: 5.3.0 - i18next: 23.7.16 - import-fresh: 3.3.0 - is-hotkey: 0.1.8 - jsdom: 23.2.0 - jsdom-global: 3.0.2(jsdom@23.2.0) - json-lexer: 1.2.0 - json-reduce: 3.0.0 - json5: 2.2.3 - lodash: 4.17.21 - log-symbols: 2.2.0 - mendoza: 3.0.3 - module-alias: 2.2.3 - nano-pubsub: 2.0.1 - nanoid: 3.3.7 - observable-callback: 1.0.3(rxjs@7.8.1) - oneline: 1.0.3 - open: 8.4.2 - pirates: 4.0.6 - pluralize-esm: 9.0.5 - polished: 4.2.2 - pretty-ms: 7.0.1 - raf: 3.4.1 - react: 18.2.0 - react-copy-to-clipboard: 5.1.0(react@18.2.0) - react-dom: 18.2.0(react@18.2.0) - react-fast-compare: 3.2.2 - react-focus-lock: 2.9.6(@types/react@18.2.47)(react@18.2.0) - react-i18next: 13.5.0(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0) - react-is: 18.2.0 - react-refractor: 2.1.7(react@18.2.0) - react-rx: 2.1.3(react@18.2.0)(rxjs@7.8.1) - read-pkg-up: 7.0.1 - refractor: 3.6.0 - resolve-from: 5.0.0 - rimraf: 3.0.2 - rxjs: 7.8.1 - rxjs-etc: 10.6.2(rxjs@7.8.1) - rxjs-exhaustmap-with-trailing: 2.1.1(rxjs@7.8.1) - sanity-diff-patch: 3.0.2 - scroll-into-view-if-needed: 3.1.0 - semver: 7.5.4 - shallow-equals: 1.0.0 - speakingurl: 14.0.1 - styled-components: 6.1.7(react-dom@18.2.0)(react@18.2.0) - tar-fs: 2.1.1 - use-device-pixel-ratio: 1.1.2(react@18.2.0) - use-hot-module-reload: 1.0.3(react@18.2.0) - use-sync-external-store: 1.2.0(react@18.2.0) - vite: 4.5.1(@types/node@20.10.7) - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - '@types/react' - - bufferutil - - canvas - - less - - lightningcss - - react-native - - sass - - stylus - - sugarss - - supports-color - - terser - - utf-8-validate - dev: false - - /sanity@3.23.4(react-dom@18.2.0)(react@18.2.0)(styled-components@6.1.7): resolution: {integrity: sha512-KUzyA4+1/bLiL24HQNBMYtOwzZOaKh/dGDZ/Y9FVPsUFr2iyr8Mer6T1e/UT/4Lw2iQB6myfF4IfnEaqdvuWJA==} engines: {node: '>=18'} hasBin: true diff --git a/templates/hydrogen-theme/app/components/Button.tsx b/templates/hydrogen-theme/app/components/Button.tsx new file mode 100644 index 0000000..a354539 --- /dev/null +++ b/templates/hydrogen-theme/app/components/Button.tsx @@ -0,0 +1,56 @@ +import {Slot} from '@radix-ui/react-slot'; +import {type VariantProps, cva} from 'class-variance-authority'; +import * as React from 'react'; + +import {cn} from '~/lib/utils'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', + { + defaultVariants: { + size: 'default', + variant: 'default', + }, + variants: { + size: { + default: 'h-10 px-4 py-2', + icon: 'h-10 w-10', + lg: 'h-11 rounded-md px-8', + sm: 'h-9 rounded-md px-3', + }, + variant: { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: + 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + outline: + 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', + secondary: + 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + }, + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({asChild = false, className, size, variant, ...props}, ref) => { + const Comp = asChild ? Slot : 'button'; + return ( + + ); + }, +); +Button.displayName = 'Button'; + +export {Button, buttonVariants}; diff --git a/templates/hydrogen-theme/app/components/Carousel.tsx b/templates/hydrogen-theme/app/components/Carousel.tsx new file mode 100644 index 0000000..9e50cc4 --- /dev/null +++ b/templates/hydrogen-theme/app/components/Carousel.tsx @@ -0,0 +1,319 @@ +// https://ui.shadcn.com/docs/components/carousel +import type { + EmblaCarouselType as CarouselApi, + EmblaOptionsType as CarouselOptions, + EmblaPluginType as CarouselPlugin, +} from 'embla-carousel'; + +import {cx} from 'class-variance-authority'; +import useEmblaCarousel from 'embla-carousel-react'; +import { + createContext, + forwardRef, + useCallback, + useContext, + useEffect, + useState, +} from 'react'; + +import {cn} from '~/lib/utils'; + +import {Button} from './Button'; +import {IconArrow} from './icons/IconArrow'; + +type CarouselProps = { + opts?: CarouselOptions; + orientation?: 'horizontal' | 'vertical'; + plugins?: CarouselPlugin[]; + setApi?: (api: CarouselApi) => void; +}; + +type CarouselContextProps = { + api: ReturnType[1]; + canScrollNext: boolean; + canScrollPrev: boolean; + carouselRef: ReturnType[0]; + scrollNext: () => void; + scrollPrev: () => void; + scrollSnaps: number[]; + scrollTo: (index: number) => void; + selectedIndex: number; +} & CarouselProps; + +const CarouselContext = createContext(null); + +function useCarousel() { + const context = useContext(CarouselContext); + + if (!context) { + throw new Error('useCarousel must be used within a '); + } + + return context; +} + +const Carousel = forwardRef< + HTMLDivElement, + React.HTMLAttributes & CarouselProps +>( + ( + { + children, + className, + opts, + orientation = 'horizontal', + plugins, + setApi, + ...props + }, + ref, + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === 'horizontal' ? 'x' : 'y', + }, + plugins, + ); + const [canScrollPrev, setCanScrollPrev] = useState(false); + const [canScrollNext, setCanScrollNext] = useState(false); + const [selectedIndex, setSelectedIndex] = useState(0); + const [scrollSnaps, setScrollSnaps] = useState([]); + + const onInit = useCallback( + (api: CarouselApi) => setScrollSnaps(api.scrollSnapList()), + [], + ); + + const onSelect = useCallback((api: CarouselApi) => { + if (!api) { + return; + } + + setSelectedIndex(api.selectedScrollSnap()); + setCanScrollPrev(api.canScrollPrev()); + setCanScrollNext(api.canScrollNext()); + }, []); + + const scrollPrev = useCallback(() => { + api?.scrollPrev(); + }, [api]); + + const scrollNext = useCallback(() => { + api?.scrollNext(); + }, [api]); + + const scrollTo = useCallback( + (index: number) => api && api.scrollTo(index), + [api], + ); + + const handleKeyDown = useCallback( + (event: React.KeyboardEvent) => { + if (event.key === 'ArrowLeft') { + event.preventDefault(); + scrollPrev(); + } else if (event.key === 'ArrowRight') { + event.preventDefault(); + scrollNext(); + } + }, + [scrollPrev, scrollNext], + ); + + useEffect(() => { + if (!api || !setApi) { + return; + } + + setApi(api); + }, [api, setApi]); + + useEffect(() => { + if (!api) { + return; + } + + onInit(api); + onSelect(api); + api.on('reInit', onSelect); + api.on('select', onSelect); + + return () => { + api?.off('select', onSelect); + }; + }, [api, onSelect, onInit]); + + return ( + +
+ {children} +
+
+ ); + }, +); +Carousel.displayName = 'Carousel'; + +const CarouselContent = forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => { + const {carouselRef, orientation} = useCarousel(); + + return ( +
+
+
+ ); +}); +CarouselContent.displayName = 'CarouselContent'; + +const CarouselItem = forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => { + const {orientation} = useCarousel(); + + return ( +
+ ); +}); +CarouselItem.displayName = 'CarouselItem'; + +const CarouselPagination = forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({className, ...props}, ref) => { + const {scrollSnaps, scrollTo, selectedIndex} = useCarousel(); + + return ( +
+ {scrollSnaps.map((_, index) => ( + + ))} +
+ ); +}); +CarouselPagination.displayName = 'CarouselDots'; + +const CarouselPrevious = forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({className, size = 'icon', variant = 'outline', ...props}, ref) => { + const {canScrollPrev, orientation, scrollPrev} = useCarousel(); + + return ( + + ); +}); +CarouselPrevious.displayName = 'CarouselPrevious'; + +const CarouselNext = forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({className, size = 'icon', variant = 'outline', ...props}, ref) => { + const {canScrollNext, orientation, scrollNext} = useCarousel(); + + return ( + + ); +}); +CarouselNext.displayName = 'CarouselNext'; + +export { + Carousel, + type CarouselApi, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPagination, + CarouselPrevious, +}; diff --git a/templates/hydrogen-theme/app/components/icons/IconArrow.tsx b/templates/hydrogen-theme/app/components/icons/IconArrow.tsx new file mode 100644 index 0000000..d203c47 --- /dev/null +++ b/templates/hydrogen-theme/app/components/icons/IconArrow.tsx @@ -0,0 +1,39 @@ +import {cn} from '~/lib/utils'; + +import type {IconProps} from './Icon'; + +import {Icon} from './Icon'; + +export function IconArrow( + props: {direction: 'down' | 'left' | 'right' | 'up'} & IconProps, +) { + let rotate; + + switch (props.direction) { + case 'right': + rotate = 'rotate-0'; + break; + case 'left': + rotate = 'rotate-180'; + break; + case 'up': + rotate = '-rotate-90'; + break; + case 'down': + rotate = 'rotate-90'; + break; + default: + rotate = 'rotate-0'; + } + + return ( + + Arrow + + + ); +} diff --git a/templates/hydrogen-theme/app/components/sections/CarouselSection.tsx b/templates/hydrogen-theme/app/components/sections/CarouselSection.tsx new file mode 100644 index 0000000..670fb3d --- /dev/null +++ b/templates/hydrogen-theme/app/components/sections/CarouselSection.tsx @@ -0,0 +1,84 @@ +import type {TypeFromSelection} from 'groqd'; + +import Autoplay from 'embla-carousel-autoplay'; +import {useMemo} from 'react'; + +import type {SectionDefaultProps} from '~/lib/type'; +import type {CAROUSEL_SECTION_FRAGMENT} from '~/qroq/sections'; + +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPagination, + CarouselPrevious, +} from '../Carousel'; +import {SanityImage} from '../sanity/SanityImage'; + +type CarouselSectionProps = TypeFromSelection; + +export function CarouselSection( + props: SectionDefaultProps & {data: CarouselSectionProps}, +) { + const {data} = props; + const { + arrows, + autoplay, + loop, + pagination, + slides, + slidesPerViewDesktop, + title, + } = data; + + const slidesPerView = slidesPerViewDesktop ? 100 / slidesPerViewDesktop : 100; + const plugins = useMemo(() => (autoplay ? [Autoplay()] : []), [autoplay]); + const imageSizes = slidesPerViewDesktop + ? `(min-width: 1024px) ${ + 100 / slidesPerViewDesktop + }vw, (min-width: 768px) 50vw, 100vw` + : '(min-width: 768px) 50vw, 100vw'; + + return ( +
+

{title}

+
+ {slides && slides?.length > 0 && ( + +
+ + {slides.map((slide) => ( + + + + ))} + + {arrows && ( +
+ + +
+ )} +
+ {pagination && } +
+ )} +
+
+ ); +} diff --git a/templates/hydrogen-theme/app/components/sections/CtaSection.tsx b/templates/hydrogen-theme/app/components/sections/CtaSection.tsx index b4b90f8..683cc8a 100644 --- a/templates/hydrogen-theme/app/components/sections/CtaSection.tsx +++ b/templates/hydrogen-theme/app/components/sections/CtaSection.tsx @@ -13,7 +13,7 @@ export function CtaSection( return title ? (
-

{title}

+

{title}

) : null; } diff --git a/templates/hydrogen-theme/app/lib/sectionRelsolver.ts b/templates/hydrogen-theme/app/lib/sectionRelsolver.ts index 0532f08..c10d304 100644 --- a/templates/hydrogen-theme/app/lib/sectionRelsolver.ts +++ b/templates/hydrogen-theme/app/lib/sectionRelsolver.ts @@ -3,6 +3,11 @@ import {lazy} from 'react'; export const sections: { [key: string]: React.FC; } = { + carouselSection: lazy(() => + import('../components/sections/CarouselSection').then((module) => ({ + default: module.CarouselSection, + })), + ), collectionListSection: lazy(() => import('../components/sections/CollectionListSection').then((module) => ({ default: module.CollectionListSection, diff --git a/templates/hydrogen-theme/app/lib/utils.ts b/templates/hydrogen-theme/app/lib/utils.ts index 24e97bf..de95464 100644 --- a/templates/hydrogen-theme/app/lib/utils.ts +++ b/templates/hydrogen-theme/app/lib/utils.ts @@ -1,7 +1,10 @@ import type {SelectedOption} from '@shopify/hydrogen/storefront-api-types'; +import type {ClassValue} from 'class-variance-authority/types'; import {useLocation} from '@remix-run/react'; +import {cx} from 'class-variance-authority'; import {useMemo} from 'react'; +import {twMerge} from 'tailwind-merge'; import type {I18nLocale} from './type'; @@ -83,3 +86,7 @@ export function parseAsCurrency(value: number, locale: I18nLocale) { style: 'currency', }).format(value); } + +export function cn(...inputs: ClassValue[]) { + return twMerge(cx(inputs)); +} diff --git a/templates/hydrogen-theme/app/qroq/sections.ts b/templates/hydrogen-theme/app/qroq/sections.ts index bd49355..6e39491 100644 --- a/templates/hydrogen-theme/app/qroq/sections.ts +++ b/templates/hydrogen-theme/app/qroq/sections.ts @@ -173,12 +173,36 @@ export const CTA_SECTION_FRAGMENT = { title: [getIntValue('title'), q.string()], } satisfies Selection; +/* +|-------------------------------------------------------------------------- +| Carousel Section +|-------------------------------------------------------------------------- +*/ +export const CAROUSEL_SECTION_FRAGMENT = { + _key: q.string().nullable(), + _type: q.literal('carouselSection'), + arrows: q.boolean().nullable(), + autoplay: q.boolean().nullable(), + loop: q.boolean().nullable(), + pagination: q.boolean().nullable(), + settings: SECTION_SETTINGS_FRAGMENT, + slides: q('slides[]', {isArray: true}) + .grab({ + _key: q.string(), + image: q('image').grab(IMAGE_FRAGMENT).nullable(), + }) + .nullable(), + slidesPerViewDesktop: q.number().nullable(), + title: [getIntValue('title'), q.string()], +} satisfies Selection; + /* |-------------------------------------------------------------------------- | List of sections |-------------------------------------------------------------------------- */ export const SECTIONS_LIST_SELECTION = { + "_type == 'carouselSection'": CAROUSEL_SECTION_FRAGMENT, "_type == 'collectionListSection'": COLLECTION_LIST_SECTION_FRAGMENT, "_type == 'ctaSection'": CTA_SECTION_FRAGMENT, "_type == 'featuredCollectionSection'": FEATURED_COLLECTION_SECTION_FRAGMENT, diff --git a/templates/hydrogen-theme/package.json b/templates/hydrogen-theme/package.json index 86cd68b..64e4c66 100644 --- a/templates/hydrogen-theme/package.json +++ b/templates/hydrogen-theme/package.json @@ -21,6 +21,7 @@ "@portabletext/react": "^3.0.11", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-navigation-menu": "^1.1.4", + "@radix-ui/react-slot": "^1.0.2", "@remix-run/react": "2.4.1", "@sanity/client": "^6.10.0", "@sanity/image-url": "^1.0.2", @@ -33,6 +34,9 @@ "@vercel/stega": "^0.1.0", "class-variance-authority": "^0.7.0", "cva": "1.0.0-beta.1", + "embla-carousel": "8.0.0-rc19", + "embla-carousel-autoplay": "8.0.0-rc19", + "embla-carousel-react": "8.0.0-rc19", "framer-motion": "^10.17.9", "graphql": "^16.8.1", "graphql-tag": "^2.12.6", diff --git a/templates/hydrogen-theme/studio/schemas/index.ts b/templates/hydrogen-theme/studio/schemas/index.ts index bde7c8f..8f1d4f2 100644 --- a/templates/hydrogen-theme/studio/schemas/index.ts +++ b/templates/hydrogen-theme/studio/schemas/index.ts @@ -35,6 +35,7 @@ import featuredProductSection from './objects/sections/featuredProductSection'; import productInformationSection from './objects/sections/productInformationSection'; import productRichtext from './objects/global/productRichtext'; import relatedProductsSection from './objects/sections/relatedProductsSection'; +import carouselSection from './objects/sections/carouselSection'; const singletons = [home, header, footer, settings, themeContent]; const documents = [page, color, collection, product, blogPost, productVariant]; @@ -46,6 +47,7 @@ const sections = [ productInformationSection, ctaSection, relatedProductsSection, + carouselSection, ]; const footers = [socialLinksOnly]; const objects = [ diff --git a/templates/hydrogen-theme/studio/schemas/objects/global/sectionsList.ts b/templates/hydrogen-theme/studio/schemas/objects/global/sectionsList.ts index 23bd6f3..4c1fdb8 100644 --- a/templates/hydrogen-theme/studio/schemas/objects/global/sectionsList.ts +++ b/templates/hydrogen-theme/studio/schemas/objects/global/sectionsList.ts @@ -18,6 +18,9 @@ const globalSections = [ { type: 'ctaSection', }, + { + type: 'carouselSection', + }, ]; const pdpSections = [ diff --git a/templates/hydrogen-theme/studio/schemas/objects/sections/carouselSection.tsx b/templates/hydrogen-theme/studio/schemas/objects/sections/carouselSection.tsx new file mode 100644 index 0000000..92d7293 --- /dev/null +++ b/templates/hydrogen-theme/studio/schemas/objects/sections/carouselSection.tsx @@ -0,0 +1,94 @@ +import {GalleryHorizontal} from 'lucide-react'; +import {defineField} from 'sanity'; + +export default defineField({ + name: 'carouselSection', + title: 'Carousel', + type: 'object', + preview: { + select: { + title: 'title', + }, + prepare({title}: any) { + return { + title: title?.[0]?.value || 'Missing title', + media: GalleryHorizontal, + }; + }, + }, + fields: [ + defineField({ + name: 'title', + title: 'Title', + type: 'internationalizedArrayString', + }), + defineField({ + name: 'pagination', + title: 'Enable dot pagination', + type: 'boolean', + }), + defineField({ + name: 'arrows', + title: 'Enable arrows navigation', + type: 'boolean', + }), + defineField({ + name: 'autoplay', + title: 'Enable autoplay', + type: 'boolean', + }), + defineField({ + name: 'loop', + title: 'Enable infinite looping', + type: 'boolean', + }), + defineField({ + name: 'slidesPerViewDesktop', + type: 'rangeSlider', + options: { + min: 1, + max: 10, + }, + }), + defineField({ + name: 'slides', + type: 'array', + of: [ + defineField({ + name: 'slide', + type: 'object', + fields: [ + defineField({ + name: 'image', + type: 'image', + }), + ], + preview: { + select: { + media: 'image', + }, + prepare(context) { + console.log(context); + + return { + title: 'Slide', + media: context.media, + }; + }, + }, + }), + ], + }), + defineField({ + type: 'sectionSettings', + name: 'settings', + }), + ], + initialValue: { + pagination: true, + arrows: true, + autoplay: false, + loop: false, + slidesPerViewDesktop: 3, + }, +}); diff --git a/templates/hydrogen-theme/studio/static/assets/carouselSection.png b/templates/hydrogen-theme/studio/static/assets/carouselSection.png new file mode 100644 index 0000000000000000000000000000000000000000..0c25c17c2c1c6faa52c04f582de0d97bd1b171fb GIT binary patch literal 21964 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