mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-05 07:24:28 +01:00
refactor: update the carousel library used for a better and lighter one
removed the old 2 used carousel libs react-multi-carousel and react-responsive-carousel, and instead installed and used react-embla-carousel, as it provides an easier api, variable items width support, and less deps.
This commit is contained in:
84
package-lock.json
generated
84
package-lock.json
generated
@@ -42,6 +42,7 @@
|
||||
"crypto": "^1.0.1",
|
||||
"dayjs": "^1.11.1",
|
||||
"dompurify": "^2.3.10",
|
||||
"embla-carousel-react": "^7.0.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"express": "^4.18.1",
|
||||
"express-session": "^1.17.3",
|
||||
@@ -76,10 +77,8 @@
|
||||
"react-loader-spinner": "^6.0.0-0",
|
||||
"react-loading-skeleton": "^3.1.0",
|
||||
"react-modal": "^3.15.1",
|
||||
"react-multi-carousel": "^2.8.0",
|
||||
"react-query": "^3.35.0",
|
||||
"react-redux": "^8.0.0",
|
||||
"react-responsive-carousel": "^3.2.23",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-select": "^5.3.2",
|
||||
@@ -21866,6 +21865,22 @@
|
||||
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/embla-carousel": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-7.0.0.tgz",
|
||||
"integrity": "sha512-vgyElJaBRTtzWROQuO9Qx/VlibzKdhwnuQ2Ldh5/7/jddrB4XTI6IQlgR5ZglRaXjH4nXjVtUSwWWZMEIZQLFQ=="
|
||||
},
|
||||
"node_modules/embla-carousel-react": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-7.0.0.tgz",
|
||||
"integrity": "sha512-y17TYtqvvziWbDwwfX5dh8n4qU1luytz4+6WWMBnR4pJfLfkKBGsqYNZ4WhmAgUcYL1Uliti8Cjg+NJd46MPxw==",
|
||||
"dependencies": {
|
||||
"embla-carousel": "7.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/emittery": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
|
||||
@@ -60197,17 +60212,6 @@
|
||||
"react-dom": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-easy-swipe": {
|
||||
"version": "0.0.21",
|
||||
"resolved": "https://registry.npmjs.org/react-easy-swipe/-/react-easy-swipe-0.0.21.tgz",
|
||||
"integrity": "sha512-OeR2jAxdoqUMHIn/nS9fgreI5hSpgGoL5ezdal4+oO7YSSgJR8ga+PkYGJrSrJ9MKlPcQjMQXnketrD7WNmNsg==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.5.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/react-element-to-jsx-string": {
|
||||
"version": "14.3.4",
|
||||
"resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz",
|
||||
@@ -60382,14 +60386,6 @@
|
||||
"react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-multi-carousel": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-multi-carousel/-/react-multi-carousel-2.8.0.tgz",
|
||||
"integrity": "sha512-xuxQVGGiH8yWDDWgt9Z9+C+zzoACuBT740cV+6To52DYCwLlQGJIntDFLOCqMsO85Ist1x+630HA8lazb/a/tA==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-onclickoutside": {
|
||||
"version": "6.12.1",
|
||||
"resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz",
|
||||
@@ -60503,16 +60499,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-responsive-carousel": {
|
||||
"version": "3.2.23",
|
||||
"resolved": "https://registry.npmjs.org/react-responsive-carousel/-/react-responsive-carousel-3.2.23.tgz",
|
||||
"integrity": "sha512-pqJLsBaKHWJhw/ItODgbVoziR2z4lpcJg+YwmRlSk4rKH32VE633mAtZZ9kDXjy4wFO+pgUZmDKPsPe1fPmHCg==",
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-easy-swipe": "^0.0.21"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz",
|
||||
@@ -84862,6 +84848,19 @@
|
||||
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
|
||||
"dev": true
|
||||
},
|
||||
"embla-carousel": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-7.0.0.tgz",
|
||||
"integrity": "sha512-vgyElJaBRTtzWROQuO9Qx/VlibzKdhwnuQ2Ldh5/7/jddrB4XTI6IQlgR5ZglRaXjH4nXjVtUSwWWZMEIZQLFQ=="
|
||||
},
|
||||
"embla-carousel-react": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-7.0.0.tgz",
|
||||
"integrity": "sha512-y17TYtqvvziWbDwwfX5dh8n4qU1luytz4+6WWMBnR4pJfLfkKBGsqYNZ4WhmAgUcYL1Uliti8Cjg+NJd46MPxw==",
|
||||
"requires": {
|
||||
"embla-carousel": "7.0.0"
|
||||
}
|
||||
},
|
||||
"emittery": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz",
|
||||
@@ -113858,14 +113857,6 @@
|
||||
"prop-types": "^15.6.0"
|
||||
}
|
||||
},
|
||||
"react-easy-swipe": {
|
||||
"version": "0.0.21",
|
||||
"resolved": "https://registry.npmjs.org/react-easy-swipe/-/react-easy-swipe-0.0.21.tgz",
|
||||
"integrity": "sha512-OeR2jAxdoqUMHIn/nS9fgreI5hSpgGoL5ezdal4+oO7YSSgJR8ga+PkYGJrSrJ9MKlPcQjMQXnketrD7WNmNsg==",
|
||||
"requires": {
|
||||
"prop-types": "^15.5.8"
|
||||
}
|
||||
},
|
||||
"react-element-to-jsx-string": {
|
||||
"version": "14.3.4",
|
||||
"resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz",
|
||||
@@ -113997,11 +113988,6 @@
|
||||
"warning": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"react-multi-carousel": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-multi-carousel/-/react-multi-carousel-2.8.0.tgz",
|
||||
"integrity": "sha512-xuxQVGGiH8yWDDWgt9Z9+C+zzoACuBT740cV+6To52DYCwLlQGJIntDFLOCqMsO85Ist1x+630HA8lazb/a/tA=="
|
||||
},
|
||||
"react-onclickoutside": {
|
||||
"version": "6.12.1",
|
||||
"resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz",
|
||||
@@ -114063,16 +114049,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
|
||||
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
|
||||
},
|
||||
"react-responsive-carousel": {
|
||||
"version": "3.2.23",
|
||||
"resolved": "https://registry.npmjs.org/react-responsive-carousel/-/react-responsive-carousel-3.2.23.tgz",
|
||||
"integrity": "sha512-pqJLsBaKHWJhw/ItODgbVoziR2z4lpcJg+YwmRlSk4rKH32VE633mAtZZ9kDXjy4wFO+pgUZmDKPsPe1fPmHCg==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.5",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-easy-swipe": "^0.0.21"
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"crypto": "^1.0.1",
|
||||
"dayjs": "^1.11.1",
|
||||
"dompurify": "^2.3.10",
|
||||
"embla-carousel-react": "^7.0.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"express": "^4.18.1",
|
||||
"express-session": "^1.17.3",
|
||||
@@ -71,10 +72,8 @@
|
||||
"react-loader-spinner": "^6.0.0-0",
|
||||
"react-loading-skeleton": "^3.1.0",
|
||||
"react-modal": "^3.15.1",
|
||||
"react-multi-carousel": "^2.8.0",
|
||||
"react-query": "^3.35.0",
|
||||
"react-redux": "^8.0.0",
|
||||
"react-responsive-carousel": "^3.2.23",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-select": "^5.3.2",
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
|
||||
import Slider from 'src/Components/Slider/Slider'
|
||||
import useEmblaCarousel from 'embla-carousel-react'
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAllCategoriesQuery } from 'src/graphql';
|
||||
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
const colors = [
|
||||
'#FDF2F8',
|
||||
@@ -18,8 +20,18 @@ const colors = [
|
||||
|
||||
export default function Categories() {
|
||||
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel({ align: 'start', slidesToScroll: 2 })
|
||||
const { data, loading } = useAllCategoriesQuery();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const scrollPrev = useCallback(() => {
|
||||
if (emblaApi) emblaApi.scrollPrev()
|
||||
}, [emblaApi])
|
||||
|
||||
const scrollNext = useCallback(() => {
|
||||
if (emblaApi) emblaApi.scrollNext()
|
||||
}, [emblaApi])
|
||||
|
||||
if (loading || !data)
|
||||
return <div className="flex gap-12">
|
||||
{Array(5).fill(0).map((_, idx) =>
|
||||
@@ -34,16 +46,28 @@ export default function Categories() {
|
||||
|
||||
|
||||
return (
|
||||
<Slider>
|
||||
{data?.allCategories.map((category, idx) =>
|
||||
<button
|
||||
key={category.id}
|
||||
onClick={() => navigate(`/apps/category/${category.id}`)}
|
||||
className=' block p-16 rounded-16 hover:bg-gray-100 active:bg-gray-200 active:scale-90 transition-transform'
|
||||
style={{ backgroundColor: colors[idx % colors.length] }}
|
||||
>{category.icon} {category.title}</button>
|
||||
)}
|
||||
</Slider>
|
||||
|
||||
<div className="relative group">
|
||||
<div className="overflow-hidden" ref={emblaRef}>
|
||||
<div className="select-none w-full flex gap-16">
|
||||
{data?.allCategories.map((category, idx) =>
|
||||
<button
|
||||
key={category.id}
|
||||
onClick={() => emblaApi?.clickAllowed() && navigate(`/apps/category/${category.id}`)}
|
||||
className='min-w-max block p-16 rounded-16 hover:bg-gray-100 active:bg-gray-200 active:scale-90 transition-transform'
|
||||
style={{ backgroundColor: colors[idx % colors.length] }}
|
||||
>{category.icon} {category.title}</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button className="absolute inset-y-0 w-42 left-0 opacity-0 group-hover:opacity-100 transition-opacity rounded-l-12 bg-gradient-to-r from-gray-700 text-white" onClick={scrollPrev}>
|
||||
<FaChevronLeft />
|
||||
</button>
|
||||
<button className="absolute inset-y-0 w-42 right-0 opacity-0 group-hover:opacity-100 transition-opacity rounded-r-12 bg-gradient-to-l from-gray-700 text-white" onClick={scrollNext}>
|
||||
<FaChevronRight />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
const CustomDot = ({ onClick, ...rest }: any) => {
|
||||
const {
|
||||
active,
|
||||
} = rest;
|
||||
const CustomDot = ({ onClick, active, ...rest }: any) => {
|
||||
|
||||
// onMove means if dragging or swiping in progress.
|
||||
// active is provided by this lib for checking if the item is active or not.
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useMediaQuery } from "@react-hookz/web";
|
||||
import Carousel from "react-multi-carousel";
|
||||
import Assets from "src/assets";
|
||||
import Button from "src/Components/Button/Button";
|
||||
import THEME from "src/utils/theme";
|
||||
import { MEDIA_QUERIES } from "src/utils/theme/media_queries";
|
||||
import CustomDot from "./CustomDot/CustomDot";
|
||||
import styles from './styles.module.css'
|
||||
import useEmblaCarousel from 'embla-carousel-react'
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
const headerLinks = [
|
||||
{
|
||||
@@ -45,46 +46,74 @@ const responsive = {
|
||||
export default function Header() {
|
||||
|
||||
const isDesktop = useMediaQuery(MEDIA_QUERIES.isMedium);
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel({
|
||||
align: 'start',
|
||||
breakpoints: {
|
||||
[MEDIA_QUERIES.isMedium]: {
|
||||
draggable: false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const [scrollSnaps, setScrollSnaps] = useState<number[]>([]);
|
||||
|
||||
const onSelect = useCallback(() => {
|
||||
if (!emblaApi) return;
|
||||
setSelectedIndex(emblaApi.selectedScrollSnap());
|
||||
}, [emblaApi, setSelectedIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!emblaApi) return;
|
||||
onSelect();
|
||||
setScrollSnaps(emblaApi.scrollSnapList());
|
||||
emblaApi.on("select", onSelect);
|
||||
}, [emblaApi, setScrollSnaps, onSelect]);
|
||||
|
||||
return (
|
||||
<Carousel
|
||||
showDots={!isDesktop}
|
||||
arrows={false}
|
||||
responsive={responsive}
|
||||
customDot={<CustomDot />}
|
||||
className={styles.header}
|
||||
containerClass='!overflow-hidden'
|
||||
>
|
||||
<div className=" rounded-20 md:mr-20 h-[280px] relative overflow-hidden p-24 flex flex-col items-start justify-end">
|
||||
<img
|
||||
className="w-full h-full object-cover absolute top-0 left-0 z-[-2]"
|
||||
src={headerLinks[0].img}
|
||||
alt=""
|
||||
/>
|
||||
<div className="w-full h-full object-cover bg-gradient-to-t from-gray-900 absolute top-0 left-0 z-[-1]"></div>
|
||||
<div className="max-w-[90%]">
|
||||
{headerLinks[0].title}
|
||||
</div>
|
||||
<div className="relative group">
|
||||
<div className="overflow-hidden" ref={emblaRef}>
|
||||
<div className="w-full flex gap-16">
|
||||
<div className="flex-[0_0_100%] md:flex-[0_0_calc(50%-8px)] rounded-20 h-[280px] relative overflow-hidden p-24 flex flex-col items-start justify-end">
|
||||
<img
|
||||
className="w-full h-full object-cover absolute top-0 left-0 z-[-2]"
|
||||
src={headerLinks[0].img}
|
||||
alt=""
|
||||
/>
|
||||
<div className="w-full h-full object-cover bg-gradient-to-t from-gray-900 absolute top-0 left-0 z-[-1]"></div>
|
||||
<div className="max-w-[90%]">
|
||||
{headerLinks[0].title}
|
||||
</div>
|
||||
|
||||
<Button href={headerLinks[0].link.url} newTab color="white" className="mt-24">
|
||||
{headerLinks[0].link.content}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="rounded-20 md:ml-20 h-[280px] relative overflow-hidden p-24 flex flex-col items-start justify-end">
|
||||
<img
|
||||
className="w-full h-full object-cover absolute top-0 left-0 z-[-2]"
|
||||
src={headerLinks[1].img}
|
||||
alt=""
|
||||
/>
|
||||
<div className="w-full h-full object-cover bg-gradient-to-t from-gray-900 absolute top-0 left-0 z-[-1]"></div>
|
||||
<div className="max-w-[90%]">
|
||||
{headerLinks[1].title}
|
||||
<Button href={headerLinks[0].link.url} newTab color="white" className="mt-24">
|
||||
{headerLinks[0].link.content}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex-[0_0_100%] md:flex-[0_0_calc(50%-8px)] rounded-20 h-[280px] relative overflow-hidden p-24 flex flex-col items-start justify-end">
|
||||
<img
|
||||
className="w-full h-full object-cover absolute top-0 left-0 z-[-2]"
|
||||
src={headerLinks[1].img}
|
||||
alt=""
|
||||
/>
|
||||
<div className="w-full h-full object-cover bg-gradient-to-t from-gray-900 absolute top-0 left-0 z-[-1]"></div>
|
||||
<div className="max-w-[90%]">
|
||||
{headerLinks[1].title}
|
||||
</div>
|
||||
<Button color="white" href={headerLinks[1].link.url} newTab className="mt-24">
|
||||
{headerLinks[1].link.content}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Button color="white" href={headerLinks[1].link.url} newTab className="mt-24">
|
||||
{headerLinks[1].link.content}
|
||||
</Button>
|
||||
</div>
|
||||
</Carousel>
|
||||
<div className="absolute inset-x-0 bottom-8 flex justify-center gap-4 md:hidden">
|
||||
{scrollSnaps.map((_, index) => (
|
||||
<CustomDot
|
||||
key={index}
|
||||
active={index === selectedIndex}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { ReactNode, useCallback, useLayoutEffect, useEffect, useRef, } from "react";
|
||||
import { ReactNode, useCallback, } from "react";
|
||||
import { ProjectCard } from "src/utils/interfaces";
|
||||
import Carousel from 'react-multi-carousel';
|
||||
import { MdDoubleArrow, } from 'react-icons/md';
|
||||
import { useAppDispatch } from "src/utils/hooks";
|
||||
import { openModal } from "src/redux/features/modals.slice";
|
||||
import { useResizeListener } from 'src/utils/hooks'
|
||||
import { IoIosArrowBack, IoIosArrowForward } from "react-icons/io";
|
||||
import './style.css';
|
||||
import { Link } from "react-router-dom";
|
||||
import ProjectCardMini from "src/features/Projects/Components/ProjectCardMini/ProjectCardMini";
|
||||
import useEmblaCarousel from 'embla-carousel-react'
|
||||
import { FaChevronLeft, FaChevronRight } from "react-icons/fa";
|
||||
|
||||
interface Props {
|
||||
title: string | ReactNode,
|
||||
@@ -16,14 +14,6 @@ interface Props {
|
||||
projects: ProjectCard[]
|
||||
}
|
||||
|
||||
const responsive = {
|
||||
all: {
|
||||
breakpoint: { max: 5000, min: 0 },
|
||||
items: calcNumItems(),
|
||||
slidesToSlide: Math.round(calcNumItems())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function calcNumItems(width = Math.min(window.innerWidth - 32, 1440)) {
|
||||
const items = ((width / (296 + 20)));
|
||||
@@ -31,50 +21,25 @@ function calcNumItems(width = Math.min(window.innerWidth - 32, 1440)) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default function ProjectsRow({ title, link, projects }: Props) {
|
||||
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel({ align: 'start' })
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
let drag = useRef(false);
|
||||
const rowRef = useRef<HTMLDivElement>(null!);
|
||||
|
||||
const recalcItemsCnt = useCallback(
|
||||
() => {
|
||||
if (rowRef.current) {
|
||||
const count = calcNumItems(rowRef.current.clientWidth);
|
||||
responsive.all.items = count;
|
||||
responsive.all.slidesToSlide = Math.round(count)
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
useLayoutEffect(recalcItemsCnt, [recalcItemsCnt]);
|
||||
useResizeListener(recalcItemsCnt)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const mousedownListener = () => drag.current = false
|
||||
const mousemoveListener = () => drag.current = true
|
||||
|
||||
document.addEventListener('mousedown', mousedownListener);
|
||||
document.addEventListener('mousemove', mousemoveListener);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', mousedownListener);
|
||||
document.removeEventListener('mousemove', mousemoveListener);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const scrollSlides = useCallback((direction = 1) => {
|
||||
if (emblaApi) emblaApi.scrollTo(emblaApi.selectedScrollSnap() + direction * Math.floor(calcNumItems()))
|
||||
}, [emblaApi])
|
||||
|
||||
|
||||
if (projects.length === 0)
|
||||
return <></>
|
||||
|
||||
|
||||
|
||||
const handleClick = (projectId: number) => {
|
||||
if (!drag.current) {
|
||||
if (emblaApi?.clickAllowed()) {
|
||||
dispatch(openModal({ Modal: "ProjectDetailsCard", props: { projectId } }))
|
||||
}
|
||||
}
|
||||
@@ -89,35 +54,23 @@ export default function ProjectsRow({ title, link, projects }: Props) {
|
||||
<MdDoubleArrow className='text-gray-200 ml-8 hover:cursor-pointer transform scale-y-110 scale-x-125 origin-left' />
|
||||
</Link>}
|
||||
</h3>
|
||||
<div ref={rowRef} className="">
|
||||
<Carousel
|
||||
showDots={false}
|
||||
autoPlay={false}
|
||||
// arrows={false}
|
||||
responsive={responsive}
|
||||
// centerMode
|
||||
itemClass='pb-[1px]'
|
||||
containerClass='group'
|
||||
customLeftArrow={
|
||||
<button className='carousel-btns opacity-0 group-hover:opacity-100 transition-opacity w-64 h-full absolute top-0 left-0 rounded-l-12 bg-gradient-to-r from-gray-700 text-white' >
|
||||
<IoIosArrowBack className='scale-150' />
|
||||
</button>
|
||||
}
|
||||
customRightArrow={
|
||||
<button className='carousel-btns opacity-0 group-hover:opacity-100 transition-opacity w-64 h-full absolute top-0 right-0 rounded-r-12 bg-gradient-to-l from-gray-700 text-white' >
|
||||
<IoIosArrowForward className='scale-150' />
|
||||
</button>
|
||||
}
|
||||
>
|
||||
{projects.map((project, idx) =>
|
||||
<div key={project.id} className='max-w-[296px]' >
|
||||
<ProjectCardMini project={project} onClick={handleClick} />
|
||||
</div>
|
||||
)}
|
||||
</Carousel>
|
||||
<div className="relative group">
|
||||
<div className="overflow-hidden" ref={emblaRef}>
|
||||
<div className="w-full flex gap-16">
|
||||
{projects.map((project, idx) =>
|
||||
<div key={project.id} className='flex-[0_0_100%] max-w-[296px]' >
|
||||
<ProjectCardMini project={project} onClick={handleClick} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button className="absolute inset-y-0 w-64 left-0 opacity-0 group-hover:opacity-100 transition-opacity rounded-l-12 bg-gradient-to-r from-gray-700 text-white" onClick={() => scrollSlides(-1)}>
|
||||
<FaChevronLeft />
|
||||
</button>
|
||||
<button className="absolute inset-y-0 w-64 right-0 opacity-0 group-hover:opacity-100 transition-opacity rounded-r-12 bg-gradient-to-l from-gray-700 text-white" onClick={() => scrollSlides(1)}>
|
||||
<FaChevronRight />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
@media (pointer: coarse) {
|
||||
.carousel-btns {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.react-multi-carousel-list {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import { setIsMobileScreen } from 'src/redux/features/ui.slice';
|
||||
import { isMobileScreen } from './helperFunctions';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
import 'react-multi-carousel/lib/styles.css';
|
||||
import 'react-loading-skeleton/dist/skeleton.css'
|
||||
import THEME from './theme';
|
||||
import ErrorBoundary from 'src/Components/ErrorBoundary/ErrorBoundary';
|
||||
|
||||
Reference in New Issue
Block a user