Skip to content

Commit

Permalink
fix: added autoscrolling cards & ui fixes for testimonials
Browse files Browse the repository at this point in the history
  • Loading branch information
VineeTagarwaL-code committed Oct 2, 2024
1 parent 1a9ff76 commit 420fe84
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 91 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"framer-motion": "^11.5.4",
"framer-motion": "^11.9.0",
"jiti": "^1.21.6",
"linkify-react": "^4.1.3",
"lodash": "^4.17.21",
Expand All @@ -73,7 +73,7 @@
"react-icons": "^5.2.1",
"react-quill": "^2.0.0",
"react-tweet": "^3.2.1",
"tailwind-merge": "^2.4.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"uuid": "^10.0.0",
"vaul": "^0.9.1",
Expand Down
88 changes: 4 additions & 84 deletions src/components/Testimonials.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client';

import React, { useRef, useState, useEffect } from 'react';
import { Tweet } from 'react-tweet';
import React from 'react';
import { InfiniteMovingCards } from './ui/infinite-moving-cards';

const tweetIds = [
'1837845866289209697',
Expand All @@ -13,95 +12,16 @@ const tweetIds = [
'1826968639049724010',
];

const tweetStyles = `
.react-tweet-theme {
transform: scale(0.9);
transform-origin: top left;
max-width: none !important;
width: 100% !important;
}
.react-tweet-theme .react-tweet-media,
.react-tweet-theme .react-tweet-media-container,
.react-tweet-theme .react-tweet-embedded {
display: none !important;
}
`;

const scrollContainerStyles = `
.scroll-container::-webkit-scrollbar {
display: none;
}
.scroll-container {
-ms-overflow-style: none;
scrollbar-width: none;
}
`;

export default function Testimonials() {
const scrollContainerRef = useRef<HTMLDivElement>(null);
const [scrollPosition, setScrollPosition] = useState(0);
const scrollAmount = 2;
const [isHovered, setIsHovered] = useState(false);

const scroll = () => {
if (isHovered) return;
const container = scrollContainerRef.current;
if (!container) return;
const newPosition = scrollPosition + scrollAmount;
const maxScrollPosition = container.scrollWidth / 3;
if (newPosition >= maxScrollPosition) {
container.scrollLeft = newPosition - maxScrollPosition;
setScrollPosition(newPosition - maxScrollPosition);
} else {
container.scrollLeft = newPosition;
setScrollPosition(newPosition);
}
};

useEffect(() => {
const interval = setInterval(scroll, 30);
return () => clearInterval(interval);
}, [scrollPosition, isHovered]);

const tripleIds = [...tweetIds, ...tweetIds, ...tweetIds];

return (
<div
id="testimonials"
className="w-full h-fit px-4 md:px-8 flex flex-col items-center py-10 dark:bg-[#020817] bg-[#FFFFFF]"
>
<style>{tweetStyles}</style>
<style>{scrollContainerStyles}</style>
<div className="flex justify-center items-center min-h-fit max-w-[100vw] overflow-hidden flex-col">
<div className="w-full h-fit flex flex-col items-center">
<h1 className="font-bold text-2xl md:text-4xl">Testimonials</h1>
<p className="text-sm md:text-base py-2 font-semibold text-[#64748B] dark:text-[#94A3B8]">
Real Success Stories from Job Seekers and Employers
</p>
</div>
<div className="w-full mt-2 relative">
<div className="relative mx-auto" style={{ maxWidth: '1200px' }}>
<div
ref={scrollContainerRef}
className="overflow-x-hidden w-full scroll-container"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div className="flex gap-4 py-4" style={{ width: '300%' }}>
{tripleIds.map((id, index) => (
<div
key={`${id}-${index}`}
className="flex-shrink-0 w-full md:w-[396px]"
data-theme="dark"
style={{ height: '550px' }}
>
<Tweet id={id} />
</div>
))}
</div>
</div>
</div>

</div>
<InfiniteMovingCards items={tweetIds} direction="right" speed="slow" />
</div>
);
}
100 changes: 100 additions & 0 deletions src/components/ui/infinite-moving-cards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use client';

import { cn } from '@/lib/utils';
import React, { useEffect, useState } from 'react';
import { Tweet } from 'react-tweet';

export const InfiniteMovingCards = ({
items,
direction = 'left',
speed = 'fast',
pauseOnHover = true,
className,
}: {
items: string[];
direction?: 'left' | 'right';
speed?: 'fast' | 'normal' | 'slow';
pauseOnHover?: boolean;
className?: string;
}) => {
const containerRef = React.useRef<HTMLDivElement>(null);
const scrollerRef = React.useRef<HTMLUListElement>(null);

useEffect(() => {
addAnimation();
}, []);
const [start, setStart] = useState(false);
function addAnimation() {
if (containerRef.current && scrollerRef.current) {
const scrollerContent = Array.from(scrollerRef.current.children);

scrollerContent.forEach((item) => {
const duplicatedItem = item.cloneNode(true);
if (scrollerRef.current) {
scrollerRef.current.appendChild(duplicatedItem);
}
});

getDirection();
getSpeed();
setStart(true);
}
}
const getDirection = () => {
if (containerRef.current) {
if (direction === 'left') {
containerRef.current.style.setProperty(
'--animation-direction',
'forwards'
);
} else {
containerRef.current.style.setProperty(
'--animation-direction',
'reverse'
);
}
}
};
const getSpeed = () => {
if (containerRef.current) {
if (speed === 'fast') {
containerRef.current.style.setProperty('--animation-duration', '20s');
} else if (speed === 'normal') {
containerRef.current.style.setProperty('--animation-duration', '40s');
} else {
containerRef.current.style.setProperty('--animation-duration', '80s');
}
}
};
return (
<div
ref={containerRef}
className={cn(
'scroller relative z-20 max-w-7xl overflow-hidden [mask-image:linear-gradient(to_right,transparent,white_20%,white_80%,transparent)]',
className
)}
>
<ul
ref={scrollerRef}
className={cn(
' flex min-w-full shrink-0 gap-4 py-4 w-max flex-nowrap',
start && 'animate-scroll ',
pauseOnHover && 'hover:[animation-play-state:paused]'
)}
>
{items.map((id, index) => (
<li
className="w-[350px] min-h-fit max-w-full relative rounded-2xl flex-shrink-0 px-8 py-6 md:w-[450px]"
// style={{
// background:
// 'linear-gradient(180deg, var(--slate-800), var(--slate-900)',
// }}
key={index}
>
<Tweet id={id} />
</li>
))}
</ul>
</div>
);
};
25 changes: 20 additions & 5 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Config } from 'tailwindcss';
const { fontFamily } = require('tailwindcss/defaultTheme');

const {
default: flattenColorPalette,
} = require('tailwindcss/lib/util/flattenColorPalette');
const config = {
darkMode: ['class'],
content: [
Expand Down Expand Up @@ -97,8 +99,9 @@ const config = {
to: { height: '0' },
},
scroll: {
'0%': { transform: 'translateX(0)' },
'100%': { transform: 'translateX(-100%)' },
to: {
transform: 'translate(calc(-50% - 0.5rem))',
},
},
'loop-scroll': {
from: { transform: 'translateX(0%)' },
Expand All @@ -110,12 +113,24 @@ const config = {
'marquee-vertical': 'marquee-vertical var(--duration) linear infinite',
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
scroll: 'scroll 5s linear infinite',
scroll:
'scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite',
'loop-scroll': 'loop-scroll 50s linear infinite',
},
},
},
plugins: [require('tailwindcss-animate')],
plugins: [require('tailwindcss-animate'), addVariablesForColors],
} satisfies Config;

export default config;

function addVariablesForColors({ addBase, theme }: any) {
let allColors = flattenColorPalette(theme('colors'));
let newVars = Object.fromEntries(
Object.entries(allColors).map(([key, val]) => [`--${key}`, val])
);

addBase({
':root': newVars,
});
}

0 comments on commit 420fe84

Please sign in to comment.