UNPKG

@ingridsandev/chatbot-widget

Version:

A framework-agnostic chatbot widget using Web Components

167 lines (143 loc) 4.38 kB
import EventEmitter from '../utils/event-emitter.js'; import Storage from '../utils/storage.js'; const STORAGE_KEY = 'selectedAgent'; const CSS_CLASSES = { CONTAINER: 'chatbot-agent-selector', SELECT: 'chatbot-agent-select' }; const DEFAULT_AVATAR = '🤖'; /** * Agent selector component for managing chatbot agents */ class AgentSelector extends EventEmitter { /** * @param {Array} agents - Array of agent objects */ constructor(agents = []) { super(); this.agents = Array.isArray(agents) ? agents : []; this.currentAgent = this.agents.length > 0 ? this.agents[0] : null; this.storage = new Storage(); this._boundHandleAgentChange = this._handleAgentChange.bind(this); this._loadSavedAgent(); } /** * Load previously saved agent preference * @private */ _loadSavedAgent() { try { const savedAgent = this.storage.get(STORAGE_KEY); if (savedAgent) { const agent = this.agents.find(a => a.id === savedAgent); if (agent) { this.currentAgent = agent; } } } catch (error) { console.warn('Failed to load saved agent preference:', error); } } render() { const container = document.createElement('div'); container.className = CSS_CLASSES.CONTAINER; if (this.agents.length <= 1) { container.style.display = 'none'; return container; } const select = document.createElement('select'); select.className = CSS_CLASSES.SELECT; select.setAttribute('aria-label', 'Select chatbot agent'); this.agents.forEach(agent => { if (!agent || !agent.id) return; const option = document.createElement('option'); option.value = agent.id; option.textContent = `${agent.avatar || DEFAULT_AVATAR} ${agent.name || 'Unnamed Agent'}`; option.selected = this.currentAgent && this.currentAgent.id === agent.id; select.appendChild(option); }); select.addEventListener('change', this._boundHandleAgentChange); this._selectElement = select; container.appendChild(select); return container; } /** * Handle agent selection change * @private */ _handleAgentChange(e) { const selectedAgent = this.agents.find(agent => agent.id === e.target.value); if (selectedAgent) { this.setAgent(selectedAgent); } } /** * Set the current agent * @param {Object} agent - Agent object to set as current */ setAgent(agent) { if (!agent || !agent.id) { console.warn('Invalid agent provided to setAgent'); return; } const previousAgent = this.currentAgent; this.currentAgent = agent; try { this.storage.set(STORAGE_KEY, agent.id); } catch (error) { console.warn('Failed to save agent preference:', error); } this.emit('agentChange', { agent, previousAgent }); } /** * Get the currently selected agent * @returns {Object|null} Current agent object or null */ getCurrentAgent() { return this.currentAgent; } /** * Update the available agents list * @param {Array} agents - New array of agent objects */ updateAgents(agents) { if (!Array.isArray(agents)) { console.warn('updateAgents expects an array, received:', typeof agents); return; } this.agents = agents; if (this.currentAgent && !this.agents.find(a => a?.id === this.currentAgent.id)) { const previousAgent = this.currentAgent; const newAgent = this.agents.length > 0 ? this.agents[0] : null; if (newAgent && newAgent !== this.currentAgent) { this.setAgent(newAgent); } else if (!newAgent) { // Handle case where no agents are available this.currentAgent = null; try { this.storage.remove?.(STORAGE_KEY); } catch (error) { console.warn('Failed to remove saved agent preference:', error); } this.emit('agentChange', { agent: null, previousAgent }); } } } /** * Clean up event listeners and references */ destroy() { if (this._selectElement && this._boundHandleAgentChange) { this._selectElement.removeEventListener('change', this._boundHandleAgentChange); this._selectElement = null; } this.removeAllListeners(); } } export default AgentSelector;