Merge remote-tracking branch 'origin/main'

This commit is contained in:
2024-10-14 19:20:40 +02:00
7 changed files with 152 additions and 81 deletions

4
.github/codeql/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
name: "Default setup"
queries:
- javascript-security-and-quality.qls
- typescript-security-and-quality.qls

4
.github/codeql/codeql-config.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
name: "Default setup"
queries:
- javascript-security-and-quality.qls
- typescript-security-and-quality.qls

39
.github/workflows/deploy-test.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Deploy to server
on:
push:
branches:
- '**'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: recette
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies and build
run: |
npm ci
npm run build
- name: Copy build directory to server
uses: Dylan700/sftp-upload-action@v1.2.3
with:
server: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
password: ${{ secrets.SERVER_PASSWORD }}
port: ${{ secrets.SERVER_PORT }}
uploads: |
./dist => /var/www/modelec.club
dry-run: true
delete: true

View File

@@ -35,3 +35,4 @@ jobs:
port: ${{ secrets.SERVER_PORT }}
uploads: |
./dist => /var/www/modelec.club
delete: true

9
package-lock.json generated
View File

@@ -23,7 +23,7 @@
"eslint-plugin-react-refresh": "^0.4.9",
"globals": "^15.10.0",
"prettier": "^3.3.3",
"typescript": "^5.5.3",
"typescript": "^5.6.3",
"typescript-eslint": "^8.8.1",
"vite": "^5.4.9"
}
@@ -3113,11 +3113,10 @@
}
},
"node_modules/typescript": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"

View File

@@ -25,7 +25,7 @@
"eslint-plugin-react-refresh": "^0.4.9",
"globals": "^15.10.0",
"prettier": "^3.3.3",
"typescript": "^5.5.3",
"typescript": "^5.6.3",
"typescript-eslint": "^8.8.1",
"vite": "^5.4.9"
}

View File

@@ -1,92 +1,116 @@
import React from "react";
import { NavArrowLeft, NavArrowRight } from "iconoir-react";
import "./carousel.css";
import React from 'react';
import { NavArrowLeft, NavArrowRight } from 'iconoir-react';
import './carousel.css';
interface CarouselProps {
carousel: CarouselImageProps[];
carousel: CarouselImageProps[];
}
interface CarouselImageProps {
image: string;
title: string;
text: string;
link?: string;
alt?: string;
image: string;
title: string;
text: string;
link?: string;
alt?: string;
}
export const Carousel: React.FC<CarouselProps> = ({ carousel }) => {
const [currentIndex, setCurrentImage] = React.useState(0);
const [imagesSize, setImagesSize] = React.useState<number[]>([]);
const carouselSlider = React.useRef<HTMLDivElement>(null);
const [currentIndex, setCurrentImage] = React.useState(0);
const [imagesSize, setImagesSize] = React.useState<number[]>([]);
const carouselSlider = React.useRef<HTMLDivElement>(null);
const nextImage = () => {
setCurrentImage((currentIndex + 1) % carousel.length);
const nextImage = () => {
setCurrentImage((prevIndex) => (prevIndex + 1) % (carousel.length * 2));
};
const previousImage = () => {
setCurrentImage(
(prevIndex) =>
(prevIndex - 1 + carousel.length * 2) % (carousel.length * 2)
);
};
React.useEffect(() => {
const interval = setInterval(() => {
nextImage();
}, 3000);
if (carouselSlider.current) {
const translate = imagesSize.reduce(
(acc, size, index) => (index < currentIndex ? acc + size : acc),
0
);
const gap = 25; // TODO: Find a way to get this value (HARDCODED)
carouselSlider.current.style.transform = `translateX(-${translate + gap * currentIndex}px)`;
}
const previousImage = () => {
setCurrentImage((currentIndex - 1 + carousel.length) % carousel.length);
}
React.useEffect(() => {
const interval = setTimeout(() => {
nextImage();
}, 3000);
if (currentIndex >= carousel.length) {
setTimeout(() => {
setCurrentImage(0);
if (carouselSlider.current) {
const translate = imagesSize.reduce((acc, size, index) => index < currentIndex ? acc + size : acc, 0);
const gap = 25; // TODO: Find a way to get this value (HARDCODED)
carouselSlider.current.style.translate = `-${translate+gap*currentIndex}px`;
carouselSlider.current.style.transition = 'none';
carouselSlider.current.style.transform = 'translateX(0)';
}
return () => clearInterval(interval);
}, [currentIndex, imagesSize, carouselSlider]);
function handleImageLoad(i: number, event: React.SyntheticEvent<HTMLImageElement>) {
setImagesSize((old) => {
const newSizes = [...old];
newSizes[i] = (event.target as HTMLImageElement).clientWidth;
return newSizes;
});
}, 300);
} else {
if (carouselSlider.current) {
carouselSlider.current.style.transition = 'transform 0.3s ease';
}
}
return (
<div className={"carousel"}>
<div className={"carousel-wrapper"}>
<div className={"carousel-slider"} ref={carouselSlider}>
{
carousel.map((image, index) => (
<div key={index} className={'slide'}>
<img
key={index}
className={`slide-image ${index === currentIndex ? "active" : ""}`}
src={image.image}
alt={image.alt ?? "Image Carousel"}
onLoad={(e) => handleImageLoad(index, e)}
/>
<div className={'slide-content'}>
<h3 className={'slide-title'}>{image.title}</h3>
<p className={'slide-text'}>{image.text}</p>
</div>
</div>
))
}
</div>
</div>
<div className="carousel-bar">
<button className={"carousel-button button_left"} onClick={previousImage}><NavArrowLeft /></button>
{
carousel.map((_, index) => (
<button
key={index}
className={`carousel-dot ${index === currentIndex ? "dot_active" : ""}`}
onClick={() => setCurrentImage(index)}
/>
))
}
<button className={"carousel-button button_right"} onClick={nextImage}><NavArrowRight /></button>
return () => clearInterval(interval);
}, [currentIndex, imagesSize, carouselSlider]);
function handleImageLoad(
i: number,
event: React.SyntheticEvent<HTMLImageElement>
) {
setImagesSize((old) => {
const newSizes = [...old];
newSizes[i] = (event.target as HTMLImageElement).clientWidth;
return newSizes;
});
}
return (
<div className={'carousel'}>
<div className={'carousel-wrapper'}>
<div className={'carousel-slider'} ref={carouselSlider}>
{[...carousel, ...carousel].map((image, index) => (
<div key={index} className={'slide'}>
<img
key={index}
className={`slide-image ${index === currentIndex ? 'active' : ''}`}
src={image.image}
alt={image.alt ?? 'Image Carousel'}
onLoad={(e) => handleImageLoad(index, e)}
/>
<div className={'slide-content'}>
<h3 className={'slide-title'}>{image.title}</h3>
<p className={'slide-text'}>{image.text}</p>
</div>
</div>
))}
</div>
)
};
</div>
<div className="carousel-bar">
<button
className={'carousel-button button_left'}
onClick={previousImage}
>
<NavArrowLeft />
</button>
{carousel.map((_, index) => (
<button
key={index}
className={`carousel-dot ${index === currentIndex % carousel.length ? 'dot_active' : ''}`}
onClick={() => setCurrentImage(index)}
/>
))}
<button className={'carousel-button button_right'} onClick={nextImage}>
<NavArrowRight />
</button>
</div>
</div>
);
};