mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-31 21:11:44 +02:00
feat: review carousel
This commit is contained in:
@@ -4,8 +4,10 @@ import {
|
|||||||
StarIcon,
|
StarIcon,
|
||||||
User2Icon,
|
User2Icon,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { markdownToHtml } from '../../lib/markdown';
|
import { markdownToHtml } from '../../lib/markdown';
|
||||||
|
import { getTailwindScreenDimension } from '../../lib/is-mobile';
|
||||||
|
import { cn } from '../../lib/classname';
|
||||||
|
|
||||||
type Review = {
|
type Review = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -107,14 +109,23 @@ export function ReviewCarousel() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const batchSize = 2;
|
const [batchSize, setBatchSize] = useState(3);
|
||||||
const maxBatchNumber = Math.ceil(reviews.length / batchSize);
|
const maxBatchNumber = Math.ceil(reviews.length / batchSize);
|
||||||
const [currentBatchNumber, setCurrentBatchNumber] = useState(0);
|
const [currentBatchNumber, setCurrentBatchNumber] = useState(0);
|
||||||
|
|
||||||
const currentBatch = reviews.slice(
|
const currentBatch = useMemo(() => {
|
||||||
currentBatchNumber * batchSize,
|
const result = reviews.slice(
|
||||||
(currentBatchNumber + 1) * batchSize,
|
currentBatchNumber * batchSize,
|
||||||
);
|
(currentBatchNumber + 1) * batchSize,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.length < batchSize) {
|
||||||
|
const remaining = batchSize - result.length;
|
||||||
|
return [...result, ...reviews.slice(0, remaining)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [currentBatchNumber, batchSize]);
|
||||||
|
|
||||||
const handleNextBatch = () => {
|
const handleNextBatch = () => {
|
||||||
setCurrentBatchNumber((prev) => (prev + 1) % maxBatchNumber);
|
setCurrentBatchNumber((prev) => (prev + 1) % maxBatchNumber);
|
||||||
@@ -126,27 +137,52 @@ export function ReviewCarousel() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const size = getTailwindScreenDimension();
|
||||||
|
if (size === 'xl' || size === '2xl') {
|
||||||
|
setBatchSize(3);
|
||||||
|
} else {
|
||||||
|
setBatchSize(2);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-24">
|
<div className="mt-24">
|
||||||
<h3 className="text-center text-2xl font-medium text-zinc-200 md:text-3xl">
|
<h3 className="text-center text-2xl font-medium text-zinc-200 md:text-3xl">
|
||||||
What other learners said
|
What other learners said
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="mt-10 flex gap-4">
|
<div className="mt-10 mb-6 flex items-center justify-end gap-2 xl:hidden">
|
||||||
<div className="flex shrink-0 flex-col items-center justify-center">
|
<NavigateButton
|
||||||
<button
|
onClick={handlePreviousBatch}
|
||||||
|
icon={<ChevronLeftIcon className="h-4 w-4 stroke-[2.5] text-white" />}
|
||||||
|
/>
|
||||||
|
<NavigateButton
|
||||||
|
onClick={handleNextBatch}
|
||||||
|
icon={
|
||||||
|
<ChevronRightIcon className="h-4 w-4 stroke-[2.5] text-white" />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative mt-0 flex gap-4 xl:mt-10">
|
||||||
|
<div className="absolute inset-y-0 -left-2 hidden shrink-0 -translate-x-full flex-col items-center justify-center xl:flex">
|
||||||
|
<NavigateButton
|
||||||
onClick={handlePreviousBatch}
|
onClick={handlePreviousBatch}
|
||||||
className="flex items-center justify-center rounded-full bg-zinc-800 p-2 hover:bg-zinc-700"
|
icon={
|
||||||
>
|
<ChevronLeftIcon className="h-5 w-5 stroke-[2.5] text-white" />
|
||||||
<ChevronLeftIcon className="h-6 w-6 stroke-[2.5] text-white" />
|
}
|
||||||
</button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2 xl:grid-cols-3">
|
||||||
{currentBatch.map((review, index) => (
|
{currentBatch.map((review, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="review-testimonial relative overflow-hidden rounded-2xl bg-linear-to-br from-yellow-500/10 via-yellow-500/5 to-transparent p-8 backdrop-blur-sm [&_strong]:font-normal [&_strong]:text-yellow-300/70"
|
className={cn(
|
||||||
|
'review-testimonial relative overflow-hidden rounded-2xl bg-linear-to-br from-yellow-500/10 via-yellow-500/5 to-transparent p-8 backdrop-blur-sm [&_strong]:font-normal [&_strong]:text-yellow-300/70',
|
||||||
|
index === 2 && batchSize === 3 && 'hidden xl:block',
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div className="absolute -top-8 -right-8 h-32 w-32 rounded-full bg-yellow-500/5" />
|
<div className="absolute -top-8 -right-8 h-32 w-32 rounded-full bg-yellow-500/5" />
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
@@ -195,15 +231,33 @@ export function ReviewCarousel() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex shrink-0 flex-col items-center justify-center">
|
<div className="absolute inset-y-0 -right-2 hidden shrink-0 translate-x-full flex-col items-center justify-center xl:flex">
|
||||||
<button
|
<NavigateButton
|
||||||
onClick={handleNextBatch}
|
onClick={handleNextBatch}
|
||||||
className="flex items-center justify-center rounded-full bg-zinc-800 p-2 hover:bg-zinc-700"
|
icon={
|
||||||
>
|
<ChevronRightIcon className="h-5 w-5 stroke-[2.5] text-white" />
|
||||||
<ChevronRightIcon className="h-6 w-6 stroke-[2.5] text-white" />
|
}
|
||||||
</button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NavigateButtonProps = {
|
||||||
|
onClick: () => void;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
function NavigateButton(props: NavigateButtonProps) {
|
||||||
|
const { onClick, icon } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
className="flex items-center justify-center rounded-full bg-zinc-800 p-2 hover:bg-zinc-700"
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user