fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled

This commit is contained in:
2025-11-24 21:28:25 +01:00
parent 4eb7134853
commit 77abc65cd7
1327 changed files with 91915 additions and 9909 deletions

View File

@@ -280,6 +280,214 @@ export class DomPatcher {
// Fallback - not guaranteed to be unique
return element.tagName.toLowerCase();
}
/**
* Swap element content using different strategies
*
* Supports multiple swap strategies similar to htmx:
* - innerHTML: Replace inner content (default)
* - outerHTML: Replace element itself
* - beforebegin: Insert before element
* - afterbegin: Insert at start of element
* - afterend: Insert after element
* - beforeend: Insert at end of element
* - none: No DOM update (only events/state)
*
* @param {HTMLElement} target - Target element to swap
* @param {string} html - HTML content to swap
* @param {string} strategy - Swap strategy (default: 'innerHTML')
* @param {string} transition - Optional transition type (fade, slide, none)
* @param {Object} scrollOptions - Optional scroll options { enabled, target, behavior }
* @returns {boolean} - True if swap was successful
*/
swapElement(target, html, strategy = 'innerHTML', transition = null, scrollOptions = null) {
if (!target || !target.parentNode) {
console.warn('[DomPatcher] Invalid target element for swap');
return false;
}
// Preserve focus state before swap
const restoreFocus = this.preserveFocus(target);
// Apply transition if specified
if (transition && transition !== 'none') {
this.applyTransition(target, transition, strategy);
}
// Parse HTML into document fragment
const temp = document.createElement('div');
temp.innerHTML = html;
const newContent = temp.firstElementChild || temp;
try {
switch (strategy) {
case 'innerHTML':
// Replace inner content (default behavior)
target.innerHTML = html;
break;
case 'outerHTML':
// Replace element itself
if (newContent.nodeType === Node.ELEMENT_NODE) {
target.parentNode.replaceChild(newContent.cloneNode(true), target);
} else {
// If HTML doesn't have a single root element, wrap it
const wrapper = document.createElement('div');
wrapper.innerHTML = html;
while (wrapper.firstChild) {
target.parentNode.insertBefore(wrapper.firstChild, target);
}
target.parentNode.removeChild(target);
}
break;
case 'beforebegin':
// Insert before target element
if (newContent.nodeType === Node.ELEMENT_NODE) {
target.parentNode.insertBefore(newContent.cloneNode(true), target);
} else {
// Multiple nodes - insert all
const wrapper = document.createElement('div');
wrapper.innerHTML = html;
while (wrapper.firstChild) {
target.parentNode.insertBefore(wrapper.firstChild, target);
}
}
break;
case 'afterbegin':
// Insert at start of target element
if (newContent.nodeType === Node.ELEMENT_NODE) {
target.insertBefore(newContent.cloneNode(true), target.firstChild);
} else {
// Multiple nodes - insert all at start
const wrapper = document.createElement('div');
wrapper.innerHTML = html;
while (wrapper.firstChild) {
target.insertBefore(wrapper.firstChild, target.firstChild);
}
}
break;
case 'afterend':
// Insert after target element
if (newContent.nodeType === Node.ELEMENT_NODE) {
target.parentNode.insertBefore(newContent.cloneNode(true), target.nextSibling);
} else {
// Multiple nodes - insert all after
const wrapper = document.createElement('div');
wrapper.innerHTML = html;
const nextSibling = target.nextSibling;
while (wrapper.firstChild) {
target.parentNode.insertBefore(wrapper.firstChild, nextSibling);
}
}
break;
case 'beforeend':
// Insert at end of target element
if (newContent.nodeType === Node.ELEMENT_NODE) {
target.appendChild(newContent.cloneNode(true));
} else {
// Multiple nodes - append all
const wrapper = document.createElement('div');
wrapper.innerHTML = html;
while (wrapper.firstChild) {
target.appendChild(wrapper.firstChild);
}
}
break;
case 'none':
// No DOM update - only events/state will be handled
// This is handled by the caller, so we just return success
return true;
default:
console.warn(`[DomPatcher] Unknown swap strategy: ${strategy}, falling back to innerHTML`);
target.innerHTML = html;
break;
}
// Restore focus after swap (only for strategies that don't remove the target)
if (strategy !== 'outerHTML' && strategy !== 'none') {
restoreFocus();
}
// Handle scroll behavior if specified
if (scrollOptions && scrollOptions.enabled) {
this.scrollToTarget(scrollOptions.target || target, scrollOptions.behavior || 'smooth');
}
return true;
} catch (error) {
console.error('[DomPatcher] Error during swap:', error);
return false;
}
}
/**
* Apply CSS transition to element
*
* @param {HTMLElement} element - Element to apply transition to
* @param {string} transition - Transition type (fade, slide)
* @param {string} strategy - Swap strategy
*/
applyTransition(element, transition, strategy) {
// Add transition class
const transitionClass = `lc-transition-${transition}`;
// For fade/slide transitions, we need to apply the transition class
// and then trigger the transition by adding an active class
element.classList.add(transitionClass);
// Force reflow to ensure class is applied
void element.offsetWidth;
// Add active class to trigger transition
requestAnimationFrame(() => {
element.classList.add('lc-transition-active');
});
// Remove transition classes after animation completes
const handleTransitionEnd = (e) => {
// Only handle transition end for this element
if (e.target === element) {
element.classList.remove(transitionClass);
element.classList.remove('lc-transition-active');
element.removeEventListener('transitionend', handleTransitionEnd);
}
};
element.addEventListener('transitionend', handleTransitionEnd, { once: true });
}
/**
* Scroll to target element
*
* @param {HTMLElement|string} target - Target element or selector
* @param {string} behavior - Scroll behavior (smooth, instant)
*/
scrollToTarget(target, behavior = 'smooth') {
let targetElement = target;
// If target is a string, try to find element
if (typeof target === 'string') {
targetElement = document.querySelector(target);
}
if (!targetElement || !(targetElement instanceof HTMLElement)) {
console.warn('[DomPatcher] Scroll target not found:', target);
return;
}
// Scroll to element
targetElement.scrollIntoView({
behavior: behavior === 'smooth' ? 'smooth' : 'auto',
block: 'start',
inline: 'nearest'
});
}
}
// Create singleton instance