feat(journal): beautiful entrance animation for entries — cascade fade-up on load, yellow glow pop for newly submitted entries

This commit is contained in:
dev
2026-09-16 20:14:09 +03:00
parent 2784084c68
commit cc9a9e6c2d
2 changed files with 26 additions and 2 deletions
+17 -2
View File
@@ -1,4 +1,7 @@
let currentEntries = [];
let seenEntryIds = new Set();
let highlightNewIds = new Set();
let highlightClearTimer = null;
let editId = null;
let editGroups = [];
let groupsLoaded = false;
@@ -91,7 +94,7 @@ async function detachFile(id) {
function gridEntryHTML(e) {
const mp = mainPhotoPath(e);
return `
<div class="card">
<div class="card${highlightNewIds.has(e.id) ? ' entry-appear' : ' entry-enter'}">
${mp ? `<img src="${thumbSrc(mp)}" data-img="${API}${mp}" loading="lazy" decoding="async" alt="">` : ''}
<div class="info">
<div class="name">${esc(e.student_name)}</div>
@@ -109,7 +112,7 @@ function gridEntryHTML(e) {
function cardEntryHTML(e) {
const mp = mainPhotoPath(e);
return `
<div class="journal-card">
<div class="journal-card${highlightNewIds.has(e.id) ? ' entry-appear' : ' entry-enter'}">
${mp
? `<img src="${thumbSrc(mp)}" data-img="${API}${mp}" loading="lazy" decoding="async" alt="">`
: `<div class="no-photo"><svg viewBox="0 0 240 160" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><circle cx="105" cy="48" r="15"/><path d="M74 140 C74 106 88 92 105 92 C122 92 136 106 136 140 Z"/><circle cx="145" cy="58" r="18"/><path d="M108 144 C108 104 124 88 145 88 C166 88 182 104 182 144 Z"/></svg></div>`}
@@ -151,6 +154,10 @@ function renderGrid() {
grid.className = 'grid';
grid.innerHTML = currentEntries.map(gridEntryHTML).join('');
}
const items = Array.from(grid.children);
items.forEach((el, i) => {
el.style.animationDelay = `${Math.min(i * 45, 500)}ms`;
});
renderIcons();
}
@@ -208,6 +215,14 @@ async function loadEntries() {
p.set('offset', (page - 1) * PAGE_SIZE);
const res = await fetch(`${API}/api/entries?${p}`, { headers: hdr() });
const data = await res.json();
const firstLoad = seenEntryIds.size === 0;
const newIds = firstLoad ? [] : data.entries.filter(e => !seenEntryIds.has(e.id)).map(e => e.id);
if (newIds.length) {
highlightNewIds = new Set([...highlightNewIds, ...newIds]);
if (highlightClearTimer) clearTimeout(highlightClearTimer);
highlightClearTimer = setTimeout(() => { highlightNewIds = new Set(); }, 8000);
}
data.entries.forEach(e => seenEntryIds.add(e.id));
currentEntries = data.entries;
totalEntries = data.total;
renderGrid();