Files
awoooi/apps/web/src/components/ui/border-beam.tsx
OG T 196d269b92 feat: add all application source code
- apps/api: FastAPI backend with Dockerfile
- apps/web: Next.js frontend with Dockerfile
- apps/sensor: Signal collection agent
- packages: shared packages

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-22 18:57:44 +08:00

119 lines
3.1 KiB
TypeScript

'use client'
/**
* BorderBeam - Magic UI 流光邊框特效
* ===================================
* 用於 CRITICAL 風險等級的審核卡片
*
* Features:
* - 沿邊框流動的光束動畫
* - 可配置顏色、速度、寬度
* - 支援暫停/啟動
*/
import { cn } from '@/lib/utils'
import { type CSSProperties, type HTMLAttributes } from 'react'
// =============================================================================
// Types
// =============================================================================
export interface BorderBeamProps extends HTMLAttributes<HTMLDivElement> {
/** 動畫持續時間 (秒) */
duration?: number
/** 邊框寬度 (px) */
borderWidth?: number
/** 光束顏色 - 起始色 */
colorFrom?: string
/** 光束顏色 - 結束色 */
colorTo?: string
/** 動畫延遲 (秒) */
delay?: number
/** 光束尺寸 (px) */
size?: number
/** 是否暫停動畫 */
paused?: boolean
}
// =============================================================================
// Component
// =============================================================================
export function BorderBeam({
className,
duration = 6,
borderWidth = 2,
colorFrom = '#FF3300',
colorTo = '#FF6B35',
delay = 0,
size = 200,
paused = false,
...props
}: BorderBeamProps) {
return (
<div
className={cn(
'pointer-events-none absolute inset-0 rounded-xl overflow-hidden',
'[mask-composite:intersect] [mask:linear-gradient(#fff_0_0)_content-box,linear-gradient(#fff_0_0)]',
className
)}
style={
{
'--duration': `${duration}s`,
'--delay': `${delay}s`,
'--color-from': colorFrom,
'--color-to': colorTo,
'--size': `${size}px`,
padding: `${borderWidth}px`,
} as CSSProperties
}
{...props}
>
<div
className={cn(
'absolute inset-0',
'bg-[conic-gradient(from_calc(var(--start,_0)*1turn),transparent_0%,var(--color-from)_10%,var(--color-to)_20%,transparent_30%)]',
'[animation:border-beam_var(--duration)_linear_infinite]',
paused && '[animation-play-state:paused]'
)}
style={{
width: `calc(100% + var(--size))`,
height: `calc(100% + var(--size))`,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
animationDelay: `var(--delay)`,
}}
/>
</div>
)
}
/**
* CriticalGlow - CRITICAL 風險等級的脈動光暈
*/
export function CriticalGlow({
className,
intensity = 'medium',
...props
}: HTMLAttributes<HTMLDivElement> & { intensity?: 'low' | 'medium' | 'high' }) {
const intensityStyles = {
low: 'opacity-20',
medium: 'opacity-40',
high: 'opacity-60',
}
return (
<div
className={cn(
'absolute inset-0 rounded-xl pointer-events-none',
'bg-gradient-to-br from-status-critical/20 via-transparent to-status-critical/10',
intensityStyles[intensity],
'animate-pulse',
className
)}
{...props}
/>
)
}