update border
This commit is contained in:
@@ -19,6 +19,6 @@
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {
|
||||
"@aceternity": "https://ui.aceternity.com/registry/{name}.json"
|
||||
"@magicui": "https://magicui.design/r/{name}.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
"use client";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
|
||||
import { motion } from "motion/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type Direction = "TOP" | "LEFT" | "BOTTOM" | "RIGHT";
|
||||
|
||||
export function HoverBorderGradient({
|
||||
children,
|
||||
containerClassName,
|
||||
className,
|
||||
as: Tag = "button",
|
||||
duration = 1,
|
||||
clockwise = true,
|
||||
...props
|
||||
}: React.PropsWithChildren<
|
||||
{
|
||||
as?: React.ElementType;
|
||||
containerClassName?: string;
|
||||
className?: string;
|
||||
duration?: number;
|
||||
clockwise?: boolean;
|
||||
} & React.HTMLAttributes<HTMLElement>
|
||||
>) {
|
||||
const [hovered, setHovered] = useState<boolean>(false);
|
||||
const [direction, setDirection] = useState<Direction>("TOP");
|
||||
|
||||
const rotateDirection = (currentDirection: Direction): Direction => {
|
||||
const directions: Direction[] = ["TOP", "LEFT", "BOTTOM", "RIGHT"];
|
||||
const currentIndex = directions.indexOf(currentDirection);
|
||||
const nextIndex = clockwise
|
||||
? (currentIndex - 1 + directions.length) % directions.length
|
||||
: (currentIndex + 1) % directions.length;
|
||||
return directions[nextIndex];
|
||||
};
|
||||
|
||||
const movingMap: Record<Direction, string> = {
|
||||
TOP: "radial-gradient(20.7% 50% at 50% 0%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)",
|
||||
LEFT: "radial-gradient(16.6% 43.1% at 0% 50%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)",
|
||||
BOTTOM:
|
||||
"radial-gradient(20.7% 50% at 50% 100%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)",
|
||||
RIGHT:
|
||||
"radial-gradient(16.2% 41.199999999999996% at 100% 50%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)",
|
||||
};
|
||||
|
||||
const highlight =
|
||||
"radial-gradient(75% 181.15942028985506% at 50% 50%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)";
|
||||
|
||||
useEffect(() => {
|
||||
if (!hovered) {
|
||||
const interval = setInterval(() => {
|
||||
setDirection((prevState) => rotateDirection(prevState));
|
||||
}, duration * 1000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [hovered]);
|
||||
return (
|
||||
<Tag
|
||||
onMouseEnter={(event: React.MouseEvent<HTMLDivElement>) => {
|
||||
setHovered(true);
|
||||
}}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
className={cn(
|
||||
"relative flex rounded-full border content-center bg-black/20 hover:bg-black/10 transition duration-500 dark:bg-white/20 items-center flex-col flex-nowrap gap-10 h-min justify-center overflow-visible p-px decoration-clone w-fit",
|
||||
containerClassName
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"w-auto text-white z-10 bg-black px-4 py-2 rounded-[inherit]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<motion.div
|
||||
className={cn(
|
||||
"flex-none inset-0 overflow-hidden absolute z-0 rounded-[inherit]"
|
||||
)}
|
||||
style={{
|
||||
filter: "blur(2px)",
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
initial={{ background: movingMap[direction] }}
|
||||
animate={{
|
||||
background: hovered
|
||||
? [movingMap[direction], highlight]
|
||||
: movingMap[direction],
|
||||
}}
|
||||
transition={{ ease: "linear", duration: duration ?? 1 }}
|
||||
/>
|
||||
<div className="bg-black absolute z-1 flex-none inset-[2px] rounded-[100px]" />
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
63
packages/ui/src/components/ui/shine-border.tsx
Normal file
63
packages/ui/src/components/ui/shine-border.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
interface ShineBorderProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* Width of the border in pixels
|
||||
* @default 1
|
||||
*/
|
||||
borderWidth?: number
|
||||
/**
|
||||
* Duration of the animation in seconds
|
||||
* @default 14
|
||||
*/
|
||||
duration?: number
|
||||
/**
|
||||
* Color of the border, can be a single color or an array of colors
|
||||
* @default "#000000"
|
||||
*/
|
||||
shineColor?: string | string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Shine Border
|
||||
*
|
||||
* An animated background border effect component with configurable properties.
|
||||
*/
|
||||
export function ShineBorder({
|
||||
borderWidth = 1,
|
||||
duration = 14,
|
||||
shineColor = "#000000",
|
||||
className,
|
||||
style,
|
||||
...props
|
||||
}: ShineBorderProps) {
|
||||
return (
|
||||
<div
|
||||
style={
|
||||
{
|
||||
"--border-width": `${borderWidth}px`,
|
||||
"--duration": `${duration}s`,
|
||||
backgroundImage: `radial-gradient(transparent,transparent, ${
|
||||
Array.isArray(shineColor) ? shineColor.join(",") : shineColor
|
||||
},transparent,transparent)`,
|
||||
backgroundSize: "300% 300%",
|
||||
mask: `linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)`,
|
||||
WebkitMask: `linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)`,
|
||||
WebkitMaskComposite: "xor",
|
||||
maskComposite: "exclude",
|
||||
padding: "var(--border-width)",
|
||||
...style,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
className={cn(
|
||||
"motion-safe:animate-shine pointer-events-none absolute inset-0 size-full rounded-[inherit] will-change-[background-position]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -57,6 +57,22 @@ body {
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--animate-shine:
|
||||
shine var(--duration) infinite linear;
|
||||
@keyframes shine {
|
||||
0% {
|
||||
background-position:
|
||||
0% 0%;
|
||||
}
|
||||
50% {
|
||||
background-position:
|
||||
100% 100%;
|
||||
}
|
||||
to {
|
||||
background-position:
|
||||
0% 0%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
@@ -135,4 +151,4 @@ body {
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user