animate connection table
This commit is contained in:
parent
1a254bdd10
commit
fd83b89e6d
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user