Format codebase

This commit is contained in:
Thomas Camlong
2025-04-17 15:12:28 +02:00
parent 5be07bbfb8
commit 16d6e34c3f
15 changed files with 743 additions and 1001 deletions

View File

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

View File

@@ -1,43 +1,37 @@
"use client";
"use client"
import React, { memo } from "react";
import type React from "react"
import { memo } from "react"
interface AuroraTextProps {
children: React.ReactNode;
className?: string;
colors?: string[];
speed?: number;
children: React.ReactNode
className?: string
colors?: string[]
speed?: number
}
export const AuroraText = memo(
({
children,
className = "",
colors = ["#FF0080", "#7928CA", "#0070F3", "#38bdf8"],
speed = 1,
}: AuroraTextProps) => {
const gradientStyle = {
backgroundImage: `linear-gradient(135deg, ${colors.join(", ")}, ${
colors[0]
})`,
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
animationDuration: `${10 / speed}s`,
};
({ children, className = "", colors = ["#FF0080", "#7928CA", "#0070F3", "#38bdf8"], speed = 1 }: AuroraTextProps) => {
const gradientStyle = {
backgroundImage: `linear-gradient(135deg, ${colors.join(", ")}, ${colors[0]})`,
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
animationDuration: `${10 / speed}s`,
}
return (
<span className={`relative inline-block ${className}`}>
<span className="sr-only">{children}</span>
<span
className="relative animate-aurora bg-[length:200%_auto] bg-clip-text text-transparent"
style={gradientStyle}
aria-hidden="true"
>
{children}
</span>
</span>
);
},
);
return (
<span className={`relative inline-block ${className}`}>
<span className="sr-only">{children}</span>
<span
className="relative animate-aurora bg-[length:200%_auto] bg-clip-text text-transparent"
style={gradientStyle}
aria-hidden="true"
>
{children}
</span>
</span>
)
},
)
AuroraText.displayName = "AuroraText";
AuroraText.displayName = "AuroraText"

View File

@@ -1,35 +1,31 @@
import React from "react";
import { ArrowRight } from "lucide-react";
import { cn } from "@/lib/utils";
import { cn } from "@/lib/utils"
import { ArrowRight } from "lucide-react"
import React from "react"
interface InteractiveHoverButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
interface InteractiveHoverButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
export const InteractiveHoverButton = React.forwardRef<
HTMLButtonElement,
InteractiveHoverButtonProps
>(({ children, className, ...props }, ref) => {
return (
<button
ref={ref}
className={cn(
"group relative w-auto cursor-pointer overflow-hidden rounded-full border bg-background p-2 px-6 text-center font-semibold",
className,
)}
{...props}
>
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-primary transition-all duration-300 group-hover:scale-[100.8]"></div>
<span className="inline-block transition-all duration-300 group-hover:translate-x-12 group-hover:opacity-0">
{children}
</span>
</div>
<div className="absolute top-0 z-10 flex h-full w-full translate-x-12 items-center justify-center gap-2 text-primary-foreground opacity-0 transition-all duration-300 group-hover:-translate-x-5 group-hover:opacity-100">
<span>{children}</span>
<ArrowRight />
</div>
</button>
);
});
export const InteractiveHoverButton = React.forwardRef<HTMLButtonElement, InteractiveHoverButtonProps>(
({ children, className, ...props }, ref) => {
return (
<button
ref={ref}
className={cn(
"group relative w-auto cursor-pointer overflow-hidden rounded-full border bg-background p-2 px-6 text-center font-semibold",
className,
)}
{...props}
>
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-primary transition-all duration-300 group-hover:scale-[100.8]" />
<span className="inline-block transition-all duration-300 group-hover:translate-x-12 group-hover:opacity-0">{children}</span>
</div>
<div className="absolute top-0 z-10 flex h-full w-full translate-x-12 items-center justify-center gap-2 text-primary-foreground opacity-0 transition-all duration-300 group-hover:-translate-x-5 group-hover:opacity-100">
<span>{children}</span>
<ArrowRight />
</div>
</button>
)
},
)
InteractiveHoverButton.displayName = "InteractiveHoverButton";
InteractiveHoverButton.displayName = "InteractiveHoverButton"

View File

@@ -1,108 +1,106 @@
"use client";
"use client"
import { motion, useMotionTemplate, useMotionValue } from "motion/react";
import React, { useCallback, useEffect, useRef } from "react";
import { motion, useMotionTemplate, useMotionValue } from "motion/react"
import type React from "react"
import { useCallback, useEffect, useRef } from "react"
import { cn } from "@/lib/utils";
import { cn } from "@/lib/utils"
interface MagicCardProps {
children?: React.ReactNode;
className?: string;
gradientSize?: number;
gradientColor?: string;
gradientOpacity?: number;
gradientFrom?: string;
gradientTo?: string;
children?: React.ReactNode
className?: string
gradientSize?: number
gradientColor?: string
gradientOpacity?: number
gradientFrom?: string
gradientTo?: string
}
export function MagicCard({
children,
className,
gradientSize = 200,
gradientColor = "",
gradientOpacity = 0.8,
gradientFrom = "#ff0a54",
gradientTo = "#f9bec7",
children,
className,
gradientSize = 200,
gradientColor = "",
gradientOpacity = 0.8,
gradientFrom = "#ff0a54",
gradientTo = "#f9bec7",
}: MagicCardProps) {
const cardRef = useRef<HTMLDivElement>(null);
const mouseX = useMotionValue(-gradientSize);
const mouseY = useMotionValue(-gradientSize);
const cardRef = useRef<HTMLDivElement>(null)
const mouseX = useMotionValue(-gradientSize)
const mouseY = useMotionValue(-gradientSize)
const handleMouseMove = useCallback(
(e: MouseEvent) => {
if (cardRef.current) {
const { left, top } = cardRef.current.getBoundingClientRect();
const clientX = e.clientX;
const clientY = e.clientY;
mouseX.set(clientX - left);
mouseY.set(clientY - top);
}
},
[mouseX, mouseY],
);
const handleMouseMove = useCallback(
(e: MouseEvent) => {
if (cardRef.current) {
const { left, top } = cardRef.current.getBoundingClientRect()
const clientX = e.clientX
const clientY = e.clientY
mouseX.set(clientX - left)
mouseY.set(clientY - top)
}
},
[mouseX, mouseY],
)
const handleMouseOut = useCallback(
(e: MouseEvent) => {
if (!e.relatedTarget) {
document.removeEventListener("mousemove", handleMouseMove);
mouseX.set(-gradientSize);
mouseY.set(-gradientSize);
}
},
[handleMouseMove, mouseX, gradientSize, mouseY],
);
const handleMouseOut = useCallback(
(e: MouseEvent) => {
if (!e.relatedTarget) {
document.removeEventListener("mousemove", handleMouseMove)
mouseX.set(-gradientSize)
mouseY.set(-gradientSize)
}
},
[handleMouseMove, mouseX, gradientSize, mouseY],
)
const handleMouseEnter = useCallback(() => {
document.addEventListener("mousemove", handleMouseMove);
mouseX.set(-gradientSize);
mouseY.set(-gradientSize);
}, [handleMouseMove, mouseX, gradientSize, mouseY]);
const handleMouseEnter = useCallback(() => {
document.addEventListener("mousemove", handleMouseMove)
mouseX.set(-gradientSize)
mouseY.set(-gradientSize)
}, [handleMouseMove, mouseX, gradientSize, mouseY])
useEffect(() => {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseout", handleMouseOut);
document.addEventListener("mouseenter", handleMouseEnter);
useEffect(() => {
document.addEventListener("mousemove", handleMouseMove)
document.addEventListener("mouseout", handleMouseOut)
document.addEventListener("mouseenter", handleMouseEnter)
return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseout", handleMouseOut);
document.removeEventListener("mouseenter", handleMouseEnter);
};
}, [handleMouseEnter, handleMouseMove, handleMouseOut]);
return () => {
document.removeEventListener("mousemove", handleMouseMove)
document.removeEventListener("mouseout", handleMouseOut)
document.removeEventListener("mouseenter", handleMouseEnter)
}
}, [handleMouseEnter, handleMouseMove, handleMouseOut])
useEffect(() => {
mouseX.set(-gradientSize);
mouseY.set(-gradientSize);
}, [gradientSize, mouseX, mouseY]);
useEffect(() => {
mouseX.set(-gradientSize)
mouseY.set(-gradientSize)
}, [gradientSize, mouseX, mouseY])
return (
<div
ref={cardRef}
className={cn("group relative rounded-[inherit]", className)}
>
<motion.div
className="pointer-events-none absolute inset-0 rounded-[inherit] bg-border duration-300 group-hover:opacity-100"
style={{
background: useMotionTemplate`
return (
<div ref={cardRef} className={cn("group relative rounded-[inherit]", className)}>
<motion.div
className="pointer-events-none absolute inset-0 rounded-[inherit] bg-border duration-300 group-hover:opacity-100"
style={{
background: useMotionTemplate`
radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px,
${gradientFrom},
${gradientTo},
var(--border) 100%
)
`,
}}
/>
<div className="absolute inset-px rounded-[inherit] bg-background" />
<motion.div
className="pointer-events-none absolute inset-px rounded-[inherit] opacity-0 transition-opacity duration-300 group-hover:opacity-100"
style={{
background: useMotionTemplate`
}}
/>
<div className="absolute inset-px rounded-[inherit] bg-background" />
<motion.div
className="pointer-events-none absolute inset-px rounded-[inherit] opacity-0 transition-opacity duration-300 group-hover:opacity-100"
style={{
background: useMotionTemplate`
radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px, ${gradientColor}, transparent 100%)
`,
opacity: gradientOpacity,
}}
/>
<div className="relative">{children}</div>
</div>
);
opacity: gradientOpacity,
}}
/>
<div className="relative">{children}</div>
</div>
)
}

View File

@@ -1,73 +1,73 @@
import { cn } from "@/lib/utils";
import { ComponentPropsWithoutRef } from "react";
import { cn } from "@/lib/utils"
import type { ComponentPropsWithoutRef } from "react"
interface MarqueeProps extends ComponentPropsWithoutRef<"div"> {
/**
* Optional CSS class name to apply custom styles
*/
className?: string;
/**
* Whether to reverse the animation direction
* @default false
*/
reverse?: boolean;
/**
* Whether to pause the animation on hover
* @default false
*/
pauseOnHover?: boolean;
/**
* Content to be displayed in the marquee
*/
children: React.ReactNode;
/**
* Whether to animate vertically instead of horizontally
* @default false
*/
vertical?: boolean;
/**
* Number of times to repeat the content
* @default 4
*/
repeat?: number;
/**
* Optional CSS class name to apply custom styles
*/
className?: string
/**
* Whether to reverse the animation direction
* @default false
*/
reverse?: boolean
/**
* Whether to pause the animation on hover
* @default false
*/
pauseOnHover?: boolean
/**
* Content to be displayed in the marquee
*/
children: React.ReactNode
/**
* Whether to animate vertically instead of horizontally
* @default false
*/
vertical?: boolean
/**
* Number of times to repeat the content
* @default 4
*/
repeat?: number
}
export function Marquee({
className,
reverse = false,
pauseOnHover = false,
children,
vertical = false,
repeat = 4,
...props
className,
reverse = false,
pauseOnHover = false,
children,
vertical = false,
repeat = 4,
...props
}: MarqueeProps) {
return (
<div
{...props}
className={cn(
"group flex overflow-hidden p-2 [--duration:40s] [--gap:1rem] [gap:var(--gap)]",
{
"flex-row": !vertical,
"flex-col": vertical,
},
className,
)}
>
{Array(repeat)
.fill(0)
.map((_, i) => (
<div
key={i}
className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
"animate-marquee flex-row": !vertical,
"animate-marquee-vertical flex-col": vertical,
"group-hover:[animation-play-state:paused]": pauseOnHover,
"[animation-direction:reverse]": reverse,
})}
>
{children}
</div>
))}
</div>
);
return (
<div
{...props}
className={cn(
"group flex overflow-hidden p-2 [--duration:40s] [--gap:1rem] [gap:var(--gap)]",
{
"flex-row": !vertical,
"flex-col": vertical,
},
className,
)}
>
{Array(repeat)
.fill(0)
.map((_, i) => (
<div
key={i}
className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
"animate-marquee flex-row": !vertical,
"animate-marquee-vertical flex-col": vertical,
"group-hover:[animation-play-state:paused]": pauseOnHover,
"[animation-direction:reverse]": reverse,
})}
>
{children}
</div>
))}
</div>
)
}