feat(journal): beautiful entrance animation for entries — cascade fade-up on load, yellow glow pop for newly submitted entries
This commit is contained in:
+17
-2
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user