UNPKG

@agentman/chat-widget

Version:

Agentman Chat Widget for easy integration with web applications

251 lines (200 loc) 7.58 kB
# Header Button System ## Overview The Header Button System provides a **unified, maintainable approach** to managing all header buttons across the chat widget, replacing the previous fragmented implementation with multiple button components for the same actions. ## Problem Solved ### Before (Unmaintainable) - **5+ different button components** for collapse/minimize action - Duplicate code across `InputBarSheetController`, `ConversationView`, `ConversationManager`, `WelcomeScreen`, `UIManager` - Inconsistent styling and behavior - Hard to maintain and extend - Violates DRY principles ### After (Unified) - **Single `HeaderButton` component** for all button types - **`HeaderButtonManager`** handles lifecycle and positioning - Consistent behavior across all variants - Easy to maintain and extend - DRY compliant ## Architecture ### Core Components #### 1. HeaderButton (`assistantWidget/components/HeaderButton.ts`) Single button component with semantic types: - `collapse` - Collapses widget (to toggle in corner, to bar in sheet) - `new` - Creates new conversation - `chats` - Opens conversation history - `back` - Returns to previous view - `expand` - Expands widget - `minimize` - Minimizes widget **Key Features:** - Variant-aware (different icons for input-bar-sheet vs corner) - Optional text labels - Proper accessibility attributes - Consistent styling via classes #### 2. HeaderButtonManager (`assistantWidget/components/HeaderButtonManager.ts`) Manages button lifecycle and positioning: - Registers buttons - Positions buttons in correct order - Handles view transitions - Manages visibility - Cleans up on destroy ## Usage ### Creating Buttons ```typescript import { HeaderButton } from './components/HeaderButton'; // Create collapse button (variant-aware) const collapseBtn = HeaderButton.createCollapseButton( () => this.collapse(), 'input-bar-sheet', // variant false // showLabel ); // Create new conversation button const newBtn = HeaderButton.createNewButton( () => this.handleNewConversation(), true // showLabel ); // Create chats button const chatsBtn = HeaderButton.createChatsButton( () => this.toggleConversationList(), true // showLabel ); ``` ### Using HeaderButtonManager ```typescript import { HeaderButtonManager } from './components/HeaderButtonManager'; // Initialize manager this.buttonManager = new HeaderButtonManager('input-bar-sheet'); // Register buttons this.buttonManager.registerButton('collapse', collapseBtn); this.buttonManager.registerButton('new', newBtn); this.buttonManager.registerButton('chats', chatsBtn); // Position buttons in conversation header const headerElement = document.querySelector('.am-chat-header'); this.buttonManager.setContext('conversation-header', headerElement); this.buttonManager.positionButtons(['chats', 'new', 'collapse']); // Or use automatic ordering based on context const buttonTypes = this.buttonManager.getButtonOrderForContext( 'conversation-header', hasConversations ); this.buttonManager.positionButtons(buttonTypes); // Position collapse button standalone (welcome view) const container = document.querySelector('.am-chat-container'); this.buttonManager.positionCollapseStandalone(container); ``` ## Button Contexts ### `conversation-header` **Location**: Main conversation view header **Buttons**: `[chats] [new] [collapse]` **Behavior**: - Shows chats button only if conversations exist - All buttons positioned in header actions - Consistent left-to-right order ### `conversation-list-header` **Location**: Conversation list overlay header **Buttons**: `[back] [new] [minimize]` **Behavior**: - Back button to return to conversation - New button to start fresh conversation - Minimize to close list (corner variant only) ### `welcome-standalone` **Location**: Welcome screen (input-bar-sheet only) **Buttons**: `[collapse]` **Behavior**: - Single collapse button - Absolutely positioned top-right - Shows only when expanded ## CSS Classes ### Unified Button Classes - `.am-chat-header-button` - Base class for all header buttons - `.am-header-button-{type}` - Type-specific class (e.g., `.am-header-button-collapse`) - `.am-header-button-with-text` - Button with text label - `.am-header-button-standalone` - Standalone positioned button ### Legacy Classes (Deprecated) These still work for backwards compatibility: - `.am-sheet-collapse``.am-header-button-collapse` - `.am-conversation-new-header``.am-header-button-new` - `.am-conversation-toggle``.am-header-button-chats` - `.am-chat-minimize``.am-header-button-minimize` ## Variant-Aware Behavior ### Collapse Button **Corner Variant:** - Icon: Minimize icon (➖) or Close (✕) - Action: Hides widget completely → toggle button - Title: "Close" **Input-Bar-Sheet Variant:** - Icon: Chevron down (▼) - Action: Collapses to 72px bottom bar - Title: "Minimize" ### New & Chats Buttons **All Variants:** - Consistent icons and labels - Same behavior across variants - Same styling and positioning ## Migration Path ### Phase 1: Foundation (Current) - ✅ Create `HeaderButton` component - ✅ Create `HeaderButtonManager` - ✅ Update CSS for unified classes - ✅ Document system ### Phase 2: Refactor Components (Next) - Refactor `InputBarSheetController` to use `HeaderButton` - Refactor `ConversationManager` to use `HeaderButtonManager` - Refactor `ConversationView` to use unified system - Remove old button creation code ### Phase 3: Cleanup - Remove legacy button classes - Remove duplicate code - Add tests for button system ## Benefits 1. **Maintainability**: Single source of truth for button behavior 2. **Consistency**: All buttons behave the same way 3. **Extensibility**: Easy to add new button types 4. **Accessibility**: Proper ARIA attributes everywhere 5. **Styling**: Consistent classes and styling 6. **Testing**: Easier to test single component 7. **Debugging**: Clear lifecycle and ownership ## Example: Full Implementation ```typescript // In InputBarSheetController import { HeaderButton } from '../components/HeaderButton'; import { HeaderButtonManager } from '../components/HeaderButtonManager'; class InputBarSheetController { private buttonManager: HeaderButtonManager; constructor(element: HTMLElement, viewManager: ViewManager, config: ChatConfig) { this.buttonManager = new HeaderButtonManager('input-bar-sheet'); this.initializeButtons(); } private initializeButtons(): void { // Create collapse button const collapseBtn = HeaderButton.createCollapseButton( () => this.collapse(), 'input-bar-sheet' ); this.buttonManager.registerButton('collapse', collapseBtn); } public positionCollapseButton(): void { // Check view context const header = this.element.querySelector('.am-chat-header'); if (header) { // Conversation view - in header this.buttonManager.setContext('conversation-header', header as HTMLElement); this.buttonManager.positionButtons(['collapse']); } else { // Welcome view - standalone const container = this.element.querySelector('.am-chat-container'); if (container) { this.buttonManager.positionCollapseStandalone(container as HTMLElement); } } } public destroy(): void { this.buttonManager.destroy(); } } ``` ## Future Enhancements - Add button groups/toolbars - Support for dropdowns/menus - Button state management (loading, disabled, etc.) - Keyboard shortcuts integration - Animation system for button transitions - Theme-aware icons