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 || [];

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