style: replace animated grid with subtle horizontal lines in chart
Simple repeating horizontal lines at 40px intervals with 50% opacity, faded at top/bottom edges via CSS mask. No extra components needed.
This commit is contained in:
@@ -11,7 +11,6 @@ import { Checkbox } from '@ciphera-net/ui'
|
||||
import { ArrowUpRight, ArrowDownRight } from '@phosphor-icons/react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { AnimatedGridPattern } from '@/components/ui/animated-grid-pattern'
|
||||
|
||||
const ANNOTATION_COLORS: Record<string, string> = {
|
||||
deploy: '#3b82f6',
|
||||
@@ -386,16 +385,15 @@ export default function Chart({
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="px-2.5 py-4 relative overflow-hidden">
|
||||
<AnimatedGridPattern
|
||||
numSquares={30}
|
||||
maxOpacity={0.1}
|
||||
duration={3}
|
||||
repeatDelay={1}
|
||||
width={30}
|
||||
height={30}
|
||||
x={-1}
|
||||
y={-1}
|
||||
className="fill-neutral-400/10 stroke-neutral-400/15 dark:fill-neutral-500/10 dark:stroke-neutral-500/15 [mask-image:linear-gradient(to_bottom,transparent,white_20%,white_80%,transparent)]"
|
||||
{/* Subtle horizontal lines background */}
|
||||
<div
|
||||
className="pointer-events-none absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: 'repeating-linear-gradient(to bottom, transparent, transparent 39px, var(--chart-grid) 39px, var(--chart-grid) 40px)',
|
||||
opacity: 0.5,
|
||||
maskImage: 'linear-gradient(to bottom, transparent, black 15%, black 85%, transparent)',
|
||||
WebkitMaskImage: 'linear-gradient(to bottom, transparent, black 15%, black 85%, transparent)',
|
||||
}}
|
||||
/>
|
||||
{/* Toolbar */}
|
||||
<div className="flex items-center justify-between gap-3 mb-4 px-2">
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useId, useRef, useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface AnimatedGridPatternProps {
|
||||
width?: number;
|
||||
height?: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
strokeDasharray?: any;
|
||||
numSquares?: number;
|
||||
className?: string;
|
||||
maxOpacity?: number;
|
||||
duration?: number;
|
||||
repeatDelay?: number;
|
||||
}
|
||||
|
||||
export function AnimatedGridPattern({
|
||||
width = 40,
|
||||
height = 40,
|
||||
x = -1,
|
||||
y = -1,
|
||||
strokeDasharray = 0,
|
||||
numSquares = 50,
|
||||
className,
|
||||
maxOpacity = 0.5,
|
||||
duration = 4,
|
||||
repeatDelay = 0.5,
|
||||
...props
|
||||
}: AnimatedGridPatternProps) {
|
||||
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),
|
||||
];
|
||||
}
|
||||
|
||||
// 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,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
// 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 (let entry of entries) {
|
||||
setDimensions({
|
||||
width: entry.contentRect.width,
|
||||
height: entry.contentRect.height,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (containerRef.current) {
|
||||
resizeObserver.observe(containerRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (containerRef.current) {
|
||||
resizeObserver.unobserve(containerRef.current);
|
||||
}
|
||||
};
|
||||
}, [containerRef]);
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={containerRef}
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user