.
This commit is contained in:
@@ -40,28 +40,70 @@
|
|||||||
#modal .modal-content { position:relative; background:white; padding:1rem; border-radius:6px; box-shadow:0 6px 24px rgba(0,0,0,0.2); max-width:90%; max-height:90%; overflow:auto; z-index:2 }
|
#modal .modal-content { position:relative; background:white; padding:1rem; border-radius:6px; box-shadow:0 6px 24px rgba(0,0,0,0.2); max-width:90%; max-height:90%; overflow:auto; z-index:2 }
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
// Show modal when HTMX swaps content into #modal, and provide backdrop/close behavior
|
// Show modal when HTMX swaps content into #modal, and provide backdrop/close behavior.
|
||||||
|
// Important: avoid destructive DOM moves (innerHTML replacement) while HTMX may still hold
|
||||||
|
// references to nodes. Instead, add a backdrop and tag the first returned element as
|
||||||
|
// the modal content to minimize structural changes and avoid racing HTMX internals.
|
||||||
document.body.addEventListener('htmx:afterSwap', function(evt){
|
document.body.addEventListener('htmx:afterSwap', function(evt){
|
||||||
try{
|
try{
|
||||||
const target = evt.detail.target;
|
const target = evt.detail && evt.detail.target;
|
||||||
if(target && target.id === 'modal'){
|
if(!target || target.id !== 'modal') return;
|
||||||
const modal = document.getElementById('modal');
|
const modal = document.getElementById('modal');
|
||||||
// If server returned empty content, hide modal
|
if(!modal) return;
|
||||||
if(!modal.innerHTML.trim()){ modal.style.display='none';
|
|
||||||
// clear calendar selections on modal close
|
// If server returned empty content, hide modal and clear selections
|
||||||
|
const raw = modal.innerHTML || '';
|
||||||
|
if(!raw.trim()){
|
||||||
|
modal.style.display='none';
|
||||||
try{ if(window.clearAllCalendarSelections) window.clearAllCalendarSelections(); else if(window._calendars) Object.values(window._calendars).forEach(c=>c.clearSelection && c.clearSelection()); }catch(e){}
|
try{ if(window.clearAllCalendarSelections) window.clearAllCalendarSelections(); else if(window._calendars) Object.values(window._calendars).forEach(c=>c.clearSelection && c.clearSelection()); }catch(e){}
|
||||||
return; }
|
return;
|
||||||
// wrap content if needed
|
}
|
||||||
|
|
||||||
|
// If no element with .modal-content exists, try a non-destructive approach:
|
||||||
|
// 1) ensure a backdrop element exists (insert if missing)
|
||||||
|
// 2) find the first returned element (excluding backdrop) and mark it as modal-content
|
||||||
|
if(!modal.querySelector('.modal-content')){
|
||||||
if(!modal.querySelector('.modal-backdrop')){
|
if(!modal.querySelector('.modal-backdrop')){
|
||||||
|
const backdrop = document.createElement('div');
|
||||||
|
backdrop.className = 'modal-backdrop';
|
||||||
|
// insert backdrop as first child to sit behind content
|
||||||
|
modal.insertBefore(backdrop, modal.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// choose an existing child element to be the content. Prefer the first element
|
||||||
|
// that isn't the backdrop. Do not recreate nodes if possible — just add a class.
|
||||||
|
let contentElem = null;
|
||||||
|
for(const child of Array.from(modal.children)){
|
||||||
|
if(child.classList && child.classList.contains('modal-backdrop')) continue;
|
||||||
|
if(child.classList && child.classList.contains('modal-content')){ contentElem = child; break; }
|
||||||
|
// skip empty text nodes
|
||||||
|
if(child.nodeType === Node.ELEMENT_NODE){ contentElem = child; break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(contentElem){
|
||||||
|
contentElem.classList.add('modal-content');
|
||||||
|
} else {
|
||||||
|
// fallback: if there was no element to mark (unlikely), safely create a wrapper
|
||||||
|
// and move other nodes inside it. This is a last-resort path guarded in try/catch.
|
||||||
|
try{
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'modal-content';
|
||||||
|
// move existing children into wrapper
|
||||||
|
while(modal.firstChild){
|
||||||
|
wrapper.appendChild(modal.firstChild);
|
||||||
|
}
|
||||||
|
// ensure backdrop is first child
|
||||||
const backdrop = document.createElement('div'); backdrop.className='modal-backdrop';
|
const backdrop = document.createElement('div'); backdrop.className='modal-backdrop';
|
||||||
const content = document.createElement('div'); content.className='modal-content';
|
|
||||||
// move existing children into content
|
|
||||||
while(modal.firstChild){ content.appendChild(modal.firstChild); }
|
|
||||||
modal.appendChild(backdrop);
|
modal.appendChild(backdrop);
|
||||||
modal.appendChild(content);
|
modal.appendChild(wrapper);
|
||||||
|
}catch(e){
|
||||||
|
// If moving nodes fails, log but don't throw — keep modal visible with whatever was returned.
|
||||||
|
console.error('modal wrapping fallback failed', e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
}
|
|
||||||
}catch(e){ console.error('modal afterSwap handler error', e); }
|
}catch(e){ console.error('modal afterSwap handler error', e); }
|
||||||
});
|
});
|
||||||
// Helper to close the app modal and clear calendar selections
|
// Helper to close the app modal and clear calendar selections
|
||||||
|
|||||||
Reference in New Issue
Block a user