this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

custom add, fix desc forever (hopefully)

alice fe36b56e d29bdc4e

+178 -23
+16 -1
index.html
··· 19 19 </div> 20 20 </header> 21 21 22 + <!-- Add Value Form (Initially Hidden) --> 23 + <div id="addValueForm" class="modal" style="display: none;"> 24 + <div class="modal-content"> 25 + <h3>Add New Value</h3> 26 + <label for="newValueName">Value Name:</label> 27 + <input type="text" id="newValueName" required> 28 + <label for="newValueDesc">Description:</label> 29 + <textarea id="newValueDesc" rows="3"></textarea> 30 + <div class="modal-buttons"> 31 + <button id="saveNewValueBtn">Save</button> 32 + <button id="cancelNewValueBtn">Cancel</button> 33 + </div> 34 + </div> 35 + </div> 36 + 22 37 <!-- Part 1: Initial Sorting --> 23 38 <section id="part1" class="exercise-part"> 24 - <h2>Part 1: Initial Sorting</h2> 39 + <h2>Part 1: Initial Sorting <button id="showAddValueFormBtn" class="inline-btn">+ Add New</button></h2> 25 40 <div class="columns"> 26 41 <div class="column" data-column="unassigned"> 27 42 <h3>Unassigned</h3>
+102 -22
index.ts
··· 6 6 // Part2: same as Part1 7 7 // Part3: "core" or "additional" (for cards carried over from veryImportant) 8 8 order: number; 9 + description?: string; // Add optional description for custom cards 10 + isCustom?: boolean; // Flag for custom cards 9 11 } 10 12 11 13 export interface AppState { ··· 44 46 private state: AppState; 45 47 public undoManager: UndoManager<AppState>; 46 48 private storageKey: string = "valuesExerciseState"; 47 - // Update type annotation to use DebouncedFunc 48 - private debouncedUpdateFinalStatement: DebouncedFunc<(cardId: number, value: string) => void>; 49 + private nextCustomCardId: number = -1; // Counter for unique negative IDs 49 50 50 51 constructor() { 51 52 // Load state from localStorage or initialize default state. ··· 54 55 if (saved) { 55 56 try { 56 57 initialState = JSON.parse(saved) as AppState; 57 - // Ensure valueSet exists in saved state, default if not 58 58 if (!initialState.valueSet) { 59 59 initialState.valueSet = 'limited'; 60 60 } 61 + // Initialize nextCustomCardId based on saved custom cards 62 + const minId = Math.min(0, ...initialState.cards.filter(c => c.isCustom).map(c => c.id)); 63 + this.nextCustomCardId = minId - 1; 61 64 } catch { 62 - initialState = this.defaultState('limited'); // Default to limited on error 65 + initialState = this.defaultState('limited'); 66 + this.nextCustomCardId = -1; // Reset on error 63 67 } 64 68 } else { 65 - initialState = this.defaultState('limited'); // Default to limited initially 69 + initialState = this.defaultState('limited'); 70 + this.nextCustomCardId = -1; // Reset if no saved state 66 71 } 67 - this.state = initialState; // Set initial state before UndoManager 72 + this.state = initialState; 68 73 this.undoManager = new UndoManager<AppState>(this.state); 69 - 70 - // Re-add initialization using lodash debounce 71 - this.debouncedUpdateFinalStatement = debounce((cardId: number, value: string) => { 72 - const newState = this.undoManager.getState(); 73 - newState.finalStatements[cardId] = value; 74 - this.updateState(newState); 75 - }, 500); 76 74 77 75 this.bindEventListeners(); 78 76 this.render(); ··· 120 118 this.updateState(newState); 121 119 } 122 120 121 + // --- Add Value Form Logic --- 122 + private showAddValueForm() { 123 + const form = document.getElementById('addValueForm') as HTMLDivElement | null; 124 + const nameInput = document.getElementById('newValueName') as HTMLInputElement | null; 125 + const descInput = document.getElementById('newValueDesc') as HTMLTextAreaElement | null; 126 + if (form && nameInput && descInput) { 127 + nameInput.value = ''; // Clear previous input 128 + descInput.value = ''; 129 + form.style.display = 'flex'; // Show the modal 130 + nameInput.focus(); // Focus the name input 131 + } 132 + } 133 + 134 + private hideAddValueForm() { 135 + const form = document.getElementById('addValueForm') as HTMLDivElement | null; 136 + if (form) { 137 + form.style.display = 'none'; // Hide the modal 138 + } 139 + } 140 + 141 + private saveNewValue() { 142 + const nameInput = document.getElementById('newValueName') as HTMLInputElement | null; 143 + const descInput = document.getElementById('newValueDesc') as HTMLTextAreaElement | null; 144 + const name = nameInput?.value.trim().toUpperCase(); // Normalize name 145 + const description = descInput?.value.trim(); 146 + 147 + if (!name) { 148 + alert("Please enter a name for the new value."); 149 + nameInput?.focus(); 150 + return; 151 + } 152 + if (!description) { 153 + alert("Please enter a description for the new value."); 154 + descInput?.focus(); 155 + return; 156 + } 157 + 158 + const newState = this.undoManager.getState(); 159 + 160 + // Check for duplicates (case-insensitive) 161 + if (newState.cards.some(card => card.name.toUpperCase() === name)) { 162 + alert(`Value "${name}" already exists.`); 163 + nameInput?.focus(); 164 + return; 165 + } 166 + 167 + // Create new card 168 + const newCard: ValueCard = { 169 + id: this.nextCustomCardId, 170 + name: name, 171 + description: description, // Store description directly on card 172 + column: 'unassigned', // Add to unassigned in current view 173 + order: newState.cards.length, // Add to end 174 + isCustom: true 175 + }; 176 + 177 + this.nextCustomCardId--; // Decrement for next custom card 178 + 179 + newState.cards.push(newCard); 180 + this.updateState(newState); 181 + this.hideAddValueForm(); 182 + } 183 + 123 184 // Bind event listeners for UI interactions. 124 185 private bindEventListeners() { 125 186 const appInstance = this; // Capture the App instance 126 187 188 + // --- Add Value Form Listeners --- 189 + document.getElementById('showAddValueFormBtn')?.addEventListener('click', () => { 190 + this.showAddValueForm(); 191 + }); 192 + document.getElementById('saveNewValueBtn')?.addEventListener('click', () => { 193 + this.saveNewValue(); 194 + }); 195 + document.getElementById('cancelNewValueBtn')?.addEventListener('click', () => { 196 + this.hideAddValueForm(); 197 + }); 198 + // Close modal if clicking outside the content 199 + document.getElementById('addValueForm')?.addEventListener('click', (e) => { 200 + if (e.target === e.currentTarget) { 201 + this.hideAddValueForm(); 202 + } 203 + }); 204 + 127 205 // Navigation buttons 128 206 document.getElementById("toPart2")?.addEventListener("click", () => { 129 207 const newState = this.undoManager.getState(); ··· 171 249 this.updateState(newState); // Call updateState directly 172 250 }); 173 251 document.getElementById("finish")?.addEventListener("click", () => { 174 - // Flush any pending debounced updates immediately 175 - this.debouncedUpdateFinalStatement.flush(); 176 - 177 252 const newState = this.undoManager.getState(); 178 253 // Check if all core values have statements 179 254 const coreCards = newState.cards.filter(c => c.column === 'core'); ··· 300 375 301 376 const descriptionElem = document.createElement("span"); 302 377 descriptionElem.className = "card-description"; 303 - // Look up description from the map 304 - descriptionElem.textContent = valueDefinitionsMap.get(card.name) || ""; 378 + // Prioritize card.description, fall back to map for built-in values 379 + descriptionElem.textContent = card.description || valueDefinitionsMap.get(card.name) || ""; 305 380 306 381 cardElem.appendChild(nameElem); 307 382 cardElem.appendChild(descriptionElem); ··· 443 518 input.type = "text"; 444 519 input.id = `statement-${card.id}`; 445 520 input.value = this.state.finalStatements[card.id] || ""; 446 - // Use 'input' event to call the debounced update function 447 - input.addEventListener("input", () => { 448 - // Call the debounced function, passing necessary info 449 - this.debouncedUpdateFinalStatement(card.id, input.value); 521 + // Use 'blur' event to update state directly when field loses focus 522 + input.addEventListener("blur", () => { 523 + const currentState = this.undoManager.getState(); // Get latest state 524 + // Avoid modifying the state if it hasn't actually changed 525 + if (currentState.finalStatements[card.id] !== input.value) { 526 + const newState = this.undoManager.getState(); // Get a fresh copy to modify 527 + newState.finalStatements[card.id] = input.value; 528 + this.updateState(newState); // Update authoritative state 529 + } 450 530 }); 451 531 wrapper.appendChild(label); 452 532 wrapper.appendChild(input);
+60
styles.css
··· 143 143 #reviewContent > ul li { 144 144 margin-bottom: 5px; 145 145 } 146 + 147 + /* Modal Styles */ 148 + .modal { 149 + position: fixed; 150 + left: 0; 151 + top: 0; 152 + width: 100%; 153 + height: 100%; 154 + background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */ 155 + display: flex; 156 + justify-content: center; 157 + align-items: center; 158 + z-index: 1000; /* Ensure it's on top */ 159 + } 160 + 161 + .modal-content { 162 + background-color: #fff; 163 + padding: 20px; 164 + border-radius: 5px; 165 + width: 90%; 166 + max-width: 400px; 167 + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 168 + } 169 + 170 + .modal-content label { 171 + display: block; 172 + margin-top: 10px; 173 + margin-bottom: 5px; 174 + font-weight: bold; 175 + } 176 + 177 + .modal-content input[type="text"], 178 + .modal-content textarea { 179 + width: calc(100% - 20px); /* Account for padding */ 180 + padding: 8px; 181 + margin-bottom: 10px; 182 + border: 1px solid #ccc; 183 + border-radius: 4px; 184 + } 185 + 186 + .modal-buttons { 187 + text-align: right; 188 + margin-top: 15px; 189 + } 190 + 191 + .modal-buttons button { 192 + margin-left: 10px; 193 + padding: 8px 15px; 194 + } 195 + 196 + /* Inline button style */ 197 + .inline-btn { 198 + margin-left: 15px; 199 + font-size: 0.9em; 200 + padding: 2px 8px; 201 + } 202 + 203 + /* Add other existing styles below */ 204 + #app { 205 + } 146 206