UNPKG

docusaurus-openai-search

Version:

AI-powered search plugin for Docusaurus - extends Algolia search with intelligent keyword generation and RAG-based answers

273 lines (272 loc) 7.04 kB
/** * Cleanup Manager Utility for Docusaurus AI Search * Provides centralized cleanup mechanisms for DOM elements, refs, and resources */ export class CleanupManager { constructor() { this.tasks = new Map(); this.isDestroyed = false; } static getInstance() { if (!CleanupManager.instance) { CleanupManager.instance = new CleanupManager(); } return CleanupManager.instance; } /** * Reset the singleton instance (for testing/cleanup) */ static reset() { if (CleanupManager.instance) { CleanupManager.instance.cleanup(); CleanupManager.instance = null; } } /** * Register a cleanup task */ register(task) { if (this.isDestroyed) return; this.tasks.set(task.id, task); } /** * Unregister a cleanup task */ unregister(taskId) { this.tasks.delete(taskId); } /** * Execute cleanup for a specific task */ cleanupTask(taskId) { const task = this.tasks.get(taskId); if (task) { try { task.cleanup(); } catch (error) { console.error(`Cleanup failed for task ${taskId}:`, error); } this.tasks.delete(taskId); } } /** * Execute all cleanup tasks */ cleanup() { if (this.isDestroyed) return; // Sort tasks by priority (higher priority first) const sortedTasks = Array.from(this.tasks.values()) .sort((a, b) => (b.priority || 0) - (a.priority || 0)); // Execute cleanup tasks for (const task of sortedTasks) { try { task.cleanup(); } catch (error) { console.error(`Cleanup failed for task ${task.id}:`, error); } } this.tasks.clear(); this.isDestroyed = true; } /** * Get number of registered tasks */ getTaskCount() { return this.tasks.size; } /** * Check if cleanup manager is destroyed */ isDestroyed_() { return this.isDestroyed; } } /** * DOM Element Cleanup Utilities */ export class DOMCleanupUtils { /** * Clear all child nodes from an element */ static clearElement(element) { if (!element) return; while (element.firstChild) { element.removeChild(element.firstChild); } } /** * Remove element from DOM if it exists */ static removeElement(element) { if (element && element.parentNode) { element.parentNode.removeChild(element); } } /** * Clear element attributes */ static clearAttributes(element, preserveAttributes = []) { if (!element) return; const attributesToRemove = []; // Collect attributes to remove for (let i = 0; i < element.attributes.length; i++) { const attr = element.attributes[i]; if (!preserveAttributes.includes(attr.name)) { attributesToRemove.push(attr.name); } } // Remove attributes attributesToRemove.forEach(attrName => { element.removeAttribute(attrName); }); } /** * Reset element to initial state */ static resetElement(element, preserveAttributes = ['id', 'class']) { if (!element) return; this.clearElement(element); this.clearAttributes(element, preserveAttributes); } } /** * Ref Cleanup Utilities */ export class RefCleanupUtils { /** * Clear React ref */ static clearRef(ref) { if (ref && 'current' in ref) { ref.current = null; } } /** * Clear multiple refs of any type */ static clearRefs(...refs) { refs.forEach(ref => this.clearRef(ref)); } /** * Reset ref to initial value */ static resetRef(ref, initialValue) { if (ref && 'current' in ref) { ref.current = initialValue; } } } /** * Timer Cleanup Utilities */ export class TimerCleanupUtils { /** * Register a timer for cleanup */ static registerTimer(timer) { this.timers.add(timer); return timer; } /** * Clear a specific timer */ static clearTimer(timer) { clearTimeout(timer); this.timers.delete(timer); } /** * Clear all registered timers */ static clearAllTimers() { this.timers.forEach(timer => clearTimeout(timer)); this.timers.clear(); } /** * Create a timeout that auto-registers for cleanup */ static setTimeout(callback, delay) { const timer = setTimeout(() => { callback(); this.timers.delete(timer); }, delay); return this.registerTimer(timer); } /** * Create an interval that auto-registers for cleanup */ static setInterval(callback, delay) { const timer = setInterval(callback, delay); return this.registerTimer(timer); } } TimerCleanupUtils.timers = new Set(); /** * Modal-specific cleanup utilities */ export class ModalCleanupUtils { /** * Reset modal state */ static resetModalState(setState, initialState) { setState(initialState); } /** * Clear modal refs and DOM elements */ static clearModalRefs(...refs) { RefCleanupUtils.clearRefs(...refs); } /** * Complete modal cleanup */ static cleanupModal(options) { const { refs = [], states = [], cleanupTasks = [] } = options; // Clear refs RefCleanupUtils.clearRefs(...refs); // Reset states states.forEach(({ setter, initialValue }) => { setter(initialValue); }); // Execute cleanup tasks const cleanupManager = CleanupManager.getInstance(); cleanupTasks.forEach(taskId => { cleanupManager.cleanupTask(taskId); }); } } /** * Hook for component cleanup */ export function useCleanup(componentId) { const cleanupManager = CleanupManager.getInstance(); const registerCleanup = (taskId, cleanup, priority) => { cleanupManager.register({ id: `${componentId}-${taskId}`, cleanup, priority }); }; const unregisterCleanup = (taskId) => { cleanupManager.unregister(`${componentId}-${taskId}`); }; const cleanupComponent = () => { // Find and cleanup all tasks for this component const tasks = Array.from(cleanupManager['tasks'].entries()) .filter(([id]) => id.startsWith(`${componentId}-`)); tasks.forEach(([id]) => { cleanupManager.cleanupTask(id); }); }; return { registerCleanup, unregisterCleanup, cleanupComponent }; }