Input Container
The InputContainer
component provides a foundational container for building custom input interfaces. It handles common input behaviors like focus states, error states, labels, hints, and animations. This component is used internally by other input components but can also be used directly to create custom input experiences.
Import
Section titled “Import”import { InputContainer, type InputRef } from '@space-uy/pulsar-ui';
Basic usage
Section titled “Basic usage”<InputContainer label="Custom Input" onPress={() => console.log('Pressed')}> <Text>Custom content here</Text></InputContainer>
Properties
Section titled “Properties”Property | Type | Required | Default value | Description |
---|---|---|---|---|
onPress | () => void | ✅ | - | Function called when container is pressed |
label | string | ❌ | - | Label text displayed above the container |
hint | string | ❌ | - | Hint text displayed below the container |
error | boolean | ❌ | false | Whether the container is in error state |
focused | boolean | ❌ | false | Whether the container appears focused |
disabled | boolean | ❌ | false | Whether the container is disabled |
size | 'small' | 'default' | ❌ | 'default' | Size variant of the container |
height | number | ❌ | - | Custom height override |
iconName | IconName | ❌ | - | Icon displayed on the left side |
style | StyleProp<ViewStyle> | ❌ | - | Custom styles for the outer container |
contentContainerStyle | StyleProp<ViewStyle> | ❌ | - | Custom styles for the inner content container |
children | React.ReactNode | ❌ | - | Content to display inside the container |
InputRef Type
Section titled “InputRef Type”When using a ref, the following methods are available:
Method | Type | Description |
---|---|---|
focus | () => void | Focus the container (if applicable) |
Basic examples
Section titled “Basic examples”Simple custom input
Section titled “Simple custom input”const [value, setValue] = useState('');const [focused, setFocused] = useState(false);
<InputContainer label="Custom Text Input" focused={focused} onPress={() => setFocused(true)} hint="Enter your custom text"> <TextInput value={value} onChangeText={setValue} onFocus={() => setFocused(true)} onBlur={() => setFocused(false)} style={{ flex: 1, paddingVertical: 8 }} placeholder="Type here..." /></InputContainer>;
Picker-style input
Section titled “Picker-style input”const [selectedValue, setSelectedValue] = useState<string | null>(null);const [showPicker, setShowPicker] = useState(false);
<InputContainer label="Select Option" onPress={() => setShowPicker(true)} iconName="ChevronDown"> <Text style={{ flex: 1, paddingVertical: 8 }}> {selectedValue || 'Choose an option'} </Text></InputContainer>;
With validation state
Section titled “With validation state”const [input, setInput] = useState('');const [hasError, setHasError] = useState(false);
const validateInput = (text: string) => { const isValid = text.length >= 3; setHasError(!isValid && text.length > 0); return isValid;};
<InputContainer label="Validated Input" error={hasError} hint={ hasError ? 'Minimum 3 characters required' : 'Enter at least 3 characters' } onPress={() => { /* handle press */ }}> <TextInput value={input} onChangeText={(text) => { setInput(text); validateInput(text); }} style={{ flex: 1, paddingVertical: 8 }} placeholder="Validated input..." /></InputContainer>;
Advanced examples
Section titled “Advanced examples”Custom date picker
Section titled “Custom date picker”const [selectedDate, setSelectedDate] = useState<Date | null>(null);const [showDatePicker, setShowDatePicker] = useState(false);
<InputContainer label="Birth Date" onPress={() => setShowDatePicker(true)} iconName="Calendar" hint="Select your birth date"> <Text style={{ flex: 1, paddingVertical: 8, color: selectedDate ? colors.foreground : colors.foreground + '80', }} > {selectedDate ? format(selectedDate, 'MMM dd, yyyy') : 'Select date'} </Text> <Icon name="ChevronDown" size={16} color={colors.foreground} /></InputContainer>;
Multi-select input
Section titled “Multi-select input”const [selectedItems, setSelectedItems] = useState<string[]>([]);const [showSelector, setShowSelector] = useState(false);
const displayText = selectedItems.length > 0 ? `${selectedItems.length} items selected` : 'Select items';
<InputContainer label="Categories" onPress={() => setShowSelector(true)} iconName="Tag" focused={showSelector}> <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: 8, paddingVertical: 4, }} > {selectedItems.slice(0, 2).map((item) => ( <Chip key={item} text={item} size="small" /> ))} {selectedItems.length > 2 && ( <Text variant="ps" style={{ opacity: 0.7 }}> +{selectedItems.length - 2} more </Text> )} {selectedItems.length === 0 && ( <Text style={{ color: colors.foreground + '80', paddingVertical: 4 }}> Select categories </Text> )} </View> <Icon name="ChevronDown" size={16} color={colors.foreground} /></InputContainer>;
File upload input
Section titled “File upload input”const [selectedFile, setSelectedFile] = useState<string | null>(null);const [uploading, setUploading] = useState(false);
const handleFileSelect = async () => { try { setUploading(true); // File selection logic here const file = await selectFile(); setSelectedFile(file.name); } catch (error) { // Handle error } finally { setUploading(false); }};
<InputContainer label="Upload Document" onPress={handleFileSelect} iconName="Upload" disabled={uploading} hint={selectedFile ? `Selected: ${selectedFile}` : 'Choose a file to upload'}> <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: 8, paddingVertical: 8, }} > {uploading ? ( <> <LoadingIndicator size={16} /> <Text>Uploading...</Text> </> ) : selectedFile ? ( <> <Icon name="File" size={16} color={colors.primary} /> <Text style={{ flex: 1 }} numberOfLines={1}> {selectedFile} </Text> <IconButton iconName="X" size="small" variant="transparent" onPress={() => setSelectedFile(null)} /> </> ) : ( <Text style={{ color: colors.foreground + '80' }}>No file selected</Text> )} </View></InputContainer>;
Color picker input
Section titled “Color picker input”const [selectedColor, setSelectedColor] = useState<string>('#3b82f6');const [showColorPicker, setShowColorPicker] = useState(false);
<InputContainer label="Brand Color" onPress={() => setShowColorPicker(true)} iconName="Palette" hint="Choose your brand color"> <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 8, }} > <View style={{ width: 24, height: 24, borderRadius: 12, backgroundColor: selectedColor, borderWidth: 1, borderColor: colors.border, }} /> <Text style={{ color: colors.foreground }}>{selectedColor}</Text> </View> <Icon name="ChevronDown" size={16} color={colors.foreground} /></InputContainer>;
Search input with suggestions
Section titled “Search input with suggestions”const [searchQuery, setSearchQuery] = useState('');const [focused, setFocused] = useState(false);const [suggestions, setSuggestions] = useState<string[]>([]);
const handleSearch = (query: string) => { setSearchQuery(query); // Mock suggestions if (query) { setSuggestions([ `${query} option 1`, `${query} option 2`, `${query} option 3`, ]); } else { setSuggestions([]); }};
<View> <InputContainer label="Search" focused={focused} onPress={() => {}} iconName="Search" hint="Start typing to see suggestions" > <TextInput value={searchQuery} onChangeText={handleSearch} onFocus={() => setFocused(true)} onBlur={() => setFocused(false)} style={{ flex: 1, paddingVertical: 8 }} placeholder="Search for items..." /> </InputContainer>
{suggestions.length > 0 && ( <Card style={{ marginTop: 8 }}> {suggestions.map((suggestion, index) => ( <Button key={index} text={suggestion} variant="transparent" onPress={() => { setSearchQuery(suggestion); setSuggestions([]); }} style={{ justifyContent: 'flex-start' }} /> ))} </Card> )}</View>;
Implementation notes
Section titled “Implementation notes”- The component provides a consistent foundation for all input components
- Focus states are automatically managed with theme-appropriate styling
- Error states display with distinctive red border and error text coloring
- Icons are positioned consistently on the left side with proper spacing
- Animations are smooth and respect reduced motion preferences
- The component supports custom heights while maintaining proper touch targets
- Theme integration ensures consistent styling across your application
Animation details
Section titled “Animation details”- Focus transitions: 200ms duration with smooth border color changes
- Error state: 200ms duration with color transitions to error state
- Press feedback: Subtle opacity changes for better user feedback
- Icon animations: Smooth color transitions based on component state
Accessibility
Section titled “Accessibility”- The container properly handles focus states for keyboard navigation
- Screen readers can navigate and understand the input structure
- Error states are properly announced to assistive technology
- All interactive elements maintain minimum touch target sizes
- Color changes maintain proper contrast ratios for accessibility