feat: adição de novos ícones SVG e configuração do ambiente
- Adicionados ícones SVG para ChatGPT, Claude, Gemini e OpenRouter - Implementados ícones para modos claro e escuro do ChatGPT - Criado script de inicialização para PostgreSQL com extensão pgcrypto - Adicionado script de configuração de ambiente que faz backup do .env - Configurado tsconfig.json para TypeScript com opções de compilação
This commit is contained in:
101
components/magnet-lines.tsx
Normal file
101
components/magnet-lines.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React, { CSSProperties, useEffect, useRef } from "react";
|
||||
|
||||
interface MagnetLinesProps {
|
||||
rows?: number;
|
||||
columns?: number;
|
||||
containerSize?: string;
|
||||
lineColor?: string;
|
||||
lineWidth?: string;
|
||||
lineHeight?: string;
|
||||
baseAngle?: number;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
const MagnetLines: React.FC<MagnetLinesProps> = ({
|
||||
rows = 9,
|
||||
columns = 9,
|
||||
containerSize = "80vmin",
|
||||
lineColor = "#efefef",
|
||||
lineWidth = "1vmin",
|
||||
lineHeight = "6vmin",
|
||||
baseAngle = -10,
|
||||
className = "",
|
||||
style = {},
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const items = container.querySelectorAll<HTMLSpanElement>("span");
|
||||
|
||||
const onPointerMove = (pointer: { x: number; y: number }) => {
|
||||
items.forEach((item) => {
|
||||
const rect = item.getBoundingClientRect();
|
||||
const centerX = rect.x + rect.width / 2;
|
||||
const centerY = rect.y + rect.height / 2;
|
||||
|
||||
const b = pointer.x - centerX;
|
||||
const a = pointer.y - centerY;
|
||||
const c = Math.sqrt(a * a + b * b) || 1;
|
||||
const r =
|
||||
((Math.acos(b / c) * 180) / Math.PI) * (pointer.y > centerY ? 1 : -1);
|
||||
|
||||
item.style.setProperty("--rotate", `${r}deg`);
|
||||
});
|
||||
};
|
||||
|
||||
const handlePointerMove = (e: PointerEvent) => {
|
||||
onPointerMove({ x: e.x, y: e.y });
|
||||
};
|
||||
|
||||
window.addEventListener("pointermove", handlePointerMove);
|
||||
|
||||
if (items.length) {
|
||||
const middleIndex = Math.floor(items.length / 2);
|
||||
const rect = items[middleIndex].getBoundingClientRect();
|
||||
onPointerMove({ x: rect.x, y: rect.y });
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("pointermove", handlePointerMove);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const total = rows * columns;
|
||||
const spans = Array.from({ length: total }, (_, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="block origin-center"
|
||||
style={{
|
||||
backgroundColor: lineColor,
|
||||
width: lineWidth,
|
||||
height: lineHeight,
|
||||
// @ts-expect-error -- CSS custom property para controlar a rotação inline
|
||||
"--rotate": `${baseAngle}deg`,
|
||||
transform: "rotate(var(--rotate))",
|
||||
willChange: "transform",
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`grid place-items-center ${className}`}
|
||||
style={{
|
||||
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
||||
gridTemplateRows: `repeat(${rows}, 1fr)`,
|
||||
width: containerSize,
|
||||
height: containerSize,
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{spans}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MagnetLines;
|
||||
Reference in New Issue
Block a user