Refactor Prettier configuration to adhere to code style guidelines
This commit is contained in:
@@ -1,57 +1,57 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion'
|
||||
import { ChevronDownIcon } from '@radix-ui/react-icons'
|
||||
import * as React from "react";
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||
import { ChevronDownIcon } from "@radix-ui/react-icons";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const Accordion = AccordionPrimitive.Root
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
|
||||
const AccordionItem = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AccordionPrimitive.Item
|
||||
ref={ref}
|
||||
className={ny('border-b', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AccordionItem.displayName = 'AccordionItem'
|
||||
<AccordionPrimitive.Item
|
||||
ref={ref}
|
||||
className={ny("border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AccordionItem.displayName = "AccordionItem";
|
||||
|
||||
const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-muted-foreground size-4 shrink-0 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
))
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="size-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||
|
||||
const AccordionContent = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
|
||||
{...props}
|
||||
>
|
||||
<div className={ny('pb-4 pt-0', className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
))
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
{...props}
|
||||
>
|
||||
<div className={ny("pb-4 pt-0", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import type { ReactNode } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
export default function AnimatedGradientText({
|
||||
children,
|
||||
className,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
'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="absolute inset-0 block h-full w-full animate-gradient bg-gradient-to-r from-[#ffaa40]/50 via-[#9c40ff]/50 to-[#ffaa40]/50 bg-[length:var(--bg-size)_100%] p-[1px] ![mask-composite:subtract] [border-radius:inherit] [mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]" />
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
"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>
|
||||
)
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,146 +1,144 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { useEffect, useId, useRef, useState } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import { motion } from "framer-motion";
|
||||
import { useEffect, useId, useRef, useState } from "react";
|
||||
import { ny } 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
|
||||
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
|
||||
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))
|
||||
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),
|
||||
]
|
||||
}
|
||||
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(),
|
||||
}))
|
||||
}
|
||||
// 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,
|
||||
),
|
||||
)
|
||||
}
|
||||
// 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])
|
||||
// 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 (const entry of entries) {
|
||||
setDimensions({
|
||||
width: entry.contentRect.width,
|
||||
height: entry.contentRect.height,
|
||||
})
|
||||
}
|
||||
})
|
||||
// Resize observer to update container dimensions
|
||||
useEffect(() => {
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
setDimensions({
|
||||
width: entry.contentRect.width,
|
||||
height: entry.contentRect.height,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (containerRef.current)
|
||||
resizeObserver.observe(containerRef.current)
|
||||
if (containerRef.current) resizeObserver.observe(containerRef.current);
|
||||
|
||||
return () => {
|
||||
if (containerRef.current)
|
||||
resizeObserver.unobserve(containerRef.current)
|
||||
}
|
||||
}, [containerRef])
|
||||
return () => {
|
||||
if (containerRef.current) resizeObserver.unobserve(containerRef.current);
|
||||
};
|
||||
}, [containerRef]);
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={containerRef}
|
||||
aria-hidden="true"
|
||||
className={ny(
|
||||
'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>
|
||||
)
|
||||
return (
|
||||
<svg
|
||||
ref={containerRef}
|
||||
aria-hidden="true"
|
||||
className={ny(
|
||||
"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
|
||||
export default GridPattern;
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import type { CSSProperties, FC, ReactNode } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import type { CSSProperties, FC, ReactNode } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface AnimatedShinyTextProps {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
shimmerWidth?: number
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
shimmerWidth?: number;
|
||||
}
|
||||
|
||||
const AnimatedShinyText: FC<AnimatedShinyTextProps> = ({
|
||||
children,
|
||||
className,
|
||||
shimmerWidth = 100,
|
||||
children,
|
||||
className,
|
||||
shimmerWidth = 100,
|
||||
}) => {
|
||||
return (
|
||||
<p
|
||||
style={
|
||||
{
|
||||
'--shimmer-width': `${shimmerWidth}px`,
|
||||
} as CSSProperties
|
||||
}
|
||||
className={ny(
|
||||
'mx-auto max-w-md text-neutral-600/50 dark:text-neutral-400/50 ',
|
||||
return (
|
||||
<p
|
||||
style={
|
||||
{
|
||||
"--shimmer-width": `${shimmerWidth}px`,
|
||||
} as CSSProperties
|
||||
}
|
||||
className={ny(
|
||||
"mx-auto max-w-md text-neutral-600/50 dark:text-neutral-400/50",
|
||||
|
||||
// Shimmer effect
|
||||
'animate-shimmer bg-clip-text bg-no-repeat [background-position:0_0] [background-size:var(--shimmer-width)_100%] [transition:background-position_1s_cubic-bezier(.6,.6,0,1)_infinite]',
|
||||
// Shimmer effect
|
||||
"animate-shimmer bg-clip-text bg-no-repeat [background-position:0_0] [background-size:var(--shimmer-width)_100%] [transition:background-position_1s_cubic-bezier(.6,.6,0,1)_infinite]",
|
||||
|
||||
// Shimmer gradient
|
||||
'bg-gradient-to-r from-transparent via-black/80 via-50% to-transparent dark:via-white/80',
|
||||
// Shimmer gradient
|
||||
"bg-gradient-to-r from-transparent via-black/80 via-50% to-transparent dark:via-white/80",
|
||||
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnimatedShinyText
|
||||
export default AnimatedShinyText;
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { useRef } from 'react'
|
||||
import type { Variants } from 'framer-motion'
|
||||
import { AnimatePresence, motion, useInView } from 'framer-motion'
|
||||
import { useRef } from "react";
|
||||
import type { Variants } from "framer-motion";
|
||||
import { AnimatePresence, motion, useInView } from "framer-motion";
|
||||
|
||||
interface BlurFadeProps {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
variant?: {
|
||||
hidden: { y: number }
|
||||
visible: { y: number }
|
||||
}
|
||||
duration?: number
|
||||
delay?: number
|
||||
yOffset?: number
|
||||
inView?: boolean
|
||||
inViewMargin?: any
|
||||
blur?: string
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
variant?: {
|
||||
hidden: { y: number };
|
||||
visible: { y: number };
|
||||
};
|
||||
duration?: number;
|
||||
delay?: number;
|
||||
yOffset?: number;
|
||||
inView?: boolean;
|
||||
inViewMargin?: any;
|
||||
blur?: string;
|
||||
}
|
||||
|
||||
export default function BlurFade({
|
||||
children,
|
||||
className,
|
||||
variant,
|
||||
duration = 0.4,
|
||||
delay = 0,
|
||||
yOffset = 6,
|
||||
inView = false,
|
||||
inViewMargin = '-50px',
|
||||
blur = '6px',
|
||||
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>
|
||||
)
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
'use client'
|
||||
import { motion } from 'framer-motion'
|
||||
import { ny } from '@/lib/utils'
|
||||
"use client";
|
||||
import { motion } from "framer-motion";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface BlurIntProps {
|
||||
word: string
|
||||
className?: string
|
||||
variant?: {
|
||||
hidden: { filter: string, opacity: number }
|
||||
visible: { filter: string, opacity: number }
|
||||
}
|
||||
duration?: number
|
||||
word: string;
|
||||
className?: string;
|
||||
variant?: {
|
||||
hidden: { filter: string; opacity: number };
|
||||
visible: { filter: string; opacity: number };
|
||||
};
|
||||
duration?: number;
|
||||
}
|
||||
function 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
|
||||
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={ny(
|
||||
className,
|
||||
'font-display text-center text-4xl font-bold tracking-[-0.02em] drop-shadow-sm md:text-7xl md:leading-[5rem]',
|
||||
)}
|
||||
>
|
||||
{word}
|
||||
</motion.h1>
|
||||
)
|
||||
return (
|
||||
<motion.h1
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
transition={{ duration }}
|
||||
variants={combinedVariants}
|
||||
className={ny(
|
||||
className,
|
||||
"font-display text-center text-4xl font-bold tracking-[-0.02em] drop-shadow-sm md:text-7xl md:leading-[5rem]",
|
||||
)}
|
||||
>
|
||||
{word}
|
||||
</motion.h1>
|
||||
);
|
||||
}
|
||||
|
||||
export default BlurIn
|
||||
export default BlurIn;
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface BorderBeamProps {
|
||||
className?: string
|
||||
size?: number
|
||||
duration?: number
|
||||
borderWidth?: number
|
||||
anchor?: number
|
||||
colorFrom?: string
|
||||
colorTo?: string
|
||||
delay?: number
|
||||
className?: string;
|
||||
size?: number;
|
||||
duration?: number;
|
||||
borderWidth?: number;
|
||||
anchor?: number;
|
||||
colorFrom?: string;
|
||||
colorTo?: string;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
export function BorderBeam({
|
||||
className,
|
||||
size = 200,
|
||||
duration = 15,
|
||||
anchor = 90,
|
||||
borderWidth = 1.5,
|
||||
colorFrom = '#ffaa40',
|
||||
colorTo = '#9c40ff',
|
||||
delay = 0,
|
||||
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={ny(
|
||||
'pointer-events-none absolute inset-0 rounded-[inherit] [border:calc(var(--border-width)*1px)_solid_transparent]',
|
||||
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={ny(
|
||||
"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)]',
|
||||
// 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,
|
||||
)}
|
||||
/>
|
||||
)
|
||||
// pseudo styles
|
||||
"after:absolute after:aspect-square after:w-[calc(var(--size)*1px)] after:animate-border-beam 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,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,52 +5,52 @@ import { type VariantProps, cva } from "class-variance-authority";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-full text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground ",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-12 px-8 py-4",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-full text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground ",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-12 px-8 py-4",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
return (
|
||||
<Comp
|
||||
className={ny(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
return (
|
||||
<Comp
|
||||
className={ny(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
Button.displayName = "Button";
|
||||
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
|
||||
import { CheckIcon } from '@radix-ui/react-icons'
|
||||
import * as React from "react";
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
||||
import { CheckIcon } from "@radix-ui/react-icons";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={ny('flex items-center justify-center text-current')}
|
||||
>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
))
|
||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={ny("flex items-center justify-center text-current")}
|
||||
>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
));
|
||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
||||
|
||||
export { Checkbox }
|
||||
export { Checkbox };
|
||||
|
||||
@@ -1,119 +1,124 @@
|
||||
'use client'
|
||||
import confetti from 'canvas-confetti'
|
||||
import type { ReactNode } from 'react'
|
||||
import React, { createContext, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react'
|
||||
"use client";
|
||||
import confetti from "canvas-confetti";
|
||||
import type { ReactNode } from "react";
|
||||
import React, {
|
||||
createContext,
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from "react";
|
||||
|
||||
import type {
|
||||
GlobalOptions as ConfettiGlobalOptions,
|
||||
CreateTypes as ConfettiInstance,
|
||||
Options as ConfettiOptions,
|
||||
} from 'canvas-confetti'
|
||||
import { Button, ButtonProps } from './button'
|
||||
GlobalOptions as ConfettiGlobalOptions,
|
||||
CreateTypes as ConfettiInstance,
|
||||
Options as ConfettiOptions,
|
||||
} from "canvas-confetti";
|
||||
import { Button, ButtonProps } from "./button";
|
||||
|
||||
interface Api {
|
||||
fire: (options?: ConfettiOptions) => void
|
||||
fire: (options?: ConfettiOptions) => void;
|
||||
}
|
||||
|
||||
type Props = React.ComponentPropsWithRef<'canvas'> & {
|
||||
options?: ConfettiOptions
|
||||
globalOptions?: ConfettiGlobalOptions
|
||||
manualstart?: boolean
|
||||
children?: ReactNode
|
||||
}
|
||||
type Props = React.ComponentPropsWithRef<"canvas"> & {
|
||||
options?: ConfettiOptions;
|
||||
globalOptions?: ConfettiGlobalOptions;
|
||||
manualstart?: boolean;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export type ConfettiRef = Api | null
|
||||
export type ConfettiRef = Api | null;
|
||||
|
||||
const ConfettiContext = createContext<Api>({} as Api)
|
||||
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 {
|
||||
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],
|
||||
)
|
||||
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],
|
||||
)
|
||||
// `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],
|
||||
)
|
||||
const api = useMemo(
|
||||
() => ({
|
||||
fire,
|
||||
}),
|
||||
[fire],
|
||||
);
|
||||
|
||||
useImperativeHandle(ref, () => api, [api])
|
||||
useImperativeHandle(ref, () => api, [api]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!manualstart)
|
||||
fire()
|
||||
}, [manualstart, fire])
|
||||
useEffect(() => {
|
||||
if (!manualstart) fire();
|
||||
}, [manualstart, fire]);
|
||||
|
||||
return (
|
||||
<ConfettiContext.Provider value={api}>
|
||||
<canvas ref={canvasRef} {...rest} />
|
||||
{children}
|
||||
</ConfettiContext.Provider>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<ConfettiContext.Provider value={api}>
|
||||
<canvas ref={canvasRef} {...rest} />
|
||||
{children}
|
||||
</ConfettiContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
interface ConfettiButtonProps extends ButtonProps {
|
||||
options?: ConfettiOptions &
|
||||
ConfettiGlobalOptions & { canvas?: HTMLCanvasElement }
|
||||
children?: React.ReactNode
|
||||
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,
|
||||
},
|
||||
})
|
||||
}
|
||||
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>
|
||||
)
|
||||
return (
|
||||
<Button onClick={handleClick} {...props}>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export { Confetti, ConfettiButton }
|
||||
export { Confetti, ConfettiButton };
|
||||
|
||||
export default Confetti
|
||||
export default Confetti;
|
||||
|
||||
@@ -1,239 +1,234 @@
|
||||
'use client'
|
||||
import type { ReactNode } from 'react'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
"use client";
|
||||
import type { ReactNode } from "react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
|
||||
export interface BaseParticle {
|
||||
element: HTMLElement | SVGSVGElement
|
||||
left: number
|
||||
size: number
|
||||
top: number
|
||||
element: HTMLElement | SVGSVGElement;
|
||||
left: number;
|
||||
size: number;
|
||||
top: number;
|
||||
}
|
||||
|
||||
export interface BaseParticleOptions {
|
||||
particle?: string
|
||||
size?: number
|
||||
particle?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface CoolParticle extends BaseParticle {
|
||||
direction: number
|
||||
speedHorz: number
|
||||
speedUp: number
|
||||
spinSpeed: number
|
||||
spinVal: number
|
||||
direction: number;
|
||||
speedHorz: number;
|
||||
speedUp: number;
|
||||
spinSpeed: number;
|
||||
spinVal: number;
|
||||
}
|
||||
|
||||
export interface CoolParticleOptions extends BaseParticleOptions {
|
||||
particleCount?: number
|
||||
speedHorz?: number
|
||||
speedUp?: number
|
||||
particleCount?: number;
|
||||
speedHorz?: number;
|
||||
speedUp?: number;
|
||||
}
|
||||
|
||||
function getContainer() {
|
||||
const id = '_coolMode_effect'
|
||||
const existingContainer = document.getElementById(id)
|
||||
const id = "_coolMode_effect";
|
||||
const existingContainer = document.getElementById(id);
|
||||
|
||||
if (existingContainer)
|
||||
return existingContainer
|
||||
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',
|
||||
)
|
||||
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)
|
||||
document.body.appendChild(container);
|
||||
|
||||
return container
|
||||
return container;
|
||||
}
|
||||
|
||||
let instanceCounter = 0
|
||||
let instanceCounter = 0;
|
||||
|
||||
function applyParticleEffect(
|
||||
element: HTMLElement,
|
||||
options?: CoolParticleOptions,
|
||||
element: HTMLElement,
|
||||
options?: CoolParticleOptions,
|
||||
): () => void {
|
||||
instanceCounter++
|
||||
instanceCounter++;
|
||||
|
||||
const defaultParticle = 'circle'
|
||||
const particleType = options?.particle || defaultParticle
|
||||
const sizes = [15, 20, 25, 35, 45]
|
||||
const limit = 45
|
||||
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
|
||||
let particles: CoolParticle[] = [];
|
||||
let autoAddParticle = false;
|
||||
let mouseX = 0;
|
||||
let mouseY = 0;
|
||||
|
||||
const container = getContainer()
|
||||
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
|
||||
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')
|
||||
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%)`,
|
||||
)
|
||||
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())
|
||||
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.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)`
|
||||
particle.style.position = "absolute";
|
||||
particle.style.transform = `translate3d(${left}px, ${top}px, 0px) rotate(${spinVal}deg)`;
|
||||
|
||||
container.appendChild(particle)
|
||||
container.appendChild(particle);
|
||||
|
||||
particles.push({
|
||||
direction,
|
||||
element: particle,
|
||||
left,
|
||||
size,
|
||||
speedHorz,
|
||||
speedUp,
|
||||
spinSpeed,
|
||||
spinVal,
|
||||
top,
|
||||
})
|
||||
}
|
||||
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
|
||||
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()
|
||||
}
|
||||
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(';'),
|
||||
)
|
||||
})
|
||||
}
|
||||
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 animationFrame: number | undefined;
|
||||
|
||||
let lastParticleTimestamp = 0
|
||||
const particleGenerationDelay = 30
|
||||
let lastParticleTimestamp = 0;
|
||||
const particleGenerationDelay = 30;
|
||||
|
||||
function loop() {
|
||||
const currentTime = performance.now()
|
||||
if (
|
||||
autoAddParticle
|
||||
&& particles.length < limit
|
||||
&& currentTime - lastParticleTimestamp > particleGenerationDelay
|
||||
) {
|
||||
generateParticle()
|
||||
lastParticleTimestamp = currentTime
|
||||
}
|
||||
function loop() {
|
||||
const currentTime = performance.now();
|
||||
if (
|
||||
autoAddParticle &&
|
||||
particles.length < limit &&
|
||||
currentTime - lastParticleTimestamp > particleGenerationDelay
|
||||
) {
|
||||
generateParticle();
|
||||
lastParticleTimestamp = currentTime;
|
||||
}
|
||||
|
||||
refreshParticles()
|
||||
animationFrame = requestAnimationFrame(loop)
|
||||
}
|
||||
refreshParticles();
|
||||
animationFrame = requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
loop()
|
||||
loop();
|
||||
|
||||
const isTouchInteraction = 'ontouchstart' in window
|
||||
const isTouchInteraction = "ontouchstart" in window;
|
||||
|
||||
const tap = isTouchInteraction ? 'touchstart' : 'mousedown'
|
||||
const tapEnd = isTouchInteraction ? 'touchend' : 'mouseup'
|
||||
const move = isTouchInteraction ? 'touchmove' : 'mousemove'
|
||||
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 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 tapHandler = (e: MouseEvent | TouchEvent) => {
|
||||
updateMousePosition(e);
|
||||
autoAddParticle = true;
|
||||
};
|
||||
|
||||
const disableAutoAddParticle = () => {
|
||||
autoAddParticle = false
|
||||
}
|
||||
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,
|
||||
})
|
||||
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)
|
||||
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)
|
||||
const interval = setInterval(() => {
|
||||
if (animationFrame && particles.length === 0) {
|
||||
cancelAnimationFrame(animationFrame);
|
||||
clearInterval(interval);
|
||||
|
||||
if (--instanceCounter === 0)
|
||||
container.remove()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
if (--instanceCounter === 0) container.remove();
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
}
|
||||
|
||||
interface CoolModeProps {
|
||||
children: ReactNode
|
||||
options?: CoolParticleOptions
|
||||
children: ReactNode;
|
||||
options?: CoolParticleOptions;
|
||||
}
|
||||
|
||||
export const CoolMode: React.FC<CoolModeProps> = ({ children, options }) => {
|
||||
const ref = useRef<HTMLElement>(null)
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current)
|
||||
return applyParticleEffect(ref.current, options)
|
||||
}, [options])
|
||||
useEffect(() => {
|
||||
if (ref.current) return applyParticleEffect(ref.current, options);
|
||||
}, [options]);
|
||||
|
||||
return React.cloneElement(children as React.ReactElement, { ref })
|
||||
}
|
||||
return React.cloneElement(children as React.ReactElement, { ref });
|
||||
};
|
||||
|
||||
@@ -1,126 +1,126 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
||||
import { Cross2Icon } from '@radix-ui/react-icons'
|
||||
import * as React from "react";
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const Dialog = DialogPrimitive.Root
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
const DialogTrigger = DialogPrimitive.Trigger
|
||||
const DialogTrigger = DialogPrimitive.Trigger;
|
||||
|
||||
const DialogPortal = DialogPrimitive.Portal
|
||||
const DialogPortal = DialogPrimitive.Portal;
|
||||
|
||||
const DialogClose = DialogPrimitive.Close
|
||||
const DialogClose = DialogPrimitive.Close;
|
||||
|
||||
const DialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
|
||||
<Cross2Icon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
))
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<Cross2Icon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
));
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||
|
||||
function DialogHeader({
|
||||
className,
|
||||
...props
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
'flex flex-col space-y-1.5 text-center sm:text-left',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
DialogHeader.displayName = 'DialogHeader'
|
||||
DialogHeader.displayName = "DialogHeader";
|
||||
|
||||
function DialogFooter({
|
||||
className,
|
||||
...props
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
DialogFooter.displayName = 'DialogFooter'
|
||||
DialogFooter.displayName = "DialogFooter";
|
||||
|
||||
const DialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'text-lg font-semibold leading-none tracking-tight',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
||||
|
||||
const DialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={ny('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={ny("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogTrigger,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
}
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogTrigger,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
};
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
import { useId } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import { useId } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface DotPatternProps {
|
||||
width?: any
|
||||
height?: any
|
||||
x?: any
|
||||
y?: any
|
||||
cx?: any
|
||||
cy?: any
|
||||
cr?: any
|
||||
className?: string
|
||||
[key: string]: any
|
||||
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
|
||||
width = 16,
|
||||
height = 16,
|
||||
x = 0,
|
||||
y = 0,
|
||||
cx = 1,
|
||||
cy = 1,
|
||||
cr = 1,
|
||||
className,
|
||||
...props
|
||||
}: DotPatternProps) {
|
||||
const id = useId()
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className={ny(
|
||||
'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>
|
||||
)
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className={ny(
|
||||
"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
|
||||
export default DotPattern;
|
||||
|
||||
@@ -1,205 +1,205 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
|
||||
import * as React from "react";
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||
import {
|
||||
CheckIcon,
|
||||
ChevronRightIcon,
|
||||
DotFilledIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
CheckIcon,
|
||||
ChevronRightIcon,
|
||||
DotFilledIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'focus:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto size-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
))
|
||||
DropdownMenuSubTrigger.displayName
|
||||
= DropdownMenuPrimitive.SubTrigger.displayName
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
||||
inset && "pl-8",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto size-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
));
|
||||
DropdownMenuSubTrigger.displayName =
|
||||
DropdownMenuPrimitive.SubTrigger.displayName;
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSubContent.displayName
|
||||
= DropdownMenuPrimitive.SubContent.displayName
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSubContent.displayName =
|
||||
DropdownMenuPrimitive.SubContent.displayName;
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={ny(
|
||||
'bg-popover text-popover-foreground z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-md',
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
))
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={ny(
|
||||
"z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
));
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
}
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
inset && "pl-8",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
))
|
||||
DropdownMenuCheckboxItem.displayName
|
||||
= DropdownMenuPrimitive.CheckboxItem.displayName
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
));
|
||||
DropdownMenuCheckboxItem.displayName =
|
||||
DropdownMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<DotFilledIcon className="size-4 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
))
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<DotFilledIcon className="size-4 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
));
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean;
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'px-2 py-1.5 text-sm font-semibold',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"px-2 py-1.5 text-sm font-semibold",
|
||||
inset && "pl-8",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={ny('bg-muted -mx-1 my-1 h-px', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={ny("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
||||
|
||||
function DropdownMenuShortcut({
|
||||
className,
|
||||
...props
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) {
|
||||
return (
|
||||
<span
|
||||
className={ny('ml-auto text-xs tracking-widest opacity-60', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<span
|
||||
className={ny("ml-auto text-xs tracking-widest opacity-60", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
|
||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
}
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
};
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import type { Variants } from 'framer-motion'
|
||||
import { motion } from 'framer-motion'
|
||||
import { useMemo } from 'react'
|
||||
import type { Variants } from "framer-motion";
|
||||
import { motion } from "framer-motion";
|
||||
import { useMemo } from "react";
|
||||
|
||||
interface FadeTextProps {
|
||||
className?: string
|
||||
direction?: 'up' | 'down' | 'left' | 'right'
|
||||
framerProps?: Variants
|
||||
text: string
|
||||
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,
|
||||
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 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 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 }
|
||||
}
|
||||
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 {
|
||||
...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>
|
||||
)
|
||||
return (
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
viewport={{ once: true }}
|
||||
variants={FADE_ANIMATION_VARIANTS}
|
||||
>
|
||||
<motion.span className={className}>{text}</motion.span>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,189 +1,192 @@
|
||||
import * as React from 'react'
|
||||
import type * as LabelPrimitive from '@radix-ui/react-label'
|
||||
import { Slot } from '@radix-ui/react-slot'
|
||||
import type { ControllerProps, FieldPath, FieldValues } from 'react-hook-form'
|
||||
import { Controller, FormProvider, useFormContext, useFormState } from 'react-hook-form'
|
||||
import * as React from "react";
|
||||
import type * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import type { ControllerProps, FieldPath, FieldValues } from "react-hook-form";
|
||||
import {
|
||||
Controller,
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
useFormState,
|
||||
} from "react-hook-form";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { ny } from "@/lib/utils";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
const Form = FormProvider
|
||||
const Form = FormProvider;
|
||||
|
||||
interface FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
> {
|
||||
name: TName
|
||||
name: TName;
|
||||
}
|
||||
|
||||
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||
{} as FormFieldContextValue,
|
||||
)
|
||||
{} as FormFieldContextValue,
|
||||
);
|
||||
|
||||
function FormField<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
>({ ...props }: ControllerProps<TFieldValues, TName>) {
|
||||
return (
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
)
|
||||
return (
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function useFormField() {
|
||||
const fieldContext = React.useContext(FormFieldContext)
|
||||
const itemContext = React.useContext(FormItemContext)
|
||||
const { getFieldState, formState } = useFormContext()
|
||||
const fieldContext = React.useContext(FormFieldContext);
|
||||
const itemContext = React.useContext(FormItemContext);
|
||||
const { getFieldState, formState } = useFormContext();
|
||||
|
||||
const fieldState = getFieldState(fieldContext.name, formState)
|
||||
const fieldState = getFieldState(fieldContext.name, formState);
|
||||
|
||||
if (!fieldContext)
|
||||
throw new Error('useFormField should be used within <FormField>')
|
||||
if (!fieldContext)
|
||||
throw new Error("useFormField should be used within <FormField>");
|
||||
|
||||
const { id } = itemContext
|
||||
const { id } = itemContext;
|
||||
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
}
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
};
|
||||
}
|
||||
|
||||
interface FormItemContextValue {
|
||||
id: string
|
||||
id: string;
|
||||
}
|
||||
|
||||
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||
{} as FormItemContextValue,
|
||||
)
|
||||
{} as FormItemContextValue,
|
||||
);
|
||||
|
||||
const FormItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const id = React.useId()
|
||||
const id = React.useId();
|
||||
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div ref={ref} className={ny('space-y-2', className)} {...props} />
|
||||
</FormItemContext.Provider>
|
||||
)
|
||||
})
|
||||
FormItem.displayName = 'FormItem'
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div ref={ref} className={ny("space-y-2", className)} {...props} />
|
||||
</FormItemContext.Provider>
|
||||
);
|
||||
});
|
||||
FormItem.displayName = "FormItem";
|
||||
|
||||
const FormLabel = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { error, formItemId } = useFormField()
|
||||
const { error, formItemId } = useFormField();
|
||||
|
||||
return (
|
||||
<Label
|
||||
ref={ref}
|
||||
className={ny(error && 'text-destructive', className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormLabel.displayName = 'FormLabel'
|
||||
return (
|
||||
<Label
|
||||
ref={ref}
|
||||
className={ny(error && "text-destructive", className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormLabel.displayName = "FormLabel";
|
||||
|
||||
const FormControl = React.forwardRef<
|
||||
React.ElementRef<typeof Slot>,
|
||||
React.ComponentPropsWithoutRef<typeof Slot>
|
||||
React.ElementRef<typeof Slot>,
|
||||
React.ComponentPropsWithoutRef<typeof Slot>
|
||||
>(({ ...props }, ref) => {
|
||||
const { error, formItemId, formDescriptionId, formMessageId }
|
||||
= useFormField()
|
||||
const { error, formItemId, formDescriptionId, formMessageId } =
|
||||
useFormField();
|
||||
|
||||
return (
|
||||
<Slot
|
||||
ref={ref}
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
? `${formDescriptionId}`
|
||||
: `${formDescriptionId} ${formMessageId}`
|
||||
}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormControl.displayName = 'FormControl'
|
||||
return (
|
||||
<Slot
|
||||
ref={ref}
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
? `${formDescriptionId}`
|
||||
: `${formDescriptionId} ${formMessageId}`
|
||||
}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormControl.displayName = "FormControl";
|
||||
|
||||
const FormDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { formDescriptionId } = useFormField()
|
||||
const { formDescriptionId } = useFormField();
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formDescriptionId}
|
||||
className={ny('text-[0.8rem] text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
FormDescription.displayName = 'FormDescription'
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formDescriptionId}
|
||||
className={ny("text-[0.8rem] text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormDescription.displayName = "FormDescription";
|
||||
|
||||
const FormMessage = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, children, ...props }, ref) => {
|
||||
const { error, formMessageId } = useFormField()
|
||||
const body = error ? String(error?.message) : children
|
||||
const { error, formMessageId } = useFormField();
|
||||
const body = error ? String(error?.message) : children;
|
||||
|
||||
if (!body)
|
||||
return null
|
||||
if (!body) return null;
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formMessageId}
|
||||
className={ny('text-[0.8rem] font-medium text-destructive', className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
)
|
||||
})
|
||||
FormMessage.displayName = 'FormMessage'
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formMessageId}
|
||||
className={ny("text-[0.8rem] font-medium text-destructive", className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
);
|
||||
});
|
||||
FormMessage.displayName = "FormMessage";
|
||||
|
||||
const FormGlobalError = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { errors } = useFormState()
|
||||
const rootError = errors.root
|
||||
if (!rootError)
|
||||
return null
|
||||
const { errors } = useFormState();
|
||||
const rootError = errors.root;
|
||||
if (!rootError) return null;
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
className={ny('text-sm font-medium text-destructive', className)}
|
||||
{...props}
|
||||
>
|
||||
{rootError.message}
|
||||
</p>
|
||||
)
|
||||
})
|
||||
FormGlobalError.displayName = 'FormGlobalError'
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
className={ny("text-sm font-medium text-destructive", className)}
|
||||
{...props}
|
||||
>
|
||||
{rootError.message}
|
||||
</p>
|
||||
);
|
||||
});
|
||||
FormGlobalError.displayName = "FormGlobalError";
|
||||
|
||||
export {
|
||||
useFormField,
|
||||
Form,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormMessage,
|
||||
FormGlobalError,
|
||||
FormField,
|
||||
}
|
||||
useFormField,
|
||||
Form,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormMessage,
|
||||
FormGlobalError,
|
||||
FormField,
|
||||
};
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import type { Variants } from 'framer-motion'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { ny } from '@/lib/utils'
|
||||
import type { Variants } from "framer-motion";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface GradualSpacingProps {
|
||||
text: string
|
||||
duration?: number
|
||||
delayMultiple?: number
|
||||
framerProps?: Variants
|
||||
className?: string
|
||||
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,
|
||||
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={ny('drop-shadow-sm ', className)}
|
||||
>
|
||||
{char === ' ' ? <span> </span> : char}
|
||||
</motion.h1>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
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={ny("drop-shadow-sm", className)}
|
||||
>
|
||||
{char === " " ? <span> </span> : char}
|
||||
</motion.h1>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
import { useId } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import { useId } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface GridPatternProps {
|
||||
width?: any
|
||||
height?: any
|
||||
x?: any
|
||||
y?: any
|
||||
squares?: Array<[x: number, y: number]>
|
||||
strokeDasharray?: any
|
||||
className?: string
|
||||
[key: string]: any
|
||||
width?: any;
|
||||
height?: any;
|
||||
x?: any;
|
||||
y?: any;
|
||||
squares?: Array<[x: number, y: number]>;
|
||||
strokeDasharray?: any;
|
||||
className?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export function GridPattern({
|
||||
width = 40,
|
||||
height = 40,
|
||||
x = -1,
|
||||
y = -1,
|
||||
strokeDasharray = 0,
|
||||
squares,
|
||||
className,
|
||||
...props
|
||||
width = 40,
|
||||
height = 40,
|
||||
x = -1,
|
||||
y = -1,
|
||||
strokeDasharray = 0,
|
||||
squares,
|
||||
className,
|
||||
...props
|
||||
}: GridPatternProps) {
|
||||
const id = useId()
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className={ny(
|
||||
'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>
|
||||
)
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className={ny(
|
||||
"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
|
||||
export default GridPattern;
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as LabelPrimitive from '@radix-ui/react-label'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import * as React from "react";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const labelVariants = cva(
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
)
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||
);
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||
VariantProps<typeof labelVariants>
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||
VariantProps<typeof labelVariants>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Label.displayName = LabelPrimitive.Root.displayName
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Label.displayName = LabelPrimitive.Root.displayName;
|
||||
|
||||
export { Label }
|
||||
export { Label };
|
||||
|
||||
@@ -1,128 +1,128 @@
|
||||
import * as React from "react"
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||
import { cva } from "class-variance-authority"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
import * as React from "react";
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
|
||||
import { cva } from "class-variance-authority";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
|
||||
import { ny } from "@/lib/utils"
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const NavigationMenu = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"relative z-10 flex max-w-max flex-1 items-center justify-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<NavigationMenuViewport />
|
||||
</NavigationMenuPrimitive.Root>
|
||||
))
|
||||
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
|
||||
<NavigationMenuPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"relative z-10 flex max-w-max flex-1 items-center justify-center",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<NavigationMenuViewport />
|
||||
</NavigationMenuPrimitive.Root>
|
||||
));
|
||||
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName;
|
||||
|
||||
const NavigationMenuList = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.List
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"group flex flex-1 list-none items-center justify-center space-x-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
|
||||
<NavigationMenuPrimitive.List
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"group flex flex-1 list-none items-center justify-center space-x-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
|
||||
|
||||
const NavigationMenuItem = NavigationMenuPrimitive.Item
|
||||
const NavigationMenuItem = NavigationMenuPrimitive.Item;
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
|
||||
)
|
||||
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50",
|
||||
);
|
||||
|
||||
const NavigationMenuTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(navigationMenuTriggerStyle(), "group", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}{" "}
|
||||
<ChevronDown
|
||||
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
))
|
||||
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
|
||||
<NavigationMenuPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(navigationMenuTriggerStyle(), "group", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}{" "}
|
||||
<ChevronDown
|
||||
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
));
|
||||
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName;
|
||||
|
||||
const NavigationMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
|
||||
<NavigationMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName;
|
||||
|
||||
const NavigationMenuLink = NavigationMenuPrimitive.Link
|
||||
const NavigationMenuLink = NavigationMenuPrimitive.Link;
|
||||
|
||||
const NavigationMenuViewport = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className={ny("absolute left-0 right-0 top-full flex justify-center")}>
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
className={ny(
|
||||
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-fit overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
<div className={ny("absolute left-0 right-0 top-full flex justify-center")}>
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
className={ny(
|
||||
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-fit overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
NavigationMenuViewport.displayName =
|
||||
NavigationMenuPrimitive.Viewport.displayName
|
||||
NavigationMenuPrimitive.Viewport.displayName;
|
||||
|
||||
const NavigationMenuIndicator = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
))
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
));
|
||||
NavigationMenuIndicator.displayName =
|
||||
NavigationMenuPrimitive.Indicator.displayName
|
||||
NavigationMenuPrimitive.Indicator.displayName;
|
||||
|
||||
export {
|
||||
navigationMenuTriggerStyle,
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuViewport,
|
||||
}
|
||||
navigationMenuTriggerStyle,
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuViewport,
|
||||
};
|
||||
|
||||
@@ -1,144 +1,144 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import type { CSSProperties, ReactElement, ReactNode } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import type { CSSProperties, ReactElement, ReactNode } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface NeonColorsProps {
|
||||
firstColor: string
|
||||
secondColor: string
|
||||
firstColor: string;
|
||||
secondColor: string;
|
||||
}
|
||||
|
||||
interface NeonGradientCardProps {
|
||||
/**
|
||||
* @default <div />
|
||||
* @type ReactElement
|
||||
* @description
|
||||
* The component to be rendered as the card
|
||||
*/
|
||||
as?: ReactElement
|
||||
/**
|
||||
* @default ""
|
||||
* @type string
|
||||
* @description
|
||||
* The className of the card
|
||||
*/
|
||||
className?: string
|
||||
/**
|
||||
* @default <div />
|
||||
* @type ReactElement
|
||||
* @description
|
||||
* The component to be rendered as the card
|
||||
*/
|
||||
as?: ReactElement;
|
||||
/**
|
||||
* @default ""
|
||||
* @type string
|
||||
* @description
|
||||
* The className of the card
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* @default ""
|
||||
* @type ReactNode
|
||||
* @description
|
||||
* The children of the card
|
||||
*/
|
||||
children?: ReactNode
|
||||
/**
|
||||
* @default ""
|
||||
* @type ReactNode
|
||||
* @description
|
||||
* The children of the card
|
||||
*/
|
||||
children?: ReactNode;
|
||||
|
||||
/**
|
||||
* @default 5
|
||||
* @type number
|
||||
* @description
|
||||
* The size of the border in pixels
|
||||
*/
|
||||
borderSize?: number
|
||||
/**
|
||||
* @default 5
|
||||
* @type number
|
||||
* @description
|
||||
* The size of the border in pixels
|
||||
*/
|
||||
borderSize?: number;
|
||||
|
||||
/**
|
||||
* @default 20
|
||||
* @type number
|
||||
* @description
|
||||
* The size of the radius in pixels
|
||||
*/
|
||||
borderRadius?: number
|
||||
/**
|
||||
* @default 20
|
||||
* @type number
|
||||
* @description
|
||||
* The size of the radius in pixels
|
||||
*/
|
||||
borderRadius?: number;
|
||||
|
||||
/**
|
||||
* @default "{ firstColor: '#ff00aa', secondColor: '#00FFF1' }"
|
||||
* @type string
|
||||
* @description
|
||||
* The colors of the neon gradient
|
||||
*/
|
||||
neonColors?: NeonColorsProps
|
||||
/**
|
||||
* @default "{ firstColor: '#ff00aa', secondColor: '#00FFF1' }"
|
||||
* @type string
|
||||
* @description
|
||||
* The colors of the neon gradient
|
||||
*/
|
||||
neonColors?: NeonColorsProps;
|
||||
|
||||
[key: string]: any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const NeonGradientCard: React.FC<NeonGradientCardProps> = ({
|
||||
className,
|
||||
children,
|
||||
borderSize = 2,
|
||||
borderRadius = 20,
|
||||
neonColors = {
|
||||
firstColor: '#ff00aa',
|
||||
secondColor: '#00FFF1',
|
||||
},
|
||||
...props
|
||||
className,
|
||||
children,
|
||||
borderSize = 2,
|
||||
borderRadius = 20,
|
||||
neonColors = {
|
||||
firstColor: "#ff00aa",
|
||||
secondColor: "#00FFF1",
|
||||
},
|
||||
...props
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
const updateDimensions = () => {
|
||||
if (containerRef.current) {
|
||||
const { offsetWidth, offsetHeight } = containerRef.current
|
||||
setDimensions({ width: offsetWidth, height: offsetHeight })
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
const updateDimensions = () => {
|
||||
if (containerRef.current) {
|
||||
const { offsetWidth, offsetHeight } = containerRef.current;
|
||||
setDimensions({ width: offsetWidth, height: offsetHeight });
|
||||
}
|
||||
};
|
||||
|
||||
updateDimensions()
|
||||
window.addEventListener('resize', updateDimensions)
|
||||
updateDimensions();
|
||||
window.addEventListener("resize", updateDimensions);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateDimensions)
|
||||
}
|
||||
}, [])
|
||||
return () => {
|
||||
window.removeEventListener("resize", updateDimensions);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
const { offsetWidth, offsetHeight } = containerRef.current
|
||||
setDimensions({ width: offsetWidth, height: offsetHeight })
|
||||
}
|
||||
}, [children])
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
const { offsetWidth, offsetHeight } = containerRef.current;
|
||||
setDimensions({ width: offsetWidth, height: offsetHeight });
|
||||
}
|
||||
}, [children]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={
|
||||
{
|
||||
'--border-size': `${borderSize}px`,
|
||||
'--border-radius': `${borderRadius}px`,
|
||||
'--neon-first-color': neonColors.firstColor,
|
||||
'--neon-second-color': neonColors.secondColor,
|
||||
'--card-width': `${dimensions.width}px`,
|
||||
'--card-height': `${dimensions.height}px`,
|
||||
'--card-content-radius': `${borderRadius - borderSize}px`,
|
||||
'--pseudo-element-background-image': `linear-gradient(0deg, ${neonColors.firstColor}, ${neonColors.secondColor})`,
|
||||
'--pseudo-element-width': `${dimensions.width + borderSize * 2}px`,
|
||||
'--pseudo-element-height': `${dimensions.height + borderSize * 2}px`,
|
||||
'--after-blur': `${dimensions.width / 3}px`,
|
||||
} as CSSProperties
|
||||
}
|
||||
className={ny(
|
||||
'relative z-10 size-full rounded-[var(--border-radius)]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={ny(
|
||||
'relative size-full min-h-[inherit] rounded-[var(--card-content-radius)] bg-gray-100 p-6',
|
||||
'before:absolute before:-left-[var(--border-size)] before:-top-[var(--border-size)] before:-z-10 before:block',
|
||||
'before:h-[var(--pseudo-element-height)] before:w-[var(--pseudo-element-width)] before:rounded-[var(--border-radius)] before:content-[\'\']',
|
||||
'before:bg-[linear-gradient(0deg,var(--neon-first-color),var(--neon-second-color))] before:bg-[length:100%_200%]',
|
||||
'before:animate-backgroundPositionSpin',
|
||||
'after:absolute after:-left-[var(--border-size)] after:-top-[var(--border-size)] after:-z-10 after:block',
|
||||
'after:h-[var(--pseudo-element-height)] after:w-[var(--pseudo-element-width)] after:rounded-[var(--border-radius)] after:blur-[var(--after-blur)] after:content-[\'\']',
|
||||
'after:bg-[linear-gradient(0deg,var(--neon-first-color),var(--neon-second-color))] after:bg-[length:100%_200%] after:opacity-80',
|
||||
'after:animate-backgroundPositionSpin',
|
||||
'dark:bg-neutral-900',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={
|
||||
{
|
||||
"--border-size": `${borderSize}px`,
|
||||
"--border-radius": `${borderRadius}px`,
|
||||
"--neon-first-color": neonColors.firstColor,
|
||||
"--neon-second-color": neonColors.secondColor,
|
||||
"--card-width": `${dimensions.width}px`,
|
||||
"--card-height": `${dimensions.height}px`,
|
||||
"--card-content-radius": `${borderRadius - borderSize}px`,
|
||||
"--pseudo-element-background-image": `linear-gradient(0deg, ${neonColors.firstColor}, ${neonColors.secondColor})`,
|
||||
"--pseudo-element-width": `${dimensions.width + borderSize * 2}px`,
|
||||
"--pseudo-element-height": `${dimensions.height + borderSize * 2}px`,
|
||||
"--after-blur": `${dimensions.width / 3}px`,
|
||||
} as CSSProperties
|
||||
}
|
||||
className={ny(
|
||||
"relative z-10 size-full rounded-[var(--border-radius)]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={ny(
|
||||
"relative size-full min-h-[inherit] rounded-[var(--card-content-radius)] bg-gray-100 p-6",
|
||||
"before:absolute before:-left-[var(--border-size)] before:-top-[var(--border-size)] before:-z-10 before:block",
|
||||
"before:h-[var(--pseudo-element-height)] before:w-[var(--pseudo-element-width)] before:rounded-[var(--border-radius)] before:content-['']",
|
||||
"before:bg-[linear-gradient(0deg,var(--neon-first-color),var(--neon-second-color))] before:bg-[length:100%_200%]",
|
||||
"before:animate-backgroundPositionSpin",
|
||||
"after:absolute after:-left-[var(--border-size)] after:-top-[var(--border-size)] after:-z-10 after:block",
|
||||
"after:h-[var(--pseudo-element-height)] after:w-[var(--pseudo-element-width)] after:rounded-[var(--border-radius)] after:blur-[var(--after-blur)] after:content-['']",
|
||||
"after:bg-[linear-gradient(0deg,var(--neon-first-color),var(--neon-second-color))] after:bg-[length:100%_200%] after:opacity-80",
|
||||
"after:animate-backgroundPositionSpin",
|
||||
"dark:bg-neutral-900",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { NeonGradientCard }
|
||||
export { NeonGradientCard };
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
export default function OrbitingCircles({
|
||||
className,
|
||||
children,
|
||||
reverse,
|
||||
duration = 20,
|
||||
delay = 10,
|
||||
radius = 50,
|
||||
path = true,
|
||||
className,
|
||||
children,
|
||||
reverse,
|
||||
duration = 20,
|
||||
delay = 10,
|
||||
radius = 50,
|
||||
path = true,
|
||||
}: {
|
||||
className?: string
|
||||
children?: React.ReactNode
|
||||
reverse?: boolean
|
||||
duration?: number
|
||||
delay?: number
|
||||
radius?: number
|
||||
path?: boolean
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
reverse?: boolean;
|
||||
duration?: number;
|
||||
delay?: number;
|
||||
radius?: number;
|
||||
path?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{path && (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
className="pointer-events-none absolute inset-0 size-full"
|
||||
>
|
||||
<circle
|
||||
className="stroke-black/10 stroke-1 dark:stroke-white/10"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
r={radius}
|
||||
fill="none"
|
||||
strokeDasharray="4 4"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
return (
|
||||
<>
|
||||
{path && (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
className="pointer-events-none absolute inset-0 size-full"
|
||||
>
|
||||
<circle
|
||||
className="stroke-black/10 stroke-1 dark:stroke-white/10"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
r={radius}
|
||||
fill="none"
|
||||
strokeDasharray="4 4"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
|
||||
<div
|
||||
style={
|
||||
{
|
||||
'--duration': duration,
|
||||
'--radius': radius,
|
||||
'--delay': -delay,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={ny(
|
||||
'animate-orbit absolute flex size-full transform-gpu items-center justify-center rounded-full border bg-black/10 [animation-delay:calc(var(--delay)*1000ms)] dark:bg-white/10',
|
||||
{ '[animation-direction:reverse]': reverse },
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--duration": duration,
|
||||
"--radius": radius,
|
||||
"--delay": -delay,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={ny(
|
||||
"absolute flex size-full transform-gpu animate-orbit items-center justify-center rounded-full border bg-black/10 [animation-delay:calc(var(--delay)*1000ms)] dark:bg-white/10",
|
||||
{ "[animation-direction:reverse]": reverse },
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,271 +1,272 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
interface MousePosition {
|
||||
x: number
|
||||
y: number
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
function MousePosition(): MousePosition {
|
||||
const [mousePosition, setMousePosition] = useState<MousePosition>({
|
||||
x: 0,
|
||||
y: 0,
|
||||
})
|
||||
const [mousePosition, setMousePosition] = useState<MousePosition>({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
setMousePosition({ x: event.clientX, y: event.clientY })
|
||||
}
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
setMousePosition({ x: event.clientX, y: event.clientY });
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', handleMouseMove)
|
||||
window.addEventListener("mousemove", handleMouseMove);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
}
|
||||
}, [])
|
||||
return () => {
|
||||
window.removeEventListener("mousemove", handleMouseMove);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return mousePosition
|
||||
return mousePosition;
|
||||
}
|
||||
|
||||
interface ParticlesProps {
|
||||
className?: string
|
||||
quantity?: number
|
||||
staticity?: number
|
||||
ease?: number
|
||||
size?: number
|
||||
refresh?: boolean
|
||||
color?: string
|
||||
vx?: number
|
||||
vy?: number
|
||||
className?: string;
|
||||
quantity?: number;
|
||||
staticity?: number;
|
||||
ease?: number;
|
||||
size?: number;
|
||||
refresh?: boolean;
|
||||
color?: string;
|
||||
vx?: number;
|
||||
vy?: number;
|
||||
}
|
||||
function hexToRgb(hex: string): number[] {
|
||||
hex = hex.replace('#', '')
|
||||
const hexInt = Number.parseInt(hex, 16)
|
||||
const red = (hexInt >> 16) & 255
|
||||
const green = (hexInt >> 8) & 255
|
||||
const blue = hexInt & 255
|
||||
return [red, green, blue]
|
||||
hex = hex.replace("#", "");
|
||||
const hexInt = Number.parseInt(hex, 16);
|
||||
const red = (hexInt >> 16) & 255;
|
||||
const green = (hexInt >> 8) & 255;
|
||||
const blue = hexInt & 255;
|
||||
return [red, green, blue];
|
||||
}
|
||||
|
||||
const Particles: React.FC<ParticlesProps> = ({
|
||||
className = '',
|
||||
quantity = 100,
|
||||
staticity = 50,
|
||||
ease = 50,
|
||||
size = 0.4,
|
||||
refresh = false,
|
||||
color = '#ffffff',
|
||||
vx = 0,
|
||||
vy = 0,
|
||||
className = "",
|
||||
quantity = 100,
|
||||
staticity = 50,
|
||||
ease = 50,
|
||||
size = 0.4,
|
||||
refresh = false,
|
||||
color = "#ffffff",
|
||||
vx = 0,
|
||||
vy = 0,
|
||||
}) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
const canvasContainerRef = useRef<HTMLDivElement>(null)
|
||||
const context = useRef<CanvasRenderingContext2D | null>(null)
|
||||
const circles = useRef<any[]>([])
|
||||
const mousePosition = MousePosition()
|
||||
const mouse = useRef<{ x: number, y: number }>({ x: 0, y: 0 })
|
||||
const canvasSize = useRef<{ w: number, h: number }>({ w: 0, h: 0 })
|
||||
const dpr = typeof window !== 'undefined' ? window.devicePixelRatio : 1
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const canvasContainerRef = useRef<HTMLDivElement>(null);
|
||||
const context = useRef<CanvasRenderingContext2D | null>(null);
|
||||
const circles = useRef<any[]>([]);
|
||||
const mousePosition = MousePosition();
|
||||
const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
||||
const canvasSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 });
|
||||
const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1;
|
||||
|
||||
useEffect(() => {
|
||||
if (canvasRef.current) {
|
||||
context.current = canvasRef.current.getContext('2d')
|
||||
}
|
||||
initCanvas()
|
||||
animate()
|
||||
window.addEventListener('resize', initCanvas)
|
||||
useEffect(() => {
|
||||
if (canvasRef.current) {
|
||||
context.current = canvasRef.current.getContext("2d");
|
||||
}
|
||||
initCanvas();
|
||||
animate();
|
||||
window.addEventListener("resize", initCanvas);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', initCanvas)
|
||||
}
|
||||
}, [color])
|
||||
return () => {
|
||||
window.removeEventListener("resize", initCanvas);
|
||||
};
|
||||
}, [color]);
|
||||
|
||||
useEffect(() => {
|
||||
onMouseMove()
|
||||
}, [mousePosition.x, mousePosition.y])
|
||||
useEffect(() => {
|
||||
onMouseMove();
|
||||
}, [mousePosition.x, mousePosition.y]);
|
||||
|
||||
useEffect(() => {
|
||||
initCanvas()
|
||||
}, [refresh])
|
||||
useEffect(() => {
|
||||
initCanvas();
|
||||
}, [refresh]);
|
||||
|
||||
const initCanvas = () => {
|
||||
resizeCanvas()
|
||||
drawParticles()
|
||||
}
|
||||
const initCanvas = () => {
|
||||
resizeCanvas();
|
||||
drawParticles();
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (canvasRef.current) {
|
||||
const rect = canvasRef.current.getBoundingClientRect()
|
||||
const { w, h } = canvasSize.current
|
||||
const x = mousePosition.x - rect.left - w / 2
|
||||
const y = mousePosition.y - rect.top - h / 2
|
||||
const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2
|
||||
if (inside) {
|
||||
mouse.current.x = x
|
||||
mouse.current.y = y
|
||||
}
|
||||
}
|
||||
}
|
||||
const onMouseMove = () => {
|
||||
if (canvasRef.current) {
|
||||
const rect = canvasRef.current.getBoundingClientRect();
|
||||
const { w, h } = canvasSize.current;
|
||||
const x = mousePosition.x - rect.left - w / 2;
|
||||
const y = mousePosition.y - rect.top - h / 2;
|
||||
const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2;
|
||||
if (inside) {
|
||||
mouse.current.x = x;
|
||||
mouse.current.y = y;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface Circle {
|
||||
x: number
|
||||
y: number
|
||||
translateX: number
|
||||
translateY: number
|
||||
size: number
|
||||
alpha: number
|
||||
targetAlpha: number
|
||||
dx: number
|
||||
dy: number
|
||||
magnetism: number
|
||||
}
|
||||
interface Circle {
|
||||
x: number;
|
||||
y: number;
|
||||
translateX: number;
|
||||
translateY: number;
|
||||
size: number;
|
||||
alpha: number;
|
||||
targetAlpha: number;
|
||||
dx: number;
|
||||
dy: number;
|
||||
magnetism: number;
|
||||
}
|
||||
|
||||
const resizeCanvas = () => {
|
||||
if (canvasContainerRef.current && canvasRef.current && context.current) {
|
||||
circles.current.length = 0
|
||||
canvasSize.current.w = canvasContainerRef.current.offsetWidth
|
||||
canvasSize.current.h = canvasContainerRef.current.offsetHeight
|
||||
canvasRef.current.width = canvasSize.current.w * dpr
|
||||
canvasRef.current.height = canvasSize.current.h * dpr
|
||||
canvasRef.current.style.width = `${canvasSize.current.w}px`
|
||||
canvasRef.current.style.height = `${canvasSize.current.h}px`
|
||||
context.current.scale(dpr, dpr)
|
||||
}
|
||||
}
|
||||
const resizeCanvas = () => {
|
||||
if (canvasContainerRef.current && canvasRef.current && context.current) {
|
||||
circles.current.length = 0;
|
||||
canvasSize.current.w = canvasContainerRef.current.offsetWidth;
|
||||
canvasSize.current.h = canvasContainerRef.current.offsetHeight;
|
||||
canvasRef.current.width = canvasSize.current.w * dpr;
|
||||
canvasRef.current.height = canvasSize.current.h * dpr;
|
||||
canvasRef.current.style.width = `${canvasSize.current.w}px`;
|
||||
canvasRef.current.style.height = `${canvasSize.current.h}px`;
|
||||
context.current.scale(dpr, dpr);
|
||||
}
|
||||
};
|
||||
|
||||
const circleParams = (): Circle => {
|
||||
const x = Math.floor(Math.random() * canvasSize.current.w)
|
||||
const y = Math.floor(Math.random() * canvasSize.current.h)
|
||||
const translateX = 0
|
||||
const translateY = 0
|
||||
const pSize = Math.floor(Math.random() * 2) + size
|
||||
const alpha = 0
|
||||
const targetAlpha = Number.parseFloat((Math.random() * 0.6 + 0.1).toFixed(1))
|
||||
const dx = (Math.random() - 0.5) * 0.1
|
||||
const dy = (Math.random() - 0.5) * 0.1
|
||||
const magnetism = 0.1 + Math.random() * 4
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
translateX,
|
||||
translateY,
|
||||
size: pSize,
|
||||
alpha,
|
||||
targetAlpha,
|
||||
dx,
|
||||
dy,
|
||||
magnetism,
|
||||
}
|
||||
}
|
||||
const circleParams = (): Circle => {
|
||||
const x = Math.floor(Math.random() * canvasSize.current.w);
|
||||
const y = Math.floor(Math.random() * canvasSize.current.h);
|
||||
const translateX = 0;
|
||||
const translateY = 0;
|
||||
const pSize = Math.floor(Math.random() * 2) + size;
|
||||
const alpha = 0;
|
||||
const targetAlpha = Number.parseFloat(
|
||||
(Math.random() * 0.6 + 0.1).toFixed(1),
|
||||
);
|
||||
const dx = (Math.random() - 0.5) * 0.1;
|
||||
const dy = (Math.random() - 0.5) * 0.1;
|
||||
const magnetism = 0.1 + Math.random() * 4;
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
translateX,
|
||||
translateY,
|
||||
size: pSize,
|
||||
alpha,
|
||||
targetAlpha,
|
||||
dx,
|
||||
dy,
|
||||
magnetism,
|
||||
};
|
||||
};
|
||||
|
||||
const rgb = hexToRgb(color)
|
||||
const rgb = hexToRgb(color);
|
||||
|
||||
const drawCircle = (circle: Circle, update = false) => {
|
||||
if (context.current) {
|
||||
const { x, y, translateX, translateY, size, alpha } = circle
|
||||
context.current.translate(translateX, translateY)
|
||||
context.current.beginPath()
|
||||
context.current.arc(x, y, size, 0, 2 * Math.PI)
|
||||
context.current.fillStyle = `rgba(${rgb.join(', ')}, ${alpha})`
|
||||
context.current.fill()
|
||||
context.current.setTransform(dpr, 0, 0, dpr, 0, 0)
|
||||
const drawCircle = (circle: Circle, update = false) => {
|
||||
if (context.current) {
|
||||
const { x, y, translateX, translateY, size, alpha } = circle;
|
||||
context.current.translate(translateX, translateY);
|
||||
context.current.beginPath();
|
||||
context.current.arc(x, y, size, 0, 2 * Math.PI);
|
||||
context.current.fillStyle = `rgba(${rgb.join(", ")}, ${alpha})`;
|
||||
context.current.fill();
|
||||
context.current.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
|
||||
if (!update) {
|
||||
circles.current.push(circle)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!update) {
|
||||
circles.current.push(circle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const clearContext = () => {
|
||||
if (context.current) {
|
||||
context.current.clearRect(
|
||||
0,
|
||||
0,
|
||||
canvasSize.current.w,
|
||||
canvasSize.current.h,
|
||||
)
|
||||
}
|
||||
}
|
||||
const clearContext = () => {
|
||||
if (context.current) {
|
||||
context.current.clearRect(
|
||||
0,
|
||||
0,
|
||||
canvasSize.current.w,
|
||||
canvasSize.current.h,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const drawParticles = () => {
|
||||
clearContext()
|
||||
const particleCount = quantity
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
const circle = circleParams()
|
||||
drawCircle(circle)
|
||||
}
|
||||
}
|
||||
const drawParticles = () => {
|
||||
clearContext();
|
||||
const particleCount = quantity;
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
const circle = circleParams();
|
||||
drawCircle(circle);
|
||||
}
|
||||
};
|
||||
|
||||
const remapValue = (
|
||||
value: number,
|
||||
start1: number,
|
||||
end1: number,
|
||||
start2: number,
|
||||
end2: number,
|
||||
): number => {
|
||||
const remapped
|
||||
= ((value - start1) * (end2 - start2)) / (end1 - start1) + start2
|
||||
return remapped > 0 ? remapped : 0
|
||||
}
|
||||
const remapValue = (
|
||||
value: number,
|
||||
start1: number,
|
||||
end1: number,
|
||||
start2: number,
|
||||
end2: number,
|
||||
): number => {
|
||||
const remapped =
|
||||
((value - start1) * (end2 - start2)) / (end1 - start1) + start2;
|
||||
return remapped > 0 ? remapped : 0;
|
||||
};
|
||||
|
||||
const animate = () => {
|
||||
clearContext()
|
||||
circles.current.forEach((circle: Circle, i: number) => {
|
||||
// Handle the alpha value
|
||||
const edge = [
|
||||
circle.x + circle.translateX - circle.size, // distance from left edge
|
||||
canvasSize.current.w - circle.x - circle.translateX - circle.size, // distance from right edge
|
||||
circle.y + circle.translateY - circle.size, // distance from top edge
|
||||
canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge
|
||||
]
|
||||
const closestEdge = edge.reduce((a, b) => Math.min(a, b))
|
||||
const remapClosestEdge = Number.parseFloat(
|
||||
remapValue(closestEdge, 0, 20, 0, 1).toFixed(2),
|
||||
)
|
||||
if (remapClosestEdge > 1) {
|
||||
circle.alpha += 0.02
|
||||
if (circle.alpha > circle.targetAlpha) {
|
||||
circle.alpha = circle.targetAlpha
|
||||
}
|
||||
}
|
||||
else {
|
||||
circle.alpha = circle.targetAlpha * remapClosestEdge
|
||||
}
|
||||
circle.x += circle.dx + vx
|
||||
circle.y += circle.dy + vy
|
||||
circle.translateX
|
||||
+= (mouse.current.x / (staticity / circle.magnetism) - circle.translateX)
|
||||
/ ease
|
||||
circle.translateY
|
||||
+= (mouse.current.y / (staticity / circle.magnetism) - circle.translateY)
|
||||
/ ease
|
||||
const animate = () => {
|
||||
clearContext();
|
||||
circles.current.forEach((circle: Circle, i: number) => {
|
||||
// Handle the alpha value
|
||||
const edge = [
|
||||
circle.x + circle.translateX - circle.size, // distance from left edge
|
||||
canvasSize.current.w - circle.x - circle.translateX - circle.size, // distance from right edge
|
||||
circle.y + circle.translateY - circle.size, // distance from top edge
|
||||
canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge
|
||||
];
|
||||
const closestEdge = edge.reduce((a, b) => Math.min(a, b));
|
||||
const remapClosestEdge = Number.parseFloat(
|
||||
remapValue(closestEdge, 0, 20, 0, 1).toFixed(2),
|
||||
);
|
||||
if (remapClosestEdge > 1) {
|
||||
circle.alpha += 0.02;
|
||||
if (circle.alpha > circle.targetAlpha) {
|
||||
circle.alpha = circle.targetAlpha;
|
||||
}
|
||||
} else {
|
||||
circle.alpha = circle.targetAlpha * remapClosestEdge;
|
||||
}
|
||||
circle.x += circle.dx + vx;
|
||||
circle.y += circle.dy + vy;
|
||||
circle.translateX +=
|
||||
(mouse.current.x / (staticity / circle.magnetism) - circle.translateX) /
|
||||
ease;
|
||||
circle.translateY +=
|
||||
(mouse.current.y / (staticity / circle.magnetism) - circle.translateY) /
|
||||
ease;
|
||||
|
||||
drawCircle(circle, true)
|
||||
drawCircle(circle, true);
|
||||
|
||||
// circle gets out of the canvas
|
||||
if (
|
||||
circle.x < -circle.size
|
||||
|| circle.x > canvasSize.current.w + circle.size
|
||||
|| circle.y < -circle.size
|
||||
|| circle.y > canvasSize.current.h + circle.size
|
||||
) {
|
||||
// remove the circle from the array
|
||||
circles.current.splice(i, 1)
|
||||
// create a new circle
|
||||
const newCircle = circleParams()
|
||||
drawCircle(newCircle)
|
||||
// update the circle position
|
||||
}
|
||||
})
|
||||
window.requestAnimationFrame(animate)
|
||||
}
|
||||
// circle gets out of the canvas
|
||||
if (
|
||||
circle.x < -circle.size ||
|
||||
circle.x > canvasSize.current.w + circle.size ||
|
||||
circle.y < -circle.size ||
|
||||
circle.y > canvasSize.current.h + circle.size
|
||||
) {
|
||||
// remove the circle from the array
|
||||
circles.current.splice(i, 1);
|
||||
// create a new circle
|
||||
const newCircle = circleParams();
|
||||
drawCircle(newCircle);
|
||||
// update the circle position
|
||||
}
|
||||
});
|
||||
window.requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className} ref={canvasContainerRef} aria-hidden="true">
|
||||
<canvas ref={canvasRef} className="size-full" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className={className} ref={canvasContainerRef} aria-hidden="true">
|
||||
<canvas ref={canvasRef} className="size-full" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Particles
|
||||
export default Particles;
|
||||
|
||||
@@ -1,78 +1,76 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import React from 'react'
|
||||
import React from "react";
|
||||
|
||||
interface PulsatingButtonProps {
|
||||
text: string
|
||||
pulseColor: string
|
||||
backgroundColor: string
|
||||
textColor: string
|
||||
animationDuration: string
|
||||
buttonWidth: string
|
||||
buttonHeight: string
|
||||
text: string;
|
||||
pulseColor: string;
|
||||
backgroundColor: string;
|
||||
textColor: string;
|
||||
animationDuration: string;
|
||||
buttonWidth: string;
|
||||
buttonHeight: string;
|
||||
}
|
||||
|
||||
export const PulsatingButton: React.FC<PulsatingButtonProps> = ({
|
||||
text,
|
||||
pulseColor,
|
||||
backgroundColor,
|
||||
textColor,
|
||||
animationDuration,
|
||||
buttonWidth,
|
||||
buttonHeight,
|
||||
text,
|
||||
pulseColor,
|
||||
backgroundColor,
|
||||
textColor,
|
||||
animationDuration,
|
||||
buttonWidth,
|
||||
buttonHeight,
|
||||
}) => {
|
||||
const pulseKeyframes = {
|
||||
'--tw-pulse-color': pulseColor,
|
||||
'animation': `pulse ${animationDuration} linear infinite`,
|
||||
}
|
||||
const pulseKeyframes = {
|
||||
"--tw-pulse-color": pulseColor,
|
||||
animation: `pulse ${animationDuration} linear infinite`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex justify-center items-center"
|
||||
>
|
||||
<button
|
||||
className="relative block text-center cursor-pointer flex justify-center items-center"
|
||||
style={{
|
||||
color: textColor,
|
||||
backgroundColor,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight,
|
||||
borderRadius: '12px',
|
||||
...pulseKeyframes,
|
||||
}}
|
||||
>
|
||||
<div>{text}</div>
|
||||
<style jsx>
|
||||
{`
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(var(--tw-pulse-color), 0);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 8px rgba(var(--tw-pulse-color), 0.5);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(var(--tw-pulse-color), 0);
|
||||
}
|
||||
}
|
||||
button::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 20px;
|
||||
background: inherit;
|
||||
animation: inherit;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: -1;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<button
|
||||
className="relative block flex cursor-pointer items-center justify-center text-center"
|
||||
style={{
|
||||
color: textColor,
|
||||
backgroundColor,
|
||||
width: buttonWidth,
|
||||
height: buttonHeight,
|
||||
borderRadius: "12px",
|
||||
...pulseKeyframes,
|
||||
}}
|
||||
>
|
||||
<div>{text}</div>
|
||||
<style jsx>
|
||||
{`
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(var(--tw-pulse-color), 0);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 8px rgba(var(--tw-pulse-color), 0.5);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(var(--tw-pulse-color), 0);
|
||||
}
|
||||
}
|
||||
button::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 20px;
|
||||
background: inherit;
|
||||
animation: inherit;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: -1;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PulsatingButton
|
||||
export default PulsatingButton;
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
import type { CSSProperties } from 'react'
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
type Type = 'circle' | 'ellipse'
|
||||
type Type = "circle" | "ellipse";
|
||||
|
||||
type Origin =
|
||||
| 'center'
|
||||
| 'top'
|
||||
| 'bottom'
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'top left'
|
||||
| 'top right'
|
||||
| 'bottom left'
|
||||
| 'bottom right'
|
||||
| "center"
|
||||
| "top"
|
||||
| "bottom"
|
||||
| "left"
|
||||
| "right"
|
||||
| "top left"
|
||||
| "top right"
|
||||
| "bottom left"
|
||||
| "bottom right";
|
||||
|
||||
interface RadialProps {
|
||||
/**
|
||||
* The type of radial gradient
|
||||
* @default circle
|
||||
* @type string
|
||||
*/
|
||||
type?: Type
|
||||
/**
|
||||
* The color to transition from
|
||||
* @default #00000000
|
||||
* @type string
|
||||
*/
|
||||
from?: string
|
||||
/**
|
||||
* The type of radial gradient
|
||||
* @default circle
|
||||
* @type string
|
||||
*/
|
||||
type?: Type;
|
||||
/**
|
||||
* The color to transition from
|
||||
* @default #00000000
|
||||
* @type string
|
||||
*/
|
||||
from?: string;
|
||||
|
||||
/**
|
||||
* The color to transition to
|
||||
* @default #290A5C
|
||||
* @type string
|
||||
*/
|
||||
to?: string
|
||||
/**
|
||||
* The color to transition to
|
||||
* @default #290A5C
|
||||
* @type string
|
||||
*/
|
||||
to?: string;
|
||||
|
||||
/**
|
||||
* The size of the gradient in pixels
|
||||
* @default 300
|
||||
* @type number
|
||||
*/
|
||||
size?: number
|
||||
/**
|
||||
* The size of the gradient in pixels
|
||||
* @default 300
|
||||
* @type number
|
||||
*/
|
||||
size?: number;
|
||||
|
||||
/**
|
||||
* The origin of the gradient
|
||||
* @default center
|
||||
* @type string
|
||||
*/
|
||||
origin?: Origin
|
||||
/**
|
||||
* The origin of the gradient
|
||||
* @default center
|
||||
* @type string
|
||||
*/
|
||||
origin?: Origin;
|
||||
|
||||
/**
|
||||
* The class name to apply to the gradient
|
||||
* @default ""
|
||||
* @type string
|
||||
*/
|
||||
className?: string
|
||||
/**
|
||||
* The class name to apply to the gradient
|
||||
* @default ""
|
||||
* @type string
|
||||
*/
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function RadialGradient({
|
||||
type = 'circle',
|
||||
from = 'rgba(120,119,198,0.3)',
|
||||
to = 'hsla(0, 0%, 0%, 0)',
|
||||
size = 300,
|
||||
origin = 'center',
|
||||
className,
|
||||
type = "circle",
|
||||
from = "rgba(120,119,198,0.3)",
|
||||
to = "hsla(0, 0%, 0%, 0)",
|
||||
size = 300,
|
||||
origin = "center",
|
||||
className,
|
||||
}: RadialProps) {
|
||||
const styles: CSSProperties = {
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
inset: 0,
|
||||
backgroundImage: `radial-gradient(${type} ${size}px at ${origin}, ${from}, ${to})`,
|
||||
}
|
||||
const styles: CSSProperties = {
|
||||
position: "absolute",
|
||||
pointerEvents: "none",
|
||||
inset: 0,
|
||||
backgroundImage: `radial-gradient(${type} ${size}px at ${origin}, ${from}, ${to})`,
|
||||
};
|
||||
|
||||
return <div className={className} style={styles} />
|
||||
return <div className={className} style={styles} />;
|
||||
}
|
||||
|
||||
export default RadialGradient
|
||||
export default RadialGradient;
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
export default function RetroGrid({ className }: { className?: string }) {
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
'pointer-events-none absolute h-full w-full overflow-hidden opacity-50 [perspective:200px]',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{/* Grid */}
|
||||
<div className="absolute inset-0 [transform:rotateX(35deg)]">
|
||||
<div
|
||||
className={ny(
|
||||
'animate-grid',
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
"pointer-events-none absolute h-full w-full overflow-hidden opacity-50 [perspective:200px]",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{/* Grid */}
|
||||
<div className="absolute inset-0 [transform:rotateX(35deg)]">
|
||||
<div
|
||||
className={ny(
|
||||
"animate-grid",
|
||||
|
||||
'[background-repeat:repeat] [background-size:60px_60px] [height:300vh] [inset:0%_0px] [margin-left:-50%] [transform-origin:100%_0_0] [width:600vw]',
|
||||
"[background-repeat:repeat] [background-size:60px_60px] [height:300vh] [inset:0%_0px] [margin-left:-50%] [transform-origin:100%_0_0] [width:600vw]",
|
||||
|
||||
// Light Styles
|
||||
'[background-image:linear-gradient(to_right,rgba(0,0,0,0.3)_1px,transparent_0),linear-gradient(to_bottom,rgba(0,0,0,0.3)_1px,transparent_0)]',
|
||||
// Light Styles
|
||||
"[background-image:linear-gradient(to_right,rgba(0,0,0,0.3)_1px,transparent_0),linear-gradient(to_bottom,rgba(0,0,0,0.3)_1px,transparent_0)]",
|
||||
|
||||
// Dark styles
|
||||
'dark:[background-image:linear-gradient(to_right,rgba(255,255,255,0.2)_1px,transparent_0),linear-gradient(to_bottom,rgba(255,255,255,0.2)_1px,transparent_0)]',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
// Dark styles
|
||||
"dark:[background-image:linear-gradient(to_right,rgba(255,255,255,0.2)_1px,transparent_0),linear-gradient(to_bottom,rgba(255,255,255,0.2)_1px,transparent_0)]",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Background Gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-white to-transparent to-90% dark:from-black" />
|
||||
</div>
|
||||
)
|
||||
{/* Background Gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-white to-transparent to-90% dark:from-black" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
|
||||
import * as React from "react";
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny('relative overflow-hidden', className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="size-full rounded-[inherit]">
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
))
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny("relative overflow-hidden", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="size-full rounded-[inherit]">
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
));
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = 'vertical', ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={ny(
|
||||
'flex touch-none select-none transition-colors',
|
||||
orientation === 'vertical'
|
||||
&& 'h-full w-2.5 border-l border-l-transparent p-px',
|
||||
orientation === 'horizontal'
|
||||
&& 'h-2.5 flex-col border-t border-t-transparent p-px',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="bg-border relative flex-1 rounded-full" />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
))
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = "vertical", ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={ny(
|
||||
"flex touch-none select-none transition-colors",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent p-px",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent p-px",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
));
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
||||
export { ScrollArea, ScrollBar };
|
||||
|
||||
@@ -1,168 +1,167 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as React from "react";
|
||||
import {
|
||||
CaretSortIcon,
|
||||
CheckIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
import * as SelectPrimitive from '@radix-ui/react-select'
|
||||
CaretSortIcon,
|
||||
CheckIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const Select = SelectPrimitive.Root
|
||||
const Select = SelectPrimitive.Root;
|
||||
|
||||
const SelectGroup = SelectPrimitive.Group
|
||||
const SelectGroup = SelectPrimitive.Group;
|
||||
|
||||
const SelectValue = SelectPrimitive.Value
|
||||
const SelectValue = SelectPrimitive.Value;
|
||||
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-left text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 [&>span]:text-left',
|
||||
className,
|
||||
)}
|
||||
onPointerDown={(e) => {
|
||||
if (e.pointerType === 'touch')
|
||||
e.preventDefault()
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<CaretSortIcon className="h-4 w-4 shrink-0 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
))
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-left text-sm shadow-sm ring-offset-background focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&>span]:text-left",
|
||||
className,
|
||||
)}
|
||||
onPointerDown={(e) => {
|
||||
if (e.pointerType === "touch") e.preventDefault();
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<CaretSortIcon className="h-4 w-4 shrink-0 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
));
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||
|
||||
const SelectScrollUpButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'flex cursor-default items-center justify-center py-1',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUpIcon />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
))
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUpIcon />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
));
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
||||
|
||||
const SelectScrollDownButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'flex cursor-default items-center justify-center py-1',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
))
|
||||
SelectScrollDownButton.displayName
|
||||
= SelectPrimitive.ScrollDownButton.displayName
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
));
|
||||
SelectScrollDownButton.displayName =
|
||||
SelectPrimitive.ScrollDownButton.displayName;
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = 'popper', ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
position === 'popper'
|
||||
&& 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
className,
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={ny(
|
||||
'p-1',
|
||||
position === 'popper'
|
||||
&& 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
))
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className,
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={ny(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
));
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={ny('px-2 py-1.5 text-sm font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={ny("px-2 py-1.5 text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
))
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
));
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={ny('-mx-1 my-1 h-px bg-muted', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={ny("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectGroup,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectLabel,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectScrollUpButton,
|
||||
SelectScrollDownButton,
|
||||
}
|
||||
Select,
|
||||
SelectGroup,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectLabel,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectScrollUpButton,
|
||||
SelectScrollDownButton,
|
||||
};
|
||||
|
||||
@@ -1,144 +1,144 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as SheetPrimitive from '@radix-ui/react-dialog'
|
||||
import { Cross2Icon } from '@radix-ui/react-icons'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import * as React from "react";
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const Sheet = SheetPrimitive.Root
|
||||
const Sheet = SheetPrimitive.Root;
|
||||
|
||||
const SheetTrigger = SheetPrimitive.Trigger
|
||||
const SheetTrigger = SheetPrimitive.Trigger;
|
||||
|
||||
const SheetClose = SheetPrimitive.Close
|
||||
const SheetClose = SheetPrimitive.Close;
|
||||
|
||||
const SheetPortal = SheetPrimitive.Portal
|
||||
const SheetPortal = SheetPrimitive.Portal;
|
||||
|
||||
const SheetOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Overlay
|
||||
className={ny(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
))
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
|
||||
<SheetPrimitive.Overlay
|
||||
className={ny(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
));
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||
|
||||
const sheetVariants = cva(
|
||||
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out',
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
|
||||
bottom:
|
||||
'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
|
||||
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
|
||||
right:
|
||||
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: 'right',
|
||||
},
|
||||
},
|
||||
)
|
||||
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||
bottom:
|
||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||
right:
|
||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "right",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
interface SheetContentProps
|
||||
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||
VariantProps<typeof sheetVariants> {}
|
||||
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||
VariantProps<typeof sheetVariants> {}
|
||||
|
||||
const SheetContent = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||
SheetContentProps
|
||||
>(({ side = 'right', className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(sheetVariants({ side }), className)}
|
||||
{...props}
|
||||
>
|
||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
|
||||
<Cross2Icon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
{children}
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
))
|
||||
SheetContent.displayName = SheetPrimitive.Content.displayName
|
||||
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||
SheetContentProps
|
||||
>(({ side = "right", className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(sheetVariants({ side }), className)}
|
||||
{...props}
|
||||
>
|
||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||
<Cross2Icon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
{children}
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
));
|
||||
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
||||
|
||||
function SheetHeader({
|
||||
className,
|
||||
...props
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
'flex flex-col space-y-2 text-center sm:text-left',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
"flex flex-col space-y-2 text-center sm:text-left",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
SheetHeader.displayName = 'SheetHeader'
|
||||
SheetHeader.displayName = "SheetHeader";
|
||||
|
||||
function SheetFooter({
|
||||
className,
|
||||
...props
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<div
|
||||
className={ny(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
SheetFooter.displayName = 'SheetFooter'
|
||||
SheetFooter.displayName = "SheetFooter";
|
||||
|
||||
const SheetTitle = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={ny('text-foreground text-lg font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SheetTitle.displayName = SheetPrimitive.Title.displayName
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={ny("text-lg font-semibold text-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
||||
|
||||
const SheetDescription = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={ny('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={ny("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Sheet,
|
||||
SheetPortal,
|
||||
SheetOverlay,
|
||||
SheetTrigger,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
}
|
||||
Sheet,
|
||||
SheetPortal,
|
||||
SheetOverlay,
|
||||
SheetTrigger,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
type TColorProp = `#${string}` | `#${string}`[]
|
||||
type TColorProp = `#${string}` | `#${string}`[];
|
||||
interface ShineBorderProps {
|
||||
borderRadius?: number
|
||||
borderWidth?: number
|
||||
duration?: number
|
||||
color?: TColorProp
|
||||
className?: string
|
||||
children: React.ReactNode
|
||||
borderRadius?: number;
|
||||
borderWidth?: number;
|
||||
duration?: number;
|
||||
color?: TColorProp;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,44 +23,43 @@ interface ShineBorderProps {
|
||||
* @param children contains react node elements.
|
||||
*/
|
||||
export default function ShineBorder({
|
||||
borderRadius = 8,
|
||||
borderWidth = 1,
|
||||
duration = 14,
|
||||
color = '#fff',
|
||||
className,
|
||||
children,
|
||||
borderRadius = 8,
|
||||
borderWidth = 1,
|
||||
duration = 14,
|
||||
color = "#fff",
|
||||
className,
|
||||
children,
|
||||
}: ShineBorderProps) {
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
{
|
||||
'--border-radius': `${borderRadius}px`,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={ny(
|
||||
'relative grid min-h-[60px] w-fit min-w-[300px] place-items-center rounded-[--border-radius] bg-white p-3 text-black dark:bg-black dark:text-white',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
{
|
||||
'--border-width': `${borderWidth}px`,
|
||||
'--border-radius': `${borderRadius}px`,
|
||||
'--border-radius-child': `${borderRadius * 0.2}px`,
|
||||
'--shine-pulse-duration': `${duration}s`,
|
||||
'--mask-linear-gradient': `linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)`,
|
||||
'--background-radial-gradient': `radial-gradient(transparent,transparent, ${
|
||||
!Array.isArray(color) ? color : color.join(',')
|
||||
},transparent,transparent)`,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={`before:bg-shine-size before:absolute before:inset-[0] before:aspect-square before:h-full before:w-full before:rounded-[--border-radius] before:p-[--border-width] before:will-change-[background-position] before:content-[""] before:![-webkit-mask-composite:xor] before:![mask-composite:exclude] before:[background-image:var(--background-radial-gradient)] before:[background-size:300%_300%] before:[mask:var(--mask-linear-gradient)] motion-safe:before:animate-[shine-pulse_var(--shine-pulse-duration)_infinite_linear]`}
|
||||
>
|
||||
</div>
|
||||
<div className="z-[1] h-full w-full rounded-[--border-radius-child]">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--border-radius": `${borderRadius}px`,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={ny(
|
||||
"relative grid min-h-[60px] w-fit min-w-[300px] place-items-center rounded-[--border-radius] bg-white p-3 text-black dark:bg-black dark:text-white",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--border-width": `${borderWidth}px`,
|
||||
"--border-radius": `${borderRadius}px`,
|
||||
"--border-radius-child": `${borderRadius * 0.2}px`,
|
||||
"--shine-pulse-duration": `${duration}s`,
|
||||
"--mask-linear-gradient": `linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)`,
|
||||
"--background-radial-gradient": `radial-gradient(transparent,transparent, ${
|
||||
!Array.isArray(color) ? color : color.join(",")
|
||||
},transparent,transparent)`,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={`before:bg-shine-size before:absolute before:inset-[0] before:aspect-square before:h-full before:w-full before:rounded-[--border-radius] before:p-[--border-width] before:will-change-[background-position] before:content-[""] before:![-webkit-mask-composite:xor] before:[background-image:var(--background-radial-gradient)] before:[background-size:300%_300%] before:![mask-composite:exclude] before:[mask:var(--mask-linear-gradient)] motion-safe:before:animate-[shine-pulse_var(--shine-pulse-duration)_infinite_linear]`}
|
||||
></div>
|
||||
<div className="z-[1] h-full w-full rounded-[--border-radius-child]">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
'use client'
|
||||
import { type AnimationProps, motion } from 'framer-motion'
|
||||
"use client";
|
||||
import { type AnimationProps, motion } from "framer-motion";
|
||||
|
||||
const animationProps = {
|
||||
initial: { '--x': '100%', 'scale': 0.8 },
|
||||
animate: { '--x': '-100%', 'scale': 1 },
|
||||
whileTap: { scale: 0.95 },
|
||||
transition: {
|
||||
repeat: Infinity,
|
||||
repeatType: 'loop',
|
||||
repeatDelay: 1,
|
||||
type: 'spring',
|
||||
stiffness: 20,
|
||||
damping: 15,
|
||||
mass: 2,
|
||||
scale: {
|
||||
type: 'spring',
|
||||
stiffness: 200,
|
||||
damping: 5,
|
||||
mass: 0.5,
|
||||
},
|
||||
},
|
||||
} as AnimationProps
|
||||
initial: { "--x": "100%", scale: 0.8 },
|
||||
animate: { "--x": "-100%", scale: 1 },
|
||||
whileTap: { scale: 0.95 },
|
||||
transition: {
|
||||
repeat: Infinity,
|
||||
repeatType: "loop",
|
||||
repeatDelay: 1,
|
||||
type: "spring",
|
||||
stiffness: 20,
|
||||
damping: 15,
|
||||
mass: 2,
|
||||
scale: {
|
||||
type: "spring",
|
||||
stiffness: 200,
|
||||
damping: 5,
|
||||
mass: 0.5,
|
||||
},
|
||||
},
|
||||
} as AnimationProps;
|
||||
|
||||
function ShinyButton({ text = 'shiny-button' }) {
|
||||
return (
|
||||
<motion.button
|
||||
{...animationProps}
|
||||
className="relative rounded-lg px-6 py-2 font-medium backdrop-blur-xl transition-[box-shadow] duration-300 ease-in-out hover:shadow dark:bg-[radial-gradient(circle_at_50%_0%,hsl(var(--primary)/10%)_0%,transparent_60%)] dark:hover:shadow-[0_0_20px_hsl(var(--primary)/10%)]"
|
||||
>
|
||||
<span
|
||||
className="relative block h-full w-full text-sm uppercase tracking-wide text-[rgb(0,0,0,65%)] dark:font-light dark:text-[rgb(255,255,255,90%)]"
|
||||
style={{
|
||||
maskImage:
|
||||
'linear-gradient(-75deg,hsl(var(--primary)) calc(var(--x) + 20%),transparent calc(var(--x) + 30%),hsl(var(--primary)) calc(var(--x) + 100%))',
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
mask: 'linear-gradient(rgb(0,0,0), rgb(0,0,0)) content-box,linear-gradient(rgb(0,0,0), rgb(0,0,0))',
|
||||
maskComposite: 'exclude',
|
||||
}}
|
||||
className="absolute inset-0 z-10 block rounded-[inherit] bg-[linear-gradient(-75deg,hsl(var(--primary)/10%)_calc(var(--x)+20%),hsl(var(--primary)/50%)_calc(var(--x)+25%),hsl(var(--primary)/10%)_calc(var(--x)+100%))] p-px"
|
||||
>
|
||||
</span>
|
||||
</motion.button>
|
||||
)
|
||||
function ShinyButton({ text = "shiny-button" }) {
|
||||
return (
|
||||
<motion.button
|
||||
{...animationProps}
|
||||
className="relative rounded-lg px-6 py-2 font-medium backdrop-blur-xl transition-[box-shadow] duration-300 ease-in-out hover:shadow dark:bg-[radial-gradient(circle_at_50%_0%,hsl(var(--primary)/10%)_0%,transparent_60%)] dark:hover:shadow-[0_0_20px_hsl(var(--primary)/10%)]"
|
||||
>
|
||||
<span
|
||||
className="relative block h-full w-full text-sm uppercase tracking-wide text-[rgb(0,0,0,65%)] dark:font-light dark:text-[rgb(255,255,255,90%)]"
|
||||
style={{
|
||||
maskImage:
|
||||
"linear-gradient(-75deg,hsl(var(--primary)) calc(var(--x) + 20%),transparent calc(var(--x) + 30%),hsl(var(--primary)) calc(var(--x) + 100%))",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
mask: "linear-gradient(rgb(0,0,0), rgb(0,0,0)) content-box,linear-gradient(rgb(0,0,0), rgb(0,0,0))",
|
||||
maskComposite: "exclude",
|
||||
}}
|
||||
className="absolute inset-0 z-10 block rounded-[inherit] bg-[linear-gradient(-75deg,hsl(var(--primary)/10%)_calc(var(--x)+20%),hsl(var(--primary)/50%)_calc(var(--x)+25%),hsl(var(--primary)/10%)_calc(var(--x)+100%))] p-px"
|
||||
></span>
|
||||
</motion.button>
|
||||
);
|
||||
}
|
||||
|
||||
export default ShinyButton
|
||||
export default ShinyButton;
|
||||
|
||||
@@ -1,136 +1,189 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as SliderPrimitive from '@radix-ui/react-slider'
|
||||
import * as React from "react";
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface SliderProps extends React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> {
|
||||
showSteps?: 'none' | 'half' | 'full'
|
||||
formatLabel?: (value: number) => string
|
||||
formatLabelSide?: string
|
||||
interface SliderProps
|
||||
extends React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> {
|
||||
showSteps?: "none" | "half" | "full";
|
||||
formatLabel?: (value: number) => string;
|
||||
formatLabelSide?: string;
|
||||
}
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
SliderProps
|
||||
>(({ className, showSteps = 'none', formatLabel, formatLabelSide = 'top', ...props }, ref) => {
|
||||
const { min = 0, max = 100, step = 1, orientation = 'horizontal', value, defaultValue, onValueChange } = props
|
||||
const [hoveredThumbIndex, setHoveredThumbIndex] = React.useState<boolean>(false)
|
||||
const numberOfSteps = Math.floor((max - min) / step)
|
||||
const stepLines = Array.from({ length: numberOfSteps }, (_, index) => index * step + min)
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
SliderProps
|
||||
>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
showSteps = "none",
|
||||
formatLabel,
|
||||
formatLabelSide = "top",
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const {
|
||||
min = 0,
|
||||
max = 100,
|
||||
step = 1,
|
||||
orientation = "horizontal",
|
||||
value,
|
||||
defaultValue,
|
||||
onValueChange,
|
||||
} = props;
|
||||
const [hoveredThumbIndex, setHoveredThumbIndex] =
|
||||
React.useState<boolean>(false);
|
||||
const numberOfSteps = Math.floor((max - min) / step);
|
||||
const stepLines = Array.from(
|
||||
{ length: numberOfSteps },
|
||||
(_, index) => index * step + min,
|
||||
);
|
||||
|
||||
const initialValue = Array.isArray(value) ? value : (Array.isArray(defaultValue) ? defaultValue : [min, max])
|
||||
const [localValues, setLocalValues] = React.useState<number[]>(initialValue)
|
||||
const initialValue = Array.isArray(value)
|
||||
? value
|
||||
: Array.isArray(defaultValue)
|
||||
? defaultValue
|
||||
: [min, max];
|
||||
const [localValues, setLocalValues] =
|
||||
React.useState<number[]>(initialValue);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isEqual(value, localValues))
|
||||
setLocalValues(Array.isArray(value) ? value : (Array.isArray(defaultValue) ? defaultValue : [min, max]))
|
||||
}, [min, max, value])
|
||||
React.useEffect(() => {
|
||||
if (!isEqual(value, localValues))
|
||||
setLocalValues(
|
||||
Array.isArray(value)
|
||||
? value
|
||||
: Array.isArray(defaultValue)
|
||||
? defaultValue
|
||||
: [min, max],
|
||||
);
|
||||
}, [min, max, value]);
|
||||
|
||||
const handleValueChange = (newValues: number[]) => {
|
||||
setLocalValues(newValues)
|
||||
if (onValueChange)
|
||||
onValueChange(newValues)
|
||||
}
|
||||
const handleValueChange = (newValues: number[]) => {
|
||||
setLocalValues(newValues);
|
||||
if (onValueChange) onValueChange(newValues);
|
||||
};
|
||||
|
||||
function isEqual(array1: number[] | undefined, array2: number[] | undefined) {
|
||||
array1 = array1 ?? []
|
||||
array2 = array2 ?? []
|
||||
function isEqual(
|
||||
array1: number[] | undefined,
|
||||
array2: number[] | undefined,
|
||||
) {
|
||||
array1 = array1 ?? [];
|
||||
array2 = array2 ?? [];
|
||||
|
||||
if (array1.length !== array2.length)
|
||||
return false
|
||||
if (array1.length !== array2.length) return false;
|
||||
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
if (array1[i] !== array2[i])
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
if (array1[i] !== array2[i]) return false;
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'relative flex cursor-pointer touch-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
orientation === 'horizontal' ? 'w-full items-center' : 'h-full justify-center',
|
||||
className,
|
||||
)}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
value={localValues}
|
||||
onValueChange={value => handleValueChange(value)}
|
||||
{...props}
|
||||
onFocus={() => setHoveredThumbIndex(true)}
|
||||
onBlur={() => setHoveredThumbIndex(false)}
|
||||
>
|
||||
<SliderPrimitive.Track className={ny(
|
||||
'bg-primary/20 relative grow overflow-hidden rounded-full',
|
||||
orientation === 'horizontal' ? 'h-1.5 w-full' : 'h-full w-1.5',
|
||||
)}
|
||||
>
|
||||
<SliderPrimitive.Range className={ny(
|
||||
'bg-primary absolute',
|
||||
orientation === 'horizontal' ? 'h-full' : 'w-full',
|
||||
)}
|
||||
/>
|
||||
{showSteps !== undefined && showSteps !== 'none' && stepLines.map((value, index) => {
|
||||
if (value === min || value === max)
|
||||
return null
|
||||
return (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"relative flex cursor-pointer touch-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
orientation === "horizontal"
|
||||
? "w-full items-center"
|
||||
: "h-full justify-center",
|
||||
className,
|
||||
)}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
value={localValues}
|
||||
onValueChange={(value) => handleValueChange(value)}
|
||||
{...props}
|
||||
onFocus={() => setHoveredThumbIndex(true)}
|
||||
onBlur={() => setHoveredThumbIndex(false)}
|
||||
>
|
||||
<SliderPrimitive.Track
|
||||
className={ny(
|
||||
"relative grow overflow-hidden rounded-full bg-primary/20",
|
||||
orientation === "horizontal" ? "h-1.5 w-full" : "h-full w-1.5",
|
||||
)}
|
||||
>
|
||||
<SliderPrimitive.Range
|
||||
className={ny(
|
||||
"absolute bg-primary",
|
||||
orientation === "horizontal" ? "h-full" : "w-full",
|
||||
)}
|
||||
/>
|
||||
{showSteps !== undefined &&
|
||||
showSteps !== "none" &&
|
||||
stepLines.map((value, index) => {
|
||||
if (value === min || value === max) return null;
|
||||
|
||||
const positionPercentage = ((value - min) / (max - min)) * 100
|
||||
const adjustedPosition = 50 + (positionPercentage - 50) * 0.96
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={ny(
|
||||
{ 'w-0.5 h-2': orientation !== 'vertical', 'w-2 h-0.5': orientation === 'vertical' },
|
||||
'bg-muted-foreground absolute',
|
||||
{
|
||||
'left-1': orientation === 'vertical' && showSteps === 'half',
|
||||
'top-1': orientation !== 'vertical' && showSteps === 'half',
|
||||
'left-0': orientation === 'vertical' && showSteps === 'full',
|
||||
'top-0': orientation !== 'vertical' && showSteps === 'full',
|
||||
'-translate-x-1/2': orientation !== 'vertical',
|
||||
'-translate-y-1/2': orientation === 'vertical',
|
||||
},
|
||||
)}
|
||||
style={{
|
||||
[orientation === 'vertical' ? 'bottom' : 'left']: `${adjustedPosition}%`,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
const positionPercentage = ((value - min) / (max - min)) * 100;
|
||||
const adjustedPosition = 50 + (positionPercentage - 50) * 0.96;
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={ny(
|
||||
{
|
||||
"h-2 w-0.5": orientation !== "vertical",
|
||||
"h-0.5 w-2": orientation === "vertical",
|
||||
},
|
||||
"absolute bg-muted-foreground",
|
||||
{
|
||||
"left-1":
|
||||
orientation === "vertical" && showSteps === "half",
|
||||
"top-1":
|
||||
orientation !== "vertical" && showSteps === "half",
|
||||
"left-0":
|
||||
orientation === "vertical" && showSteps === "full",
|
||||
"top-0":
|
||||
orientation !== "vertical" && showSteps === "full",
|
||||
"-translate-x-1/2": orientation !== "vertical",
|
||||
"-translate-y-1/2": orientation === "vertical",
|
||||
},
|
||||
)}
|
||||
style={{
|
||||
[orientation === "vertical" ? "bottom" : "left"]:
|
||||
`${adjustedPosition}%`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SliderPrimitive.Track>
|
||||
{localValues.map((numberStep, index) => (
|
||||
<SliderPrimitive.Thumb
|
||||
key={index}
|
||||
className={ny(
|
||||
"block size-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
||||
)}
|
||||
>
|
||||
{hoveredThumbIndex && formatLabel && (
|
||||
<div
|
||||
className={ny(
|
||||
{
|
||||
"bottom-8 left-1/2 -translate-x-1/2":
|
||||
formatLabelSide === "top",
|
||||
},
|
||||
{
|
||||
"left-1/2 top-8 -translate-x-1/2":
|
||||
formatLabelSide === "bottom",
|
||||
},
|
||||
{ "right-8 -translate-y-1/4": formatLabelSide === "left" },
|
||||
{ "left-8 -translate-y-1/4": formatLabelSide === "right" },
|
||||
"absolute z-30 w-max items-center justify-items-center rounded-md border bg-popover px-2 py-1 text-center text-popover-foreground shadow-sm",
|
||||
)}
|
||||
>
|
||||
{formatLabel(numberStep)}
|
||||
</div>
|
||||
)}
|
||||
</SliderPrimitive.Thumb>
|
||||
))}
|
||||
</SliderPrimitive.Root>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
</SliderPrimitive.Track>
|
||||
{localValues.map((numberStep, index) => (
|
||||
<SliderPrimitive.Thumb
|
||||
key={index}
|
||||
className={ny(
|
||||
'border-primary/50 bg-background focus-visible:ring-ring block size-4 rounded-full border shadow transition-colors focus-visible:outline-none focus-visible:ring-1',
|
||||
)}
|
||||
>
|
||||
{hoveredThumbIndex && formatLabel && (
|
||||
<div
|
||||
className={ny(
|
||||
{ 'bottom-8 left-1/2 -translate-x-1/2': formatLabelSide === 'top' },
|
||||
{ 'top-8 left-1/2 -translate-x-1/2': formatLabelSide === 'bottom' },
|
||||
{ 'right-8 -translate-y-1/4': formatLabelSide === 'left' },
|
||||
{ 'left-8 -translate-y-1/4': formatLabelSide === 'right' },
|
||||
'bg-popover text-popover-foreground absolute z-30 w-max items-center justify-items-center rounded-md border px-2 py-1 text-center shadow-sm',
|
||||
)}
|
||||
>
|
||||
{formatLabel(numberStep)}
|
||||
</div>
|
||||
)}
|
||||
</SliderPrimitive.Thumb>
|
||||
))}
|
||||
</SliderPrimitive.Root>
|
||||
)
|
||||
})
|
||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
Slider.displayName = SliderPrimitive.Root.displayName
|
||||
|
||||
export { Slider }
|
||||
export { Slider };
|
||||
|
||||
@@ -1,152 +1,151 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import type { CSSProperties, ReactElement } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import { motion } from "framer-motion";
|
||||
import type { CSSProperties, ReactElement } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface Sparkle {
|
||||
id: string
|
||||
x: string
|
||||
y: string
|
||||
color: string
|
||||
delay: number
|
||||
scale: number
|
||||
lifespan: number
|
||||
id: string;
|
||||
x: string;
|
||||
y: string;
|
||||
color: string;
|
||||
delay: number;
|
||||
scale: number;
|
||||
lifespan: number;
|
||||
}
|
||||
|
||||
interface SparklesTextProps {
|
||||
/**
|
||||
* @default <div />
|
||||
* @type ReactElement
|
||||
* @description
|
||||
* The component to be rendered as the text
|
||||
*/
|
||||
as?: ReactElement
|
||||
/**
|
||||
* @default <div />
|
||||
* @type ReactElement
|
||||
* @description
|
||||
* The component to be rendered as the text
|
||||
*/
|
||||
as?: ReactElement;
|
||||
|
||||
/**
|
||||
* @default ""
|
||||
* @type string
|
||||
* @description
|
||||
* The className of the text
|
||||
*/
|
||||
className?: string
|
||||
/**
|
||||
* @default ""
|
||||
* @type string
|
||||
* @description
|
||||
* The className of the text
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* @required
|
||||
* @type string
|
||||
* @description
|
||||
* The text to be displayed
|
||||
*/
|
||||
text: string
|
||||
/**
|
||||
* @required
|
||||
* @type string
|
||||
* @description
|
||||
* The text to be displayed
|
||||
*/
|
||||
text: string;
|
||||
|
||||
/**
|
||||
* @default 10
|
||||
* @type number
|
||||
* @description
|
||||
* The count of sparkles
|
||||
*/
|
||||
sparklesCount?: number
|
||||
/**
|
||||
* @default 10
|
||||
* @type number
|
||||
* @description
|
||||
* The count of sparkles
|
||||
*/
|
||||
sparklesCount?: number;
|
||||
|
||||
/**
|
||||
* @default "{first: '#A07CFE', second: '#FE8FB5'}"
|
||||
* @type string
|
||||
* @description
|
||||
* The colors of the sparkles
|
||||
*/
|
||||
colors?: {
|
||||
first: string
|
||||
second: string
|
||||
}
|
||||
/**
|
||||
* @default "{first: '#A07CFE', second: '#FE8FB5'}"
|
||||
* @type string
|
||||
* @description
|
||||
* The colors of the sparkles
|
||||
*/
|
||||
colors?: {
|
||||
first: string;
|
||||
second: string;
|
||||
};
|
||||
}
|
||||
|
||||
const SparklesText: React.FC<SparklesTextProps> = ({
|
||||
text,
|
||||
colors = { first: '#A07CFE', second: '#FE8FB5' },
|
||||
className,
|
||||
sparklesCount = 10,
|
||||
...props
|
||||
text,
|
||||
colors = { first: "#A07CFE", second: "#FE8FB5" },
|
||||
className,
|
||||
sparklesCount = 10,
|
||||
...props
|
||||
}) => {
|
||||
const [sparkles, setSparkles] = useState<Sparkle[]>([])
|
||||
const [sparkles, setSparkles] = useState<Sparkle[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const generateStar = (): Sparkle => {
|
||||
const starX = `${Math.random() * 100}%`
|
||||
const starY = `${Math.random() * 100}%`
|
||||
const color = Math.random() > 0.5 ? colors.first : colors.second
|
||||
const delay = Math.random() * 2
|
||||
const scale = Math.random() * 1 + 0.3
|
||||
const lifespan = Math.random() * 10 + 5
|
||||
const id = `${starX}-${starY}-${Date.now()}`
|
||||
return { id, x: starX, y: starY, color, delay, scale, lifespan }
|
||||
}
|
||||
useEffect(() => {
|
||||
const generateStar = (): Sparkle => {
|
||||
const starX = `${Math.random() * 100}%`;
|
||||
const starY = `${Math.random() * 100}%`;
|
||||
const color = Math.random() > 0.5 ? colors.first : colors.second;
|
||||
const delay = Math.random() * 2;
|
||||
const scale = Math.random() * 1 + 0.3;
|
||||
const lifespan = Math.random() * 10 + 5;
|
||||
const id = `${starX}-${starY}-${Date.now()}`;
|
||||
return { id, x: starX, y: starY, color, delay, scale, lifespan };
|
||||
};
|
||||
|
||||
const initializeStars = () => {
|
||||
const newSparkles = Array.from({ length: sparklesCount }, generateStar)
|
||||
setSparkles(newSparkles)
|
||||
}
|
||||
const initializeStars = () => {
|
||||
const newSparkles = Array.from({ length: sparklesCount }, generateStar);
|
||||
setSparkles(newSparkles);
|
||||
};
|
||||
|
||||
const updateStars = () => {
|
||||
setSparkles(currentSparkles =>
|
||||
currentSparkles.map((star) => {
|
||||
if (star.lifespan <= 0)
|
||||
return generateStar()
|
||||
else return { ...star, lifespan: star.lifespan - 0.1 }
|
||||
}),
|
||||
)
|
||||
}
|
||||
const updateStars = () => {
|
||||
setSparkles((currentSparkles) =>
|
||||
currentSparkles.map((star) => {
|
||||
if (star.lifespan <= 0) return generateStar();
|
||||
else return { ...star, lifespan: star.lifespan - 0.1 };
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
initializeStars()
|
||||
const interval = setInterval(updateStars, 100)
|
||||
initializeStars();
|
||||
const interval = setInterval(updateStars, 100);
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [colors.first, colors.second])
|
||||
return () => clearInterval(interval);
|
||||
}, [colors.first, colors.second]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ny('text-6xl font-bold', className)}
|
||||
{...props}
|
||||
style={
|
||||
{
|
||||
'--sparkles-first-color': `${colors.first}`,
|
||||
'--sparkles-second-color': `${colors.second}`,
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<span className="relative inline-block">
|
||||
{sparkles.map(sparkle => (
|
||||
<Sparkle key={sparkle.id} {...sparkle} />
|
||||
))}
|
||||
<strong className="bg-gradient-to-r from-[var(--sparkles-first-color)] to-[var(--sparkles-second-color)] bg-clip-text text-transparent">
|
||||
{text}
|
||||
</strong>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={ny("text-6xl font-bold", className)}
|
||||
{...props}
|
||||
style={
|
||||
{
|
||||
"--sparkles-first-color": `${colors.first}`,
|
||||
"--sparkles-second-color": `${colors.second}`,
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<span className="relative inline-block">
|
||||
{sparkles.map((sparkle) => (
|
||||
<Sparkle key={sparkle.id} {...sparkle} />
|
||||
))}
|
||||
<strong className="bg-gradient-to-r from-[var(--sparkles-first-color)] to-[var(--sparkles-second-color)] bg-clip-text text-transparent">
|
||||
{text}
|
||||
</strong>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Sparkle: React.FC<Sparkle> = ({ id, x, y, color, delay, scale }) => {
|
||||
return (
|
||||
<motion.svg
|
||||
key={id}
|
||||
className="pointer-events-none absolute z-20"
|
||||
initial={{ opacity: 0, left: x, top: y }}
|
||||
animate={{
|
||||
opacity: [0, 1, 0],
|
||||
scale: [0, scale, 0],
|
||||
rotate: [75, 120, 150],
|
||||
}}
|
||||
transition={{ duration: 0.8, repeat: Infinity, delay }}
|
||||
width="21"
|
||||
height="21"
|
||||
viewBox="0 0 21 21"
|
||||
>
|
||||
<path
|
||||
d="M9.82531 0.843845C10.0553 0.215178 10.9446 0.215178 11.1746 0.843845L11.8618 2.72026C12.4006 4.19229 12.3916 6.39157 13.5 7.5C14.6084 8.60843 16.8077 8.59935 18.2797 9.13822L20.1561 9.82534C20.7858 10.0553 20.7858 10.9447 20.1561 11.1747L18.2797 11.8618C16.8077 12.4007 14.6084 12.3916 13.5 13.5C12.3916 14.6084 12.4006 16.8077 11.8618 18.2798L11.1746 20.1562C10.9446 20.7858 10.0553 20.7858 9.82531 20.1562L9.13819 18.2798C8.59932 16.8077 8.60843 14.6084 7.5 13.5C6.39157 12.3916 4.19225 12.4007 2.72023 11.8618L0.843814 11.1747C0.215148 10.9447 0.215148 10.0553 0.843814 9.82534L2.72023 9.13822C4.19225 8.59935 6.39157 8.60843 7.5 7.5C8.60843 6.39157 8.59932 4.19229 9.13819 2.72026L9.82531 0.843845Z"
|
||||
fill={color}
|
||||
/>
|
||||
</motion.svg>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<motion.svg
|
||||
key={id}
|
||||
className="pointer-events-none absolute z-20"
|
||||
initial={{ opacity: 0, left: x, top: y }}
|
||||
animate={{
|
||||
opacity: [0, 1, 0],
|
||||
scale: [0, scale, 0],
|
||||
rotate: [75, 120, 150],
|
||||
}}
|
||||
transition={{ duration: 0.8, repeat: Infinity, delay }}
|
||||
width="21"
|
||||
height="21"
|
||||
viewBox="0 0 21 21"
|
||||
>
|
||||
<path
|
||||
d="M9.82531 0.843845C10.0553 0.215178 10.9446 0.215178 11.1746 0.843845L11.8618 2.72026C12.4006 4.19229 12.3916 6.39157 13.5 7.5C14.6084 8.60843 16.8077 8.59935 18.2797 9.13822L20.1561 9.82534C20.7858 10.0553 20.7858 10.9447 20.1561 11.1747L18.2797 11.8618C16.8077 12.4007 14.6084 12.3916 13.5 13.5C12.3916 14.6084 12.4006 16.8077 11.8618 18.2798L11.1746 20.1562C10.9446 20.7858 10.0553 20.7858 9.82531 20.1562L9.13819 18.2798C8.59932 16.8077 8.60843 14.6084 7.5 13.5C6.39157 12.3916 4.19225 12.4007 2.72023 11.8618L0.843814 11.1747C0.215148 10.9447 0.215148 10.0553 0.843814 9.82534L2.72023 9.13822C4.19225 8.59935 6.39157 8.60843 7.5 7.5C8.60843 6.39157 8.59932 4.19229 9.13819 2.72026L9.82531 0.843845Z"
|
||||
fill={color}
|
||||
/>
|
||||
</motion.svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default SparklesText
|
||||
export default SparklesText;
|
||||
|
||||
@@ -1,120 +1,120 @@
|
||||
import * as React from 'react'
|
||||
import * as React from "react";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const Table = React.forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className="relative w-full overflow-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={ny('w-full caption-bottom text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
Table.displayName = 'Table'
|
||||
<div className="relative w-full overflow-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={ny("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
Table.displayName = "Table";
|
||||
|
||||
const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={ny('[&_tr]:border-b', className)} {...props} />
|
||||
))
|
||||
TableHeader.displayName = 'TableHeader'
|
||||
<thead ref={ref} className={ny("[&_tr]:border-b", className)} {...props} />
|
||||
));
|
||||
TableHeader.displayName = "TableHeader";
|
||||
|
||||
const TableBody = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={ny('[&_tr:last-child]:border-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableBody.displayName = 'TableBody'
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={ny("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableBody.displayName = "TableBody";
|
||||
|
||||
const TableFooter = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableFooter.displayName = 'TableFooter'
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableFooter.displayName = "TableFooter";
|
||||
|
||||
const TableRow = React.forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableRow.displayName = 'TableRow'
|
||||
<tr
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableRow.displayName = "TableRow";
|
||||
|
||||
const TableHead = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<th
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'text-muted-foreground h-10 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableHead.displayName = 'TableHead'
|
||||
<th
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableHead.displayName = "TableHead";
|
||||
|
||||
const TableCell = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<td
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableCell.displayName = 'TableCell'
|
||||
<td
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCell.displayName = "TableCell";
|
||||
|
||||
const TableCaption = React.forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={ny('text-muted-foreground mt-4 text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TableCaption.displayName = 'TableCaption'
|
||||
<caption
|
||||
ref={ref}
|
||||
className={ny("mt-4 text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCaption.displayName = "TableCaption";
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
};
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as TabsPrimitive from '@radix-ui/react-tabs'
|
||||
import * as React from "react";
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||
|
||||
import { ny } from '@/lib/utils'
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
const Tabs = TabsPrimitive.Root
|
||||
const Tabs = TabsPrimitive.Root;
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsList.displayName = TabsPrimitive.List.displayName
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={ny(
|
||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { motion, useScroll, useTransform } from 'framer-motion'
|
||||
import type { FC, ReactNode } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import { motion, useScroll, useTransform } from "framer-motion";
|
||||
import type { FC, ReactNode } from "react";
|
||||
import { useRef } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface TextRevealByWordProps {
|
||||
text: string
|
||||
className?: string
|
||||
text: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TextRevealByWord: FC<TextRevealByWordProps> = ({
|
||||
text,
|
||||
className,
|
||||
text,
|
||||
className,
|
||||
}) => {
|
||||
const targetRef = useRef<HTMLDivElement | null>(null)
|
||||
const targetRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: targetRef,
|
||||
})
|
||||
const words = text.split(' ')
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: targetRef,
|
||||
});
|
||||
const words = text.split(" ");
|
||||
|
||||
return (
|
||||
<div ref={targetRef} className={ny('relative z-0 h-[200vh]', className)}>
|
||||
<div className="sticky top-0 mx-auto flex h-[50%] max-w-4xl items-center bg-transparent px-[1rem] py-[5rem]">
|
||||
<p
|
||||
ref={targetRef}
|
||||
className="flex flex-wrap p-5 text-2xl font-bold text-black/20 dark:text-white/20 md:p-8 md:text-3xl lg:p-10 lg:text-4xl xl:text-5xl"
|
||||
>
|
||||
{words.map((word, i) => {
|
||||
const start = i / words.length
|
||||
const end = start + 1 / words.length
|
||||
return (
|
||||
<Word key={i} progress={scrollYProgress} range={[start, end]}>
|
||||
{word}
|
||||
</Word>
|
||||
)
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div ref={targetRef} className={ny("relative z-0 h-[200vh]", className)}>
|
||||
<div className="sticky top-0 mx-auto flex h-[50%] max-w-4xl items-center bg-transparent px-[1rem] py-[5rem]">
|
||||
<p
|
||||
ref={targetRef}
|
||||
className="flex flex-wrap p-5 text-2xl font-bold text-black/20 dark:text-white/20 md:p-8 md:text-3xl lg:p-10 lg:text-4xl xl:text-5xl"
|
||||
>
|
||||
{words.map((word, i) => {
|
||||
const start = i / words.length;
|
||||
const end = start + 1 / words.length;
|
||||
return (
|
||||
<Word key={i} progress={scrollYProgress} range={[start, end]}>
|
||||
{word}
|
||||
</Word>
|
||||
);
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface WordProps {
|
||||
children: ReactNode
|
||||
progress: any
|
||||
range: [number, number]
|
||||
children: ReactNode;
|
||||
progress: any;
|
||||
range: [number, number];
|
||||
}
|
||||
|
||||
const Word: FC<WordProps> = ({ children, progress, range }) => {
|
||||
const opacity = useTransform(progress, range, [0, 1])
|
||||
return (
|
||||
<span className="xl:lg-3 relative mx-1 lg:mx-2.5">
|
||||
<span className="absolute opacity-30">{children}</span>
|
||||
<motion.span style={{ opacity }} className="text-black dark:text-white">
|
||||
{children}
|
||||
</motion.span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
const opacity = useTransform(progress, range, [0, 1]);
|
||||
return (
|
||||
<span className="xl:lg-3 relative mx-1 lg:mx-2.5">
|
||||
<span className="absolute opacity-30">{children}</span>
|
||||
<motion.span style={{ opacity }} className="text-black dark:text-white">
|
||||
{children}
|
||||
</motion.span>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextRevealByWord
|
||||
export default TextRevealByWord;
|
||||
|
||||
@@ -1,46 +1,45 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import { useEffect, useState } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface TypingAnimationProps {
|
||||
text: string
|
||||
duration?: number
|
||||
className?: string
|
||||
text: string;
|
||||
duration?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function TypingAnimation({
|
||||
text,
|
||||
duration = 200,
|
||||
className,
|
||||
text,
|
||||
duration = 200,
|
||||
className,
|
||||
}: TypingAnimationProps) {
|
||||
const [displayedText, setDisplayedText] = useState<string>('')
|
||||
const [i, setI] = useState<number>(0)
|
||||
const [displayedText, setDisplayedText] = useState<string>("");
|
||||
const [i, setI] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
const typingEffect = setInterval(() => {
|
||||
if (i < text.length) {
|
||||
setDisplayedText(text.substring(0, i + 1))
|
||||
setI(i + 1)
|
||||
}
|
||||
else {
|
||||
clearInterval(typingEffect)
|
||||
}
|
||||
}, duration)
|
||||
useEffect(() => {
|
||||
const typingEffect = setInterval(() => {
|
||||
if (i < text.length) {
|
||||
setDisplayedText(text.substring(0, i + 1));
|
||||
setI(i + 1);
|
||||
} else {
|
||||
clearInterval(typingEffect);
|
||||
}
|
||||
}, duration);
|
||||
|
||||
return () => {
|
||||
clearInterval(typingEffect)
|
||||
}
|
||||
}, [duration, i])
|
||||
return () => {
|
||||
clearInterval(typingEffect);
|
||||
};
|
||||
}, [duration, i]);
|
||||
|
||||
return (
|
||||
<h1
|
||||
className={ny(
|
||||
'font-display text-center text-4xl font-bold leading-[5rem] tracking-[-0.02em] drop-shadow-sm',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{displayedText || text}
|
||||
</h1>
|
||||
)
|
||||
return (
|
||||
<h1
|
||||
className={ny(
|
||||
"font-display text-center text-4xl font-bold leading-[5rem] tracking-[-0.02em] drop-shadow-sm",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{displayedText || text}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { useMemo } from 'react'
|
||||
import { ny } from '@/lib/utils'
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useMemo } from "react";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface WavyTextProps {
|
||||
word: string
|
||||
className?: string
|
||||
variant?: {
|
||||
hidden: { y: number }
|
||||
visible: { y: number }
|
||||
}
|
||||
duration?: number
|
||||
delay?: number
|
||||
word: string;
|
||||
className?: string;
|
||||
variant?: {
|
||||
hidden: { y: number };
|
||||
visible: { y: number };
|
||||
};
|
||||
duration?: number;
|
||||
delay?: number;
|
||||
}
|
||||
function WavyText({
|
||||
word,
|
||||
className,
|
||||
variant,
|
||||
duration = 0.5,
|
||||
delay = 0.05,
|
||||
word,
|
||||
className,
|
||||
variant,
|
||||
duration = 0.5,
|
||||
delay = 0.05,
|
||||
}: WavyTextProps) {
|
||||
const defaultVariants = {
|
||||
hidden: { y: 10 },
|
||||
visible: { y: -10 },
|
||||
}
|
||||
const combinedVariants = variant || defaultVariants
|
||||
const characters = useMemo(() => word.split(''), [word])
|
||||
return (
|
||||
<div className="flex justify-center space-x-2 overflow-hidden p-3">
|
||||
<AnimatePresence>
|
||||
{characters.map((char, i) => (
|
||||
<motion.h1
|
||||
key={i}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
exit="hidden"
|
||||
variants={combinedVariants}
|
||||
transition={{
|
||||
yoyo: Infinity,
|
||||
duration,
|
||||
delay: i * delay,
|
||||
}}
|
||||
className={ny(
|
||||
className,
|
||||
'font-display text-center text-4xl font-bold tracking-[-0.15em] md:text-7xl',
|
||||
)}
|
||||
>
|
||||
{char}
|
||||
</motion.h1>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
const defaultVariants = {
|
||||
hidden: { y: 10 },
|
||||
visible: { y: -10 },
|
||||
};
|
||||
const combinedVariants = variant || defaultVariants;
|
||||
const characters = useMemo(() => word.split(""), [word]);
|
||||
return (
|
||||
<div className="flex justify-center space-x-2 overflow-hidden p-3">
|
||||
<AnimatePresence>
|
||||
{characters.map((char, i) => (
|
||||
<motion.h1
|
||||
key={i}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
exit="hidden"
|
||||
variants={combinedVariants}
|
||||
transition={{
|
||||
yoyo: Infinity,
|
||||
duration,
|
||||
delay: i * delay,
|
||||
}}
|
||||
className={ny(
|
||||
className,
|
||||
"font-display text-center text-4xl font-bold tracking-[-0.15em] md:text-7xl",
|
||||
)}
|
||||
>
|
||||
{char}
|
||||
</motion.h1>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WavyText
|
||||
export default WavyText;
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import type { Variants } from 'framer-motion'
|
||||
import { motion } from 'framer-motion'
|
||||
import { ny } from '@/lib/utils'
|
||||
import type { Variants } from "framer-motion";
|
||||
import { motion } from "framer-motion";
|
||||
import { ny } from "@/lib/utils";
|
||||
|
||||
interface WordPullUpProps {
|
||||
words: string
|
||||
delayMultiple?: number
|
||||
wrapperFramerProps?: Variants
|
||||
framerProps?: Variants
|
||||
className?: string
|
||||
words: string;
|
||||
delayMultiple?: number;
|
||||
wrapperFramerProps?: Variants;
|
||||
framerProps?: Variants;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function WordPullUp({
|
||||
words,
|
||||
wrapperFramerProps = {
|
||||
hidden: { opacity: 0 },
|
||||
show: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.2,
|
||||
},
|
||||
},
|
||||
},
|
||||
framerProps = {
|
||||
hidden: { y: 20, opacity: 0 },
|
||||
show: { y: 0, opacity: 1 },
|
||||
},
|
||||
className,
|
||||
words,
|
||||
wrapperFramerProps = {
|
||||
hidden: { opacity: 0 },
|
||||
show: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.2,
|
||||
},
|
||||
},
|
||||
},
|
||||
framerProps = {
|
||||
hidden: { y: 20, opacity: 0 },
|
||||
show: { y: 0, opacity: 1 },
|
||||
},
|
||||
className,
|
||||
}: WordPullUpProps) {
|
||||
return (
|
||||
<motion.h1
|
||||
variants={wrapperFramerProps}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
className={ny(
|
||||
'font-display text-center text-4xl font-bold leading-[5rem] tracking-[-0.02em] drop-shadow-sm',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{words.split(' ').map((word, i) => (
|
||||
<motion.span
|
||||
key={i}
|
||||
variants={framerProps}
|
||||
style={{ display: 'inline-block', paddingRight: '8px' }}
|
||||
>
|
||||
{word === '' ? <span> </span> : word}
|
||||
</motion.span>
|
||||
))}
|
||||
</motion.h1>
|
||||
)
|
||||
return (
|
||||
<motion.h1
|
||||
variants={wrapperFramerProps}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
className={ny(
|
||||
"font-display text-center text-4xl font-bold leading-[5rem] tracking-[-0.02em] drop-shadow-sm",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{words.split(" ").map((word, i) => (
|
||||
<motion.span
|
||||
key={i}
|
||||
variants={framerProps}
|
||||
style={{ display: "inline-block", paddingRight: "8px" }}
|
||||
>
|
||||
{word === "" ? <span> </span> : word}
|
||||
</motion.span>
|
||||
))}
|
||||
</motion.h1>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user