diff --git a/public/companies/facebook.png b/public/companies/facebook.png new file mode 100644 index 00000000..759cfdb6 Binary files /dev/null and b/public/companies/facebook.png differ diff --git a/public/companies/insta.png b/public/companies/insta.png new file mode 100644 index 00000000..2003dadc Binary files /dev/null and b/public/companies/insta.png differ diff --git a/public/companies/microsoft.png b/public/companies/microsoft.png new file mode 100644 index 00000000..d4248255 Binary files /dev/null and b/public/companies/microsoft.png differ diff --git a/public/companies/tinder.png b/public/companies/tinder.png new file mode 100644 index 00000000..77c9df81 Binary files /dev/null and b/public/companies/tinder.png differ diff --git a/public/companies/twitch.png b/public/companies/twitch.png new file mode 100644 index 00000000..2529c328 Binary files /dev/null and b/public/companies/twitch.png differ diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 03730372..3833b6ed 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -25,7 +25,7 @@ export default async function RootLayout({ diff --git a/src/components/Companies.tsx b/src/components/Companies.tsx new file mode 100644 index 00000000..a92aa69a --- /dev/null +++ b/src/components/Companies.tsx @@ -0,0 +1,34 @@ +'use client'; + +import { InfiniteMovingCards } from './ui/infinite-moving-cards'; + +export function Companies() { + return ( +
+ +
+ ); +} + +const testimonials = [ + { + id: '1', + image: '/companies/microsoft.png', + }, + { + id: '2', + image: '/companies/facebook.png', + }, + { + id: '3', + image: '/companies/twitch.png', + }, + { + id: '4', + image: '/companies/tinder.png', + }, +]; diff --git a/src/components/hero-section.tsx b/src/components/hero-section.tsx index 5f216a17..2e87d2b5 100644 --- a/src/components/hero-section.tsx +++ b/src/components/hero-section.tsx @@ -1,7 +1,7 @@ import { GITHUB_REPO } from '@/lib/constant/app.constant'; import Link from 'next/link'; import Icon from './ui/icon'; -import Image from 'next/image'; +import { Companies } from './Companies'; const HeroSection = () => { return ( @@ -36,12 +36,7 @@ const HeroSection = () => {
- companies +
diff --git a/src/components/loader.tsx b/src/components/loader.tsx index 639e2389..84559080 100644 --- a/src/components/loader.tsx +++ b/src/components/loader.tsx @@ -1,26 +1,22 @@ -interface LoaderParams { - size?: number; -} +import { Skeleton } from './ui/skeleton'; -const Loader = ({ size = 8 }: LoaderParams) => { +const Loader = () => { return ( <> - +
+ {Array.from({ length: 5 }).map((_, i) => ( +
+ +
+ + +
+
+ ))} +
); }; diff --git a/src/components/ui/infinite-moving-cards.tsx b/src/components/ui/infinite-moving-cards.tsx new file mode 100644 index 00000000..36479c63 --- /dev/null +++ b/src/components/ui/infinite-moving-cards.tsx @@ -0,0 +1,102 @@ +'use client'; + +import { cn } from '@/lib/utils'; +import Image from 'next/image'; +import React, { useEffect, useState } from 'react'; + +export const InfiniteMovingCards = ({ + items, + direction = 'left', + speed = 'fast', + pauseOnHover = true, + className, +}: { + items: { + id: string; + image: string; + }[]; + direction?: 'left' | 'right'; + speed?: 'fast' | 'normal' | 'slow'; + pauseOnHover?: boolean; + className?: string; +}) => { + const containerRef = React.useRef(null); + const scrollerRef = React.useRef(null); + + useEffect(() => { + addAnimation(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + 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 ( +
+
    + {items.map((item) => ( +
  • + companyImages +
  • + ))} +
+
+ ); +}; diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx new file mode 100644 index 00000000..a626d9ba --- /dev/null +++ b/src/components/ui/skeleton.tsx @@ -0,0 +1,15 @@ +import { cn } from '@/lib/utils'; + +function Skeleton({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ); +} + +export { Skeleton }; diff --git a/src/components/ui/theme-toggle.tsx b/src/components/ui/theme-toggle.tsx deleted file mode 100644 index 1f46f8dc..00000000 --- a/src/components/ui/theme-toggle.tsx +++ /dev/null @@ -1,49 +0,0 @@ -'use client'; - -import { useTheme } from 'next-themes'; - -import { Button } from '@/components/ui/button'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu'; -import Icon from './icon'; - -export function ModeToggle() { - const { setTheme } = useTheme(); - - return ( - - - - - - setTheme('light')}> - Light - - setTheme('dark')}> - Dark - - setTheme('system')}> - System - - - - ); -} diff --git a/src/layouts/header.tsx b/src/layouts/header.tsx index 23fce6d1..28c1cb50 100644 --- a/src/layouts/header.tsx +++ b/src/layouts/header.tsx @@ -1,6 +1,5 @@ 'use client'; import { MobileNav } from '@/layouts/mobile-nav'; -import { ModeToggle } from '@/components/ui/theme-toggle'; import APP_PATHS from '@/config/path.config'; import { navbar } from '@/lib/constant/app.constant'; import { useSession } from 'next-auth/react'; @@ -35,7 +34,6 @@ const Header = () => { -
diff --git a/tailwind.config.ts b/tailwind.config.ts index 201536c3..8ef02b35 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,5 +1,8 @@ import type { Config } from 'tailwindcss'; const { fontFamily } = require('tailwindcss/defaultTheme'); +const { + default: flattenColorPalette, +} = require('tailwindcss/lib/util/flattenColorPalette'); const config = { darkMode: ['class'], @@ -78,14 +81,33 @@ const config = { from: { height: 'var(--radix-accordion-content-height)' }, to: { height: '0' }, }, + scroll: { + to: { + transform: 'translate(calc(-50% - 0.5rem))', + }, + }, }, animation: { 'accordion-down': 'accordion-down 0.2s ease-out', 'accordion-up': 'accordion-up 0.2s ease-out', + scroll: + 'scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite', }, }, + plugins: [addVariablesForColors], }, plugins: [require('tailwindcss-animate')], } satisfies 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, + }); +} + export default config;