From 778285ddb220e7b37980c0fd5fb785d13060efea Mon Sep 17 00:00:00 2001 From: David Ball Date: Mon, 15 Jul 2024 06:57:34 -0400 Subject: [PATCH] Switched ImageCarousel to use SwiperJS React client-side component. --- astro.config.mjs | 44 +++++-- package-lock.json | 172 +++++++++++++++++++++++++- package.json | 8 +- src/components/CarouselSwiper.tsx | 60 +++++++++ src/components/CarouselSwiperProps.ts | 10 ++ src/components/ImageCarousel.astro | 50 +++++--- src/components/ProductCard.astro | 1 + src/components/carousel-swiper.css | 64 ++++++++++ tsconfig.json | 6 +- 9 files changed, 382 insertions(+), 33 deletions(-) create mode 100644 src/components/CarouselSwiper.tsx create mode 100644 src/components/CarouselSwiperProps.ts create mode 100644 src/components/carousel-swiper.css diff --git a/astro.config.mjs b/astro.config.mjs index 5279626..124cd35 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -2,22 +2,40 @@ import { defineConfig } from 'astro/config'; import sitemap from '@astrojs/sitemap'; import { loadEnv } from "vite"; import { ALL_PRODUCTS } from './src/data/products'; -const { SITE_URL } = loadEnv(process.env.NODE_ENV, process.cwd(), ""); - +import react from "@astrojs/react"; +const { + SITE_URL +} = loadEnv(process.env.NODE_ENV, process.cwd(), ""); function generateRedirectsForAmazonProductIds() { - let redirects = {}; - for (let p = 0; p < ALL_PRODUCTS.length; p++) { - let product = ALL_PRODUCTS[p]; - if (product.amazonProductId && product.slug !== product.amazonProductId) { - redirects[`/${product.amazonProductId}`] = `/${product.slug}`; - } + let redirects = {}; + for (let p = 0; p < ALL_PRODUCTS.length; p++) { + let product = ALL_PRODUCTS[p]; + if (product.amazonProductId && product.slug !== product.amazonProductId) { + redirects[`/${product.amazonProductId}`] = `/${product.slug}`; } - return redirects; + } + return redirects; } + // https://astro.build/config export default defineConfig({ - site: SITE_URL||'http://localhost', - integrations: [sitemap()], - redirects: generateRedirectsForAmazonProductIds(), -}); + site: SITE_URL || 'http://localhost', + integrations: [sitemap(), react()], + redirects: generateRedirectsForAmazonProductIds() + // vite: { + // resolve: { + // alias: [ + // { find: /^swiper\/(.+)/, replacement: 'swiper/$1 '}, + // ], + // }, + // }, + // experimental: { + // resolveId: (id) => { + // if (id === 'swiper') { + // return './node_modules/swiper/swiper.esm.js'; + // } + // return null; + // } + // } +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 61b17be..87f2863 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,10 @@ "version": "0.0.1", "dependencies": { "@astrojs/check": "^0.8.1", + "@astrojs/react": "^3.6.0", "@astrojs/sitemap": "^3.1.6", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", "astro": "^4.11.5", "bootstrap": "^5.3.3", "cheerio": "*", @@ -18,7 +21,10 @@ "dotenv-expand": "^11.0.6", "markdown-it": "^14.0.0", "markdown-it-attrs": "^4.1.6", - "playwright": "*" + "playwright": "*", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "swiper": "^11.1.4" }, "devDependencies": { "@apify/tsconfig": "^0.1.0", @@ -220,6 +226,24 @@ "node": "^18.17.1 || ^20.3.0 || >=21.0.0" } }, + "node_modules/@astrojs/react": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@astrojs/react/-/react-3.6.0.tgz", + "integrity": "sha512-YGLxy5jCU9xKG/HAvYsWMcvrQVIhqVe0Sda3Z5UtP32rfXeG6B9J1xQvnx+kRSFTpIrj+7AwPSDSehLbCHJ56w==", + "dependencies": { + "@vitejs/plugin-react": "^4.3.1", + "ultrahtml": "^1.5.3" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0" + }, + "peerDependencies": { + "@types/react": "^17.0.50 || ^18.0.21", + "@types/react-dom": "^17.0.17 || ^18.0.6", + "react": "^17.0.2 || ^18.0.0 || ^19.0.0-beta", + "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0-beta" + } + }, "node_modules/@astrojs/sitemap": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.1.6.tgz", @@ -549,6 +573,34 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz", + "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz", + "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/template": { "version": "7.24.7", "license": "MIT", @@ -1736,6 +1788,28 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + }, + "node_modules/@types/react": { + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/sax": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", @@ -1765,6 +1839,24 @@ "version": "1.2.0", "license": "ISC" }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", + "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", + "dependencies": { + "@babel/core": "^7.24.5", + "@babel/plugin-transform-react-jsx-self": "^7.24.5", + "@babel/plugin-transform-react-jsx-source": "^7.24.1", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, "node_modules/@vladfrangu/async_event_emitter": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.0.tgz", @@ -2876,6 +2968,11 @@ "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", "license": "MIT" }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, "node_modules/csv-stringify": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.5.0.tgz", @@ -4858,6 +4955,17 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -6509,6 +6617,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -7003,6 +7142,14 @@ "node": ">=v12.22.7" } }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/section-matter": { "version": "1.0.0", "license": "MIT", @@ -7359,6 +7506,24 @@ "node": ">=4" } }, + "node_modules/swiper": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.1.4.tgz", + "integrity": "sha512-1n7kbYJB2dFEpUHRFszq7gys/ofIBrMNibwTiMvPHwneKND/t9kImnHt6CfGPScMHgI+dWMbGTycCKGMoOO1KA==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "engines": { + "node": ">= 4.7.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -7600,6 +7765,11 @@ "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", "license": "ISC" }, + "node_modules/ultrahtml": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.5.3.tgz", + "integrity": "sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/package.json b/package.json index 7bd1220..19b5470 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ }, "dependencies": { "@astrojs/check": "^0.8.1", + "@astrojs/react": "^3.6.0", "@astrojs/sitemap": "^3.1.6", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", "astro": "^4.11.5", "bootstrap": "^5.3.3", "cheerio": "*", @@ -20,7 +23,10 @@ "dotenv-expand": "^11.0.6", "markdown-it": "^14.0.0", "markdown-it-attrs": "^4.1.6", - "playwright": "*" + "playwright": "*", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "swiper": "^11.1.4" }, "devDependencies": { "@apify/tsconfig": "^0.1.0", diff --git a/src/components/CarouselSwiper.tsx b/src/components/CarouselSwiper.tsx new file mode 100644 index 0000000..b5a5574 --- /dev/null +++ b/src/components/CarouselSwiper.tsx @@ -0,0 +1,60 @@ +import { Swiper, SwiperSlide } from 'swiper/react'; +import { A11y as SwiperA11y, Navigation as SwiperNavigation, Pagination as SwiperPagination, Scrollbar as SwiperScrollbar } from 'swiper/modules'; +import 'swiper/swiper-bundle.css'; +import { type Props, type Image } from './CarouselSwiperProps'; +import './carousel-swiper.css'; + +export default function CarouselSwiper(props: Props) { + return ( + + {props.images?.map((image) => ( + {image.alt}
+ ))} +
+ ); +} \ No newline at end of file diff --git a/src/components/CarouselSwiperProps.ts b/src/components/CarouselSwiperProps.ts new file mode 100644 index 0000000..573a921 --- /dev/null +++ b/src/components/CarouselSwiperProps.ts @@ -0,0 +1,10 @@ +export interface Image { + src: string; + alt?: string; + title?: string; +} + +export interface Props { + images: Image[], + showDots?: boolean, +} \ No newline at end of file diff --git a/src/components/ImageCarousel.astro b/src/components/ImageCarousel.astro index d0dad80..14bc981 100644 --- a/src/components/ImageCarousel.astro +++ b/src/components/ImageCarousel.astro @@ -1,24 +1,40 @@ --- import type { Brand } from '../data/brands/brand'; - -export interface Image { - src: string; - alt?: string; - title?: string; -} - -interface Props { - images: Image[]; - showDots?: boolean; -} +// import { Swiper, SwiperSlide } from 'swiper/react'; +// import { Navigation as SwiperNavigation, Pagination as SwiperPagination } from 'swiper/modules'; +import CarouselSwiper from './CarouselSwiper'; +// import 'swiper/swiper-bundle.css'; +// SwiperCore.use([SwiperNavigation, SwiperPagination]) +import { type Props, type Image } from './CarouselSwiperProps'; const { images, showDots } = Astro.props; let index = 1; --- - - - + + + + + -