아 404 컴포넌트 날먹 개꿀이죠~
This commit is contained in:
parent
b67d8f84af
commit
14a04aa3e7
3 changed files with 218 additions and 4 deletions
|
|
@ -1,8 +1,17 @@
|
||||||
|
import { CommitsGrid } from "@/components/commits-grid"
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="flex flex-col justify-center items-center h-screen gap-8">
|
||||||
<h1>404 - Not Found</h1>
|
<CommitsGrid text="404" />
|
||||||
<p>The page you are looking for does not exist.</p>
|
<p className="text-2xl">The page you are looking for does not exist.</p>
|
||||||
|
<Button variant="outline" onClick={() => navigate("/")}>
|
||||||
|
Go to Home
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
179
src/components/commits-grid.tsx
Normal file
179
src/components/commits-grid.tsx
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import type { CSSProperties } from "react";
|
||||||
|
|
||||||
|
export const CommitsGrid = ({ text }: { text: string }) => {
|
||||||
|
const cleanString = (str: string): string => {
|
||||||
|
const upperStr = str.toUpperCase();
|
||||||
|
|
||||||
|
const withoutAccents = upperStr
|
||||||
|
.normalize("NFD")
|
||||||
|
.replace(/[\u0300-\u036f]/g, "");
|
||||||
|
|
||||||
|
const allowedChars = Object.keys(letterPatterns);
|
||||||
|
return withoutAccents
|
||||||
|
.split("")
|
||||||
|
.filter((char) => allowedChars.includes(char))
|
||||||
|
.join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateHighlightedCells = (text: string) => {
|
||||||
|
const cleanedText = cleanString(text);
|
||||||
|
|
||||||
|
const width = Math.max(cleanedText.length * 6, 6) + 1;
|
||||||
|
|
||||||
|
let currentPosition = 1; // we start at 1 to leave space for the top border
|
||||||
|
const highlightedCells: number[] = [];
|
||||||
|
|
||||||
|
cleanedText
|
||||||
|
.toUpperCase()
|
||||||
|
.split("")
|
||||||
|
.forEach((char) => {
|
||||||
|
if (letterPatterns[char]) {
|
||||||
|
const pattern = letterPatterns[char].map((pos) => {
|
||||||
|
const row = Math.floor(pos / 50);
|
||||||
|
const col = pos % 50;
|
||||||
|
return (row + 1) * width + col + currentPosition;
|
||||||
|
});
|
||||||
|
highlightedCells.push(...pattern);
|
||||||
|
}
|
||||||
|
currentPosition += 6;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
cells: highlightedCells,
|
||||||
|
width,
|
||||||
|
height: 9, // 7+2 for the top and bottom borders
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
cells: highlightedCells,
|
||||||
|
width: gridWidth,
|
||||||
|
height: gridHeight,
|
||||||
|
} = generateHighlightedCells(text);
|
||||||
|
|
||||||
|
const getRandomColor = () => {
|
||||||
|
const commitColors = [
|
||||||
|
"#48d55d",
|
||||||
|
"#016d32",
|
||||||
|
"#0d4429"
|
||||||
|
];
|
||||||
|
const randomIndex = Math.floor(Math.random() * commitColors.length);
|
||||||
|
return commitColors[randomIndex];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRandomDelay = () => `${(Math.random() * 0.6).toFixed(1)}s`;
|
||||||
|
const getRandomFlash = () => +(Math.random() < 0.3);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className="w-full max-w-xl bg-card border grid p-1.5 sm:p-3 gap-0.5 sm:gap-1 rounded-[10px] sm:rounded-[15px]"
|
||||||
|
style={{
|
||||||
|
gridTemplateColumns: `repeat(${gridWidth}, minmax(0, 1fr))`,
|
||||||
|
gridTemplateRows: `repeat(${gridHeight}, minmax(0, 1fr))`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Array.from({ length: gridWidth * gridHeight }).map((_, index) => {
|
||||||
|
const isHighlighted = highlightedCells.includes(index);
|
||||||
|
const shouldFlash = !isHighlighted && getRandomFlash();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={cn(
|
||||||
|
`border h-full w-full aspect-square rounded-[4px] sm:rounded-[3px]`,
|
||||||
|
isHighlighted ? "animate-highlight" : "",
|
||||||
|
shouldFlash ? "animate-flash" : "",
|
||||||
|
!isHighlighted && !shouldFlash ? "bg-card" : ""
|
||||||
|
)}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
animationDelay: getRandomDelay(),
|
||||||
|
"--highlight": getRandomColor(),
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const letterPatterns: { [key: string]: number[] } = {
|
||||||
|
A: [
|
||||||
|
1, 2, 3, 50, 100, 150, 200, 250, 300, 54, 104, 154, 204, 254, 304, 151, 152,
|
||||||
|
153,
|
||||||
|
],
|
||||||
|
B: [
|
||||||
|
0, 1, 2, 3, 4, 50, 100, 150, 151, 200, 250, 300, 301, 302, 303, 304, 54,
|
||||||
|
104, 152, 153, 204, 254, 303,
|
||||||
|
],
|
||||||
|
C: [0, 1, 2, 3, 4, 50, 100, 150, 200, 250, 300, 301, 302, 303, 304],
|
||||||
|
D: [
|
||||||
|
0, 1, 2, 3, 50, 100, 150, 200, 250, 300, 301, 302, 54, 104, 154, 204, 254,
|
||||||
|
303,
|
||||||
|
],
|
||||||
|
E: [0, 1, 2, 3, 4, 50, 100, 150, 200, 250, 300, 301, 302, 303, 304, 151, 152],
|
||||||
|
F: [0, 1, 2, 3, 4, 50, 100, 150, 200, 250, 300, 151, 152, 153],
|
||||||
|
G: [
|
||||||
|
0, 1, 2, 3, 4, 50, 100, 150, 200, 250, 300, 301, 302, 303, 153, 204, 154,
|
||||||
|
304, 254,
|
||||||
|
],
|
||||||
|
H: [
|
||||||
|
0, 50, 100, 150, 200, 250, 300, 151, 152, 153, 4, 54, 104, 154, 204, 254,
|
||||||
|
304,
|
||||||
|
],
|
||||||
|
I: [0, 1, 2, 3, 4, 52, 102, 152, 202, 252, 300, 301, 302, 303, 304],
|
||||||
|
J: [0, 1, 2, 3, 4, 52, 102, 152, 202, 250, 252, 302, 300, 301],
|
||||||
|
K: [0, 4, 50, 100, 150, 200, 250, 300, 151, 152, 103, 54, 203, 254, 304],
|
||||||
|
L: [0, 50, 100, 150, 200, 250, 300, 301, 302, 303, 304],
|
||||||
|
M: [
|
||||||
|
0, 50, 100, 150, 200, 250, 300, 51, 102, 53, 4, 54, 104, 154, 204, 254, 304,
|
||||||
|
],
|
||||||
|
N: [
|
||||||
|
0, 50, 100, 150, 200, 250, 300, 51, 102, 153, 204, 4, 54, 104, 154, 204,
|
||||||
|
254, 304,
|
||||||
|
],
|
||||||
|
Ñ: [
|
||||||
|
0, 50, 100, 150, 200, 250, 300, 51, 102, 153, 204, 4, 54, 104, 154, 204,
|
||||||
|
254, 304,
|
||||||
|
],
|
||||||
|
O: [1, 2, 3, 50, 100, 150, 200, 250, 301, 302, 303, 54, 104, 154, 204, 254],
|
||||||
|
P: [0, 50, 100, 150, 200, 250, 300, 1, 2, 3, 54, 104, 151, 152, 153],
|
||||||
|
Q: [
|
||||||
|
1, 2, 3, 50, 100, 150, 200, 250, 301, 302, 54, 104, 154, 204, 202, 253, 304,
|
||||||
|
],
|
||||||
|
R: [
|
||||||
|
0, 50, 100, 150, 200, 250, 300, 1, 2, 3, 54, 104, 151, 152, 153, 204, 254,
|
||||||
|
304,
|
||||||
|
],
|
||||||
|
S: [1, 2, 3, 4, 50, 100, 151, 152, 153, 204, 254, 300, 301, 302, 303],
|
||||||
|
T: [0, 1, 2, 3, 4, 52, 102, 152, 202, 252, 302],
|
||||||
|
U: [0, 50, 100, 150, 200, 250, 301, 302, 303, 4, 54, 104, 154, 204, 254],
|
||||||
|
V: [0, 50, 100, 150, 200, 251, 302, 4, 54, 104, 154, 204, 253],
|
||||||
|
W: [
|
||||||
|
0, 50, 100, 150, 200, 250, 301, 152, 202, 252, 4, 54, 104, 154, 204, 254,
|
||||||
|
303,
|
||||||
|
],
|
||||||
|
X: [0, 50, 203, 254, 304, 4, 54, 152, 101, 103, 201, 250, 300],
|
||||||
|
Y: [0, 50, 101, 152, 202, 252, 302, 4, 54, 103],
|
||||||
|
Z: [0, 1, 2, 3, 4, 54, 103, 152, 201, 250, 300, 301, 302, 303, 304],
|
||||||
|
"0": [1, 2, 3, 50, 100, 150, 200, 250, 301, 302, 303, 54, 104, 154, 204, 254],
|
||||||
|
"1": [1, 52, 102, 152, 202, 252, 302, 0, 2, 300, 301, 302, 303, 304],
|
||||||
|
"2": [0, 1, 2, 3, 54, 104, 152, 153, 201, 250, 300, 301, 302, 303, 304],
|
||||||
|
"3": [0, 1, 2, 3, 54, 104, 152, 153, 204, 254, 300, 301, 302, 303],
|
||||||
|
"4": [0, 50, 100, 150, 4, 54, 104, 151, 152, 153, 154, 204, 254, 304],
|
||||||
|
"5": [0, 1, 2, 3, 4, 50, 100, 151, 152, 153, 204, 254, 300, 301, 302, 303],
|
||||||
|
"6": [
|
||||||
|
1, 2, 3, 50, 100, 150, 151, 152, 153, 200, 250, 301, 302, 204, 254, 303,
|
||||||
|
],
|
||||||
|
"7": [0, 1, 2, 3, 4, 54, 103, 152, 201, 250, 300],
|
||||||
|
"8": [
|
||||||
|
1, 2, 3, 50, 100, 151, 152, 153, 200, 250, 301, 302, 303, 54, 104, 204, 254,
|
||||||
|
],
|
||||||
|
"9": [1, 2, 3, 50, 100, 151, 152, 153, 154, 204, 254, 304, 54, 104],
|
||||||
|
" ": [],
|
||||||
|
};
|
||||||
|
|
@ -142,3 +142,29 @@
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--animate-highlight: highlight 0.6s ease forwards;
|
||||||
|
--animate-flash: flash 0.6s ease forwards;
|
||||||
|
|
||||||
|
@keyframes highlight {
|
||||||
|
0% {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: var(--highlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes flash {
|
||||||
|
0% {
|
||||||
|
background-color: hsl(var(--card));
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-color: var(--highlight);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: hsl(var(--card));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue