animate connection table

This commit is contained in:
lelo 2025-04-04 18:17:43 +02:00
parent 1a254bdd10
commit fd83b89e6d

View File

@ -1,12 +1,17 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Recent Connections</title> <title>Recent Connections</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"> <link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<style> <style>
html, body { /* Basic layout */
html,
body {
height: 100%; height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -20,6 +25,39 @@
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
} }
/* Animate the table body moving down via transform */
#connectionsTableBody {
transition: transform 0.5s ease-out;
transform: translateY(0);
}
/* Advanced animation for new rows */
@keyframes slideIn {
0% {
opacity: 0;
transform: scale(0.8);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.slide-in {
animation: slideIn 0.5s ease-out;
}
/* Animation for disappearing rows */
@keyframes slideOut {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.1);
}
}
.slide-out {
animation: slideOut 0.5s ease-in forwards;
}
</style> </style>
</head> </head>
<body> <body>
@ -29,10 +67,18 @@
<a class="navbar-brand" href="#">Downloads der letzten 10 Minuten</a> <a class="navbar-brand" href="#">Downloads der letzten 10 Minuten</a>
<div class="navbar-collapse" id="navbarNav"> <div class="navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto"> <ul class="navbar-nav ms-auto">
<li class="nav-item"><a href="{{ url_for('index') }}" class="nav-link">App</a></li> <li class="nav-item">
<li class="nav-item"><a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a></li> <a href="{{ url_for('index') }}" class="nav-link">App</a>
<li class="nav-item"><a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a></li> </li>
<li class="nav-item"><a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung</a></li> <li class="nav-item">
<a href="{{ url_for('mylinks') }}" class="nav-link">Meine Links</a>
</li>
<li class="nav-item">
<a href="{{ url_for('connections') }}" class="nav-link">Verbindungen</a>
</li>
<li class="nav-item">
<a href="{{ url_for('dashboard') }}" class="nav-link">Auswertung</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -47,13 +93,13 @@
<th>Location</th> <th>Location</th>
<th>User Agent</th> <th>User Agent</th>
<th>File Path</th> <th>File Path</th>
<td>File Size</td> <th>File Size</th>
<td>MIME-Typ</td> <th>MIME-Typ</th>
<td>Cached</td> <th>Cached</th>
</tr> </tr>
</thead> </thead>
<tbody id="connectionsTableBody"> <tbody id="connectionsTableBody">
<!-- Rows will be dynamically inserted here --> <!-- Rows are dynamically inserted here -->
</tbody> </tbody>
</table> </table>
</div> </div>
@ -64,28 +110,106 @@
<script> <script>
const socket = io(); const socket = io();
// Request initial data immediately on connection // Request initial data once connected.
socket.on('connect', () => { socket.on("connect", () => {
socket.emit('request_initial_data'); socket.emit("request_initial_data");
}); });
socket.on('recent_connections', function(data) { // Helper: Create a table row.
const tbody = document.getElementById('connectionsTableBody'); // When applyAnimation is true, the row uses the slide-in animation.
tbody.innerHTML = ''; // Clear previous content function createRow(record, applyAnimation = true) {
const row = document.createElement("tr");
row.setAttribute("data-timestamp", record.timestamp);
if (applyAnimation) {
row.classList.add("slide-in");
row.addEventListener("animationend", () =>
row.classList.remove("slide-in"), { once: true });
}
row.innerHTML = `
<td>${record.timestamp}</td>
<td>${record.location}</td>
<td>${record.user_agent}</td>
<td>${record.full_path}</td>
<td>${record.filesize}</td>
<td>${record.mime_typ}</td>
<td>${record.cached}</td>
`;
return row;
}
data.forEach(record => { // Update the table body with the new data.
const row = document.createElement('tr'); // New rows animate in; existing rows simply update.
row.innerHTML = ` function updateTable(data) {
<td>${record.timestamp}</td> const tbody = document.getElementById("connectionsTableBody");
<td>${record.location}</td> // Map current rows by their timestamp.
<td>${record.user_agent}</td> const currentRows = {};
<td>${record.full_path}</td> Array.from(tbody.children).forEach(row => {
<td>${record.filesize}</td> currentRows[row.getAttribute("data-timestamp")] = row;
<td>${record.mime_typ}</td>
<td>${record.cached}</td>
`;
tbody.appendChild(row);
}); });
const fragment = document.createDocumentFragment();
data.forEach(record => {
let row;
if (currentRows[record.timestamp]) {
// Existing row: update content without new animation.
row = currentRows[record.timestamp];
row.innerHTML = `
<td>${record.timestamp}</td>
<td>${record.location}</td>
<td>${record.user_agent}</td>
<td>${record.full_path}</td>
<td>${record.filesize}</td>
<td>${record.mime_typ}</td>
<td>${record.cached}</td>
`;
row.classList.remove("slide-out");
} else {
// New row: create with slide-in animation.
row = createRow(record, true);
}
fragment.appendChild(row);
});
tbody.innerHTML = "";
tbody.appendChild(fragment);
}
// Animate the table body moving down by the height of a new row before updating.
function animateTableWithNewRow(data) {
const tbody = document.getElementById("connectionsTableBody");
// Identify any new records (by comparing timestamps).
const currentTimestamps = new Set(
Array.from(tbody.children).map(row => row.getAttribute("data-timestamp"))
);
const newRecords = data.filter(record => !currentTimestamps.has(record.timestamp));
if (newRecords.length > 0) {
// Create a temporary row to measure its height.
const tempRow = createRow(newRecords[0], false);
tempRow.style.visibility = "hidden";
tbody.appendChild(tempRow);
const newRowHeight = tempRow.getBoundingClientRect().height;
tempRow.remove();
// Animate the tbody moving down by the measured height.
tbody.style.transform = `translateY(${newRowHeight}px)`;
// After the transition, update the table and reset the transform.
setTimeout(() => {
updateTable(data);
// Remove the transform instantly.
tbody.style.transition = "none";
tbody.style.transform = "translateY(0)";
// Force reflow to apply the style immediately.
void tbody.offsetWidth;
tbody.style.transition = "transform 0.5s ease-out";
}, 500);
} else {
updateTable(data);
}
}
// Listen for incoming connection data from the server.
socket.on("recent_connections", function(data) {
animateTableWithNewRow(data);
}); });
</script> </script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>