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

@@ -0,0 +1,299 @@
/**
* Trigger Manager for LiveComponents
*
* Handles advanced trigger options:
* - delay: Delay before execution
* - throttle: Throttle instead of debounce
* - once: Execute only once
* - changed: Only execute on value change
* - from: Event from another element
* - load: Execute on element load
*/
export class TriggerManager {
constructor() {
this.triggeredOnce = new Set(); // Track elements that have triggered once
this.throttleTimers = new Map(); // Track throttle timers
this.lastValues = new Map(); // Track last values for 'changed' trigger
this.loadHandled = new Set(); // Track elements that have handled load event
}
/**
* Setup trigger for an element
*
* @param {HTMLElement} element - Element to setup trigger for
* @param {string} componentId - Component ID
* @param {string} action - Action name
* @param {Function} handler - Action handler function
* @param {string} defaultEvent - Default event type (click, input, change, etc.)
*/
setupTrigger(element, componentId, action, handler, defaultEvent = 'click') {
// Parse trigger options
const delay = this.parseDelay(element.dataset.lcTriggerDelay);
const throttle = this.parseThrottle(element.dataset.lcTriggerThrottle);
const once = element.dataset.lcTriggerOnce === 'true';
const changed = element.dataset.lcTriggerChanged === 'true';
const from = element.dataset.lcTriggerFrom;
const load = element.dataset.lcTriggerLoad === 'true';
// Handle 'once' trigger - check if already triggered
if (once) {
const triggerKey = `${componentId}_${action}_${this.getElementKey(element)}`;
if (this.triggeredOnce.has(triggerKey)) {
return; // Already triggered, don't setup again
}
}
// Handle 'load' trigger
if (load) {
const loadKey = `${componentId}_${action}_${this.getElementKey(element)}`;
if (!this.loadHandled.has(loadKey)) {
this.loadHandled.add(loadKey);
// Execute immediately if element is already loaded
if (document.readyState === 'complete' || document.readyState === 'interactive') {
this.executeWithOptions(element, componentId, action, handler, delay, throttle, changed);
} else {
// Wait for load
window.addEventListener('load', () => {
this.executeWithOptions(element, componentId, action, handler, delay, throttle, changed);
}, { once: true });
}
}
}
// Handle 'from' trigger - delegate event from another element
if (from) {
const sourceElement = document.querySelector(from);
if (sourceElement) {
this.setupDelegatedTrigger(sourceElement, element, componentId, action, handler, defaultEvent, delay, throttle, changed);
return; // Don't setup direct trigger
} else {
console.warn(`[TriggerManager] Source element not found for trigger-from: ${from}`);
}
}
// Setup direct trigger
const eventType = this.getEventType(element, defaultEvent);
const wrappedHandler = (e) => {
// Check 'once' trigger
if (once) {
const triggerKey = `${componentId}_${action}_${this.getElementKey(element)}`;
if (this.triggeredOnce.has(triggerKey)) {
return; // Already triggered
}
this.triggeredOnce.add(triggerKey);
}
this.executeWithOptions(element, componentId, action, handler, delay, throttle, changed, e);
};
element.addEventListener(eventType, wrappedHandler);
}
/**
* Setup delegated trigger (trigger-from)
*
* @param {HTMLElement} sourceElement - Element that triggers the event
* @param {HTMLElement} targetElement - Element that receives the action
* @param {string} componentId - Component ID
* @param {string} action - Action name
* @param {Function} handler - Action handler
* @param {string} eventType - Event type
* @param {number} delay - Delay in ms
* @param {number} throttle - Throttle in ms
* @param {boolean} changed - Only on value change
*/
setupDelegatedTrigger(sourceElement, targetElement, componentId, action, handler, eventType, delay, throttle, changed) {
const wrappedHandler = (e) => {
this.executeWithOptions(targetElement, componentId, action, handler, delay, throttle, changed, e);
};
sourceElement.addEventListener(eventType, wrappedHandler);
}
/**
* Execute handler with trigger options
*
* @param {HTMLElement} element - Element
* @param {string} componentId - Component ID
* @param {string} action - Action name
* @param {Function} handler - Handler function
* @param {number} delay - Delay in ms
* @param {number} throttle - Throttle in ms
* @param {boolean} changed - Only on value change
* @param {Event} event - Original event
*/
executeWithOptions(element, componentId, action, handler, delay, throttle, changed, event) {
// Check 'changed' trigger
if (changed) {
const currentValue = this.getElementValue(element);
const valueKey = `${componentId}_${action}_${this.getElementKey(element)}`;
const lastValue = this.lastValues.get(valueKey);
if (currentValue === lastValue) {
return; // Value hasn't changed
}
this.lastValues.set(valueKey, currentValue);
}
// Apply throttle
if (throttle > 0) {
const throttleKey = `${componentId}_${action}_${this.getElementKey(element)}`;
const lastExecution = this.throttleTimers.get(throttleKey);
if (lastExecution && Date.now() - lastExecution < throttle) {
return; // Throttled
}
this.throttleTimers.set(throttleKey, Date.now());
}
// Apply delay
if (delay > 0) {
setTimeout(() => {
handler(event);
}, delay);
} else {
handler(event);
}
}
/**
* Parse delay value (e.g., "500ms", "1s", "500")
*
* @param {string} delayStr - Delay string
* @returns {number} - Delay in milliseconds
*/
parseDelay(delayStr) {
if (!delayStr) return 0;
const match = delayStr.match(/^(\d+)(ms|s)?$/);
if (!match) return 0;
const value = parseInt(match[1]);
const unit = match[2] || 'ms';
return unit === 's' ? value * 1000 : value;
}
/**
* Parse throttle value (e.g., "100ms", "1s", "100")
*
* @param {string} throttleStr - Throttle string
* @returns {number} - Throttle in milliseconds
*/
parseThrottle(throttleStr) {
if (!throttleStr) return 0;
const match = throttleStr.match(/^(\d+)(ms|s)?$/);
if (!match) return 0;
const value = parseInt(match[1]);
const unit = match[2] || 'ms';
return unit === 's' ? value * 1000 : value;
}
/**
* Get event type for element
*
* @param {HTMLElement} element - Element
* @param {string} defaultEvent - Default event type
* @returns {string} - Event type
*/
getEventType(element, defaultEvent) {
// For form elements, use appropriate event
if (element.tagName === 'FORM') {
return 'submit';
}
if (element.tagName === 'SELECT') {
return 'change';
}
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
return element.type === 'checkbox' || element.type === 'radio' ? 'change' : 'input';
}
return defaultEvent;
}
/**
* Get element value for 'changed' trigger
*
* @param {HTMLElement} element - Element
* @returns {string} - Element value
*/
getElementValue(element) {
if (element.tagName === 'INPUT') {
if (element.type === 'checkbox' || element.type === 'radio') {
return element.checked ? 'true' : 'false';
}
return element.value || '';
}
if (element.tagName === 'TEXTAREA' || element.tagName === 'SELECT') {
return element.value || '';
}
return element.textContent || '';
}
/**
* Get unique key for element (for tracking)
*
* @param {HTMLElement} element - Element
* @returns {string} - Unique key
*/
getElementKey(element) {
return element.id || element.name || `${element.tagName}_${element.className}` || 'unknown';
}
/**
* Clean up resources for component
*
* @param {string} componentId - Component ID
*/
cleanup(componentId) {
// Remove all entries for this component
const keysToRemove = [];
for (const key of this.triggeredOnce) {
if (key.startsWith(`${componentId}_`)) {
keysToRemove.push(key);
}
}
keysToRemove.forEach(key => this.triggeredOnce.delete(key));
// Clean throttle timers
const timersToRemove = [];
for (const key of this.throttleTimers.keys()) {
if (key.startsWith(`${componentId}_`)) {
timersToRemove.push(key);
}
}
timersToRemove.forEach(key => this.throttleTimers.delete(key));
// Clean last values
const valuesToRemove = [];
for (const key of this.lastValues.keys()) {
if (key.startsWith(`${componentId}_`)) {
valuesToRemove.push(key);
}
}
valuesToRemove.forEach(key => this.lastValues.delete(key));
// Clean load handled
const loadToRemove = [];
for (const key of this.loadHandled) {
if (key.startsWith(`${componentId}_`)) {
loadToRemove.push(key);
}
}
loadToRemove.forEach(key => this.loadHandled.delete(key));
}
}
// Create singleton instance
export const triggerManager = new TriggerManager();
export default triggerManager;