Switched ImageCarousel to use SwiperJS React client-side component.

This commit is contained in:
David Ball 2024-07-15 06:57:34 -04:00
parent bdd48a51f7
commit 778285ddb2
9 changed files with 382 additions and 33 deletions

View File

@ -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;
// }
// }
});

172
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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 (
<Swiper
className='carousel-swiper'
allowSlideNext={true}
allowSlidePrev={true}
allowTouchMove={true}
autoplay={
{
delay: 3000,
disableOnInteraction: false,
pauseOnMouseEnter: true,
stopOnLastSlide: false,
}
}
effect={'coverflow'}
coverflowEffect={{
depth: 100,
rotate: 0,
scale: 1,
slideShadows: true,
modifier: 1,
stretch: 50,
}}
keyboard={{
enabled: true,
}}
shortSwipes={true}
longSwipes={true}
loop={true}
modules={[
SwiperNavigation,
SwiperPagination,
SwiperScrollbar,
SwiperA11y,
]}
navigation={{
enabled: true,
}}
pagination={{
clickable: true,
}}
scrollbar={{
draggable: true,
snapOnRelease: true,
}}
spaceBetween={200}
>
{props.images?.map((image) => (
<SwiperSlide><img className={'carousel-img'} src={image.src} alt={image.alt} title={image.title} /><div className="overlay"></div></SwiperSlide>
))}
</Swiper>
);
}

View File

@ -0,0 +1,10 @@
export interface Image {
src: string;
alt?: string;
title?: string;
}
export interface Props {
images: Image[],
showDots?: boolean,
}

View File

@ -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;
---
<!-- Slideshow container -->
<image-carousel>
<!-- Full-width images with number and caption text -->
<CarouselSwiper client:load images={images} />
<!-- <div class="swiper">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
<div class="swiper-scrollbar"></div>
</div>
<script>
class SwiperElement extends HTMLElement {
swiper: Swiper;
constructor() {
super();
}
}
</script> -->
<!-- <image-carousel>
{images?.map(image => (
<div class="carousel-slide carousel-fade">
<div class="carousel-number">
@ -51,9 +67,9 @@ let index = 1;
<span class="carousel-dot"></span>
))}
</div>
</image-carousel>
</image-carousel> -->
<script>
<!-- <script>
class ImageCarousel extends HTMLElement {
slideIndex: number;
idleAdvance?: any;
@ -296,4 +312,4 @@ image-carousel {
from {opacity: .1}
to {opacity: 1}
}
</style>
</style> -->

View File

@ -50,6 +50,7 @@ const { product } = Astro.props;
.product-card a img {
max-width: 100%;
border-radius: 7px;
background-color: #fff;
}
.product-card h2 {
margin: 0;

View File

@ -0,0 +1,64 @@
.carousel-swiper {
border-radius: 7px;;
--swiper-navigation-color: rgba(210, 20, 4, 0.5);
background-color: #fff;
/* background-color: coral; */
}
.carousel-swiper .overlay {
z-index: -1000;
position: absolute;
height: 100vmax;
width: 100%;
/* background-color: yellow; */
}
.carousel-swiper .carousel-img {
width: 100%;
}
.carousel-swiper .swiper-pagination-bullet {
background-color: rgba(210, 20, 4, 0.5);
}
/* .carousel-swiper .swiper-button-next {
margin: 0;
}
.carousel-swiper .swiper-button-next {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 3rem;
text-align: center;
} */
/* .carousel-swiper .swiper-button-next::after, .carousel-swiper .swiper-button-prev::after {
position: absolute;
left: 0%;
top: 0;
bottom: 0;
height: 10vmax;
width: 100%;
padding: 0;
margin: 0;
background: transparent;
background-color: #23262d;
}
.carousel-swiper .swiper-button-next:is(:hover, :focus-within)::before, .carousel-swiper .swiper-button-prev:is(:hover, :focus-within)::before,
.carousel-swiper .swiper-button-next:is(:hover, :focus-within)::after, .carousel-swiper .swiper-button-prev:is(:hover, :focus-within)::after {
background-color: #23262d;
background-image: none;
background-size: 400%;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
background-position: 0;
background-image: var(--accent-gradient);
background-size: 400%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
filter: invert(1);
mix-blend-mode: hue;
}
*/

View File

@ -1,3 +1,7 @@
{
"extends": "astro/tsconfigs/strict"
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}