Skip to content

Commit 4e2bead

Browse files
committed
feat(block-art): refactor BlockArt component to support customizable grid dimensions and animation properties
1 parent d0d51f5 commit 4e2bead

2 files changed

Lines changed: 77 additions & 14 deletions

File tree

components/shared/footer-content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { cn } from "@/lib/utils";
22
import type { ReactNode } from "react";
3-
import BlockArt from "../ui/block-art";
3+
import { BlockArt } from "../ui/block-art";
44
import { Particles } from "../ui/particles";
55

66
type Props = {

components/ui/block-art.tsx

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,86 @@
1+
"use client";
2+
13
import { cn } from "@/lib/utils";
4+
import { useEffect, useState } from "react";
25

36
type Props = {
4-
length?: number;
7+
gridCols?: number;
8+
gridRows?: number;
59
className?: string;
10+
animationColor?: string;
11+
animatedCellCount?: number;
12+
animationInterval?: number;
613
};
714

8-
export default function BlockArt({ length = 20, className }: Props) {
15+
const DEFAULT_ANIMATION_COLOR = "bg-secondary";
16+
const DEFAULT_ANIMATED_CELL_COUNT = 15;
17+
const DEFAULT_ANIMATION_INTERVAL = 1000; // ms
18+
19+
export function BlockArt({
20+
gridCols = 16,
21+
gridRows = 8,
22+
className,
23+
animationColor = DEFAULT_ANIMATION_COLOR,
24+
animatedCellCount = DEFAULT_ANIMATED_CELL_COUNT,
25+
animationInterval = DEFAULT_ANIMATION_INTERVAL,
26+
}: Props) {
27+
const COLS = Math.max(1, Math.floor(gridCols));
28+
const ROWS = Math.max(1, Math.floor(gridRows));
29+
const totalCells = COLS * ROWS;
30+
31+
const [activeIndices, setActiveIndices] = useState<Set<number>>(new Set());
32+
33+
useEffect(() => {
34+
if (totalCells === 0 || animatedCellCount === 0) {
35+
setActiveIndices(new Set());
36+
return;
37+
}
38+
39+
const effectiveAnimatedCellCount = Math.min(animatedCellCount, totalCells);
40+
41+
const intervalId = setInterval(() => {
42+
const newActiveIndices = new Set<number>();
43+
const availableIndices = Array.from({ length: totalCells }, (_, i) => i);
44+
45+
for (let i = availableIndices.length - 1; i > 0; i--) {
46+
const j = Math.floor(Math.random() * (i + 1));
47+
[availableIndices[i], availableIndices[j]] = [
48+
availableIndices[j],
49+
availableIndices[i],
50+
];
51+
}
52+
53+
for (let i = 0; i < effectiveAnimatedCellCount; i++) {
54+
newActiveIndices.add(availableIndices[i]);
55+
}
56+
setActiveIndices(newActiveIndices);
57+
}, animationInterval);
58+
59+
return () => clearInterval(intervalId);
60+
}, [totalCells, animatedCellCount, animationInterval]);
61+
962
return (
10-
<section className={cn("bg-foreground pb-30", className)}>
11-
{Array.from({ length }).map((_, i) => (
12-
<div
13-
key={i}
14-
className="bg-background"
15-
style={{
16-
marginTop: `${0 + i}px`,
17-
height: `${20 - i}px`,
18-
}}
19-
/>
20-
))}
63+
<section className={cn("h-full w-full bg-border py-px", className)}>
64+
<div
65+
className="h-full w-full"
66+
style={{
67+
display: "grid",
68+
gridTemplateColumns: `repeat(${COLS}, minmax(0, 1fr))`,
69+
gridTemplateRows: `repeat(${ROWS}, auto)`,
70+
gap: "1px",
71+
}}
72+
>
73+
{Array.from({ length: totalCells }).map((_, i) => (
74+
<div
75+
key={i}
76+
className={cn(
77+
"transition-colors duration-300 ease-in-out hover:bg-primary",
78+
activeIndices.has(i) ? animationColor : "bg-background"
79+
)}
80+
style={{ aspectRatio: "1 / 1" }}
81+
/>
82+
))}
83+
</div>
2184
</section>
2285
);
2386
}

0 commit comments

Comments
 (0)