|
| 1 | +# 🎓 GDG Pomodoro: JavaScript Live Coding Script |
| 2 | +**Duration:** 1h - 1h 30m |
| 3 | +**Goal:** Build a working Pomodoro timer from scratch (excluding Task CRUD). |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 🏁 Phase 0: Setup (5 mins) |
| 8 | +* **Goal:** Ensure everyone has the HTML/CSS ready and connected. |
| 9 | +* **Action:** Open `main.js` (empty) and `index.html`. |
| 10 | +* **Talking Point:** "HTML is the skeleton, CSS is the skin, and JavaScript is the brain. Today we are building the brain." |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## 🏗️ Phase 1: The "Memory" (State & Config) (10 mins) |
| 15 | +* **Goal:** Define the data our app needs to remember. |
| 16 | +* **Concept:** specific specific variables vs. objects. |
| 17 | + |
| 18 | +### Step 1.1: Configuration |
| 19 | +```javascript |
| 20 | +// 1. CONFIGURATION |
| 21 | +const DURATIONS = { |
| 22 | + focus: 25 * 60, |
| 23 | + "short-break": 5 * 60, |
| 24 | + "long-break": 15 * 60, |
| 25 | +}; |
| 26 | +``` |
| 27 | +* *Explain:* specialized objects (Dictionaries) map keys to values. `25 * 60` is easier to read than `1500`. |
| 28 | + |
| 29 | +### Step 1.2: State |
| 30 | +```javascript |
| 31 | +// 2. STATE |
| 32 | +let state = { |
| 33 | + mode: "focus", |
| 34 | + timeLeft: DURATIONS.focus, |
| 35 | + isRunning: false, |
| 36 | + timerInterval: null, |
| 37 | +}; |
| 38 | +``` |
| 39 | +* *Explain:* `state` is the "single source of truth". If the variable changes, the screen *should* change. |
| 40 | + |
| 41 | +--- |
| 42 | + |
| 43 | +## 🔌 Phase 2: Connecting the Wires (DOM Selection) (10 mins) |
| 44 | +* **Goal:** Get references to HTML elements so we can control them. |
| 45 | +* **Concept:** `document.getElementById` and `querySelector`. |
| 46 | + |
| 47 | +```javascript |
| 48 | +// 3. DOM ELEMENTS |
| 49 | +const elements = { |
| 50 | + timerDisplay: document.getElementById("timer-display"), |
| 51 | + toggleBtn: document.getElementById("toggle-btn"), |
| 52 | + toggleIcon: document.getElementById("toggle-icon"), // The play/pause icon |
| 53 | + resetBtn: document.getElementById("reset-btn"), |
| 54 | + modeButtons: document.querySelectorAll("[data-mode]"), // Selects all 3 buttons |
| 55 | +}; |
| 56 | +``` |
| 57 | +* *Tip:* Log `elements` to the console to show that they are real HTML nodes. |
| 58 | + |
| 59 | +--- |
| 60 | + |
| 61 | +## 🎨 Phase 3: The "Render" Loop (15 mins) |
| 62 | +* **Goal:** Make the screen reflect the `state`. **(Crucial Step)** |
| 63 | +* **Concept:** Data Driven UI. |
| 64 | + |
| 65 | +```javascript |
| 66 | +// 4. FUNCTIONS |
| 67 | + |
| 68 | +function updateUI() { |
| 69 | + // Calculated minutes and seconds |
| 70 | + const minutes = Math.floor(state.timeLeft / 60); |
| 71 | + const seconds = state.timeLeft % 60; |
| 72 | + |
| 73 | + // Format string: "25:00" using padStart |
| 74 | + elements.timerDisplay.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`; |
| 75 | + |
| 76 | + // Update Button Icon |
| 77 | + elements.toggleIcon.textContent = state.isRunning ? "pause" : "play_arrow"; |
| 78 | +} |
| 79 | + |
| 80 | +// Call it once to test! |
| 81 | +updateUI(); |
| 82 | +``` |
| 83 | +* *Demo:* Manually change `state.timeLeft = 100` in the code and refresh. See the UI update? That's the power of the render function. |
| 84 | + |
| 85 | +--- |
| 86 | + |
| 87 | +## ⏱️ Phase 4: Making it Tick (The Timer) (20 mins) |
| 88 | +* **Goal:** Start reducing `timeLeft` every second. |
| 89 | +* **Concept:** `setInterval` and `clearInterval`. |
| 90 | + |
| 91 | +### Step 4.1: Toggle Logic |
| 92 | +```javascript |
| 93 | +function toggleTimer() { |
| 94 | + if (state.isRunning) { |
| 95 | + // STOP LOGIC |
| 96 | + state.isRunning = false; |
| 97 | + clearInterval(state.timerInterval); |
| 98 | + } else { |
| 99 | + // START LOGIC |
| 100 | + state.isRunning = true; |
| 101 | + |
| 102 | + state.timerInterval = setInterval(() => { |
| 103 | + if (state.timeLeft > 0) { |
| 104 | + state.timeLeft--; |
| 105 | + updateUI(); // Don't forget to update the screen! |
| 106 | + } else { |
| 107 | + // Time's up! |
| 108 | + clearInterval(state.timerInterval); |
| 109 | + state.isRunning = false; |
| 110 | + alert("Time is up!"); |
| 111 | + updateUI(); |
| 112 | + } |
| 113 | + }, 1000); // 1000ms = 1 second |
| 114 | + } |
| 115 | + updateUI(); |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +### Step 4.2: Hook up the Event Listener |
| 120 | +```javascript |
| 121 | +// 5. EVENTS |
| 122 | +elements.toggleBtn.addEventListener("click", toggleTimer); |
| 123 | +``` |
| 124 | +* *Test:* Click the play button. Is it counting down? Click it again. Does it pause? |
| 125 | + |
| 126 | +--- |
| 127 | + |
| 128 | +## 🎛️ Phase 5: Controls (Modes & Reset) (15 mins) |
| 129 | +* **Goal:** Allow switching between Focus and Breaks. |
| 130 | +* **Concept:** Reusing logic. |
| 131 | + |
| 132 | +### Step 5.1: Reset |
| 133 | +```javascript |
| 134 | +function resetTimer() { |
| 135 | + state.isRunning = false; |
| 136 | + clearInterval(state.timerInterval); |
| 137 | + state.timeLeft = DURATIONS[state.mode]; // Reset to current mode's max time |
| 138 | + updateUI(); |
| 139 | +} |
| 140 | + |
| 141 | +elements.resetBtn.addEventListener("click", resetTimer); |
| 142 | +``` |
| 143 | + |
| 144 | +### Step 5.2: Switching Modes |
| 145 | +```javascript |
| 146 | +function switchMode(newMode) { |
| 147 | + state.mode = newMode; |
| 148 | + state.timeLeft = DURATIONS[newMode]; |
| 149 | + // Reuse reset logic effectively |
| 150 | + resetTimer(); |
| 151 | + |
| 152 | + // OPTIONAL: Update active button visual |
| 153 | + elements.modeButtons.forEach(btn => { |
| 154 | + btn.classList.toggle("active", btn.dataset.mode === newMode); |
| 155 | + }); |
| 156 | +} |
| 157 | + |
| 158 | +// Add listeners to chips |
| 159 | +elements.modeButtons.forEach(btn => { |
| 160 | + btn.addEventListener("click", () => { |
| 161 | + const mode = btn.dataset.mode; // Read from HTML data-mode="..." |
| 162 | + switchMode(mode); |
| 163 | + }); |
| 164 | +}); |
| 165 | +``` |
| 166 | + |
| 167 | +--- |
| 168 | + |
| 169 | +## 🚀 Phase 6: Wrap up & Q/A (10 mins) |
| 170 | +* Recap: We separated State (Data) from UI (View). |
| 171 | +* Challenge for students: "Can you change the colors when the mode changes?" (Hint: It's just adding a class or setting a CSS variable in `switchMode`). |
0 commit comments