Add backdrop click to Add/Edit Template modal, document dialog UI standards
- Add backdrop click handlers to showAddTemplateModal() and editTemplate() to close modal when clicking outside - Create new "Frontend UI Patterns" section in CLAUDE.md documenting modal dialog standards - Document backdrop click behavior with code example - Document close button requirements for dialogs with Cancel buttons Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
**IMPORTANT**: When making changes to server code (Python files in `src/wled_controller/`), you MUST restart the server if it's currently running to ensure the changes take effect.
|
**IMPORTANT**: When making changes to server code (Python files in `src/wled_controller/`), you MUST restart the server if it's currently running to ensure the changes take effect.
|
||||||
|
|
||||||
|
**NOTE**: Auto-reload is currently disabled (`reload=False` in `main.py`) due to watchfiles causing an infinite reload loop. Changes to server code will NOT be automatically picked up - manual server restart is required.
|
||||||
|
|
||||||
#### When to restart:
|
#### When to restart:
|
||||||
- After modifying API routes (`api/routes.py`, `api/schemas.py`)
|
- After modifying API routes (`api/routes.py`, `api/schemas.py`)
|
||||||
- After updating core logic (`core/*.py`)
|
- After updating core logic (`core/*.py`)
|
||||||
@@ -102,6 +104,56 @@ After restarting the server with new code:
|
|||||||
3. Use `t('key')` function in `static/app.js` for dynamic content
|
3. Use `t('key')` function in `static/app.js` for dynamic content
|
||||||
4. No server restart needed (frontend only)
|
4. No server restart needed (frontend only)
|
||||||
|
|
||||||
|
## Frontend UI Patterns
|
||||||
|
|
||||||
|
### Modal Dialogs
|
||||||
|
|
||||||
|
**IMPORTANT**: All modal dialogs must follow these standards for consistent UX:
|
||||||
|
|
||||||
|
#### Backdrop Click Behavior
|
||||||
|
All modals MUST close when the user clicks outside the dialog (on the backdrop). Implement this by adding a click handler that checks if the clicked element is the modal backdrop itself:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Show modal
|
||||||
|
const modal = document.getElementById('my-modal');
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
|
||||||
|
// Add backdrop click handler to close modal
|
||||||
|
modal.onclick = function(event) {
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeMyModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Where to add**: In every function that shows a modal (e.g., `showAddTemplateModal()`, `editTemplate()`, `showTestTemplateModal()`).
|
||||||
|
|
||||||
|
#### Close Button Requirement
|
||||||
|
Each modal dialog that has a "Cancel" button MUST also have a cross (×) close button at the top-right corner of the dialog. This provides users with multiple intuitive ways to dismiss the dialog:
|
||||||
|
|
||||||
|
1. Click the backdrop (outside the dialog)
|
||||||
|
2. Click the × button (top-right corner)
|
||||||
|
3. Click the Cancel button (bottom of dialog)
|
||||||
|
4. Press Escape key (if implemented)
|
||||||
|
|
||||||
|
**HTML Structure**:
|
||||||
|
```html
|
||||||
|
<div class="modal-content">
|
||||||
|
<button class="close-btn" onclick="closeMyModal()">×</button>
|
||||||
|
<h2>Dialog Title</h2>
|
||||||
|
<!-- dialog content -->
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button onclick="closeMyModal()">Cancel</button>
|
||||||
|
<button onclick="submitAction()">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**CSS Requirements**:
|
||||||
|
- Close button should be positioned absolutely at top-right
|
||||||
|
- Should be easily clickable (min 24px × 24px hit area)
|
||||||
|
- Should have clear hover state
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
Server uses API key authentication. Keys are configured in:
|
Server uses API key authentication. Keys are configured in:
|
||||||
|
|||||||
@@ -2281,7 +2281,16 @@ async function showAddTemplateModal() {
|
|||||||
// Load available engines
|
// Load available engines
|
||||||
await loadAvailableEngines();
|
await loadAvailableEngines();
|
||||||
|
|
||||||
document.getElementById('template-modal').style.display = 'flex';
|
// Show modal
|
||||||
|
const modal = document.getElementById('template-modal');
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
|
||||||
|
// Add backdrop click handler to close modal
|
||||||
|
modal.onclick = function(event) {
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeTemplateModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edit template
|
// Edit template
|
||||||
@@ -2315,7 +2324,16 @@ async function editTemplate(templateId) {
|
|||||||
document.getElementById('template-test-results').style.display = 'none';
|
document.getElementById('template-test-results').style.display = 'none';
|
||||||
document.getElementById('template-error').style.display = 'none';
|
document.getElementById('template-error').style.display = 'none';
|
||||||
|
|
||||||
document.getElementById('template-modal').style.display = 'flex';
|
// Show modal
|
||||||
|
const modal = document.getElementById('template-modal');
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
|
||||||
|
// Add backdrop click handler to close modal
|
||||||
|
modal.onclick = function(event) {
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeTemplateModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading template:', error);
|
console.error('Error loading template:', error);
|
||||||
showToast(t('templates.error.load') + ': ' + error.message, 'error');
|
showToast(t('templates.error.load') + ': ' + error.message, 'error');
|
||||||
|
|||||||
Reference in New Issue
Block a user