@agentman/chat-widget
Version:
Agentman Chat Widget for easy integration with web applications
251 lines (200 loc) • 7.58 kB
Markdown
# 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