feat(components): add carousel components

it's inmidiatly used in the image gallery
This commit is contained in:
Alexander Navarro 2023-12-17 20:58:58 -03:00
parent 4f903c3c8d
commit 8f15b45e9b
6 changed files with 162 additions and 83 deletions

View file

@ -67,6 +67,18 @@ video {
height: auto;
}
img.respect-width,
video.respect-width {
max-width: 100%;
height: auto;
}
img.respect-height,
video.respect-height {
max-height: 100%;
width: auto;
}
li:not(:last-child) {
margin-bottom: var(--prj-spacing-1);
}

View file

@ -0,0 +1,49 @@
.carousel {
height: 100%;
max-width: 90%;
margin: auto;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.container {
height: 100%;
overflow: hidden;
}
.content {
height: 100%;
display: flex;
flex-wrap: nowrap;
transition: transform 0.6s ease;
}
.item {
height: 100%;
width: 100%;
flex: 0 0 100%;
}
.itemContent {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.btnPrev,
.btnNext {
top: 50%;
z-index: 10;
}
.btnPrev {
left: 0;
}
.btnNext {
right: 0;
}

View file

@ -0,0 +1,66 @@
import React, { Children, useEffect, useRef, useState } from 'react';
import classes from './Carousel.module.css';
interface Props {
children: React.ReactNode;
}
export default function Carousel({ children }: Props): JSX.Element {
const [activeItem, setActiveItem] = useState(0);
const content = useRef<HTMLDivElement>(null);
const maxItems = Children.count(children) - 1;
useEffect(() => {
if (content.current == null) return;
const offset = content.current.clientWidth * activeItem;
// const offset = 100 * activeItem;
//
console.log(offset, activeItem, content.current.clientWidth);
content.current.style.transform = `translate3d(-${offset}px, 0px, 0px)`;
}, [activeItem]);
const offsetActiveItem = (offset: number): void => {
setActiveItem((prev) => {
const newActiveItem = prev + offset;
// Wrap on end in both sides
if (newActiveItem < 0) {
return maxItems;
}
if (newActiveItem > maxItems) {
return 0;
}
return newActiveItem;
});
};
return (
<div className={classes.carousel}>
<button
className={classes.btnPrev}
onClick={() => {
offsetActiveItem(-1);
}}
>
Prev
</button>
<div className={classes.container}>
<div ref={content} className={classes.content}>
{children}
</div>
</div>
<button
className={classes.btnNext}
onClick={() => {
offsetActiveItem(1);
}}
>
Next
</button>
</div>
);
}

View file

@ -0,0 +1,15 @@
import React from 'react';
import classes from './Carousel.module.css';
interface Props {
children: JSX.Element | JSX.Element[];
}
export default function CarouselItem({ children }: Props): JSX.Element {
return (
<div className={classes.item}>
<div className={classes.itemContent}>{children}</div>
</div>
);
}

View file

@ -1,95 +1,29 @@
import React from 'react';
import { MediaType, type Media } from './types';
import { type Media } from './types';
import classes from './Gallery.module.css';
import LightGallery from 'lightgallery/react';
// import styles
import 'lightgallery/css/lightgallery.css';
import 'lightgallery/css/lg-zoom.css';
import 'lightgallery/css/lg-thumbnail.css';
// import plugins if you need
import lgThumbnail from 'lightgallery/plugins/thumbnail';
import lgZoom from 'lightgallery/plugins/zoom';
import lgVideo from 'lightgallery/plugins/video';
import Carousel from '@components/Carousel/Carousel';
import CarouselItem from '@components/Carousel/CarouselItem';
interface Props {
items: Media[];
height: number;
}
// TODO: Replace the external library for custom module
export default function Gallery({ items }: Props): JSX.Element {
const getUrlFromGoogleDrive = (
originalUrl: string,
type: MediaType,
): string => {
// get the file id from the "share" link of google drive
const googleFileId = originalUrl.split('/').at(5);
if (googleFileId == null) {
return originalUrl;
}
switch (type) {
case MediaType.Image:
return `https://drive.google.com/uc?export=preview&id=${googleFileId}`;
case MediaType.Video:
return `https://drive.google.com/file/d/${googleFileId}/preview`;
default:
return '';
}
};
const renderItem = (item: Media, index: number): JSX.Element => {
// get the file id from the "share" link of google drive
const url = getUrlFromGoogleDrive(item.url, item.type);
let thumbnail;
if (item.thumbnail !== undefined) {
thumbnail = getUrlFromGoogleDrive(item.thumbnail, MediaType.Image);
} else {
thumbnail = url;
}
switch (item.type) {
case MediaType.Image:
export default function Gallery({ items, height = 500 }: Props): JSX.Element {
return (
<a key={index} href={url} className={classes.thumbnailItem}>
<img src={url} alt={item.alt} />
</a>
);
case MediaType.Video:
return (
<div
key={index}
className={classes.thumbnailItem}
data-src={url}
data-iframe={true}
id={item.url}
data-iframe-title="foo"
>
<img src={thumbnail} alt={item.alt} />
</div>
);
default:
break;
}
return <></>;
};
return (
<div>
<LightGallery
speed={500}
plugins={[lgThumbnail, lgZoom, lgVideo]}
elementClassNames={classes.thumbnailList}
>
{items.map(renderItem)}
</LightGallery>
<div style={{ height }}>
<Carousel>
{items.map((item, idx) => (
<CarouselItem key={idx}>
<img
className="respect-height"
src={item.thumbnail ?? item.url}
alt={item.alt}
/>
</CarouselItem>
))}
</Carousel>
</div>
);
}

View file

@ -6,6 +6,9 @@ media:
- type: image
url: /images/portafolio/observacion_clases/1696878771763.jpg
alt: this an image
- type: image
url: /images/portafolio/observacion_clases/1696878771763.jpg
alt: this an image
technologies:
- React
- NodeJS