Calendar Picker
The CalendarPicker component provides an elegant date range selection interface with a bottom sheet calendar. It supports single date selection, date ranges, and includes features like month navigation, date validation, and customizable styling.
Import
Section titled “Import”import { CalendarPicker } from '@space-uy/pulsar-ui';Basic usage
Section titled “Basic usage”const [dateRange, setDateRange] = useState<{  fromDate?: string;  toDate?: string;}>({});
<CalendarPicker  label="Select dates"  value={dateRange}  onChange={setDateRange}  placeholder="Choose date range"/>;Properties
Section titled “Properties”| Property | Type | Required | Default value | Description | 
|---|---|---|---|---|
| value | { fromDate?: string; toDate?: string } | ❌ | {} | Current selected date range | 
| onChange | (dateRange: DateRange) => void | ✅ | - | Callback when date range changes | 
| label | string | ❌ | - | Label text displayed above the picker | 
| placeholder | string | ❌ | 'Seleccionar fechas' | Placeholder text when no dates selected | 
| disabled | boolean | ❌ | false | Whether the picker is disabled | 
| error | boolean | ❌ | false | Whether the picker is in error state | 
| hint | string | ❌ | - | Hint text displayed below the picker | 
| style | StyleProp<ViewStyle> | ❌ | - | Custom styles for the container | 
DateRange Type
Section titled “DateRange Type”The date range object structure:
| Property | Type | Description | 
|---|---|---|
| fromDate | string | Start date in ISO format | 
| toDate | string | End date in ISO format | 
Basic examples
Section titled “Basic examples”Single date selection
Section titled “Single date selection”const [selectedDate, setSelectedDate] = useState<{  fromDate?: string;  toDate?: string;}>({});
<CalendarPicker  label="Event date"  value={selectedDate}  onChange={setSelectedDate}  placeholder="Select event date"/>;Date range selection
Section titled “Date range selection”const [dateRange, setDateRange] = useState<{  fromDate?: string;  toDate?: string;}>({});
<CalendarPicker  label="Travel dates"  value={dateRange}  onChange={setDateRange}  placeholder="Select check-in and check-out dates"  hint="Choose your stay duration"/>;With validation
Section titled “With validation”const [bookingDates, setBookingDates] = useState<{  fromDate?: string;  toDate?: string;}>({});const [hasError, setHasError] = useState(false);
const handleDateChange = (dateRange: {  fromDate?: string;  toDate?: string;}) => {  setBookingDates(dateRange);
  // Validate minimum stay  if (dateRange.fromDate && dateRange.toDate) {    const from = new Date(dateRange.fromDate);    const to = new Date(dateRange.toDate);    const daysDiff = Math.ceil(      (to.getTime() - from.getTime()) / (1000 * 60 * 60 * 24)    );    setHasError(daysDiff < 2);  } else {    setHasError(false);  }};
<CalendarPicker  label="Booking dates"  value={bookingDates}  onChange={handleDateChange}  placeholder="Select your stay dates"  error={hasError}  hint={    hasError      ? 'Minimum stay is 2 nights'      : 'Select check-in and check-out dates'  }/>;Advanced examples
Section titled “Advanced examples”Vacation booking system
Section titled “Vacation booking system”const [vacationDates, setVacationDates] = useState<{  fromDate?: string;  toDate?: string;}>({});const [isValidating, setIsValidating] = useState(false);
const handleDateSelection = async (dateRange: {  fromDate?: string;  toDate?: string;}) => {  setVacationDates(dateRange);
  if (dateRange.fromDate && dateRange.toDate) {    setIsValidating(true);    try {      await validateAvailability(dateRange);      // Handle success    } catch (error) {      // Handle unavailable dates    } finally {      setIsValidating(false);    }  }};
<Card style={{ padding: 16 }}>  <Text variant="h4" style={{ marginBottom: 16 }}>    Plan Your Vacation  </Text>
  <CalendarPicker    label="Travel dates"    value={vacationDates}    onChange={handleDateSelection}    placeholder="When would you like to travel?"    disabled={isValidating}    hint={      isValidating ? 'Checking availability...' : 'Select your preferred dates'    }  />
  {vacationDates.fromDate && vacationDates.toDate && (    <View      style={{        marginTop: 16,        padding: 12,        backgroundColor: colors.altBackground,        borderRadius: 8,      }}    >      <Text variant="pm">Selected dates:</Text>      <Text variant="h5">        {format(new Date(vacationDates.fromDate), 'MMM dd')} -{' '}        {format(new Date(vacationDates.toDate), 'MMM dd, yyyy')}      </Text>      <Text variant="ps" style={{ opacity: 0.7 }}>        {getDaysDifference(vacationDates.fromDate, vacationDates.toDate)} nights      </Text>    </View>  )}</Card>;Event scheduling
Section titled “Event scheduling”const [eventDates, setEventDates] = useState<{  fromDate?: string;  toDate?: string;}>({});const [eventType, setEventType] = useState<'single' | 'multi'>('single');
const handleEventTypeChange = (type: 'single' | 'multi') => {  setEventType(type);  if (type === 'single' && eventDates.toDate) {    setEventDates({ fromDate: eventDates.fromDate });  }};
<View style={{ gap: 16 }}>  <Text variant="h4">Schedule Event</Text>
  <View style={{ flexDirection: 'row', gap: 12 }}>    <Chip      text="Single Day"      onPress={() => handleEventTypeChange('single')}      style={{        backgroundColor:          eventType === 'single' ? colors.primary : colors.altBackground,      }}    />    <Chip      text="Multi Day"      onPress={() => handleEventTypeChange('multi')}      style={{        backgroundColor:          eventType === 'multi' ? colors.primary : colors.altBackground,      }}    />  </View>
  <CalendarPicker    label={eventType === 'single' ? 'Event date' : 'Event duration'}    value={eventDates}    onChange={setEventDates}    placeholder={      eventType === 'single'        ? 'Select event date'        : 'Select start and end dates'    }    hint={      eventType === 'single'        ? 'Choose the day for your event'        : 'Select the duration of your event'    }  /></View>;Recurring events
Section titled “Recurring events”const [recurringEvent, setRecurringEvent] = useState<{  fromDate?: string;  toDate?: string;}>({});const [recurringPattern, setRecurringPattern] = useState<string>('weekly');
const recurringOptions = [  { value: 'daily', label: 'Daily' },  { value: 'weekly', label: 'Weekly' },  { value: 'monthly', label: 'Monthly' },];
<Card>  <Text variant="h4">Create Recurring Event</Text>
  <View style={{ marginTop: 16, gap: 16 }}>    <CalendarPicker      label="Event dates"      value={recurringEvent}      onChange={setRecurringEvent}      placeholder="Select event period"      hint="Choose start and end dates for the recurring event"    />
    <Select      label="Repeat pattern"      options={recurringOptions}      value={recurringOptions.find((opt) => opt.value === recurringPattern)}      onChange={(option) => setRecurringPattern(option.value)}      placeholder="Select pattern"    />
    {recurringEvent.fromDate && recurringEvent.toDate && (      <View        style={{          padding: 12,          backgroundColor: colors.altBackground,          borderRadius: 8,        }}      >        <Text variant="pm">Preview:</Text>        <Text variant="ps" style={{ opacity: 0.7 }}>          Event will repeat {recurringPattern} from{' '}          {format(new Date(recurringEvent.fromDate), 'MMM dd')} to{' '}          {format(new Date(recurringEvent.toDate), 'MMM dd, yyyy')}        </Text>      </View>    )}  </View></Card>;Implementation notes
Section titled “Implementation notes”- The calendar uses a bottom sheet interface for better mobile experience
- Date range selection is intuitive with visual feedback for start and end dates
- Month navigation is smooth with proper animations
- The component respects locale settings for date formatting
- Past dates can be disabled based on configuration
- The calendar automatically handles different month lengths and leap years
Calendar Features
Section titled “Calendar Features”- Month Navigation: Smooth transitions between months with arrow controls
- Date Range Selection: Visual feedback for selecting start and end dates
- Today Highlighting: Current date is clearly marked
- Disabled Dates: Past dates or custom disabled dates are visually distinct
- Responsive Design: Adapts to different screen sizes
Accessibility
Section titled “Accessibility”- Full keyboard navigation support for calendar interaction
- Screen reader compatibility with proper date announcements
- High contrast mode support for better visibility
- Touch targets meet minimum size requirements for easy interaction
- Focus management ensures smooth navigation through calendar dates