Skip to content

Commit 57c5bc7

Browse files
committed
Added modal animation to recap
1 parent 2a057bd commit 57c5bc7

File tree

4 files changed

+99
-55
lines changed

4 files changed

+99
-55
lines changed

assets/calendar.js

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// import { initializeCalendar } from '/assets/js/calendar.js';
55
// initializeCalendar();
66

7+
import { showModal, hideModal, setupModalCloseHandlers } from './modal-utils.js';
8+
79
let calendar;
810
let currentEvents = [];
911
let currentBooks = {};
@@ -332,38 +334,23 @@ function showEventModal(_title, event) {
332334
if (event.book_path) {
333335
viewBookBtn.classList.remove('hidden');
334336
viewBookBtn.onclick = () => {
335-
hideModal(); // Ensure modal hidden immediately
337+
hideEventModal(); // Ensure modal hidden immediately
336338
window.location.href = event.book_path;
337339
};
338340
} else {
339341
viewBookBtn.classList.add('hidden');
340342
viewBookBtn.onclick = null;
341343
}
342344

343-
// Animate open
344-
modal.classList.remove('hidden');
345-
modal.classList.add('opacity-0');
346-
modalCard.classList.add('scale-95', 'opacity-0');
347-
modal.offsetHeight; // Force reflow
348-
requestAnimationFrame(() => {
349-
modal.classList.replace('opacity-0', 'opacity-100');
350-
modalCard.classList.remove('scale-95', 'opacity-0');
351-
modalCard.classList.add('scale-100', 'opacity-100');
352-
});
345+
// Animate open using shared utility
346+
showModal(modal, modalCard);
353347
}
354348

355-
function hideModal() {
349+
// Helper to hide the event modal using the shared utility
350+
function hideEventModal() {
356351
const modal = document.getElementById('eventModal');
357352
const modalCard = document.getElementById('modalCard');
358-
if (!modal || !modalCard) return;
359-
360-
modal.classList.replace('opacity-100', 'opacity-0');
361-
modalCard.classList.replace('scale-100', 'scale-95');
362-
modalCard.classList.replace('opacity-100', 'opacity-0');
363-
364-
setTimeout(() => {
365-
modal.classList.add('hidden');
366-
}, 300);
353+
hideModal(modal, modalCard);
367354
}
368355

369356
function setupEventHandlers() {
@@ -390,11 +377,11 @@ function setupEventHandlers() {
390377
}
391378
});
392379

393-
// Modal close / backdrop click
394-
document.getElementById('closeModal')?.addEventListener('click', hideModal);
395-
document.getElementById('eventModal')?.addEventListener('click', (e) => {
396-
if (e.target === e.currentTarget) hideModal();
397-
});
380+
// Modal close handlers using shared utility
381+
const modal = document.getElementById('eventModal');
382+
const modalCard = document.getElementById('modalCard');
383+
const closeBtn = document.getElementById('closeModal');
384+
setupModalCloseHandlers(modal, modalCard, closeBtn);
398385
}
399386

400387
// Update Today button disabled state based on whether we're viewing the current month

assets/modal-utils.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Shared modal animation utilities
2+
3+
/**
4+
* Show a modal with scale/fade animation
5+
* @param {HTMLElement} modal - The modal backdrop element
6+
* @param {HTMLElement} modalCard - The modal card/content element
7+
*/
8+
export function showModal(modal, modalCard) {
9+
if (!modal || !modalCard) return;
10+
11+
modal.classList.remove('hidden');
12+
modal.classList.add('flex');
13+
modal.classList.add('opacity-0');
14+
modalCard.classList.add('scale-95', 'opacity-0');
15+
16+
// Force reflow
17+
modal.offsetHeight;
18+
19+
requestAnimationFrame(() => {
20+
modal.classList.remove('opacity-0');
21+
modal.classList.add('opacity-100');
22+
modalCard.classList.remove('scale-95', 'opacity-0');
23+
modalCard.classList.add('scale-100', 'opacity-100');
24+
});
25+
}
26+
27+
/**
28+
* Hide a modal with scale/fade animation
29+
* @param {HTMLElement} modal - The modal backdrop element
30+
* @param {HTMLElement} modalCard - The modal card/content element
31+
*/
32+
export function hideModal(modal, modalCard) {
33+
if (!modal || !modalCard) return;
34+
35+
modal.classList.remove('opacity-100');
36+
modal.classList.add('opacity-0');
37+
modalCard.classList.remove('scale-100', 'opacity-100');
38+
modalCard.classList.add('scale-95', 'opacity-0');
39+
40+
setTimeout(() => {
41+
modal.classList.add('hidden');
42+
modal.classList.remove('flex');
43+
}, 300);
44+
}
45+
46+
/**
47+
* Setup standard modal close handlers (close button, backdrop click, Escape key)
48+
* @param {HTMLElement} modal - The modal backdrop element
49+
* @param {HTMLElement} modalCard - The modal card/content element
50+
* @param {HTMLElement|null} closeBtn - Optional close button element
51+
*/
52+
export function setupModalCloseHandlers(modal, modalCard, closeBtn = null) {
53+
if (!modal || !modalCard) return;
54+
55+
const hide = () => hideModal(modal, modalCard);
56+
57+
// Close button
58+
if (closeBtn) {
59+
closeBtn.addEventListener('click', hide);
60+
}
61+
62+
// Backdrop click
63+
modal.addEventListener('click', (e) => {
64+
if (e.target === modal) {
65+
hide();
66+
}
67+
});
68+
69+
// Escape key
70+
document.addEventListener('keydown', (e) => {
71+
if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
72+
hide();
73+
}
74+
});
75+
76+
return hide;
77+
}

assets/recap.js

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { StorageManager } from './storage-manager.js';
2+
import { showModal, setupModalCloseHandlers } from './modal-utils.js';
23

34
// Recap interactions: year dropdown + navigation
45
document.addEventListener('DOMContentLoaded', () => {
@@ -100,6 +101,7 @@ document.addEventListener('DOMContentLoaded', () => {
100101
// --- Share Modal Logic ---
101102
const shareBtn = document.getElementById('shareButton');
102103
const shareModal = document.getElementById('shareModal');
104+
const shareModalCard = document.getElementById('shareModalCard');
103105
const shareModalClose = document.getElementById('shareModalClose');
104106
const shareModalTitle = document.getElementById('shareModalTitle');
105107

@@ -134,36 +136,14 @@ document.addEventListener('DOMContentLoaded', () => {
134136
}
135137
}
136138

137-
if (shareBtn && shareModal) {
138-
// Open modal
139+
if (shareBtn && shareModal && shareModalCard) {
140+
// Open modal with animation
139141
shareBtn.addEventListener('click', () => {
140-
shareModal.classList.remove('hidden');
141-
shareModal.classList.add('flex');
142+
showModal(shareModal, shareModalCard);
142143
});
143144

144-
// Close modal on X button
145-
if (shareModalClose) {
146-
shareModalClose.addEventListener('click', () => {
147-
shareModal.classList.add('hidden');
148-
shareModal.classList.remove('flex');
149-
});
150-
}
151-
152-
// Close modal on backdrop click
153-
shareModal.addEventListener('click', (e) => {
154-
if (e.target === shareModal) {
155-
shareModal.classList.add('hidden');
156-
shareModal.classList.remove('flex');
157-
}
158-
});
159-
160-
// Close modal on Escape key
161-
document.addEventListener('keydown', (e) => {
162-
if (e.key === 'Escape' && !shareModal.classList.contains('hidden')) {
163-
shareModal.classList.add('hidden');
164-
shareModal.classList.remove('flex');
165-
}
166-
});
145+
// Setup close handlers (X button, backdrop click, Escape key)
146+
setupModalCloseHandlers(shareModal, shareModalCard, shareModalClose);
167147

168148
// Handle share/download button clicks
169149
document.querySelectorAll('.share-webp-btn').forEach(btn => {

templates/recap/recap_year.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ <h2 class="hidden lg:block text-2xl font-bold text-gray-900 dark:text-white">Rec
107107
</header>
108108

109109
<!-- Share Modal -->
110-
<div id="shareModal" class="fixed inset-0 z-[60] hidden items-center justify-center bg-black/50 backdrop-blur-sm p-4">
111-
<div class="bg-gray-100 dark:bg-dark-900 rounded-2xl shadow-2xl max-w-md w-full max-h-[90vh] overflow-y-auto border border-gray-200/70 dark:border-dark-700/50">
110+
<div id="shareModal" class="fixed inset-0 z-[60] hidden items-center justify-center bg-black/50 backdrop-blur-sm p-4 opacity-0 transition-opacity duration-300">
111+
<div id="shareModalCard" class="bg-gray-100 dark:bg-dark-900 rounded-2xl shadow-2xl max-w-md w-full max-h-[90vh] overflow-y-auto border border-gray-200/70 dark:border-dark-700/50 transform transition-all duration-300 scale-95 opacity-0">
112112
<!-- Modal Header -->
113113
<div class="flex items-center justify-between p-4 border-b border-gray-200/70 dark:border-dark-700/50">
114114
<h3 id="shareModalTitle" class="text-lg font-bold text-gray-900 dark:text-white">Download Recap Image</h3>

0 commit comments

Comments
 (0)