♻️ refactor(ui): Dependencies

This commit is contained in:
web@ppanel 2024-11-14 21:21:00 +07:00
parent 5af60aa4de
commit 727d779b84
187 changed files with 26448 additions and 14037 deletions

View File

@ -13,15 +13,15 @@
"@iconify/react": "^5.0.2", "@iconify/react": "^5.0.2",
"@repo/ui": "workspace:*", "@repo/ui": "workspace:*",
"@shadcn/ui": "workspace:*", "@shadcn/ui": "workspace:*",
"@tanstack/react-query": "^5.59.20", "@tanstack/react-query": "^5.60.2",
"@tanstack/react-query-next-experimental": "^5.59.20", "@tanstack/react-query-next-experimental": "^5.60.2",
"ahooks": "^3.8.1", "ahooks": "^3.8.1",
"axios": "^1.7.7", "axios": "^1.7.7",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"mathjs": "^13.2.1", "mathjs": "^13.2.2",
"nanoid": "^5.0.8", "nanoid": "^5.0.8",
"next": "^15.0.3", "next": "^15.0.3",
"next-intl": "^3.25.0", "next-intl": "^3.25.1",
"next-runtime-env": "^3.2.2", "next-runtime-env": "^3.2.2",
"next-themes": "^0.4.3", "next-themes": "^0.4.3",
"nextjs-toploader": "^3.7.15", "nextjs-toploader": "^3.7.15",
@ -36,8 +36,8 @@
"@repo/eslint-config": "workspace:*", "@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*", "@repo/typescript-config": "workspace:*",
"@types/node": "^22.9.0", "@types/node": "^22.9.0",
"@types/react": "^18", "@types/react": "^18.3.12",
"@types/react-dom": "^18", "@types/react-dom": "^18.3.1",
"typescript": "^5.6.3" "typescript": "^5.6.3"
} }
} }

View File

@ -10,10 +10,8 @@ import {
UsersIcon, UsersIcon,
} from '@repo/ui/lotties'; } from '@repo/ui/lotties';
import { Button, buttonVariants } from '@shadcn/ui/button'; import { Button, buttonVariants } from '@shadcn/ui/button';
import Marquee from '@shadcn/ui/marquee';
import { AnimationProps, motion, MotionProps } from 'framer-motion'; import { AnimationProps, motion, MotionProps } from 'framer-motion';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import Image from 'next/legacy/image';
import Link from 'next/link'; import Link from 'next/link';
import { useMemo } from 'react'; import { useMemo } from 'react';
@ -243,46 +241,6 @@ export default function Page() {
<GlobalMapIcon className='-mt-[25%] w-full' /> <GlobalMapIcon className='-mt-[25%] w-full' />
</motion.div> </motion.div>
</ScrollAnimationWrapper> </ScrollAnimationWrapper>
<ScrollAnimationWrapper>
<motion.div
className='relative mx-auto flex items-center justify-center overflow-hidden py-6'
variants={scrollAnimation}
>
<Marquee pauseOnHover className='[--duration:20s]'>
{[
'facebook',
'google',
'hbo',
'instagram',
'netflix',
'primevideo',
'reddit',
'snapchat',
'spotify',
'twitch',
'twitter',
'whatsapp',
'youtube',
].map((logo) => (
<div
className='mx-10 flex shrink-0 items-center justify-center dark:invert'
key={logo}
>
<Image
src={`/index/${logo}.png`}
alt={logo}
width={120}
height={48}
className='h-12 w-auto object-contain'
/>
</div>
))}
</Marquee>
<div className='from-background absolute inset-y-0 left-0 w-12 bg-gradient-to-r to-transparent'></div>
<div className='from-background absolute inset-y-0 right-0 w-12 bg-gradient-to-l to-transparent'></div>
</motion.div>
</ScrollAnimationWrapper>
</main> </main>
); );
} }

View File

@ -15,16 +15,16 @@
"@shadcn/ui": "workspace:*", "@shadcn/ui": "workspace:*",
"@stripe/react-stripe-js": "^2.9.0", "@stripe/react-stripe-js": "^2.9.0",
"@stripe/stripe-js": "^4.10.0", "@stripe/stripe-js": "^4.10.0",
"@tanstack/react-query": "^5.59.20", "@tanstack/react-query": "^5.60.2",
"@tanstack/react-query-next-experimental": "^5.59.20", "@tanstack/react-query-next-experimental": "^5.60.2",
"ahooks": "^3.8.1", "ahooks": "^3.8.1",
"axios": "^1.7.7", "axios": "^1.7.7",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"framer-motion": "^11.11.11", "framer-motion": "^11.11.16",
"lucide-react": "^0.446.0", "lucide-react": "^0.456.0",
"mathjs": "^13.2.1", "mathjs": "^13.2.2",
"next": "^15.0.3", "next": "^15.0.3",
"next-intl": "^3.25.0", "next-intl": "^3.25.1",
"next-runtime-env": "^3.2.2", "next-runtime-env": "^3.2.2",
"next-themes": "^0.4.3", "next-themes": "^0.4.3",
"nextjs-toploader": "^3.7.15", "nextjs-toploader": "^3.7.15",
@ -40,8 +40,8 @@
"@repo/eslint-config": "workspace:*", "@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*", "@repo/typescript-config": "workspace:*",
"@types/node": "^22.9.0", "@types/node": "^22.9.0",
"@types/react": "^18", "@types/react": "^18.3.12",
"@types/react-dom": "^18", "@types/react-dom": "^18.3.1",
"typescript": "^5.6.3" "typescript": "^5.6.3"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@ -9,8 +9,8 @@
"devDependencies": { "devDependencies": {
"@next/eslint-plugin-next": "^15.0.3", "@next/eslint-plugin-next": "^15.0.3",
"@types/eslint": "^8", "@types/eslint": "^8",
"@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/parser": "^8.13.0", "@typescript-eslint/parser": "^8.14.0",
"@vercel/style-guide": "^6.0.0", "@vercel/style-guide": "^6.0.0",
"eslint": "^8", "eslint": "^8",
"eslint-config-next": "^15.0.3", "eslint-config-next": "^15.0.3",

View File

@ -8,7 +8,11 @@ module.exports = {
tsconfigRootDir: __dirname, tsconfigRootDir: __dirname,
}, },
rules: { rules: {
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'no-redeclare': 'off', 'no-redeclare': 'off',
'no-unused-vars': 'off', 'no-unused-vars': 'off',
'no-undef': 'off',
}, },
}; };

View File

@ -33,7 +33,7 @@
"@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-progress": "^1.1.0", "@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-radio-group": "^1.2.1", "@radix-ui/react-radio-group": "^1.2.1",
"@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-scroll-area": "^1.2.1",
"@radix-ui/react-select": "^2.1.2", "@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slider": "^1.2.1", "@radix-ui/react-slider": "^1.2.1",
@ -43,31 +43,37 @@
"@radix-ui/react-toast": "^1.2.2", "@radix-ui/react-toast": "^1.2.2",
"@radix-ui/react-toggle": "^1.1.0", "@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.3", "@radix-ui/react-tooltip": "^1.1.4",
"@types/canvas-confetti": "^1.6.4", "@react-three/drei": "^9.116.0",
"canvas-confetti": "^1.9.3", "@react-three/fiber": "^8.17.10",
"@tabler/icons-react": "^3.22.0",
"@tsparticles/engine": "^3.5.0",
"@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.5.0",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "1.0.0", "cmdk": "1.0.4",
"cobe": "^0.6.3", "cobe": "^0.6.3",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"embla-carousel-react": "^8.3.1", "embla-carousel-react": "^8.3.1",
"framer-motion": "^11.11.11", "framer-motion": "^11.11.16",
"input-otp": "^1.4.1", "input-otp": "^1.4.1",
"lucide-react": "^0.446.0", "lucide-react": "^0.456.0",
"mini-svg-data-uri": "^1.4.4",
"next-themes": "^0.4.3", "next-themes": "^0.4.3",
"react": "^18.3.1", "qss": "^3.0.0",
"react-day-picker": "8.10.1", "react-day-picker": "8.10.1",
"react-dropzone": "^14.3.5",
"react-hook-form": "^7.53.2", "react-hook-form": "^7.53.2",
"react-icon-cloud": "^4.1.4", "react-icons": "^5.3.0",
"react-resizable-panels": "^2.1.6", "react-resizable-panels": "^2.1.6",
"react-spring": "^9.7.4",
"react-tweet": "^3.2.1",
"recharts": "^2.13.3", "recharts": "^2.13.3",
"shiki": "^1.22.2", "simplex-noise": "^4.0.3",
"sonner": "^1.7.0", "sonner": "^1.7.0",
"tailwind-merge": "^2.5.2", "tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"three": "^0.170.0",
"three-globe": "^2.34.4",
"vaul": "^1.1.1", "vaul": "^1.1.1",
"zod": "^3.23.8" "zod": "^3.23.8"
}, },
@ -75,10 +81,13 @@
"@repo/eslint-config": "workspace:*", "@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*", "@repo/typescript-config": "workspace:*",
"@types/node": "^22.9.0", "@types/node": "^22.9.0",
"@types/react": "^18", "@types/react": "^18.3.12",
"@types/react-dom": "^18", "@types/react-dom": "^18.3.1",
"@types/three": "^0.170.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"postcss": "^8.4.47", "next": "^15.0.3",
"postcss": "^8.4.49",
"react": "^18.3.1",
"tailwindcss": "^3.4.14", "tailwindcss": "^3.4.14",
"typescript": "^5.6.3" "typescript": "^5.6.3"
} }

View File

@ -0,0 +1,141 @@
// @ts-nocheck
'use client';
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
const MouseEnterContext = createContext<
[boolean, React.Dispatch<React.SetStateAction<boolean>>] | undefined
>(undefined);
export const CardContainer = ({
children,
className,
containerClassName,
}: {
children?: React.ReactNode;
className?: string;
containerClassName?: string;
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const [isMouseEntered, setIsMouseEntered] = useState(false);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return;
const { left, top, width, height } = containerRef.current.getBoundingClientRect();
const x = (e.clientX - left - width / 2) / 25;
const y = (e.clientY - top - height / 2) / 25;
containerRef.current.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`;
};
const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {
setIsMouseEntered(true);
if (!containerRef.current) return;
};
const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {
if (!containerRef.current) return;
setIsMouseEntered(false);
containerRef.current.style.transform = `rotateY(0deg) rotateX(0deg)`;
};
return (
<MouseEnterContext.Provider value={[isMouseEntered, setIsMouseEntered]}>
<div
className={cn('flex items-center justify-center py-20', containerClassName)}
style={{
perspective: '1000px',
}}
>
<div
ref={containerRef}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
className={cn(
'relative flex items-center justify-center transition-all duration-200 ease-linear',
className,
)}
style={{
transformStyle: 'preserve-3d',
}}
>
{children}
</div>
</div>
</MouseEnterContext.Provider>
);
};
export const CardBody = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
return (
<div
className={cn(
'h-96 w-96 [transform-style:preserve-3d] [&>*]:[transform-style:preserve-3d]',
className,
)}
>
{children}
</div>
);
};
export const CardItem = ({
as: Tag = 'div',
children,
className,
translateX = 0,
translateY = 0,
translateZ = 0,
rotateX = 0,
rotateY = 0,
rotateZ = 0,
...rest
}: {
as?: React.ElementType;
children: React.ReactNode;
className?: string;
translateX?: number | string;
translateY?: number | string;
translateZ?: number | string;
rotateX?: number | string;
rotateY?: number | string;
rotateZ?: number | string;
[key: string]: any;
}) => {
const ref = useRef<HTMLDivElement>(null);
const [isMouseEntered] = useMouseEnter();
useEffect(() => {
handleAnimations();
}, [isMouseEntered]);
const handleAnimations = () => {
if (!ref.current) return;
if (isMouseEntered) {
ref.current.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`;
} else {
ref.current.style.transform = `translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)`;
}
};
return (
<Tag ref={ref} className={cn('w-fit transition duration-200 ease-linear', className)} {...rest}>
{children}
</Tag>
);
};
// Create a hook to use the context
export const useMouseEnter = () => {
const context = useContext(MouseEnterContext);
if (context === undefined) {
throw new Error('useMouseEnter must be used within a MouseEnterProvider');
}
return context;
};

View File

@ -0,0 +1,156 @@
// @ts-nocheck
'use client';
import { motion } from 'framer-motion';
import Link from 'next/link';
import React, { useState } from 'react';
import { cn } from '../../lib/utils';
export const PinContainer = ({
children,
title,
href,
className,
containerClassName,
}: {
children: React.ReactNode;
title?: string;
href?: string;
className?: string;
containerClassName?: string;
}) => {
const [transform, setTransform] = useState('translate(-50%,-50%) rotateX(0deg)');
const onMouseEnter = () => {
setTransform('translate(-50%,-50%) rotateX(40deg) scale(0.8)');
};
const onMouseLeave = () => {
setTransform('translate(-50%,-50%) rotateX(0deg) scale(1)');
};
return (
<Link
className={cn('group/pin relative z-50 cursor-pointer', containerClassName)}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
href={href || '/'}
>
<div
style={{
perspective: '1000px',
transform: 'rotateX(70deg) translateZ(0deg)',
}}
className='absolute left-1/2 top-1/2 ml-[0.09375rem] mt-4 -translate-x-1/2 -translate-y-1/2'
>
<div
style={{
transform: transform,
}}
className='absolute left-1/2 top-1/2 flex items-start justify-start overflow-hidden rounded-2xl border border-white/[0.1] bg-black p-4 shadow-[0_8px_16px_rgb(0_0_0/0.4)] transition duration-700 group-hover/pin:border-white/[0.2]'
>
<div className={cn('relative z-50', className)}>{children}</div>
</div>
</div>
<PinPerspective title={title} href={href} />
</Link>
);
};
export const PinPerspective = ({ title, href }: { title?: string; href?: string }) => {
return (
<motion.div className='pointer-events-none z-[60] flex h-80 w-96 items-center justify-center opacity-0 transition duration-500 group-hover/pin:opacity-100'>
<div className='inset-0 -mt-7 h-full w-full flex-none'>
<div className='absolute inset-x-0 top-0 flex justify-center'>
<a
href={href}
target={'_blank'}
className='relative z-10 flex items-center space-x-2 rounded-full bg-zinc-950 px-4 py-0.5 ring-1 ring-white/10'
>
<span className='relative z-20 inline-block py-0.5 text-xs font-bold text-white'>
{title}
</span>
<span className='absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-emerald-400/90 to-emerald-400/0 transition-opacity duration-500 group-hover/btn:opacity-40'></span>
</a>
</div>
<div
style={{
perspective: '1000px',
transform: 'rotateX(70deg) translateZ(0)',
}}
className='absolute left-1/2 top-1/2 ml-[0.09375rem] mt-4 -translate-x-1/2 -translate-y-1/2'
>
<>
<motion.div
initial={{
opacity: 0,
scale: 0,
x: '-50%',
y: '-50%',
}}
animate={{
opacity: [0, 1, 0.5, 0],
scale: 1,
z: 0,
}}
transition={{
duration: 6,
repeat: Infinity,
delay: 0,
}}
className='absolute left-1/2 top-1/2 h-[11.25rem] w-[11.25rem] rounded-[50%] bg-sky-500/[0.08] shadow-[0_8px_16px_rgb(0_0_0/0.4)]'
></motion.div>
<motion.div
initial={{
opacity: 0,
scale: 0,
x: '-50%',
y: '-50%',
}}
animate={{
opacity: [0, 1, 0.5, 0],
scale: 1,
z: 0,
}}
transition={{
duration: 6,
repeat: Infinity,
delay: 2,
}}
className='absolute left-1/2 top-1/2 h-[11.25rem] w-[11.25rem] rounded-[50%] bg-sky-500/[0.08] shadow-[0_8px_16px_rgb(0_0_0/0.4)]'
></motion.div>
<motion.div
initial={{
opacity: 0,
scale: 0,
x: '-50%',
y: '-50%',
}}
animate={{
opacity: [0, 1, 0.5, 0],
scale: 1,
z: 0,
}}
transition={{
duration: 6,
repeat: Infinity,
delay: 4,
}}
className='absolute left-1/2 top-1/2 h-[11.25rem] w-[11.25rem] rounded-[50%] bg-sky-500/[0.08] shadow-[0_8px_16px_rgb(0_0_0/0.4)]'
></motion.div>
</>
</div>
<>
<motion.div className='absolute bottom-1/2 right-1/2 h-20 w-px translate-y-[14px] bg-gradient-to-b from-transparent to-cyan-500 blur-[2px] group-hover/pin:h-40' />
<motion.div className='absolute bottom-1/2 right-1/2 h-20 w-px translate-y-[14px] bg-gradient-to-b from-transparent to-cyan-500 group-hover/pin:h-40' />
<motion.div className='absolute bottom-1/2 right-1/2 z-40 h-[4px] w-[4px] translate-x-[1.5px] translate-y-[14px] rounded-full bg-cyan-600 blur-[3px]' />
<motion.div className='absolute bottom-1/2 right-1/2 z-40 h-[2px] w-[2px] translate-x-[0.5px] translate-y-[14px] rounded-full bg-cyan-300' />
</>
</div>
</motion.div>
);
};

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as AccordionPrimitive from '@radix-ui/react-accordion'; import * as AccordionPrimitive from '@radix-ui/react-accordion';

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';

View File

@ -1,3 +1,4 @@
// @ts-nocheck
import { cva, type VariantProps } from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react'; import * as React from 'react';

View File

@ -1,166 +0,0 @@
'use client';
import { motion } from 'framer-motion';
import { RefObject, useEffect, useId, useState } from 'react';
import { cn } from '../../lib/utils';
export interface AnimatedBeamProps {
className?: string;
containerRef: RefObject<HTMLElement>; // Container ref
fromRef: RefObject<HTMLElement>;
toRef: RefObject<HTMLElement>;
curvature?: number;
reverse?: boolean;
pathColor?: string;
pathWidth?: number;
pathOpacity?: number;
gradientStartColor?: string;
gradientStopColor?: string;
delay?: number;
duration?: number;
startXOffset?: number;
startYOffset?: number;
endXOffset?: number;
endYOffset?: number;
}
export const AnimatedBeam: React.FC<AnimatedBeamProps> = ({
className,
containerRef,
fromRef,
toRef,
curvature = 0,
reverse = false, // Include the reverse prop
duration = Math.random() * 3 + 4,
delay = 0,
pathColor = 'gray',
pathWidth = 2,
pathOpacity = 0.2,
gradientStartColor = '#ffaa40',
gradientStopColor = '#9c40ff',
startXOffset = 0,
startYOffset = 0,
endXOffset = 0,
endYOffset = 0,
}) => {
const id = useId();
const [pathD, setPathD] = useState('');
const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 });
// Calculate the gradient coordinates based on the reverse prop
const gradientCoordinates = reverse
? {
x1: ['90%', '-10%'],
x2: ['100%', '0%'],
y1: ['0%', '0%'],
y2: ['0%', '0%'],
}
: {
x1: ['10%', '110%'],
x2: ['0%', '100%'],
y1: ['0%', '0%'],
y2: ['0%', '0%'],
};
useEffect(() => {
const updatePath = () => {
if (containerRef.current && fromRef.current && toRef.current) {
const containerRect = containerRef.current.getBoundingClientRect();
const rectA = fromRef.current.getBoundingClientRect();
const rectB = toRef.current.getBoundingClientRect();
const svgWidth = containerRect.width;
const svgHeight = containerRect.height;
setSvgDimensions({ width: svgWidth, height: svgHeight });
const startX = rectA.left - containerRect.left + rectA.width / 2 + startXOffset;
const startY = rectA.top - containerRect.top + rectA.height / 2 + startYOffset;
const endX = rectB.left - containerRect.left + rectB.width / 2 + endXOffset;
const endY = rectB.top - containerRect.top + rectB.height / 2 + endYOffset;
const controlY = startY - curvature;
const d = `M ${startX},${startY} Q ${(startX + endX) / 2},${controlY} ${endX},${endY}`;
setPathD(d);
}
};
// Initialize ResizeObserver
const resizeObserver = new ResizeObserver((entries) => {
// For all entries, recalculate the path
for (let entry of entries) {
updatePath();
}
});
// Observe the container element
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}
// Call the updatePath initially to set the initial path
updatePath();
// Clean up the observer on component unmount
return () => {
resizeObserver.disconnect();
};
}, [containerRef, fromRef, toRef, curvature, startXOffset, startYOffset, endXOffset, endYOffset]);
return (
<svg
fill='none'
width={svgDimensions.width}
height={svgDimensions.height}
xmlns='http://www.w3.org/2000/svg'
className={cn('pointer-events-none absolute left-0 top-0 transform-gpu stroke-2', className)}
viewBox={`0 0 ${svgDimensions.width} ${svgDimensions.height}`}
>
<path
d={pathD}
stroke={pathColor}
strokeWidth={pathWidth}
strokeOpacity={pathOpacity}
strokeLinecap='round'
/>
<path
d={pathD}
strokeWidth={pathWidth}
stroke={`url(#${id})`}
strokeOpacity='1'
strokeLinecap='round'
/>
<defs>
<motion.linearGradient
className='transform-gpu'
id={id}
gradientUnits={'userSpaceOnUse'}
initial={{
x1: '0%',
x2: '0%',
y1: '0%',
y2: '0%',
}}
animate={{
x1: gradientCoordinates.x1,
x2: gradientCoordinates.x2,
y1: gradientCoordinates.y1,
y2: gradientCoordinates.y2,
}}
transition={{
delay,
duration,
ease: [0.16, 1, 0.3, 1], // https://easings.net/#easeOutExpo
repeat: Infinity,
repeatDelay: 0,
}}
>
<stop stopColor={gradientStartColor} stopOpacity='0'></stop>
<stop stopColor={gradientStartColor}></stop>
<stop offset='32.5%' stopColor={gradientStopColor}></stop>
<stop offset='100%' stopColor={gradientStopColor} stopOpacity='0'></stop>
</motion.linearGradient>
</defs>
</svg>
);
};

View File

@ -1,101 +0,0 @@
import { cn } from '../../lib/utils';
interface Props {
max: number;
value: number;
min: number;
gaugePrimaryColor: string;
gaugeSecondaryColor: string;
className?: string;
}
export default function AnimatedCircularProgressBar({
max = 100,
min = 0,
value = 0,
gaugePrimaryColor,
gaugeSecondaryColor,
className,
}: Props) {
const circumference = 2 * Math.PI * 45;
const percentPx = circumference / 100;
const currentPercent = ((value - min) / (max - min)) * 100;
return (
<div
className={cn('relative size-40 text-2xl font-semibold', className)}
style={
{
'--circle-size': '100px',
'--circumference': circumference,
'--percent-to-px': `${percentPx}px`,
'--gap-percent': '5',
'--offset-factor': '0',
'--transition-length': '1s',
'--transition-step': '200ms',
'--delay': '0s',
'--percent-to-deg': '3.6deg',
'transform': 'translateZ(0)',
} as React.CSSProperties
}
>
<svg fill='none' className='size-full' strokeWidth='2' viewBox='0 0 100 100'>
{currentPercent <= 90 && currentPercent >= 0 && (
<circle
cx='50'
cy='50'
r='45'
strokeWidth='10'
strokeDashoffset='0'
strokeLinecap='round'
strokeLinejoin='round'
className='opacity-100'
style={
{
'stroke': gaugeSecondaryColor,
'--stroke-percent': 90 - currentPercent,
'--offset-factor-secondary': 'calc(1 - var(--offset-factor))',
'strokeDasharray':
'calc(var(--stroke-percent) * var(--percent-to-px)) var(--circumference)',
'transform':
'rotate(calc(1turn - 90deg - (var(--gap-percent) * var(--percent-to-deg) * var(--offset-factor-secondary)))) scaleY(-1)',
'transition': 'all var(--transition-length) ease var(--delay)',
'transformOrigin': 'calc(var(--circle-size) / 2) calc(var(--circle-size) / 2)',
} as React.CSSProperties
}
/>
)}
<circle
cx='50'
cy='50'
r='45'
strokeWidth='10'
strokeDashoffset='0'
strokeLinecap='round'
strokeLinejoin='round'
className='opacity-100'
style={
{
'stroke': gaugePrimaryColor,
'--stroke-percent': currentPercent,
'strokeDasharray':
'calc(var(--stroke-percent) * var(--percent-to-px)) var(--circumference)',
'transition':
'var(--transition-length) ease var(--delay),stroke var(--transition-length) ease var(--delay)',
'transitionProperty': 'stroke-dasharray,transform',
'transform':
'rotate(calc(-90deg + var(--gap-percent) * var(--offset-factor) * var(--percent-to-deg)))',
'transformOrigin': 'calc(var(--circle-size) / 2) calc(var(--circle-size) / 2)',
} as React.CSSProperties
}
/>
</svg>
<span
data-current-value={currentPercent}
className='animate-in fade-in absolute inset-0 m-auto size-fit delay-[var(--delay)] duration-[var(--transition-length)] ease-linear'
>
{currentPercent}
</span>
</div>
);
}

View File

@ -1,26 +0,0 @@
import { ReactNode } from 'react';
import { cn } from '../../lib/utils';
export default function AnimatedGradientText({
children,
className,
}: {
children: ReactNode;
className?: string;
}) {
return (
<div
className={cn(
'group relative mx-auto flex max-w-fit flex-row items-center justify-center rounded-2xl bg-white/40 px-4 py-1.5 text-sm font-medium shadow-[inset_0_-8px_10px_#8fdfff1f] backdrop-blur-sm transition-shadow duration-500 ease-out [--bg-size:300%] hover:shadow-[inset_0_-5px_10px_#8fdfff3f] dark:bg-black/40',
className,
)}
>
<div
className={`animate-gradient absolute inset-0 block h-full w-full bg-gradient-to-r from-[#ffaa40]/50 via-[#9c40ff]/50 to-[#ffaa40]/50 bg-[length:var(--bg-size)_100%] p-[1px] [border-radius:inherit] ![mask-composite:subtract] [mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]`}
/>
{children}
</div>
);
}

View File

@ -1,139 +0,0 @@
'use client';
import { motion } from 'framer-motion';
import { useEffect, useId, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
interface GridPatternProps {
width?: number;
height?: number;
x?: number;
y?: number;
strokeDasharray?: any;
numSquares?: number;
className?: string;
maxOpacity?: number;
duration?: number;
repeatDelay?: number;
}
export function GridPattern({
width = 40,
height = 40,
x = -1,
y = -1,
strokeDasharray = 0,
numSquares = 50,
className,
maxOpacity = 0.5,
duration = 4,
repeatDelay = 0.5,
...props
}: GridPatternProps) {
const id = useId();
const containerRef = useRef(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const [squares, setSquares] = useState(() => generateSquares(numSquares));
function getPos() {
return [
Math.floor((Math.random() * dimensions.width) / width),
Math.floor((Math.random() * dimensions.height) / height),
];
}
// Adjust the generateSquares function to return objects with an id, x, and y
function generateSquares(count: number) {
return Array.from({ length: count }, (_, i) => ({
id: i,
pos: getPos(),
}));
}
// Function to update a single square's position
const updateSquarePosition = (id: number) => {
setSquares((currentSquares) =>
currentSquares.map((sq) =>
sq.id === id
? {
...sq,
pos: getPos(),
}
: sq,
),
);
};
// Update squares to animate in
useEffect(() => {
if (dimensions.width && dimensions.height) {
setSquares(generateSquares(numSquares));
}
}, [dimensions, numSquares]);
// Resize observer to update container dimensions
useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
setDimensions({
width: entry.contentRect.width,
height: entry.contentRect.height,
});
}
});
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}
return () => {
if (containerRef.current) {
resizeObserver.unobserve(containerRef.current);
}
};
}, [containerRef]);
return (
<svg
ref={containerRef}
aria-hidden='true'
className={cn(
'pointer-events-none absolute inset-0 h-full w-full fill-gray-400/30 stroke-gray-400/30',
className,
)}
{...props}
>
<defs>
<pattern id={id} width={width} height={height} patternUnits='userSpaceOnUse' x={x} y={y}>
<path d={`M.5 ${height}V.5H${width}`} fill='none' strokeDasharray={strokeDasharray} />
</pattern>
</defs>
<rect width='100%' height='100%' fill={`url(#${id})`} />
<svg x={x} y={y} className='overflow-visible'>
{squares.map(({ pos: [x, y], id }, index) => (
<motion.rect
initial={{ opacity: 0 }}
animate={{ opacity: maxOpacity }}
transition={{
duration,
repeat: 1,
delay: index * 0.1,
repeatType: 'reverse',
}}
onAnimationComplete={() => updateSquarePosition(id)}
key={`${x}-${y}-${index}`}
width={width - 1}
height={height - 1}
x={x * width + 1}
y={y * height + 1}
fill='currentColor'
strokeWidth='0'
/>
))}
</svg>
</svg>
);
}
export default GridPattern;

View File

@ -1,57 +0,0 @@
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import React, { ReactElement, useEffect, useMemo, useState } from 'react';
export interface AnimatedListProps {
className?: string;
children: React.ReactNode;
delay?: number;
}
export const AnimatedList = React.memo(
({ className, children, delay = 1000 }: AnimatedListProps) => {
const [index, setIndex] = useState(0);
const childrenArray = React.Children.toArray(children);
useEffect(() => {
const interval = setInterval(() => {
setIndex((prevIndex) => (prevIndex + 1) % childrenArray.length);
}, delay);
return () => clearInterval(interval);
}, [childrenArray.length, delay]);
const itemsToShow = useMemo(
() => childrenArray.slice(0, index + 1).reverse(),
[index, childrenArray],
);
return (
<div className={`flex flex-col items-center gap-4 ${className}`}>
<AnimatePresence>
{itemsToShow.map((item) => (
<AnimatedListItem key={(item as ReactElement).key}>{item}</AnimatedListItem>
))}
</AnimatePresence>
</div>
);
},
);
AnimatedList.displayName = 'AnimatedList';
export function AnimatedListItem({ children }: { children: React.ReactNode }) {
const animations = {
initial: { scale: 0, opacity: 0 },
animate: { scale: 1, opacity: 1, originY: 0 },
exit: { scale: 0, opacity: 0 },
transition: { type: 'spring', stiffness: 350, damping: 40 },
};
return (
<motion.div {...animations} layout className='mx-auto w-full'>
{children}
</motion.div>
);
}

View File

@ -0,0 +1,212 @@
// @ts-nocheck
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import React, { ReactNode, createContext, useContext, useEffect, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
interface ModalContextType {
open: boolean;
setOpen: (open: boolean) => void;
}
const ModalContext = createContext<ModalContextType | undefined>(undefined);
export const ModalProvider = ({ children }: { children: ReactNode }) => {
const [open, setOpen] = useState(false);
return <ModalContext.Provider value={{ open, setOpen }}>{children}</ModalContext.Provider>;
};
export const useModal = () => {
const context = useContext(ModalContext);
if (!context) {
throw new Error('useModal must be used within a ModalProvider');
}
return context;
};
export function Modal({ children }: { children: ReactNode }) {
return <ModalProvider>{children}</ModalProvider>;
}
export const ModalTrigger = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => {
const { setOpen } = useModal();
return (
<button
className={cn(
'relative overflow-hidden rounded-md px-4 py-2 text-center text-black dark:text-white',
className,
)}
onClick={() => setOpen(true)}
>
{children}
</button>
);
};
export const ModalBody = ({ children, className }: { children: ReactNode; className?: string }) => {
const { open } = useModal();
useEffect(() => {
if (open) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'auto';
}
}, [open]);
const modalRef = useRef(null);
const { setOpen } = useModal();
useOutsideClick(modalRef, () => setOpen(false));
return (
<AnimatePresence>
{open && (
<motion.div
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
backdropFilter: 'blur(10px)',
}}
exit={{
opacity: 0,
backdropFilter: 'blur(0px)',
}}
className='fixed inset-0 z-50 flex h-full w-full items-center justify-center [perspective:800px] [transform-style:preserve-3d]'
>
<Overlay />
<motion.div
ref={modalRef}
className={cn(
'relative z-50 flex max-h-[90%] min-h-[50%] flex-1 flex-col overflow-hidden border border-transparent bg-white md:max-w-[40%] md:rounded-2xl dark:border-neutral-800 dark:bg-neutral-950',
className,
)}
initial={{
opacity: 0,
scale: 0.5,
rotateX: 40,
y: 40,
}}
animate={{
opacity: 1,
scale: 1,
rotateX: 0,
y: 0,
}}
exit={{
opacity: 0,
scale: 0.8,
rotateX: 10,
}}
transition={{
type: 'spring',
stiffness: 260,
damping: 15,
}}
>
<CloseIcon />
{children}
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
};
export const ModalContent = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => {
return <div className={cn('flex flex-1 flex-col p-8 md:p-10', className)}>{children}</div>;
};
export const ModalFooter = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => {
return (
<div className={cn('flex justify-end bg-gray-100 p-4 dark:bg-neutral-900', className)}>
{children}
</div>
);
};
const Overlay = ({ className }: { className?: string }) => {
return (
<motion.div
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
backdropFilter: 'blur(10px)',
}}
exit={{
opacity: 0,
backdropFilter: 'blur(0px)',
}}
className={`fixed inset-0 z-50 h-full w-full bg-black bg-opacity-50 ${className}`}
></motion.div>
);
};
const CloseIcon = () => {
const { setOpen } = useModal();
return (
<button onClick={() => setOpen(false)} className='group absolute right-4 top-4'>
<svg
xmlns='http://www.w3.org/2000/svg'
width='24'
height='24'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeWidth='2'
strokeLinecap='round'
strokeLinejoin='round'
className='h-4 w-4 text-black transition duration-200 group-hover:rotate-3 group-hover:scale-125 dark:text-white'
>
<path stroke='none' d='M0 0h24v24H0z' fill='none' />
<path d='M18 6l-12 12' />
<path d='M6 6l12 12' />
</svg>
</button>
);
};
// Hook to detect clicks outside of a component.
// Add it in a separate file, I've added here for simplicity
export const useOutsideClick = (ref: React.RefObject<HTMLDivElement>, callback: Function) => {
useEffect(() => {
const listener = (event: any) => {
// DO NOTHING if the element being clicked is the target element or their children
if (!ref.current || ref.current.contains(event.target)) {
return;
}
callback(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
}, [ref, callback]);
};

View File

@ -1,40 +0,0 @@
import { CSSProperties, FC, ReactNode } from 'react';
import { cn } from '../../lib/utils';
interface AnimatedShinyTextProps {
children: ReactNode;
className?: string;
shimmerWidth?: number;
}
const AnimatedShinyText: FC<AnimatedShinyTextProps> = ({
children,
className,
shimmerWidth = 100,
}) => {
return (
<p
style={
{
'--shiny-width': `${shimmerWidth}px`,
} as CSSProperties
}
className={cn(
'mx-auto max-w-md text-neutral-600/70 dark:text-neutral-400/70',
// Shine effect
'animate-shiny-text bg-clip-text bg-no-repeat [background-position:0_0] [background-size:var(--shiny-width)_100%] [transition:background-position_1s_cubic-bezier(.6,.6,0,1)_infinite]',
// Shine gradient
'bg-gradient-to-r from-transparent via-black/80 via-50% to-transparent dark:via-white/80',
className,
)}
>
{children}
</p>
);
};
export default AnimatedShinyText;

View File

@ -1,64 +0,0 @@
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import React, { useState } from 'react';
interface AnimatedSubscribeButtonProps {
buttonColor: string;
buttonTextColor?: string;
subscribeStatus: boolean;
initialText: React.ReactElement | string;
changeText: React.ReactElement | string;
}
export const AnimatedSubscribeButton: React.FC<AnimatedSubscribeButtonProps> = ({
buttonColor,
subscribeStatus,
buttonTextColor,
changeText,
initialText,
}) => {
const [isSubscribed, setIsSubscribed] = useState<boolean>(subscribeStatus);
return (
<AnimatePresence mode='wait'>
{isSubscribed ? (
<motion.button
className='relative flex w-[200px] items-center justify-center overflow-hidden rounded-md bg-white p-[10px] outline outline-1 outline-black'
onClick={() => setIsSubscribed(false)}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.span
key='action'
className='relative block h-full w-full font-semibold'
initial={{ y: -50 }}
animate={{ y: 0 }}
style={{ color: buttonColor }}
>
{changeText}
</motion.span>
</motion.button>
) : (
<motion.button
className='relative flex w-[200px] cursor-pointer items-center justify-center rounded-md border-none p-[10px]'
style={{ backgroundColor: buttonColor, color: buttonTextColor }}
onClick={() => setIsSubscribed(true)}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.span
key='reaction'
className='relative block font-semibold'
initial={{ x: 0 }}
exit={{ x: 50, transition: { duration: 0.1 } }}
>
{initialText}
</motion.span>
</motion.button>
)}
</AnimatePresence>
);
};

View File

@ -0,0 +1,163 @@
'use client';
import { IconArrowLeft, IconArrowRight } from '@tabler/icons-react';
import { AnimatePresence, motion } from 'framer-motion';
import Image from 'next/image';
import { useEffect, useState } from 'react';
type Testimonial = {
quote: string;
name: string;
designation: string;
src: string;
};
export const AnimatedTestimonials = ({
testimonials,
autoplay = false,
}: {
testimonials: Testimonial[];
autoplay?: boolean;
}) => {
const [active, setActive] = useState(0);
const handleNext = () => {
setActive((prev) => (prev + 1) % testimonials.length);
};
const handlePrev = () => {
setActive((prev) => (prev - 1 + testimonials.length) % testimonials.length);
};
const isActive = (index: number) => {
return index === active;
};
useEffect(() => {
if (autoplay) {
const interval = setInterval(handleNext, 5000);
return () => clearInterval(interval);
}
}, [autoplay]);
const randomRotateY = () => {
return Math.floor(Math.random() * 21) - 10;
};
return (
<div className='mx-auto max-w-sm px-4 py-20 font-sans antialiased md:max-w-4xl md:px-8 lg:px-12'>
<div className='relative grid grid-cols-1 gap-20 md:grid-cols-2'>
<div>
<div className='relative h-80 w-full'>
<AnimatePresence>
{testimonials.map((testimonial, index) => (
<motion.div
key={testimonial.src}
initial={{
opacity: 0,
scale: 0.9,
z: -100,
rotate: randomRotateY(),
}}
animate={{
opacity: isActive(index) ? 1 : 0.7,
scale: isActive(index) ? 1 : 0.95,
z: isActive(index) ? 0 : -100,
rotate: isActive(index) ? 0 : randomRotateY(),
zIndex: isActive(index) ? 999 : testimonials.length + 2 - index,
y: isActive(index) ? [0, -80, 0] : 0,
}}
exit={{
opacity: 0,
scale: 0.9,
z: 100,
rotate: randomRotateY(),
}}
transition={{
duration: 0.4,
ease: 'easeInOut',
}}
className='absolute inset-0 origin-bottom'
>
<Image
src={testimonial.src}
alt={testimonial.name}
width={500}
height={500}
draggable={false}
className='h-full w-full rounded-3xl object-cover object-center'
/>
</motion.div>
))}
</AnimatePresence>
</div>
</div>
<div className='flex flex-col justify-between py-4'>
<motion.div
key={active}
initial={{
y: 20,
opacity: 0,
}}
animate={{
y: 0,
opacity: 1,
}}
exit={{
y: -20,
opacity: 0,
}}
transition={{
duration: 0.2,
ease: 'easeInOut',
}}
>
<h3 className='text-2xl font-bold text-black dark:text-white'>
{testimonials[active].name}
</h3>
<p className='text-sm text-gray-500 dark:text-neutral-500'>
{testimonials[active].designation}
</p>
<motion.p className='mt-8 text-lg text-gray-500 dark:text-neutral-300'>
{testimonials[active].quote.split(' ').map((word, index) => (
<motion.span
key={index}
initial={{
filter: 'blur(10px)',
opacity: 0,
y: 5,
}}
animate={{
filter: 'blur(0px)',
opacity: 1,
y: 0,
}}
transition={{
duration: 0.2,
ease: 'easeInOut',
delay: 0.02 * index,
}}
className='inline-block'
>
{word}&nbsp;
</motion.span>
))}
</motion.p>
</motion.div>
<div className='flex gap-4 pt-12 md:pt-0'>
<button
onClick={handlePrev}
className='group/button flex h-7 w-7 items-center justify-center rounded-full bg-gray-100 dark:bg-neutral-800'
>
<IconArrowLeft className='h-5 w-5 text-black transition-transform duration-300 group-hover/button:rotate-12 dark:text-neutral-400' />
</button>
<button
onClick={handleNext}
className='group/button flex h-7 w-7 items-center justify-center rounded-full bg-gray-100 dark:bg-neutral-800'
>
<IconArrowRight className='h-5 w-5 text-black transition-transform duration-300 group-hover/button:-rotate-12 dark:text-neutral-400' />
</button>
</div>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,78 @@
'use client';
import { AnimatePresence, motion, useMotionValue, useSpring, useTransform } from 'framer-motion';
import Image from 'next/image';
import { useState } from 'react';
export const AnimatedTooltip = ({
items,
}: {
items: {
id: number;
name: string;
designation: string;
image: string;
}[];
}) => {
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
const springConfig = { stiffness: 100, damping: 5 };
const x = useMotionValue(0); // going to set this value on mouse move
// rotate the tooltip
const rotate = useSpring(useTransform(x, [-100, 100], [-45, 45]), springConfig);
// translate the tooltip
const translateX = useSpring(useTransform(x, [-100, 100], [-50, 50]), springConfig);
const handleMouseMove = (event: any) => {
const halfWidth = event.target.offsetWidth / 2;
x.set(event.nativeEvent.offsetX - halfWidth); // set the x value, which is then used in transform and rotate
};
return (
<>
{items.map((item, idx) => (
<div
className='group relative -mr-4'
key={item.name}
onMouseEnter={() => setHoveredIndex(item.id)}
onMouseLeave={() => setHoveredIndex(null)}
>
<AnimatePresence mode='popLayout'>
{hoveredIndex === item.id && (
<motion.div
initial={{ opacity: 0, y: 20, scale: 0.6 }}
animate={{
opacity: 1,
y: 0,
scale: 1,
transition: {
type: 'spring',
stiffness: 260,
damping: 10,
},
}}
exit={{ opacity: 0, y: 20, scale: 0.6 }}
style={{
translateX: translateX,
rotate: rotate,
whiteSpace: 'nowrap',
}}
className='absolute -left-1/2 -top-16 z-50 flex translate-x-1/2 flex-col items-center justify-center rounded-md bg-black px-4 py-2 text-xs shadow-xl'
>
<div className='absolute inset-x-10 -bottom-px z-30 h-px w-[20%] bg-gradient-to-r from-transparent via-emerald-500 to-transparent' />
<div className='absolute -bottom-px left-10 z-30 h-px w-[40%] bg-gradient-to-r from-transparent via-sky-500 to-transparent' />
<div className='relative z-30 text-base font-bold text-white'>{item.name}</div>
<div className='text-xs text-white'>{item.designation}</div>
</motion.div>
)}
</AnimatePresence>
<Image
onMouseMove={handleMouseMove}
height={100}
width={100}
src={item.image}
alt={item.name}
className='relative !m-0 h-14 w-14 rounded-full border-2 border-white object-cover object-top !p-0 transition duration-500 group-hover:z-30 group-hover:scale-105'
/>
</div>
))}
</>
);
};

View File

@ -0,0 +1,275 @@
// @ts-nocheck
'use client';
import { IconArrowNarrowLeft, IconArrowNarrowRight, IconX } from '@tabler/icons-react';
import { AnimatePresence, motion } from 'framer-motion';
import Image, { ImageProps } from 'next/image';
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { cn } from '../../lib/utils';
interface CarouselProps {
items: JSX.Element[];
initialScroll?: number;
}
type Card = {
src: string;
title: string;
category: string;
content: React.ReactNode;
};
export const CarouselContext = createContext<{
onCardClose: (index: number) => void;
currentIndex: number;
}>({
onCardClose: () => {},
currentIndex: 0,
});
export const Carousel = ({ items, initialScroll = 0 }: CarouselProps) => {
const carouselRef = React.useRef<HTMLDivElement>(null);
const [canScrollLeft, setCanScrollLeft] = React.useState(false);
const [canScrollRight, setCanScrollRight] = React.useState(true);
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
if (carouselRef.current) {
carouselRef.current.scrollLeft = initialScroll;
checkScrollability();
}
}, [initialScroll]);
const checkScrollability = () => {
if (carouselRef.current) {
const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current;
setCanScrollLeft(scrollLeft > 0);
setCanScrollRight(scrollLeft < scrollWidth - clientWidth);
}
};
const scrollLeft = () => {
if (carouselRef.current) {
carouselRef.current.scrollBy({ left: -300, behavior: 'smooth' });
}
};
const scrollRight = () => {
if (carouselRef.current) {
carouselRef.current.scrollBy({ left: 300, behavior: 'smooth' });
}
};
const handleCardClose = (index: number) => {
if (carouselRef.current) {
const cardWidth = isMobile() ? 230 : 384; // (md:w-96)
const gap = isMobile() ? 4 : 8;
const scrollPosition = (cardWidth + gap) * (index + 1);
carouselRef.current.scrollTo({
left: scrollPosition,
behavior: 'smooth',
});
setCurrentIndex(index);
}
};
const isMobile = () => {
return window && window.innerWidth < 768;
};
return (
<CarouselContext.Provider value={{ onCardClose: handleCardClose, currentIndex }}>
<div className='relative w-full'>
<div
className='flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 [scrollbar-width:none] md:py-20'
ref={carouselRef}
onScroll={checkScrollability}
>
<div
className={cn(
'absolute right-0 z-[1000] h-auto w-[5%] overflow-hidden bg-gradient-to-l',
)}
></div>
<div
className={cn(
'flex flex-row justify-start gap-4 pl-4',
'mx-auto max-w-7xl', // remove max-w-4xl if you want the carousel to span the full width of its container
)}
>
{items.map((item, index) => (
<motion.div
initial={{
opacity: 0,
y: 20,
}}
animate={{
opacity: 1,
y: 0,
transition: {
duration: 0.5,
delay: 0.2 * index,
ease: 'easeOut',
once: true,
},
}}
key={'card' + index}
className='rounded-3xl last:pr-[5%] md:last:pr-[33%]'
>
{item}
</motion.div>
))}
</div>
</div>
<div className='mr-10 flex justify-end gap-2'>
<button
className='relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 disabled:opacity-50'
onClick={scrollLeft}
disabled={!canScrollLeft}
>
<IconArrowNarrowLeft className='h-6 w-6 text-gray-500' />
</button>
<button
className='relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 disabled:opacity-50'
onClick={scrollRight}
disabled={!canScrollRight}
>
<IconArrowNarrowRight className='h-6 w-6 text-gray-500' />
</button>
</div>
</div>
</CarouselContext.Provider>
);
};
export const Card = ({
card,
index,
layout = false,
}: {
card: Card;
index: number;
layout?: boolean;
}) => {
const [open, setOpen] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
const { onCardClose, currentIndex } = useContext(CarouselContext);
useEffect(() => {
function onKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') {
handleClose();
}
}
if (open) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'auto';
}
window.addEventListener('keydown', onKeyDown);
return () => window.removeEventListener('keydown', onKeyDown);
}, [open]);
useOutsideClick(containerRef, () => handleClose());
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
onCardClose(index);
};
return (
<>
<AnimatePresence>
{open && (
<div className='fixed inset-0 z-50 h-screen overflow-auto'>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className='fixed inset-0 h-full w-full bg-black/80 backdrop-blur-lg'
/>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
ref={containerRef}
layoutId={layout ? `card-${card.title}` : undefined}
className='relative z-[60] mx-auto my-10 h-fit max-w-5xl rounded-3xl bg-white p-4 font-sans md:p-10 dark:bg-neutral-900'
>
<button
className='sticky right-0 top-4 ml-auto flex h-8 w-8 items-center justify-center rounded-full bg-black dark:bg-white'
onClick={handleClose}
>
<IconX className='h-6 w-6 text-neutral-100 dark:text-neutral-900' />
</button>
<motion.p
layoutId={layout ? `category-${card.title}` : undefined}
className='text-base font-medium text-black dark:text-white'
>
{card.category}
</motion.p>
<motion.p
layoutId={layout ? `title-${card.title}` : undefined}
className='mt-4 text-2xl font-semibold text-neutral-700 md:text-5xl dark:text-white'
>
{card.title}
</motion.p>
<div className='py-10'>{card.content}</div>
</motion.div>
</div>
)}
</AnimatePresence>
<motion.button
layoutId={layout ? `card-${card.title}` : undefined}
onClick={handleOpen}
className='relative z-10 flex h-80 w-56 flex-col items-start justify-start overflow-hidden rounded-3xl bg-gray-100 md:h-[40rem] md:w-96 dark:bg-neutral-900'
>
<div className='pointer-events-none absolute inset-x-0 top-0 z-30 h-full bg-gradient-to-b from-black/50 via-transparent to-transparent' />
<div className='relative z-40 p-8'>
<motion.p
layoutId={layout ? `category-${card.category}` : undefined}
className='text-left font-sans text-sm font-medium text-white md:text-base'
>
{card.category}
</motion.p>
<motion.p
layoutId={layout ? `title-${card.title}` : undefined}
className='mt-2 max-w-xs text-left font-sans text-xl font-semibold text-white [text-wrap:balance] md:text-3xl'
>
{card.title}
</motion.p>
</div>
<BlurImage
src={card.src}
alt={card.title}
fill
className='absolute inset-0 z-10 object-cover'
/>
</motion.button>
</>
);
};
export const BlurImage = ({ height, width, src, className, alt, ...rest }: ImageProps) => {
const [isLoading, setLoading] = useState(true);
return (
<Image
className={cn('transition duration-300', isLoading ? 'blur-sm' : 'blur-0', className)}
onLoad={() => setLoading(false)}
src={src}
width={width}
height={height}
loading='lazy'
decoding='async'
blurDataURL={typeof src === 'string' ? src : undefined}
alt={alt ? alt : 'Background of a beautiful view'}
{...rest}
/>
);
};

View File

@ -0,0 +1,41 @@
// @ts-nocheck
'use client';
import React, { ReactNode } from 'react';
import { cn } from '../../lib/utils';
interface AuroraBackgroundProps extends React.HTMLProps<HTMLDivElement> {
children: ReactNode;
showRadialGradient?: boolean;
}
export const AuroraBackground = ({
className,
children,
showRadialGradient = true,
...props
}: AuroraBackgroundProps) => {
return (
<main>
<div
className={cn(
'transition-bg relative flex h-[100vh] flex-col items-center justify-center bg-zinc-50 text-slate-950 dark:bg-zinc-900',
className,
)}
{...props}
>
<div className='absolute inset-0 overflow-hidden'>
<div
// I'm sorry but this is what peak developer performance looks like // trigger warning
className={cn(
`after:animate-aurora pointer-events-none absolute -inset-[10px] opacity-50 blur-[10px] invert filter will-change-transform [--aurora:repeating-linear-gradient(100deg,var(--blue-500)_10%,var(--indigo-300)_15%,var(--blue-300)_20%,var(--violet-200)_25%,var(--blue-400)_30%)] [--dark-gradient:repeating-linear-gradient(100deg,var(--black)_0%,var(--black)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--black)_16%)] [--white-gradient:repeating-linear-gradient(100deg,var(--white)_0%,var(--white)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--white)_16%)] [background-image:var(--white-gradient),var(--aurora)] [background-position:50%_50%,50%_50%] [background-size:300%,_200%] after:absolute after:inset-0 after:mix-blend-difference after:content-[""] after:[background-attachment:fixed] after:[background-image:var(--white-gradient),var(--aurora)] after:[background-size:200%,_100%] dark:invert-0 dark:[background-image:var(--dark-gradient),var(--aurora)] after:dark:[background-image:var(--dark-gradient),var(--aurora)]`,
showRadialGradient &&
`[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]`,
)}
></div>
</div>
{children}
</div>
</main>
);
};

View File

@ -1,34 +0,0 @@
'use client';
import { cn } from '../../lib/utils';
interface AvatarCirclesProps {
className?: string;
numPeople?: number;
avatarUrls: string[];
}
const AvatarCircles = ({ numPeople, className, avatarUrls }: AvatarCirclesProps) => {
return (
<div className={cn('z-10 flex -space-x-4 rtl:space-x-reverse', className)}>
{avatarUrls.map((url, index) => (
<img
key={index}
className='h-10 w-10 rounded-full border-2 border-white dark:border-gray-800'
src={url}
width={40}
height={40}
alt={`Avatar ${index + 1}`}
/>
))}
<a
className='flex h-10 w-10 items-center justify-center rounded-full border-2 border-white bg-black text-center text-xs font-medium text-white hover:bg-gray-600 dark:border-gray-800 dark:bg-white dark:text-black'
href=''
>
+{numPeople}
</a>
</div>
);
};
export default AvatarCircles;

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as AvatarPrimitive from '@radix-ui/react-avatar'; import * as AvatarPrimitive from '@radix-ui/react-avatar';

View File

@ -0,0 +1,254 @@
// @ts-nocheck
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import React, { useEffect, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
export const BackgroundBeamsWithCollision = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const parentRef = useRef<HTMLDivElement>(null);
const beams = [
{
initialX: 10,
translateX: 10,
duration: 7,
repeatDelay: 3,
delay: 2,
},
{
initialX: 600,
translateX: 600,
duration: 3,
repeatDelay: 3,
delay: 4,
},
{
initialX: 100,
translateX: 100,
duration: 7,
repeatDelay: 7,
className: 'h-6',
},
{
initialX: 400,
translateX: 400,
duration: 5,
repeatDelay: 14,
delay: 4,
},
{
initialX: 800,
translateX: 800,
duration: 11,
repeatDelay: 2,
className: 'h-20',
},
{
initialX: 1000,
translateX: 1000,
duration: 4,
repeatDelay: 2,
className: 'h-12',
},
{
initialX: 1200,
translateX: 1200,
duration: 6,
repeatDelay: 4,
delay: 2,
className: 'h-6',
},
];
return (
<div
ref={parentRef}
className={cn(
'relative flex h-96 w-full items-center justify-center overflow-hidden bg-gradient-to-b from-white to-neutral-100 md:h-[40rem] dark:from-neutral-950 dark:to-neutral-800',
// h-screen if you want bigger
className,
)}
>
{beams.map((beam) => (
<CollisionMechanism
key={beam.initialX + 'beam-idx'}
beamOptions={beam}
containerRef={containerRef}
parentRef={parentRef}
/>
))}
{children}
<div
ref={containerRef}
className='pointer-events-none absolute inset-x-0 bottom-0 w-full bg-neutral-100'
style={{
boxShadow:
'0 0 24px rgba(34, 42, 53, 0.06), 0 1px 1px rgba(0, 0, 0, 0.05), 0 0 0 1px rgba(34, 42, 53, 0.04), 0 0 4px rgba(34, 42, 53, 0.08), 0 16px 68px rgba(47, 48, 55, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1) inset',
}}
></div>
</div>
);
};
const CollisionMechanism = React.forwardRef<
HTMLDivElement,
{
containerRef: React.RefObject<HTMLDivElement>;
parentRef: React.RefObject<HTMLDivElement>;
beamOptions?: {
initialX?: number;
translateX?: number;
initialY?: number;
translateY?: number;
rotate?: number;
className?: string;
duration?: number;
delay?: number;
repeatDelay?: number;
};
}
>(({ parentRef, containerRef, beamOptions = {} }, ref) => {
const beamRef = useRef<HTMLDivElement>(null);
const [collision, setCollision] = useState<{
detected: boolean;
coordinates: { x: number; y: number } | null;
}>({
detected: false,
coordinates: null,
});
const [beamKey, setBeamKey] = useState(0);
const [cycleCollisionDetected, setCycleCollisionDetected] = useState(false);
useEffect(() => {
const checkCollision = () => {
if (beamRef.current && containerRef.current && parentRef.current && !cycleCollisionDetected) {
const beamRect = beamRef.current.getBoundingClientRect();
const containerRect = containerRef.current.getBoundingClientRect();
const parentRect = parentRef.current.getBoundingClientRect();
if (beamRect.bottom >= containerRect.top) {
const relativeX = beamRect.left - parentRect.left + beamRect.width / 2;
const relativeY = beamRect.bottom - parentRect.top;
setCollision({
detected: true,
coordinates: {
x: relativeX,
y: relativeY,
},
});
setCycleCollisionDetected(true);
}
}
};
const animationInterval = setInterval(checkCollision, 50);
return () => clearInterval(animationInterval);
}, [cycleCollisionDetected, containerRef]);
useEffect(() => {
if (collision.detected && collision.coordinates) {
setTimeout(() => {
setCollision({ detected: false, coordinates: null });
setCycleCollisionDetected(false);
}, 2000);
setTimeout(() => {
setBeamKey((prevKey) => prevKey + 1);
}, 2000);
}
}, [collision]);
return (
<>
<motion.div
key={beamKey}
ref={beamRef}
animate='animate'
initial={{
translateY: beamOptions.initialY || '-200px',
translateX: beamOptions.initialX || '0px',
rotate: beamOptions.rotate || 0,
}}
variants={{
animate: {
translateY: beamOptions.translateY || '1800px',
translateX: beamOptions.translateX || '0px',
rotate: beamOptions.rotate || 0,
},
}}
transition={{
duration: beamOptions.duration || 8,
repeat: Infinity,
repeatType: 'loop',
ease: 'linear',
delay: beamOptions.delay || 0,
repeatDelay: beamOptions.repeatDelay || 0,
}}
className={cn(
'absolute left-0 top-20 m-auto h-14 w-px rounded-full bg-gradient-to-t from-indigo-500 via-purple-500 to-transparent',
beamOptions.className,
)}
/>
<AnimatePresence>
{collision.detected && collision.coordinates && (
<Explosion
key={`${collision.coordinates.x}-${collision.coordinates.y}`}
className=''
style={{
left: `${collision.coordinates.x}px`,
top: `${collision.coordinates.y}px`,
transform: 'translate(-50%, -50%)',
}}
/>
)}
</AnimatePresence>
</>
);
});
CollisionMechanism.displayName = 'CollisionMechanism';
const Explosion = ({ ...props }: React.HTMLProps<HTMLDivElement>) => {
const spans = Array.from({ length: 20 }, (_, index) => ({
id: index,
initialX: 0,
initialY: 0,
directionX: Math.floor(Math.random() * 80 - 40),
directionY: Math.floor(Math.random() * -50 - 10),
}));
return (
<div {...props} className={cn('absolute z-50 h-2 w-2', props.className)}>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 1.5, ease: 'easeOut' }}
className='absolute -inset-x-10 top-0 m-auto h-2 w-10 rounded-full bg-gradient-to-r from-transparent via-indigo-500 to-transparent blur-sm'
></motion.div>
{spans.map((span) => (
<motion.span
key={span.id}
initial={{ x: span.initialX, y: span.initialY, opacity: 1 }}
animate={{
x: span.directionX,
y: span.directionY,
opacity: 0,
}}
transition={{ duration: Math.random() * 1.5 + 0.5, ease: 'easeOut' }}
className='absolute h-1 w-1 rounded-full bg-gradient-to-b from-indigo-500 to-purple-500'
/>
))}
</div>
);
};

View File

@ -0,0 +1,140 @@
// @ts-nocheck
'use client';
import { motion } from 'framer-motion';
import React from 'react';
import { cn } from '../../lib/utils';
export const BackgroundBeams = React.memo(({ className }: { className?: string }) => {
const paths = [
'M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875',
'M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867',
'M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859',
'M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851',
'M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843',
'M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835',
'M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827',
'M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819',
'M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811',
'M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803',
'M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795',
'M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787',
'M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779',
'M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771',
'M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763',
'M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755',
'M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747',
'M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739',
'M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731',
'M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723',
'M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715',
'M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707',
'M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699',
'M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691',
'M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683',
'M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675',
'M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667',
'M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659',
'M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651',
'M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643',
'M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635',
'M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627',
'M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619',
'M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611',
'M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603',
'M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595',
'M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587',
'M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579',
'M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571',
'M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563',
'M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555',
'M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547',
'M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539',
'M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531',
'M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523',
'M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515',
'M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507',
'M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499',
'M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491',
'M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483',
];
return (
<div
className={cn(
'absolute inset-0 flex h-full w-full items-center justify-center [mask-repeat:no-repeat] [mask-size:40px]',
className,
)}
>
<svg
className='pointer-events-none absolute z-0 h-full w-full'
width='100%'
height='100%'
viewBox='0 0 696 316'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483M-30 -589C-30 -589 38 -184 502 -57C966 70 1034 475 1034 475M-23 -597C-23 -597 45 -192 509 -65C973 62 1041 467 1041 467M-16 -605C-16 -605 52 -200 516 -73C980 54 1048 459 1048 459M-9 -613C-9 -613 59 -208 523 -81C987 46 1055 451 1055 451M-2 -621C-2 -621 66 -216 530 -89C994 38 1062 443 1062 443M5 -629C5 -629 73 -224 537 -97C1001 30 1069 435 1069 435M12 -637C12 -637 80 -232 544 -105C1008 22 1076 427 1076 427M19 -645C19 -645 87 -240 551 -113C1015 14 1083 419 1083 419'
stroke='url(#paint0_radial_242_278)'
strokeOpacity='0.05'
strokeWidth='0.5'
></path>
{paths.map((path, index) => (
<motion.path
key={`path-` + index}
d={path}
stroke={`url(#linearGradient-${index})`}
strokeOpacity='0.4'
strokeWidth='0.5'
></motion.path>
))}
<defs>
{paths.map((path, index) => (
<motion.linearGradient
id={`linearGradient-${index}`}
key={`gradient-${index}`}
initial={{
x1: '0%',
x2: '0%',
y1: '0%',
y2: '0%',
}}
animate={{
x1: ['0%', '100%'],
x2: ['0%', '95%'],
y1: ['0%', '100%'],
y2: ['0%', `${93 + Math.random() * 8}%`],
}}
transition={{
duration: Math.random() * 10 + 10,
ease: 'easeInOut',
repeat: Infinity,
delay: Math.random() * 10,
}}
>
<stop stopColor='#18CCFC' stopOpacity='0'></stop>
<stop stopColor='#18CCFC'></stop>
<stop offset='32.5%' stopColor='#6344F5'></stop>
<stop offset='100%' stopColor='#AE48FF' stopOpacity='0'></stop>
</motion.linearGradient>
))}
<radialGradient
id='paint0_radial_242_278'
cx='0'
cy='0'
r='1'
gradientUnits='userSpaceOnUse'
gradientTransform='translate(352 34) rotate(90) scale(555 1560.62)'
>
<stop offset='0.0666667' stopColor='var(--neutral-300)'></stop>
<stop offset='0.243243' stopColor='var(--neutral-300)'></stop>
<stop offset='0.43594' stopColor='white' stopOpacity='0'></stop>
</radialGradient>
</defs>
</svg>
</div>
);
});
BackgroundBeams.displayName = 'BackgroundBeams';

View File

@ -0,0 +1,70 @@
// @ts-nocheck
'use client';
import { motion } from 'framer-motion';
import React from 'react';
import { cn } from '../../lib/utils';
export const BoxesCore = ({ className, ...rest }: { className?: string }) => {
const rows = new Array(150).fill(1);
const cols = new Array(100).fill(1);
let colors = [
'--sky-300',
'--pink-300',
'--green-300',
'--yellow-300',
'--red-300',
'--purple-300',
'--blue-300',
'--indigo-300',
'--violet-300',
];
const getRandomColor = () => {
return colors[Math.floor(Math.random() * colors.length)];
};
return (
<div
style={{
transform: `translate(-40%,-60%) skewX(-48deg) skewY(14deg) scale(0.675) rotate(0deg) translateZ(0)`,
}}
className={cn(
'absolute -top-1/4 left-1/4 z-0 flex h-full w-full -translate-x-1/2 -translate-y-1/2 p-4',
className,
)}
{...rest}
>
{rows.map((_, i) => (
<motion.div key={`row` + i} className='relative h-8 w-16 border-l border-slate-700'>
{cols.map((_, j) => (
<motion.div
whileHover={{
backgroundColor: `var(${getRandomColor()})`,
transition: { duration: 0 },
}}
animate={{
transition: { duration: 2 },
}}
key={`col` + j}
className='relative h-8 w-16 border-r border-t border-slate-700'
>
{j % 2 === 0 && i % 2 === 0 ? (
<svg
xmlns='http://www.w3.org/2000/svg'
fill='none'
viewBox='0 0 24 24'
strokeWidth='1.5'
stroke='currentColor'
className='pointer-events-none absolute -left-[22px] -top-[14px] h-6 w-10 stroke-[1px] text-slate-700'
>
<path strokeLinecap='round' strokeLinejoin='round' d='M12 6v12m6-6H6' />
</svg>
) : null}
</motion.div>
))}
</motion.div>
))}
</div>
);
};
export const Boxes = React.memo(BoxesCore);

View File

@ -0,0 +1,172 @@
// @ts-nocheck
'use client';
import { useEffect, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
export const BackgroundGradientAnimation = ({
gradientBackgroundStart = 'rgb(108, 0, 162)',
gradientBackgroundEnd = 'rgb(0, 17, 82)',
firstColor = '18, 113, 255',
secondColor = '221, 74, 255',
thirdColor = '100, 220, 255',
fourthColor = '200, 50, 50',
fifthColor = '180, 180, 50',
pointerColor = '140, 100, 255',
size = '80%',
blendingValue = 'hard-light',
children,
className,
interactive = true,
containerClassName,
}: {
gradientBackgroundStart?: string;
gradientBackgroundEnd?: string;
firstColor?: string;
secondColor?: string;
thirdColor?: string;
fourthColor?: string;
fifthColor?: string;
pointerColor?: string;
size?: string;
blendingValue?: string;
children?: React.ReactNode;
className?: string;
interactive?: boolean;
containerClassName?: string;
}) => {
const interactiveRef = useRef<HTMLDivElement>(null);
const [curX, setCurX] = useState(0);
const [curY, setCurY] = useState(0);
const [tgX, setTgX] = useState(0);
const [tgY, setTgY] = useState(0);
useEffect(() => {
document.body.style.setProperty('--gradient-background-start', gradientBackgroundStart);
document.body.style.setProperty('--gradient-background-end', gradientBackgroundEnd);
document.body.style.setProperty('--first-color', firstColor);
document.body.style.setProperty('--second-color', secondColor);
document.body.style.setProperty('--third-color', thirdColor);
document.body.style.setProperty('--fourth-color', fourthColor);
document.body.style.setProperty('--fifth-color', fifthColor);
document.body.style.setProperty('--pointer-color', pointerColor);
document.body.style.setProperty('--size', size);
document.body.style.setProperty('--blending-value', blendingValue);
}, []);
useEffect(() => {
function move() {
if (!interactiveRef.current) {
return;
}
setCurX(curX + (tgX - curX) / 20);
setCurY(curY + (tgY - curY) / 20);
interactiveRef.current.style.transform = `translate(${Math.round(
curX,
)}px, ${Math.round(curY)}px)`;
}
move();
}, [tgX, tgY]);
const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
if (interactiveRef.current) {
const rect = interactiveRef.current.getBoundingClientRect();
setTgX(event.clientX - rect.left);
setTgY(event.clientY - rect.top);
}
};
const [isSafari, setIsSafari] = useState(false);
useEffect(() => {
setIsSafari(/^((?!chrome|android).)*safari/i.test(navigator.userAgent));
}, []);
return (
<div
className={cn(
'relative left-0 top-0 h-screen w-screen overflow-hidden bg-[linear-gradient(40deg,var(--gradient-background-start),var(--gradient-background-end))]',
containerClassName,
)}
>
<svg className='hidden'>
<defs>
<filter id='blurMe'>
<feGaussianBlur in='SourceGraphic' stdDeviation='10' result='blur' />
<feColorMatrix
in='blur'
mode='matrix'
values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -8'
result='goo'
/>
<feBlend in='SourceGraphic' in2='goo' />
</filter>
</defs>
</svg>
<div className={cn('', className)}>{children}</div>
<div
className={cn(
'gradients-container h-full w-full blur-lg',
isSafari ? 'blur-2xl' : '[filter:url(#blurMe)_blur(40px)]',
)}
>
<div
className={cn(
`absolute [background:radial-gradient(circle_at_center,_var(--first-color)_0,_var(--first-color)_50%)_no-repeat]`,
`left-[calc(50%-var(--size)/2)] top-[calc(50%-var(--size)/2)] h-[var(--size)] w-[var(--size)] [mix-blend-mode:var(--blending-value)]`,
`[transform-origin:center_center]`,
`animate-first`,
`opacity-100`,
)}
></div>
<div
className={cn(
`absolute [background:radial-gradient(circle_at_center,_rgba(var(--second-color),_0.8)_0,_rgba(var(--second-color),_0)_50%)_no-repeat]`,
`left-[calc(50%-var(--size)/2)] top-[calc(50%-var(--size)/2)] h-[var(--size)] w-[var(--size)] [mix-blend-mode:var(--blending-value)]`,
`[transform-origin:calc(50%-400px)]`,
`animate-second`,
`opacity-100`,
)}
></div>
<div
className={cn(
`absolute [background:radial-gradient(circle_at_center,_rgba(var(--third-color),_0.8)_0,_rgba(var(--third-color),_0)_50%)_no-repeat]`,
`left-[calc(50%-var(--size)/2)] top-[calc(50%-var(--size)/2)] h-[var(--size)] w-[var(--size)] [mix-blend-mode:var(--blending-value)]`,
`[transform-origin:calc(50%+400px)]`,
`animate-third`,
`opacity-100`,
)}
></div>
<div
className={cn(
`absolute [background:radial-gradient(circle_at_center,_rgba(var(--fourth-color),_0.8)_0,_rgba(var(--fourth-color),_0)_50%)_no-repeat]`,
`left-[calc(50%-var(--size)/2)] top-[calc(50%-var(--size)/2)] h-[var(--size)] w-[var(--size)] [mix-blend-mode:var(--blending-value)]`,
`[transform-origin:calc(50%-200px)]`,
`animate-fourth`,
`opacity-70`,
)}
></div>
<div
className={cn(
`absolute [background:radial-gradient(circle_at_center,_rgba(var(--fifth-color),_0.8)_0,_rgba(var(--fifth-color),_0)_50%)_no-repeat]`,
`left-[calc(50%-var(--size)/2)] top-[calc(50%-var(--size)/2)] h-[var(--size)] w-[var(--size)] [mix-blend-mode:var(--blending-value)]`,
`[transform-origin:calc(50%-800px)_calc(50%+800px)]`,
`animate-fifth`,
`opacity-100`,
)}
></div>
{interactive && (
<div
ref={interactiveRef}
onMouseMove={handleMouseMove}
className={cn(
`absolute [background:radial-gradient(circle_at_center,_rgba(var(--pointer-color),_0.8)_0,_rgba(var(--pointer-color),_0)_50%)_no-repeat]`,
`-left-1/2 -top-1/2 h-full w-full [mix-blend-mode:var(--blending-value)]`,
`opacity-70`,
)}
></div>
)}
</div>
</div>
);
};

View File

@ -0,0 +1,73 @@
// @ts-nocheck
import { motion } from 'framer-motion';
import React from 'react';
import { cn } from '../../lib/utils';
export const BackgroundGradient = ({
children,
className,
containerClassName,
animate = true,
}: {
children?: React.ReactNode;
className?: string;
containerClassName?: string;
animate?: boolean;
}) => {
const variants = {
initial: {
backgroundPosition: '0 50%',
},
animate: {
backgroundPosition: ['0, 50%', '100% 50%', '0 50%'],
},
};
return (
<div className={cn('group relative p-[4px]', containerClassName)}>
<motion.div
variants={animate ? variants : undefined}
initial={animate ? 'initial' : undefined}
animate={animate ? 'animate' : undefined}
transition={
animate
? {
duration: 5,
repeat: Infinity,
repeatType: 'reverse',
}
: undefined
}
style={{
backgroundSize: animate ? '400% 400%' : undefined,
}}
className={cn(
'absolute inset-0 z-[1] rounded-3xl opacity-60 blur-xl transition duration-500 will-change-transform group-hover:opacity-100',
'bg-[radial-gradient(circle_farthest-side_at_0_100%,#00ccb1,transparent),radial-gradient(circle_farthest-side_at_100%_0,#7b61ff,transparent),radial-gradient(circle_farthest-side_at_100%_100%,#ffc414,transparent),radial-gradient(circle_farthest-side_at_0_0,#1ca0fb,#141316)]',
)}
/>
<motion.div
variants={animate ? variants : undefined}
initial={animate ? 'initial' : undefined}
animate={animate ? 'animate' : undefined}
transition={
animate
? {
duration: 5,
repeat: Infinity,
repeatType: 'reverse',
}
: undefined
}
style={{
backgroundSize: animate ? '400% 400%' : undefined,
}}
className={cn(
'absolute inset-0 z-[1] rounded-3xl will-change-transform',
'bg-[radial-gradient(circle_farthest-side_at_0_100%,#00ccb1,transparent),radial-gradient(circle_farthest-side_at_100%_0,#7b61ff,transparent),radial-gradient(circle_farthest-side_at_100%_100%,#ffc414,transparent),radial-gradient(circle_farthest-side_at_0_0,#1ca0fb,#141316)]',
)}
/>
<div className={cn('relative z-10', className)}>{children}</div>
</div>
);
};

View File

@ -0,0 +1,143 @@
// @ts-nocheck
'use client';
import { motion } from 'framer-motion';
import React from 'react';
import { cn } from '../../lib/utils';
export const BackgroundLines = ({
children,
className,
svgOptions,
}: {
children: React.ReactNode;
className?: string;
svgOptions?: {
duration?: number;
};
}) => {
return (
<div className={cn('h-[20rem] w-full bg-white md:h-screen dark:bg-black', className)}>
<SVG svgOptions={svgOptions} />
{children}
</div>
);
};
const pathVariants = {
initial: { strokeDashoffset: 800, strokeDasharray: '50 800' },
animate: {
strokeDashoffset: 0,
strokeDasharray: '20 800',
opacity: [0, 1, 1, 0],
},
};
const SVG = ({
svgOptions,
}: {
svgOptions?: {
duration?: number;
};
}) => {
const paths = [
'M720 450C720 450 742.459 440.315 755.249 425.626C768.039 410.937 778.88 418.741 789.478 401.499C800.076 384.258 817.06 389.269 826.741 380.436C836.423 371.603 851.957 364.826 863.182 356.242C874.408 347.657 877.993 342.678 898.867 333.214C919.741 323.75 923.618 319.88 934.875 310.177C946.133 300.474 960.784 300.837 970.584 287.701C980.384 274.564 993.538 273.334 1004.85 263.087C1016.15 252.84 1026.42 250.801 1038.22 242.1C1050.02 233.399 1065.19 230.418 1074.63 215.721C1084.07 201.024 1085.49 209.128 1112.65 194.884C1139.8 180.64 1132.49 178.205 1146.43 170.636C1160.37 163.066 1168.97 158.613 1181.46 147.982C1193.95 137.35 1191.16 131.382 1217.55 125.645C1243.93 119.907 1234.19 118.899 1254.53 100.846C1274.86 82.7922 1275.12 92.8914 1290.37 76.09C1305.62 59.2886 1313.91 62.1868 1323.19 56.7536C1332.48 51.3204 1347.93 42.8082 1361.95 32.1468C1375.96 21.4855 1374.06 25.168 1397.08 10.1863C1420.09 -4.79534 1421.41 -3.16992 1431.52 -15.0078',
'M720 450C720 450 741.044 435.759 753.062 410.636C765.079 385.514 770.541 386.148 782.73 370.489C794.918 354.83 799.378 353.188 811.338 332.597C823.298 312.005 825.578 306.419 843.707 295.493C861.837 284.568 856.194 273.248 877.376 256.48C898.558 239.713 887.536 227.843 909.648 214.958C931.759 202.073 925.133 188.092 941.063 177.621C956.994 167.151 952.171 154.663 971.197 135.041C990.222 115.418 990.785 109.375 999.488 96.1291C1008.19 82.8827 1011.4 82.2181 1032.65 61.8861C1053.9 41.5541 1045.74 48.0281 1064.01 19.5798C1082.29 -8.86844 1077.21 -3.89415 1093.7 -19.66C1110.18 -35.4258 1105.91 -46.1146 1127.68 -60.2834C1149.46 -74.4523 1144.37 -72.1024 1154.18 -97.6802C1163.99 -123.258 1165.6 -111.332 1186.21 -135.809C1206.81 -160.285 1203.29 -160.861 1220.31 -177.633C1237.33 -194.406 1236.97 -204.408 1250.42 -214.196',
'M720 450C720 450 712.336 437.768 690.248 407.156C668.161 376.544 672.543 394.253 665.951 365.784C659.358 337.316 647.903 347.461 636.929 323.197C625.956 298.933 626.831 303.639 609.939 281.01C593.048 258.381 598.7 255.282 582.342 242.504C565.985 229.726 566.053 217.66 559.169 197.116C552.284 176.572 549.348 171.846 529.347 156.529C509.345 141.211 522.053 134.054 505.192 115.653C488.33 97.2527 482.671 82.5627 473.599 70.7833C464.527 59.0039 464.784 50.2169 447 32.0721C429.215 13.9272 436.29 0.858563 423.534 -12.6868C410.777 -26.2322 407.424 -44.0808 394.364 -56.4916C381.303 -68.9024 373.709 -72.6804 365.591 -96.1992C357.473 -119.718 358.364 -111.509 338.222 -136.495C318.08 -161.481 322.797 -149.499 315.32 -181.761C307.843 -214.023 294.563 -202.561 285.795 -223.25C277.026 -243.94 275.199 -244.055 258.602 -263.871',
'M720 450C720 450 738.983 448.651 790.209 446.852C841.436 445.052 816.31 441.421 861.866 437.296C907.422 433.172 886.273 437.037 930.656 436.651C975.04 436.264 951.399 432.343 1001.57 425.74C1051.73 419.138 1020.72 425.208 1072.85 424.127C1124.97 423.047 1114.39 420.097 1140.02 414.426C1165.65 408.754 1173.1 412.143 1214.55 411.063C1256.01 409.983 1242.78 406.182 1285.56 401.536C1328.35 396.889 1304.66 400.796 1354.41 399.573C1404.16 398.35 1381.34 394.315 1428.34 389.376C1475.35 384.438 1445.96 386.509 1497.93 385.313C1549.9 384.117 1534.63 382.499 1567.23 381.48',
'M720 450C720 450 696.366 458.841 682.407 472.967C668.448 487.093 673.23 487.471 647.919 492.882C622.608 498.293 636.85 499.899 609.016 512.944C581.182 525.989 596.778 528.494 571.937 533.778C547.095 539.062 551.762 548.656 536.862 556.816C521.962 564.975 515.626 563.279 497.589 575.159C479.552 587.04 484.343 590.435 461.111 598.728C437.879 607.021 442.512 605.226 423.603 618.397C404.694 631.569 402.411 629.541 390.805 641.555C379.2 653.568 369.754 658.175 353.238 663.929C336.722 669.683 330.161 674.689 312.831 684.116C295.5 693.543 288.711 698.815 278.229 704.041C267.747 709.267 258.395 712.506 240.378 726.65C222.361 740.795 230.097 738.379 203.447 745.613C176.797 752.847 193.747 752.523 166.401 767.148C139.056 781.774 151.342 783.641 130.156 791.074C108.97 798.507 116.461 802.688 96.0974 808.817C75.7334 814.946 83.8553 819.505 59.4513 830.576C35.0473 841.648 48.2548 847.874 21.8337 853.886C-4.58739 859.898 10.5966 869.102 -16.396 874.524',
'M720 450C720 450 695.644 482.465 682.699 506.197C669.755 529.929 671.059 521.996 643.673 556.974C616.286 591.951 625.698 590.8 606.938 615.255C588.178 639.71 592.715 642.351 569.76 665.92C546.805 689.49 557.014 687.498 538.136 722.318C519.258 757.137 520.671 760.818 503.256 774.428C485.841 788.038 491.288 790.063 463.484 831.358C435.681 872.653 437.554 867.001 425.147 885.248C412.74 903.495 411.451 911.175 389.505 934.331C367.559 957.486 375.779 966.276 352.213 990.918C328.647 1015.56 341.908 1008.07 316.804 1047.24C291.699 1086.42 301.938 1060.92 276.644 1100.23C251.349 1139.54 259.792 1138.78 243.151 1153.64',
'M719.974 450C719.974 450 765.293 459.346 789.305 476.402C813.318 493.459 825.526 487.104 865.093 495.586C904.659 504.068 908.361 510.231 943.918 523.51C979.475 536.789 963.13 535.277 1009.79 547.428C1056.45 559.579 1062.34 555.797 1089.82 568.96C1117.31 582.124 1133.96 582.816 1159.12 592.861C1184.28 602.906 1182.84 603.359 1233.48 614.514C1284.12 625.67 1254.63 632.207 1306.33 644.465C1358.04 656.723 1359.27 656.568 1378.67 670.21C1398.07 683.852 1406.16 676.466 1456.34 692.827C1506.51 709.188 1497.73 708.471 1527.54 715.212',
'M720 450C720 450 727.941 430.821 734.406 379.251C740.87 327.681 742.857 359.402 757.864 309.798C772.871 260.194 761.947 271.093 772.992 244.308C784.036 217.524 777.105 200.533 786.808 175.699C796.511 150.864 797.141 144.333 808.694 107.307C820.247 70.2821 812.404 88.4169 819.202 37.1016C826 -14.2137 829.525 -0.990829 839.341 -30.3874C849.157 -59.784 844.404 -61.5924 855.042 -98.7516C865.68 -135.911 862.018 -144.559 876.924 -167.488C891.83 -190.418 886.075 -213.535 892.87 -237.945C899.664 -262.355 903.01 -255.031 909.701 -305.588C916.393 -356.144 917.232 -330.612 925.531 -374.777',
'M720 450C720 450 722.468 499.363 726.104 520.449C729.739 541.535 730.644 550.025 738.836 589.07C747.028 628.115 743.766 639.319 746.146 659.812C748.526 680.306 754.006 693.598 757.006 732.469C760.007 771.34 760.322 765.244 763.893 805.195C767.465 845.146 769.92 822.227 773.398 868.469C776.875 914.71 776.207 901.365 778.233 940.19C780.259 979.015 782.53 990.477 787.977 1010.39C793.424 1030.3 791.788 1060.01 797.243 1082.24C802.698 1104.47 801.758 1130.29 808.181 1149.64C814.604 1168.99 813.135 1171.5 818.026 1225.28C822.918 1279.06 820.269 1267.92 822.905 1293.75',
'M720 450C720 450 737.033 492.46 757.251 515.772C777.468 539.084 768.146 548.687 785.517 570.846C802.887 593.005 814.782 609.698 824.589 634.112C834.395 658.525 838.791 656.702 855.55 695.611C872.31 734.519 875.197 724.854 890.204 764.253C905.21 803.653 899.844 790.872 919.927 820.763C940.01 850.654 939.071 862.583 954.382 886.946C969.693 911.309 968.683 909.254 993.997 945.221C1019.31 981.187 1006.67 964.436 1023.49 1007.61C1040.32 1050.79 1046.15 1038.25 1059.01 1073.05C1071.88 1107.86 1081.39 1096.19 1089.45 1131.96C1097.51 1167.73 1106.52 1162.12 1125.77 1196.89',
'M720 450C720 450 687.302 455.326 670.489 467.898C653.676 480.47 653.159 476.959 626.58 485.127C600.002 493.295 599.626 495.362 577.94 503.841C556.254 512.319 556.35 507.426 533.958 517.44C511.566 527.454 505.82 526.441 486.464 539.172C467.108 551.904 461.312 546.36 439.357 553.508C417.402 560.657 406.993 567.736 389.393 572.603C371.794 577.47 371.139 583.76 344.54 587.931C317.941 592.102 327.375 593.682 299.411 607.275C271.447 620.868 283.617 615.022 249.868 622.622C216.119 630.223 227.07 630.86 203.77 638.635C180.47 646.41 168.948 652.487 156.407 657.28C143.866 662.073 132.426 669.534 110.894 675.555C89.3615 681.575 90.3234 680.232 61.1669 689.897C32.0105 699.562 34.3696 702.021 15.9011 709.789C-2.56738 717.558 2.38861 719.841 -29.9494 729.462C-62.2873 739.083 -52.5552 738.225 -77.4307 744.286',
'M720 450C720 450 743.97 465.061 754.884 490.648C765.798 516.235 781.032 501.34 791.376 525.115C801.72 548.889 808.417 538.333 829.306 564.807C850.195 591.281 852.336 582.531 865.086 601.843C877.835 621.155 874.512 621.773 902.383 643.857C930.255 665.94 921.885 655.976 938.025 681.74C954.164 707.505 959.384 709.719 977.273 720.525C995.162 731.33 994.233 731.096 1015.92 757.676C1037.61 784.257 1025.74 768.848 1047.82 795.343C1069.91 821.837 1065.95 815.45 1085.93 834.73C1105.91 854.009 1110.53 848.089 1124.97 869.759C1139.4 891.428 1140.57 881.585 1158.53 911.499C1176.5 941.414 1184.96 933.829 1194.53 948.792C1204.09 963.755 1221.35 973.711 1232.08 986.224C1242.8 998.738 1257.34 1015.61 1269.99 1026.53C1282.63 1037.45 1293.81 1040.91 1307.21 1064.56',
'M720 450C720 450 718.24 412.717 716.359 397.31C714.478 381.902 713.988 362.237 710.785 344.829C707.582 327.42 708.407 322.274 701.686 292.106C694.965 261.937 699.926 270.857 694.84 240.765C689.753 210.674 693.055 217.076 689.674 184.902C686.293 152.728 686.041 149.091 682.676 133.657C679.311 118.223 682.23 106.005 681.826 80.8297C681.423 55.6545 677.891 60.196 675.66 30.0226C673.429 -0.150848 672.665 -7.94842 668.592 -26.771C664.52 -45.5935 664.724 -43.0755 661.034 -78.7766C657.343 -114.478 658.509 -103.181 653.867 -133.45C649.226 -163.719 650.748 -150.38 647.052 -182.682C643.357 -214.984 646.125 -214.921 645.216 -238.402C644.307 -261.883 640.872 -253.4 637.237 -291.706C633.602 -330.012 634.146 -309.868 630.717 -343.769C627.288 -377.669 628.008 -370.682 626.514 -394.844',
'M720 450C720 450 730.384 481.55 739.215 507.557C748.047 533.564 751.618 537.619 766.222 562.033C780.825 586.447 774.187 582.307 787.606 618.195C801.025 654.082 793.116 653.536 809.138 678.315C825.16 703.095 815.485 717.073 829.898 735.518C844.311 753.964 845.351 773.196 852.197 786.599C859.042 800.001 862.876 805.65 872.809 845.974C882.742 886.297 885.179 874.677 894.963 903.246C904.747 931.816 911.787 924.243 921.827 961.809C931.867 999.374 927.557 998.784 940.377 1013.59C953.197 1028.4 948.555 1055.77 966.147 1070.54C983.739 1085.31 975.539 1105.69 988.65 1125.69C1001.76 1145.69 1001.82 1141.59 1007.54 1184.37C1013.27 1227.15 1018.98 1198.8 1029.67 1241.58',
'M720 450C720 450 684.591 447.135 657.288 439.014C629.985 430.894 618.318 435.733 600.698 431.723C583.077 427.714 566.975 425.639 537.839 423.315C508.704 420.991 501.987 418.958 476.29 413.658C450.592 408.359 460.205 410.268 416.97 408.927C373.736 407.586 396.443 401.379 359.262 396.612C322.081 391.844 327.081 393.286 300.224 391.917C273.368 390.547 264.902 385.49 241.279 382.114C217.655 378.739 205.497 378.95 181.98 377.253C158.464 375.556 150.084 369.938 117.474 366.078C84.8644 362.218 81.5401 361.501 58.8734 358.545C36.2067 355.59 33.6442 351.938 -3.92281 346.728C-41.4898 341.519 -18.6466 345.082 -61.4654 341.179C-104.284 337.275 -102.32 338.048 -121.821 332.369',
'M720 450C720 450 714.384 428.193 708.622 410.693C702.86 393.193 705.531 397.066 703.397 372.66C701.264 348.254 697.8 345.181 691.079 330.466C684.357 315.751 686.929 312.356 683.352 292.664C679.776 272.973 679.079 273.949 674.646 255.07C670.213 236.192 670.622 244.371 665.271 214.561C659.921 184.751 659.864 200.13 653.352 172.377C646.841 144.623 647.767 151.954 644.123 136.021C640.48 120.088 638.183 107.491 636.127 96.8178C634.072 86.1443 632.548 77.5871 626.743 54.0492C620.938 30.5112 622.818 28.9757 618.613 16.577C614.407 4.17831 615.555 -13.1527 608.752 -24.5691C601.95 -35.9855 603.375 -51.0511 599.526 -60.1492C595.678 -69.2472 593.676 -79.3623 587.865 -100.431C582.053 -121.5 584.628 -117.913 578.882 -139.408C573.137 -160.903 576.516 -161.693 571.966 -182.241C567.416 -202.789 567.42 -198.681 562.834 -218.28C558.248 -237.879 555.335 -240.47 552.072 -260.968C548.808 -281.466 547.605 -280.956 541.772 -296.427C535.94 -311.898 537.352 -315.211 535.128 -336.018C532.905 -356.826 531.15 -360.702 524.129 -377.124',
'M720 450C720 450 711.433 430.82 707.745 409.428C704.056 388.035 704.937 381.711 697.503 370.916C690.069 360.121 691.274 359.999 685.371 334.109C679.469 308.22 677.496 323.883 671.24 294.303C664.984 264.724 667.608 284.849 662.065 258.116C656.522 231.383 656.357 229.024 647.442 216.172C638.527 203.319 640.134 192.925 635.555 178.727C630.976 164.529 630.575 150.179 624.994 139.987C619.413 129.794 615.849 112.779 612.251 103.074C608.654 93.3696 606.942 85.6729 603.041 63.0758C599.14 40.4787 595.242 36.9267 589.533 23.8967C583.823 10.8666 581.18 -2.12401 576.96 -14.8333C572.739 -27.5425 572.696 -37.7703 568.334 -51.3441C563.972 -64.9179 562.14 -67.2124 556.992 -93.299C551.844 -119.386 550.685 -109.743 544.056 -129.801C537.428 -149.859 534.97 -151.977 531.034 -170.076C527.099 -188.175 522.979 -185.119 519.996 -207.061C517.012 -229.004 511.045 -224.126 507.478 -247.077C503.912 -270.029 501.417 -271.033 495.534 -287C489.651 -302.968 491.488 -300.977 484.68 -326.317C477.872 -351.657 476.704 -348.494 472.792 -363.258',
'M720 450C720 450 723.524 466.673 728.513 497.319C733.503 527.964 731.894 519.823 740.001 542.706C748.108 565.589 744.225 560.598 748.996 588.365C753.766 616.131 756.585 602.096 761.881 636.194C767.178 670.293 768.155 649.089 771.853 679.845C775.551 710.6 775.965 703.738 781.753 724.555C787.54 745.372 787.248 758.418 791.422 773.79C795.596 789.162 798.173 807.631 804.056 819.914C809.938 832.197 806.864 843.07 811.518 865.275C816.171 887.48 816.551 892.1 822.737 912.643C828.922 933.185 830.255 942.089 833.153 956.603C836.052 971.117 839.475 969.242 846.83 1003.98C854.185 1038.71 850.193 1028.86 854.119 1048.67C858.045 1068.48 857.963 1074.39 863.202 1094.94C868.44 1115.49 867.891 1108.03 874.497 1138.67C881.102 1169.31 880.502 1170.72 887.307 1186.56C894.111 1202.4 890.388 1209.75 896.507 1231.25C902.627 1252.76 902.54 1245.39 906.742 1279.23',
'M720 450C720 450 698.654 436.893 669.785 424.902C640.916 412.91 634.741 410.601 615.568 402.586C596.396 394.571 594.829 395.346 568.66 378.206C542.492 361.067 547.454 359.714 514.087 348.978C480.721 338.242 479.79 334.731 467.646 329.846C455.502 324.96 448.63 312.156 416.039 303.755C383.448 295.354 391.682 293.73 365.021 280.975C338.36 268.219 328.715 267.114 309.809 252.575C290.903 238.036 277.185 246.984 259.529 230.958C241.873 214.931 240.502 224.403 211.912 206.241C183.323 188.078 193.288 190.89 157.03 181.714C120.772 172.538 127.621 170.109 108.253 154.714C88.8857 139.319 75.4927 138.974 56.9647 132.314C38.4366 125.654 33.8997 118.704 4.77584 106.7C-24.348 94.6959 -19.1326 90.266 -46.165 81.9082',
'M720 450C720 450 711.596 475.85 701.025 516.114C690.455 556.378 697.124 559.466 689.441 579.079C681.758 598.693 679.099 597.524 675.382 642.732C671.665 687.94 663.4 677.024 657.844 700.179C652.288 723.333 651.086 724.914 636.904 764.536C622.723 804.158 631.218 802.853 625.414 827.056C619.611 851.259 613.734 856.28 605.94 892.262C598.146 928.244 595.403 924.314 588.884 957.785C582.364 991.255 583.079 991.176 575.561 1022.63C568.044 1054.08 566.807 1058.45 558.142 1084.32C549.476 1110.2 553.961 1129.13 542.367 1149.25C530.772 1169.37 538.268 1180.37 530.338 1207.27C522.407 1234.17 520.826 1245.53 512.156 1274.2',
'M720 450C720 450 730.571 424.312 761.424 411.44C792.277 398.569 772.385 393.283 804.069 377.232C835.752 361.182 829.975 361.373 848.987 342.782C867.999 324.192 877.583 330.096 890.892 303.897C904.201 277.698 910.277 282.253 937.396 264.293C964.514 246.333 949.357 246.834 978.7 230.438C1008.04 214.042 990.424 217.952 1021.51 193.853C1052.6 169.753 1054.28 184.725 1065.97 158.075C1077.65 131.425 1087.76 139.068 1111.12 120.345C1134.49 101.622 1124.9 104.858 1151.67 86.3162C1178.43 67.7741 1167.09 66.2676 1197.53 47.2606C1227.96 28.2536 1225.78 23.2186 1239.27 12.9649C1252.76 2.7112 1269.32 -9.47929 1282.88 -28.5587C1296.44 -47.6381 1305.81 -41.3853 1323.82 -62.7027C1341.83 -84.0202 1340.32 -82.3794 1368.98 -98.9326',
];
const colors = [
'#46A5CA',
'#8C2F2F',
'#4FAE4D',
'#D6590C',
'#811010',
'#247AFB',
'#A534A0',
'#A8A438',
'#D6590C',
'#46A29C',
'#670F6D',
'#D7C200',
'#59BBEB',
'#504F1C',
'#55BC54',
'#4D3568',
'#9F39A5',
'#363636',
'#860909',
'#6A286F',
'#604483',
];
return (
<motion.svg
viewBox='0 0 1440 900'
fill='none'
xmlns='http://www.w3.org/2000/svg'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1 }}
className='absolute inset-0 h-full w-full'
>
{paths.map((path, idx) => (
<motion.path
d={path}
stroke={colors[idx]}
strokeWidth='2.3'
strokeLinecap='round'
variants={pathVariants}
initial='initial'
animate='animate'
transition={{
duration: svgOptions?.duration || 10,
ease: 'linear',
repeat: Infinity,
repeatType: 'loop',
delay: Math.floor(Math.random() * 10),
repeatDelay: Math.floor(Math.random() * 10 + 2),
}}
key={`path-first-${idx}`}
/>
))}
{/* duplicate for more paths */}
{paths.map((path, idx) => (
<motion.path
d={path}
stroke={colors[idx]}
strokeWidth='2.3'
strokeLinecap='round'
variants={pathVariants}
initial='initial'
animate='animate'
transition={{
duration: svgOptions?.duration || 10,
ease: 'linear',
repeat: Infinity,
repeatType: 'loop',
delay: Math.floor(Math.random() * 10),
repeatDelay: Math.floor(Math.random() * 10 + 2),
}}
key={`path-second-${idx}`}
/>
))}
</motion.svg>
);
};

View File

@ -1,3 +1,4 @@
// @ts-nocheck
import { cva, type VariantProps } from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react'; import * as React from 'react';

View File

@ -1,66 +1,55 @@
import { ArrowRightIcon } from '@radix-ui/react-icons'; // @ts-nocheck
import { ReactNode } from 'react';
import { cn } from '../../lib/utils'; import { cn } from '../../lib/utils';
import { Button } from './button';
const BentoGrid = ({ children, className }: { children: ReactNode; className?: string }) => { export const BentoGrid = ({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) => {
return ( return (
<div className={cn('grid w-full auto-rows-[22rem] grid-cols-3 gap-4', className)}> <div
className={cn(
'mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[18rem] md:grid-cols-3',
className,
)}
>
{children} {children}
</div> </div>
); );
}; };
const BentoCard = ({ export const BentoGridItem = ({
name,
className, className,
background, title,
Icon,
description, description,
href, header,
cta, icon,
}: { }: {
name: string; className?: string;
className: string; title?: string | React.ReactNode;
background: ReactNode; description?: string | React.ReactNode;
Icon: any; header?: React.ReactNode;
description: string; icon?: React.ReactNode;
href: string; }) => {
cta: string; return (
}) => (
<div
key={name}
className={cn(
'group relative col-span-3 flex flex-col justify-between overflow-hidden rounded-xl',
// light styles
'bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]',
// dark styles
'transform-gpu dark:bg-black dark:[border:1px_solid_rgba(255,255,255,.1)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]',
className,
)}
>
<div>{background}</div>
<div className='pointer-events-none z-10 flex transform-gpu flex-col gap-1 p-6 transition-all duration-300 group-hover:-translate-y-10'>
<Icon className='h-12 w-12 origin-left transform-gpu text-neutral-700 transition-all duration-300 ease-in-out group-hover:scale-75' />
<h3 className='text-xl font-semibold text-neutral-700 dark:text-neutral-300'>{name}</h3>
<p className='max-w-lg text-neutral-400'>{description}</p>
</div>
<div <div
className={cn( className={cn(
'pointer-events-none absolute bottom-0 flex w-full translate-y-10 transform-gpu flex-row items-center p-4 opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100', 'group/bento shadow-input row-span-1 flex flex-col justify-between space-y-4 rounded-xl border border-transparent bg-white p-4 transition duration-200 hover:shadow-xl dark:border-white/[0.2] dark:bg-black dark:shadow-none',
className,
)} )}
> >
<Button variant='ghost' asChild size='sm' className='pointer-events-auto'> {header}
<a href={href}> <div className='transition duration-200 group-hover/bento:translate-x-2'>
{cta} {icon}
<ArrowRightIcon className='ml-2 h-4 w-4' /> <div className='mb-2 mt-2 font-sans font-bold text-neutral-600 dark:text-neutral-200'>
</a> {title}
</Button> </div>
<div className='font-sans text-xs font-normal text-neutral-600 dark:text-neutral-300'>
{description}
</div>
</div>
</div> </div>
<div className='pointer-events-none absolute inset-0 transform-gpu transition-all duration-300 group-hover:bg-black/[.03] group-hover:dark:bg-neutral-800/10' /> );
</div> };
);
export { BentoCard, BentoGrid };

View File

@ -1,61 +0,0 @@
'use client';
import { AnimatePresence, motion, useInView, UseInViewOptions, Variants } from 'framer-motion';
import { useRef } from 'react';
type MarginType = UseInViewOptions['margin'];
interface BlurFadeProps {
children: React.ReactNode;
className?: string;
variant?: {
hidden: { y: number };
visible: { y: number };
};
duration?: number;
delay?: number;
yOffset?: number;
inView?: boolean;
inViewMargin?: MarginType;
blur?: string;
}
export default function BlurFade({
children,
className,
variant,
duration = 0.4,
delay = 0,
yOffset = 6,
inView = false,
inViewMargin = '-50px',
blur = '6px',
}: BlurFadeProps) {
const ref = useRef(null);
const inViewResult = useInView(ref, { once: true, margin: inViewMargin });
const isInView = !inView || inViewResult;
const defaultVariants: Variants = {
hidden: { y: yOffset, opacity: 0, filter: `blur(${blur})` },
visible: { y: -yOffset, opacity: 1, filter: `blur(0px)` },
};
const combinedVariants = variant || defaultVariants;
return (
<AnimatePresence>
<motion.div
ref={ref}
initial='hidden'
animate={isInView ? 'visible' : 'hidden'}
exit='hidden'
variants={combinedVariants}
transition={{
delay: 0.04 + delay,
duration,
ease: 'easeOut',
}}
className={className}
>
{children}
</motion.div>
</AnimatePresence>
);
}

View File

@ -1,39 +0,0 @@
'use client';
import { motion } from 'framer-motion';
import { cn } from '../../lib/utils';
interface BlurIntProps {
word: string;
className?: string;
variant?: {
hidden: { filter: string; opacity: number };
visible: { filter: string; opacity: number };
};
duration?: number;
}
const BlurIn = ({ word, className, variant, duration = 1 }: BlurIntProps) => {
const defaultVariants = {
hidden: { filter: 'blur(10px)', opacity: 0 },
visible: { filter: 'blur(0px)', opacity: 1 },
};
const combinedVariants = variant || defaultVariants;
return (
<motion.h1
initial='hidden'
animate='visible'
transition={{ duration }}
variants={combinedVariants}
className={cn(
'font-display text-center text-4xl font-bold tracking-[-0.02em] drop-shadow-sm md:text-7xl md:leading-[5rem]',
className,
)}
>
{word}
</motion.h1>
);
};
export default BlurIn;

View File

@ -1,49 +0,0 @@
import { cn } from '../../lib/utils';
interface BorderBeamProps {
className?: string;
size?: number;
duration?: number;
borderWidth?: number;
anchor?: number;
colorFrom?: string;
colorTo?: string;
delay?: number;
}
export const BorderBeam = ({
className,
size = 200,
duration = 15,
anchor = 90,
borderWidth = 1.5,
colorFrom = '#ffaa40',
colorTo = '#9c40ff',
delay = 0,
}: BorderBeamProps) => {
return (
<div
style={
{
'--size': size,
'--duration': duration,
'--anchor': anchor,
'--border-width': borderWidth,
'--color-from': colorFrom,
'--color-to': colorTo,
'--delay': `-${delay}s`,
} as React.CSSProperties
}
className={cn(
'pointer-events-none absolute inset-0 rounded-[inherit] [border:calc(var(--border-width)*1px)_solid_transparent]',
// mask styles
'![mask-clip:padding-box,border-box] ![mask-composite:intersect] [mask:linear-gradient(transparent,transparent),linear-gradient(white,white)]',
// pseudo styles
'after:animate-border-beam after:absolute after:aspect-square after:w-[calc(var(--size)*1px)] after:[animation-delay:var(--delay)] after:[background:linear-gradient(to_left,var(--color-from),var(--color-to),transparent)] after:[offset-anchor:calc(var(--anchor)*1%)_50%] after:[offset-path:rect(0_auto_auto_0_round_calc(var(--size)*1px))]',
className,
)}
/>
);
};

View File

@ -1,71 +0,0 @@
'use client';
import { motion, useAnimation, useInView } from 'framer-motion';
import { useEffect, useRef } from 'react';
interface BoxRevealProps {
children: JSX.Element;
width?: 'fit-content' | '100%';
boxColor?: string;
duration?: number;
}
export const BoxReveal = ({
children,
width = 'fit-content',
boxColor,
duration,
}: BoxRevealProps) => {
const mainControls = useAnimation();
const slideControls = useAnimation();
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
useEffect(() => {
if (isInView) {
slideControls.start('visible');
mainControls.start('visible');
} else {
slideControls.start('hidden');
mainControls.start('hidden');
}
}, [isInView, mainControls, slideControls]);
return (
<div ref={ref} style={{ position: 'relative', width, overflow: 'hidden' }}>
<motion.div
variants={{
hidden: { opacity: 0, y: 75 },
visible: { opacity: 1, y: 0 },
}}
initial='hidden'
animate={mainControls}
transition={{ duration: duration ? duration : 0.5, delay: 0.25 }}
>
{children}
</motion.div>
<motion.div
variants={{
hidden: { left: 0 },
visible: { left: '100%' },
}}
initial='hidden'
animate={slideControls}
transition={{ duration: duration ? duration : 0.5, ease: 'easeIn' }}
style={{
position: 'absolute',
top: 4,
bottom: 4,
left: 0,
right: 0,
zIndex: 20,
background: boxColor ? boxColor : '#5046e6',
}}
/>
</div>
);
};
export default BoxReveal;

View File

@ -1,3 +1,4 @@
// @ts-nocheck
import { ChevronRightIcon, DotsHorizontalIcon } from '@radix-ui/react-icons'; import { ChevronRightIcon, DotsHorizontalIcon } from '@radix-ui/react-icons';
import { Slot } from '@radix-ui/react-slot'; import { Slot } from '@radix-ui/react-slot';
import * as React from 'react'; import * as React from 'react';

View File

@ -1,3 +1,4 @@
// @ts-nocheck
import { Slot } from '@radix-ui/react-slot'; import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react'; import * as React from 'react';

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';

View File

@ -0,0 +1,278 @@
// @ts-nocheck
'use client';
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import React, { useMemo, useRef } from 'react';
import * as THREE from 'three';
import { cn } from '../../lib/utils';
export const CanvasRevealEffect = ({
animationSpeed = 0.4,
opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1],
colors = [[0, 255, 255]],
containerClassName,
dotSize,
showGradient = true,
}: {
/**
* 0.1 - slower
* 1.0 - faster
*/
animationSpeed?: number;
opacities?: number[];
colors?: number[][];
containerClassName?: string;
dotSize?: number;
showGradient?: boolean;
}) => {
return (
<div className={cn('relative h-full w-full bg-white', containerClassName)}>
<div className='h-full w-full'>
<DotMatrix
colors={colors ?? [[0, 255, 255]]}
dotSize={dotSize ?? 3}
opacities={opacities ?? [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1]}
shader={`
float animation_speed_factor = ${animationSpeed.toFixed(1)};
float intro_offset = distance(u_resolution / 2.0 / u_total_size, st2) * 0.01 + (random(st2) * 0.15);
opacity *= step(intro_offset, u_time * animation_speed_factor);
opacity *= clamp((1.0 - step(intro_offset + 0.1, u_time * animation_speed_factor)) * 1.25, 1.0, 1.25);
`}
center={['x', 'y']}
/>
</div>
{showGradient && <div className='absolute inset-0 bg-gradient-to-t from-gray-950 to-[84%]' />}
</div>
);
};
interface DotMatrixProps {
colors?: number[][];
opacities?: number[];
totalSize?: number;
dotSize?: number;
shader?: string;
center?: ('x' | 'y')[];
}
const DotMatrix: React.FC<DotMatrixProps> = ({
colors = [[0, 0, 0]],
opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14],
totalSize = 4,
dotSize = 2,
shader = '',
center = ['x', 'y'],
}) => {
const uniforms = React.useMemo(() => {
let colorsArray = [colors[0], colors[0], colors[0], colors[0], colors[0], colors[0]];
if (colors.length === 2) {
colorsArray = [colors[0], colors[0], colors[0], colors[1], colors[1], colors[1]];
} else if (colors.length === 3) {
colorsArray = [colors[0], colors[0], colors[1], colors[1], colors[2], colors[2]];
}
return {
u_colors: {
value: colorsArray.map((color) => [color[0] / 255, color[1] / 255, color[2] / 255]),
type: 'uniform3fv',
},
u_opacities: {
value: opacities,
type: 'uniform1fv',
},
u_total_size: {
value: totalSize,
type: 'uniform1f',
},
u_dot_size: {
value: dotSize,
type: 'uniform1f',
},
};
}, [colors, opacities, totalSize, dotSize]);
return (
<Shader
source={`
precision mediump float;
in vec2 fragCoord;
uniform float u_time;
uniform float u_opacities[10];
uniform vec3 u_colors[6];
uniform float u_total_size;
uniform float u_dot_size;
uniform vec2 u_resolution;
out vec4 fragColor;
float PHI = 1.61803398874989484820459;
float random(vec2 xy) {
return fract(tan(distance(xy * PHI, xy) * 0.5) * xy.x);
}
float map(float value, float min1, float max1, float min2, float max2) {
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
void main() {
vec2 st = fragCoord.xy;
${
center.includes('x')
? 'st.x -= abs(floor((mod(u_resolution.x, u_total_size) - u_dot_size) * 0.5));'
: ''
}
${
center.includes('y')
? 'st.y -= abs(floor((mod(u_resolution.y, u_total_size) - u_dot_size) * 0.5));'
: ''
}
float opacity = step(0.0, st.x);
opacity *= step(0.0, st.y);
vec2 st2 = vec2(int(st.x / u_total_size), int(st.y / u_total_size));
float frequency = 5.0;
float show_offset = random(st2);
float rand = random(st2 * floor((u_time / frequency) + show_offset + frequency) + 1.0);
opacity *= u_opacities[int(rand * 10.0)];
opacity *= 1.0 - step(u_dot_size / u_total_size, fract(st.x / u_total_size));
opacity *= 1.0 - step(u_dot_size / u_total_size, fract(st.y / u_total_size));
vec3 color = u_colors[int(show_offset * 6.0)];
${shader}
fragColor = vec4(color, opacity);
fragColor.rgb *= fragColor.a;
}`}
uniforms={uniforms}
maxFps={60}
/>
);
};
type Uniforms = {
[key: string]: {
value: number[] | number[][] | number;
type: string;
};
};
const ShaderMaterial = ({
source,
uniforms,
maxFps = 60,
}: {
source: string;
hovered?: boolean;
maxFps?: number;
uniforms: Uniforms;
}) => {
const { size } = useThree();
const ref = useRef<THREE.Mesh>();
let lastFrameTime = 0;
useFrame(({ clock }) => {
if (!ref.current) return;
const timestamp = clock.getElapsedTime();
if (timestamp - lastFrameTime < 1 / maxFps) {
return;
}
lastFrameTime = timestamp;
const material: any = ref.current.material;
const timeLocation = material.uniforms.u_time;
timeLocation.value = timestamp;
});
const getUniforms = () => {
const preparedUniforms: any = {};
for (const uniformName in uniforms) {
const uniform: any = uniforms[uniformName];
switch (uniform.type) {
case 'uniform1f':
preparedUniforms[uniformName] = { value: uniform.value, type: '1f' };
break;
case 'uniform3f':
preparedUniforms[uniformName] = {
value: new THREE.Vector3().fromArray(uniform.value),
type: '3f',
};
break;
case 'uniform1fv':
preparedUniforms[uniformName] = { value: uniform.value, type: '1fv' };
break;
case 'uniform3fv':
preparedUniforms[uniformName] = {
value: uniform.value.map((v: number[]) => new THREE.Vector3().fromArray(v)),
type: '3fv',
};
break;
case 'uniform2f':
preparedUniforms[uniformName] = {
value: new THREE.Vector2().fromArray(uniform.value),
type: '2f',
};
break;
default:
console.error(`Invalid uniform type for '${uniformName}'.`);
break;
}
}
preparedUniforms['u_time'] = { value: 0, type: '1f' };
preparedUniforms['u_resolution'] = {
value: new THREE.Vector2(size.width * 2, size.height * 2),
}; // Initialize u_resolution
return preparedUniforms;
};
// Shader material
const material = useMemo(() => {
const materialObject = new THREE.ShaderMaterial({
vertexShader: `
precision mediump float;
in vec2 coordinates;
uniform vec2 u_resolution;
out vec2 fragCoord;
void main(){
float x = position.x;
float y = position.y;
gl_Position = vec4(x, y, 0.0, 1.0);
fragCoord = (position.xy + vec2(1.0)) * 0.5 * u_resolution;
fragCoord.y = u_resolution.y - fragCoord.y;
}
`,
fragmentShader: source,
uniforms: getUniforms(),
glslVersion: THREE.GLSL3,
blending: THREE.CustomBlending,
blendSrc: THREE.SrcAlphaFactor,
blendDst: THREE.OneFactor,
});
return materialObject;
}, [size.width, size.height, source]);
return (
<mesh ref={ref as any}>
<planeGeometry args={[2, 2]} />
<primitive object={material} attach='material' />
</mesh>
);
};
const Shader: React.FC<ShaderProps> = ({ source, uniforms, maxFps = 60 }) => {
return (
<Canvas className='absolute inset-0 h-full w-full'>
<ShaderMaterial source={source} uniforms={uniforms} maxFps={maxFps} />
</Canvas>
);
};
interface ShaderProps {
source: string;
uniforms: {
[key: string]: {
value: number[] | number[][] | number;
type: string;
};
};
maxFps?: number;
}

View File

@ -0,0 +1,100 @@
// @ts-nocheck
import { AnimatePresence, motion } from 'framer-motion';
import Link from 'next/link';
import { useState } from 'react';
import { cn } from '../../lib/utils';
export const HoverEffect = ({
items,
className,
}: {
items: {
title: string;
description: string;
link: string;
}[];
className?: string;
}) => {
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
return (
<div className={cn('grid grid-cols-1 py-10 md:grid-cols-2 lg:grid-cols-3', className)}>
{items.map((item, idx) => (
<Link
href={item?.link}
key={item?.link}
className='group relative block h-full w-full p-2'
onMouseEnter={() => setHoveredIndex(idx)}
onMouseLeave={() => setHoveredIndex(null)}
>
<AnimatePresence>
{hoveredIndex === idx && (
<motion.span
className='absolute inset-0 block h-full w-full rounded-3xl bg-neutral-200 dark:bg-slate-800/[0.8]'
layoutId='hoverBackground'
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { duration: 0.15 },
}}
exit={{
opacity: 0,
transition: { duration: 0.15, delay: 0.2 },
}}
/>
)}
</AnimatePresence>
<Card>
<CardTitle>{item.title}</CardTitle>
<CardDescription>{item.description}</CardDescription>
</Card>
</Link>
))}
</div>
);
};
export const Card = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<div
className={cn(
'relative z-20 h-full w-full overflow-hidden rounded-2xl border border-transparent bg-black p-4 group-hover:border-slate-700 dark:border-white/[0.2]',
className,
)}
>
<div className='relative z-50'>
<div className='p-4'>{children}</div>
</div>
</div>
);
};
export const CardTitle = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<h4 className={cn('mt-4 font-bold tracking-wide text-zinc-100', className)}>{children}</h4>
);
};
export const CardDescription = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<p className={cn('mt-8 text-sm leading-relaxed tracking-wide text-zinc-400', className)}>
{children}
</p>
);
};

View File

@ -0,0 +1,71 @@
// @ts-nocheck
'use client';
import { motion, useMotionTemplate, useMotionValue } from 'framer-motion';
import React, { MouseEvent as ReactMouseEvent, useState } from 'react';
import { cn } from '../../lib/utils';
import { CanvasRevealEffect } from './canvas-reveal-effect';
export const CardSpotlight = ({
children,
radius = 350,
color = '#262626',
className,
...props
}: {
radius?: number;
color?: string;
children: React.ReactNode;
} & React.HTMLAttributes<HTMLDivElement>) => {
const mouseX = useMotionValue(0);
const mouseY = useMotionValue(0);
function handleMouseMove({ currentTarget, clientX, clientY }: ReactMouseEvent<HTMLDivElement>) {
let { left, top } = currentTarget.getBoundingClientRect();
mouseX.set(clientX - left);
mouseY.set(clientY - top);
}
const [isHovering, setIsHovering] = useState(false);
const handleMouseEnter = () => setIsHovering(true);
const handleMouseLeave = () => setIsHovering(false);
return (
<div
className={cn(
'group/spotlight relative rounded-md border border-neutral-800 bg-black p-10 dark:border-neutral-800',
className,
)}
onMouseMove={handleMouseMove}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
{...props}
>
<motion.div
className='pointer-events-none absolute -inset-px z-0 rounded-md opacity-0 transition duration-300 group-hover/spotlight:opacity-100'
style={{
backgroundColor: color,
maskImage: useMotionTemplate`
radial-gradient(
${radius}px circle at ${mouseX}px ${mouseY}px,
white,
transparent 80%
)
`,
}}
>
{isHovering && (
<CanvasRevealEffect
animationSpeed={5}
containerClassName='bg-transparent absolute inset-0 pointer-events-none'
colors={[
[59, 130, 246],
[139, 92, 246],
]}
dotSize={3}
/>
)}
</motion.div>
{children}
</div>
);
};

View File

@ -0,0 +1,70 @@
'use client';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
let interval: any;
type Card = {
id: number;
name: string;
designation: string;
content: React.ReactNode;
};
export const CardStack = ({
items,
offset,
scaleFactor,
}: {
items: Card[];
offset?: number;
scaleFactor?: number;
}) => {
const CARD_OFFSET = offset || 10;
const SCALE_FACTOR = scaleFactor || 0.06;
const [cards, setCards] = useState<Card[]>(items);
useEffect(() => {
startFlipping();
return () => clearInterval(interval);
}, []);
const startFlipping = () => {
interval = setInterval(() => {
setCards((prevCards: Card[]) => {
const newArray = [...prevCards]; // create a copy of the array
newArray.unshift(newArray.pop()!); // move the last element to the front
return newArray;
});
}, 5000);
};
return (
<div className='relative h-60 w-60 md:h-60 md:w-96'>
{cards.map((card, index) => {
return (
<motion.div
key={card.id}
className='absolute flex h-60 w-60 flex-col justify-between rounded-3xl border border-neutral-200 bg-white p-4 shadow-xl shadow-black/[0.1] md:h-60 md:w-96 dark:border-white/[0.1] dark:bg-black dark:shadow-white/[0.05]'
style={{
transformOrigin: 'top center',
}}
animate={{
top: index * -CARD_OFFSET,
scale: 1 - index * SCALE_FACTOR, // decrease scale for cards that are behind
zIndex: cards.length - index, // decrease z-index for the cards that are behind
}}
>
<div className='font-normal text-neutral-700 dark:text-neutral-200'>{card.content}</div>
<div>
<p className='font-medium text-neutral-500 dark:text-white'>{card.name}</p>
<p className='font-normal text-neutral-400 dark:text-neutral-200'>
{card.designation}
</p>
</div>
</motion.div>
);
})}
</div>
);
};

View File

@ -1,3 +1,4 @@
// @ts-nocheck
import * as React from 'react'; import * as React from 'react';
import { cn } from '../../lib/utils'; import { cn } from '../../lib/utils';

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import { ArrowLeftIcon, ArrowRightIcon } from '@radix-ui/react-icons'; import { ArrowLeftIcon, ArrowRightIcon } from '@radix-ui/react-icons';

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
@ -131,7 +132,7 @@ const ChartTooltipContent = React.forwardRef<
} }
const [item] = payload; const [item] = payload;
const key = `${labelKey || item?.dataKey || item?.name || 'value'}`; const key = `${labelKey || item.dataKey || item.name || 'value'}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key); const itemConfig = getPayloadConfigFromPayload(config, item, key);
const value = const value =
!labelKey && typeof label === 'string' !labelKey && typeof label === 'string'

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; import * as CheckboxPrimitive from '@radix-ui/react-checkbox';

View File

@ -1,27 +0,0 @@
'use client';
import { TweetProps, useTweet } from 'react-tweet';
import { MagicTweet, TweetNotFound, TweetSkeleton } from '@/components/magicui/tweet-card';
const ClientTweetCard = ({
id,
apiUrl,
fallback = <TweetSkeleton />,
components,
fetchOptions,
onError,
...props
}: TweetProps & { className?: string }) => {
const { data, error, isLoading } = useTweet(id, apiUrl, fetchOptions);
if (isLoading) return fallback;
if (error || !data) {
const NotFound = components?.TweetNotFound || TweetNotFound;
return <NotFound error={onError ? onError(error) : error} />;
}
return <MagicTweet tweet={data} components={components} {...props} />;
};
export default ClientTweetCard;

View File

@ -1,92 +0,0 @@
'use client';
import { FileIcon } from 'lucide-react';
import { useTheme } from 'next-themes';
import { useEffect, useState } from 'react';
import { codeToHtml } from 'shiki';
interface CodeComparisonProps {
beforeCode: string;
afterCode: string;
language: string;
filename: string;
lightTheme: string;
darkTheme: string;
}
export default function CodeComparison({
beforeCode,
afterCode,
language,
filename,
lightTheme,
darkTheme,
}: CodeComparisonProps) {
const { theme, systemTheme } = useTheme();
const [highlightedBefore, setHighlightedBefore] = useState('');
const [highlightedAfter, setHighlightedAfter] = useState('');
useEffect(() => {
const currentTheme = theme === 'system' ? systemTheme : theme;
const selectedTheme = currentTheme === 'dark' ? darkTheme : lightTheme;
async function highlightCode() {
const before = await codeToHtml(beforeCode, {
lang: language,
theme: selectedTheme,
});
const after = await codeToHtml(afterCode, {
lang: language,
theme: selectedTheme,
});
setHighlightedBefore(before);
setHighlightedAfter(after);
}
highlightCode();
}, [theme, systemTheme, beforeCode, afterCode, language, lightTheme, darkTheme]);
const renderCode = (code: string, highlighted: string) => {
if (highlighted) {
return (
<div
className='bg-background h-full overflow-auto font-mono text-xs [&>pre]:h-full [&>pre]:!bg-transparent [&>pre]:p-4 [&_code]:break-all'
dangerouslySetInnerHTML={{ __html: highlighted }}
/>
);
} else {
return (
<pre className='bg-background text-foreground h-full overflow-auto break-all p-4 font-mono text-xs'>
{code}
</pre>
);
}
};
return (
<div className='mx-auto w-full max-w-5xl'>
<div className='border-border relative w-full overflow-hidden rounded-xl border'>
<div className='md:divide-border relative grid md:grid-cols-2 md:divide-x'>
<div>
<div className='bg-accent text-foreground flex items-center p-2 text-sm'>
<FileIcon className='mr-2 h-4 w-4' />
{filename}
<span className='ml-auto'>before</span>
</div>
{renderCode(beforeCode, highlightedBefore)}
</div>
<div>
<div className='bg-accent text-foreground flex items-center p-2 text-sm'>
<FileIcon className='mr-2 h-4 w-4' />
{filename}
<span className='ml-auto'>after</span>
</div>
{renderCode(afterCode, highlightedAfter)}
</div>
</div>
<div className='bg-accent text-foreground absolute left-1/2 top-1/2 flex h-8 w-8 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-md text-xs'>
VS
</div>
</div>
</div>
);
}

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import { type DialogProps } from '@radix-ui/react-dialog'; import { type DialogProps } from '@radix-ui/react-dialog';

View File

@ -0,0 +1,237 @@
// @ts-nocheck
'use client';
import { IconDotsVertical } from '@tabler/icons-react';
import { AnimatePresence, motion } from 'framer-motion';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
import { SparklesCore } from './sparkles';
interface CompareProps {
firstImage?: string;
secondImage?: string;
className?: string;
firstImageClassName?: string;
secondImageClassname?: string;
initialSliderPercentage?: number;
slideMode?: 'hover' | 'drag';
showHandlebar?: boolean;
autoplay?: boolean;
autoplayDuration?: number;
}
export const Compare = ({
firstImage = '',
secondImage = '',
className,
firstImageClassName,
secondImageClassname,
initialSliderPercentage = 50,
slideMode = 'hover',
showHandlebar = true,
autoplay = false,
autoplayDuration = 5000,
}: CompareProps) => {
const [sliderXPercent, setSliderXPercent] = useState(initialSliderPercentage);
const [isDragging, setIsDragging] = useState(false);
const sliderRef = useRef<HTMLDivElement>(null);
const [isMouseOver, setIsMouseOver] = useState(false);
const autoplayRef = useRef<NodeJS.Timeout | null>(null);
const startAutoplay = useCallback(() => {
if (!autoplay) return;
const startTime = Date.now();
const animate = () => {
const elapsedTime = Date.now() - startTime;
const progress = (elapsedTime % (autoplayDuration * 2)) / autoplayDuration;
const percentage = progress <= 1 ? progress * 100 : (2 - progress) * 100;
setSliderXPercent(percentage);
autoplayRef.current = setTimeout(animate, 16); // ~60fps
};
animate();
}, [autoplay, autoplayDuration]);
const stopAutoplay = useCallback(() => {
if (autoplayRef.current) {
clearTimeout(autoplayRef.current);
autoplayRef.current = null;
}
}, []);
useEffect(() => {
startAutoplay();
return () => stopAutoplay();
}, [startAutoplay, stopAutoplay]);
function mouseEnterHandler() {
setIsMouseOver(true);
stopAutoplay();
}
function mouseLeaveHandler() {
setIsMouseOver(false);
if (slideMode === 'hover') {
setSliderXPercent(initialSliderPercentage);
}
if (slideMode === 'drag') {
setIsDragging(false);
}
startAutoplay();
}
const handleStart = useCallback(
(clientX: number) => {
if (slideMode === 'drag') {
setIsDragging(true);
}
},
[slideMode],
);
const handleEnd = useCallback(() => {
if (slideMode === 'drag') {
setIsDragging(false);
}
}, [slideMode]);
const handleMove = useCallback(
(clientX: number) => {
if (!sliderRef.current) return;
if (slideMode === 'hover' || (slideMode === 'drag' && isDragging)) {
const rect = sliderRef.current.getBoundingClientRect();
const x = clientX - rect.left;
const percent = (x / rect.width) * 100;
requestAnimationFrame(() => {
setSliderXPercent(Math.max(0, Math.min(100, percent)));
});
}
},
[slideMode, isDragging],
);
const handleMouseDown = useCallback(
(e: React.MouseEvent) => handleStart(e.clientX),
[handleStart],
);
const handleMouseUp = useCallback(() => handleEnd(), [handleEnd]);
const handleMouseMove = useCallback((e: React.MouseEvent) => handleMove(e.clientX), [handleMove]);
const handleTouchStart = useCallback(
(e: React.TouchEvent) => {
if (!autoplay) {
handleStart(e.touches[0].clientX);
}
},
[handleStart, autoplay],
);
const handleTouchEnd = useCallback(() => {
if (!autoplay) {
handleEnd();
}
}, [handleEnd, autoplay]);
const handleTouchMove = useCallback(
(e: React.TouchEvent) => {
if (!autoplay) {
handleMove(e.touches[0].clientX);
}
},
[handleMove, autoplay],
);
return (
<div
ref={sliderRef}
className={cn('h-[400px] w-[400px] overflow-hidden', className)}
style={{
position: 'relative',
cursor: slideMode === 'drag' ? 'grab' : 'col-resize',
}}
onMouseMove={handleMouseMove}
onMouseLeave={mouseLeaveHandler}
onMouseEnter={mouseEnterHandler}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onTouchMove={handleTouchMove}
>
<AnimatePresence initial={false}>
<motion.div
className='absolute top-0 z-30 m-auto h-full w-px bg-gradient-to-b from-transparent from-[5%] via-indigo-500 to-transparent to-[95%]'
style={{
left: `${sliderXPercent}%`,
top: '0',
zIndex: 40,
}}
transition={{ duration: 0 }}
>
<div className='absolute left-0 top-1/2 z-20 h-full w-36 -translate-y-1/2 bg-gradient-to-r from-indigo-400 via-transparent to-transparent opacity-50 [mask-image:radial-gradient(100px_at_left,white,transparent)]' />
<div className='absolute left-0 top-1/2 z-10 h-1/2 w-10 -translate-y-1/2 bg-gradient-to-r from-cyan-400 via-transparent to-transparent opacity-100 [mask-image:radial-gradient(50px_at_left,white,transparent)]' />
<div className='absolute -right-10 top-1/2 h-3/4 w-10 -translate-y-1/2 [mask-image:radial-gradient(100px_at_left,white,transparent)]'>
<MemoizedSparklesCore
background='transparent'
minSize={0.4}
maxSize={1}
particleDensity={1200}
className='h-full w-full'
particleColor='#FFFFFF'
/>
</div>
{showHandlebar && (
<div className='absolute -right-2.5 top-1/2 z-30 flex h-5 w-5 -translate-y-1/2 items-center justify-center rounded-md bg-white shadow-[0px_-1px_0px_0px_#FFFFFF40]'>
<IconDotsVertical className='h-4 w-4 text-black' />
</div>
)}
</motion.div>
</AnimatePresence>
<div className='pointer-events-none relative z-20 h-full w-full overflow-hidden'>
<AnimatePresence initial={false}>
{firstImage ? (
<motion.div
className={cn(
'absolute inset-0 z-20 h-full w-full flex-shrink-0 select-none overflow-hidden rounded-2xl',
firstImageClassName,
)}
style={{
clipPath: `inset(0 ${100 - sliderXPercent}% 0 0)`,
}}
transition={{ duration: 0 }}
>
<img
alt='first image'
src={firstImage}
className={cn(
'absolute inset-0 z-20 h-full w-full flex-shrink-0 select-none rounded-2xl',
firstImageClassName,
)}
draggable={false}
/>
</motion.div>
) : null}
</AnimatePresence>
</div>
<AnimatePresence initial={false}>
{secondImage ? (
<motion.img
className={cn(
'absolute left-0 top-0 z-[19] h-full w-full select-none rounded-2xl',
secondImageClassname,
)}
alt='second image'
src={secondImage}
draggable={false}
/>
) : null}
</AnimatePresence>
</div>
);
};
const MemoizedSparklesCore = React.memo(SparklesCore);

View File

@ -1,126 +0,0 @@
import type {
GlobalOptions as ConfettiGlobalOptions,
CreateTypes as ConfettiInstance,
Options as ConfettiOptions,
} from 'canvas-confetti';
import confetti from 'canvas-confetti';
import type { ReactNode } from 'react';
import React, {
createContext,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
} from 'react';
import { Button, ButtonProps } from './button';
type Api = {
fire: (options?: ConfettiOptions) => void;
};
type Props = React.ComponentPropsWithRef<'canvas'> & {
options?: ConfettiOptions;
globalOptions?: ConfettiGlobalOptions;
manualstart?: boolean;
children?: ReactNode;
};
export type ConfettiRef = Api | null;
const ConfettiContext = createContext<Api>({} as Api);
const Confetti = forwardRef<ConfettiRef, Props>((props, ref) => {
const {
options,
globalOptions = { resize: true, useWorker: true },
manualstart = false,
children,
...rest
} = props;
const instanceRef = useRef<ConfettiInstance | null>(null); // confetti instance
const canvasRef = useCallback(
// https://react.dev/reference/react-dom/components/common#ref-callback
// https://reactjs.org/docs/refs-and-the-dom.html#callback-refs
(node: HTMLCanvasElement) => {
if (node !== null) {
// <canvas> is mounted => create the confetti instance
if (instanceRef.current) return; // if not already created
instanceRef.current = confetti.create(node, {
...globalOptions,
resize: true,
});
} else {
// <canvas> is unmounted => reset and destroy instanceRef
if (instanceRef.current) {
instanceRef.current.reset();
instanceRef.current = null;
}
}
},
[globalOptions],
);
// `fire` is a function that calls the instance() with `opts` merged with `options`
const fire = useCallback(
(opts = {}) => instanceRef.current?.({ ...options, ...opts }),
[options],
);
const api = useMemo(
() => ({
fire,
}),
[fire],
);
useImperativeHandle(ref, () => api, [api]);
useEffect(() => {
if (!manualstart) {
fire();
}
}, [manualstart, fire]);
return (
<ConfettiContext.Provider value={api}>
<canvas ref={canvasRef} {...rest} />
{children}
</ConfettiContext.Provider>
);
});
interface ConfettiButtonProps extends ButtonProps {
options?: ConfettiOptions & ConfettiGlobalOptions & { canvas?: HTMLCanvasElement };
children?: React.ReactNode;
}
function ConfettiButton({ options, children, ...props }: ConfettiButtonProps) {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
const rect = event.currentTarget.getBoundingClientRect();
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;
confetti({
...options,
origin: {
x: x / window.innerWidth,
y: y / window.innerHeight,
},
});
};
return (
<Button onClick={handleClick} {...props}>
{children}
</Button>
);
}
Confetti.displayName = 'Confetti';
export { Confetti, ConfettiButton };
export default Confetti;

View File

@ -0,0 +1,95 @@
'use client';
import { motion, MotionValue, useScroll, useTransform } from 'framer-motion';
import React, { useRef } from 'react';
export const ContainerScroll = ({
titleComponent,
children,
}: {
titleComponent: string | React.ReactNode;
children: React.ReactNode;
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target: containerRef,
});
const [isMobile, setIsMobile] = React.useState(false);
React.useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth <= 768);
};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => {
window.removeEventListener('resize', checkMobile);
};
}, []);
const scaleDimensions = () => {
return isMobile ? [0.7, 0.9] : [1.05, 1];
};
const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
const scale = useTransform(scrollYProgress, [0, 1], scaleDimensions());
const translate = useTransform(scrollYProgress, [0, 1], [0, -100]);
return (
<div
className='relative flex h-[60rem] items-center justify-center p-2 md:h-[80rem] md:p-20'
ref={containerRef}
>
<div
className='relative w-full py-10 md:py-40'
style={{
perspective: '1000px',
}}
>
<Header translate={translate} titleComponent={titleComponent} />
<Card rotate={rotate} translate={translate} scale={scale}>
{children}
</Card>
</div>
</div>
);
};
export const Header = ({ translate, titleComponent }: any) => {
return (
<motion.div
style={{
translateY: translate,
}}
className='div mx-auto max-w-5xl text-center'
>
{titleComponent}
</motion.div>
);
};
export const Card = ({
rotate,
scale,
children,
}: {
rotate: MotionValue<number>;
scale: MotionValue<number>;
translate: MotionValue<number>;
children: React.ReactNode;
}) => {
return (
<motion.div
style={{
rotateX: rotate,
scale,
boxShadow:
'0 0 #0000004d, 0 9px 20px #0000004a, 0 37px 37px #00000042, 0 84px 50px #00000026, 0 149px 60px #0000000a, 0 233px 65px #00000003',
}}
className='mx-auto -mt-12 h-[30rem] w-full max-w-5xl rounded-[30px] border-4 border-[#6C6C6C] bg-[#222222] p-2 shadow-2xl md:h-[40rem] md:p-6'
>
<div className='h-full w-full overflow-hidden rounded-2xl bg-gray-100 md:rounded-2xl md:p-4 dark:bg-zinc-900'>
{children}
</div>
</motion.div>
);
};

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as ContextMenuPrimitive from '@radix-ui/react-context-menu'; import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';

View File

@ -1,227 +0,0 @@
import React, { ReactNode, useEffect, useRef } from 'react';
export interface BaseParticle {
element: HTMLElement | SVGSVGElement;
left: number;
size: number;
top: number;
}
export interface BaseParticleOptions {
particle?: string;
size?: number;
}
export interface CoolParticle extends BaseParticle {
direction: number;
speedHorz: number;
speedUp: number;
spinSpeed: number;
spinVal: number;
}
export interface CoolParticleOptions extends BaseParticleOptions {
particleCount?: number;
speedHorz?: number;
speedUp?: number;
}
const getContainer = () => {
const id = '_coolMode_effect';
let existingContainer = document.getElementById(id);
if (existingContainer) {
return existingContainer;
}
const container = document.createElement('div');
container.setAttribute('id', id);
container.setAttribute(
'style',
'overflow:hidden; position:fixed; height:100%; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483647',
);
document.body.appendChild(container);
return container;
};
let instanceCounter = 0;
const applyParticleEffect = (element: HTMLElement, options?: CoolParticleOptions): (() => void) => {
instanceCounter++;
const defaultParticle = 'circle';
const particleType = options?.particle || defaultParticle;
const sizes = [15, 20, 25, 35, 45];
const limit = 45;
let particles: CoolParticle[] = [];
let autoAddParticle = false;
let mouseX = 0;
let mouseY = 0;
const container = getContainer();
function generateParticle() {
const size = options?.size || sizes[Math.floor(Math.random() * sizes.length)];
const speedHorz = options?.speedHorz || Math.random() * 10;
const speedUp = options?.speedUp || Math.random() * 25;
const spinVal = Math.random() * 360;
const spinSpeed = Math.random() * 35 * (Math.random() <= 0.5 ? -1 : 1);
const top = mouseY - size / 2;
const left = mouseX - size / 2;
const direction = Math.random() <= 0.5 ? -1 : 1;
const particle = document.createElement('div');
if (particleType === 'circle') {
const svgNS = 'http://www.w3.org/2000/svg';
const circleSVG = document.createElementNS(svgNS, 'svg');
const circle = document.createElementNS(svgNS, 'circle');
circle.setAttributeNS(null, 'cx', (size / 2).toString());
circle.setAttributeNS(null, 'cy', (size / 2).toString());
circle.setAttributeNS(null, 'r', (size / 2).toString());
circle.setAttributeNS(null, 'fill', `hsl(${Math.random() * 360}, 70%, 50%)`);
circleSVG.appendChild(circle);
circleSVG.setAttribute('width', size.toString());
circleSVG.setAttribute('height', size.toString());
particle.appendChild(circleSVG);
} else {
particle.innerHTML = `<img src="${particleType}" width="${size}" height="${size}" style="border-radius: 50%">`;
}
particle.style.position = 'absolute';
particle.style.transform = `translate3d(${left}px, ${top}px, 0px) rotate(${spinVal}deg)`;
container.appendChild(particle);
particles.push({
direction,
element: particle,
left,
size,
speedHorz,
speedUp,
spinSpeed,
spinVal,
top,
});
}
function refreshParticles() {
particles.forEach((p) => {
p.left = p.left - p.speedHorz * p.direction;
p.top = p.top - p.speedUp;
p.speedUp = Math.min(p.size, p.speedUp - 1);
p.spinVal = p.spinVal + p.spinSpeed;
if (p.top >= Math.max(window.innerHeight, document.body.clientHeight) + p.size) {
particles = particles.filter((o) => o !== p);
p.element.remove();
}
p.element.setAttribute(
'style',
[
'position:absolute',
'will-change:transform',
`top:${p.top}px`,
`left:${p.left}px`,
`transform:rotate(${p.spinVal}deg)`,
].join(';'),
);
});
}
let animationFrame: number | undefined;
let lastParticleTimestamp = 0;
const particleGenerationDelay = 30;
function loop() {
const currentTime = performance.now();
if (
autoAddParticle &&
particles.length < limit &&
currentTime - lastParticleTimestamp > particleGenerationDelay
) {
generateParticle();
lastParticleTimestamp = currentTime;
}
refreshParticles();
animationFrame = requestAnimationFrame(loop);
}
loop();
const isTouchInteraction = 'ontouchstart' in window;
const tap = isTouchInteraction ? 'touchstart' : 'mousedown';
const tapEnd = isTouchInteraction ? 'touchend' : 'mouseup';
const move = isTouchInteraction ? 'touchmove' : 'mousemove';
const updateMousePosition = (e: MouseEvent | TouchEvent) => {
if ('touches' in e) {
mouseX = e.touches?.[0].clientX;
mouseY = e.touches?.[0].clientY;
} else {
mouseX = e.clientX;
mouseY = e.clientY;
}
};
const tapHandler = (e: MouseEvent | TouchEvent) => {
updateMousePosition(e);
autoAddParticle = true;
};
const disableAutoAddParticle = () => {
autoAddParticle = false;
};
element.addEventListener(move, updateMousePosition, { passive: true });
element.addEventListener(tap, tapHandler, { passive: true });
element.addEventListener(tapEnd, disableAutoAddParticle, { passive: true });
element.addEventListener('mouseleave', disableAutoAddParticle, {
passive: true,
});
return () => {
element.removeEventListener(move, updateMousePosition);
element.removeEventListener(tap, tapHandler);
element.removeEventListener(tapEnd, disableAutoAddParticle);
element.removeEventListener('mouseleave', disableAutoAddParticle);
const interval = setInterval(() => {
if (animationFrame && particles.length === 0) {
cancelAnimationFrame(animationFrame);
clearInterval(interval);
if (--instanceCounter === 0) {
container.remove();
}
}
}, 500);
};
};
interface CoolModeProps {
children: ReactNode;
options?: CoolParticleOptions;
}
export const CoolMode: React.FC<CoolModeProps> = ({ children, options }) => {
const ref = useRef<HTMLElement>(null);
useEffect(() => {
if (ref.current) {
return applyParticleEffect(ref.current, options);
}
}, [options]);
return React.cloneElement(children as React.ReactElement, { ref });
};

View File

@ -0,0 +1,219 @@
// @ts-nocheck
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import React, { useEffect, useId, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
import { SparklesCore } from './sparkles';
export const Cover = ({
children,
className,
}: {
children?: React.ReactNode;
className?: string;
}) => {
const [hovered, setHovered] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const [containerWidth, setContainerWidth] = useState(0);
const [beamPositions, setBeamPositions] = useState<number[]>([]);
useEffect(() => {
if (ref.current) {
setContainerWidth(ref.current?.clientWidth ?? 0);
const height = ref.current?.clientHeight ?? 0;
const numberOfBeams = Math.floor(height / 10); // Adjust the divisor to control the spacing
const positions = Array.from(
{ length: numberOfBeams },
(_, i) => (i + 1) * (height / (numberOfBeams + 1)),
);
setBeamPositions(positions);
}
}, [ref.current]);
return (
<div
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
ref={ref}
className='group/cover relative inline-block rounded-sm bg-neutral-100 px-2 py-2 transition duration-200 hover:bg-neutral-900 dark:bg-neutral-900'
>
<AnimatePresence>
{hovered && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{
opacity: {
duration: 0.2,
},
}}
className='absolute inset-0 h-full w-full overflow-hidden'
>
<motion.div
animate={{
translateX: ['-50%', '0%'],
}}
transition={{
translateX: {
duration: 10,
ease: 'linear',
repeat: Infinity,
},
}}
className='flex h-full w-[200%]'
>
<SparklesCore
background='transparent'
minSize={0.4}
maxSize={1}
particleDensity={500}
className='h-full w-full'
particleColor='#FFFFFF'
/>
<SparklesCore
background='transparent'
minSize={0.4}
maxSize={1}
particleDensity={500}
className='h-full w-full'
particleColor='#FFFFFF'
/>
</motion.div>
</motion.div>
)}
</AnimatePresence>
{beamPositions.map((position, index) => (
<Beam
key={index}
hovered={hovered}
duration={Math.random() * 2 + 1}
delay={Math.random() * 2 + 1}
width={containerWidth}
style={{
top: `${position}px`,
}}
/>
))}
<motion.span
key={String(hovered)}
animate={{
scale: hovered ? 0.8 : 1,
x: hovered ? [0, -30, 30, -30, 30, 0] : 0,
y: hovered ? [0, 30, -30, 30, -30, 0] : 0,
}}
exit={{
filter: 'none',
scale: 1,
x: 0,
y: 0,
}}
transition={{
duration: 0.2,
x: {
duration: 0.2,
repeat: Infinity,
repeatType: 'loop',
},
y: {
duration: 0.2,
repeat: Infinity,
repeatType: 'loop',
},
scale: {
duration: 0.2,
},
filter: {
duration: 0.2,
},
}}
className={cn(
'relative z-20 inline-block text-neutral-900 transition duration-200 group-hover/cover:text-white dark:text-white',
className,
)}
>
{children}
</motion.span>
<CircleIcon className='absolute -right-[2px] -top-[2px]' />
<CircleIcon className='absolute -bottom-[2px] -right-[2px]' delay={0.4} />
<CircleIcon className='absolute -left-[2px] -top-[2px]' delay={0.8} />
<CircleIcon className='absolute -bottom-[2px] -left-[2px]' delay={1.6} />
</div>
);
};
export const Beam = ({
className,
delay,
duration,
hovered,
width = 600,
...svgProps
}: {
className?: string;
delay?: number;
duration?: number;
hovered?: boolean;
width?: number;
} & React.ComponentProps<typeof motion.svg>) => {
const id = useId();
return (
<motion.svg
width={width ?? '600'}
height='1'
viewBox={`0 0 ${width ?? '600'} 1`}
fill='none'
xmlns='http://www.w3.org/2000/svg'
className={cn('absolute inset-x-0 w-full', className)}
{...svgProps}
>
<motion.path d={`M0 0.5H${width ?? '600'}`} stroke={`url(#svgGradient-${id})`} />
<defs>
<motion.linearGradient
id={`svgGradient-${id}`}
key={String(hovered)}
gradientUnits='userSpaceOnUse'
initial={{
x1: '0%',
x2: hovered ? '-10%' : '-5%',
y1: 0,
y2: 0,
}}
animate={{
x1: '110%',
x2: hovered ? '100%' : '105%',
y1: 0,
y2: 0,
}}
transition={{
duration: hovered ? 0.5 : (duration ?? 2),
ease: 'linear',
repeat: Infinity,
delay: hovered ? Math.random() * (1 - 0.2) + 0.2 : 0,
repeatDelay: hovered ? Math.random() * (2 - 1) + 1 : (delay ?? 1),
}}
>
<stop stopColor='#2EB9DF' stopOpacity='0' />
<stop stopColor='#3b82f6' />
<stop offset='1' stopColor='#3b82f6' stopOpacity='0' />
</motion.linearGradient>
</defs>
</motion.svg>
);
};
export const CircleIcon = ({ className, delay }: { className?: string; delay?: number }) => {
return (
<div
className={cn(
`group pointer-events-none h-2 w-2 animate-pulse rounded-full bg-neutral-600 opacity-20 group-hover/cover:hidden group-hover/cover:bg-white group-hover/cover:opacity-100 dark:bg-white`,
className,
)}
></div>
);
};

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as DialogPrimitive from '@radix-ui/react-dialog'; import * as DialogPrimitive from '@radix-ui/react-dialog';

View File

@ -0,0 +1,157 @@
// @ts-nocheck
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import Image from 'next/image';
import { useRef, useState } from 'react';
import { cn } from '../../lib/utils';
export const DirectionAwareHover = ({
imageUrl,
children,
childrenClassName,
imageClassName,
className,
}: {
imageUrl: string;
children: React.ReactNode | string;
childrenClassName?: string;
imageClassName?: string;
className?: string;
}) => {
const ref = useRef<HTMLDivElement>(null);
const [direction, setDirection] = useState<'top' | 'bottom' | 'left' | 'right' | string>('left');
const handleMouseEnter = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
if (!ref.current) return;
const direction = getDirection(event, ref.current);
console.log('direction', direction);
switch (direction) {
case 0:
setDirection('top');
break;
case 1:
setDirection('right');
break;
case 2:
setDirection('bottom');
break;
case 3:
setDirection('left');
break;
default:
setDirection('left');
break;
}
};
const getDirection = (ev: React.MouseEvent<HTMLDivElement, MouseEvent>, obj: HTMLElement) => {
const { width: w, height: h, left, top } = obj.getBoundingClientRect();
const x = ev.clientX - left - (w / 2) * (w > h ? h / w : 1);
const y = ev.clientY - top - (h / 2) * (h > w ? w / h : 1);
const d = Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4;
return d;
};
return (
<motion.div
onMouseEnter={handleMouseEnter}
ref={ref}
className={cn(
'group/card relative h-60 w-60 overflow-hidden rounded-lg bg-transparent md:h-96 md:w-96',
className,
)}
>
<AnimatePresence mode='wait'>
<motion.div
className='relative h-full w-full'
initial='initial'
whileHover={direction}
exit='exit'
>
<motion.div className='absolute inset-0 z-10 hidden h-full w-full bg-black/40 transition duration-500 group-hover/card:block' />
<motion.div
variants={variants}
className='relative h-full w-full bg-gray-50 dark:bg-black'
transition={{
duration: 0.2,
ease: 'easeOut',
}}
>
<Image
alt='image'
className={cn('h-full w-full scale-[1.15] object-cover', imageClassName)}
width='1000'
height='1000'
src={imageUrl}
/>
</motion.div>
<motion.div
variants={textVariants}
transition={{
duration: 0.5,
ease: 'easeOut',
}}
className={cn('absolute bottom-4 left-4 z-40 text-white', childrenClassName)}
>
{children}
</motion.div>
</motion.div>
</AnimatePresence>
</motion.div>
);
};
const variants = {
initial: {
x: 0,
},
exit: {
x: 0,
y: 0,
},
top: {
y: 20,
},
bottom: {
y: -20,
},
left: {
x: 20,
},
right: {
x: -20,
},
};
const textVariants = {
initial: {
y: 0,
x: 0,
opacity: 0,
},
exit: {
y: 0,
x: 0,
opacity: 0,
},
top: {
y: -20,
opacity: 1,
},
bottom: {
y: 2,
opacity: 1,
},
left: {
x: -2,
opacity: 1,
},
right: {
x: 20,
opacity: 1,
},
};

View File

@ -1,124 +0,0 @@
'use client';
import { cva, type VariantProps } from 'class-variance-authority';
import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion';
import React, { PropsWithChildren, useRef } from 'react';
import { cn } from '../../lib/utils';
export interface DockProps extends VariantProps<typeof dockVariants> {
className?: string;
magnification?: number;
distance?: number;
direction?: 'top' | 'middle' | 'bottom';
children: React.ReactNode;
}
const DEFAULT_MAGNIFICATION = 60;
const DEFAULT_DISTANCE = 140;
const dockVariants = cva(
'supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max gap-2 rounded-2xl border p-2 backdrop-blur-md',
);
const Dock = React.forwardRef<HTMLDivElement, DockProps>(
(
{
className,
children,
magnification = DEFAULT_MAGNIFICATION,
distance = DEFAULT_DISTANCE,
direction = 'bottom',
...props
},
ref,
) => {
const mouseX = useMotionValue(Infinity);
const renderChildren = () => {
return React.Children.map(children, (child) => {
if (React.isValidElement(child) && child.type === DockIcon) {
return React.cloneElement(child, {
...child.props,
mouseX: mouseX,
magnification: magnification,
distance: distance,
});
}
return child;
});
};
return (
<motion.div
ref={ref}
onMouseMove={(e) => mouseX.set(e.pageX)}
onMouseLeave={() => mouseX.set(Infinity)}
{...props}
className={cn(dockVariants({ className }), {
'items-start': direction === 'top',
'items-center': direction === 'middle',
'items-end': direction === 'bottom',
})}
>
{renderChildren()}
</motion.div>
);
},
);
Dock.displayName = 'Dock';
export interface DockIconProps {
size?: number;
magnification?: number;
distance?: number;
mouseX?: any;
className?: string;
children?: React.ReactNode;
props?: PropsWithChildren;
}
const DockIcon = ({
size,
magnification = DEFAULT_MAGNIFICATION,
distance = DEFAULT_DISTANCE,
mouseX,
className,
children,
...props
}: DockIconProps) => {
const ref = useRef<HTMLDivElement>(null);
const distanceCalc = useTransform(mouseX, (val: number) => {
const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };
return val - bounds.x - bounds.width / 2;
});
const widthSync = useTransform(distanceCalc, [-distance, 0, distance], [40, magnification, 40]);
const width = useSpring(widthSync, {
mass: 0.1,
stiffness: 150,
damping: 12,
});
return (
<motion.div
ref={ref}
style={{ width }}
className={cn(
'flex aspect-square cursor-pointer items-center justify-center rounded-full',
className,
)}
{...props}
>
{children}
</motion.div>
);
};
DockIcon.displayName = 'DockIcon';
export { Dock, DockIcon, dockVariants };

View File

@ -1,56 +0,0 @@
import { useId } from 'react';
import { cn } from '../../lib/utils';
interface DotPatternProps {
width?: any;
height?: any;
x?: any;
y?: any;
cx?: any;
cy?: any;
cr?: any;
className?: string;
[key: string]: any;
}
export function DotPattern({
width = 16,
height = 16,
x = 0,
y = 0,
cx = 1,
cy = 1,
cr = 1,
className,
...props
}: DotPatternProps) {
const id = useId();
return (
<svg
aria-hidden='true'
className={cn(
'pointer-events-none absolute inset-0 h-full w-full fill-neutral-400/80',
className,
)}
{...props}
>
<defs>
<pattern
id={id}
width={width}
height={height}
patternUnits='userSpaceOnUse'
patternContentUnits='userSpaceOnUse'
x={x}
y={y}
>
<circle id='pattern-circle' cx={cx} cy={cy} r={cr} />
</pattern>
</defs>
<rect width='100%' height='100%' strokeWidth={0} fill={`url(#${id})`} />
</svg>
);
}
export default DotPattern;

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';

View File

@ -0,0 +1,96 @@
// @ts-nocheck
'use client';
import { motion, useMotionTemplate, useMotionValue } from 'framer-motion';
import { useEffect, useState } from 'react';
import { cn } from '../../lib/utils';
export const EvervaultCard = ({ text, className }: { text?: string; className?: string }) => {
let mouseX = useMotionValue(0);
let mouseY = useMotionValue(0);
const [randomString, setRandomString] = useState('');
useEffect(() => {
let str = generateRandomString(1500);
setRandomString(str);
}, []);
function onMouseMove({ currentTarget, clientX, clientY }: any) {
let { left, top } = currentTarget.getBoundingClientRect();
mouseX.set(clientX - left);
mouseY.set(clientY - top);
const str = generateRandomString(1500);
setRandomString(str);
}
return (
<div
className={cn(
'relative flex aspect-square h-full w-full items-center justify-center bg-transparent p-0.5',
className,
)}
>
<div
onMouseMove={onMouseMove}
className='group/card relative flex h-full w-full items-center justify-center overflow-hidden rounded-3xl bg-transparent'
>
<CardPattern mouseX={mouseX} mouseY={mouseY} randomString={randomString} />
<div className='relative z-10 flex items-center justify-center'>
<div className='relative flex h-44 w-44 items-center justify-center rounded-full text-4xl font-bold text-white'>
<div className='absolute h-full w-full rounded-full bg-white/[0.8] blur-sm dark:bg-black/[0.8]' />
<span className='z-20 text-black dark:text-white'>{text}</span>
</div>
</div>
</div>
</div>
);
};
export function CardPattern({ mouseX, mouseY, randomString }: any) {
let maskImage = useMotionTemplate`radial-gradient(250px at ${mouseX}px ${mouseY}px, white, transparent)`;
let style = { maskImage, WebkitMaskImage: maskImage };
return (
<div className='pointer-events-none'>
<div className='absolute inset-0 rounded-2xl [mask-image:linear-gradient(white,transparent)] group-hover/card:opacity-50'></div>
<motion.div
className='absolute inset-0 rounded-2xl bg-gradient-to-r from-green-500 to-blue-700 opacity-0 backdrop-blur-xl transition duration-500 group-hover/card:opacity-100'
style={style}
/>
<motion.div
className='absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay group-hover/card:opacity-100'
style={style}
>
<p className='absolute inset-x-0 h-full whitespace-pre-wrap break-words font-mono text-xs font-bold text-white transition duration-500'>
{randomString}
</p>
</motion.div>
</div>
);
}
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
export const generateRandomString = (length: number) => {
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
};
export const Icon = ({ className, ...rest }: any) => {
return (
<svg
xmlns='http://www.w3.org/2000/svg'
fill='none'
viewBox='0 0 24 24'
strokeWidth='1.5'
stroke='currentColor'
className={className}
{...rest}
>
<path strokeLinecap='round' strokeLinejoin='round' d='M12 6v12m6-6H6' />
</svg>
);
};

View File

@ -1,59 +0,0 @@
'use client';
import { motion, Variants } from 'framer-motion';
import { useMemo } from 'react';
type FadeTextProps = {
className?: string;
direction?: 'up' | 'down' | 'left' | 'right';
framerProps?: Variants;
text: string;
};
export function FadeText({
direction = 'up',
className,
framerProps = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { type: 'spring' } },
},
text,
}: FadeTextProps) {
const directionOffset = useMemo(() => {
const map = { up: 10, down: -10, left: -10, right: 10 };
return map[direction];
}, [direction]);
const axis = direction === 'up' || direction === 'down' ? 'y' : 'x';
const FADE_ANIMATION_VARIANTS = useMemo(() => {
const { hidden, show, ...rest } = framerProps as {
[name: string]: { [name: string]: number; opacity: number };
};
return {
...rest,
hidden: {
...(hidden ?? {}),
opacity: hidden?.opacity ?? 0,
[axis]: hidden?.[axis] ?? directionOffset,
},
show: {
...(show ?? {}),
opacity: show?.opacity ?? 1,
[axis]: show?.[axis] ?? 0,
},
};
}, [directionOffset, axis, framerProps]);
return (
<motion.div
initial='hidden'
animate='show'
viewport={{ once: true }}
variants={FADE_ANIMATION_VARIANTS}
>
<motion.span className={className}>{text}</motion.span>
</motion.div>
);
}

View File

@ -1,338 +0,0 @@
'use client';
import * as AccordionPrimitive from '@radix-ui/react-accordion';
import { FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react';
import React, {
createContext,
forwardRef,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import { cn } from '../../lib/utils';
import { Button } from './button';
import { ScrollArea } from './scroll-area';
type TreeViewElement = {
id: string;
name: string;
isSelectable?: boolean;
children?: TreeViewElement[];
};
type TreeContextProps = {
selectedId: string | undefined;
expandedItems: string[] | undefined;
indicator: boolean;
handleExpand: (id: string) => void;
selectItem: (id: string) => void;
setExpandedItems?: React.Dispatch<React.SetStateAction<string[] | undefined>>;
openIcon?: React.ReactNode;
closeIcon?: React.ReactNode;
direction: 'rtl' | 'ltr';
};
const TreeContext = createContext<TreeContextProps | null>(null);
const useTree = () => {
const context = useContext(TreeContext);
if (!context) {
throw new Error('useTree must be used within a TreeProvider');
}
return context;
};
interface TreeViewComponentProps extends React.HTMLAttributes<HTMLDivElement> {}
type Direction = 'rtl' | 'ltr' | undefined;
type TreeViewProps = {
initialSelectedId?: string;
indicator?: boolean;
elements?: TreeViewElement[];
initialExpandedItems?: string[];
openIcon?: React.ReactNode;
closeIcon?: React.ReactNode;
} & TreeViewComponentProps;
const Tree = forwardRef<HTMLDivElement, TreeViewProps>(
(
{
className,
elements,
initialSelectedId,
initialExpandedItems,
children,
indicator = true,
openIcon,
closeIcon,
dir,
...props
},
ref,
) => {
const [selectedId, setSelectedId] = useState<string | undefined>(initialSelectedId);
const [expandedItems, setExpandedItems] = useState<string[] | undefined>(initialExpandedItems);
const selectItem = useCallback((id: string) => {
setSelectedId(id);
}, []);
const handleExpand = useCallback((id: string) => {
setExpandedItems((prev) => {
if (prev?.includes(id)) {
return prev.filter((item) => item !== id);
}
return [...(prev ?? []), id];
});
}, []);
const expandSpecificTargetedElements = useCallback(
(elements?: TreeViewElement[], selectId?: string) => {
if (!elements || !selectId) return;
const findParent = (currentElement: TreeViewElement, currentPath: string[] = []) => {
const isSelectable = currentElement.isSelectable ?? true;
const newPath = [...currentPath, currentElement.id];
if (currentElement.id === selectId) {
if (isSelectable) {
setExpandedItems((prev) => [...(prev ?? []), ...newPath]);
} else {
if (newPath.includes(currentElement.id)) {
newPath.pop();
setExpandedItems((prev) => [...(prev ?? []), ...newPath]);
}
}
return;
}
if (isSelectable && currentElement.children && currentElement.children.length > 0) {
currentElement.children.forEach((child) => {
findParent(child, newPath);
});
}
};
elements.forEach((element) => {
findParent(element);
});
},
[],
);
useEffect(() => {
if (initialSelectedId) {
expandSpecificTargetedElements(elements, initialSelectedId);
}
}, [initialSelectedId, elements]);
const direction = dir === 'rtl' ? 'rtl' : 'ltr';
return (
<TreeContext.Provider
value={{
selectedId,
expandedItems,
handleExpand,
selectItem,
setExpandedItems,
indicator,
openIcon,
closeIcon,
direction,
}}
>
<div className={cn('size-full', className)}>
<ScrollArea ref={ref} className='relative h-full px-2' dir={dir as Direction}>
<AccordionPrimitive.Root
{...props}
type='multiple'
defaultValue={expandedItems}
value={expandedItems}
className='flex flex-col gap-1'
onValueChange={(value) => setExpandedItems((prev) => [...(prev ?? []), value[0]])}
dir={dir as Direction}
>
{children}
</AccordionPrimitive.Root>
</ScrollArea>
</div>
</TreeContext.Provider>
);
},
);
Tree.displayName = 'Tree';
const TreeIndicator = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const { direction } = useTree();
return (
<div
dir={direction}
ref={ref}
className={cn(
'bg-muted absolute left-1.5 h-full w-px rounded-md py-3 duration-300 ease-in-out hover:bg-slate-300 rtl:right-1.5',
className,
)}
{...props}
/>
);
},
);
TreeIndicator.displayName = 'TreeIndicator';
interface FolderComponentProps
extends React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> {}
type FolderProps = {
expandedItems?: string[];
element: string;
isSelectable?: boolean;
isSelect?: boolean;
} & FolderComponentProps;
const Folder = forwardRef<HTMLDivElement, FolderProps & React.HTMLAttributes<HTMLDivElement>>(
({ className, element, value, isSelectable = true, isSelect, children, ...props }, ref) => {
const {
direction,
handleExpand,
expandedItems,
indicator,
setExpandedItems,
openIcon,
closeIcon,
} = useTree();
return (
<AccordionPrimitive.Item {...props} value={value} className='relative h-full overflow-hidden'>
<AccordionPrimitive.Trigger
className={cn(`flex items-center gap-1 rounded-md text-sm`, className, {
'bg-muted rounded-md': isSelect && isSelectable,
'cursor-pointer': isSelectable,
'cursor-not-allowed opacity-50': !isSelectable,
})}
disabled={!isSelectable}
onClick={() => handleExpand(value)}
>
{expandedItems?.includes(value)
? (openIcon ?? <FolderOpenIcon className='size-4' />)
: (closeIcon ?? <FolderIcon className='size-4' />)}
<span>{element}</span>
</AccordionPrimitive.Trigger>
<AccordionPrimitive.Content className='data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down relative h-full overflow-hidden text-sm'>
{element && indicator && <TreeIndicator aria-hidden='true' />}
<AccordionPrimitive.Root
dir={direction}
type='multiple'
className='ml-5 flex flex-col gap-1 py-1 rtl:mr-5'
defaultValue={expandedItems}
value={expandedItems}
onValueChange={(value) => {
setExpandedItems?.((prev) => [...(prev ?? []), value[0]]);
}}
>
{children}
</AccordionPrimitive.Root>
</AccordionPrimitive.Content>
</AccordionPrimitive.Item>
);
},
);
Folder.displayName = 'Folder';
const File = forwardRef<
HTMLButtonElement,
{
value: string;
handleSelect?: (id: string) => void;
isSelectable?: boolean;
isSelect?: boolean;
fileIcon?: React.ReactNode;
} & React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(
(
{ value, className, handleSelect, isSelectable = true, isSelect, fileIcon, children, ...props },
ref,
) => {
const { direction, selectedId, selectItem } = useTree();
const isSelected = isSelect ?? selectedId === value;
return (
<AccordionPrimitive.Item value={value} className='relative'>
<AccordionPrimitive.Trigger
ref={ref}
{...props}
dir={direction}
disabled={!isSelectable}
aria-label='File'
className={cn(
'flex cursor-pointer items-center gap-1 rounded-md pr-1 text-sm duration-200 ease-in-out rtl:pl-1 rtl:pr-0',
{
'bg-muted': isSelected && isSelectable,
},
isSelectable ? 'cursor-pointer' : 'cursor-not-allowed opacity-50',
className,
)}
onClick={() => selectItem(value)}
>
{fileIcon ?? <FileIcon className='size-4' />}
{children}
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Item>
);
},
);
File.displayName = 'File';
const CollapseButton = forwardRef<
HTMLButtonElement,
{
elements: TreeViewElement[];
expandAll?: boolean;
} & React.HTMLAttributes<HTMLButtonElement>
>(({ className, elements, expandAll = false, children, ...props }, ref) => {
const { expandedItems, setExpandedItems } = useTree();
const expendAllTree = useCallback((elements: TreeViewElement[]) => {
const expandTree = (element: TreeViewElement) => {
const isSelectable = element.isSelectable ?? true;
if (isSelectable && element.children && element.children.length > 0) {
setExpandedItems?.((prev) => [...(prev ?? []), element.id]);
element.children.forEach(expandTree);
}
};
elements.forEach(expandTree);
}, []);
const closeAll = useCallback(() => {
setExpandedItems?.([]);
}, []);
useEffect(() => {
console.log(expandAll);
if (expandAll) {
expendAllTree(elements);
}
}, [expandAll]);
return (
<Button
variant={'ghost'}
className='absolute bottom-1 right-2 h-8 w-fit p-1'
onClick={expandedItems && expandedItems.length > 0 ? closeAll : () => expendAllTree(elements)}
ref={ref}
{...props}
>
{children}
<span className='sr-only'>Toggle</span>
</Button>
);
});
CollapseButton.displayName = 'CollapseButton';
export { CollapseButton, File, Folder, Tree, type TreeViewElement };

View File

@ -0,0 +1,185 @@
// @ts-nocheck
import { IconUpload } from '@tabler/icons-react';
import { motion } from 'framer-motion';
import { useRef, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { cn } from '../../lib/utils';
const mainVariant = {
initial: {
x: 0,
y: 0,
},
animate: {
x: 20,
y: -20,
opacity: 0.9,
},
};
const secondaryVariant = {
initial: {
opacity: 0,
},
animate: {
opacity: 1,
},
};
export const FileUpload = ({ onChange }: { onChange?: (files: File[]) => void }) => {
const [files, setFiles] = useState<File[]>([]);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleFileChange = (newFiles: File[]) => {
setFiles((prevFiles) => [...prevFiles, ...newFiles]);
onChange && onChange(newFiles);
};
const handleClick = () => {
fileInputRef.current?.click();
};
const { getRootProps, isDragActive } = useDropzone({
multiple: false,
noClick: true,
onDrop: handleFileChange,
onDropRejected: (error) => {
console.log(error);
},
});
return (
<div className='w-full' {...getRootProps()}>
<motion.div
onClick={handleClick}
whileHover='animate'
className='group/file relative block w-full cursor-pointer overflow-hidden rounded-lg p-10'
>
<input
ref={fileInputRef}
id='file-upload-handle'
type='file'
onChange={(e) => handleFileChange(Array.from(e.target.files || []))}
className='hidden'
/>
<div className='absolute inset-0 [mask-image:radial-gradient(ellipse_at_center,white,transparent)]'>
<GridPattern />
</div>
<div className='flex flex-col items-center justify-center'>
<p className='relative z-20 font-sans text-base font-bold text-neutral-700 dark:text-neutral-300'>
Upload file
</p>
<p className='relative z-20 mt-2 font-sans text-base font-normal text-neutral-400 dark:text-neutral-400'>
Drag or drop your files here or click to upload
</p>
<div className='relative mx-auto mt-10 w-full max-w-xl'>
{files.length > 0 &&
files.map((file, idx) => (
<motion.div
key={'file' + idx}
layoutId={idx === 0 ? 'file-upload' : 'file-upload-' + idx}
className={cn(
'relative z-40 mx-auto mt-4 flex w-full flex-col items-start justify-start overflow-hidden rounded-md bg-white p-4 md:h-24 dark:bg-neutral-900',
'shadow-sm',
)}
>
<div className='flex w-full items-center justify-between gap-4'>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
layout
className='max-w-xs truncate text-base text-neutral-700 dark:text-neutral-300'
>
{file.name}
</motion.p>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
layout
className='shadow-input w-fit flex-shrink-0 rounded-lg px-2 py-1 text-sm text-neutral-600 dark:bg-neutral-800 dark:text-white'
>
{(file.size / (1024 * 1024)).toFixed(2)} MB
</motion.p>
</div>
<div className='mt-2 flex w-full flex-col items-start justify-between text-sm text-neutral-600 md:flex-row md:items-center dark:text-neutral-400'>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
layout
className='rounded-md bg-gray-100 px-1 py-0.5 dark:bg-neutral-800'
>
{file.type}
</motion.p>
<motion.p initial={{ opacity: 0 }} animate={{ opacity: 1 }} layout>
modified {new Date(file.lastModified).toLocaleDateString()}
</motion.p>
</div>
</motion.div>
))}
{!files.length && (
<motion.div
layoutId='file-upload'
variants={mainVariant}
transition={{
type: 'spring',
stiffness: 300,
damping: 20,
}}
className={cn(
'relative z-40 mx-auto mt-4 flex h-32 w-full max-w-[8rem] items-center justify-center rounded-md bg-white group-hover/file:shadow-2xl dark:bg-neutral-900',
'shadow-[0px_10px_50px_rgba(0,0,0,0.1)]',
)}
>
{isDragActive ? (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className='flex flex-col items-center text-neutral-600'
>
Drop it
<IconUpload className='h-4 w-4 text-neutral-600 dark:text-neutral-400' />
</motion.p>
) : (
<IconUpload className='h-4 w-4 text-neutral-600 dark:text-neutral-300' />
)}
</motion.div>
)}
{!files.length && (
<motion.div
variants={secondaryVariant}
className='absolute inset-0 z-30 mx-auto mt-4 flex h-32 w-full max-w-[8rem] items-center justify-center rounded-md border border-dashed border-sky-400 bg-transparent opacity-0'
></motion.div>
)}
</div>
</div>
</motion.div>
</div>
);
};
export function GridPattern() {
const columns = 41;
const rows = 11;
return (
<div className='flex flex-shrink-0 scale-105 flex-wrap items-center justify-center gap-x-px gap-y-px bg-gray-100 dark:bg-neutral-900'>
{Array.from({ length: rows }).map((_, row) =>
Array.from({ length: columns }).map((_, col) => {
const index = row * columns + col;
return (
<div
key={`${col}-${row}`}
className={`flex h-10 w-10 flex-shrink-0 rounded-[2px] ${
index % 2 === 0
? 'bg-gray-50 dark:bg-neutral-950'
: 'bg-gray-50 shadow-[0px_0px_1px_3px_rgba(255,255,255,1)_inset] dark:bg-neutral-950 dark:shadow-[0px_0px_1px_3px_rgba(0,0,0,1)_inset]'
}`}
/>
);
}),
)}
</div>
);
}

View File

@ -1,178 +0,0 @@
'use client';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
interface FlickeringGridProps {
squareSize?: number;
gridGap?: number;
flickerChance?: number;
color?: string;
width?: number;
height?: number;
className?: string;
maxOpacity?: number;
}
const FlickeringGrid: React.FC<FlickeringGridProps> = ({
squareSize = 4,
gridGap = 6,
flickerChance = 0.3,
color = 'rgb(0, 0, 0)',
width,
height,
className,
maxOpacity = 0.3,
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [isInView, setIsInView] = useState(false);
const memoizedColor = useMemo(() => {
const toRGBA = (color: string) => {
if (typeof window === 'undefined') {
return `rgba(0, 0, 0,`;
}
const canvas = document.createElement('canvas');
canvas.width = canvas.height = 1;
const ctx = canvas.getContext('2d');
if (!ctx) return 'rgba(255, 0, 0,';
ctx.fillStyle = color;
ctx.fillRect(0, 0, 1, 1);
const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data;
return `rgba(${r}, ${g}, ${b},`;
};
return toRGBA(color);
}, [color]);
const setupCanvas = useCallback(
(canvas: HTMLCanvasElement) => {
const canvasWidth = width || canvas.clientWidth;
const canvasHeight = height || canvas.clientHeight;
const dpr = window.devicePixelRatio || 1;
canvas.width = canvasWidth * dpr;
canvas.height = canvasHeight * dpr;
canvas.style.width = `${canvasWidth}px`;
canvas.style.height = `${canvasHeight}px`;
const cols = Math.floor(canvasWidth / (squareSize + gridGap));
const rows = Math.floor(canvasHeight / (squareSize + gridGap));
const squares = new Float32Array(cols * rows);
for (let i = 0; i < squares.length; i++) {
squares[i] = Math.random() * maxOpacity;
}
return {
width: canvasWidth,
height: canvasHeight,
cols,
rows,
squares,
dpr,
};
},
[squareSize, gridGap, width, height, maxOpacity],
);
const updateSquares = useCallback(
(squares: Float32Array, deltaTime: number) => {
for (let i = 0; i < squares.length; i++) {
if (Math.random() < flickerChance * deltaTime) {
squares[i] = Math.random() * maxOpacity;
}
}
},
[flickerChance, maxOpacity],
);
const drawGrid = useCallback(
(
ctx: CanvasRenderingContext2D,
width: number,
height: number,
cols: number,
rows: number,
squares: Float32Array,
dpr: number,
) => {
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = 'transparent';
ctx.fillRect(0, 0, width, height);
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
const opacity = squares[i * rows + j];
ctx.fillStyle = `${memoizedColor}${opacity})`;
ctx.fillRect(
i * (squareSize + gridGap) * dpr,
j * (squareSize + gridGap) * dpr,
squareSize * dpr,
squareSize * dpr,
);
}
}
},
[memoizedColor, squareSize, gridGap],
);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
let animationFrameId: number;
let { width, height, cols, rows, squares, dpr } = setupCanvas(canvas);
let lastTime = 0;
const animate = (time: number) => {
if (!isInView) return;
const deltaTime = (time - lastTime) / 1000;
lastTime = time;
updateSquares(squares, deltaTime);
drawGrid(ctx, width * dpr, height * dpr, cols, rows, squares, dpr);
animationFrameId = requestAnimationFrame(animate);
};
const handleResize = () => {
({ width, height, cols, rows, squares, dpr } = setupCanvas(canvas));
};
const observer = new IntersectionObserver(
([entry]) => {
setIsInView(entry.isIntersecting);
},
{ threshold: 0 },
);
observer.observe(canvas);
window.addEventListener('resize', handleResize);
if (isInView) {
animationFrameId = requestAnimationFrame(animate);
}
return () => {
window.removeEventListener('resize', handleResize);
cancelAnimationFrame(animationFrameId);
observer.disconnect();
};
}, [setupCanvas, updateSquares, drawGrid, width, height, isInView]);
return (
<canvas
ref={canvasRef}
className={`pointer-events-none size-full ${className}`}
style={{
width: width || '100%',
height: height || '100%',
}}
width={width}
height={height}
/>
);
};
export default FlickeringGrid;

View File

@ -1,44 +0,0 @@
'use client';
import { AnimatePresence, motion, Variants } from 'framer-motion';
import { cn } from '../../lib/utils';
interface SlightFlipProps {
word: string;
duration?: number;
delayMultiple?: number;
framerProps?: Variants;
className?: string;
}
export default function SlightFlip({
word,
duration = 0.5,
delayMultiple = 0.08,
framerProps = {
hidden: { rotateX: -90, opacity: 0 },
visible: { rotateX: 0, opacity: 1 },
},
className,
}: SlightFlipProps) {
return (
<div className='flex justify-center space-x-2'>
<AnimatePresence mode='wait'>
{word.split('').map((char, i) => (
<motion.span
key={i}
initial='hidden'
animate='visible'
exit='hidden'
variants={framerProps}
transition={{ duration, delay: i * delayMultiple }}
className={cn('origin-center drop-shadow-sm', className)}
>
{char}
</motion.span>
))}
</AnimatePresence>
</div>
);
}

View File

@ -0,0 +1,99 @@
// @ts-nocheck
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import { useCallback, useEffect, useState } from 'react';
import { cn } from '../../lib/utils';
export const FlipWords = ({
words,
duration = 3000,
className,
}: {
words: string[];
duration?: number;
className?: string;
}) => {
const [currentWord, setCurrentWord] = useState(words[0]);
const [isAnimating, setIsAnimating] = useState<boolean>(false);
// thanks for the fix Julian - https://github.com/Julian-AT
const startAnimation = useCallback(() => {
const word = words[words.indexOf(currentWord) + 1] || words[0];
setCurrentWord(word);
setIsAnimating(true);
}, [currentWord, words]);
useEffect(() => {
if (!isAnimating)
setTimeout(() => {
startAnimation();
}, duration);
}, [isAnimating, duration, startAnimation]);
return (
<AnimatePresence
onExitComplete={() => {
setIsAnimating(false);
}}
>
<motion.div
initial={{
opacity: 0,
y: 10,
}}
animate={{
opacity: 1,
y: 0,
}}
transition={{
type: 'spring',
stiffness: 100,
damping: 10,
}}
exit={{
opacity: 0,
y: -40,
x: 40,
filter: 'blur(8px)',
scale: 2,
position: 'absolute',
}}
className={cn(
'relative z-10 inline-block px-2 text-left text-neutral-900 dark:text-neutral-100',
className,
)}
key={currentWord}
>
{/* edit suggested by Sajal: https://x.com/DewanganSajal */}
{currentWord.split(' ').map((word, wordIndex) => (
<motion.span
key={word + wordIndex}
initial={{ opacity: 0, y: 10, filter: 'blur(8px)' }}
animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
transition={{
delay: wordIndex * 0.3,
duration: 0.3,
}}
className='inline-block whitespace-nowrap'
>
{word.split('').map((letter, letterIndex) => (
<motion.span
key={word + letterIndex}
initial={{ opacity: 0, y: 10, filter: 'blur(8px)' }}
animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
transition={{
delay: wordIndex * 0.3 + letterIndex * 0.05,
duration: 0.2,
}}
className='inline-block'
>
{letter}
</motion.span>
))}
<span className='inline-block'>&nbsp;</span>
</motion.span>
))}
</motion.div>
</AnimatePresence>
);
};

View File

@ -0,0 +1,196 @@
// @ts-nocheck
/**
* Note: Use position fixed according to your needs
* Desktop navbar is better positioned at the bottom
* Mobile navbar is better positioned at bottom right.
**/
import { IconLayoutNavbarCollapse } from '@tabler/icons-react';
import {
AnimatePresence,
MotionValue,
motion,
useMotionValue,
useSpring,
useTransform,
} from 'framer-motion';
import Link from 'next/link';
import { useRef, useState } from 'react';
import { cn } from '../../lib/utils';
export const FloatingDock = ({
items,
desktopClassName,
mobileClassName,
}: {
items: { title: string; icon: React.ReactNode; href: string }[];
desktopClassName?: string;
mobileClassName?: string;
}) => {
return (
<>
<FloatingDockDesktop items={items} className={desktopClassName} />
<FloatingDockMobile items={items} className={mobileClassName} />
</>
);
};
const FloatingDockMobile = ({
items,
className,
}: {
items: { title: string; icon: React.ReactNode; href: string }[];
className?: string;
}) => {
const [open, setOpen] = useState(false);
return (
<div className={cn('relative block md:hidden', className)}>
<AnimatePresence>
{open && (
<motion.div
layoutId='nav'
className='absolute inset-x-0 bottom-full mb-2 flex flex-col gap-2'
>
{items.map((item, idx) => (
<motion.div
key={item.title}
initial={{ opacity: 0, y: 10 }}
animate={{
opacity: 1,
y: 0,
}}
exit={{
opacity: 0,
y: 10,
transition: {
delay: idx * 0.05,
},
}}
transition={{ delay: (items.length - 1 - idx) * 0.05 }}
>
<Link
href={item.href}
key={item.title}
className='flex h-10 w-10 items-center justify-center rounded-full bg-gray-50 dark:bg-neutral-900'
>
<div className='h-4 w-4'>{item.icon}</div>
</Link>
</motion.div>
))}
</motion.div>
)}
</AnimatePresence>
<button
onClick={() => setOpen(!open)}
className='flex h-10 w-10 items-center justify-center rounded-full bg-gray-50 dark:bg-neutral-800'
>
<IconLayoutNavbarCollapse className='h-5 w-5 text-neutral-500 dark:text-neutral-400' />
</button>
</div>
);
};
const FloatingDockDesktop = ({
items,
className,
}: {
items: { title: string; icon: React.ReactNode; href: string }[];
className?: string;
}) => {
let mouseX = useMotionValue(Infinity);
return (
<motion.div
onMouseMove={(e) => mouseX.set(e.pageX)}
onMouseLeave={() => mouseX.set(Infinity)}
className={cn(
'mx-auto hidden h-16 items-end gap-4 rounded-2xl bg-gray-50 px-4 pb-3 md:flex dark:bg-neutral-900',
className,
)}
>
{items.map((item) => (
<IconContainer mouseX={mouseX} key={item.title} {...item} />
))}
</motion.div>
);
};
function IconContainer({
mouseX,
title,
icon,
href,
}: {
mouseX: MotionValue;
title: string;
icon: React.ReactNode;
href: string;
}) {
let ref = useRef<HTMLDivElement>(null);
let distance = useTransform(mouseX, (val) => {
let bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };
return val - bounds.x - bounds.width / 2;
});
let widthTransform = useTransform(distance, [-150, 0, 150], [40, 80, 40]);
let heightTransform = useTransform(distance, [-150, 0, 150], [40, 80, 40]);
let widthTransformIcon = useTransform(distance, [-150, 0, 150], [20, 40, 20]);
let heightTransformIcon = useTransform(distance, [-150, 0, 150], [20, 40, 20]);
let width = useSpring(widthTransform, {
mass: 0.1,
stiffness: 150,
damping: 12,
});
let height = useSpring(heightTransform, {
mass: 0.1,
stiffness: 150,
damping: 12,
});
let widthIcon = useSpring(widthTransformIcon, {
mass: 0.1,
stiffness: 150,
damping: 12,
});
let heightIcon = useSpring(heightTransformIcon, {
mass: 0.1,
stiffness: 150,
damping: 12,
});
const [hovered, setHovered] = useState(false);
return (
<Link href={href}>
<motion.div
ref={ref}
style={{ width, height }}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className='relative flex aspect-square items-center justify-center rounded-full bg-gray-200 dark:bg-neutral-800'
>
<AnimatePresence>
{hovered && (
<motion.div
initial={{ opacity: 0, y: 10, x: '-50%' }}
animate={{ opacity: 1, y: 0, x: '-50%' }}
exit={{ opacity: 0, y: 2, x: '-50%' }}
className='absolute -top-8 left-1/2 w-fit -translate-x-1/2 whitespace-pre rounded-md border border-gray-200 bg-gray-100 px-2 py-0.5 text-xs text-neutral-700 dark:border-neutral-900 dark:bg-neutral-800 dark:text-white'
>
{title}
</motion.div>
)}
</AnimatePresence>
<motion.div
style={{ width: widthIcon, height: heightIcon }}
className='flex items-center justify-center'
>
{icon}
</motion.div>
</motion.div>
</Link>
);
}

View File

@ -0,0 +1,78 @@
// @ts-nocheck
'use client';
import { AnimatePresence, motion, useMotionValueEvent, useScroll } from 'framer-motion';
import Link from 'next/link';
import { useState } from 'react';
import { cn } from '../../lib/utils';
export const FloatingNav = ({
navItems,
className,
}: {
navItems: {
name: string;
link: string;
icon?: JSX.Element;
}[];
className?: string;
}) => {
const { scrollYProgress } = useScroll();
const [visible, setVisible] = useState(false);
useMotionValueEvent(scrollYProgress, 'change', (current) => {
// Check if current is not undefined and is a number
if (typeof current === 'number') {
let direction = current! - scrollYProgress.getPrevious()!;
if (scrollYProgress.get() < 0.05) {
setVisible(false);
} else {
if (direction < 0) {
setVisible(true);
} else {
setVisible(false);
}
}
}
});
return (
<AnimatePresence mode='wait'>
<motion.div
initial={{
opacity: 1,
y: -100,
}}
animate={{
y: visible ? 0 : -100,
opacity: visible ? 1 : 0,
}}
transition={{
duration: 0.2,
}}
className={cn(
'fixed inset-x-0 top-10 z-[5000] mx-auto flex max-w-fit items-center justify-center space-x-4 rounded-full border border-transparent bg-white py-2 pl-8 pr-2 shadow-[0px_2px_3px_-1px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(25,28,33,0.02),0px_0px_0px_1px_rgba(25,28,33,0.08)] dark:border-white/[0.2] dark:bg-black',
className,
)}
>
{navItems.map((navItem: any, idx: number) => (
<Link
key={`link=${idx}`}
href={navItem.link}
className={cn(
'relative flex items-center space-x-1 text-neutral-600 hover:text-neutral-500 dark:text-neutral-50 dark:hover:text-neutral-300',
)}
>
<span className='block sm:hidden'>{navItem.icon}</span>
<span className='hidden text-sm sm:block'>{navItem.name}</span>
</Link>
))}
<button className='relative rounded-full border border-neutral-200 px-4 py-2 text-sm font-medium text-black dark:border-white/[0.2] dark:text-white'>
<span>Login</span>
<span className='absolute inset-x-0 -bottom-px mx-auto h-px w-1/2 bg-gradient-to-r from-transparent via-blue-500 to-transparent' />
</button>
</motion.div>
</AnimatePresence>
);
};

View File

@ -0,0 +1,65 @@
// @ts-nocheck
'use client';
import Image from 'next/image';
import React, { useState } from 'react';
import { cn } from '../../lib/utils';
export const Card = React.memo(
({
card,
index,
hovered,
setHovered,
}: {
card: any;
index: number;
hovered: number | null;
setHovered: React.Dispatch<React.SetStateAction<number | null>>;
}) => (
<div
onMouseEnter={() => setHovered(index)}
onMouseLeave={() => setHovered(null)}
className={cn(
'relative h-60 w-full overflow-hidden rounded-lg bg-gray-100 transition-all duration-300 ease-out md:h-96 dark:bg-neutral-900',
hovered !== null && hovered !== index && 'scale-[0.98] blur-sm',
)}
>
<Image src={card.src} alt={card.title} fill className='absolute inset-0 object-cover' />
<div
className={cn(
'absolute inset-0 flex items-end bg-black/50 px-4 py-8 transition-opacity duration-300',
hovered === index ? 'opacity-100' : 'opacity-0',
)}
>
<div className='bg-gradient-to-b from-neutral-50 to-neutral-200 bg-clip-text text-xl font-medium text-transparent md:text-2xl'>
{card.title}
</div>
</div>
</div>
),
);
Card.displayName = 'Card';
type Card = {
title: string;
src: string;
};
export function FocusCards({ cards }: { cards: Card[] }) {
const [hovered, setHovered] = useState<number | null>(null);
return (
<div className='mx-auto grid w-full max-w-5xl grid-cols-1 gap-10 md:grid-cols-3 md:px-8'>
{cards.map((card, index) => (
<Card
key={card.title}
card={card}
index={index}
hovered={hovered}
setHovered={setHovered}
/>
))}
</div>
);
}

View File

@ -0,0 +1,137 @@
// @ts-nocheck
// Core component that receives mouse positions and renders pointer and content
import React, { useEffect, useState } from 'react';
import { AnimatePresence, motion, useMotionValue } from 'framer-motion';
import { cn } from '../../lib/utils';
export const FollowerPointerCard = ({
children,
className,
title,
}: {
children: React.ReactNode;
className?: string;
title?: string | React.ReactNode;
}) => {
const x = useMotionValue(0);
const y = useMotionValue(0);
const ref = React.useRef<HTMLDivElement>(null);
const [rect, setRect] = useState<DOMRect | null>(null);
const [isInside, setIsInside] = useState<boolean>(false); // Add this line
useEffect(() => {
if (ref.current) {
setRect(ref.current.getBoundingClientRect());
}
}, []);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (rect) {
const scrollX = window.scrollX;
const scrollY = window.scrollY;
x.set(e.clientX - rect.left + scrollX);
y.set(e.clientY - rect.top + scrollY);
}
};
const handleMouseLeave = () => {
setIsInside(false);
};
const handleMouseEnter = () => {
setIsInside(true);
};
return (
<div
onMouseLeave={handleMouseLeave}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
style={{
cursor: 'none',
}}
ref={ref}
className={cn('relative', className)}
>
<AnimatePresence>{isInside && <FollowPointer x={x} y={y} title={title} />}</AnimatePresence>
{children}
</div>
);
};
export const FollowPointer = ({
x,
y,
title,
}: {
x: any;
y: any;
title?: string | React.ReactNode;
}) => {
const colors = [
'var(--sky-500)',
'var(--neutral-500)',
'var(--teal-500)',
'var(--green-500)',
'var(--blue-500)',
'var(--red-500)',
'var(--yellow-500)',
];
return (
<motion.div
className='absolute z-50 h-4 w-4 rounded-full'
style={{
top: y,
left: x,
pointerEvents: 'none',
}}
initial={{
scale: 1,
opacity: 1,
}}
animate={{
scale: 1,
opacity: 1,
}}
exit={{
scale: 0,
opacity: 0,
}}
>
<svg
stroke='currentColor'
fill='currentColor'
strokeWidth='1'
viewBox='0 0 16 16'
className='h-6 w-6 -translate-x-[12px] -translate-y-[10px] -rotate-[70deg] transform stroke-sky-600 text-sky-500'
height='1em'
width='1em'
xmlns='http://www.w3.org/2000/svg'
>
<path d='M14.082 2.182a.5.5 0 0 1 .103.557L8.528 15.467a.5.5 0 0 1-.917-.007L5.57 10.694.803 8.652a.5.5 0 0 1-.006-.916l12.728-5.657a.5.5 0 0 1 .556.103z'></path>
</svg>
<motion.div
style={{
backgroundColor: colors[Math.floor(Math.random() * colors.length)],
}}
initial={{
scale: 0.5,
opacity: 0,
}}
animate={{
scale: 1,
opacity: 1,
}}
exit={{
scale: 0.5,
opacity: 0,
}}
className={
'min-w-max whitespace-nowrap rounded-full bg-neutral-200 px-2 py-2 text-xs text-white'
}
>
{title || `William Shakespeare`}
</motion.div>
</motion.div>
);
};

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as LabelPrimitive from '@radix-ui/react-label'; import * as LabelPrimitive from '@radix-ui/react-label';

View File

@ -0,0 +1,132 @@
// @ts-nocheck
import { useRef } from 'react';
import { cn } from '../../lib/utils';
export const GlareCard = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
const isPointerInside = useRef(false);
const refElement = useRef<HTMLDivElement>(null);
const state = useRef({
glare: {
x: 50,
y: 50,
},
background: {
x: 50,
y: 50,
},
rotate: {
x: 0,
y: 0,
},
});
const containerStyle = {
'--m-x': '50%',
'--m-y': '50%',
'--r-x': '0deg',
'--r-y': '0deg',
'--bg-x': '50%',
'--bg-y': '50%',
'--duration': '300ms',
'--foil-size': '100%',
'--opacity': '0',
'--radius': '48px',
'--easing': 'ease',
'--transition': 'var(--duration) var(--easing)',
} as any;
const backgroundStyle = {
'--step': '5%',
'--foil-svg': `url("data:image/svg+xml,%3Csvg width='26' height='26' viewBox='0 0 26 26' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.99994 3.419C2.99994 3.419 21.6142 7.43646 22.7921 12.153C23.97 16.8695 3.41838 23.0306 3.41838 23.0306' stroke='white' stroke-width='5' stroke-miterlimit='3.86874' stroke-linecap='round' style='mix-blend-mode:darken'/%3E%3C/svg%3E")`,
'--pattern': 'var(--foil-svg) center/100% no-repeat',
'--rainbow':
'repeating-linear-gradient( 0deg,rgb(255,119,115) calc(var(--step) * 1),rgba(255,237,95,1) calc(var(--step) * 2),rgba(168,255,95,1) calc(var(--step) * 3),rgba(131,255,247,1) calc(var(--step) * 4),rgba(120,148,255,1) calc(var(--step) * 5),rgb(216,117,255) calc(var(--step) * 6),rgb(255,119,115) calc(var(--step) * 7) ) 0% var(--bg-y)/200% 700% no-repeat',
'--diagonal':
'repeating-linear-gradient( 128deg,#0e152e 0%,hsl(180,10%,60%) 3.8%,hsl(180,10%,60%) 4.5%,hsl(180,10%,60%) 5.2%,#0e152e 10%,#0e152e 12% ) var(--bg-x) var(--bg-y)/300% no-repeat',
'--shade':
'radial-gradient( farthest-corner circle at var(--m-x) var(--m-y),rgba(255,255,255,0.1) 12%,rgba(255,255,255,0.15) 20%,rgba(255,255,255,0.25) 120% ) var(--bg-x) var(--bg-y)/300% no-repeat',
'backgroundBlendMode': 'hue, hue, hue, overlay',
};
const updateStyles = () => {
if (refElement.current) {
console.log(state.current);
const { background, rotate, glare } = state.current;
refElement.current?.style.setProperty('--m-x', `${glare.x}%`);
refElement.current?.style.setProperty('--m-y', `${glare.y}%`);
refElement.current?.style.setProperty('--r-x', `${rotate.x}deg`);
refElement.current?.style.setProperty('--r-y', `${rotate.y}deg`);
refElement.current?.style.setProperty('--bg-x', `${background.x}%`);
refElement.current?.style.setProperty('--bg-y', `${background.y}%`);
}
};
return (
<div
style={containerStyle}
className='relative isolate w-[320px] transition-transform delay-[var(--delay)] duration-[var(--duration)] ease-[var(--easing)] will-change-transform [aspect-ratio:17/21] [contain:layout_style] [perspective:600px]'
ref={refElement}
onPointerMove={(event) => {
const rotateFactor = 0.4;
const rect = event.currentTarget.getBoundingClientRect();
const position = {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
};
const percentage = {
x: (100 / rect.width) * position.x,
y: (100 / rect.height) * position.y,
};
const delta = {
x: percentage.x - 50,
y: percentage.y - 50,
};
const { background, rotate, glare } = state.current;
background.x = 50 + percentage.x / 4 - 12.5;
background.y = 50 + percentage.y / 3 - 16.67;
rotate.x = -(delta.x / 3.5);
rotate.y = delta.y / 2;
rotate.x *= rotateFactor;
rotate.y *= rotateFactor;
glare.x = percentage.x;
glare.y = percentage.y;
updateStyles();
}}
onPointerEnter={() => {
isPointerInside.current = true;
if (refElement.current) {
setTimeout(() => {
if (isPointerInside.current) {
refElement.current?.style.setProperty('--duration', '0s');
}
}, 300);
}
}}
onPointerLeave={() => {
isPointerInside.current = false;
if (refElement.current) {
refElement.current.style.removeProperty('--duration');
refElement.current?.style.setProperty('--r-x', `0deg`);
refElement.current?.style.setProperty('--r-y', `0deg`);
}
}}
>
<div className='grid h-full origin-center overflow-hidden rounded-[var(--radius)] border border-slate-800 transition-transform delay-[var(--delay)] duration-[var(--duration)] ease-[var(--easing)] will-change-transform [transform:rotateY(var(--r-x))_rotateX(var(--r-y))] hover:filter-none hover:[--duration:200ms] hover:[--easing:linear] hover:[--opacity:0.6]'>
<div className='grid h-full w-full mix-blend-soft-light [clip-path:inset(0_0_0_0_round_var(--radius))] [grid-area:1/1]'>
<div className={cn('h-full w-full bg-slate-950', className)}>{children}</div>
</div>
<div className='transition-background will-change-background grid h-full w-full opacity-[var(--opacity)] mix-blend-soft-light transition-opacity delay-[var(--delay)] duration-[var(--duration)] ease-[var(--easing)] [background:radial-gradient(farthest-corner_circle_at_var(--m-x)_var(--m-y),_rgba(255,255,255,0.8)_10%,_rgba(255,255,255,0.65)_20%,_rgba(255,255,255,0)_90%)] [clip-path:inset(0_0_1px_0_round_var(--radius))] [grid-area:1/1]' />
<div
className="will-change-background after:grid-area-[inherit] after:bg-repeat-[inherit] after:bg-attachment-[inherit] after:bg-origin-[inherit] after:bg-clip-[inherit] relative grid h-full w-full opacity-[var(--opacity)] mix-blend-color-dodge transition-opacity [background-blend-mode:hue_hue_hue_overlay] [background:var(--pattern),_var(--rainbow),_var(--diagonal),_var(--shade)] [clip-path:inset(0_0_1px_0_round_var(--radius))] [grid-area:1/1] after:bg-[inherit] after:mix-blend-exclusion after:content-[''] after:[background-blend-mode:soft-light,_hue,_hard-light] after:[background-position:center,_0%_var(--bg-y),_calc(var(--bg-x)*_-1)_calc(var(--bg-y)*_-1),_var(--bg-x)_var(--bg-y)] after:[background-size:var(--foil-size),_200%_400%,_800%,_200%]"
style={{ ...backgroundStyle }}
/>
</div>
</div>
);
};

View File

@ -1,113 +1,296 @@
// @ts-nocheck
'use client'; 'use client';
import { OrbitControls } from '@react-three/drei';
import { Canvas, extend, Object3DNode, useThree } from '@react-three/fiber';
import { useEffect, useRef, useState } from 'react';
import { Color, Fog, PerspectiveCamera, Scene, Vector3 } from 'three';
import ThreeGlobe from 'three-globe';
import countries from '../../data/globe.json';
declare module '@react-three/fiber' {
interface ThreeElements {
threeGlobe: Object3DNode<ThreeGlobe, typeof ThreeGlobe>;
}
}
import createGlobe, { COBEOptions } from 'cobe'; extend({ ThreeGlobe });
import { useCallback, useEffect, useRef, useState } from 'react';
import { cn } from '../../lib/utils'; const RING_PROPAGATION_SPEED = 3;
const aspect = 1.2;
const cameraZ = 300;
const GLOBE_CONFIG: COBEOptions = { type Position = {
width: 800, order: number;
height: 800, startLat: number;
onRender: () => {}, startLng: number;
devicePixelRatio: 2, endLat: number;
phi: 0, endLng: number;
theta: 0.3, arcAlt: number;
dark: 0, color: string;
diffuse: 0.4,
mapSamples: 16000,
mapBrightness: 1.2,
baseColor: [1, 1, 1],
markerColor: [251 / 255, 100 / 255, 21 / 255],
glowColor: [1, 1, 1],
markers: [
{ location: [14.5995, 120.9842], size: 0.03 },
{ location: [19.076, 72.8777], size: 0.1 },
{ location: [23.8103, 90.4125], size: 0.05 },
{ location: [30.0444, 31.2357], size: 0.07 },
{ location: [39.9042, 116.4074], size: 0.08 },
{ location: [-23.5505, -46.6333], size: 0.1 },
{ location: [19.4326, -99.1332], size: 0.1 },
{ location: [40.7128, -74.006], size: 0.1 },
{ location: [34.6937, 135.5022], size: 0.05 },
{ location: [41.0082, 28.9784], size: 0.06 },
],
}; };
export default function Globe({ export type GlobeConfig = {
className, pointSize?: number;
config = GLOBE_CONFIG, globeColor?: string;
}: { showAtmosphere?: boolean;
className?: string; atmosphereColor?: string;
config?: COBEOptions; atmosphereAltitude?: number;
}) { emissive?: string;
let phi = 0; emissiveIntensity?: number;
let width = 0; shininess?: number;
const canvasRef = useRef<HTMLCanvasElement>(null); polygonColor?: string;
const pointerInteracting = useRef(null); ambientLight?: string;
const pointerInteractionMovement = useRef(0); directionalLeftLight?: string;
const [r, setR] = useState(0); directionalTopLight?: string;
pointLight?: string;
const updatePointerInteraction = (value: any) => { arcTime?: number;
pointerInteracting.current = value; arcLength?: number;
if (canvasRef.current) { rings?: number;
canvasRef.current.style.cursor = value ? 'grabbing' : 'grab'; maxRings?: number;
} initialPosition?: {
lat: number;
lng: number;
}; };
autoRotate?: boolean;
autoRotateSpeed?: number;
};
const updateMovement = (clientX: any) => { interface WorldProps {
if (pointerInteracting.current !== null) { globeConfig: GlobeConfig;
const delta = clientX - pointerInteracting.current; data: Position[];
pointerInteractionMovement.current = delta; }
setR(delta / 200);
}
};
const onRender = useCallback( let numbersOfRings = [0];
(state: Record<string, any>) => {
if (!pointerInteracting.current) phi += 0.005;
state.phi = phi + r;
state.width = width * 2;
state.height = width * 2;
},
[r],
);
const onResize = () => { export function Globe({ globeConfig, data }: WorldProps) {
if (canvasRef.current) { const [globeData, setGlobeData] = useState<
width = canvasRef.current.offsetWidth; | {
} size: number;
order: number;
color: (t: number) => string;
lat: number;
lng: number;
}[]
| null
>(null);
const globeRef = useRef<ThreeGlobe | null>(null);
const defaultProps = {
pointSize: 1,
atmosphereColor: '#ffffff',
showAtmosphere: true,
atmosphereAltitude: 0.1,
polygonColor: 'rgba(255,255,255,0.7)',
globeColor: '#1d072e',
emissive: '#000000',
emissiveIntensity: 0.1,
shininess: 0.9,
arcTime: 2000,
arcLength: 0.9,
rings: 1,
maxRings: 3,
...globeConfig,
}; };
useEffect(() => { useEffect(() => {
window.addEventListener('resize', onResize); if (globeRef.current) {
onResize(); _buildData();
_buildMaterial();
}
}, [globeRef.current]);
const globe = createGlobe(canvasRef.current!, { const _buildMaterial = () => {
...config, if (!globeRef.current) return;
width: width * 2,
height: width * 2,
onRender,
});
setTimeout(() => (canvasRef.current!.style.opacity = '1')); const globeMaterial = globeRef.current.globeMaterial() as unknown as {
return () => globe.destroy(); color: Color;
}, []); emissive: Color;
emissiveIntensity: number;
shininess: number;
};
globeMaterial.color = new Color(globeConfig.globeColor);
globeMaterial.emissive = new Color(globeConfig.emissive);
globeMaterial.emissiveIntensity = globeConfig.emissiveIntensity || 0.1;
globeMaterial.shininess = globeConfig.shininess || 0.9;
};
const _buildData = () => {
const arcs = data;
let points = [];
for (let i = 0; i < arcs.length; i++) {
const arc = arcs[i];
const rgb = hexToRgb(arc.color) as { r: number; g: number; b: number };
points.push({
size: defaultProps.pointSize,
order: arc.order,
color: (t: number) => `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${1 - t})`,
lat: arc.startLat,
lng: arc.startLng,
});
points.push({
size: defaultProps.pointSize,
order: arc.order,
color: (t: number) => `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${1 - t})`,
lat: arc.endLat,
lng: arc.endLng,
});
}
// remove duplicates for same lat and lng
const filteredPoints = points.filter(
(v, i, a) =>
a.findIndex((v2) =>
['lat', 'lng'].every((k) => v2[k as 'lat' | 'lng'] === v[k as 'lat' | 'lng']),
) === i,
);
setGlobeData(filteredPoints);
};
useEffect(() => {
if (globeRef.current && globeData) {
globeRef.current
.hexPolygonsData(countries.features)
.hexPolygonResolution(3)
.hexPolygonMargin(0.7)
.showAtmosphere(defaultProps.showAtmosphere)
.atmosphereColor(defaultProps.atmosphereColor)
.atmosphereAltitude(defaultProps.atmosphereAltitude)
.hexPolygonColor((e) => {
return defaultProps.polygonColor;
});
startAnimation();
}
}, [globeData]);
const startAnimation = () => {
if (!globeRef.current || !globeData) return;
globeRef.current
.arcsData(data)
.arcStartLat((d) => (d as { startLat: number }).startLat * 1)
.arcStartLng((d) => (d as { startLng: number }).startLng * 1)
.arcEndLat((d) => (d as { endLat: number }).endLat * 1)
.arcEndLng((d) => (d as { endLng: number }).endLng * 1)
.arcColor((e: any) => (e as { color: string }).color)
.arcAltitude((e) => {
return (e as { arcAlt: number }).arcAlt * 1;
})
.arcStroke((e) => {
return [0.32, 0.28, 0.3][Math.round(Math.random() * 2)];
})
.arcDashLength(defaultProps.arcLength)
.arcDashInitialGap((e) => (e as { order: number }).order * 1)
.arcDashGap(15)
.arcDashAnimateTime((e) => defaultProps.arcTime);
globeRef.current
.pointsData(data)
.pointColor((e) => (e as { color: string }).color)
.pointsMerge(true)
.pointAltitude(0.0)
.pointRadius(2);
globeRef.current
.ringsData([])
.ringColor((e: any) => (t: any) => e.color(t))
.ringMaxRadius(defaultProps.maxRings)
.ringPropagationSpeed(RING_PROPAGATION_SPEED)
.ringRepeatPeriod((defaultProps.arcTime * defaultProps.arcLength) / defaultProps.rings);
};
useEffect(() => {
if (!globeRef.current || !globeData) return;
const interval = setInterval(() => {
if (!globeRef.current || !globeData) return;
numbersOfRings = genRandomNumbers(0, data.length, Math.floor((data.length * 4) / 5));
globeRef.current.ringsData(globeData.filter((d, i) => numbersOfRings.includes(i)));
}, 2000);
return () => {
clearInterval(interval);
};
}, [globeRef.current, globeData]);
return ( return (
<div className={cn('absolute inset-0 mx-auto aspect-[1/1] w-full max-w-[600px]', className)}> <>
<canvas <threeGlobe ref={globeRef} />
className={cn( </>
'size-full opacity-0 transition-opacity duration-500 [contain:layout_paint_size]',
)}
ref={canvasRef}
onPointerDown={(e) =>
updatePointerInteraction(e.clientX - pointerInteractionMovement.current)
}
onPointerUp={() => updatePointerInteraction(null)}
onPointerOut={() => updatePointerInteraction(null)}
onMouseMove={(e) => updateMovement(e.clientX)}
onTouchMove={(e) => e.touches[0] && updateMovement(e.touches[0].clientX)}
/>
</div>
); );
} }
export function WebGLRendererConfig() {
const { gl, size } = useThree();
useEffect(() => {
gl.setPixelRatio(window.devicePixelRatio);
gl.setSize(size.width, size.height);
gl.setClearColor(0xffaaff, 0);
}, []);
return null;
}
export function World(props: WorldProps) {
const { globeConfig } = props;
const scene = new Scene();
scene.fog = new Fog(0xffffff, 400, 2000);
return (
<Canvas scene={scene} camera={new PerspectiveCamera(50, aspect, 180, 1800)}>
<WebGLRendererConfig />
<ambientLight color={globeConfig.ambientLight} intensity={0.6} />
<directionalLight
color={globeConfig.directionalLeftLight}
position={new Vector3(-400, 100, 400)}
/>
<directionalLight
color={globeConfig.directionalTopLight}
position={new Vector3(-200, 500, 200)}
/>
<pointLight
color={globeConfig.pointLight}
position={new Vector3(-200, 500, 200)}
intensity={0.8}
/>
<Globe {...props} />
<OrbitControls
enablePan={false}
enableZoom={false}
minDistance={cameraZ}
maxDistance={cameraZ}
autoRotateSpeed={1}
autoRotate={true}
minPolarAngle={Math.PI / 3.5}
maxPolarAngle={Math.PI - Math.PI / 3}
/>
</Canvas>
);
}
export function hexToRgb(hex: string) {
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function (m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null;
}
export function genRandomNumbers(min: number, max: number, count: number) {
const arr = [];
while (arr.length < count) {
const r = Math.floor(Math.random() * (max - min)) + min;
if (arr.indexOf(r) === -1) arr.push(r);
}
return arr;
}

View File

@ -0,0 +1,144 @@
// @ts-nocheck
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import React, { useEffect, useRef, useState } from 'react';
import { cn } from '../../lib/utils';
export const GlowingStarsBackgroundCard = ({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) => {
const [mouseEnter, setMouseEnter] = useState(false);
return (
<div
onMouseEnter={() => {
setMouseEnter(true);
}}
onMouseLeave={() => {
setMouseEnter(false);
}}
className={cn(
'h-full max-h-[20rem] w-full max-w-md rounded-xl border border-[#eaeaea] bg-[linear-gradient(110deg,#333_0.6%,#222)] p-4 dark:border-neutral-600',
className,
)}
>
<div className='flex items-center justify-center'>
<Illustration mouseEnter={mouseEnter} />
</div>
<div className='px-2 pb-6'>{children}</div>
</div>
);
};
export const GlowingStarsDescription = ({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) => {
return <p className={cn('max-w-[16rem] text-base text-white', className)}>{children}</p>;
};
export const GlowingStarsTitle = ({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) => {
return <h2 className={cn('text-2xl font-bold text-[#eaeaea]', className)}>{children}</h2>;
};
export const Illustration = ({ mouseEnter }: { mouseEnter: boolean }) => {
const stars = 108;
const columns = 18;
const [glowingStars, setGlowingStars] = useState<number[]>([]);
const highlightedStars = useRef<number[]>([]);
useEffect(() => {
const interval = setInterval(() => {
highlightedStars.current = Array.from({ length: 5 }, () => Math.floor(Math.random() * stars));
setGlowingStars([...highlightedStars.current]);
}, 3000);
return () => clearInterval(interval);
}, []);
return (
<div
className='h-48 w-full p-1'
style={{
display: 'grid',
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gap: `1px`,
}}
>
{[...Array(stars)].map((_, starIdx) => {
const isGlowing = glowingStars.includes(starIdx);
const delay = (starIdx % 10) * 0.1;
const staticDelay = starIdx * 0.01;
return (
<div key={`matrix-col-${starIdx}}`} className='relative flex items-center justify-center'>
<Star
isGlowing={mouseEnter ? true : isGlowing}
delay={mouseEnter ? staticDelay : delay}
/>
{mouseEnter && <Glow delay={staticDelay} />}
<AnimatePresence mode='wait'>{isGlowing && <Glow delay={delay} />}</AnimatePresence>
</div>
);
})}
</div>
);
};
const Star = ({ isGlowing, delay }: { isGlowing: boolean; delay: number }) => {
return (
<motion.div
key={delay}
initial={{
scale: 1,
}}
animate={{
scale: isGlowing ? [1, 1.2, 2.5, 2.2, 1.5] : 1,
background: isGlowing ? '#fff' : '#666',
}}
transition={{
duration: 2,
ease: 'easeInOut',
delay: delay,
}}
className={cn('relative z-20 h-[1px] w-[1px] rounded-full bg-[#666]')}
></motion.div>
);
};
const Glow = ({ delay }: { delay: number }) => {
return (
<motion.div
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
}}
transition={{
duration: 2,
ease: 'easeInOut',
delay: delay,
}}
exit={{
opacity: 0,
}}
className='absolute left-1/2 z-10 h-[4px] w-[4px] -translate-x-1/2 rounded-full bg-blue-500 shadow-2xl shadow-blue-400 blur-[1px]'
/>
);
};

View File

@ -0,0 +1,161 @@
// @ts-nocheck
'use client';
import { motion, MotionValue } from 'framer-motion';
import { cn } from '../../lib/utils';
const transition = {
duration: 0,
ease: 'linear',
};
export const GoogleGeminiEffect = ({
pathLengths,
title,
description,
className,
}: {
pathLengths: MotionValue[];
title?: string;
description?: string;
className?: string;
}) => {
return (
<div className={cn('sticky top-80', className)}>
<p className='bg-gradient-to-b from-neutral-100 to-neutral-300 bg-clip-text pb-4 text-center text-lg font-normal text-transparent md:text-7xl'>
{title || `Build with Aceternity UI`}
</p>
<p className='mx-auto mt-4 max-w-lg text-center text-xs font-normal text-neutral-400 md:text-xl'>
{description ||
`Scroll this component and see the bottom SVG come to life wow this
works!`}
</p>
<div className='bg-red-transparent absolute -top-60 flex h-[890px] w-full items-center justify-center md:-top-40'>
<button className='z-30 mx-auto mt-8 w-fit rounded-full bg-white px-2 py-1 text-xs font-bold text-black md:mt-24 md:px-4 md:py-2 md:text-base'>
ui.aceternity.com
</button>
</div>
<svg
width='1440'
height='890'
viewBox='0 0 1440 890'
xmlns='http://www.w3.org/2000/svg'
className='absolute -top-60 w-full md:-top-40'
>
<motion.path
d='M0 663C145.5 663 191 666.265 269 647C326.5 630 339.5 621 397.5 566C439 531.5 455 529.5 490 523C509.664 519.348 521 503.736 538 504.236C553.591 504.236 562.429 514.739 584.66 522.749C592.042 525.408 600.2 526.237 607.356 523.019C624.755 515.195 641.446 496.324 657 496.735C673.408 496.735 693.545 519.572 712.903 526.769C718.727 528.934 725.184 528.395 730.902 525.965C751.726 517.115 764.085 497.106 782 496.735C794.831 496.47 804.103 508.859 822.469 518.515C835.13 525.171 850.214 526.815 862.827 520.069C875.952 513.049 889.748 502.706 903.5 503.736C922.677 505.171 935.293 510.562 945.817 515.673C954.234 519.76 963.095 522.792 972.199 524.954C996.012 530.611 1007.42 534.118 1034 549C1077.5 573.359 1082.5 594.5 1140 629C1206 670 1328.5 662.5 1440 662.5'
stroke='#FFB7C5'
strokeWidth='2'
fill='none'
initial={{
pathLength: 0,
}}
style={{
pathLength: pathLengths[0],
}}
transition={transition}
/>
<motion.path
d='M0 587.5C147 587.5 277 587.5 310 573.5C348 563 392.5 543.5 408 535C434 523.5 426 526.235 479 515.235C494 512.729 523 510.435 534.5 512.735C554.5 516.735 555.5 523.235 576 523.735C592 523.735 616 496.735 633 497.235C648.671 497.235 661.31 515.052 684.774 524.942C692.004 527.989 700.2 528.738 707.349 525.505C724.886 517.575 741.932 498.33 757.5 498.742C773.864 498.742 791.711 520.623 810.403 527.654C816.218 529.841 822.661 529.246 828.451 526.991C849.246 518.893 861.599 502.112 879.5 501.742C886.47 501.597 896.865 506.047 907.429 510.911C930.879 521.707 957.139 519.639 982.951 520.063C1020.91 520.686 1037.5 530.797 1056.5 537C1102.24 556.627 1116.5 570.704 1180.5 579.235C1257.5 589.5 1279 587 1440 588'
stroke='#FFDDB7'
strokeWidth='2'
fill='none'
initial={{
pathLength: 0,
}}
style={{
pathLength: pathLengths[1],
}}
transition={transition}
/>
<motion.path
d='M0 514C147.5 514.333 294.5 513.735 380.5 513.735C405.976 514.94 422.849 515.228 436.37 515.123C477.503 514.803 518.631 506.605 559.508 511.197C564.04 511.706 569.162 512.524 575 513.735C588 516.433 616 521.702 627.5 519.402C647.5 515.402 659 499.235 680.5 499.235C700.5 499.235 725 529.235 742 528.735C757.654 528.735 768.77 510.583 791.793 500.59C798.991 497.465 807.16 496.777 814.423 499.745C832.335 507.064 850.418 524.648 866 524.235C882.791 524.235 902.316 509.786 921.814 505.392C926.856 504.255 932.097 504.674 937.176 505.631C966.993 511.248 970.679 514.346 989.5 514.735C1006.3 515.083 1036.5 513.235 1055.5 513.235C1114.5 513.235 1090.5 513.235 1124 513.235C1177.5 513.235 1178.99 514.402 1241 514.402C1317.5 514.402 1274.5 512.568 1440 513.235'
stroke='#B1C5FF'
strokeWidth='2'
fill='none'
initial={{
pathLength: 0,
}}
style={{
pathLength: pathLengths[2],
}}
transition={transition}
/>
<motion.path
d='M0 438.5C150.5 438.5 261 438.318 323.5 456.5C351 464.5 387.517 484.001 423.5 494.5C447.371 501.465 472 503.735 487 507.735C503.786 512.212 504.5 516.808 523 518.735C547 521.235 564.814 501.235 584.5 501.235C604.5 501.235 626 529.069 643 528.569C658.676 528.569 672.076 511.63 695.751 501.972C703.017 499.008 711.231 498.208 718.298 501.617C735.448 509.889 751.454 529.98 767 529.569C783.364 529.569 801.211 507.687 819.903 500.657C825.718 498.469 832.141 499.104 837.992 501.194C859.178 508.764 873.089 523.365 891 523.735C907.8 524.083 923 504.235 963 506.735C1034.5 506.735 1047.5 492.68 1071 481.5C1122.5 457 1142.23 452.871 1185 446.5C1255.5 436 1294 439 1439.5 439'
stroke='#4FABFF'
strokeWidth='2'
fill='none'
initial={{
pathLength: 0,
}}
style={{
pathLength: pathLengths[3],
}}
transition={transition}
/>
<motion.path
d='M0.5 364C145.288 362.349 195 361.5 265.5 378C322 391.223 399.182 457.5 411 467.5C424.176 478.649 456.916 491.677 496.259 502.699C498.746 503.396 501.16 504.304 503.511 505.374C517.104 511.558 541.149 520.911 551.5 521.236C571.5 521.236 590 498.736 611.5 498.736C631.5 498.736 652.5 529.236 669.5 528.736C685.171 528.736 697.81 510.924 721.274 501.036C728.505 497.988 736.716 497.231 743.812 500.579C761.362 508.857 778.421 529.148 794 528.736C810.375 528.736 829.35 508.68 848.364 502.179C854.243 500.169 860.624 500.802 866.535 502.718C886.961 509.338 898.141 519.866 916 520.236C932.8 520.583 934.5 510.236 967.5 501.736C1011.5 491 1007.5 493.5 1029.5 480C1069.5 453.5 1072 440.442 1128.5 403.5C1180.5 369.5 1275 360.374 1439 364'
stroke='#076EFF'
strokeWidth='2'
fill='none'
initial={{
pathLength: 0,
}}
style={{
pathLength: pathLengths[4],
}}
transition={transition}
/>
{/* Gaussian blur for the background paths */}
<path
d='M0 663C145.5 663 191 666.265 269 647C326.5 630 339.5 621 397.5 566C439 531.5 455 529.5 490 523C509.664 519.348 521 503.736 538 504.236C553.591 504.236 562.429 514.739 584.66 522.749C592.042 525.408 600.2 526.237 607.356 523.019C624.755 515.195 641.446 496.324 657 496.735C673.408 496.735 693.545 519.572 712.903 526.769C718.727 528.934 725.184 528.395 730.902 525.965C751.726 517.115 764.085 497.106 782 496.735C794.831 496.47 804.103 508.859 822.469 518.515C835.13 525.171 850.214 526.815 862.827 520.069C875.952 513.049 889.748 502.706 903.5 503.736C922.677 505.171 935.293 510.562 945.817 515.673C954.234 519.76 963.095 522.792 972.199 524.954C996.012 530.611 1007.42 534.118 1034 549C1077.5 573.359 1082.5 594.5 1140 629C1206 670 1328.5 662.5 1440 662.5'
stroke='#FFB7C5'
strokeWidth='2'
fill='none'
pathLength={1}
filter='url(#blurMe)'
/>
<path
d='M0 587.5C147 587.5 277 587.5 310 573.5C348 563 392.5 543.5 408 535C434 523.5 426 526.235 479 515.235C494 512.729 523 510.435 534.5 512.735C554.5 516.735 555.5 523.235 576 523.735C592 523.735 616 496.735 633 497.235C648.671 497.235 661.31 515.052 684.774 524.942C692.004 527.989 700.2 528.738 707.349 525.505C724.886 517.575 741.932 498.33 757.5 498.742C773.864 498.742 791.711 520.623 810.403 527.654C816.218 529.841 822.661 529.246 828.451 526.991C849.246 518.893 861.599 502.112 879.5 501.742C886.47 501.597 896.865 506.047 907.429 510.911C930.879 521.707 957.139 519.639 982.951 520.063C1020.91 520.686 1037.5 530.797 1056.5 537C1102.24 556.627 1116.5 570.704 1180.5 579.235C1257.5 589.5 1279 587 1440 588'
stroke='#FFDDB7'
strokeWidth='2'
fill='none'
pathLength={1}
filter='url(#blurMe)'
/>
<path
d='M0 514C147.5 514.333 294.5 513.735 380.5 513.735C405.976 514.94 422.849 515.228 436.37 515.123C477.503 514.803 518.631 506.605 559.508 511.197C564.04 511.706 569.162 512.524 575 513.735C588 516.433 616 521.702 627.5 519.402C647.5 515.402 659 499.235 680.5 499.235C700.5 499.235 725 529.235 742 528.735C757.654 528.735 768.77 510.583 791.793 500.59C798.991 497.465 807.16 496.777 814.423 499.745C832.335 507.064 850.418 524.648 866 524.235C882.791 524.235 902.316 509.786 921.814 505.392C926.856 504.255 932.097 504.674 937.176 505.631C966.993 511.248 970.679 514.346 989.5 514.735C1006.3 515.083 1036.5 513.235 1055.5 513.235C1114.5 513.235 1090.5 513.235 1124 513.235C1177.5 513.235 1178.99 514.402 1241 514.402C1317.5 514.402 1274.5 512.568 1440 513.235'
stroke='#B1C5FF'
strokeWidth='2'
fill='none'
pathLength={1}
filter='url(#blurMe)'
/>
<path
d='M0 438.5C150.5 438.5 261 438.318 323.5 456.5C351 464.5 387.517 484.001 423.5 494.5C447.371 501.465 472 503.735 487 507.735C503.786 512.212 504.5 516.808 523 518.735C547 521.235 564.814 501.235 584.5 501.235C604.5 501.235 626 529.069 643 528.569C658.676 528.569 672.076 511.63 695.751 501.972C703.017 499.008 711.231 498.208 718.298 501.617C735.448 509.889 751.454 529.98 767 529.569C783.364 529.569 801.211 507.687 819.903 500.657C825.718 498.469 832.141 499.104 837.992 501.194C859.178 508.764 873.089 523.365 891 523.735C907.8 524.083 923 504.235 963 506.735C1034.5 506.735 1047.5 492.68 1071 481.5C1122.5 457 1142.23 452.871 1185 446.5C1255.5 436 1294 439 1439.5 439'
stroke='#4FABFF'
strokeWidth='2'
fill='none'
pathLength={1}
filter='url(#blurMe)'
/>
<path
d='M0.5 364C145.288 362.349 195 361.5 265.5 378C322 391.223 399.182 457.5 411 467.5C424.176 478.649 456.916 491.677 496.259 502.699C498.746 503.396 501.16 504.304 503.511 505.374C517.104 511.558 541.149 520.911 551.5 521.236C571.5 521.236 590 498.736 611.5 498.736C631.5 498.736 652.5 529.236 669.5 528.736C685.171 528.736 697.81 510.924 721.274 501.036C728.505 497.988 736.716 497.231 743.812 500.579C761.362 508.857 778.421 529.148 794 528.736C810.375 528.736 829.35 508.68 848.364 502.179C854.243 500.169 860.624 500.802 866.535 502.718C886.961 509.338 898.141 519.866 916 520.236C932.8 520.583 934.5 510.236 967.5 501.736C1011.5 491 1007.5 493.5 1029.5 480C1069.5 453.5 1072 440.442 1128.5 403.5C1180.5 369.5 1275 360.374 1439 364'
stroke='#076EFF'
strokeWidth='2'
fill='none'
pathLength={1}
filter='url(#blurMe)'
/>
<defs>
<filter id='blurMe'>
<feGaussianBlur in='SourceGraphic' stdDeviation='5' />
</filter>
</defs>
</svg>
</div>
);
};

View File

@ -1,44 +0,0 @@
'use client';
import { AnimatePresence, motion, Variants } from 'framer-motion';
import { cn } from '../../lib/utils';
interface GradualSpacingProps {
text: string;
duration?: number;
delayMultiple?: number;
framerProps?: Variants;
className?: string;
}
export default function GradualSpacing({
text,
duration = 0.5,
delayMultiple = 0.04,
framerProps = {
hidden: { opacity: 0, x: -20 },
visible: { opacity: 1, x: 0 },
},
className,
}: GradualSpacingProps) {
return (
<div className='flex justify-center space-x-1'>
<AnimatePresence>
{text.split('').map((char, i) => (
<motion.h1
key={i}
initial='hidden'
animate='visible'
exit='hidden'
variants={framerProps}
transition={{ duration, delay: i * delayMultiple }}
className={cn('drop-shadow-sm', className)}
>
{char === ' ' ? <span>&nbsp;</span> : char}
</motion.h1>
))}
</AnimatePresence>
</div>
);
}

View File

@ -1,61 +0,0 @@
import { useId } from 'react';
import { cn } from '../../lib/utils';
interface GridPatternProps {
width?: number;
height?: number;
x?: number;
y?: number;
squares?: Array<[x: number, y: number]>;
strokeDasharray?: string;
className?: string;
[key: string]: unknown;
}
export function GridPattern({
width = 40,
height = 40,
x = -1,
y = -1,
strokeDasharray = '0',
squares,
className,
...props
}: GridPatternProps) {
const id = useId();
return (
<svg
aria-hidden='true'
className={cn(
'pointer-events-none absolute inset-0 h-full w-full fill-gray-400/30 stroke-gray-400/30',
className,
)}
{...props}
>
<defs>
<pattern id={id} width={width} height={height} patternUnits='userSpaceOnUse' x={x} y={y}>
<path d={`M.5 ${height}V.5H${width}`} fill='none' strokeDasharray={strokeDasharray} />
</pattern>
</defs>
<rect width='100%' height='100%' strokeWidth={0} fill={`url(#${id})`} />
{squares && (
<svg x={x} y={y} className='overflow-visible'>
{squares.map(([x, y]) => (
<rect
strokeWidth='0'
key={`${x}-${y}`}
width={width - 1}
height={height - 1}
x={x * width + 1}
y={y * height + 1}
/>
))}
</svg>
)}
</svg>
);
}
export default GridPattern;

View File

@ -0,0 +1,13 @@
const GridBackgroundPreview = () => {
return (
<div className='bg-grid-white/[0.1] relative flex h-[50rem] w-full items-center justify-center bg-black'>
{/* Radial gradient for the container to give a faded look */}
<div className='pointer-events-none absolute inset-0 flex items-center justify-center bg-black [mask-image:radial-gradient(ellipse_at_center,transparent_20%,black)]'></div>
<p className='relative z-20 bg-gradient-to-b from-neutral-200 to-neutral-500 bg-clip-text py-8 text-4xl font-bold text-transparent sm:text-7xl'>
Backgrounds
</p>
</div>
);
};
export default GridBackgroundPreview;

View File

@ -0,0 +1,93 @@
// @ts-nocheck
'use client';
import { motion, useMotionTemplate, useMotionValue } from 'framer-motion';
import React from 'react';
import { cn } from '../../lib/utils';
export const HeroHighlight = ({
children,
className,
containerClassName,
}: {
children: React.ReactNode;
className?: string;
containerClassName?: string;
}) => {
let mouseX = useMotionValue(0);
let mouseY = useMotionValue(0);
function handleMouseMove({ currentTarget, clientX, clientY }: React.MouseEvent<HTMLDivElement>) {
if (!currentTarget) return;
let { left, top } = currentTarget.getBoundingClientRect();
mouseX.set(clientX - left);
mouseY.set(clientY - top);
}
return (
<div
className={cn(
'group relative flex h-[40rem] w-full items-center justify-center bg-white dark:bg-black',
containerClassName,
)}
onMouseMove={handleMouseMove}
>
<div className='bg-dot-thick-neutral-300 dark:bg-dot-thick-neutral-800 pointer-events-none absolute inset-0' />
<motion.div
className='bg-dot-thick-indigo-500 dark:bg-dot-thick-indigo-500 pointer-events-none absolute inset-0 opacity-0 transition duration-300 group-hover:opacity-100'
style={{
WebkitMaskImage: useMotionTemplate`
radial-gradient(
200px circle at ${mouseX}px ${mouseY}px,
black 0%,
transparent 100%
)
`,
maskImage: useMotionTemplate`
radial-gradient(
200px circle at ${mouseX}px ${mouseY}px,
black 0%,
transparent 100%
)
`,
}}
/>
<div className={cn('relative z-20', className)}>{children}</div>
</div>
);
};
export const Highlight = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
return (
<motion.span
initial={{
backgroundSize: '0% 100%',
}}
animate={{
backgroundSize: '100% 100%',
}}
transition={{
duration: 2,
ease: 'linear',
delay: 0.5,
}}
style={{
backgroundRepeat: 'no-repeat',
backgroundPosition: 'left center',
display: 'inline',
}}
className={cn(
`relative inline-block rounded-lg bg-gradient-to-r from-indigo-300 to-purple-300 px-1 pb-1 dark:from-indigo-500 dark:to-purple-500`,
className,
)}
>
{children}
</motion.span>
);
};

View File

@ -0,0 +1,122 @@
'use client';
import { motion, MotionValue, useScroll, useSpring, useTransform } from 'framer-motion';
import Image from 'next/image';
import Link from 'next/link';
import React from 'react';
export const HeroParallax = ({
products,
}: {
products: {
title: string;
link: string;
thumbnail: string;
}[];
}) => {
const firstRow = products.slice(0, 5);
const secondRow = products.slice(5, 10);
const thirdRow = products.slice(10, 15);
const ref = React.useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ['start start', 'end start'],
});
const springConfig = { stiffness: 300, damping: 30, bounce: 100 };
const translateX = useSpring(useTransform(scrollYProgress, [0, 1], [0, 1000]), springConfig);
const translateXReverse = useSpring(
useTransform(scrollYProgress, [0, 1], [0, -1000]),
springConfig,
);
const rotateX = useSpring(useTransform(scrollYProgress, [0, 0.2], [15, 0]), springConfig);
const opacity = useSpring(useTransform(scrollYProgress, [0, 0.2], [0.2, 1]), springConfig);
const rotateZ = useSpring(useTransform(scrollYProgress, [0, 0.2], [20, 0]), springConfig);
const translateY = useSpring(useTransform(scrollYProgress, [0, 0.2], [-700, 500]), springConfig);
return (
<div
ref={ref}
className='relative flex h-[300vh] flex-col self-auto overflow-hidden py-40 antialiased [perspective:1000px] [transform-style:preserve-3d]'
>
<Header />
<motion.div
style={{
rotateX,
rotateZ,
translateY,
opacity,
}}
className=''
>
<motion.div className='mb-20 flex flex-row-reverse space-x-20 space-x-reverse'>
{firstRow.map((product) => (
<ProductCard product={product} translate={translateX} key={product.title} />
))}
</motion.div>
<motion.div className='mb-20 flex flex-row space-x-20'>
{secondRow.map((product) => (
<ProductCard product={product} translate={translateXReverse} key={product.title} />
))}
</motion.div>
<motion.div className='flex flex-row-reverse space-x-20 space-x-reverse'>
{thirdRow.map((product) => (
<ProductCard product={product} translate={translateX} key={product.title} />
))}
</motion.div>
</motion.div>
</div>
);
};
export const Header = () => {
return (
<div className='relative left-0 top-0 mx-auto w-full max-w-7xl px-4 py-20 md:py-40'>
<h1 className='text-2xl font-bold md:text-7xl dark:text-white'>
The Ultimate <br /> development studio
</h1>
<p className='mt-8 max-w-2xl text-base md:text-xl dark:text-neutral-200'>
We build beautiful products with the latest technologies and frameworks. We are a team of
passionate developers and designers that love to build amazing products.
</p>
</div>
);
};
export const ProductCard = ({
product,
translate,
}: {
product: {
title: string;
link: string;
thumbnail: string;
};
translate: MotionValue<number>;
}) => {
return (
<motion.div
style={{
x: translate,
}}
whileHover={{
y: -20,
}}
key={product.title}
className='group/product relative h-96 w-[30rem] flex-shrink-0'
>
<Link href={product.link} className='block group-hover/product:shadow-2xl'>
<Image
src={product.thumbnail}
height='600'
width='600'
className='absolute inset-0 h-full w-full object-cover object-left-top'
alt={product.title}
/>
</Link>
<div className='pointer-events-none absolute inset-0 h-full w-full bg-black opacity-0 group-hover/product:opacity-80'></div>
<h2 className='absolute bottom-4 left-4 text-white opacity-0 group-hover/product:opacity-100'>
{product.title}
</h2>
</motion.div>
);
};

View File

@ -1,137 +0,0 @@
'use client';
import { AnimatePresence, motion } from 'framer-motion';
import { Play, XIcon } from 'lucide-react';
import { useState } from 'react';
import { cn } from '../../lib/utils';
type AnimationStyle =
| 'from-bottom'
| 'from-center'
| 'from-top'
| 'from-left'
| 'from-right'
| 'fade'
| 'top-in-bottom-out'
| 'left-in-right-out';
interface HeroVideoProps {
animationStyle?: AnimationStyle;
videoSrc: string;
thumbnailSrc: string;
thumbnailAlt?: string;
className?: string;
}
const animationVariants = {
'from-bottom': {
initial: { y: '100%', opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: '100%', opacity: 0 },
},
'from-center': {
initial: { scale: 0.5, opacity: 0 },
animate: { scale: 1, opacity: 1 },
exit: { scale: 0.5, opacity: 0 },
},
'from-top': {
initial: { y: '-100%', opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: '-100%', opacity: 0 },
},
'from-left': {
initial: { x: '-100%', opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: '-100%', opacity: 0 },
},
'from-right': {
initial: { x: '100%', opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: '100%', opacity: 0 },
},
'fade': {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
},
'top-in-bottom-out': {
initial: { y: '-100%', opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: '100%', opacity: 0 },
},
'left-in-right-out': {
initial: { x: '-100%', opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: '100%', opacity: 0 },
},
};
export default function HeroVideoDialog({
animationStyle = 'from-center',
videoSrc,
thumbnailSrc,
thumbnailAlt = 'Video thumbnail',
className,
}: HeroVideoProps) {
const [isVideoOpen, setIsVideoOpen] = useState(false);
const selectedAnimation = animationVariants[animationStyle];
return (
<div className={cn('relative', className)}>
<div className='group relative cursor-pointer' onClick={() => setIsVideoOpen(true)}>
<img
src={thumbnailSrc}
alt={thumbnailAlt}
width={1920}
height={1080}
className='w-full rounded-md border shadow-lg transition-all duration-200 ease-out group-hover:brightness-[0.8]'
/>
<div className='absolute inset-0 flex scale-[0.9] items-center justify-center rounded-2xl transition-all duration-200 ease-out group-hover:scale-100'>
<div className='bg-primary/10 flex size-28 items-center justify-center rounded-full backdrop-blur-md'>
<div
className={`from-primary/30 to-primary relative flex size-20 scale-100 items-center justify-center rounded-full bg-gradient-to-b shadow-md transition-all duration-200 ease-out group-hover:scale-[1.2]`}
>
<Play
className='size-8 scale-100 fill-white text-white transition-transform duration-200 ease-out group-hover:scale-105'
style={{
filter:
'drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06))',
}}
/>
</div>
</div>
</div>
</div>
<AnimatePresence>
{isVideoOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
onClick={() => setIsVideoOpen(false)}
exit={{ opacity: 0 }}
className='fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-md'
>
<motion.div
{...selectedAnimation}
transition={{ type: 'spring', damping: 30, stiffness: 300 }}
className='relative mx-4 aspect-video w-full max-w-4xl md:mx-0'
>
<motion.button className='absolute -top-16 right-0 rounded-full bg-neutral-900/50 p-2 text-xl text-white ring-1 backdrop-blur-md dark:bg-neutral-100/50 dark:text-black'>
<XIcon className='size-5' />
</motion.button>
<div className='relative isolate z-[1] size-full overflow-hidden rounded-2xl border-2 border-white'>
<iframe
src={videoSrc}
className='size-full rounded-2xl'
allowFullScreen
allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'
></iframe>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}

View File

@ -0,0 +1,91 @@
// @ts-nocheck
'use client';
import React, { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
import { cn } from '../../lib/utils';
type Direction = 'TOP' | 'LEFT' | 'BOTTOM' | 'RIGHT';
export function HoverBorderGradient({
children,
containerClassName,
className,
as: Tag = 'button',
duration = 1,
clockwise = true,
...props
}: React.PropsWithChildren<
{
as?: React.ElementType;
containerClassName?: string;
className?: string;
duration?: number;
clockwise?: boolean;
} & React.HTMLAttributes<HTMLElement>
>) {
const [hovered, setHovered] = useState<boolean>(false);
const [direction, setDirection] = useState<Direction>('TOP');
const rotateDirection = (currentDirection: Direction): Direction => {
const directions: Direction[] = ['TOP', 'LEFT', 'BOTTOM', 'RIGHT'];
const currentIndex = directions.indexOf(currentDirection);
const nextIndex = clockwise
? (currentIndex - 1 + directions.length) % directions.length
: (currentIndex + 1) % directions.length;
return directions[nextIndex];
};
const movingMap: Record<Direction, string> = {
TOP: 'radial-gradient(20.7% 50% at 50% 0%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)',
LEFT: 'radial-gradient(16.6% 43.1% at 0% 50%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)',
BOTTOM:
'radial-gradient(20.7% 50% at 50% 100%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)',
RIGHT:
'radial-gradient(16.2% 41.199999999999996% at 100% 50%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)',
};
const highlight =
'radial-gradient(75% 181.15942028985506% at 50% 50%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)';
useEffect(() => {
if (!hovered) {
const interval = setInterval(() => {
setDirection((prevState) => rotateDirection(prevState));
}, duration * 1000);
return () => clearInterval(interval);
}
}, [hovered]);
return (
<Tag
onMouseEnter={(event: React.MouseEvent<HTMLDivElement>) => {
setHovered(true);
}}
onMouseLeave={() => setHovered(false)}
className={cn(
'relative flex h-min w-fit flex-col flex-nowrap content-center items-center justify-center gap-10 overflow-visible rounded-full border bg-black/20 decoration-clone p-px transition duration-500 hover:bg-black/10 dark:bg-white/20',
containerClassName,
)}
{...props}
>
<div className={cn('z-10 w-auto rounded-[inherit] bg-black px-4 py-2 text-white', className)}>
{children}
</div>
<motion.div
className={cn('absolute inset-0 z-0 flex-none overflow-hidden rounded-[inherit]')}
style={{
filter: 'blur(2px)',
position: 'absolute',
width: '100%',
height: '100%',
}}
initial={{ background: movingMap[direction] }}
animate={{
background: hovered ? [movingMap[direction], highlight] : movingMap[direction],
}}
transition={{ ease: 'linear', duration: duration ?? 1 }}
/>
<div className='z-1 absolute inset-[2px] flex-none rounded-[100px] bg-black' />
</Tag>
);
}

View File

@ -1,3 +1,4 @@
// @ts-nocheck
'use client'; 'use client';
import * as HoverCardPrimitive from '@radix-ui/react-hover-card'; import * as HoverCardPrimitive from '@radix-ui/react-hover-card';

Some files were not shown because too many files have changed in this diff Show More