History Templates Drawer Component
Overview
The History Templates Drawer Component (src/components/HistoryTemplatesDrawer.tsx) provides a mobile-optimized drawer interface for accessing message history and template management. It serves as a dedicated mobile implementation that complements the popover-based desktop interface.
Key Features
Mobile-First Design
- Drawer interface: Bottom-up sliding drawer optimized for mobile interaction
- Touch-friendly: Large touch targets and intuitive gesture support
- Full-screen utilization: Maximum 80vh height for optimal mobile viewing
- Auto-close functionality: Automatically closes drawer after item selection
Comprehensive Functionality
- Message history access: Browse and apply previous messages
- Template management: Save, apply, and delete message templates
- Tabbed interface: Clean separation between history and templates
- State management: Controlled drawer open/close state
Component Structure
Props Interface
interface HistoryTemplatesDrawerProps {
messageHistory: MessageHistoryItem[];
templates: SavedTemplate[];
currentInputValue: string;
canSaveToServer: boolean;
onApplyHistoryMessage: (message: string) => void;
onApplyTemplate: (template: string) => void;
onSaveTemplate: (title: string, content: string) => void;
onDeleteTemplate: (templateId: string) => void;
onDrawerOpenChange?: (open: boolean) => void;
isDrawerOpen?: boolean;
}
Data Types
interface MessageHistoryItem {
text: string;
timestamp: Date;
}
interface SavedTemplate {
id: string;
title: string;
content: string;
}
Core Implementation
Drawer Structure
<Drawer
open={isDrawerOpen}
onOpenChange={onDrawerOpenChange}
>
<DrawerTrigger asChild>
<Button
variant="outline"
size="icon"
className="h-10 w-10"
title="History & Templates"
>
<AlignLeft className="h-4 w-4" />
</Button>
</DrawerTrigger>
<DrawerContent className="px-0 pt-0 max-h-[80vh]">
<div className="pt-4">
<Tabs defaultValue="history" className="w-full">
{/* Tab content */}
</Tabs>
</div>
</DrawerContent>
</Drawer>
Tab Implementation
<Tabs defaultValue="history" className="w-full">
<TabsList className="w-full">
<TabsTrigger value="history" className="flex-1">History</TabsTrigger>
<TabsTrigger value="templates" className="flex-1">Templates</TabsTrigger>
</TabsList>
<TabsContent value="history" className="p-0">
<MessageHistoryTab
messageHistory={messageHistory}
onApplyHistoryMessage={handleApplyHistoryMessage}
/>
</TabsContent>
<TabsContent value="templates" className="p-0">
<TemplatesTab
templates={templates}
currentInputValue={currentInputValue}
canSaveToServer={canSaveToServer}
onApplyTemplate={handleApplyTemplate}
onSaveTemplate={onSaveTemplate}
onDeleteTemplate={onDeleteTemplate}
/>
</TabsContent>
</Tabs>
Auto-Close Functionality
Smart Close Behavior
const handleApplyHistoryMessage = (message: string) => {
onApplyHistoryMessage(message);
onDrawerOpenChange?.(false);
};
const handleApplyTemplate = (template: string) => {
onApplyTemplate(template);
onDrawerOpenChange?.(false);
};
Auto-close triggers:
- Message application: Closes after applying a history message
- Template application: Closes after applying a template
- Manual close: User can close via the close button
- Backdrop tap: Standard drawer behavior for closing
Component Composition
Reusable Tab Components
The drawer leverages existing tab components for consistency:
MessageHistoryTab
- Displays message history with timestamps
- Handles message selection and application
- Provides empty state messaging
TemplatesTab
- Template creation and management
- Save/delete functionality
- Template application interface
Benefits of Composition
- Code reuse: Shared logic between drawer and popover implementations
- Consistency: Identical functionality across different interfaces
- Maintainability: Single source of truth for tab behavior
- Testing: Easier to test individual components in isolation
Mobile Optimization
Touch-Friendly Design
<DrawerContent className="px-0 pt-0 max-h-[80vh]">
<div className="pt-4">
{/* Content with adequate padding for touch targets */}
</div>
</DrawerContent>
Responsive Height
- Maximum height: 80vh to ensure visibility of underlying content
- Content overflow: Scroll handling within drawer content
- Safe areas: Proper handling of mobile safe areas and notches
Gesture Support
- Swipe to close: Native drawer swipe gestures
- Touch targets: Minimum 44px touch target sizes
- Scroll behavior: Smooth scrolling within content areas
State Management
Controlled vs Uncontrolled
// Controlled mode (preferred)
<HistoryTemplatesDrawer
isDrawerOpen={drawerOpen}
onDrawerOpenChange={setDrawerOpen}
// ... other props
/>
// Uncontrolled mode (for simple use cases)
<HistoryTemplatesDrawer
// isDrawerOpen and onDrawerOpenChange are optional
// ... other props
/>
State Flow
// Parent component manages drawer state
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
// Drawer can be opened externally
const openHistoryDrawer = () => setIsDrawerOpen(true);
// Drawer closes automatically after actions
const handleItemSelection = (item) => {
applyItem(item);
setIsDrawerOpen(false); // Auto-close
};
Integration Examples
Basic Mobile Usage
import { HistoryTemplatesDrawer } from '@/components/HistoryTemplatesDrawer';
function MobileChatInput() {
const [messageHistory, setMessageHistory] = useState([]);
const [templates, setTemplates] = useState([]);
const [inputValue, setInputValue] = useState('');
const [drawerOpen, setDrawerOpen] = useState(false);
return (
<div className="mobile-chat-container">
<textarea
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Type your message..."
/>
<div className="flex gap-2">
<HistoryTemplatesDrawer
messageHistory={messageHistory}
templates={templates}
currentInputValue={inputValue}
canSaveToServer={false}
onApplyHistoryMessage={(message) => {
setInputValue(message);
// Drawer closes automatically
}}
onApplyTemplate={(template) => {
setInputValue(template);
// Drawer closes automatically
}}
onSaveTemplate={(title, content) => {
const newTemplate = { id: Date.now().toString(), title, content };
setTemplates(prev => [...prev, newTemplate]);
}}
onDeleteTemplate={(id) => {
setTemplates(prev => prev.filter(t => t.id !== id));
}}
isDrawerOpen={drawerOpen}
onDrawerOpenChange={setDrawerOpen}
/>
<Button onClick={() => sendMessage(inputValue)}>
Send
</Button>
</div>
</div>
);
}
Advanced Integration with Analytics
const handleApplyHistoryMessage = (message: string) => {
setInputValue(message);
// Analytics tracking
analytics.track('history_message_applied', {
messageLength: message.length,
source: 'mobile_drawer'
});
// Auto-close drawer
setDrawerOpen(false);
};
const handleApplyTemplate = (template: string) => {
setInputValue(template);
// Analytics tracking
analytics.track('template_applied', {
templateLength: template.length,
source: 'mobile_drawer'
});
// Auto-close drawer
setDrawerOpen(false);
};
Accessibility Features
Mobile Accessibility
- Screen reader support: Proper ARIA labels and roles
- Voice control: Compatible with voice navigation
- Touch accessibility: Large touch targets for motor impairments
- Focus management: Proper focus handling on open/close
Keyboard Support
- Tab navigation: Full keyboard navigation support
- Escape key: Closes drawer with escape key
- Enter activation: Activates selected items with enter key
Styling and Theme
CSS Classes
// Drawer trigger button
className="h-10 w-10"
// Drawer content
className="px-0 pt-0 max-h-[80vh]"
// Tab interface
className="w-full"
// Tab triggers
className="flex-1"
Mobile-Specific Styling
- Full-width tabs: Tabs use full width of drawer
- Touch-friendly spacing: Increased padding for mobile
- Reduced margins: Optimized spacing for smaller screens
Performance Considerations
Lazy Loading
- Content rendering: Tab content only renders when drawer is open
- Conditional mounting: Components unmount when drawer is closed
- Memory efficiency: Minimal memory footprint when closed
Smooth Animations
- Native drawer: Uses platform-native drawer animations
- Smooth transitions: Hardware-accelerated animations
- Responsive: Maintains 60fps during interactions
Error Handling
Graceful Degradation
- Missing callbacks: Components handle missing callback props
- Empty states: Proper empty state handling for history and templates
- Network issues: Graceful handling of storage failures
Edge Cases
// Handle undefined state
const safeOnDrawerOpenChange = (open: boolean) => {
onDrawerOpenChange?.(open);
};
// Handle missing history
const safeMessageHistory = messageHistory || [];
// Handle missing templates
const safeTemplates = templates || [];
Related Components
- HistoryTemplatesPopover - Desktop popover implementation
- MessageHistoryTab - History tab component
- TemplatesTab - Templates tab component
- ChatInput - Primary integration point
Future Enhancements
Planned Features
- Gesture controls: Enhanced swipe gestures for navigation
- Quick actions: Swipe actions for quick template deletion
- Search functionality: Search through history and templates
- Offline support: Local storage with sync capabilities
UX Improvements
- Animation refinements: Custom animations for better feel
- Haptic feedback: Tactile feedback for mobile interactions
- Voice commands: Voice-activated template application
- Smart suggestions: AI-powered template suggestions
Troubleshooting
Common Issues
Drawer not opening
// Check isDrawerOpen prop
// Verify onDrawerOpenChange callback
// Ensure Drawer component is properly imported
Auto-close not working
// Verify onDrawerOpenChange is called
// Check parent component state management
// Ensure event handlers are properly bound
Content overflow issues
// Check max-h-[80vh] constraint
// Verify scroll behavior in content areas
// Test on different mobile screen sizes
Tab switching problems
// Verify Tabs component props
// Check TabsList and TabsTrigger configuration
// Ensure tab content is properly wrapped