TAG_SELECTOR_COMPONENT.md
Component documentation for the TagSelector tag management interface.
Overview
The TagSelector component provides an intuitive interface for selecting and managing tags. It supports both predefined tags from a database and custom user-created tags, with intelligent suggestions and validation.
Component Location
- File:
src/components/TagSelector.tsx - Type: Form Input Component
- Framework: React with TypeScript
Features
- Dual Tag Sources: Supports both predefined and custom tags
- Intelligent Suggestions: Filters available tags based on user input
- Custom Tag Creation: Allows users to create new tags on-the-fly
- Tag Limit Enforcement: Configurable maximum number of tags
- Visual Feedback: Clear distinction between official and custom tags
- Keyboard Navigation: Enter key support for tag creation
- Real-time Validation: Input validation and error handling
Props Interface
interface TagSelectorProps {
selectedTags: string[]; // Currently selected tag IDs
availableTags?: Tag[]; // Database tags to suggest
onChange: (tags: string[]) => void; // Callback when tags change
placeholder?: string; // Input placeholder text
maxTags?: number; // Maximum allowed tags (default: 10)
allowCustomTags?: boolean; // Enable custom tag creation (default: true)
className?: string; // Additional CSS classes
}
Tag Data Structure
interface Tag {
id: string; // Unique identifier
name: string; // Display name
description?: string; // Optional description for tooltips
}
Usage Examples
Basic Implementation
import { TagSelector } from '@/components/TagSelector';
import { useState } from 'react';
function AssistantForm() {
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [availableTags] = useState<Tag[]>([
{ id: 'ai', name: 'AI Assistant', description: 'General AI functionality' },
{ id: 'coding', name: 'Code Helper', description: 'Programming assistance' },
{ id: 'writing', name: 'Writing Aid', description: 'Text composition help' }
]);
return (
<TagSelector
selectedTags={selectedTags}
availableTags={availableTags}
onChange={setSelectedTags}
placeholder="Add relevant tags..."
maxTags={5}
/>
);
}
Read-Only Mode (No Custom Tags)
<TagSelector
selectedTags={selectedTags}
availableTags={predefinedTags}
onChange={setSelectedTags}
allowCustomTags={false}
placeholder="Select from available tags..."
/>
High Tag Limit for Flexible Use
<TagSelector
selectedTags={selectedTags}
availableTags={availableTags}
onChange={setSelectedTags}
maxTags={20}
placeholder="Add multiple tags..."
/>
Tag Suggestion Logic
The component intelligently filters suggestions based on user input:
useEffect(() => {
if (inputValue.trim() && availableTags.length > 0) {
const filtered = availableTags.filter(
tag =>
tag.name.toLowerCase().includes(inputValue.toLowerCase()) &&
!selectedTags.includes(tag.id) // Exclude already selected tags
);
setSuggestions(filtered);
setShowSuggestions(filtered.length > 0);
} else {
setSuggestions([]);
setShowSuggestions(false);
}
}, [inputValue, availableTags, selectedTags]);
Tag Creation Workflow
Adding Predefined Tags
- User types in input field
- Matching suggestions appear in dropdown
- User clicks suggestion or presses Enter
- Tag is added to selected list
Creating Custom Tags
- User types text that doesn’t match existing tags
- “Create [tag name]” option appears in dropdown
- User clicks option or presses Enter
- Custom tag is added with input text as both ID and name
const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && inputValue.trim()) {
e.preventDefault();
// Check for matching suggestion
const matchingTag = suggestions.find(
tag => tag.name.toLowerCase() === inputValue.trim().toLowerCase()
);
if (matchingTag) {
handleAddTag(matchingTag.id);
} else if (allowCustomTags) {
// Add as custom tag
handleAddTag(inputValue.trim());
}
}
};
Tag Display and Management
Selected Tags Visualization
<div className="flex flex-wrap gap-2">
{selectedTags.map((tagId) => (
<Badge key={tagId} variant="secondary" className="flex items-center gap-1">
<TagIcon className="h-3 w-3" />
<span>{getTagDisplay(tagId)}</span>
{isCustomTag(tagId) && (
<span className="text-xs text-muted-foreground">(custom)</span>
)}
<button onClick={() => handleRemoveTag(tagId)}>
<X className="h-3 w-3" />
</button>
</Badge>
))}
</div>
Tag Limit Enforcement
const handleAddTag = (tagId: string) => {
if (selectedTags.length >= maxTags) {
return; // Silently reject if at limit
}
if (!selectedTags.includes(tagId)) {
onChange([...selectedTags, tagId]);
}
setInputValue('');
setShowSuggestions(false);
};
Suggestions Dropdown
The component displays intelligent suggestions with descriptions:
{showSuggestions && (
<div className="absolute top-full left-0 right-0 mt-1 bg-background border rounded-md shadow-lg z-10">
{suggestions.map((tag) => (
<button
key={tag.id}
className="w-full text-left px-3 py-2 hover:bg-muted"
onClick={() => handleAddTag(tag.id)}
>
<div className="flex items-center gap-2">
<TagIcon className="h-4 w-4" />
<div className="flex-1">
<div className="text-sm font-medium">{tag.name}</div>
{tag.description && (
<div className="text-xs text-muted-foreground">{tag.description}</div>
)}
</div>
</div>
</button>
))}
{/* Custom tag creation option */}
{allowCustomTags && !matchingTag && (
<button
className="w-full text-left px-3 py-2 border-t"
onClick={() => handleAddTag(inputValue.trim())}
>
<div className="flex items-center gap-2">
<Plus className="h-4 w-4" />
<div>
<div className="text-sm">Create "{inputValue.trim()}"</div>
<div className="text-xs text-muted-foreground">Add as custom tag</div>
</div>
</div>
</button>
)}
</div>
)}
Utility Functions
Tag Display Resolution
const getTagDisplay = (tagId: string): string => {
const tag = availableTags.find(t => t.id === tagId);
return tag ? tag.name : tagId; // Use name if available, fallback to ID
};
Custom Tag Detection
const isCustomTag = (tagId: string): boolean => {
return !availableTags.some(tag => tag.id === tagId);
};
Accessibility Features
- ARIA Labels: Proper labeling for screen readers
- Keyboard Navigation: Full keyboard support
- Focus Management: Proper focus handling in dropdowns
- Semantic HTML: Use of proper form elements
Styling and Theming
Container Styling
.space-y-4 /* Vertical spacing between elements */
Tag Badge Styling
.flex items-center gap-1 pl-2 pr-1 py-1 /* Badge spacing */
Input Field
.pr-8 /* Right padding for counter */
Suggestions Dropdown
.absolute top-full left-0 right-0 mt-1 bg-background border rounded-md shadow-lg z-10 max-h-48 overflow-y-auto
Error Handling
- Prevents duplicate tag selection
- Enforces maximum tag limits
- Handles empty input gracefully
- Safe string manipulation for custom tags
Performance Considerations
- Efficient filtering with array methods
- Debounced suggestion updates
- Minimal re-renders with proper state management
- Optimized event handling
Dependencies
UI Components
@/components/ui/inputfor text input@/components/ui/buttonfor actions@/components/ui/badgefor tag display@/components/ui/labelfor form labeling
Icons
- Lucide React icons (
Tag,Plus,X)
Utilities
@/lib/utilsfor class name utilities
Related Components
- Form components that use tag selection
- Database tag management interfaces
- Assistant configuration forms
- Content categorization systems