"use client";
import { useState, useEffect, useMemo } from "react";
import { createPortal } from "react-dom";
import {
Search,
X,
Grid,
List,
Filter,
AlertTriangle,
Info,
Tag,
Zap,
Sliders,
CheckCircle,
ExternalLink
} from "lucide-react";
const FILTER_CATEGORIES = [
{ id: "All", name: "Todos", color: "#8b5cf6" }, // purple
{ id: "GND", name: "Terra (GND)", color: "#64748b" }, // slate
{ id: "Power", name: "Alimentação", color: "#ef4444" }, // red
{ id: "GPIO", name: "GPIO", color: "#22c55e" }, // green
{ id: "Low Power", name: "GPIO LP", color: "#f59e0b" }, // amber
{ id: "ADC", name: "ADC", color: "#ec4899" }, // pink
{ id: "DAC", name: "DAC", color: "#a855f7" }, // purple
{ id: "Touch", name: "Touch", color: "#06b6d4" }, // cyan
{ id: "UART", name: "UART", color: "#3b82f6" }, // blue
{ id: "SPI", name: "SPI", color: "#f43f5e" }, // rose
{ id: "USB", name: "USB", color: "#a855f7" }, // purple
{ id: "Clock", name: "Clock", color: "#eab308" }, // yellow
{ id: "JTAG", name: "JTAG", color: "#6366f1" }, // indigo
{ id: "Strapping", name: "Inicialização", color: "#dc2626" }, // red
{ id: "DSI", name: "Display (DSI)", color: "#f97316" }, // orange
{ id: "CSI", name: "Câmera (CSI)", color: "#14b8a6" }, // teal
{ id: "Ethernet", name: "Ethernet", color: "#0284c7" }, // sky
{ id: "SD", name: "Cartão SD", color: "#10b981" }, // emerald
{ id: "I2C", name: "I2C", color: "#f59e0b" }, // amber
{ id: "Antena", name: "Antena", color: "#06b6d4" }, // cyan
];
const type_colors = {
power: "#ef4444", // high-voltage red
ground: "#64748b", // slate gray
io: "#22c55e", // laser green
analog: "#ec4899", // dynamic pink
dedicated: "#3b82f6", // electric blue
nc: "#94a3b8", // muted gray
};
export default function ConnectionsDiagram({ connections, serie }) {
const [activeFilter, setActiveFilter] = useState("All");
const [selectedConnection, setSelectedConnection] = useState(null);
const [hoveredConnection, setHoveredConnection] = useState(null);
const [searchQuery, setSearchQuery] = useState("");
const [viewMode, setViewMode] = useState("grid");
const [showTooltip, setShowTooltip] = useState(null);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
const isConnectionVisible = (connection) => {
const matchesFilter = activeFilter === "All" || connection.categorias.includes(activeFilter);
const matchesSearch = searchQuery === "" ||
connection.nome.toLowerCase().includes(searchQuery.toLowerCase()) ||
connection.numero.toString().toLowerCase().includes(searchQuery.toLowerCase());
return matchesFilter && matchesSearch;
};
const getCategoryColor = (category) => {
const filter = FILTER_CATEGORIES.find((f) => f.id === category);
return filter?.color || "#64748b";
};
const visibleConnections = useMemo(() => {
return connections.filter(isConnectionVisible);
}, [connections, activeFilter, searchQuery]);
const activeCategories = useMemo(() => {
if (activeFilter === "All") return [];
return FILTER_CATEGORIES.filter(cat =>
visibleConnections.some(conn => conn.categorias.includes(cat.id))
);
}, [visibleConnections, activeFilter]);
useEffect(() => {
const handleEscape = (e) => {
if (e.key === "Escape") {
setSelectedConnection(null);
setShowTooltip(null);
}
};
if (selectedConnection) {
document.addEventListener("keydown", handleEscape);
document.body.style.overflow = "hidden";
}
return () => {
document.removeEventListener("keydown", handleEscape);
document.body.style.overflow = "unset";
};
}, [selectedConnection]);
useEffect(() => {
const handleKeyDown = (e) => {
if (selectedConnection) return;
if (e.key === "Tab") {
const buttons = document.querySelectorAll('[data-connection-button]');
const currentIndex = Array.from(buttons).findIndex(btn => btn === document.activeElement);
if (e.shiftKey && currentIndex > 0) {
e.preventDefault();
buttons[currentIndex - 1].focus();
} else if (!e.shiftKey && currentIndex < buttons.length - 1) {
e.preventDefault();
buttons[currentIndex + 1].focus();
}
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [selectedConnection]);
if (!connections || connections.length === 0) {
return (
Diagrama de conexões indisponível
Este modelo ainda não possui um mapa detalhado de conexões e barramentos.
);
}
return (
{/* Header info */}
Diagrama de Conexões Interativo
Explore todas as conexões e multiplexação do SoC do {serie.nome_completo}. Utilize o painel de busca rápida ou filtre por categoria para acender conexões específicas e ver mapas de barramentos.
{/* Search Panel */}
setSearchQuery(e.target.value)}
className="w-full pl-12 pr-10 py-3 rounded-xl border border-slate-200/60 dark:border-slate-800 bg-white dark:bg-slate-950/40 focus:border-purple-500/80 focus:ring-4 focus:ring-purple-500/10 text-slate-800 dark:text-slate-200 transition-all outline-none text-sm font-semibold"
aria-label="Search connections"
/>
{searchQuery && (
setSearchQuery("")}
className="absolute right-4 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600 transition-colors"
aria-label="Clear search"
>
)}
{/* View Mode Toggle */}
setViewMode("grid")}
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all text-xs font-bold uppercase tracking-wider cursor-pointer active:scale-95 ${
viewMode === "grid"
? "bg-white dark:bg-slate-900 shadow-md text-slate-900 dark:text-slate-100"
: "text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
}`}
aria-label="Grid view"
aria-pressed={viewMode === "grid"}
>
Grade
setViewMode("list")}
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-all text-xs font-bold uppercase tracking-wider cursor-pointer active:scale-95 ${
viewMode === "list"
? "bg-white dark:bg-slate-900 shadow-md text-slate-900 dark:text-slate-100"
: "text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
}`}
aria-label="List view"
aria-pressed={viewMode === "list"}
>
Lista
{/* Filters Panel */}
Filtre por Função do Pino
{visibleConnections.length} de {connections.length} ativos
{FILTER_CATEGORIES.map((filter) => {
const isActive = activeFilter === filter.id;
const count = connections.filter(c =>
filter.id === "All" ? true : c.categorias.includes(filter.id)
).length;
if (count === 0 && !isActive) return null; // Hide filters that don't apply to this chip
return (
setActiveFilter(filter.id)}
aria-pressed={isActive}
aria-label={`Filter by ${filter.name}`}
className={`group relative px-4 py-2 rounded-xl text-xs font-extrabold transition-all duration-300 focus:outline-none cursor-pointer active:scale-95 ${
isActive
? "shadow-lg text-white"
: "bg-slate-50/70 dark:bg-slate-950/20 text-slate-650 dark:text-slate-350 hover:bg-slate-100/50 dark:hover:bg-slate-850/30 border border-slate-200/40 dark:border-slate-850/40"
}`}
style={{
backgroundColor: isActive ? filter.color : undefined,
boxShadow: isActive ? `0 8px 15px -8px ${filter.color}` : undefined
}}
>
{filter.name}
{count}
);
})}
{/* Active Categories badges */}
{activeFilter !== "All" && activeCategories.length > 0 && (
Subcategorias ativadas por esse filtro:
{activeCategories.map(cat => (
{cat.name}
))}
)}
{/* Diagram Workspace */}
{visibleConnections.length === 0 ? (
Nenhuma conexão encontrada
Tente ajustar seus termos de pesquisa ou limpe os filtros rápidos.
{
setActiveFilter("All");
setSearchQuery("");
}}
className="mt-5 px-6 py-2.5 bg-slate-900 dark:bg-slate-100 text-white dark:text-slate-950 rounded-xl hover:opacity-90 transition-all font-bold text-xs uppercase tracking-wider cursor-pointer active:scale-95 shadow-lg"
>
Limpar Filtros e Busca
) : viewMode === "grid" ? (
/* Grid View Mode: Futuristic Electronic Chip Sockets */
{visibleConnections.map((connection, index) => {
const isHovered = hoveredConnection === index;
const connectionId = `connection-${index}`;
const baseColor = type_colors[connection.tipo] || "#94a3b8";
return (
setSelectedConnection(connection)}
onMouseEnter={() => {
setHoveredConnection(index);
setShowTooltip(index);
}}
onMouseLeave={() => {
setHoveredConnection(null);
setShowTooltip(null);
}}
onFocus={() => setHoveredConnection(index)}
onBlur={() => setHoveredConnection(null)}
aria-label={`Pino ${connection.nome}, número ${connection.numero}`}
aria-describedby={showTooltip === index ? `tooltip-${index}` : undefined}
className="w-full flex flex-col items-center justify-center p-4 rounded-2xl border-2 transition-all duration-300 cursor-pointer active:scale-95 focus:outline-none"
style={{
backgroundColor: isHovered ? `${baseColor}24` : `${baseColor}0c`,
borderColor: isHovered ? baseColor : `${baseColor}40`,
boxShadow: isHovered ? `0 0 20px -5px ${baseColor}60` : 'none',
transform: isHovered ? "translateY(-4px)" : undefined,
}}
>
{/* Pad Contact Core Circle */}
{typeof connection.numero === "number"
? connection.numero
: connection.numero.substring(0, 4)}
{/* Pad Name label */}
{connection.nome}
{/* Small Category dots indicator */}
{connection.categorias.slice(0, 5).map((cat) => (
))}
{connection.categorias.length > 5 && (
+{connection.categorias.length - 5}
)}
{/* High-Voltage warnings tags */}
{connection.avisos && (
)}
{/* Sleek Tooltip Popover */}
{showTooltip === index && (
)}
);
})}
) : (
/* List View Mode: Interactive Zebra Table */
Pino
Nome do Pad
Categorias
Multiplexação de Funções
Restrições
{visibleConnections.map((connection, index) => {
const baseColor = type_colors[connection.tipo] || "#94a3b8";
return (
setSelectedConnection(connection)}
className="hover:bg-purple-500/5 dark:hover:bg-purple-400/5 cursor-pointer transition-colors group"
>
{typeof connection.numero === "number"
? connection.numero
: connection.numero.substring(0, 4)}
{connection.nome}
{connection.categorias.map((cat) => (
{cat === "Strapping" ? "Inicialização" : cat}
))}
{connection.funcoes.join(", ")}
{connection.avisos ? (
Restrito
) : (
Livre
)}
);
})}
)}
{/* Global Warning notice */}
{connections.some(c => c.avisos) && (
Notas Importantes sobre a Pinagem
• Pinos contendo tags de Avisos/Restrições (cor vermelha) possuem papéis especiais de Strapping na inicialização, ou limitações analógicas. Clique neles para ver detalhes.
• Sempre garanta isolamento de circuitos capacitivos de toque em pinos com ruído de RF acoplado.
)}
{/* Interactive Overlay Dialog Details Sheet */}
{selectedConnection && mounted && createPortal(
setSelectedConnection(null)}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
e.stopPropagation()}
>
{/* Dialog Header */}
{typeof selectedConnection.numero === "number"
? selectedConnection.numero
: selectedConnection.numero.substring(0, 4)}
{selectedConnection.nome}
Pino Físico {selectedConnection.numero}
setSelectedConnection(null)}
aria-label="Fechar"
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 transition-colors p-2 bg-slate-100 dark:bg-slate-900 rounded-xl shrink-0 cursor-pointer active:scale-95"
>
{/* Dialog Body Contents */}
{/* Categories */}
{selectedConnection.categorias.length > 0 && (
Categorias de Multiplexação
{selectedConnection.categorias.map((cat) => (
{cat === "Strapping" ? "Inicialização" : cat}
))}
)}
{/* Power Source */}
{selectedConnection.alimentacao && (
Domínio de Alimentação Interna
{selectedConnection.alimentacao.map((source, index) => (
{source}
))}
)}
{/* Function Multiplexation */}
Barramentos de Funções Disponíveis
{selectedConnection.funcoes.map((func, index) => (
{func}
))}
{/* Alerts/Warnings */}
{selectedConnection.avisos && (
Avisos Importantes de Engenharia
{selectedConnection.avisos.map((aviso, index) => (
•
{aviso}
))}
)}
{/* Dialog Footer */}
setSelectedConnection(null)}
className="w-full py-3.5 rounded-xl font-bold text-white text-xs uppercase tracking-wider shadow-lg hover:opacity-90 active:scale-98 transition-all cursor-pointer"
style={{
backgroundColor: serie.color,
boxShadow: `0 8px 25px -8px ${serie.color}`
}}
aria-label="Close modal"
>
Confirmar e Fechar
,
document.body
)}
);
}