From edebe5a2945a2f22437c323217f69d47143e9223 Mon Sep 17 00:00:00 2001
From: Raza Khan <110333158+raza-khan0108@users.noreply.github.com>
Date: Wed, 28 Jan 2026 23:14:22 +0530
Subject: [PATCH 1/2] feat: add interactive calendar with event support
---
examples/interactive-calendar/README.md | 66 +++++++++++
examples/interactive-calendar/index.html | 39 ++++++
examples/interactive-calendar/script.js | 121 +++++++++++++++++++
examples/interactive-calendar/style.css | 145 +++++++++++++++++++++++
4 files changed, 371 insertions(+)
create mode 100644 examples/interactive-calendar/README.md
create mode 100644 examples/interactive-calendar/index.html
create mode 100644 examples/interactive-calendar/script.js
create mode 100644 examples/interactive-calendar/style.css
diff --git a/examples/interactive-calendar/README.md b/examples/interactive-calendar/README.md
new file mode 100644
index 00000000..08eb75a5
--- /dev/null
+++ b/examples/interactive-calendar/README.md
@@ -0,0 +1,66 @@
+# 📅 Interactive Calendar
+
+A lightweight, responsive calendar widget built with vanilla JavaScript. This project demonstrates DOM manipulation, Date object handling, and local state management without external libraries.
+
+## 🚀 Features
+
+- **Dynamic Rendering:** Automatically generates the correct grid for any month and year.
+- **Navigation:** Browse through past and future months.
+- **Current Date Highlighting:** visual indicator for today's date.
+- **Event Management (Bonus):** Click any date to add, view, or delete notes.
+- **Data Persistence:** Events are saved to the browser's `localStorage`, so they remain after refreshing the page.
+- **Responsive Design:** Built with CSS Grid to adapt to different screen sizes.
+
+## 🛠️ Technologies Used
+
+- **HTML5:** Semantic structure.
+- **CSS3:** Flexbox and Grid layout; CSS Variables for theming.
+- **JavaScript (ES6+):** Logic for date calculation and event handling.
+
+## 📂 Project Structure
+
+```text
+interactive-calendar/
+├── index.html # Main HTML structure
+├── style.css # Styling and Grid layout
+├── script.js # Calendar logic and Event handling
+└── README.md # Project documentation
+
+##💡 How It Works
+1. Date Calculation
+The calendar grid is calculated using the native Date object:
+
+Start Day: new Date(year, month, 1).getDay() determines which day of the week the month starts on (0=Sunday, 1=Monday).
+
+Total Days: new Date(year, month + 1, 0).getDate() retrieves the exact number of days in the current month.
+
+2. Rendering the Grid
+We use a loop to generate
elements.
+
+First, we insert empty placeholder divs to align the 1st of the month with the correct weekday column.
+
+Then, we generate the actual numbered days.
+
+3. State Management
+Events are stored in a simple JSON object and saved to LocalStorage:
+
+JavaScript
+// Data Structure Example
+{
+ "2023-10-25": "Meeting with team",
+ "2023-10-31": "Halloween Party"
+}
+
+## 🏃♂️ How to Run
+Clone the repository.
+
+Navigate to the interactive-calendar folder.
+
+Open index.html in your browser.
+
+# 🔮 Future Improvements
+Add drag-and-drop functionality for events.
+
+Support for multiple events per day.
+
+Add specific time slots for events.
\ No newline at end of file
diff --git a/examples/interactive-calendar/index.html b/examples/interactive-calendar/index.html
new file mode 100644
index 00000000..bbca4a72
--- /dev/null
+++ b/examples/interactive-calendar/index.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+ Interactive Calendar
+
+
+
+
+
+
+
+ <
+
+ >
+
+
+
+
+
Sun
Mon
Tue
Wed
Thu
Fri
Sat
+
+
+
+
+
+
+
+ ×
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/interactive-calendar/script.js b/examples/interactive-calendar/script.js
new file mode 100644
index 00000000..ec4b04b2
--- /dev/null
+++ b/examples/interactive-calendar/script.js
@@ -0,0 +1,121 @@
+const monthYearElement = document.getElementById('month-year');
+const datesElement = document.getElementById('calendar-dates');
+const prevBtn = document.getElementById('month-prev');
+const nextBtn = document.getElementById('month-next');
+
+// Modal Elements
+const modal = document.getElementById('event-modal');
+const closeModalBtn = document.querySelector('.close-btn');
+const selectedDateTitle = document.getElementById('selected-date');
+const eventInput = document.getElementById('event-input');
+const saveEventBtn = document.getElementById('save-event-btn');
+const deleteEventBtn = document.getElementById('delete-event-btn');
+
+let currentDate = new Date();
+let clickedDate = null;
+let events = JSON.parse(localStorage.getItem('events')) || {};
+
+const months = [
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+];
+
+// --- Core Functions ---
+
+function renderCalendar() {
+ const year = currentDate.getFullYear();
+ const month = currentDate.getMonth();
+
+ monthYearElement.innerText = `${months[month]} ${year}`;
+ datesElement.innerHTML = '';
+
+ // First day of the month (0 = Sunday, 1 = Monday, etc.)
+ const firstDay = new Date(year, month, 1).getDay();
+ // Total days in the current month
+ const totalDays = new Date(year, month + 1, 0).getDate();
+
+ // Add empty divs for days before the first day of the month
+ for (let i = 0; i < firstDay; i++) {
+ const emptyDiv = document.createElement('div');
+ datesElement.appendChild(emptyDiv);
+ }
+
+ // Render days
+ for (let day = 1; day <= totalDays; day++) {
+ const dayDiv = document.createElement('div');
+ dayDiv.classList.add('day');
+ dayDiv.innerText = day;
+
+ // Check if this day is "Today"
+ const today = new Date();
+ if (day === today.getDate() && month === today.getMonth() && year === today.getFullYear()) {
+ dayDiv.classList.add('current-date');
+ }
+
+ // Check for Events
+ const dateString = `${year}-${month + 1}-${day}`;
+ if (events[dateString]) {
+ dayDiv.classList.add('has-event');
+ dayDiv.title = events[dateString]; // Tooltip
+ }
+
+ // Add Click Event for Modal
+ dayDiv.addEventListener('click', () => openModal(dateString));
+
+ datesElement.appendChild(dayDiv);
+ }
+}
+
+function openModal(dateStr) {
+ clickedDate = dateStr;
+ selectedDateTitle.innerText = `Event for: ${dateStr}`;
+ eventInput.value = events[dateStr] || '';
+ modal.classList.remove('hidden');
+}
+
+function closeModal() {
+ modal.classList.add('hidden');
+ clickedDate = null;
+}
+
+// --- Event Listeners ---
+
+prevBtn.addEventListener('click', () => {
+ currentDate.setMonth(currentDate.getMonth() - 1);
+ renderCalendar();
+});
+
+nextBtn.addEventListener('click', () => {
+ currentDate.setMonth(currentDate.getMonth() + 1);
+ renderCalendar();
+});
+
+closeModalBtn.addEventListener('click', closeModal);
+
+saveEventBtn.addEventListener('click', () => {
+ if (eventInput.value.trim()) {
+ events[clickedDate] = eventInput.value;
+ localStorage.setItem('events', JSON.stringify(events));
+ }
+ closeModal();
+ renderCalendar();
+});
+
+deleteEventBtn.addEventListener('click', () => {
+ if (events[clickedDate]) {
+ delete events[clickedDate];
+ localStorage.setItem('events', JSON.stringify(events));
+ }
+ closeModal();
+ renderCalendar();
+});
+
+// Close modal if clicking outside
+window.addEventListener('click', (e) => {
+ if (e.target === modal) {
+ closeModal();
+ }
+});
+
+// Initial Render
+renderCalendar();
\ No newline at end of file
diff --git a/examples/interactive-calendar/style.css b/examples/interactive-calendar/style.css
new file mode 100644
index 00000000..f5559b09
--- /dev/null
+++ b/examples/interactive-calendar/style.css
@@ -0,0 +1,145 @@
+:root {
+ --primary-color: #4a90e2;
+ --bg-color: #f4f4f9;
+ --text-color: #333;
+ --highlight-color: #e0f7fa;
+}
+
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background-color: var(--bg-color);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 100vh;
+ margin: 0;
+}
+
+.calendar-container {
+ background: white;
+ width: 90%;
+ max-width: 400px;
+ border-radius: 10px;
+ box-shadow: 0 10px 25px rgba(0,0,0,0.1);
+ overflow: hidden;
+ padding: 20px;
+}
+
+.calendar-header {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+.calendar-navigation {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.nav-btn {
+ cursor: pointer;
+ font-size: 1.5rem;
+ -webkit-user-select: none;
+ user-select: none;
+ padding: 0 10px;
+}
+
+.nav-btn:hover {
+ color: var(--primary-color);
+}
+
+/* Grid System */
+.calendar-weekdays, .calendar-dates {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ text-align: center;
+}
+
+.calendar-weekdays div {
+ font-weight: bold;
+ color: #888;
+ margin-bottom: 10px;
+}
+
+.day {
+ padding: 10px;
+ cursor: pointer;
+ border-radius: 5px;
+ transition: background 0.2s;
+ position: relative;
+}
+
+.day:hover {
+ background-color: #f0f0f0;
+}
+
+.day.current-date {
+ background-color: var(--primary-color);
+ color: white;
+ font-weight: bold;
+}
+
+.day.has-event::after {
+ content: '';
+ position: absolute;
+ bottom: 5px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 5px;
+ height: 5px;
+ background-color: red;
+ border-radius: 50%;
+}
+
+.day.inactive {
+ color: #ccc;
+ pointer-events: none;
+}
+
+/* Modal Styles */
+.modal {
+ position: fixed;
+ top: 0; left: 0; width: 100%; height: 100%;
+ background: rgba(0,0,0,0.5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.hidden { display: none; }
+
+.modal-content {
+ background: white;
+ padding: 20px;
+ border-radius: 8px;
+ width: 300px;
+ position: relative;
+}
+
+.close-btn {
+ position: absolute;
+ top: 10px; right: 15px;
+ cursor: pointer;
+ font-size: 1.2rem;
+}
+
+textarea {
+ width: 100%;
+ height: 60px;
+ margin: 10px 0;
+ padding: 5px;
+}
+
+button {
+ background: var(--primary-color);
+ color: white;
+ border: none;
+ padding: 8px 15px;
+ cursor: pointer;
+ border-radius: 4px;
+}
+
+button.secondary { background: #ccc; }
\ No newline at end of file
From 8daabe08e5a7814cceeffddd4322630e1be444e7 Mon Sep 17 00:00:00 2001
From: Raza Khan <110333158+raza-khan0108@users.noreply.github.com>
Date: Thu, 29 Jan 2026 23:11:07 +0530
Subject: [PATCH 2/2] fix: address code review feedback (accessibility,
renaming, and logic fixes)
---
examples/interactive-calendar/README.md | 76 ++++++++++++++-------
examples/interactive-calendar/index.html | 11 ++-
examples/interactive-calendar/script.js | 87 ++++++++++++++----------
examples/interactive-calendar/style.css | 2 +
4 files changed, 109 insertions(+), 67 deletions(-)
diff --git a/examples/interactive-calendar/README.md b/examples/interactive-calendar/README.md
index 08eb75a5..8bdaacf9 100644
--- a/examples/interactive-calendar/README.md
+++ b/examples/interactive-calendar/README.md
@@ -1,21 +1,28 @@
# 📅 Interactive Calendar
-A lightweight, responsive calendar widget built with vanilla JavaScript. This project demonstrates DOM manipulation, Date object handling, and local state management without external libraries.
+A lightweight, responsive calendar widget built with **vanilla JavaScript**.
+This project demonstrates **DOM manipulation**, **Date object handling**, and **local state management** without external libraries.
+
+---
## 🚀 Features
- **Dynamic Rendering:** Automatically generates the correct grid for any month and year.
- **Navigation:** Browse through past and future months.
-- **Current Date Highlighting:** visual indicator for today's date.
+- **Current Date Highlighting:** Visual indicator for today's date.
- **Event Management (Bonus):** Click any date to add, view, or delete notes.
- **Data Persistence:** Events are saved to the browser's `localStorage`, so they remain after refreshing the page.
- **Responsive Design:** Built with CSS Grid to adapt to different screen sizes.
+---
+
## 🛠️ Technologies Used
-- **HTML5:** Semantic structure.
-- **CSS3:** Flexbox and Grid layout; CSS Variables for theming.
-- **JavaScript (ES6+):** Logic for date calculation and event handling.
+- **HTML5:** Semantic structure
+- **CSS3:** Flexbox and Grid layout, CSS Variables for theming
+- **JavaScript (ES6+):** Date calculation and event handling logic
+
+---
## 📂 Project Structure
@@ -23,44 +30,61 @@ A lightweight, responsive calendar widget built with vanilla JavaScript. This pr
interactive-calendar/
├── index.html # Main HTML structure
├── style.css # Styling and Grid layout
-├── script.js # Calendar logic and Event handling
+├── script.js # Calendar logic and event handling
└── README.md # Project documentation
+```
+
+---
+
+## 💡 How It Works
-##💡 How It Works
-1. Date Calculation
-The calendar grid is calculated using the native Date object:
+### 1. Date Calculation
-Start Day: new Date(year, month, 1).getDay() determines which day of the week the month starts on (0=Sunday, 1=Monday).
+The calendar grid is calculated using the native `Date` object:
-Total Days: new Date(year, month + 1, 0).getDate() retrieves the exact number of days in the current month.
+- **Start Day:**
+ `new Date(year, month, 1).getDay()`
+ Determines which day of the week the month starts on
+ `(0 = Sunday, 1 = Monday, ...)`
-2. Rendering the Grid
-We use a loop to generate
elements.
+- **Total Days:**
+ `new Date(year, month + 1, 0).getDate()`
+ Retrieves the exact number of days in the current month
-First, we insert empty placeholder divs to align the 1st of the month with the correct weekday column.
+---
-Then, we generate the actual numbered days.
+### 2. Rendering the Grid
-3. State Management
-Events are stored in a simple JSON object and saved to LocalStorage:
+- A loop generates `
` elements for the calendar.
+- Empty placeholder divs are inserted to align the first day of the month correctly.
+- Numbered day cells are then rendered dynamically.
-JavaScript
+---
+
+### 3. State Management
+
+Events are stored in a simple JSON object and persisted using `localStorage`.
+
+```javascript
// Data Structure Example
{
"2023-10-25": "Meeting with team",
"2023-10-31": "Halloween Party"
}
+```
-## 🏃♂️ How to Run
-Clone the repository.
+---
-Navigate to the interactive-calendar folder.
+## 🏃♂️ How to Run
-Open index.html in your browser.
+1. Clone the repository
+2. Navigate to the `interactive-calendar` folder
+3. Open `index.html` in your browser
-# 🔮 Future Improvements
-Add drag-and-drop functionality for events.
+---
-Support for multiple events per day.
+## 🔮 Future Improvements
-Add specific time slots for events.
\ No newline at end of file
+- Add drag-and-drop functionality for events
+- Support multiple events per day
+- Add specific time slots for events
diff --git a/examples/interactive-calendar/index.html b/examples/interactive-calendar/index.html
index bbca4a72..4dfc408f 100644
--- a/examples/interactive-calendar/index.html
+++ b/examples/interactive-calendar/index.html
@@ -4,16 +4,15 @@
Interactive Calendar
-
-
+
- <
+ <
- >
+ >
@@ -24,9 +23,9 @@
-
+
- ×
+ ×
diff --git a/examples/interactive-calendar/script.js b/examples/interactive-calendar/script.js
index ec4b04b2..cf534641 100644
--- a/examples/interactive-calendar/script.js
+++ b/examples/interactive-calendar/script.js
@@ -3,7 +3,6 @@ const datesElement = document.getElementById('calendar-dates');
const prevBtn = document.getElementById('month-prev');
const nextBtn = document.getElementById('month-next');
-// Modal Elements
const modal = document.getElementById('event-modal');
const closeModalBtn = document.querySelector('.close-btn');
const selectedDateTitle = document.getElementById('selected-date');
@@ -20,7 +19,8 @@ const months = [
"July", "August", "September", "October", "November", "December"
];
-// --- Core Functions ---
+// Helper: Zero-pad numbers (Fixed: Date formatting consistency)
+const pad = (n) => (n < 10 ? '0' + n : n);
function renderCalendar() {
const year = currentDate.getFullYear();
@@ -29,48 +29,54 @@ function renderCalendar() {
monthYearElement.innerText = `${months[month]} ${year}`;
datesElement.innerHTML = '';
- // First day of the month (0 = Sunday, 1 = Monday, etc.)
const firstDay = new Date(year, month, 1).getDay();
- // Total days in the current month
const totalDays = new Date(year, month + 1, 0).getDate();
- // Add empty divs for days before the first day of the month
for (let i = 0; i < firstDay; i++) {
const emptyDiv = document.createElement('div');
datesElement.appendChild(emptyDiv);
}
- // Render days
for (let day = 1; day <= totalDays; day++) {
const dayDiv = document.createElement('div');
dayDiv.classList.add('day');
dayDiv.innerText = day;
- // Check if this day is "Today"
const today = new Date();
if (day === today.getDate() && month === today.getMonth() && year === today.getFullYear()) {
dayDiv.classList.add('current-date');
}
- // Check for Events
- const dateString = `${year}-${month + 1}-${day}`;
+ // Fixed: Use padded date format YYYY-MM-DD
+ const dateString = `${year}-${pad(month + 1)}-${pad(day)}`;
+
+ // Store date in data attribute for Event Delegation
+ dayDiv.dataset.date = dateString;
+
if (events[dateString]) {
dayDiv.classList.add('has-event');
- dayDiv.title = events[dateString]; // Tooltip
+ dayDiv.title = events[dateString];
}
- // Add Click Event for Modal
- dayDiv.addEventListener('click', () => openModal(dateString));
-
+ // Removed individual event listeners here (Performance fix)
datesElement.appendChild(dayDiv);
}
}
+// Fixed: Event Delegation (One listener for all days)
+datesElement.addEventListener('click', (e) => {
+ const target = e.target.closest('.day');
+ if (target && target.dataset.date) {
+ openModal(target.dataset.date);
+ }
+});
+
function openModal(dateStr) {
clickedDate = dateStr;
selectedDateTitle.innerText = `Event for: ${dateStr}`;
eventInput.value = events[dateStr] || '';
modal.classList.remove('hidden');
+ eventInput.focus();
}
function closeModal() {
@@ -78,25 +84,17 @@ function closeModal() {
clickedDate = null;
}
-// --- Event Listeners ---
-
-prevBtn.addEventListener('click', () => {
- currentDate.setMonth(currentDate.getMonth() - 1);
- renderCalendar();
-});
-
-nextBtn.addEventListener('click', () => {
- currentDate.setMonth(currentDate.getMonth() + 1);
- renderCalendar();
-});
-
-closeModalBtn.addEventListener('click', closeModal);
-
+// Fixed: Save logic handles whitespace and deletion
saveEventBtn.addEventListener('click', () => {
- if (eventInput.value.trim()) {
- events[clickedDate] = eventInput.value;
- localStorage.setItem('events', JSON.stringify(events));
+ const eventText = eventInput.value.trim();
+
+ if (eventText) {
+ events[clickedDate] = eventText;
+ } else {
+ delete events[clickedDate]; // Remove event if input is cleared
}
+
+ localStorage.setItem('events', JSON.stringify(events));
closeModal();
renderCalendar();
});
@@ -110,12 +108,31 @@ deleteEventBtn.addEventListener('click', () => {
renderCalendar();
});
-// Close modal if clicking outside
+// Navigation & Close
+prevBtn.addEventListener('click', () => {
+ currentDate.setMonth(currentDate.getMonth() - 1);
+ renderCalendar();
+});
+
+nextBtn.addEventListener('click', () => {
+ currentDate.setMonth(currentDate.getMonth() + 1);
+ renderCalendar();
+});
+
+closeModalBtn.addEventListener('click', closeModal);
+
window.addEventListener('click', (e) => {
- if (e.target === modal) {
- closeModal();
- }
+ if (e.target === modal) closeModal();
+});
+
+// Fixed: Keyboard Accessibility for Nav Buttons
+[prevBtn, nextBtn, closeModalBtn].forEach(btn => {
+ btn.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ btn.click();
+ }
+ });
});
-// Initial Render
renderCalendar();
\ No newline at end of file
diff --git a/examples/interactive-calendar/style.css b/examples/interactive-calendar/style.css
index f5559b09..c132b164 100644
--- a/examples/interactive-calendar/style.css
+++ b/examples/interactive-calendar/style.css
@@ -131,6 +131,8 @@ textarea {
height: 60px;
margin: 10px 0;
padding: 5px;
+ box-sizing: border-box; /* Fixed: prevents width overflow */
+ resize: vertical; /* Optional: improves UX */
}
button {