Files
EvoBGP/apps/web/src/components/dashboard/card-dot-field.tsx
T
Denozordec 538daea0f1
CI / changes (push) Successful in 11s
CI / commitlint (push) Has been skipped
CI / openapi (push) Has been skipped
CI / web (push) Successful in 1m7s
CI / go (push) Has been skipped
CI / bird2 (push) Has been skipped
CI / release (push) Successful in 4m21s
refactor: update dashboard components for improved layout and functionality
Refactored the DashboardQuickLinks component to utilize the new DashboardQuickLinkCard for better organization and presentation. Enhanced the DashboardComponent to integrate quick links within a DashboardFramePanel, improving the overall user experience. Updated the metric-tile component import path for consistency. Removed unnecessary imports from the separator component to streamline the codebase.
2026-07-09 18:49:55 +07:00

88 lines
2.3 KiB
TypeScript

'use client'
import { useEffect, useRef } from 'react'
import { cn } from '@evobgp/ui/lib/utils'
function grain(i: number, j: number) {
const n = Math.sin(i * 127.1 + j * 311.7) * 43758.5453
return n - Math.floor(n)
}
/**
* Static dot field adapted from REUI card-17: theme-aware canvas dots,
* repainted on resize and theme change.
*/
export function CardDotField({ className }: { className?: string }) {
const canvasRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext('2d')
if (!ctx) return
const GAP = 3
const DOT = 1.5
const BASE = 0.05
const PEAK = 0.32
const draw = () => {
const rect = canvas.getBoundingClientRect()
if (!rect.width || !rect.height) return
const dpr = Math.min(window.devicePixelRatio || 1, 2)
canvas.width = Math.round(rect.width * dpr)
canvas.height = Math.round(rect.height * dpr)
ctx.fillStyle = getComputedStyle(canvas).color || 'rgb(115,115,115)'
ctx.fillRect(0, 0, 1, 1)
const px = ctx.getImageData(0, 0, 1, 1).data
const color = `rgb(${px[0]}, ${px[1]}, ${px[2]})`
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
ctx.clearRect(0, 0, rect.width, rect.height)
ctx.fillStyle = color
const cols = Math.ceil(rect.width / GAP) + 1
const rows = Math.ceil(rect.height / GAP) + 1
for (let i = 0; i < cols; i++) {
const x = i * GAP
for (let j = 0; j < rows; j++) {
const q = grain(i, j)
const amp = 0.7 + 0.6 * grain(j * 2 + 1, i * 2 + 1)
let a = (BASE + (PEAK - BASE) * q * q) * amp
if (a > 1) a = 1
ctx.globalAlpha = a
ctx.fillRect(x, j * GAP, DOT, DOT)
}
}
ctx.globalAlpha = 1
}
draw()
const resizeObserver = new ResizeObserver(() => draw())
resizeObserver.observe(canvas)
const themeObserver = new MutationObserver(() => draw())
themeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class', 'style'],
})
return () => {
resizeObserver.disconnect()
themeObserver.disconnect()
}
}, [])
return (
<canvas
ref={canvasRef}
aria-hidden="true"
className={cn('pointer-events-none absolute inset-0 h-full w-full', className)}
/>
)
}